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