KDEGames

kgamesvgdocument.h
1 /*
2  SPDX-FileCopyrightText: 2007 Mark A. Taff <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-only
5 */
6 
7 #ifndef _KGAMESVGDOCUMENT_H_
8 #define _KGAMESVGDOCUMENT_H_
9 
10 // own
11 #include "libkdegamesprivate_export.h"
12 // Qt
13 #include <QHash>
14 #include <QStringList>
15 #include <QTransform>
16 #include <QDomDocument>
17 #include <QLoggingCategory>
18 // Std
19 #include <memory>
20 
21 Q_DECLARE_LOGGING_CATEGORY(GAMES_LIB)
22 
23 class KGameSvgDocumentPrivate;
24 
25 /**
26  * \class KGameSvgDocument kgamesvgdocument.h <KGameSvgDocument>
27  *
28  * @brief A class for manipulating an SVG file using DOM
29  *
30  * This class is a wrapper around QDomDocument for SVG files.
31  * It:
32  * @li implements elementById();
33  * @li manipulates a node's style properties; and,
34  * @li manipulates a node's transform properties.
35  *
36  * @note The DOM standard requires all changes to be "live", so we cannot cache any values
37  * from the file; instead, we always have to query the DOM for the current value. This also
38  * means that style & matrix changes we make happen to the DOM immediately.
39  *
40  * A typical use is to read in an SVG file, edit the style or transform attributes
41  * in DOM as desired, and then output a QByteArray suitable for being loaded with
42  * QSvgRenderer::load().
43  *
44  * To read an SVG file into DOM:
45  * @code
46  * KGameSvgDocument svgDom;
47  * svgDom.load("/path/to/svgFile.svg");
48  * @endcode
49  *
50  * To find a node with a specific value in its id attribute, for example where id="playerOneGamepiece":
51  * @code
52  * QDomNode playerOneGamepiece = svgDom.elementById("playerOneGamepiece");
53  *
54  * // This works too
55  * QDomNode playerOneGamepiece = svgDom.elementByUniqueAttributeValue("id", "playerOneGamepiece");
56  * @endcode
57  *
58  * Most methods operate on the last node found by @c elementById() or @c elementByUniqueAttributeValue().
59  * If the methods are working on the wrong node, then you are mistaken about which node was
60  * the last node (or you found a bug). Try calling @c setCurrentNode() with the node you are
61  * wanting to modify to see if this is the issue. Consider the following code for example:
62  * @code
63  * QDomNode playerOneGamepiece = svgDom.elementById("playerOneGamepiece");
64  * QDomNode playerTwoGamepiece = svgDom.elementById("playerTwoGamepiece");
65  *
66  * // Set player one's game piece to have a white fill
67  * svgDom.setStyleProperty("fill", "#ffffff"); // INCORRECT: playerTwoGamepiece is the last node, not playerOneGamepiece
68  *
69  * svgDom.setCurrentNode(playerOneGamepiece); // CORRECT: Set current node to the node we want,
70  * svgDom.setStyleProperty("fill", "#ffffff"); // then we modify the node
71  * @endcode
72  *
73  * To skew the @c currentNode():
74  * @code
75  * // Skew the horizontal axis 7.5 degrees
76  * svgDom.skew(-7.5, 0, KGameSvgDocument::ReplaceCurrentMatrix);
77  * @endcode
78  *
79  * @warning Be careful when using the KGameSvgDocument::ApplyToCurrentMatrix flag. It multiplies the matrices,
80  * so if you repeatedly apply the same matrix to a node, you have a polynomial series @c x^2, and you will
81  * very quickly run into overflow issues.
82  *
83  * To output @c currentNode() to be rendered:
84  * @code
85  * QSvgRenderer svgRenderer;
86  * QByteArray svg = svgDom.nodeToByteArray();
87  * svgRenderer.load(svg);
88  * @endcode
89  *
90  * To output the whole document to be rendered (See QDomDocument::toByteArray()):
91  * @code
92  * QSvgRenderer svgRenderer;
93  * QByteArray svg = svgDom.toByteArray();
94  * svgRenderer.load(svg);
95  * @endcode
96  *
97  * @see QDomDocument, QSvgRenderer
98  * @author Mark A. Taff <[email protected]>
99  * @version 0.1
100  *
101  * @todo Add convenience functions for getting/setting individual style properties.
102  * I haven't completely convinced myself of the utility of this, so don't hold your breathe. ;-)
103  */
104 class KDEGAMESPRIVATE_EXPORT KGameSvgDocument : public QDomDocument
105 {
106 public:
107  /**
108  * @brief Constructor
109  */
110  explicit KGameSvgDocument();
111 
112  /**
113  * @brief Copy Constructor
114  */
116 
117  /**
118  * @brief Destructor
119  */
120  virtual ~KGameSvgDocument();
121 
122  /**
123  * @brief Assignment Operator
124  */
125  KGameSvgDocument& operator=(const KGameSvgDocument &doc);
126 
127  /**
128  * @brief Options for applying (multiplying) or replacing the current matrix
129  */
131  /**
132  * Apply to current matrix
133  */
134  ApplyToCurrentMatrix = 0x01,
135  /**
136  * Replace the current matrix
137  */
138  ReplaceCurrentMatrix = 0x02
139  };
140  /** @brief Q_DECLARE_FLAGS macro confuses doxygen, so create typedef's manually */
142 
143  /**
144  * Options for sorting style properties when building a style attribute
145  */
147  /**
148  * When building a style attribute, do not sort
149  */
150  Unsorted = 0x01,
151  /**
152  * When building a style attribute, sort properties the same way Inkscape does
153  */
154  UseInkscapeOrder = 0x02
155  };
156  /** @brief Q_DECLARE_FLAGS macro confuses doxygen, so create typedef's manually */
158 
159  /**
160  * @brief Returns the node with the given value for the given attribute.
161  *
162  * Returns the element whose attribute given in @p attributeName is equal to the value
163  * given in @p attributeValue.
164  *
165  * QDomDocument::elementById() always returns a null node because TT says they can't know
166  * which attribute is the id attribute. Here, we allow the id attribute to be specified.
167  *
168  * This function also sets @p m_currentNode to this node.
169  *
170  * @param attributeName The name of the identifying attribute, such as "id" to find.
171  * @param attributeValue The value to look for in the attribute @p attributeName
172  * The values held in this attribute must be unique in the document, or the consequences
173  * may be unpredictably incorrect. You've been warned. ;-)
174  * @returns the matching node, or a null node if no matching node found
175  */
176  QDomNode elementByUniqueAttributeValue(const QString& attributeName, const QString& attributeValue);
177 
178  /**
179  * @brief Returns a node with the given id.
180  *
181  * This is a convenience function. We call elementByUniqueAttributeValue(), but we assume
182  * that the name of the attribute is "id". This assumption will be correct for valid SVG files.
183  *
184  * Returns the element whose ID is equal to elementId. If no element with the ID was found,
185  * this function returns a null element.
186  *
187  * @param attributeValue The value of the id attribute to find
188  * @returns the matching node, or a null node if no matching node found
189  * @see elementByUniqueAttributeValue()
190  */
191  QDomNode elementById(const QString& attributeValue);
192 
193  /**
194  * @brief Reads the SVG file svgFilename() into DOM.
195  * @returns nothing
196  */
197  void load();
198 
199  /**
200  * @overload
201  * @brief This function permits specifying the svg file and loading it at once.
202  *
203  * @param svgFilename The filename of the SVG file to open.
204  * @returns nothing
205  */
206  void load(const QString& svgFilename);
207 
208  /**
209  * @brief Rotates the origin of the current node counterclockwise.
210  *
211  * @param degrees The amount in degrees to rotate by.
212  * @param options Apply to current matrix or replace current matrix.
213  * @returns nothing
214  * @see QTransform#rotate()
215  */
216  void rotate(double degrees, const MatrixOptions& options = ApplyToCurrentMatrix);
217 
218  /**
219  * @brief Moves the origin of the current node
220  *
221  * @param xPixels The number of pixels to move the x-axis by.
222  * @param yPixels The number of pixels to move the y-axis by.
223  * @param options Apply to current matrix or replace current matrix.
224  * @returns nothing
225  * @see QTransform::translate()
226  */
227  void translate(int xPixels, int yPixels, const MatrixOptions& options = ApplyToCurrentMatrix);
228 
229  /**
230  * @brief Shears the origin of the current node.
231  *
232  * @param xRadians The amount in radians to shear (skew) the x-axis by.
233  * @param yRadians The amount in radians to shear (skew) the y-axis by.
234  * @param options Apply to current matrix or replace current matrix.
235  * @returns nothing
236  * @see QTransform::shear()
237  */
238  void shear(double xRadians, double yRadians, const MatrixOptions& options = ApplyToCurrentMatrix);
239 
240  /**
241  * @brief Skews the origin of the current node.
242  *
243  * This is a convenience function. It simply converts its arguments to
244  * radians, then calls shear().
245  *
246  * @param xDegrees The amount in degrees to shear (skew) the x-axis by.
247  * @param yDegrees The amount in degrees to shear (skew) the y-axis by.
248  * @param options Apply to current matrix or replace current matrix.
249  * @returns nothing
250  * @see shear()
251  */
252  void skew(double xDegrees, double yDegrees, const MatrixOptions& options = ApplyToCurrentMatrix);
253 
254  /**
255  * @brief Scales the origin of the current node.
256  *
257  * @note Neither @c xFactor nor @c yFactor may be zero, otherwise you scale
258  * the element into nonexistence.
259  *
260  * @param xFactor The factor to scale the x-axis by.
261  * @param yFactor The factor to scale the y-axis by.
262  * @param options Apply to current matrix or replace current matrix.
263  * @returns nothing
264  * @see QTransform::scale()
265  */
266  void scale(double xFactor, double yFactor, const MatrixOptions& options = ApplyToCurrentMatrix);
267 
268  /**
269  * @brief Returns the last node found by elementById, or null if node not found
270  *
271  * @returns The current node
272  * @see setCurrentNode()
273  */
274  QDomNode currentNode() const;
275 
276  /**
277  * @brief Sets the current node.
278  *
279  * @param node The node to set currentNode to.
280  * @returns nothing
281  * @see currentNode()
282  */
283  void setCurrentNode(const QDomNode& node);
284 
285  /**
286  * @brief Returns the name of the SVG file this DOM represents.
287  *
288  * @returns The current filename.
289  * @see setSvgFilename()
290  */
291  QString svgFilename() const;
292 
293  /**
294  * @brief Sets the current SVG filename.
295  *
296  * @param svgFilename The filename of the SVG file to open.
297  * @returns nothing
298  * @see svgFilename()
299  */
300  void setSvgFilename(const QString& svgFilename);
301 
302  /**
303  * @brief Returns the value of the style property given for the current node.
304  *
305  * @note Internally, we create a hash with @c styleProperties, then return the value
306  * of the @c propertyName property. As such, if you need the values of multiple
307  * properties, it will be more efficient to call @c styleProperties()
308  * and then use the hash directly.
309  *
310  * See KGameSvgDocumentPrivate::m_inkscapeOrder for a list of common SVG style properties
311  *
312  * @param propertyName the name of the property to return
313  * @returns The value style property given, or null if no such property for this node.
314  * @see setStyleProperty(), styleProperties(), setStyleProperties()
315  */
316  QString styleProperty(const QString& propertyName) const;
317 
318  /**
319  * @brief Sets the value of the style property given for the current node.
320  *
321  * @note Internally, we create a hash with @c styleProperties, then update the
322  * @p propertyName to @p propertyValue, before finally applying the hash to
323  * DOM via @c setStyleProperties(). Because of this, if you need to set multiple
324  * properties per node, it will be more efficient to call @c styleProperties(),
325  * modify the hash it returns, and then apply the hash with @c setStyleProperties().
326  *
327  * @param propertyName The name of the property to set.
328  * @param propertyValue The value of the property to set.
329  * @returns nothing
330  * @see styleProperty(), styleProperties(), setStyleProperties()
331  */
332  void setStyleProperty(const QString& propertyName, const QString& propertyValue);
333 
334  /**
335  * @brief Returns the current node and its children as a new xml svg document.
336  *
337  * @returns The xml for the new svg document
338  */
339  QString nodeToSvg() const;
340 
341  /**
342  * @brief Builds a new svg document and returns a QByteArray suitable for passing to QSvgRenderer::load().
343  *
344  * Internally, we call @c nodeToSvg() and then convert to a QByteArray, so this method
345  * should be called @b instead of @c nodeToSvg().
346  *
347  * @returns the QByteArray
348  */
349  QByteArray nodeToByteArray() const;
350 
351  /**
352  * @brief Returns the style attribute of the current node.
353  *
354  * Unless you are parsing your own style attribute for some reason, you probably
355  * want to use styleProperty() or styleProperties().
356  *
357  * @returns The style attribute.
358  * @see styleProperty() styleProperties()
359  */
360  QString style() const;
361 
362  /**
363  * @brief Sets the style attribute of the current node.
364  *
365  * Unless you are parsing your own style attribute for some reason, you probably
366  * want to use setStyleProperty() or setStyleProperties().
367  *
368  * @param styleAttribute The style attribute to apply.
369  * @returns nothing
370  *
371  * @see setStyleProperty() setStyleProperties()
372  */
373  void setStyle(const QString& styleAttribute);
374 
375  /**
376  * @brief Returns the patterns in the document
377  *
378  * @returns The patterns in the document
379  */
380  QDomNodeList patterns() const;
381 
382  /**
383  * @brief Returns the linearGradients in the document
384  *
385  * @returns The linearGradients in the document
386  */
387  QDomNodeList linearGradients() const;
388 
389  /**
390  * @brief Returns the radialGradients in the document
391  *
392  * @returns The radialGradients in the document
393  */
394  QDomNodeList radialGradients() const;
395 
396  /**
397  * @brief Returns the defs in the document
398  *
399  * @returns The defs in the document
400  */
401  QDomNodeList defs() const;
402 
403  /**
404  * @brief Returns the first def in the document
405  *
406  * @returns The first def in the document
407  */
408  QDomNode def() const;
409 
410  /**
411  * @brief Returns the transform attribute of the current node.
412  * @returns The transform attribute.
413  * @see setTransform(), transformMatrix(), setTransformMatrix()
414  */
415  QString transform() const;
416 
417  /**
418  * @brief Sets the transform attribute of the current node.
419  *
420  * As this function works on QStrings, it <b>replaces</b> the existing
421  * transform attribute. If you need to multiply, use setTransformMatrix() instead.
422  *
423  * @param transformAttribute The transform attribute to apply.
424  * @returns nothing
425  * @see transform(), transformMatrix(), setTransformMatrix()
426  */
427  void setTransform(const QString& transformAttribute);
428 
429  /**
430  * @brief Returns a hash of the style properties of the current node.
431  * @returns The style properties.
432  * @see setStyleProperties()
433  */
434  QHash<QString, QString> styleProperties() const;
435 
436  /**
437  * @brief Sets the style properties of the current node.
438  *
439  * The only(?) reason to set useInkscapeOrder to true is if you are saving the svg xml to a file
440  * that may be human-edited later, for consistency. There is a performance hit, since hashes store
441  * their data unsorted.
442  *
443  * @param _styleProperties The hash of style properties to apply.
444  * @param options Apply the hash so the properties are in the same order as Inkscape writes them.
445  * @returns nothing
446  * @see styleProperties()
447  */
448  void setStyleProperties(const QHash<QString, QString>& _styleProperties, const StylePropertySortOptions& options = Unsorted);
449 
450  /**
451  * @brief Returns the transform attribute of the current node as a matrix.
452  *
453  * @returns The matrix for the transform attribute.
454  * @see setTransformMatrix()
455  */
456  QTransform transformMatrix() const;
457 
458  /**
459  * @brief Sets the transform attribute of the current node.
460  *
461  * @param matrix The matrix to apply.
462  * @param options Should we apply matrix to the current matrix?
463  * We modify matrix internally if @p options includes ApplyToCurrentMatrix, so it can't
464  * be passed as const.
465  * Normally we want to apply the existing matrix. If we apply the matrix,
466  * we potentially end up squaring with each call, e.g. x^2.
467  * @returns nothing
468  * @see transformMatrix()
469  */
470  void setTransformMatrix(QTransform& matrix, const MatrixOptions& options = ApplyToCurrentMatrix);
471 
472 private:
473 
474 
475  /**
476  * @brief d-pointer
477  */
478  std::unique_ptr<KGameSvgDocumentPrivate> const d;
479 
480 };
481 Q_DECLARE_OPERATORS_FOR_FLAGS(KGameSvgDocument::MatrixOptions)
482 Q_DECLARE_OPERATORS_FOR_FLAGS(KGameSvgDocument::StylePropertySortOptions)
483 
484 #endif // _KGAMESVGDOCUMENT_H_
QFlags< StylePropertySortOption > StylePropertySortOptions
Q_DECLARE_FLAGS macro confuses doxygen, so create typedef&#39;s manually.
StylePropertySortOption
Options for sorting style properties when building a style attribute.
A class for manipulating an SVG file using DOM.
QFlags< MatrixOption > MatrixOptions
Q_DECLARE_FLAGS macro confuses doxygen, so create typedef&#39;s manually.
MatrixOption
Options for applying (multiplying) or replacing the current matrix.
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.