22#include <QPainterPath>
23#include <QRandomGenerator>
45#include <QXmlSimpleReader>
68#include <QXmlStreamReader>
73 El(
const QDomElement* ue,
const QString& key) :
83 void add(
const QDomElement* useE,
const QString& key) {
92 const QString
id = b.attribute(
"id");
99 [&](
const El& e) ->
bool {return e.m_key != id;});
101 while (i !=
m_uses.end()) {
117 debugFlake <<
"WARNING: could not find path in <use xlink:href=\"#xxxxx\" expression. Losing data here. Key:"
128 : m_context(documentResourceManager)
129 , m_documentResourceManager(documentResourceManager)
159 reader.setNamespaceProcessing(
false);
160 if (!doc.setContent(&reader,
false, errorMsg, errorLine, errorColumn)) {
172 [
this](
const QString &name) {
174 possibleNames << name;
176 for (QString fileName : possibleNames) {
177 QFile file(fileName);
179 file.open(QIODevice::ReadOnly);
180 return file.readAll();
194 const qreal scale = 72.0 / pixelsPerInch;
195 const QTransform t = QTransform::fromScale(scale, scale);
243 if (e.tagName().contains(
"Gradient")) {
245 }
else if (e.tagName() ==
"meshgradient") {
260 if (e.tagName() ==
"pattern") {
279 if (e.childNodes().count() == 0) {
280 QString mhref = e.attribute(
"xlink:href").mid(1);
352 QString gradientId = e.attribute(
"id");
353 if (gradientId.isEmpty())
return 0;
361 if (e.hasAttribute(
"xlink:href")) {
363 QString href = e.attribute(
"xlink:href").mid(1);
365 if (!href.isEmpty()) {
374 const QGradientStops defaultStops = gradHelper.
gradient()->stops();
376 if (e.attribute(
"gradientUnits") ==
"userSpaceOnUse") {
383 if (e.tagName() ==
"linearGradient") {
384 QLinearGradient *g =
new QLinearGradient();
386 g->setCoordinateMode(QGradient::ObjectBoundingMode);
392 g->setStart(QPointF(
parseUnitX(e.attribute(
"x1")),
394 g->setFinalStop(QPointF(
parseUnitX(e.attribute(
"x2")),
399 }
else if (e.tagName() ==
"radialGradient") {
400 QRadialGradient *g =
new QRadialGradient();
402 g->setCoordinateMode(QGradient::ObjectBoundingMode);
409 g->setCenter(QPointF(
parseUnitX(e.attribute(
"cx")),
411 g->setFocalPoint(QPointF(
parseUnitX(e.attribute(
"fx")),
417 debugFlake <<
"WARNING: Failed to parse gradient with tag" << e.tagName();
421 QGradient::Spread spreadMethod = QGradient::PadSpread;
422 QString spreadMethodStr = e.attribute(
"spreadMethod");
423 if (!spreadMethodStr.isEmpty()) {
424 if (spreadMethodStr ==
"reflect") {
425 spreadMethod = QGradient::ReflectSpread;
426 }
else if (spreadMethodStr ==
"repeat") {
427 spreadMethod = QGradient::RepeatSpread;
436 if (e.hasAttribute(
"gradientTransform")) {
453 QString gradientId = e.attribute(
"id");
462 if (e.hasAttribute(
"xlink:href")) {
464 QString href = e.attribute(
"xlink:href").mid(1);
466 if (!href.isEmpty()) {
475 if (e.attribute(
"gradientUnits") ==
"userSpaceOnUse") {
479 if (e.hasAttribute(
"transform")) {
486 QString type = e.attribute(
"type");
488 if (!type.isEmpty() && type ==
"bicubic") {
493 for (
int i = 0; i < e.childNodes().size(); ++i) {
494 QDomNode node = e.childNodes().at(i);
496 if (node.nodeName() ==
"meshrow") {
500 startingNode.
point = QPointF(
503 startingNode.
color = QColor();
507 g->getMeshArray()->newRow();
508 for (
int j = 0; j < node.childNodes().size() ; ++j) {
509 QDomNode meshpatchNode = node.childNodes().at(j);
511 if (meshpatchNode.nodeName() ==
"meshpatch") {
515 }
else if (icols != 0) {
522 if (!g->getMeshArray()->addPatch(rawStops, startingNode.
point)) {
523 debugFlake <<
"WARNING: Failed to create meshpatch";
537#define forEachElement( elem, parent ) \
538 for ( QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
539 if ( ( elem = _node.toElement() ).isNull() ) {} else
547 if (!gc)
return rawstops;
549 QDomElement e = meshpatchNode.toElement();
557 QString pathStr = stop.attribute(
"path");
559 rawstops.append({pathStr, color});
565inline QPointF
bakeShapeOffset(
const QTransform &patternTransform,
const QPointF &shapeOffset)
569 QTransform::fromTranslate(-shapeOffset.x(), -shapeOffset.y()) *
570 patternTransform.inverted();
573 return QPointF(result.dx(), result.dy());
587 if (!gc)
return pattHelper;
589 const QString patternId = e.attribute(
"id");
590 if (patternId.isEmpty())
return pattHelper;
594 if (e.hasAttribute(
"xlink:href")) {
596 QString href = e.attribute(
"xlink:href").mid(1);
598 if (!href.isEmpty() &&href != patternId) {
607 pattHelper->setReferenceCoordinates(
609 pattHelper->referenceCoordinates()));
611 pattHelper->setContentCoordinates(
613 pattHelper->contentCoordinates()));
615 if (e.hasAttribute(
"patternTransform")) {
618 pattHelper->setPatternTransform(
p.transform());
623 QRectF referenceRect(
629 pattHelper->setReferenceRect(referenceRect);
631 QRectF referenceRect(
637 pattHelper->setReferenceRect(referenceRect);
651 const QTransform shapeOffsetTransform = dstShapeTransform * gc->
matrix.inverted();
653 const QPointF
extraShapeOffset(shapeOffsetTransform.dx(), shapeOffsetTransform.dy());
660 gc->
matrix = QTransform();
662 const QRectF boundingRect = shape->
outline().boundingRect();
663 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
664 boundingRect.x(), boundingRect.y());
674 if (e.hasAttribute(
"viewBox")) {
677 relativeToShape.mapRect(pattHelper->referenceRect()) :
678 pattHelper->referenceRect();
681 pattHelper->setContentCoordinates(pattHelper->referenceCoordinates());
697 Q_FOREACH (
KoShape *shape, patternShapes) {
709 QRectF ref = pattHelper->referenceRect();
710 ref.translate(offset);
711 pattHelper->setReferenceRect(ref);
717 if (!patternShapes.isEmpty()) {
718 pattHelper->setShapes(patternShapes);
730 if (!referencedBy.isNull())
736 if (e.hasAttribute(
"xlink:href")) {
737 QString href = e.attribute(
"xlink:href").mid(1);
738 if (! href.isEmpty()) {
748 if (b.attribute(
"filterUnits") ==
"userSpaceOnUse")
750 if (b.attribute(
"primitiveUnits") ==
"objectBoundingBox")
768 m_filters.insert(b.attribute(
"id"), filter);
775 const QString
id = e.attribute(
"id");
776 if (
id.isEmpty())
return false;
778 QScopedPointer<KoMarker> marker(
new KoMarker());
779 marker->setCoordinateSystem(
782 marker->setReferencePoint(QPointF(
parseUnitX(e.attribute(
"refX")),
785 marker->setReferenceSize(QSizeF(
parseUnitX(e.attribute(
"markerWidth",
"3")),
786 parseUnitY(e.attribute(
"markerHeight",
"3"))));
788 const QString orientation = e.attribute(
"orient",
"0");
790 if (orientation ==
"auto") {
791 marker->setAutoOrientation(
true);
793 marker->setExplicitOrientation(
parseAngular(orientation));
805 if (!markerShape)
return false;
807 marker->setShapes({markerShape});
809 m_markers.insert(
id, QExplicitlySharedDataPointer<KoMarker>(marker.take()));
816 const QString
id = e.attribute(
"id");
818 if (
id.isEmpty())
return false;
820 QScopedPointer<KoSvgSymbol> svgSymbol(
new KoSvgSymbol());
827 QString title = e.firstChildElement(
"title").toElement().text();
829 QScopedPointer<KoShape> symbolShape(
parseGroup(e));
833 if (!symbolShape)
return false;
835 svgSymbol->shape = symbolShape.take();
836 svgSymbol->title = title;
838 if (title.isEmpty()) svgSymbol->title = id;
840 if (svgSymbol->shape->boundingRect() == QRectF(0.0, 0.0, 0.0, 0.0)) {
841 debugFlake <<
"Symbol" <<
id <<
"seems to be empty, discarding";
859 const QString titleTag =
"title";
860 const QString descriptionTag =
"desc";
861 QDomElement title = e.firstChildElement(titleTag);
862 if (!title.isNull()) {
864 if (!text.data().isEmpty()) {
868 QDomElement description = e.firstChildElement(descriptionTag);
869 if (!description.isNull()) {
871 if (!text.data().isEmpty()) {
881 const QString
id = e.attribute(
"id");
882 if (
id.isEmpty())
return false;
896 if (!clipShape)
return false;
908 const QString
id = e.attribute(
"id");
909 if (
id.isEmpty())
return false;
930 clipMask->setMaskRect(maskRect);
942 if (!clipShape)
return false;
943 clipMask->setShapes({clipShape});
1042 QTransform *transform)
1044 QGradient *resultGradient = 0;
1051 if (gradient->
gradient()->type() == QGradient::LinearGradient) {
1057 const QRectF boundingRect = shape->
outline().boundingRect();
1058 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
1059 boundingRect.x(), boundingRect.y());
1061 const QTransform relativeToUser =
1064 const QTransform userToRelative = relativeToUser.inverted();
1066 const QLinearGradient *o =
static_cast<const QLinearGradient*
>(gradient->
gradient());
1067 QLinearGradient *g =
new QLinearGradient();
1068 g->setStart(userToRelative.map(o->start()));
1069 g->setFinalStop(userToRelative.map(o->finalStop()));
1070 g->setCoordinateMode(QGradient::ObjectBoundingMode);
1071 g->setStops(o->stops());
1072 g->setSpread(o->spread());
1075 *transform = relativeToUser * gradient->
transform() * userToRelative;
1077 }
else if (gradient->
gradient()->type() == QGradient::RadialGradient) {
1084 if (outlineRect.isEmpty())
return resultGradient;
1092 QRadialGradient *rgradient =
static_cast<QRadialGradient*
>(resultGradient);
1095 const QRectF uniformSize(outlineRect.topLeft(), QSizeF(maxDimension, maxDimension));
1097 const QTransform uniformizeTransform =
1098 QTransform::fromTranslate(-outlineRect.x(), -outlineRect.y()) *
1099 QTransform::fromScale(maxDimension / shape->
outlineRect().width(),
1101 QTransform::fromTranslate(outlineRect.x(), outlineRect.y());
1103 const QPointF centerLocal = transform->map(rgradient->center());
1104 const QPointF focalLocal = transform->map(rgradient->focalPoint());
1109 rgradient->setCenter(centerOBB);
1110 rgradient->setFocalPoint(focalOBB);
1115 rgradient->setCenterRadius(centerRadiusOBB);
1116 rgradient->setFocalRadius(focalRadiusOBB);
1118 rgradient->setCoordinateMode(QGradient::ObjectBoundingMode);
1121 *transform = uniformizeTransform * gradient->
transform();
1127 resultGradient->setInterpolationMode(QGradient::ComponentInterpolation);
1129 return resultGradient;
1142 const QRectF boundingRect = shape->
outline().boundingRect();
1143 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
1144 boundingRect.x(), boundingRect.y());
1158 const QTransform translationOffset = QTransform::fromTranslate(-shapeglobal.dx(), -shapeglobal.dy());
1167 return resultGradient;
1184 QTransform transform;
1198 bg->setTransform(transform);
1224 const double lineWidth = srcStroke->lineWidth();
1228 if (dashes.count() && lineWidth > 0.0) {
1229 const double dashOffset = srcStroke->dashOffset();
1232 for (
int i = 0; i < dashes.count(); ++i) {
1233 dashes[i] /= lineWidth;
1236 dstStroke->setLineStyle(Qt::CustomDashLine, dashes);
1237 dstStroke->setDashOffset(dashOffset / lineWidth);
1251 stroke->setLineWidth(0.0);
1252 const QColor color = Qt::transparent;
1253 stroke->setColor(color);
1263 QTransform transform;
1266 QBrush brush = *result;
1268 brush.setTransform(transform);
1271 stroke->setLineBrush(brush);
1299 QDomElement content = filter->
content();
1307 QRectF filterRegion(filter->
position(bound), filter->
size(bound));
1310 QRectF objectFilterRegion;
1324 QSet<QString> stdInputs;
1325 stdInputs <<
"SourceGraphic" <<
"SourceAlpha";
1326 stdInputs <<
"BackgroundImage" <<
"BackgroundAlpha";
1327 stdInputs <<
"FillPaint" <<
"StrokePaint";
1329 QMap<QString, KoFilterEffect*> inputs;
1332 for (QDomNode n = content.firstChild(); !n.isNull(); n = n.nextSibling()) {
1333 QDomElement primitive = n.toElement();
1335 if (!filterEffect) {
1336 debugFlake <<
"filter effect" << primitive.tagName() <<
"is not implemented yet";
1340 const QString input = primitive.attribute(
"in");
1341 if (!input.isEmpty()) {
1344 const QString output = primitive.attribute(
"result");
1345 if (!output.isEmpty()) {
1352 const QString xa = primitive.attribute(
"x");
1353 const QString ya = primitive.attribute(
"y");
1354 const QString wa = primitive.attribute(
"width");
1355 const QString ha = primitive.attribute(
"height");
1357 if (xa.isEmpty() || ya.isEmpty() || wa.isEmpty() || ha.isEmpty()) {
1358 bool hasStdInput =
false;
1359 bool isFirstEffect = filterStack == 0;
1361 Q_FOREACH (
const QString &input, filterEffect->
inputs()) {
1362 if ((isFirstEffect && input.isEmpty()) || stdInputs.contains(input)) {
1367 if (hasStdInput || primitive.tagName() ==
"feImage") {
1369 subRegion.setTopLeft(QPointF(0, 0));
1370 subRegion.setSize(QSizeF(1, 1));
1373 Q_FOREACH (
const QString &input, filterEffect->
inputs()) {
1374 if (!inputs.contains(input))
1397 subRegion = QRectF(QPointF(x, y), QSizeF(w, h));
1406 inputs[filterEffect->
output()] = filterEffect;
1444 Q_FOREACH(
const QString
p, paintOrder) {
1447 }
else if (
p ==
"stroke") {
1449 }
else if (
p ==
"markers") {
1453 if (paintOrder.size() == 1 && order.isEmpty()) {
1456 if (order.size() == 1) {
1464 }
else if (order.size() > 1) {
1480 if (!clipPath || clipPath->
isEmpty())
1489 shapes.append(clonedShape);
1492 if (!shapeToOriginalUserCoordinates.isNull()) {
1493 const QTransform t =
1494 QTransform::fromTranslate(shapeToOriginalUserCoordinates.x(),
1495 shapeToOriginalUserCoordinates.y());
1519 if (!originalClipMask || originalClipMask->isEmpty())
return;
1521 KoClipMask *clipMask = originalClipMask->clone();
1530 QString href = e.attribute(
"xlink:href");
1534 QString key = href.mid(1);
1538 }
else if (deferredUseStore) {
1539 deferredUseStore->
add(&e, key);
1542 debugFlake <<
"WARNING: Did not find reference for svg 'use' element. Skipping. Id: "
1557 result =
parseGroup(e, referencedElement,
false);
1567 if (!group ||
shapes.isEmpty())
1585 const QString w = e.attribute(
"width");
1586 const QString h = e.attribute(
"height");
1588 qreal width = w.isEmpty() ? 666.0 :
parseUnitX(w);
1589 qreal height = h.isEmpty() ? 555.0 :
parseUnitY(h);
1591 if (w.isEmpty() || h.isEmpty()) {
1593 QTransform viewTransform_unused;
1594 QRectF fakeBoundingRect(0.0, 0.0, 1.0, 1.0);
1597 &viewRect, &viewTransform_unused)) {
1599 QSizeF estimatedSize = viewRect.size();
1601 if (estimatedSize.isValid()) {
1604 estimatedSize = QSizeF(width, width * estimatedSize.height() / estimatedSize.width());
1605 }
else if (!h.isEmpty()) {
1606 estimatedSize = QSizeF(height * estimatedSize.width() / estimatedSize.height(), height);
1609 width = estimatedSize.width();
1610 height = estimatedSize.height();
1615 QSizeF svgFragmentSize(QSizeF(width, height));
1618 *fragmentSize = svgFragmentSize;
1625 const qreal x =
parseUnit(e.attribute(
"x",
"0"));
1626 const qreal y =
parseUnit(e.attribute(
"y",
"0"));
1628 QTransform move = QTransform::fromTranslate(x, y);
1646 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1647 QDomElement b = n.toElement();
1651 if (b.tagName() ==
"title") {
1654 else if (b.tagName() ==
"desc") {
1657 else if (b.tagName() ==
"metadata") {
1679 QTransform viewTransform;
1682 &viewRect, &viewTransform)) {
1722 const QTransform shapeToOriginalUserCoordinates =
1724 coordinateSystemOnLoading;
1727 return QPointF(shapeToOriginalUserCoordinates.dx(), shapeToOriginalUserCoordinates.dy());
1732 if (createContext) {
1747 if (!overrideChildrenFrom.isNull()) {
1750 if (overrideChildrenFrom.tagName() ==
"symbol") {
1751 childShapes = {
parseGroup(overrideChildrenFrom)};
1760 applyId(b.attribute(
"id"), group);
1768 if (createContext) {
1777 QDomNode firstChild = e.firstChild();
1778 return !firstChild.isNull() && firstChild == e.lastChild() && firstChild.isText() ?
1779 firstChild.toText() : QDomText();
1783 if (e.hasAttribute(
"path")) {
1784 QDomElement
p = e.ownerDocument().createElement(
"path");
1785 p.setAttribute(
"d", e.attribute(
"path"));
1790 if (e.hasAttribute(
"href")) {
1791 pathId = e.attribute(
"href").remove(0, 1);
1792 }
else if (e.hasAttribute(
"xlink:href")) {
1793 pathId = e.attribute(
"xlink:href").remove(0, 1);
1795 if (!pathId.isNull()) {
1814 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1815 QDomElement b = n.toElement();
1816 if (b.tagName() ==
"title" || b.tagName() ==
"desc")
continue;
1827 if (b.hasChildNodes()) {
1849 if (mergeIntoShape) {
1850 rootTextShape = mergeIntoShape;
1857 if (rootTextShape) {
1864 if (rootTextShape) {
1876 if (e.hasAttribute(
"krita:textVersion")) {
1880 debugFlake <<
"WARNING: \"krita:textVersion\" attribute appeared in non-root text shape";
1886 if (!mergeIntoShape) {
1894 static const KoID warning(
"warn_text_version_1",
1895 i18nc(
"warning while loading SVG text",
1896 "The document has vector text created "
1897 "in Krita 4.x. When you save the document, "
1898 "the text object will be converted into "
1899 "Krita 5 format that will no longer be "
1900 "compatible with Krita 4.x"));
1911 if (!mergeIntoShape) {
1917 applyId(e.attribute(
"id"), rootTextShape);
1925 if (!onlyTextChild.isNull()) {
1941 return rootTextShape;
1949 bool isSwitch = e.tagName() ==
"switch";
1953 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
1954 QDomElement b = n.toElement();
1963 if (b.hasAttribute(
"requiredFeatures")) {
1966 if (b.hasAttribute(
"requiredExtensions")) {
1970 if (b.hasAttribute(
"systemLanguage")) {
1976 shapes.append(currentShapes);
1979 if (isSwitch && !currentShapes.isEmpty())
1997 if (deferredUseStore) {
2001 if (b.tagName() ==
"svg") {
2003 }
else if (b.tagName() ==
"g" || b.tagName() ==
"a") {
2006 }
else if (b.tagName() ==
"symbol") {
2008 }
else if (b.tagName() ==
"switch") {
2012 }
else if (b.tagName() ==
"defs") {
2013 if (b.childNodes().count() > 0) {
2024 }
else if (b.tagName() ==
"linearGradient" || b.tagName() ==
"radialGradient") {
2025 }
else if (b.tagName() ==
"pattern") {
2026 }
else if (b.tagName() ==
"filter") {
2028 }
else if (b.tagName() ==
"clipPath") {
2030 }
else if (b.tagName() ==
"mask") {
2032 }
else if (b.tagName() ==
"marker") {
2034 }
else if (b.tagName() ==
"style") {
2036 }
else if (b.tagName() ==
"text" || b.tagName() ==
"tspan" || b.tagName() ==
"textPath") {
2038 }
else if (b.tagName() ==
"rect" || b.tagName() ==
"ellipse" || b.tagName() ==
"circle" || b.tagName() ==
"line" || b.tagName() ==
"polyline"
2039 || b.tagName() ==
"polygon" || b.tagName() ==
"path" || b.tagName() ==
"image") {
2050 QTextStream stream(&
string);
2058 }
else if (b.tagName() ==
"use") {
2063 }
else if (b.tagName() ==
"color-profile") {
2083 if (element.tagName() ==
"line") {
2086 double x1 = element.attribute(
"x1").isEmpty() ? 0.0 :
parseUnitX(element.attribute(
"x1"));
2087 double y1 = element.attribute(
"y1").isEmpty() ? 0.0 :
parseUnitY(element.attribute(
"y1"));
2088 double x2 = element.attribute(
"x2").isEmpty() ? 0.0 :
parseUnitX(element.attribute(
"x2"));
2089 double y2 = element.attribute(
"y2").isEmpty() ? 0.0 :
parseUnitY(element.attribute(
"y2"));
2091 path->moveTo(QPointF(x1, y1));
2092 path->lineTo(QPointF(x2, y2));
2096 }
else if (element.tagName() ==
"polyline" || element.tagName() ==
"polygon") {
2103 for (QStringList::Iterator it = pointList.begin(); it != pointList.end(); ++it) {
2107 if (it == pointList.end())
2111 path->moveTo(point);
2114 path->lineTo(point);
2116 if (element.tagName() ==
"polygon")
2119 path->setPosition(path->normalize());
2123 }
else if (element.tagName() ==
"path") {
2129 loader.
parseSvg(element.attribute(
"d"),
true);
2130 path->setPosition(path->normalize());
2137 path->setSize(newSize);
2138 path->setPosition(newPosition);
2140 if (element.hasAttribute(
"sodipodi:nodetypes")) {
2141 path->loadNodeTypes(element.attribute(
"sodipodi:nodetypes"));
2163 applyId(b.attribute(
"id"), obj);
2190 applyId(b.attribute(
"id"), obj);
2233 if (!svgShape->
loadSvg(element, context)) {
2251 if (
value.isEmpty()) {
2254 unsigned int start =
value.indexOf(
'(') + 1;
2255 unsigned int end =
value.indexOf(
')', start);
2257 QString val =
value.mid(start, end - start);
2259 if (val.startsWith(
"evenodd,")) {
2260 start += QString(
"evenodd,").
size();
2261 fillRule =
"evenodd";
2262 }
else if (val.startsWith(
"nonzero,")) {
2263 start += QString(
"nonzero,").size();
2264 fillRule =
"nonzero";
2266 val =
value.mid(start, end - start);
2269 if (
value.startsWith(
"url(")) {
2270 start =
value.indexOf(
'#') + 1;
2278 }
else if (
value.startsWith(
"circle(")) {
2279 el = e.ownerDocument().createElement(
"circle");
2282 if (params.contains(
"at")) {
2287 }
else if (
value.startsWith(
"ellipse(")) {
2288 el = e.ownerDocument().createElement(
"ellipse");
2292 if (params.contains(
"at")) {
2297 }
else if (
value.startsWith(
"polygon(")) {
2298 el = e.ownerDocument().createElement(
"polygon");
2301 bool xVal = points.size() % 2;
2308 el.setAttribute(
"points", points.join(
" "));
2309 }
else if (
value.startsWith(
"path(")) {
2310 el = e.ownerDocument().createElement(
"path");
2314 el.setAttribute(
"d",
value.mid(start, end - start));
2317 el.setAttribute(
"fill-rule", fillRule);
2328 Q_FOREACH(
const QString param, params) {
2331 shapeList.append(s);
2341 debugFlake <<
"Could not find factory for shape id" << shapeID;
2347 debugFlake <<
"Could not create Default shape for shape id" << shapeID;
2350 if (shape->
shapeId().isEmpty()) {
2373 if (existingShape) {
2374 debugFlake <<
"SVG contains nodes with duplicated id:" << id;
2378 const QString suffix = QString::number(QRandomGenerator::system()->bounded(0x10000000, 0x7FFFFFFF), 16);
2379 const QString newName =
id +
'_' + suffix;
float value(const T *src, size_t ch)
QSharedPointer< KoShapeStrokeModel > KoShapeStrokeModelSP
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.
This class provides a loading context for filter effects.
void enableFilterPrimitiveUnitsConversion(bool enable)
Enables conversion of filter primitive units.
void enableFilterUnitsConversion(bool enable)
Enables conversion of filter units.
void setShapeBoundingBox(const QRectF &shapeBound)
KoFilterEffect * createFilterEffectFromXml(const QDomElement &element, const KoFilterEffectLoadingContext &context)
static KoFilterEffectRegistry * instance()
This class manages a stack of filter effects.
void setClipRect(const QRectF &clipRect)
Sets the clipping rectangle used for this filter in bounding box units.
void appendFilterEffect(KoFilterEffect *filter)
void setOutput(const QString &output)
void setFilterRect(const QRectF &filterRect)
Sets the region the filter is applied to in bounding box units.
void setInput(int index, const QString &input)
Sets an existing input to a new value.
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
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
void setFilterEffectStack(KoFilterEffectStack *filterEffectStack)
Sets the new filter effect stack, removing the old one.
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)
QPointF position() const
Get the position of the shape in pt.
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)
void setPosition(const QPointF &position)
Sets filter position.
KoFlake::CoordinateSystem filterUnits() const
Returns the filter units type.
void setContent(const QDomElement &content)
Sets the dom element containing the filter.
QSizeF size(const QRectF &objectBound) const
Returns filter size (objectBound is used when filterUnits == ObjectBoundingBox)
void setFilterUnits(KoFlake::CoordinateSystem filterUnits)
Set the filter units type.
QDomElement content() const
Return the filer element.
void setPrimitiveUnits(KoFlake::CoordinateSystem primitiveUnits)
Set the filter primitive units type.
QPointF position(const QRectF &objectBound) const
Returns filter position (objectBound is used when filterUnits == ObjectBoundingBox)
KoFlake::CoordinateSystem primitiveUnits() const
Returns the filter primitive units type.
void setSize(const QSizeF &size)
Sets filter size.
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.
QTransform viewboxTransform
view box transformation
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 filterId
the current filter id
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 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
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)
QMap< QString, SvgFilterHelper > m_filters
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
SvgFilterHelper * findFilter(const QString &id, const QString &href=QString())
find filter with given id in filter map
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.
void applyFilter(KoShape *shape)
Applies the current filter to the object.
std::function< QByteArray(const QString &) FileFetcherFunc)
KoShape * parseUse(const QDomElement &, DeferredUseStore *deferredUseStore)
Parses a use element, returning a list of child shapes.
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
KoShape * getTextPath(const QDomElement &e)
Get the path for the gives textPath element.
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)
SvgLoadingContext m_context
KoShape * createShapeFromCSS(const QDomElement e, const QString value, SvgLoadingContext &context)
Creates a shape from a CSS shapes definition.
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
QList< KoShape * > createListOfShapesFromCSS(const QDomElement e, const QString value, SvgLoadingContext &context)
Create a list of shapes from a CSS shapes definition with potentially multiple shapes.
void setFillStrokeInheritByDefault(const bool enable)
bool parseFilter(const QDomElement &, const QDomElement &referencedBy=QDomElement())
Parses a filter element.
KoShape * resolveUse(const QDomElement &e, const QString &key)
void parseDefsElement(const QDomElement &e)
void parseTextChildren(const QDomElement &e, KoSvgTextLoader &textLoader)
parse children of a <text > element into the root shape.
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 QPointF userSpaceToObject(const QPointF &position, const QRectF &objectBound)
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)