KDEGames

kgamesvgdocument.cpp
1 /*
2  SPDX-FileCopyrightText: 2007 Mark A. Taff <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-only
5 */
6 
7 #include "kgamesvgdocument.h"
8 #include "kgamesvgdocument_p.h"
9 
10 // KF
11 #include <KCompressionDevice>
12 // Qt
13 #include <QBuffer>
14 #include <QFile>
15 #include <QString>
16 #include <QDomElement>
17 #include <QDomNode>
18 #include <QDebug>
19 // Std
20 #include <cmath>
21 
22 //
23 // Public
24 //
25 
26 /**
27  * @brief A class holding private members for KGameSvgDocument
28  *
29  * @see KGameSvgDocument
30  * @author Mark A. Taff <[email protected]>
31  * @version 0.1
32  */
33 class KGameSvgDocumentPrivate
34 {
35  public:
36 
37  /**
38  * @brief Instantiates a KGameSvgDocumentPrivate object
39  */
40  KGameSvgDocumentPrivate()
41  {}
42 
43  ~KGameSvgDocumentPrivate()
44  {}
45 
46  /**
47  * @brief Performs a preorder traversal of the DOM tree to find element matching @c attributeName & @c attributeValue
48  *
49  * @param attributeName The name of the attribute to find
50  * @param attributeValue The value of the @p attributeName attribute to find
51  * @param node The node to start the traversal from.
52  * @returns the node with id of @c elementId. If no node has that id, returns a null node.
53  */
54  QDomNode findElementById(const QString& attributeName, const QString& attributeValue, const QDomNode& node);
55 
56  /**
57  * @brief Returns the current element
58  * @returns The current element
59  */
60  QDomElement currentElement() const;
61 
62  /**
63  * @brief Sets the current element
64  *
65  * @returns nothing
66  */
67  void setCurrentElement();
68 
69  /**
70  * @brief Returns whether the original style attribute has a trailing semicolon
71  * @returns whether the original style attribute has a trailing semicolon
72  */
73  bool styleHasTrailingSemicolon() const;
74 
75  /**
76  * @brief Sets whether the original style attribute has a trailing semicolon
77  *
78  * @param hasSemicolon whether the original style attribute has a trailing semicolon
79  * @returns nothing
80  */
81  void setStyleHasTrailingSemicolon(bool hasSemicolon);
82 
83  /**
84  * @brief The last node found by elementById, or a null node if not found.
85  */
86  QDomNode m_currentNode;
87 
88  /**
89  * @brief The current node turned into an element.
90  */
91  QDomElement m_currentElement;
92 
93  /**
94  * @brief The order Inkscape write properties in the style attribute of an element.
95  *
96  * Inkscape order is defined as:
97  * "fill", "fill-opacity", "fill-rule", "stroke", "stroke-width", "stroke-linecap",
98  * "stroke-linejoin", "stroke-miterlimit", "stroke-dasharray", "stroke-opacity"
99  */
100  QStringList m_inkscapeOrder;
101 
102  /**
103  * @brief The xml that must be prepended to a node to make it a valid svg document
104  *
105  * Defined as: <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg>
106  */
107  static const QString SVG_XML_PREPEND;
108 
109  /**
110  * @brief The xml that must be appended to a node to make it a valid svg document
111  *
112  * Defined as: </svg>
113  */
114  static const QString SVG_XML_APPEND;
115 
116  /**
117  * @brief The filename of the SVG file to open.
118  */
119  QString m_svgFilename;
120 
121  /**
122  * @brief Whether the style attribute has a trailing semicolon
123  */
124  bool m_hasSemicolon;
125 
126 
127 };
128 
129 const QString KGameSvgDocumentPrivate::SVG_XML_PREPEND = QStringLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><svg>");
130 const QString KGameSvgDocumentPrivate::SVG_XML_APPEND = QStringLiteral("</svg>");
131 
133  : QDomDocument(), d(new KGameSvgDocumentPrivate)
134 {}
135 
137  : QDomDocument(), d(new KGameSvgDocumentPrivate(*doc.d))
138 {
139 }
140 
142 
144 {
146  *d = *doc.d;
147  return *this;
148 }
149 
150 QDomNode KGameSvgDocument::elementByUniqueAttributeValue(const QString& attributeName, const QString& attributeValue)
151 {
152  /* DOM is always "live", so there maybe a new root node. We always have to ask for the
153  * root node instead of keeping a pointer to it.
154  */
155  QDomElement docElem = documentElement();
156  QDomNode n = docElem.firstChild();
157 
158  QDomNode node = d->findElementById(attributeName, attributeValue, n);
159  setCurrentNode(node);
160  return node;
161 }
162 
164 {
165  return elementByUniqueAttributeValue(QStringLiteral( "id" ), attributeValue);
166 }
167 
169 {
170  if (d->m_svgFilename.isNull())
171  {
172  qCDebug(GAMES_LIB) << "KGameSvgDocument::load(): Filename not specified.";
173  return;
174  }
175 
176  QFile file(d->m_svgFilename);
177  if (!file.open(QIODevice::ReadOnly))
178  {
179  return;
180  }
181  QByteArray content = file.readAll();
182 
183  // If the file is compressed, decompress the contents before loading it.
184  if (!content.startsWith("<?xml")) // krazy:exclude=strings
185  {
186  QBuffer buf(&content);
187  KCompressionDevice flt(&buf, /*autoDeleteInputDevice*/ false, KCompressionDevice::GZip);
188  if (!flt.open(QIODevice::ReadOnly))
189  {
190  flt.close();
191  return;
192  }
193  QByteArray ar = flt.readAll();
194  flt.close();
195  content = ar;
196  }
197 
198  if (!setContent(content))
199  {
200  file.close();
201  qCDebug(GAMES_LIB) << "DOM content not set.";
202  return;
203  }
204  file.close();
205 }
206 
208 {
209  setSvgFilename(svgFilename);
210  load();
211 }
212 
213 void KGameSvgDocument::rotate(double degrees, const MatrixOptions& options)
214 {
215  QTransform matrix;
216 
217  if (options == ApplyToCurrentMatrix)
218  {
219  matrix = transformMatrix().QTransform::rotate(degrees);
220  }
221  else
222  {
223  matrix = QTransform();
224  matrix.QTransform::rotate(degrees);
225  }
227 }
228 
229 void KGameSvgDocument::translate(int xPixels, int yPixels, const MatrixOptions& options)
230 {
231  QTransform matrix;
232 
233  if (options == ApplyToCurrentMatrix)
234  {
235  matrix = transformMatrix().QTransform::translate(xPixels, yPixels);
236  }
237  else
238  {
239  matrix = QTransform();
240  matrix.QTransform::translate(xPixels, yPixels);
241  }
243 }
244 
245 void KGameSvgDocument::shear(double xRadians, double yRadians, const MatrixOptions& options)
246 {
247  QTransform matrix;
248 
249  if (options == ApplyToCurrentMatrix)
250  {
251  matrix = transformMatrix().QTransform::shear(xRadians, yRadians);
252  }
253  else
254  {
255  matrix = QTransform();
256  matrix.QTransform::shear(xRadians, yRadians);
257  }
259 }
260 
261 void KGameSvgDocument::skew(double xDegrees, double yDegrees, const MatrixOptions& options)
262 {
263  double xRadians = xDegrees * (M_PI / 180);
264  double yRadians = yDegrees * (M_PI / 180);
265 
266  shear(xRadians, yRadians, options);
267 }
268 
269 void KGameSvgDocument::scale(double xFactor, double yFactor, const MatrixOptions& options)
270 {
271  QTransform matrix;
272  if ((xFactor == 0) || (yFactor == 0))
273  {
274  qWarning () << "KGameSvgDocument::scale: You cannot scale by zero";
275  }
276 
277  if (options == ApplyToCurrentMatrix)
278  {
279  matrix = transformMatrix().QTransform::scale(xFactor, yFactor);
280  }
281  else
282  {
283  matrix = QTransform();
284  matrix.QTransform::scale(xFactor, yFactor);
285  }
287 }
288 
290 {
291  return d->m_currentNode;
292 }
293 
295 {
296  d->m_currentNode = node;
297  d->setCurrentElement();
298 }
299 
301 {
302  return d->m_svgFilename;
303 }
304 
306 {
307  d->m_svgFilename = svgFilename;
308 }
309 
311 {
312  return styleProperties().value(propertyName);
313 }
314 
315 void KGameSvgDocument::setStyleProperty(const QString& propertyName, const QString& propertyValue)
316 {
317  QHash<QString, QString> properties;
318 
319  properties = styleProperties();
320  properties.insert(propertyName, propertyValue);
321 
323 }
324 
326 {
327  QString s, t, xml, defs, pattern;
328  QTextStream str(&s);
329  QTextStream str_t(&t);
330  QStringList defsAdded;
331  int result = 0;
332  QRegExp rx;
333 
334  currentNode().save(str, 1);
335  xml = *str.string();
336 
337  // Find and add any required gradients or patterns
338  pattern = QLatin1String( "url" ) + WSP_ASTERISK + OPEN_PARENS + WSP_ASTERISK + QLatin1String( "#(.*)" ) + WSP_ASTERISK + CLOSE_PARENS;
339  rx.setPattern(pattern);
340  if (rx.indexIn(xml, result) != -1)
341  {
342  QDomNode node, nodeBase;
343  QString baseId;
344  QDomNode n = def();
345 
346  result = 0;
347  while ((result = rx.indexIn(xml, result)) != -1)
348  {
349  // Find the pattern or gradient referenced
350  result += rx.matchedLength();
351  if (!defsAdded.contains(rx.cap(1)))
352  {
353  node = d->findElementById(QStringLiteral( "id" ), rx.cap(1), n);
354  node.save(str_t, 1);
355  defsAdded.append(rx.cap(1));
356  }
357 
358  // Find the gradient the above gradient is based on
359  baseId = node.toElement().attribute(QStringLiteral( "xlink:href" )).mid(1);
360  if (!defsAdded.contains(baseId))
361  {
362  nodeBase = d->findElementById(QStringLiteral( "id" ), baseId, n);
363  nodeBase.save(str_t, 1);
364  defsAdded.append(baseId);
365  }
366  }
367  defs = *str_t.string();
368  defs = QLatin1String( "<defs>" ) + defs + QLatin1String( "</defs>" );
369  }
370 
371  // Need to make node be a real svg document, so prepend and append required tags.
372  xml = d->SVG_XML_PREPEND + defs + xml + d->SVG_XML_APPEND;
373  return xml;
374 }
375 
377 {
378  return nodeToSvg().toUtf8();
379 }
380 
382 {
383  return d->m_currentElement.attribute( QStringLiteral( "style" ), QStringLiteral( "Element has no style attribute." ));
384 }
385 
386 void KGameSvgDocument::setStyle(const QString& styleAttribute)
387 {
388  d->m_currentElement.setAttribute(QStringLiteral( "style" ), styleAttribute);
389 }
390 
392 {
393  return elementsByTagName(QStringLiteral( "pattern" ));
394 }
395 
397 {
398  return elementsByTagName(QStringLiteral( "linearGradient" ));
399 }
400 
402 {
403  return elementsByTagName(QStringLiteral( "radialGradient" ));
404 }
405 
407 {
408  return elementsByTagName(QStringLiteral( "defs" ));
409 }
410 
412 {
413  return defs().at(0);
414 }
415 
417 {
418  return d->m_currentElement.attribute( QStringLiteral( "transform" ), QStringLiteral( "Element has no transform attribute." ) );
419 }
420 
421 void KGameSvgDocument::setTransform(const QString& transformAttribute)
422 {
423  d->m_currentElement.setAttribute(QStringLiteral( "transform" ), transformAttribute);
424 }
425 
427 {
428  QHash<QString, QString> stylePropertiesHash;
429  QStringList styleProperties, keyValuePair;
431 
432  styleProperties = style().split(QLatin1Char( ';' ));
433 
434  /* The style attr may have a trailing semi-colon. If it does, split()
435  * gives us an empty final element. Remove it or we get 'index out of range' errors
436  */
437  if (styleProperties.at((styleProperties.count()-1)).isEmpty())
438  {
439  styleProperties.removeAt((styleProperties.count()-1));
440  d->setStyleHasTrailingSemicolon(true);
441  }
442  else {d->setStyleHasTrailingSemicolon(false);}
443 
444  for (int i = 0; i < styleProperties.size(); i++)
445  {
446  styleProperty = styleProperties.at(i);
447  keyValuePair = styleProperty.split(QLatin1Char( ':' ));
448  stylePropertiesHash.insert(keyValuePair.at(0), keyValuePair.at(1));
449  }
450  return stylePropertiesHash;
451 }
452 
454 {
455  QHash<QString, QString> styleProperties = _styleProperties;
456  QString styleBuffer, property;
457 
458  d->m_inkscapeOrder << QStringLiteral( "fill" ) << QStringLiteral( "fill-opacity" ) << QStringLiteral( "fill-rule" ) << QStringLiteral( "stroke" ) << QStringLiteral( "stroke-width" ) << QStringLiteral( "stroke-linecap" )
459  << QStringLiteral( "stroke-linejoin" ) << QStringLiteral( "stroke-miterlimit" ) << QStringLiteral( "stroke-dasharray" ) << QStringLiteral( "stroke-opacity" );
460 
461  if (options == UseInkscapeOrder)
462  {
463  for (int i = 0; i < d->m_inkscapeOrder.size(); i++)
464  {
465  property = d->m_inkscapeOrder.at(i);
466  if (styleProperties.contains(property))
467  {
468  styleBuffer += property + QLatin1Char( ':' ) + styleProperties.take(property) + QLatin1Char( ';' );
469  }
470  else
471  {
472  // Do Nothing
473  }
474  }
475  }
476 
477  // Append any style properties
478  if (!styleProperties.isEmpty())
479  {
480  QHashIterator<QString, QString> it(styleProperties);
481  while (it.hasNext())
482  {
483  it.next();
484  styleBuffer += it.key() + QLatin1Char( ':' ) + it.value() + QLatin1Char( ';' );
485  }
486  }
487 
488  // Remove trailing semicolon if original didn't have one
489  if (!d->styleHasTrailingSemicolon()) {styleBuffer.chop(1);}
490  setStyle(styleBuffer);
491 }
492 
494 {
495  /*
496  * Transform attributes can be quite complex. Here, we assemble this tangled web of
497  * complexity into an single matrix.
498  *
499  * The regex's that make this bearable live in kgamesvgdocument_p.h. As these regex's
500  * get quite complex, we have some code in tests/kgamesvgdocumenttest.cpp to help verify
501  * they are still correct after being edited.
502  *
503  * Warning: This code depends on the capturing parenthesis in the regex's not changing.
504  *
505  * For all the gory details, see http://www.w3.org/TR/SVG/coords.html#TransformAttribute
506  */
507  QRegExp rx;
508  QString transformAttribute;
509  int result;
510  int i = 0;
511  QTransform baseMatrix = QTransform();
512 
513  transformAttribute = transform();
514  if (transformAttribute == QLatin1String( "Element has no transform attribute." ))
515  {
516  return QTransform();
517  }
518  transformAttribute = transformAttribute.trimmed();
519 
520  rx.setPattern(TRANSFORMS);
521  if (!rx.exactMatch(transformAttribute))
522  {
523  qWarning () << "Transform attribute seems to be invalid. Check your SVG file.";
524  return QTransform();
525  }
526 
527  rx.setPattern(TRANSFORM);
528 
529  while (transformAttribute.size() > 0 && i < 32) // 32 is an arbitrary limit for the number of transforms for a single node
530  {
531  result = rx.indexIn(transformAttribute);
532  if (result != -1) // Found left-most transform
533  {
534  if (rx.cap(1) == QLatin1String( "matrix" ))
535  {
536  // If the first transform found is a matrix, use it as the base,
537  // else we use a null matrix.
538  if (i == 0)
539  {
540  baseMatrix = QTransform(rx.cap(2).toDouble(), rx.cap(3).toDouble(), rx.cap(4).toDouble(),
541  rx.cap(5).toDouble(), rx.cap(6).toDouble(), rx.cap(7).toDouble());
542  }
543  else
544  {
545  baseMatrix = QTransform(rx.cap(2).toDouble(), rx.cap(3).toDouble(), rx.cap(4).toDouble(),
546  rx.cap(5).toDouble(), rx.cap(6).toDouble(), rx.cap(7).toDouble()) * baseMatrix;
547  }
548  }
549 
550  if (rx.cap(8) == QLatin1String( "translate" ))
551  {
552  double x = rx.cap(9).toDouble();
553  double y = rx.cap(10).toDouble();
554  if (rx.cap(10).isEmpty()) // y defaults to zero per SVG standard
555  {
556  y = 0;
557  }
558  baseMatrix = baseMatrix.translate(x, y);
559  }
560 
561  if (rx.cap(11) == QLatin1String( "scale" ))
562  {
563  double x = rx.cap(12).toDouble();
564  double y = rx.cap(12).toDouble();
565  if (rx.cap(13).isEmpty()) // y defaults to x per SVG standard
566  {
567  y = x;
568  }
569  baseMatrix = baseMatrix.scale(x, y);
570  }
571 
572  if (rx.cap(14) == QLatin1String( "rotate" ))
573  {
574  double a = rx.cap(15).toDouble();
575  double cx = rx.cap(16).toDouble();
576  double cy = rx.cap(17).toDouble();
577 
578  if ((cx > 0) || (cy > 0)) // rotate around point (cx, cy)
579  {
580  baseMatrix.translate(cx, cy);
581  baseMatrix.rotate(a);
582  baseMatrix.translate((cx * -1), (cy * -1));
583  }
584  else
585  {
586  baseMatrix = baseMatrix.rotate(a); // rotate around origin
587  }
588  }
589 
590  if (rx.cap(18) == QLatin1String( "skewX" ))
591  {
592  baseMatrix = baseMatrix.shear(rx.cap(19).toDouble() * (M_PI / 180), 0);
593  }
594 
595  if (rx.cap(20) == QLatin1String( "skewY" ))
596  {
597  baseMatrix = baseMatrix.shear(0, rx.cap(21).toDouble() * (M_PI / 180));
598  }
599  }
600  transformAttribute = transformAttribute.mid(rx.matchedLength() + result);
601  i++;
602  }
603 
604  return baseMatrix;
605 }
606 
608 {
609  QString transformBuffer, tmp;
610  QTransform null = QTransform();
611 
612  if (options == ApplyToCurrentMatrix)
613  {
614  matrix = transformMatrix() * matrix;
615  }
616 
617  transformBuffer = QStringLiteral( "matrix(" );
618  transformBuffer += tmp.setNum(matrix.m11(),'g',7) + QLatin1Char( ',' );
619  transformBuffer += tmp.setNum(matrix.m12(),'g',7) + QLatin1Char( ',' );
620  transformBuffer += tmp.setNum(matrix.m21(),'g',7) + QLatin1Char( ',' );
621  transformBuffer += tmp.setNum(matrix.m22(),'g',7) + QLatin1Char( ',' );
622  transformBuffer += tmp.setNum(matrix.dx(),'g',7) + QLatin1Char( ',' );
623  transformBuffer += tmp.setNum(matrix.dy(),'g',7) + QLatin1Char( ')' );
624 
625  if ((transform() == QLatin1String( "Element has no transform attribute." )) && (matrix == null))
626  {
627  // Do not write a meaningless matrix to DOM
628  }
629  else
630  {
631  setTransform(transformBuffer);
632  }
633 }
634 
635 
636 //
637 // Private
638 //
639 
640 QDomNode KGameSvgDocumentPrivate::findElementById(const QString& attributeName, const QString& attributeValue, const QDomNode& node)
641 {
642  QDomElement e = node.toElement(); // try to convert the node to an element.
643  QString value = e.attribute( attributeName, QStringLiteral( "Element has no attribute with that name." ));
644 
645  if (value == attributeValue)
646  {
647  // We found our node. Stop recursion and return it.
648  return node;
649  }
650 
651  if (!node.firstChild().isNull())
652  {
653  QDomNode result = findElementById(attributeName, attributeValue, node.firstChild());
654  /** We have recursed, now we need to have this recursion end when
655  * the function call above returns
656  */
657  if (!result.isNull()) return result; // If we found the node with id, then return it
658  }
659  if (!node.nextSibling().isNull())
660  {
661  QDomNode result = findElementById(attributeName, attributeValue, node.nextSibling());
662  /** We have recursed, now we need to have this recursion end when
663  * the function call above returns */
664  if (!result.isNull()) return result;
665  }
666  if (!node.firstChild().isNull() && !node.nextSibling().isNull())
667  {
668  // Do Nothing
669  //qCDebug(GAMES_LIB) << "No children or siblings.";
670  }
671 
672  // Matching node not found, so return a null node.
673  return QDomNode();
674 }
675 
676 QDomElement KGameSvgDocumentPrivate::currentElement() const
677 {
678  return m_currentElement;
679 }
680 
681 void KGameSvgDocumentPrivate::setCurrentElement()
682 {
683  m_currentElement = m_currentNode.toElement();
684 }
685 
686 bool KGameSvgDocumentPrivate::styleHasTrailingSemicolon() const
687 {
688  return m_hasSemicolon;
689 }
690 
691 void KGameSvgDocumentPrivate::setStyleHasTrailingSemicolon(bool hasSemicolon)
692 {
693  m_hasSemicolon = hasSemicolon;
694 }
695 
QString cap(int nth) const const
QHash::iterator insert(const Key &key, const T &value)
QString svgFilename() const
Returns the name of the SVG file this DOM represents.
void setSvgFilename(const QString &svgFilename)
Sets the current SVG filename.
qreal dx() const const
qreal dy() const const
const Key & key() const const
void setTransform(const QString &transformAttribute)
Sets the transform attribute of the current node.
QString attribute(const QString &name, const QString &defValue) const const
bool hasNext() const const
const T & at(int i) const const
int size() const const
void removeAt(int i)
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QString styleProperty(const QString &propertyName) const
Returns the value of the style property given for the current node.
bool startsWith(const QByteArray &ba) const const
QHash< QString, QString > styleProperties() const
Returns a hash of the style properties of the current node.
QString style() const
Returns the style attribute of the current node.
QDomElement documentElement() const const
QDomNodeList defs() const
Returns the defs in the document.
QDomNodeList radialGradients() const
Returns the radialGradients in the document.
void chop(int n)
double toDouble(bool *ok) const const
QTransform transformMatrix() const
Returns the transform attribute of the current node as a matrix.
int size() const const
QDomNode nextSibling() const const
void setPattern(const QString &pattern)
QDomElement toElement() const const
int matchedLength() const const
int indexIn(const QString &str, int offset, QRegExp::CaretMode caretMode) const const
QTransform & translate(qreal dx, qreal dy)
int count(const T &value) const const
void append(const T &value)
When building a style attribute, sort properties the same way Inkscape does.
QTransform & scale(qreal sx, qreal sy)
void shear(double xRadians, double yRadians, const MatrixOptions &options=ApplyToCurrentMatrix)
Shears the origin of the current node.
qreal m11() const const
qreal m12() const const
qreal m21() const const
qreal m22() const const
QDomNodeList elementsByTagName(const QString &tagname) const const
bool isEmpty() const const
QString trimmed() const const
virtual ~KGameSvgDocument()
Destructor.
QByteArray readAll()
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
A class for manipulating an SVG file using DOM.
QDomNode currentNode() const
Returns the last node found by elementById, or null if node not found.
KGameSvgDocument & operator=(const KGameSvgDocument &doc)
Assignment Operator.
void setStyleProperties(const QHash< QString, QString > &_styleProperties, const StylePropertySortOptions &options=Unsorted)
Sets the style properties of the current node.
virtual bool open(QIODevice::OpenMode mode) override
QString transform() const
Returns the transform attribute of the current node.
QHashIterator::Item next()
void skew(double xDegrees, double yDegrees, const MatrixOptions &options=ApplyToCurrentMatrix)
Skews the origin of the current node.
void load()
Reads the SVG file svgFilename() into DOM.
void scale(double xFactor, double yFactor, const MatrixOptions &options=ApplyToCurrentMatrix)
Scales the origin of the current node.
bool isNull() const const
QTransform & rotate(qreal angle, Qt::Axis axis)
void close() override
Replace the current matrix.
QDomDocument & operator=(const QDomDocument &x)
void translate(int xPixels, int yPixels, const MatrixOptions &options=ApplyToCurrentMatrix)
Moves the origin of the current node.
QString * string() const const
void save(QTextStream &stream, int indent, QDomNode::EncodingPolicy encodingPolicy) const const
QDomNode firstChild() const const
QString mid(int position, int n) const const
T take(const Key &key)
KGameSvgDocument()
Constructor.
bool isEmpty() const const
virtual void close() override
QString & setNum(short n, int base)
QString nodeToSvg() const
Returns the current node and its children as a new xml svg document.
bool open(QIODevice::OpenMode mode) override
bool contains(const Key &key) const const
QDomNode elementByUniqueAttributeValue(const QString &attributeName, const QString &attributeValue)
Returns the node with the given value for the given attribute.
const T & value() const const
QDomNode def() const
Returns the first def in the document.
QDomNodeList patterns() const
Returns the patterns in the document.
QDomNodeList linearGradients() const
Returns the linearGradients in the document.
void setTransformMatrix(QTransform &matrix, const MatrixOptions &options=ApplyToCurrentMatrix)
Sets the transform attribute of the current node.
QByteArray nodeToByteArray() const
Builds a new svg document and returns a QByteArray suitable for passing to QSvgRenderer::load().
QTransform & shear(qreal sh, qreal sv)
void setCurrentNode(const QDomNode &node)
Sets the current node.
QDomNode elementById(const QString &attributeValue)
Returns a node with the given id.
void setStyle(const QString &styleAttribute)
Sets the style attribute of the current node.
void rotate(double degrees, const MatrixOptions &options=ApplyToCurrentMatrix)
Rotates the origin of the current node counterclockwise.
bool exactMatch(const QString &str) const const
void setStyleProperty(const QString &propertyName, const QString &propertyValue)
Sets the value of the style property given for the current node.
QDomNode at(int index) const const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Dec 7 2021 22:34:15 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.