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);
230 file.open(QIODevice::ReadOnly);
231 return file.readAll();
245 const qreal scale = 72.0 / pixelsPerInch;
246 const QTransform t = QTransform::fromScale(scale, scale);
294 if (e.tagName().contains(
"Gradient")) {
296 }
else if (e.tagName() ==
"meshgradient") {
311 if (e.tagName() ==
"pattern") {
366 QString gradientId = e.attribute(
"id");
367 if (gradientId.isEmpty())
return 0;
375 if (e.hasAttribute(
"xlink:href")) {
377 QString href = e.attribute(
"xlink:href").mid(1);
379 if (!href.isEmpty()) {
388 const QGradientStops defaultStops = gradHelper.
gradient()->stops();
390 if (e.attribute(
"gradientUnits") ==
"userSpaceOnUse") {
397 if (e.tagName() ==
"linearGradient") {
398 QLinearGradient *g =
new QLinearGradient();
400 g->setCoordinateMode(QGradient::ObjectBoundingMode);
406 g->setStart(QPointF(
parseUnitX(e.attribute(
"x1")),
408 g->setFinalStop(QPointF(
parseUnitX(e.attribute(
"x2")),
413 }
else if (e.tagName() ==
"radialGradient") {
414 QRadialGradient *g =
new QRadialGradient();
416 g->setCoordinateMode(QGradient::ObjectBoundingMode);
423 g->setCenter(QPointF(
parseUnitX(e.attribute(
"cx")),
425 g->setFocalPoint(QPointF(
parseUnitX(e.attribute(
"fx")),
431 debugFlake <<
"WARNING: Failed to parse gradient with tag" << e.tagName();
435 QGradient::Spread spreadMethod = QGradient::PadSpread;
436 QString spreadMethodStr = e.attribute(
"spreadMethod");
437 if (!spreadMethodStr.isEmpty()) {
438 if (spreadMethodStr ==
"reflect") {
439 spreadMethod = QGradient::ReflectSpread;
440 }
else if (spreadMethodStr ==
"repeat") {
441 spreadMethod = QGradient::RepeatSpread;
450 if (e.hasAttribute(
"gradientTransform")) {
467 QString gradientId = e.attribute(
"id");
476 if (e.hasAttribute(
"xlink:href")) {
478 QString href = e.attribute(
"xlink:href").mid(1);
480 if (!href.isEmpty()) {
489 if (e.attribute(
"gradientUnits") ==
"userSpaceOnUse") {
493 if (e.hasAttribute(
"transform")) {
500 QString type = e.attribute(
"type");
502 if (!type.isEmpty() && type ==
"bicubic") {
507 for (
int i = 0; i < e.childNodes().size(); ++i) {
508 QDomNode node = e.childNodes().at(i);
510 if (node.nodeName() ==
"meshrow") {
514 startingNode.
point = QPointF(
517 startingNode.
color = QColor();
521 g->getMeshArray()->newRow();
522 for (
int j = 0; j < node.childNodes().size() ; ++j) {
523 QDomNode meshpatchNode = node.childNodes().at(j);
525 if (meshpatchNode.nodeName() ==
"meshpatch") {
529 }
else if (icols != 0) {
536 if (!g->getMeshArray()->addPatch(rawStops, startingNode.
point)) {
537 debugFlake <<
"WARNING: Failed to create meshpatch";
551#define forEachElement( elem, parent ) \
552 for ( QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
553 if ( ( elem = _node.toElement() ).isNull() ) {} else
561 if (!gc)
return rawstops;
563 QDomElement e = meshpatchNode.toElement();
571 QString pathStr = stop.attribute(
"path");
573 rawstops.append({pathStr, color});
579inline QPointF
bakeShapeOffset(
const QTransform &patternTransform,
const QPointF &shapeOffset)
583 QTransform::fromTranslate(-shapeOffset.x(), -shapeOffset.y()) *
584 patternTransform.inverted();
587 return QPointF(result.dx(), result.dy());
601 if (!gc)
return pattHelper;
603 const QString patternId = e.attribute(
"id");
604 if (patternId.isEmpty())
return pattHelper;
608 if (e.hasAttribute(
"xlink:href")) {
610 QString href = e.attribute(
"xlink:href").mid(1);
612 if (!href.isEmpty() &&href != patternId) {
621 pattHelper->setReferenceCoordinates(
623 pattHelper->referenceCoordinates()));
625 pattHelper->setContentCoordinates(
627 pattHelper->contentCoordinates()));
629 if (e.hasAttribute(
"patternTransform")) {
632 pattHelper->setPatternTransform(
p.transform());
637 QRectF referenceRect(
643 pattHelper->setReferenceRect(referenceRect);
645 QRectF referenceRect(
651 pattHelper->setReferenceRect(referenceRect);
665 const QTransform shapeOffsetTransform = dstShapeTransform * gc->
matrix.inverted();
667 const QPointF
extraShapeOffset(shapeOffsetTransform.dx(), shapeOffsetTransform.dy());
674 gc->
matrix = QTransform();
676 const QRectF boundingRect = shape->
outline().boundingRect();
677 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
678 boundingRect.x(), boundingRect.y());
688 if (e.hasAttribute(
"viewBox")) {
691 relativeToShape.mapRect(pattHelper->referenceRect()) :
692 pattHelper->referenceRect();
695 pattHelper->setContentCoordinates(pattHelper->referenceCoordinates());
711 Q_FOREACH (
KoShape *shape, patternShapes) {
723 QRectF ref = pattHelper->referenceRect();
724 ref.translate(offset);
725 pattHelper->setReferenceRect(ref);
731 if (!patternShapes.isEmpty()) {
732 pattHelper->setShapes(patternShapes);
740 const QString
id = e.attribute(
"id");
741 if (
id.isEmpty())
return false;
743 QScopedPointer<KoMarker> marker(
new KoMarker());
744 marker->setCoordinateSystem(
747 marker->setReferencePoint(QPointF(
parseUnitX(e.attribute(
"refX")),
750 marker->setReferenceSize(QSizeF(
parseUnitX(e.attribute(
"markerWidth",
"3")),
751 parseUnitY(e.attribute(
"markerHeight",
"3"))));
753 const QString orientation = e.attribute(
"orient",
"0");
755 if (orientation ==
"auto") {
756 marker->setAutoOrientation(
true);
758 marker->setExplicitOrientation(
parseAngular(orientation));
770 if (!markerShape)
return false;
772 marker->setShapes({markerShape});
774 m_markers.insert(
id, QExplicitlySharedDataPointer<KoMarker>(marker.take()));
781 const QString
id = e.attribute(
"id");
783 if (
id.isEmpty())
return false;
785 QScopedPointer<KoSvgSymbol> svgSymbol(
new KoSvgSymbol());
792 QString title = e.firstChildElement(
"title").toElement().text();
794 QScopedPointer<KoShape> symbolShape(
parseGroup(e));
798 if (!symbolShape)
return false;
800 svgSymbol->shape = symbolShape.take();
801 svgSymbol->title = title;
803 if (title.isEmpty()) svgSymbol->title = id;
805 if (svgSymbol->shape->boundingRect() == QRectF(0.0, 0.0, 0.0, 0.0)) {
806 debugFlake <<
"Symbol" <<
id <<
"seems to be empty, discarding";
824 const QString titleTag =
"title";
825 const QString descriptionTag =
"desc";
826 QDomElement title = e.firstChildElement(titleTag);
827 if (!title.isNull()) {
829 if (!text.data().isEmpty()) {
833 QDomElement description = e.firstChildElement(descriptionTag);
834 if (!description.isNull()) {
836 if (!text.data().isEmpty()) {
846 const QString
id = e.attribute(
"id");
847 if (
id.isEmpty())
return false;
861 if (!clipShape)
return false;
873 const QString
id = e.attribute(
"id");
874 if (
id.isEmpty())
return false;
895 clipMask->setMaskRect(maskRect);
907 if (!clipShape)
return false;
908 clipMask->setShapes({clipShape});
1005 QTransform *transform)
1007 QGradient *resultGradient = 0;
1014 if (gradient->
gradient()->type() == QGradient::LinearGradient) {
1020 const QRectF boundingRect = shape->
outline().boundingRect();
1021 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
1022 boundingRect.x(), boundingRect.y());
1024 const QTransform relativeToUser =
1027 const QTransform userToRelative = relativeToUser.inverted();
1029 const QLinearGradient *o =
static_cast<const QLinearGradient*
>(gradient->
gradient());
1030 QLinearGradient *g =
new QLinearGradient();
1031 g->setStart(userToRelative.map(o->start()));
1032 g->setFinalStop(userToRelative.map(o->finalStop()));
1033 g->setCoordinateMode(QGradient::ObjectBoundingMode);
1034 g->setStops(o->stops());
1035 g->setSpread(o->spread());
1038 *transform = relativeToUser * gradient->
transform() * userToRelative;
1040 }
else if (gradient->
gradient()->type() == QGradient::RadialGradient) {
1047 if (outlineRect.isEmpty())
return resultGradient;
1055 QRadialGradient *rgradient =
static_cast<QRadialGradient*
>(resultGradient);
1058 const QRectF uniformSize(outlineRect.topLeft(), QSizeF(maxDimension, maxDimension));
1060 const QTransform uniformizeTransform =
1061 QTransform::fromTranslate(-outlineRect.x(), -outlineRect.y()) *
1062 QTransform::fromScale(maxDimension / shape->
outlineRect().width(),
1064 QTransform::fromTranslate(outlineRect.x(), outlineRect.y());
1066 const QPointF centerLocal = transform->map(rgradient->center());
1067 const QPointF focalLocal = transform->map(rgradient->focalPoint());
1072 rgradient->setCenter(centerOBB);
1073 rgradient->setFocalPoint(focalOBB);
1078 rgradient->setCenterRadius(centerRadiusOBB);
1079 rgradient->setFocalRadius(focalRadiusOBB);
1081 rgradient->setCoordinateMode(QGradient::ObjectBoundingMode);
1084 *transform = uniformizeTransform * gradient->
transform();
1090 resultGradient->setInterpolationMode(QGradient::ComponentInterpolation);
1092 return resultGradient;
1105 const QRectF boundingRect = shape->
outline().boundingRect();
1106 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
1107 boundingRect.x(), boundingRect.y());
1121 const QTransform translationOffset = QTransform::fromTranslate(-shapeglobal.dx(), -shapeglobal.dy());
1130 return resultGradient;
1147 QTransform transform;
1161 bg->setTransform(transform);
1187 const double lineWidth = srcStroke->lineWidth();
1191 if (dashes.count() && lineWidth > 0.0) {
1192 const double dashOffset = srcStroke->dashOffset();
1195 for (
int i = 0; i < dashes.count(); ++i) {
1196 dashes[i] /= lineWidth;
1199 dstStroke->setLineStyle(Qt::CustomDashLine, dashes);
1200 dstStroke->setDashOffset(dashOffset / lineWidth);
1214 stroke->setLineWidth(0.0);
1215 const QColor color = Qt::transparent;
1216 stroke->setColor(color);
1226 QTransform transform;
1229 QBrush brush = *result;
1231 brush.setTransform(transform);
1234 stroke->setLineBrush(brush);
1279 Q_FOREACH(
const QString
p, paintOrder) {
1282 }
else if (
p ==
"stroke") {
1284 }
else if (
p ==
"markers") {
1288 if (paintOrder.size() == 1 && order.isEmpty()) {
1291 if (order.size() == 1) {
1299 }
else if (order.size() > 1) {
1315 if (!clipPath || clipPath->
isEmpty())
1324 shapes.append(clonedShape);
1327 if (!shapeToOriginalUserCoordinates.isNull()) {
1328 const QTransform t =
1329 QTransform::fromTranslate(shapeToOriginalUserCoordinates.x(),
1330 shapeToOriginalUserCoordinates.y());
1354 if (!originalClipMask || originalClipMask->isEmpty())
return;
1356 KoClipMask *clipMask = originalClipMask->clone();
1365 QString href = e.attribute(
"xlink:href");
1369 QString key = href.mid(1);
1373 }
else if (deferredUseStore) {
1374 deferredUseStore->
add(&e, key);
1377 debugFlake <<
"WARNING: Did not find reference for svg 'use' element. Skipping. Id: "
1392 result =
parseGroup(e, referencedElement,
false);
1402 if (!group ||
shapes.isEmpty())
1420 const QString w = e.attribute(
"width");
1421 const QString h = e.attribute(
"height");
1423 qreal width = w.isEmpty() ? 666.0 :
parseUnitX(w);
1424 qreal height = h.isEmpty() ? 555.0 :
parseUnitY(h);
1426 if (w.isEmpty() || h.isEmpty()) {
1428 QTransform viewTransform_unused;
1429 QRectF fakeBoundingRect(0.0, 0.0, 1.0, 1.0);
1432 &viewRect, &viewTransform_unused)) {
1434 QSizeF estimatedSize = viewRect.size();
1436 if (estimatedSize.isValid()) {
1439 estimatedSize = QSizeF(width, width * estimatedSize.height() / estimatedSize.width());
1440 }
else if (!h.isEmpty()) {
1441 estimatedSize = QSizeF(height * estimatedSize.width() / estimatedSize.height(), height);
1444 width = estimatedSize.width();
1445 height = estimatedSize.height();
1450 QSizeF svgFragmentSize(QSizeF(width, height));
1453 *fragmentSize = svgFragmentSize;
1460 const qreal x =
parseUnit(e.attribute(
"x",
"0"));
1461 const qreal y =
parseUnit(e.attribute(
"y",
"0"));
1463 QTransform move = QTransform::fromTranslate(x, y);
1481 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1482 QDomElement b = n.toElement();
1486 if (b.tagName() ==
"title") {
1489 else if (b.tagName() ==
"desc") {
1492 else if (b.tagName() ==
"metadata") {
1514 QTransform viewTransform;
1517 &viewRect, &viewTransform)) {
1557 const QTransform shapeToOriginalUserCoordinates =
1559 coordinateSystemOnLoading;
1562 return QPointF(shapeToOriginalUserCoordinates.dx(), shapeToOriginalUserCoordinates.dy());
1567 if (createContext) {
1582 if (!overrideChildrenFrom.isNull()) {
1585 if (overrideChildrenFrom.tagName() ==
"symbol") {
1586 childShapes = {
parseGroup(overrideChildrenFrom)};
1595 applyId(b.attribute(
"id"), group);
1598 Q_FOREACH(
KoShape *shape, childShapes) {
1602 if (createContext) {
1615 if (createContext) {
1624 QDomNode firstChild = e.firstChild();
1625 return !firstChild.isNull() && firstChild == e.lastChild() && firstChild.isText() ?
1626 firstChild.toText() : QDomText();
1633 if (!dShape)
continue;
1640 if (e.hasAttribute(
"path")) {
1641 QDomElement
p = e.ownerDocument().createElement(
"path");
1642 p.setAttribute(
"d", e.attribute(
"path"));
1644 if (hideShapesFromDefs) {
1650 if (e.hasAttribute(
"href")) {
1651 pathId = e.attribute(
"href").remove(0, 1);
1652 }
else if (e.hasAttribute(
"xlink:href")) {
1653 pathId = e.attribute(
"xlink:href").remove(0, 1);
1655 if (!pathId.isNull()) {
1661 if(cloned &&
shapeInDefs(s) && hideShapesFromDefs) {
1677 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1678 QDomElement b = n.toElement();
1679 if (b.tagName() ==
"title" || b.tagName() ==
"desc")
continue;
1690 if (b.hasChildNodes()) {
1712 bool hideShapesFromDefs =
true;
1713 if (mergeIntoShape) {
1714 rootTextShape = mergeIntoShape;
1715 hideShapesFromDefs =
false;
1722 if (rootTextShape) {
1729 if (rootTextShape) {
1741 if (e.hasAttribute(
"krita:textVersion")) {
1745 debugFlake <<
"WARNING: \"krita:textVersion\" attribute appeared in non-root text shape";
1751 if (!mergeIntoShape) {
1758 static const KoID warning(
"warn_text_version_1",
1759 i18nc(
"warning while loading SVG text",
1760 "The document has vector text created "
1761 "in Krita 4.x. When you save the document, "
1762 "the text object will be converted into "
1763 "Krita 5 format that will no longer be "
1764 "compatible with Krita 4.x"));
1775 if (!mergeIntoShape) {
1781 applyId(e.attribute(
"id"), rootTextShape);
1789 if (!onlyTextChild.isNull()) {
1803 return rootTextShape;
1811 bool isSwitch = e.tagName() ==
"switch";
1815 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1816 QDomElement b = n.toElement();
1825 if (b.hasAttribute(
"requiredFeatures")) {
1828 if (b.hasAttribute(
"requiredExtensions")) {
1832 if (b.hasAttribute(
"systemLanguage")) {
1838 shapes.append(currentShapes);
1841 if (isSwitch && !currentShapes.isEmpty())
1859 if (deferredUseStore) {
1863 if (b.tagName() ==
"svg") {
1865 }
else if (b.tagName() ==
"g" || b.tagName() ==
"a") {
1868 }
else if (b.tagName() ==
"symbol") {
1870 }
else if (b.tagName() ==
"switch") {
1874 }
else if (b.tagName() ==
"defs") {
1875 if (b.childNodes().count() > 0) {
1886 }
else if (b.tagName() ==
"linearGradient" || b.tagName() ==
"radialGradient") {
1887 }
else if (b.tagName() ==
"pattern") {
1888 }
else if (b.tagName() ==
"filter") {
1890 }
else if (b.tagName() ==
"clipPath") {
1892 }
else if (b.tagName() ==
"mask") {
1894 }
else if (b.tagName() ==
"marker") {
1896 }
else if (b.tagName() ==
"style") {
1898 }
else if (b.tagName() ==
"text" || b.tagName() ==
"tspan" || b.tagName() ==
"textPath") {
1900 }
else if (b.tagName() ==
"rect" || b.tagName() ==
"ellipse" || b.tagName() ==
"circle" || b.tagName() ==
"line" || b.tagName() ==
"polyline"
1901 || b.tagName() ==
"polygon" || b.tagName() ==
"path" || b.tagName() ==
"image") {
1912 QTextStream stream(&
string);
1920 }
else if (b.tagName() ==
"use") {
1925 }
else if (b.tagName() ==
"color-profile") {
1945 if (element.tagName() ==
"line") {
1948 double x1 = element.attribute(
"x1").isEmpty() ? 0.0 :
parseUnitX(element.attribute(
"x1"));
1949 double y1 = element.attribute(
"y1").isEmpty() ? 0.0 :
parseUnitY(element.attribute(
"y1"));
1950 double x2 = element.attribute(
"x2").isEmpty() ? 0.0 :
parseUnitX(element.attribute(
"x2"));
1951 double y2 = element.attribute(
"y2").isEmpty() ? 0.0 :
parseUnitY(element.attribute(
"y2"));
1953 path->moveTo(QPointF(x1, y1));
1954 path->lineTo(QPointF(x2, y2));
1958 }
else if (element.tagName() ==
"polyline" || element.tagName() ==
"polygon") {
1965 for (QStringList::Iterator it = pointList.begin(); it != pointList.end(); ++it) {
1969 if (it == pointList.end())
1973 path->moveTo(point);
1976 path->lineTo(point);
1978 if (element.tagName() ==
"polygon")
1981 path->setPosition(path->normalize());
1985 }
else if (element.tagName() ==
"path") {
1991 loader.
parseSvg(element.attribute(
"d"),
true);
1992 path->setPosition(path->normalize());
1999 path->setSize(newSize);
2000 path->setPosition(newPosition);
2002 if (element.hasAttribute(
"sodipodi:nodetypes")) {
2003 path->loadNodeTypes(element.attribute(
"sodipodi:nodetypes"));
2025 applyId(b.attribute(
"id"), obj);
2052 applyId(b.attribute(
"id"), obj);
2095 if (!svgShape->
loadSvg(element, context)) {
2113 if (
value.isEmpty()) {
2116 unsigned int start =
value.indexOf(
'(') + 1;
2117 unsigned int end =
value.indexOf(
')', start);
2119 QString val =
value.mid(start, end - start);
2121 if (val.startsWith(
"evenodd,")) {
2122 start += QString(
"evenodd,").
size();
2123 fillRule =
"evenodd";
2124 }
else if (val.startsWith(
"nonzero,")) {
2125 start += QString(
"nonzero,").size();
2126 fillRule =
"nonzero";
2128 val =
value.mid(start, end - start);
2131 if (
value.startsWith(
"url(")) {
2132 start =
value.indexOf(
'#') + 1;
2141 if (cloned &&
shapeInDefs(s) && hideShapesFromDefs) {
2146 }
else if (
value.startsWith(
"circle(")) {
2147 el = e.ownerDocument().createElement(
"circle");
2150 if (params.contains(
"at")) {
2155 }
else if (
value.startsWith(
"ellipse(")) {
2156 el = e.ownerDocument().createElement(
"ellipse");
2160 if (params.contains(
"at")) {
2165 }
else if (
value.startsWith(
"polygon(")) {
2166 el = e.ownerDocument().createElement(
"polygon");
2169 bool xVal = points.size() % 2;
2176 el.setAttribute(
"points", points.join(
" "));
2177 }
else if (
value.startsWith(
"path(")) {
2178 el = e.ownerDocument().createElement(
"path");
2182 el.setAttribute(
"d",
value.mid(start, end - start));
2185 el.setAttribute(
"fill-rule", fillRule);
2198 Q_FOREACH(
const QString param, params) {
2201 shapeList.append(s);
2211 debugFlake <<
"Could not find factory for shape id" << shapeID;
2217 debugFlake <<
"Could not create Default shape for shape id" << shapeID;
2220 if (shape->
shapeId().isEmpty()) {
2243 if (existingShape) {
2244 debugFlake <<
"SVG contains nodes with duplicated id:" << id;
2248 const QString suffix = QString::number(QRandomGenerator::system()->bounded(0x10000000, 0x7FFFFFFF), 16);
2249 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.
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.
KoSvgTextProperties resolvedProperties() const
These are the text properties, completely resolved, ensuring that everything is inherited and the siz...
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)