Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qguisvg.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5
6#include "qguisvg_p.h"
7#include <cmath>
8#include <QtCore/qpoint.h>
9#include <QtCore/qvarlengtharray.h>
10#include <QtGui/private/qmath_p.h>
11
12QT_BEGIN_NAMESPACE
13
14namespace {
15
16static void pathArcSegment(QPainterPath &path,
17 qreal xc, qreal yc,
18 qreal th0, qreal th1,
19 qreal rx, qreal ry, qreal xAxisRotation)
20{
21 qreal sinTh, cosTh;
22 qreal a00, a01, a10, a11;
23 qreal x1, y1, x2, y2, x3, y3;
24 qreal t;
25 qreal thHalf;
26
27 sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
28 cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
29
30 a00 = cosTh * rx;
31 a01 = -sinTh * ry;
32 a10 = sinTh * rx;
33 a11 = cosTh * ry;
34
35 thHalf = 0.5 * (th1 - th0);
36 t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
37 x1 = xc + qCos(th0) - t * qSin(th0);
38 y1 = yc + qSin(th0) + t * qCos(th0);
39 x3 = xc + qCos(th1);
40 y3 = yc + qSin(th1);
41 x2 = x3 + t * qSin(th1);
42 y2 = y3 - t * qCos(th1);
43
44 path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
45 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
46 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
47}
48
49}
50namespace QGuiSvg {
51
52// '0' is 0x30 and '9' is 0x39
53bool isDigit(ushort ch)
54{
55 static quint16 magic = 0x3ff;
56 return ((ch >> 4) == 3) && (magic >> (ch & 15));
57}
58
59qreal toDouble(QStringView *str)
60{
61 const int maxLen = 255;//technically doubles can go til 308+ but whatever
62 char temp[maxLen+1];
63 int pos = 0;
64
65 if (str->startsWith(QLatin1Char('-'))) {
66 temp[pos++] = '-';
67 str->slice(1);
68 } else if (str->startsWith(QLatin1Char('+'))) {
69 str->slice(1);
70 }
71 while (!str->isEmpty() && isDigit(str->first().unicode()) && pos < maxLen) {
72 temp[pos++] = str->first().toLatin1();
73 str->slice(1);
74 }
75 if (str->startsWith(QLatin1Char('.')) && pos < maxLen) {
76 temp[pos++] = '.';
77 str->slice(1);
78 }
79 while (!str->isEmpty() && isDigit(str->first().unicode()) && pos < maxLen) {
80 temp[pos++] = str->first().toLatin1();
81 str->slice(1);
82 }
83 bool exponent = false;
84 if ((str->startsWith(QLatin1Char('e')) || str->startsWith(QLatin1Char('E'))) && pos < maxLen) {
85 exponent = true;
86 temp[pos++] = 'e';
87 str->slice(1);
88 if ((str->startsWith(QLatin1Char('-')) || str->startsWith(QLatin1Char('+')))
89 && pos < maxLen) {
90 temp[pos++] = str->first().toLatin1();
91 str->slice(1);
92 }
93 while (!str->isEmpty() && isDigit(str->first().unicode()) && pos < maxLen) {
94 temp[pos++] = str->first().toLatin1();
95 str->slice(1);
96 }
97 }
98
99 temp[pos] = '\0';
100
101 qreal val;
102 if (!exponent && pos < 10) {
103 int ival = 0;
104 const char *t = temp;
105 bool neg = false;
106 if (*t == '-') {
107 neg = true;
108 ++t;
109 }
110 while (*t && *t != '.') {
111 ival *= 10;
112 ival += (*t) - '0';
113 ++t;
114 }
115 if (*t == '.') {
116 ++t;
117 int div = 1;
118 while (*t) {
119 ival *= 10;
120 ival += (*t) - '0';
121 div *= 10;
122 ++t;
123 }
124 val = ((qreal)ival)/((qreal)div);
125 } else {
126 val = ival;
127 }
128 if (neg)
129 val = -val;
130 } else {
131 val = QByteArray::fromRawData(temp, pos).toDouble();
132 // Do not tolerate values too wild to be represented normally by floats
133 if (qFpClassify(float(val)) != FP_NORMAL)
134 val = 0;
135 }
136 return val;
137
138}
139
140qreal toDouble(QStringView str, bool *ok)
141{
142 const qreal res{ toDouble(&str) };
143 if (ok)
144 *ok = str.isEmpty();
145 return res;
146}
147
148qreal parseLength(QStringView str, LengthType *type, bool *ok)
149{
150 QStringView numStr = str.trimmed();
151
152 if (numStr.isEmpty()) {
153 if (ok)
154 *ok = false;
155 *type = LengthType::LT_OTHER;
156 return false;
157 }
158 if (numStr.endsWith(QLatin1Char('%'))) {
159 numStr.chop(1);
161 } else if (numStr.endsWith(QLatin1String("px"))) {
162 numStr.chop(2);
163 *type = LengthType::LT_PX;
164 } else if (numStr.endsWith(QLatin1String("pc"))) {
165 numStr.chop(2);
166 *type = LengthType::LT_PC;
167 } else if (numStr.endsWith(QLatin1String("pt"))) {
168 numStr.chop(2);
169 *type = LengthType::LT_PT;
170 } else if (numStr.endsWith(QLatin1String("mm"))) {
171 numStr.chop(2);
172 *type = LengthType::LT_MM;
173 } else if (numStr.endsWith(QLatin1String("cm"))) {
174 numStr.chop(2);
175 *type = LengthType::LT_CM;
176 } else if (numStr.endsWith(QLatin1String("in"))) {
177 numStr.chop(2);
178 *type = LengthType::LT_IN;
179 } else {
180 // default coordinate system
181 *type = LengthType::LT_PX;
182 }
183 qreal len = toDouble(numStr, ok);
184 return len;
185}
186
187// this should really be called convertToDefaultCoordinateSystem
188// and convert when type != QSvgHandler::defaultCoordinateSystem
189qreal convertToPixels(qreal len, bool , LengthType type)
190{
191
192 switch (type) {
194 break;
196 break;
198 break;
200 return len * 1.25;
201 break;
203 return len * 3.543307;
204 break;
206 return len * 35.43307;
207 break;
209 return len * 90;
210 break;
212 break;
213 default:
214 break;
215 }
216 return len;
217}
218
219// Parses the angle from a string and convert it to degrees.
220// In CSS, units are not optional so if a non-zero value is defined,
221// without a unit it should be treated as invalid.
222std::optional<qreal> parseAngle(QStringView str)
223{
224 QStringView numStr = str.trimmed();
225
226 if (numStr.isEmpty())
227 return std::nullopt;
228
229 qreal unitFactor = 1.0;
230 if (numStr.endsWith(QLatin1String("deg"))) {
231 numStr.chop(3);
232 unitFactor = 1.0;
233 } else if (numStr.endsWith(QLatin1String("grad"))) {
234 numStr.chop(4);
235 // deg = grad * 0.9;
236 unitFactor = 0.9;
237 } else if (numStr.endsWith(QLatin1String("rad"))) {
238 numStr.chop(3);
239 unitFactor = 180.0 / Q_PI;
240 } else if (numStr.endsWith(QLatin1String("turn"))) {
241 numStr.chop(4);
242 // one circle = one turn
243 unitFactor = 360.0;
244 } else {
245 return std::nullopt;
246 }
247
248 bool ok;
249 qreal angle = numStr.toDouble(&ok);
250 if (!ok)
251 return std::nullopt;
252
253 return angle * unitFactor;
254}
255
256void parseNumbersArray(QStringView *str, QVarLengthArray<qreal, 8> &points, const char *pattern)
257{
258 const size_t patternLen = qstrlen(pattern);
259 while (!str->isEmpty() && str->first().isSpace())
260 str->slice(1);
261 while ((!str->isEmpty() && QGuiSvg::isDigit(str->first().unicode()))
262 || str->startsWith(QLatin1Char('-')) || str->startsWith(QLatin1Char('+'))
263 || str->startsWith(QLatin1Char('.'))) {
264
265 if (patternLen && pattern[points.size() % patternLen] == 'f') {
266 // flag expected, may only be 0 or 1
267 if (!str->startsWith(QLatin1Char('0')) && !str->startsWith(QLatin1Char('1')))
268 return;
269 points.append(str->startsWith(QLatin1Char('0')) ? 0.0 : 1.0);
270 str->slice(1);
271 } else {
272 points.append(QGuiSvg::toDouble(str));
273 }
274
275 while (!str->isEmpty() && str->first().isSpace())
276 str->slice(1);
277 if (str->startsWith(QLatin1Char(',')))
278 str->slice(1);
279
280 //eat the rest of space
281 while (!str->isEmpty() && str->first().isSpace())
282 str->slice(1);
283 }
284}
285
286std::optional<QPainterPath> parsePath(QStringView dataStr, bool limitLength)
287{
288 if (dataStr.isEmpty())
289 return std::nullopt;
290
291 const int maxElementCount = 0x7fff; // Assume file corruption if more path elements than this
292 qreal x0 = 0, y0 = 0; // starting point
293 qreal x = 0, y = 0; // current point
294 char lastMode = 0;
295 QPointF ctrlPt;
296
297 QPainterPath path;
298 while (!dataStr.isEmpty()) {
299 while (dataStr.first().isSpace() && dataStr.length() > 1)
300 dataStr.slice(1);
301 QChar pathElem = dataStr.first();
302 dataStr.slice(1);
303 const char *pattern = nullptr;
304 if (pathElem == QLatin1Char('a') || pathElem == QLatin1Char('A'))
305 pattern = "rrrffrr";
306 QVarLengthArray<qreal, 8> arg;
307 parseNumbersArray(&dataStr, arg, pattern);
308 if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
309 arg.append(0);//dummy
310 const qreal *num = arg.constData();
311 int count = arg.size();
312 while (count > 0) {
313 qreal offsetX = x; // correction offsets
314 qreal offsetY = y; // for relative commands
315 switch (pathElem.unicode()) {
316 case 'm': {
317 if (count < 2)
318 return std::nullopt;
319 x = x0 = num[0] + offsetX;
320 y = y0 = num[1] + offsetY;
321 num += 2;
322 count -= 2;
323 path.moveTo(x0, y0);
324
325 // As per 1.2 spec 8.3.2 The "moveto" commands
326 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
327 // the subsequent pairs shall be treated as implicit 'lineto' commands.
328 pathElem = QLatin1Char('l');
329 }
330 break;
331 case 'M': {
332 if (count < 2)
333 return std::nullopt;
334
335 x = x0 = num[0];
336 y = y0 = num[1];
337 num += 2;
338 count -= 2;
339 path.moveTo(x0, y0);
340
341 // As per 1.2 spec 8.3.2 The "moveto" commands
342 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
343 // the subsequent pairs shall be treated as implicit 'lineto' commands.
344 pathElem = QLatin1Char('L');
345 }
346 break;
347 case 'z':
348 case 'Z': {
349 x = x0;
350 y = y0;
351 count--; // skip dummy
352 num++;
353 path.closeSubpath();
354 }
355 break;
356 case 'l': {
357 if (count < 2)
358 return std::nullopt;
359
360 x = num[0] + offsetX;
361 y = num[1] + offsetY;
362 num += 2;
363 count -= 2;
364 path.lineTo(x, y);
365
366 }
367 break;
368 case 'L': {
369 if (count < 2)
370 return std::nullopt;
371
372 x = num[0];
373 y = num[1];
374 num += 2;
375 count -= 2;
376 path.lineTo(x, y);
377 }
378 break;
379 case 'h': {
380 x = num[0] + offsetX;
381 num++;
382 count--;
383 path.lineTo(x, y);
384 }
385 break;
386 case 'H': {
387 x = num[0];
388 num++;
389 count--;
390 path.lineTo(x, y);
391 }
392 break;
393 case 'v': {
394 y = num[0] + offsetY;
395 num++;
396 count--;
397 path.lineTo(x, y);
398 }
399 break;
400 case 'V': {
401 y = num[0];
402 num++;
403 count--;
404 path.lineTo(x, y);
405 }
406 break;
407 case 'c': {
408 if (count < 6)
409 return std::nullopt;
410
411 QPointF c1(num[0] + offsetX, num[1] + offsetY);
412 QPointF c2(num[2] + offsetX, num[3] + offsetY);
413 QPointF e(num[4] + offsetX, num[5] + offsetY);
414 num += 6;
415 count -= 6;
416 path.cubicTo(c1, c2, e);
417 ctrlPt = c2;
418 x = e.x();
419 y = e.y();
420 break;
421 }
422 case 'C': {
423 if (count < 6)
424 return std::nullopt;
425
426 QPointF c1(num[0], num[1]);
427 QPointF c2(num[2], num[3]);
428 QPointF e(num[4], num[5]);
429 num += 6;
430 count -= 6;
431 path.cubicTo(c1, c2, e);
432 ctrlPt = c2;
433 x = e.x();
434 y = e.y();
435 break;
436 }
437 case 's': {
438 if (count < 4)
439 return std::nullopt;
440
441 QPointF c1;
442 if (lastMode == 'c' || lastMode == 'C' ||
443 lastMode == 's' || lastMode == 'S')
444 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
445 else
446 c1 = QPointF(x, y);
447 QPointF c2(num[0] + offsetX, num[1] + offsetY);
448 QPointF e(num[2] + offsetX, num[3] + offsetY);
449 num += 4;
450 count -= 4;
451 path.cubicTo(c1, c2, e);
452 ctrlPt = c2;
453 x = e.x();
454 y = e.y();
455 break;
456 }
457 case 'S': {
458 if (count < 4)
459 return std::nullopt;
460
461 QPointF c1;
462 if (lastMode == 'c' || lastMode == 'C' ||
463 lastMode == 's' || lastMode == 'S')
464 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
465 else
466 c1 = QPointF(x, y);
467 QPointF c2(num[0], num[1]);
468 QPointF e(num[2], num[3]);
469 num += 4;
470 count -= 4;
471 path.cubicTo(c1, c2, e);
472 ctrlPt = c2;
473 x = e.x();
474 y = e.y();
475 break;
476 }
477 case 'q': {
478 if (count < 4)
479 return std::nullopt;
480
481 QPointF c(num[0] + offsetX, num[1] + offsetY);
482 QPointF e(num[2] + offsetX, num[3] + offsetY);
483 num += 4;
484 count -= 4;
485 path.quadTo(c, e);
486 ctrlPt = c;
487 x = e.x();
488 y = e.y();
489 break;
490 }
491 case 'Q': {
492 if (count < 4)
493 return std::nullopt;
494
495 QPointF c(num[0], num[1]);
496 QPointF e(num[2], num[3]);
497 num += 4;
498 count -= 4;
499 path.quadTo(c, e);
500 ctrlPt = c;
501 x = e.x();
502 y = e.y();
503 break;
504 }
505 case 't': {
506 if (count < 2)
507 return std::nullopt;
508
509 QPointF e(num[0] + offsetX, num[1] + offsetY);
510 num += 2;
511 count -= 2;
512 QPointF c;
513 if (lastMode == 'q' || lastMode == 'Q' ||
514 lastMode == 't' || lastMode == 'T')
515 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
516 else
517 c = QPointF(x, y);
518 path.quadTo(c, e);
519 ctrlPt = c;
520 x = e.x();
521 y = e.y();
522 break;
523 }
524 case 'T': {
525 if (count < 2)
526 return std::nullopt;
527
528 QPointF e(num[0], num[1]);
529 num += 2;
530 count -= 2;
531 QPointF c;
532 if (lastMode == 'q' || lastMode == 'Q' ||
533 lastMode == 't' || lastMode == 'T')
534 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
535 else
536 c = QPointF(x, y);
537 path.quadTo(c, e);
538 ctrlPt = c;
539 x = e.x();
540 y = e.y();
541 break;
542 }
543 case 'a': {
544 if (count < 7)
545 return std::nullopt;
546
547 qreal rx = (*num++);
548 qreal ry = (*num++);
549 qreal xAxisRotation = (*num++);
550 qreal largeArcFlag = (*num++);
551 qreal sweepFlag = (*num++);
552 qreal ex = (*num++) + offsetX;
553 qreal ey = (*num++) + offsetY;
554 count -= 7;
555 qreal curx = x;
556 qreal cury = y;
557 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
558 int(sweepFlag), ex, ey, curx, cury);
559
560 x = ex;
561 y = ey;
562 }
563 break;
564 case 'A': {
565 if (count < 7)
566 return std::nullopt;
567
568 qreal rx = (*num++);
569 qreal ry = (*num++);
570 qreal xAxisRotation = (*num++);
571 qreal largeArcFlag = (*num++);
572 qreal sweepFlag = (*num++);
573 qreal ex = (*num++);
574 qreal ey = (*num++);
575 count -= 7;
576 qreal curx = x;
577 qreal cury = y;
578 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
579 int(sweepFlag), ex, ey, curx, cury);
580
581 x = ex;
582 y = ey;
583 }
584 break;
585 default:
586 return std::nullopt;
587 }
588 lastMode = pathElem.toLatin1();
589 if (limitLength && path.elementCount() > maxElementCount)
590 return std::nullopt;
591 }
592 }
593
594 return path;
595}
596
597// the arc handling code underneath is from XSVG (BSD license)
598/*
599 * Copyright 2002 USC/Information Sciences Institute
600 *
601 * Permission to use, copy, modify, distribute, and sell this software
602 * and its documentation for any purpose is hereby granted without
603 * fee, provided that the above copyright notice appear in all copies
604 * and that both that copyright notice and this permission notice
605 * appear in supporting documentation, and that the name of
606 * Information Sciences Institute not be used in advertising or
607 * publicity pertaining to distribution of the software without
608 * specific, written prior permission. Information Sciences Institute
609 * makes no representations about the suitability of this software for
610 * any purpose. It is provided "as is" without express or implied
611 * warranty.
612 *
613 * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
614 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
615 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
616 * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
617 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
618 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
619 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
620 * PERFORMANCE OF THIS SOFTWARE.
621 *
622 */
623void pathArc(QPainterPath &path, qreal rx, qreal ry,
624 qreal x_axis_rotation, int large_arc_flag,
625 int sweep_flag, qreal x, qreal y,
626 qreal curx, qreal cury)
627{
628 const qreal Pr1 = rx * rx;
629 const qreal Pr2 = ry * ry;
630
631 if (!Pr1 || !Pr2)
632 return;
633
634 qreal sin_th, cos_th;
635 qreal a00, a01, a10, a11;
636 qreal x0, y0, x1, y1, xc, yc;
637 qreal d, sfactor, sfactor_sq;
638 qreal th0, th1, th_arc;
639 int i, n_segs;
640 qreal dx, dy, dx1, dy1, Px, Py, check;
641
642 rx = qAbs(rx);
643 ry = qAbs(ry);
644
645 sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
646 cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
647
648 dx = (curx - x) / 2.0;
649 dy = (cury - y) / 2.0;
650 dx1 = cos_th * dx + sin_th * dy;
651 dy1 = -sin_th * dx + cos_th * dy;
652 Px = dx1 * dx1;
653 Py = dy1 * dy1;
654 /* Spec : check if radii are large enough */
655 check = Px / Pr1 + Py / Pr2;
656 if (check > 1) {
657 rx = rx * qSqrt(check);
658 ry = ry * qSqrt(check);
659 }
660
661 a00 = cos_th / rx;
662 a01 = sin_th / rx;
663 a10 = -sin_th / ry;
664 a11 = cos_th / ry;
665 x0 = a00 * curx + a01 * cury;
666 y0 = a10 * curx + a11 * cury;
667 x1 = a00 * x + a01 * y;
668 y1 = a10 * x + a11 * y;
669 /* (x0, y0) is current point in transformed coordinate space.
670 (x1, y1) is new point in transformed coordinate space.
671
672 The arc fits a unit-radius circle in this space.
673 */
674 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
675 if (!d)
676 return;
677 sfactor_sq = 1.0 / d - 0.25;
678 if (sfactor_sq < 0) sfactor_sq = 0;
679 sfactor = qSqrt(sfactor_sq);
680 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
681 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
682 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
683 /* (xc, yc) is center of the circle. */
684
685 th0 = qAtan2(y0 - yc, x0 - xc);
686 th1 = qAtan2(y1 - yc, x1 - xc);
687
688 th_arc = th1 - th0;
689 if (th_arc < 0 && sweep_flag)
690 th_arc += 2 * Q_PI;
691 else if (th_arc > 0 && !sweep_flag)
692 th_arc -= 2 * Q_PI;
693
694 n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
695
696 for (i = 0; i < n_segs; i++) {
697 pathArcSegment(path, xc, yc,
698 th0 + i * th_arc / n_segs,
699 th0 + (i + 1) * th_arc / n_segs,
700 rx, ry, x_axis_rotation);
701 }
702}
703
704}
705
706QT_END_NAMESPACE
std::optional< qreal > parseAngle(QStringView str)
Definition qguisvg.cpp:222
@ LT_PERCENT
Definition qguisvg_p.h:30
@ LT_OTHER
Definition qguisvg_p.h:37
qreal parseLength(QStringView str, LengthType *type, bool *ok)
Definition qguisvg.cpp:148
std::optional< QPainterPath > parsePath(QStringView dataStr, bool limitLength)
Definition qguisvg.cpp:286
void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation, int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx, qreal cury)
Definition qguisvg.cpp:623
void parseNumbersArray(QStringView *str, QVarLengthArray< qreal, 8 > &points, const char *pattern)
Definition qguisvg.cpp:256
bool isDigit(ushort ch)
Definition qguisvg.cpp:53
qreal convertToPixels(qreal len, bool, LengthType type)
Definition qguisvg.cpp:189
qreal toDouble(QStringView str, bool *ok)
Definition qguisvg.cpp:140
qreal toDouble(QStringView *str)
Definition qguisvg.cpp:59
static void pathArcSegment(QPainterPath &path, qreal xc, qreal yc, qreal th0, qreal th1, qreal rx, qreal ry, qreal xAxisRotation)
Definition qguisvg.cpp:16