22#include <QPainterPath>
23#include <QRandomGenerator>
42#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
43#include <QXmlSimpleReader>
44#include <QXmlInputSource>
67#include <QXmlStreamReader>
72 El(
const QDomElement* ue,
const QString& key) :
82 void add(
const QDomElement* useE,
const QString& key) {
91 const QString
id = b.attribute(
"id");
98 [&](
const El& e) ->
bool {return e.m_key != id;});
100 while (i !=
m_uses.end()) {
116 debugFlake <<
"WARNING: could not find path in <use xlink:href=\"#xxxxx\" expression. Losing data here. Key:"
127 : m_context(documentResourceManager)
128 , m_documentResourceManager(documentResourceManager)
148#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
149QDomDocument createDocumentFromXmlInputSource(QXmlInputSource *
source, QString *errorMsg,
int *errorLine,
int *errorColumn) {
151 QXmlSimpleReader simpleReader;
152 simpleReader.setFeature(
"http://qt-project.org/xml/features/report-whitespace-only-CharData",
true);
153 simpleReader.setFeature(
"http://xml.org/sax/features/namespaces",
false);
154 simpleReader.setFeature(
"http://xml.org/sax/features/namespace-prefixes",
true);
155 if (!doc.setContent(
source, &simpleReader, errorMsg, errorLine, errorColumn)) {
164#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
165 QXmlInputSource
source(device);
166 return createDocumentFromXmlInputSource(&
source, errorMsg, errorLine, errorColumn);
174#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
177 return createDocumentFromXmlInputSource(&
source, errorMsg, errorLine, errorColumn);
185#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
188 return createDocumentFromXmlInputSource(&
source, errorMsg, errorLine, errorColumn);
199 reader.setNamespaceProcessing(
false);
200#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0))
201 if (!doc.setContent(&reader,
false, errorMsg, errorLine, errorColumn)) {
205 QDomDocument::ParseResult result = doc.setContent(&reader, QDomDocument::ParseOption::PreserveSpacingOnlyNodes);
207 if (errorMsg && errorLine && errorColumn) {
208 *errorMsg = result.errorMessage;
209 *errorLine = result.errorLine;
210 *errorColumn = result.errorColumn;
223 [
this](
const QString &name) {
225 possibleNames << name;
227 for (QString fileName : possibleNames) {
228 QFile file(fileName);
229 if (file.open(QIODevice::ReadOnly)) {
230 return file.readAll();
244 const qreal scale = 72.0 / pixelsPerInch;
245 const QTransform t = QTransform::fromScale(scale, scale);
293 if (e.tagName().contains(
"Gradient")) {
295 }
else if (e.tagName() ==
"meshgradient") {
310 if (e.tagName() ==
"pattern") {
365 QString gradientId = e.attribute(
"id");
366 if (gradientId.isEmpty())
return 0;
374 if (e.hasAttribute(
"xlink:href")) {
376 QString href = e.attribute(
"xlink:href").mid(1);
378 if (!href.isEmpty()) {
387 const QGradientStops defaultStops = gradHelper.
gradient()->stops();
389 if (e.attribute(
"gradientUnits") ==
"userSpaceOnUse") {
396 if (e.tagName() ==
"linearGradient") {
397 QLinearGradient *g =
new QLinearGradient();
399 g->setCoordinateMode(QGradient::ObjectBoundingMode);
405 g->setStart(QPointF(
parseUnitX(e.attribute(
"x1")),
407 g->setFinalStop(QPointF(
parseUnitX(e.attribute(
"x2")),
412 }
else if (e.tagName() ==
"radialGradient") {
413 QRadialGradient *g =
new QRadialGradient();
415 g->setCoordinateMode(QGradient::ObjectBoundingMode);
422 g->setCenter(QPointF(
parseUnitX(e.attribute(
"cx")),
424 g->setFocalPoint(QPointF(
parseUnitX(e.attribute(
"fx")),
430 debugFlake <<
"WARNING: Failed to parse gradient with tag" << e.tagName();
434 QGradient::Spread spreadMethod = QGradient::PadSpread;
435 QString spreadMethodStr = e.attribute(
"spreadMethod");
436 if (!spreadMethodStr.isEmpty()) {
437 if (spreadMethodStr ==
"reflect") {
438 spreadMethod = QGradient::ReflectSpread;
439 }
else if (spreadMethodStr ==
"repeat") {
440 spreadMethod = QGradient::RepeatSpread;
449 if (e.hasAttribute(
"gradientTransform")) {
466 QString gradientId = e.attribute(
"id");
475 if (e.hasAttribute(
"xlink:href")) {
477 QString href = e.attribute(
"xlink:href").mid(1);
479 if (!href.isEmpty()) {
488 if (e.attribute(
"gradientUnits") ==
"userSpaceOnUse") {
492 if (e.hasAttribute(
"transform")) {
499 QString type = e.attribute(
"type");
501 if (!type.isEmpty() && type ==
"bicubic") {
506 for (
int i = 0; i < e.childNodes().size(); ++i) {
507 QDomNode node = e.childNodes().at(i);
509 if (node.nodeName() ==
"meshrow") {
513 startingNode.
point = QPointF(
516 startingNode.
color = QColor();
520 g->getMeshArray()->newRow();
521 for (
int j = 0; j < node.childNodes().size() ; ++j) {
522 QDomNode meshpatchNode = node.childNodes().at(j);
524 if (meshpatchNode.nodeName() ==
"meshpatch") {
528 }
else if (icols != 0) {
535 if (!g->getMeshArray()->addPatch(rawStops, startingNode.
point)) {
536 debugFlake <<
"WARNING: Failed to create meshpatch";
550#define forEachElement( elem, parent ) \
551 for ( QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
552 if ( ( elem = _node.toElement() ).isNull() ) {} else
560 if (!gc)
return rawstops;
562 QDomElement e = meshpatchNode.toElement();
570 QString pathStr = stop.attribute(
"path");
572 rawstops.append({pathStr, color});
578inline QPointF
bakeShapeOffset(
const QTransform &patternTransform,
const QPointF &shapeOffset)
582 QTransform::fromTranslate(-shapeOffset.x(), -shapeOffset.y()) *
583 patternTransform.inverted();
586 return QPointF(result.dx(), result.dy());
600 if (!gc)
return pattHelper;
602 const QString patternId = e.attribute(
"id");
603 if (patternId.isEmpty())
return pattHelper;
607 if (e.hasAttribute(
"xlink:href")) {
609 QString href = e.attribute(
"xlink:href").mid(1);
611 if (!href.isEmpty() &&href != patternId) {
620 pattHelper->setReferenceCoordinates(
622 pattHelper->referenceCoordinates()));
624 pattHelper->setContentCoordinates(
626 pattHelper->contentCoordinates()));
628 if (e.hasAttribute(
"patternTransform")) {
631 pattHelper->setPatternTransform(
p.transform());
636 QRectF referenceRect(
642 pattHelper->setReferenceRect(referenceRect);
644 QRectF referenceRect(
650 pattHelper->setReferenceRect(referenceRect);
664 const QTransform shapeOffsetTransform = dstShapeTransform * gc->
matrix.inverted();
666 const QPointF
extraShapeOffset(shapeOffsetTransform.dx(), shapeOffsetTransform.dy());
673 gc->
matrix = QTransform();
675 const QRectF boundingRect = shape->
outline().boundingRect();
676 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
677 boundingRect.x(), boundingRect.y());
687 if (e.hasAttribute(
"viewBox")) {
690 relativeToShape.mapRect(pattHelper->referenceRect()) :
691 pattHelper->referenceRect();
694 pattHelper->setContentCoordinates(pattHelper->referenceCoordinates());
710 Q_FOREACH (
KoShape *shape, patternShapes) {
722 QRectF ref = pattHelper->referenceRect();
723 ref.translate(offset);
724 pattHelper->setReferenceRect(ref);
730 if (!patternShapes.isEmpty()) {
731 pattHelper->setShapes(patternShapes);
739 const QString
id = e.attribute(
"id");
740 if (
id.isEmpty())
return false;
742 std::unique_ptr<KoMarker> marker(
new KoMarker());
743 marker->setCoordinateSystem(
746 marker->setReferencePoint(QPointF(
parseUnitX(e.attribute(
"refX")),
749 marker->setReferenceSize(QSizeF(
parseUnitX(e.attribute(
"markerWidth",
"3")),
750 parseUnitY(e.attribute(
"markerHeight",
"3"))));
752 const QString orientation = e.attribute(
"orient",
"0");
754 if (orientation ==
"auto") {
755 marker->setAutoOrientation(
true);
757 marker->setExplicitOrientation(
parseAngular(orientation));
769 if (!markerShape)
return false;
771 marker->setShapes({markerShape});
773 m_markers.insert(
id, QExplicitlySharedDataPointer<KoMarker>(marker.release()));
780 const QString
id = e.attribute(
"id");
782 if (
id.isEmpty())
return false;
784 std::unique_ptr<KoSvgSymbol> svgSymbol(
new KoSvgSymbol());
791 QString title = e.firstChildElement(
"title").toElement().text();
793 std::unique_ptr<KoShape> symbolShape(
parseGroup(e));
797 if (!symbolShape)
return false;
799 svgSymbol->shape = symbolShape.release();
800 svgSymbol->title = title;
802 if (title.isEmpty()) svgSymbol->title = id;
804 if (svgSymbol->shape->boundingRect() == QRectF(0.0, 0.0, 0.0, 0.0)) {
805 debugFlake <<
"Symbol" <<
id <<
"seems to be empty, discarding";
816 m_symbols.insert(
id, svgSymbol.release());
823 const QString titleTag =
"title";
824 const QString descriptionTag =
"desc";
825 QDomElement title = e.firstChildElement(titleTag);
826 if (!title.isNull()) {
828 if (!text.data().isEmpty()) {
832 QDomElement description = e.firstChildElement(descriptionTag);
833 if (!description.isNull()) {
835 if (!text.data().isEmpty()) {
845 const QString
id = e.attribute(
"id");
846 if (
id.isEmpty())
return false;
860 if (!clipShape)
return false;
872 const QString
id = e.attribute(
"id");
873 if (
id.isEmpty())
return false;
894 clipMask->setMaskRect(maskRect);
906 if (!clipShape)
return false;
907 clipMask->setShapes({clipShape});
1004 QTransform *transform)
1006 QGradient *resultGradient = 0;
1013 if (gradient->
gradient()->type() == QGradient::LinearGradient) {
1019 const QRectF boundingRect = shape->
outline().boundingRect();
1020 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
1021 boundingRect.x(), boundingRect.y());
1023 const QTransform relativeToUser =
1026 const QTransform userToRelative = relativeToUser.inverted();
1028 const QLinearGradient *o =
static_cast<const QLinearGradient*
>(gradient->
gradient());
1029 QLinearGradient *g =
new QLinearGradient();
1030 g->setStart(userToRelative.map(o->start()));
1031 g->setFinalStop(userToRelative.map(o->finalStop()));
1032 g->setCoordinateMode(QGradient::ObjectBoundingMode);
1033 g->setStops(o->stops());
1034 g->setSpread(o->spread());
1037 *transform = relativeToUser * gradient->
transform() * userToRelative;
1039 }
else if (gradient->
gradient()->type() == QGradient::RadialGradient) {
1046 if (outlineRect.isEmpty())
return resultGradient;
1054 QRadialGradient *rgradient =
static_cast<QRadialGradient*
>(resultGradient);
1057 const QRectF uniformSize(outlineRect.topLeft(), QSizeF(maxDimension, maxDimension));
1059 const QTransform uniformizeTransform =
1060 QTransform::fromTranslate(-outlineRect.x(), -outlineRect.y()) *
1061 QTransform::fromScale(maxDimension / shape->
outlineRect().width(),
1063 QTransform::fromTranslate(outlineRect.x(), outlineRect.y());
1065 const QPointF centerLocal = transform->map(rgradient->center());
1066 const QPointF focalLocal = transform->map(rgradient->focalPoint());
1071 rgradient->setCenter(centerOBB);
1072 rgradient->setFocalPoint(focalOBB);
1077 rgradient->setCenterRadius(centerRadiusOBB);
1078 rgradient->setFocalRadius(focalRadiusOBB);
1080 rgradient->setCoordinateMode(QGradient::ObjectBoundingMode);
1083 *transform = uniformizeTransform * gradient->
transform();
1093 return resultGradient;
1106 const QRectF boundingRect = shape->
outline().boundingRect();
1107 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
1108 boundingRect.x(), boundingRect.y());
1122 const QTransform translationOffset = QTransform::fromTranslate(-shapeglobal.dx(), -shapeglobal.dy());
1131 return resultGradient;
1148 QTransform transform;
1162 bg->setTransform(transform);
1188 const double lineWidth = srcStroke->lineWidth();
1192 if (dashes.count() && lineWidth > 0.0) {
1193 const double dashOffset = srcStroke->dashOffset();
1196 for (
int i = 0; i < dashes.count(); ++i) {
1197 dashes[i] /= lineWidth;
1200 dstStroke->setLineStyle(Qt::CustomDashLine, dashes);
1201 dstStroke->setDashOffset(dashOffset / lineWidth);
1215 stroke->setLineWidth(0.0);
1216 const QColor color = Qt::transparent;
1217 stroke->setColor(color);
1227 QTransform transform;
1230 QBrush brush = *result;
1232 brush.setTransform(transform);
1235 stroke->setLineBrush(brush);
1280 Q_FOREACH(
const QString
p, paintOrder) {
1283 }
else if (
p ==
"stroke") {
1285 }
else if (
p ==
"markers") {
1289 if (paintOrder.size() == 1 && order.isEmpty()) {
1292 if (order.size() == 1) {
1300 }
else if (order.size() > 1) {
1316 if (!clipPath || clipPath->
isEmpty())
1325 shapes.append(clonedShape);
1328 if (!shapeToOriginalUserCoordinates.isNull()) {
1329 const QTransform t =
1330 QTransform::fromTranslate(shapeToOriginalUserCoordinates.x(),
1331 shapeToOriginalUserCoordinates.y());
1355 if (!originalClipMask || originalClipMask->isEmpty())
return;
1357 KoClipMask *clipMask = originalClipMask->clone();
1366 QString href = e.attribute(
"xlink:href");
1370 QString key = href.mid(1);
1374 }
else if (deferredUseStore) {
1375 deferredUseStore->
add(&e, key);
1378 debugFlake <<
"WARNING: Did not find reference for svg 'use' element. Skipping. Id: "
1393 result =
parseGroup(e, referencedElement,
false);
1403 if (!group ||
shapes.isEmpty())
1421 const QString w = e.attribute(
"width");
1422 const QString h = e.attribute(
"height");
1424 qreal width = w.isEmpty() ? 666.0 :
parseUnitX(w);
1425 qreal height = h.isEmpty() ? 555.0 :
parseUnitY(h);
1427 if (w.isEmpty() || h.isEmpty()) {
1429 QTransform viewTransform_unused;
1430 QRectF fakeBoundingRect(0.0, 0.0, 1.0, 1.0);
1433 &viewRect, &viewTransform_unused)) {
1435 QSizeF estimatedSize = viewRect.size();
1437 if (estimatedSize.isValid()) {
1440 estimatedSize = QSizeF(width, width * estimatedSize.height() / estimatedSize.width());
1441 }
else if (!h.isEmpty()) {
1442 estimatedSize = QSizeF(height * estimatedSize.width() / estimatedSize.height(), height);
1445 width = estimatedSize.width();
1446 height = estimatedSize.height();
1451 QSizeF svgFragmentSize(QSizeF(width, height));
1454 *fragmentSize = svgFragmentSize;
1461 const qreal x =
parseUnit(e.attribute(
"x",
"0"));
1462 const qreal y =
parseUnit(e.attribute(
"y",
"0"));
1464 QTransform move = QTransform::fromTranslate(x, y);
1482 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1483 QDomElement b = n.toElement();
1487 if (b.tagName() ==
"title") {
1490 else if (b.tagName() ==
"desc") {
1493 else if (b.tagName() ==
"metadata") {
1515 QTransform viewTransform;
1518 &viewRect, &viewTransform)) {
1558 const QTransform shapeToOriginalUserCoordinates =
1560 coordinateSystemOnLoading;
1563 return QPointF(shapeToOriginalUserCoordinates.dx(), shapeToOriginalUserCoordinates.dy());
1568 if (createContext) {
1583 if (!overrideChildrenFrom.isNull()) {
1586 if (overrideChildrenFrom.tagName() ==
"symbol") {
1587 childShapes = {
parseGroup(overrideChildrenFrom)};
1596 applyId(b.attribute(
"id"), group);
1599 Q_FOREACH(
KoShape *shape, childShapes) {
1603 if (createContext) {
1616 if (createContext) {
1625 QDomNode firstChild = e.firstChild();
1626 return !firstChild.isNull() && firstChild == e.lastChild() && firstChild.isText() ?
1627 firstChild.toText() : QDomText();
1634 if (!dShape)
continue;
1641 if (e.hasAttribute(
"path")) {
1642 QDomElement
p = e.ownerDocument().createElement(
"path");
1643 p.setAttribute(
"d", e.attribute(
"path"));
1645 if (hideShapesFromDefs) {
1651 if (e.hasAttribute(
"href")) {
1652 pathId = e.attribute(
"href").remove(0, 1);
1653 }
else if (e.hasAttribute(
"xlink:href")) {
1654 pathId = e.attribute(
"xlink:href").remove(0, 1);
1656 if (!pathId.isNull()) {
1662 if(cloned &&
shapeInDefs(s) && hideShapesFromDefs) {
1678 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1679 QDomElement b = n.toElement();
1680 if (b.tagName() ==
"title" || b.tagName() ==
"desc")
continue;
1691 if (b.hasChildNodes()) {
1713 bool hideShapesFromDefs =
true;
1714 if (mergeIntoShape) {
1715 rootTextShape = mergeIntoShape;
1716 hideShapesFromDefs =
false;
1723 if (rootTextShape) {
1730 if (rootTextShape) {
1742 if (e.hasAttribute(
"krita:textVersion")) {
1746 debugFlake <<
"WARNING: \"krita:textVersion\" attribute appeared in non-root text shape";
1752 if (!mergeIntoShape) {
1759 static const KoID warning(
"warn_text_version_1",
1760 i18nc(
"warning while loading SVG text",
1761 "The document has vector text created "
1762 "in Krita 4.x. When you save the document, "
1763 "the text object will be converted into "
1764 "Krita 5 format that will no longer be "
1765 "compatible with Krita 4.x"));
1776 if (!mergeIntoShape) {
1782 applyId(e.attribute(
"id"), rootTextShape);
1790 if (!onlyTextChild.isNull()) {
1804 return rootTextShape;
1812 bool isSwitch = e.tagName() ==
"switch";
1816 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1817 QDomElement b = n.toElement();
1826 if (b.hasAttribute(
"requiredFeatures")) {
1829 if (b.hasAttribute(
"requiredExtensions")) {
1833 if (b.hasAttribute(
"systemLanguage")) {
1839 shapes.append(currentShapes);
1842 if (isSwitch && !currentShapes.isEmpty())
1860 if (deferredUseStore) {
1864 if (b.tagName() ==
"svg") {
1866 }
else if (b.tagName() ==
"g" || b.tagName() ==
"a") {
1869 }
else if (b.tagName() ==
"symbol") {
1871 }
else if (b.tagName() ==
"switch") {
1875 }
else if (b.tagName() ==
"defs") {
1876 if (b.childNodes().count() > 0) {
1887 }
else if (b.tagName() ==
"linearGradient" || b.tagName() ==
"radialGradient") {
1888 }
else if (b.tagName() ==
"pattern") {
1889 }
else if (b.tagName() ==
"filter") {
1891 }
else if (b.tagName() ==
"clipPath") {
1893 }
else if (b.tagName() ==
"mask") {
1895 }
else if (b.tagName() ==
"marker") {
1897 }
else if (b.tagName() ==
"style") {
1899 }
else if (b.tagName() ==
"text" || b.tagName() ==
"tspan" || b.tagName() ==
"textPath") {
1901 }
else if (b.tagName() ==
"rect" || b.tagName() ==
"ellipse" || b.tagName() ==
"circle" || b.tagName() ==
"line" || b.tagName() ==
"polyline"
1902 || b.tagName() ==
"polygon" || b.tagName() ==
"path" || b.tagName() ==
"image") {
1913 QTextStream stream(&
string);
1921 }
else if (b.tagName() ==
"use") {
1926 }
else if (b.tagName() ==
"color-profile") {
1946 if (element.tagName() ==
"line") {
1949 double x1 = element.attribute(
"x1").isEmpty() ? 0.0 :
parseUnitX(element.attribute(
"x1"));
1950 double y1 = element.attribute(
"y1").isEmpty() ? 0.0 :
parseUnitY(element.attribute(
"y1"));
1951 double x2 = element.attribute(
"x2").isEmpty() ? 0.0 :
parseUnitX(element.attribute(
"x2"));
1952 double y2 = element.attribute(
"y2").isEmpty() ? 0.0 :
parseUnitY(element.attribute(
"y2"));
1954 path->moveTo(QPointF(x1, y1));
1955 path->lineTo(QPointF(x2, y2));
1959 }
else if (element.tagName() ==
"polyline" || element.tagName() ==
"polygon") {
1966 for (QStringList::Iterator it = pointList.begin(); it != pointList.end(); ++it) {
1970 if (it == pointList.end())
1974 path->moveTo(point);
1977 path->lineTo(point);
1979 if (element.tagName() ==
"polygon")
1982 path->setPosition(path->normalize());
1986 }
else if (element.tagName() ==
"path") {
1992 loader.
parseSvg(element.attribute(
"d"),
true);
1993 path->setPosition(path->normalize());
2000 path->setSize(newSize);
2001 path->setPosition(newPosition);
2003 if (element.hasAttribute(
"sodipodi:nodetypes")) {
2004 path->loadNodeTypes(element.attribute(
"sodipodi:nodetypes"));
2026 applyId(b.attribute(
"id"), obj);
2053 applyId(b.attribute(
"id"), obj);
2096 if (!svgShape->
loadSvg(element, context)) {
2114 if (
value.isEmpty()) {
2117 unsigned int start =
value.indexOf(
'(') + 1;
2118 unsigned int end =
value.indexOf(
')', start);
2120 QString val =
value.mid(start, end - start);
2122 if (val.startsWith(
"evenodd,")) {
2123 start += QString(
"evenodd,").
size();
2124 fillRule =
"evenodd";
2125 }
else if (val.startsWith(
"nonzero,")) {
2126 start += QString(
"nonzero,").size();
2127 fillRule =
"nonzero";
2129 val =
value.mid(start, end - start);
2132 if (
value.startsWith(
"url(")) {
2133 start =
value.indexOf(
'#') + 1;
2142 if (cloned &&
shapeInDefs(s) && hideShapesFromDefs) {
2147 }
else if (
value.startsWith(
"circle(")) {
2148 el = e.ownerDocument().createElement(
"circle");
2151 if (params.contains(
"at")) {
2156 }
else if (
value.startsWith(
"ellipse(")) {
2157 el = e.ownerDocument().createElement(
"ellipse");
2161 if (params.contains(
"at")) {
2166 }
else if (
value.startsWith(
"polygon(")) {
2167 el = e.ownerDocument().createElement(
"polygon");
2170 bool xVal = points.size() % 2;
2177 el.setAttribute(
"points", points.join(
" "));
2178 }
else if (
value.startsWith(
"path(")) {
2179 el = e.ownerDocument().createElement(
"path");
2183 el.setAttribute(
"d",
value.mid(start, end - start));
2186 el.setAttribute(
"fill-rule", fillRule);
2199 Q_FOREACH(
const QString param, params) {
2202 shapeList.append(s);
2212 debugFlake <<
"Could not find factory for shape id" << shapeID;
2218 debugFlake <<
"Could not create Default shape for shape id" << shapeID;
2221 if (shape->
shapeId().isEmpty()) {
2244 if (existingShape) {
2245 debugFlake <<
"SVG contains nodes with duplicated id:" << id;
2249 const QString suffix = QString::number(QRandomGenerator::system()->bounded(0x10000000, 0x7FFFFFFF), 16);
2250 const QString newName =
id +
'_' + suffix;
float value(const T *src, size_t ch)
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
QSharedPointer< KoShapeStrokeModel > KoShapeStrokeModelSP
#define KoSvgTextShape_SHAPEID
#define KoSvgTextShape_TEXTCONTOURGROUP
QPointF bakeShapeOffset(const QTransform &patternTransform, const QPointF &shapeOffset)
void applyDashes(const KoShapeStrokeSP srcStroke, KoShapeStrokeSP dstStroke)
QGradient * prepareGradientForShape(const SvgGradientHelper *gradient, const KoShape *shape, const SvgGraphicsContext *gc, QTransform *transform)
SvgMeshGradient * prepareMeshGradientForShape(SvgGradientHelper *gradient, const KoShape *shape, const SvgGraphicsContext *gc)
QPointF extraShapeOffset(const KoShape *shape, const QTransform coordinateSystemOnLoading)
#define forEachElement(elem, parent)
QMap< QString, QString > SvgStyles
Clip path used to clip shapes.
A simple solid color shape background.
const T value(const QString &id) const
T get(const QString &id) const
A gradient shape background.
static MarkerCoordinateSystem coordinateSystemFromString(const QString &value)
void parseSvg(const QString &svgInputData, bool process=false)
The position of a path point within a path shape.
void setAutoFillMarkers(bool value)
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule to be used for painting the background.
void setMarker(KoMarker *marker, KoFlake::MarkerPosition pos)
void clear()
Removes all subpaths and their points from the path.
virtual KoShape * createDefaultShape(KoDocumentResourceManager *documentResources=0) const
The undo / redo command for grouping shapes.
void redo() override
redo the command
QList< KoShapeFactoryBase * > factoriesForElement(const QString &nameSpace, const QString &elementName)
static KoShapeRegistry * instance()
virtual void setPaintOrder(PaintOrder first, PaintOrder second)
setPaintOrder set the paint order. As there's only three entries in any given paintorder,...
virtual QSizeF size() const
Get the size of the shape in pt.
void setName(const QString &name)
virtual QRectF outlineRect() const
bool hasCommonParent(const KoShape *shape) const
void setInheritBackground(bool value)
setInheritBackground marks a shape as inheriting the background from the parent shape....
void setZIndex(qint16 zIndex)
virtual QPainterPath outline() const
void setClipMask(KoClipMask *clipMask)
Sets a new clip mask, removing the old one. The mask is owned by the shape.
void setTransparency(qreal transparency)
static QVector< PaintOrder > defaultPaintOrder()
default paint order as per SVG specification
virtual KoShapeStrokeModelSP stroke() const
void applyAbsoluteTransformation(const QTransform &matrix)
virtual QRectF boundingRect() const
Get the bounding box of the shape.
virtual void setStroke(KoShapeStrokeModelSP stroke)
void setAdditionalAttribute(const QString &name, const QString &value)
QTransform absoluteTransformation() const
void setTransformation(const QTransform &matrix)
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
void setClipPath(KoClipPath *clipPath)
Sets a new clip path, removing the old one.
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
QTransform transformation() const
Returns the shapes local transformation matrix.
void setInheritStroke(bool value)
setInheritStroke marks a shape as inheriting the stroke from the parent shape. NOTE: The currently se...
void setShapeId(const QString &id)
void leaveNodeSubtree()
Set the current node to its parent, leaving the subtree.
bool loadSvgText(const QDomText &text, SvgLoadingContext &context)
Loads the textt into the current node.
void setTextPathOnCurrentNode(KoShape *s)
Set the textPath on the current node.
void nextNode()
Switch to next node.
void enterNodeSubtree()
Set the current node to its first child, entering the subtree.
void setStyleInfo(KoShape *s)
Set the style info from the shape. This is necessary because SVGParser only understands loading the b...
bool loadSvg(const QDomElement &element, SvgLoadingContext &context, bool root=false)
Create a new text node.
@ KraTextVersionId
Int, used for handling incorrectly saved files.
QVariant property(PropertyId id, const QVariant &defaultValue=QVariant()) const
bool hasProperty(PropertyId id) const
void setProperty(PropertyId id, const QVariant &value)
void setShapesSubtract(QList< KoShape * > shapesSubtract)
setShapesSubtract
void setShapesInside(QList< KoShape * > shapesInside)
setShapesInside
KoFlake::CoordinateSystem clipPathUnits() const
Returns the clip path units type.
void setClipPathUnits(KoFlake::CoordinateSystem clipPathUnits)
Set the clip path units type.
QList< KoShape * > shapes() const
void setShapes(const QList< KoShape * > &shapes)
QGradient * gradient() const
Returns the gradient.
KoFlake::CoordinateSystem gradientUnits() const
Returns gradient units type.
bool isMeshGradient() const
void setSpreadMode(const QGradient::Spread &spreadMode)
void setMeshGradient(SvgMeshGradient *g)
Sets the meshgradient.
void setGradient(QGradient *g)
Sets the gradient.
void setGradientUnits(KoFlake::CoordinateSystem units)
Sets the gradient units type.
QTransform transform() const
Returns the gradient transformation.
void setTransform(const QTransform &transform)
Sets the gradient transformation.
QScopedPointer< SvgMeshGradient > & meshgradient()
Returns the meshgradient.
QString clipPathId
the current clip path id
Qt::FillRule fillRule
the current fill rule
QRectF currentBoundingBox
the current bound box used for bounding box units
bool visible
controls visibility of the shape (inherited)
void workaroundClearInheritedFillProperties()
QString fillId
the current fill id (used for gradient/pattern fills)
KoSvgTextProperties textProperties
Stores textProperties.
QString shapeSubtractValue
String of value shape-subtract, will be parsed later.
QString strokeId
the current stroke id (used for gradient strokes)
bool display
controls display of shape
QTransform matrix
the current transformation matrix
StyleType strokeType
the current stroke type
QString paintOrder
String list indicating paint order;.
QString shapeInsideValue
String of value shape-inside, will be parsed later.
KoShapeStrokeSP stroke
the current stroke
QString clipMaskId
the current clip mask id
qreal pixelsPerInch
controls the resolution of the image raster
StyleType fillType
the current fill type
@ Complex
gradient or pattern style
qreal opacity
the shapes opacity
QColor fillColor
the current fill color. Default is black fill as per svg spec
Contains data used for loading svg.
void addDefinition(const QDomElement &element)
Adds a definition for later use.
void setInitialXmlBaseDir(const QString &baseDir)
Sets the initial xml base dir, i.e. the directory the svg file is read from.
SvgGraphicsContext * pushGraphicsContext(const QDomElement &element=QDomElement(), bool inherit=true)
Pushes a new graphics context to the stack.
void registerShape(const QString &id, KoShape *shape)
Registers a shape so it can be referenced later.
bool isRootContext() const
void popGraphicsContext()
Pops the current graphics context from the stack.
KoSvgTextProperties resolvedProperties(bool onlyFontAndLineHeight=false) const
void addStyleSheet(const QDomElement &styleSheet)
Adds a css style sheet.
QString xmlBaseDir() const
Returns the current xml base dir.
KoShape * shapeById(const QString &id)
Returns shape with specified id.
QDomElement definition(const QString &id) const
Returns the definition with the specified id.
SvgGraphicsContext * currentGC() const
Returns the current graphics context.
void parseProfile(const QDomElement &element)
parses 'color-profile' tag and saves it in the context
void setFileFetcher(FileFetcherFunc func)
SvgStyleParser * styleParser
bool hasDefinition(const QString &id) const
Checks if a definition with the specified id exists.
int nextZIndex()
Returns the next z-index.
void setTransform(const QTransform &matrix)
bool parseClipPath(const QDomElement &)
Parses a clip path element.
void applyCurrentBasicStyle(KoShape *shape)
void parseTextChildren(const QDomElement &e, KoSvgTextLoader &textLoader, bool hideShapesFromDefs=true)
parse children of a <text > element into the root shape.
void parseMetadataApplyToShape(const QDomElement &e, KoShape *shape)
This parses the SVG native title and desc elements and adds them into additional attributes.
SvgGradientHelper * parseGradient(const QDomElement &)
Parses a gradient element.
KoShape * createShapeFromElement(const QDomElement &element, SvgLoadingContext &context)
Creates shape from specified svg element.
QString m_documentDescription
QList< KoShape * > createListOfShapesFromCSS(const QDomElement e, const QString value, SvgLoadingContext &context, bool hideShapesFromDefs=true)
Create a list of shapes from a CSS shapes definition with potentially multiple shapes.
qreal parseUnitX(const QString &unit)
parses a length attribute in x-direction
void applyMarkers(KoPathShape *shape)
void applyFillStyle(KoShape *shape)
Applies the current fill style to the object.
void addToGroup(QList< KoShape * > shapes, KoShapeContainer *group)
Adds list of shapes to the given group shape.
QMap< QString, SvgClipPathHelper > m_clipPaths
bool m_inheritStrokeFillByDefault
void applyClipping(KoShape *shape, const QPointF &shapeToOriginalUserCoordinates)
Applies the current clip path to the object.
bool m_isInsideTextSubtree
bool parseClipMask(const QDomElement &e)
static QDomDocument createDocumentFromSvg(QIODevice *device, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0)
QVector< KoSvgSymbol * > takeSymbols()
QList< KoShape * > m_defsShapes
void applyStyle(KoShape *, const QDomElement &, const QPointF &shapeToOriginalUserCoordinates)
Applies styles to the given shape.
bool parseMarker(const QDomElement &e)
QMap< QString, KoSvgSymbol * > m_symbols
void setXmlBaseDir(const QString &baseDir)
Sets the initial xml base directory (the directory form where the file is read)
void applyId(const QString &id, KoShape *shape)
Applies id to specified shape.
SvgParser(KoDocumentResourceManager *documentResourceManager)
QMap< QString, QExplicitlySharedDataPointer< KoMarker > > m_markers
KoShape * parseTextElement(const QDomElement &e, KoSvgTextShape *mergeIntoShape=0)
QDomText getTheOnlyTextChild(const QDomElement &e)
void applyStrokeStyle(KoShape *shape)
Applies the current stroke style to the object.
QString documentTitle() const
KoShape * createPath(const QDomElement &)
Create path object from the given xml element.
qreal parseAngular(const QString &unit)
parses a angular attribute values, result in radians
KoShape * getTextPath(const QDomElement &e, bool hideShapesFromDefs=true)
Get the path for the gives textPath element.
QSharedPointer< KoVectorPatternBackground > parsePattern(const QDomElement &e, const KoShape *__shape)
Parses a pattern element.
void setResolveTextPropertiesForTopLevel(const bool enable)
QStringList warnings() const
KoDocumentResourceManager * m_documentResourceManager
void applyViewBoxTransform(const QDomElement &element)
SvgGradientHelper * parseMeshGradient(const QDomElement &)
Parses mesh gradient element.
void uploadStyleToContext(const QDomElement &e)
QList< KoShape * > parseSvg(const QDomElement &e, QSizeF *fragmentSize=0)
Parses a svg fragment, returning the list of top level child shapes.
std::function< QByteArray(const QString &) FileFetcherFunc)
KoShape * parseUse(const QDomElement &, DeferredUseStore *deferredUseStore)
Parses a use element, returning a list of child shapes.
bool shapeInDefs(const KoShape *shape)
Check whether the shapes are in the defs of the SVG document.
QList< KoShape * > shapes() const
Returns the list of all shapes of the svg document.
bool parseSymbol(const QDomElement &e)
void setFileFetcher(FileFetcherFunc func)
bool m_resolveTextPropertiesForTopLevel
KoShape * createObjectDirect(const QDomElement &b)
SvgClipPathHelper * findClipPath(const QString &id)
find clip path with given id in clip path map
qreal parseUnitY(const QString &unit)
parses a length attribute in y-direction
void setResolution(const QRectF boundsInPixels, qreal pixelsPerInch)
QSharedPointer< KoVectorPatternBackground > findPattern(const QString &id, const KoShape *shape)
find pattern with given id in pattern map
void setDefaultKraTextVersion(int version)
QMap< KoShape *, QTransform > m_shapeParentTransform
QList< QPair< QString, QColor > > parseMeshPatch(const QDomNode &meshpatch)
Parses a single meshpatch and returns the pointer.
QList< QExplicitlySharedDataPointer< KoMarker > > knownMarkers() const
QString documentDescription() const
void applyMaskClipping(KoShape *shape, const QPointF &shapeToOriginalUserCoordinates)
QList< KoShape * > m_shapes
void applyCurrentStyle(KoShape *shape, const QPointF &shapeToOriginalUserCoordinates)
KoShape * createShapeFromCSS(const QDomElement e, const QString value, SvgLoadingContext &context, bool hideShapesFromDefs=true)
Creates a shape from a CSS shapes definition.
SvgLoadingContext m_context
QVector< KoID > m_warnings
SvgGradientHelper * findGradient(const QString &id)
find gradient with given id in gradient map
qreal parseUnitXY(const QString &unit)
parses a length attribute in xy-direction
QList< KoShape * > parseContainer(const QDomElement &)
Parses a container element, returning a list of child shapes.
QMap< QString, QSharedPointer< KoClipMask > > m_clipMasks
KoShape * parseGroup(const QDomElement &e, const QDomElement &overrideChildrenFrom=QDomElement(), bool createContext=true)
Parses a group-like element element, saving all its topmost properties.
QList< KoShape * > parseSingleElement(const QDomElement &b, DeferredUseStore *deferredUseStore=0)
XXX.
qreal parseUnit(const QString &, bool horiz=false, bool vert=false, const QRectF &bbox=QRectF())
parses a length attribute
void setFillStrokeInheritByDefault(const bool enable)
KoShape * resolveUse(const QDomElement &e, const QString &key)
void parseDefsElement(const QDomElement &e)
void applyPaintOrder(KoShape *shape)
KoShape * createShape(const QString &shapeID)
creates a shape from the given shape id
QMap< QString, SvgGradientHelper > m_gradients
KoShape * createObject(const QDomElement &, const SvgStyles &style=SvgStyles())
Creates an object from the given xml element.
An interface providing svg loading and saving routines.
virtual bool loadSvg(const QDomElement &element, SvgLoadingContext &context)
Loads data from specified svg element.
QPair< qreal, QColor > parseColorStop(const QDomElement &, SvgGraphicsContext *context, qreal &previousOffset)
void parseColorStops(QGradient *, const QDomElement &, SvgGraphicsContext *context, const QGradientStops &defaultStops)
Parses gradient color stops.
void parseStyle(const SvgStyles &styles, const bool inheritByDefault=false)
Parses specified style attributes.
SvgStyles collectStyles(const QDomElement &)
Creates style map from given xml element.
void parseFont(const SvgStyles &styles)
Parses font attributes.
static qreal parseUnitX(SvgGraphicsContext *gc, const KoSvgTextProperties &resolved, const QString &unit)
parses a length attribute in x-direction
static double fromPercentage(QString s, bool *ok=nullptr)
static qreal parseUnitAngular(SvgGraphicsContext *gc, const QString &unit)
parses angle, result in radians!
static bool parseViewBox(const QDomElement &e, const QRectF &elementBounds, QRectF *_viewRect, QTransform *_viewTransform)
Parses a viewbox attribute into an rectangle.
static QStringList simplifyList(const QString &str)
static qreal parseUnit(SvgGraphicsContext *gc, const KoSvgTextProperties &resolved, QStringView, bool horiz=false, bool vert=false, const QRectF &bbox=QRectF())
Parses a length attribute.
static double fromUserSpace(double value)
static qreal parseUnitXY(SvgGraphicsContext *gc, const KoSvgTextProperties &resolved, const QString &unit)
parses a length attribute in xy-direction
static qreal parseUnitY(SvgGraphicsContext *gc, const KoSvgTextProperties &resolved, const QString &unit)
parses a length attribute in y-direction
static QString mapExtendedShapeTag(const QString &tagName, const QDomElement &element)
#define KIS_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_ASSERT_RECOVER_NOOP(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
QSharedPointer< T > toQShared(T *ptr)
auto maxDimension(Size size) -> decltype(size.width())
QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc)
double toDouble(const QString &str, bool *ok=nullptr)
void setUtf8OnStream(QTextStream &stream)
KRITAFLAKE_EXPORT QGradient * cloneGradient(const QGradient *gradient)
clones the given gradient
CoordinateSystem coordinatesFromString(const QString &value, CoordinateSystem defaultValue)
void setExtraShapeOffset(const QPointF &value)
El(const QDomElement *ue, const QString &key)
const QDomElement * m_useElement
void checkPendingUse(const QDomElement &b, QList< KoShape * > &shapes)
DeferredUseStore(SvgParser *p)
void add(const QDomElement *useE, const QString &key)