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
qsvggraphics.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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:significant reason:default
4
5
8#include "qsvgfont_p.h"
9
10#include <qabstracttextdocumentlayout.h>
11#include <qdebug.h>
12#include <qloggingcategory.h>
13#include <qpainter.h>
14#include <qscopedvaluerollback.h>
15#include <qtextcursor.h>
16#include <qtextdocument.h>
17#include <private/qfixed_p.h>
18
19#include <QElapsedTimer>
20#include <QLoggingCategory>
21
22#include <math.h>
23
24QT_BEGIN_NAMESPACE
25
26Q_LOGGING_CATEGORY(lcSvgDraw, "qt.svg.draw")
27
28#ifndef QT_SVG_MAX_LAYOUT_SIZE
29#define QT_SVG_MAX_LAYOUT_SIZE (qint64(QFIXED_MAX / 2))
30#endif
31
32QSvgDummyNode::~QSvgDummyNode()
33 = default;
34
35void QSvgDummyNode::drawCommand(QPainter *, QSvgExtraStates &)
36{
37 qWarning("Dummy node not meant to be drawn");
38}
39
40QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
41 : QSvgNode(parent), m_bounds(rect)
42{
43}
44
45QSvgEllipse::~QSvgEllipse()
46 = default;
47
48QRectF QSvgEllipse::internalFastBounds(QPainter *p, QSvgExtraStates &) const
49{
50 return p->transform().mapRect(m_bounds);
51}
52
53QRectF QSvgEllipse::internalBounds(QPainter *p, QSvgExtraStates &) const
54{
55 QPainterPath path;
56 path.addEllipse(m_bounds);
57 qreal sw = strokeWidth(p);
58 return qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect()
59 : boundsOnStroke(p, path, sw, BoundsMode::Simplistic);
60}
61
62QRectF QSvgEllipse::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
63{
64 QPainterPath path;
65 path.addEllipse(m_bounds);
66 qreal sw = strokeWidth(p);
67 QRectF rect = qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect()
68 : boundsOnStroke(p, path, sw, BoundsMode::IncludeMiterLimit);
69 return filterRegion(s.doc(), rect);
70}
71
72void QSvgEllipse::drawCommand(QPainter *p, QSvgExtraStates &)
73{
74 p->drawEllipse(m_bounds);
75}
76
77bool QSvgEllipse::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
78{
79 return true;
80}
81
82QSvgImage::QSvgImage(QSvgNode *parent,
83 const QImage &image,
84 const QString &filename,
85 const QRectF &bounds)
86 : QSvgNode(parent)
87 , m_filename(filename)
88 , m_image(image)
89 , m_bounds(bounds)
90{
91 if (m_bounds.width() == 0.0)
92 m_bounds.setWidth(static_cast<qreal>(m_image.width()));
93 if (m_bounds.height() == 0.0)
94 m_bounds.setHeight(static_cast<qreal>(m_image.height()));
95}
96
97QSvgImage::~QSvgImage()
98 = default;
99
100void QSvgImage::drawCommand(QPainter *p, QSvgExtraStates &)
101{
102 p->drawImage(m_bounds, m_image);
103}
104
105QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line)
106 : QSvgNode(parent), m_line(line)
107{
108}
109
110QSvgLine::~QSvgLine()
111 = default;
112
113void QSvgLine::drawCommand(QPainter *p, QSvgExtraStates &states)
114{
115 if (p->pen().widthF() != 0) {
116 qreal oldOpacity = p->opacity();
117 p->setOpacity(oldOpacity * states.strokeOpacity);
118 if (m_line.isNull() && p->pen().capStyle() != Qt::FlatCap)
119 p->drawPoint(m_line.p1());
120 else
121 p->drawLine(m_line);
122 p->setOpacity(oldOpacity);
123 }
124 QSvgMarker::drawMarkersForNode(this, p, states);
125}
126
127QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath)
128 : QSvgNode(parent), m_path(qpath)
129{
130}
131
132QSvgPath::~QSvgPath()
133 = default;
134
135void QSvgPath::drawCommand(QPainter *p, QSvgExtraStates &states)
136{
137 const qreal oldOpacity = p->opacity();
138 const bool drawingInOnePass = !separateFillStroke(p, states);
139 if (drawingInOnePass)
140 p->setOpacity(oldOpacity * states.fillOpacity);
141 m_path.setFillRule(states.fillRule);
142 if (m_path.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
143 p->drawPoint(m_path.boundingRect().topLeft());
144 else
145 p->drawPath(m_path);
146 if (!path().isEmpty())
147 QSvgMarker::drawMarkersForNode(this, p, states);
148 if (drawingInOnePass)
149 p->setOpacity(oldOpacity);
150}
151
152bool QSvgPath::separateFillStroke(const QPainter *p, const QSvgExtraStates &s) const
153{
154 return !qFuzzyCompare(s.fillOpacity, s.strokeOpacity)
155 || qFuzzyIsNull(p->pen().widthF());
156}
157
158QRectF QSvgPath::internalFastBounds(QPainter *p, QSvgExtraStates &) const
159{
160 return p->transform().mapRect(m_path.controlPointRect());
161}
162
163QRectF QSvgPath::internalBounds(QPainter *p, QSvgExtraStates &) const
164{
165 qreal sw = strokeWidth(p);
166 return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
167 : boundsOnStroke(p, m_path, sw, BoundsMode::Simplistic);
168}
169
170QRectF QSvgPath::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
171{
172 qreal sw = strokeWidth(p);
173 QRectF rect = qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
174 : boundsOnStroke(p, m_path, sw, BoundsMode::IncludeMiterLimit);
175 rect |= QSvgMarker::markersBoundsForNode(this, p, s);
176 return filterRegion(s.doc(), rect);
177}
178
179bool QSvgPath::requiresGroupRendering() const
180{
181 return hasAnyMarker();
182}
183
184QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
185 : QSvgNode(parent), m_poly(poly)
186{
187}
188
189QSvgPolygon::~QSvgPolygon()
190 = default;
191
192QRectF QSvgPolygon::internalFastBounds(QPainter *p, QSvgExtraStates &) const
193{
194 return p->transform().mapRect(m_poly.boundingRect());
195}
196
197QRectF QSvgPolygon::internalBounds(QPainter *p, QSvgExtraStates &s) const
198{
199 return internalBounds(p, s, BoundsMode::Simplistic);
200}
201
202QRectF QSvgPolygon::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
203{
204 QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
205 rect |= QSvgMarker::markersBoundsForNode(this, p, s);
206 return filterRegion(s.doc(), rect);
207}
208
209bool QSvgPolygon::requiresGroupRendering() const
210{
211 return hasAnyMarker();
212}
213
214QRectF QSvgPolygon::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
215{
216 qreal sw = strokeWidth(p);
217 if (qFuzzyIsNull(sw)) {
218 return p->transform().map(m_poly).boundingRect();
219 } else {
220 QPainterPath path;
221 path.addPolygon(m_poly);
222 return boundsOnStroke(p, path, sw, mode);
223 }
224}
225
226void QSvgPolygon::drawCommand(QPainter *p, QSvgExtraStates &states)
227{
228 if (m_poly.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
229 p->drawPoint(m_poly.first());
230 else
231 p->drawPolygon(m_poly, states.fillRule);
232 QSvgMarker::drawMarkersForNode(this, p, states);
233}
234
235bool QSvgPolygon::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
236{
237 return true;
238}
239
240QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
241 : QSvgNode(parent), m_poly(poly)
242{
243
244}
245
246QSvgPolyline::~QSvgPolyline()
247 = default;
248
249void QSvgPolyline::drawCommand(QPainter *p, QSvgExtraStates &states)
250{
251 if (p->brush().style() != Qt::NoBrush) {
252 p->drawPolygon(m_poly, states.fillRule);
253 } else {
254 if (m_poly.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
255 p->drawPoint(m_poly.first());
256 else
257 p->drawPolyline(m_poly);
258 QSvgMarker::drawMarkersForNode(this, p, states);
259 }
260}
261
262bool QSvgPolyline::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
263{
264 return true;
265}
266
267QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, qreal rx, qreal ry)
268 : QSvgNode(node),
269 m_rect(rect), m_rx(rx), m_ry(ry)
270{
271}
272
273QSvgRect::~QSvgRect()
274 = default;
275
276QRectF QSvgRect::internalFastBounds(QPainter *p, QSvgExtraStates &) const
277{
278 return p->transform().mapRect(m_rect);
279}
280
281QRectF QSvgRect::internalBounds(QPainter *p, QSvgExtraStates &s) const
282{
283 return internalBounds(p, s, BoundsMode::Simplistic);
284}
285
286QRectF QSvgRect::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
287{
288 return filterRegion(s.doc(), internalBounds(p, s, BoundsMode::IncludeMiterLimit));
289}
290
291QRectF QSvgRect::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
292{
293 qreal sw = strokeWidth(p);
294 if (qFuzzyIsNull(sw)) {
295 return p->transform().mapRect(m_rect);
296 } else {
297 QPainterPath path;
298 path.addRect(m_rect);
299 return boundsOnStroke(p, path, sw, mode);
300 }
301}
302
303void QSvgRect::drawCommand(QPainter *p, QSvgExtraStates &)
304{
305 if (m_rx || m_ry)
306 p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize);
307 else
308 p->drawRect(m_rect);
309}
310
311bool QSvgRect::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
312{
313 return true;
314}
315
316QSvgTspan * const QSvgText::LINEBREAK = 0;
317
318QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
319 : QSvgNode(parent)
320 , m_coord(coord)
321 , m_size(0, 0)
322 , m_type(Text)
323 , m_mode(Default)
324{
325}
326
327QSvgText::~QSvgText()
328{
329 for (int i = 0; i < m_tspans.size(); ++i) {
330 if (m_tspans[i] != LINEBREAK)
331 delete m_tspans[i];
332 }
333}
334
335void QSvgText::setTextArea(const QSizeF &size)
336{
337 m_size = size;
338 m_type = Textarea;
339}
340
341QRectF QSvgText::internalFastBounds(QPainter *p, QSvgExtraStates &) const
342{
343 QSvgFontStyle *fontProperty = static_cast<QSvgFontStyle *>(m_style.property(QSvgStyleProperty::Font));
344 QFont font = fontProperty ? fontProperty->qfont() : p->font();
345 QFontMetricsF fm(font);
346
347 int charCount = 0;
348 for (int i = 0; i < m_tspans.size(); ++i) {
349 if (m_tspans.at(i) != LINEBREAK)
350 charCount += m_tspans.at(i)->text().size();
351 }
352
353 QRectF approxMaximumBrect(m_coord.x(),
354 m_coord.y(),
355 charCount * fm.averageCharWidth(),
356 -m_tspans.size() * fm.height());
357 return p->transform().mapRect(approxMaximumBrect);
358}
359
360QRectF QSvgText::internalBounds(QPainter *p, QSvgExtraStates &states) const
361{
362 QRectF boundingRect;
363 if (shouldDrawNode(p, states))
364 draw_helper(p, states, &boundingRect);
365 return p->transform().mapRect(boundingRect);
366}
367
368void QSvgText::drawCommand(QPainter *p, QSvgExtraStates &states)
369{
370 draw_helper(p, states);
371}
372
373bool QSvgText::shouldDrawNode(QPainter *p, QSvgExtraStates &) const
374{
375 qsizetype numChars = 0;
376 qreal originalFontSize = p->font().pointSizeF();
377 qreal maxFontSize = originalFontSize;
378 for (const QSvgTspan *span : std::as_const(m_tspans)) {
379 if (span == LINEBREAK)
380 continue;
381
382 numChars += span->text().size();
383
384 QSvgFontStyle *style = static_cast<QSvgFontStyle *>(span->styleProperty(QSvgStyleProperty::Font));
385 if (style != nullptr && style->qfont().pointSizeF() > maxFontSize)
386 maxFontSize = style->qfont().pointSizeF();
387 }
388
389 QFont font = p->font();
390 font.setPixelSize(maxFontSize);
391 QFontMetricsF fm(font);
392 if (m_tspans.size() * fm.height() >= QT_SVG_MAX_LAYOUT_SIZE) {
393 qCWarning(lcSvgDraw) << "Text element too high to lay out, ignoring";
394 return false;
395 }
396
397 if (numChars * fm.maxWidth() >= QT_SVG_MAX_LAYOUT_SIZE) {
398 qCWarning(lcSvgDraw) << "Text element too wide to lay out, ignoring";
399 return false;
400 }
401
402 return true;
403}
404
405bool QSvgText::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
406{
407 return true;
408}
409
410void QSvgText::draw_helper(QPainter *p, QSvgExtraStates &states, QRectF *boundingRect) const
411{
412 const bool isPainting = (boundingRect == nullptr);
413 if (!isPainting || shouldDrawNode(p, states)) {
414 QFont font = p->font();
415 Qt::Alignment alignment = states.textAnchor;
416
417 qreal y = 0;
418 bool initial = true;
419 qreal px = m_coord.x();
420 qreal py = m_coord.y();
421
422 if (m_type == Textarea) {
423 if (alignment == Qt::AlignHCenter)
424 px += m_size.width() / 2;
425 else if (alignment == Qt::AlignRight)
426 px += m_size.width();
427 }
428
429 QRectF bounds;
430 if (m_size.height() != 0)
431 bounds = QRectF(0, py, 1, m_size.height()); // x and width are not used.
432
433 bool appendSpace = false;
434 QStringList paragraphs;
435 QList<QList<QTextLayout::FormatRange> > formatRanges(1);
436 // Parallel to 'paragraphs': the absolute position of a paragraph whose leading
437 // tspan carried x/y, or nullopt for a paragraph that flows from the previous one.
438 QList<std::optional<QPointF>> paragraphPos;
439 paragraphs.push_back(QString());
440 paragraphPos.push_back(std::nullopt);
441
442 for (int i = 0; i < m_tspans.size(); ++i) {
443 if (m_tspans[i] == LINEBREAK) {
444 // A line break starts a new paragraph. For a text area, an empty paragraph
445 // must be padded with a space so the blank line is preserved;
446 // plain text (e.g. multi-line tspans) just needs a fresh paragraph.
447 if (m_type == Textarea && paragraphs.back().isEmpty()) {
448 font.setPixelSize(font.pointSizeF());
449 font.setHintingPreference(QFont::PreferNoHinting);
450
451 QTextLayout::FormatRange range;
452 range.start = 0;
453 range.length = 1;
454 range.format.setFont(font);
455 formatRanges.back().append(range);
456
457 paragraphs.back().append(QLatin1Char(' '));;
458 }
459 appendSpace = false;
460 paragraphs.push_back(QString());
461 formatRanges.resize(formatRanges.size() + 1);
462 paragraphPos.push_back(std::nullopt);
463 } else {
464 // A tspan carrying an absolute x/y starts a new positioned run: a fresh
465 // paragraph anchored at that user-space coordinate. An unset axis falls
466 // back to the enclosing <text> element's origin.
467 if (m_tspans[i]->isTspan() && m_tspans[i]->hasPosition()) {
468 if (!paragraphs.back().isEmpty()) {
469 paragraphs.push_back(QString());
470 formatRanges.resize(formatRanges.size() + 1);
471 paragraphPos.push_back(std::nullopt);
472 }
473 appendSpace = false;
474 paragraphPos.back() = QPointF(m_tspans[i]->hasX() ? m_tspans[i]->x() : m_coord.x(),
475 m_tspans[i]->hasY() ? m_tspans[i]->y() : m_coord.y());
476 }
477 WhitespaceMode mode = m_tspans[i]->whitespaceMode();
478 m_tspans[i]->applyStyle(p, states);
479
480 font = p->font();
481 font.setPixelSize(font.pointSizeF());
482 font.setHintingPreference(QFont::PreferNoHinting);
483
484 QString newText(m_tspans[i]->text());
485 newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
486 newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
487
488 bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
489 if (appendSpace || prependSpace)
490 paragraphs.back().append(QLatin1Char(' '));
491
492 bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
493
494 if (mode == Default) {
495 newText = newText.simplified();
496 if (newText.isEmpty())
497 appendSpaceNext = false;
498 }
499
500 QTextLayout::FormatRange range;
501 range.start = paragraphs.back().size();
502 range.length = newText.size();
503 range.format.setFont(font);
504 if (p->pen().style() != Qt::NoPen && p->pen().brush() != Qt::NoBrush)
505 range.format.setTextOutline(p->pen());
506 // work around QTBUG-136696: NoBrush fills the foreground with the outline's pen
507 range.format.setForeground(p->brush().style() == Qt::NoBrush
508 ? QColor(Qt::transparent) : p->brush());
509
510 if (appendSpace) {
511 Q_ASSERT(!formatRanges.back().isEmpty());
512 ++formatRanges.back().back().length;
513 } else if (prependSpace) {
514 --range.start;
515 ++range.length;
516 }
517 formatRanges.back().append(range);
518
519 appendSpace = appendSpaceNext;
520 paragraphs.back() += newText;
521
522 m_tspans[i]->revertStyle(p, states);
523 }
524 }
525
526 if (states.svgFont) {
527 // SVG fonts not fully supported...
528 if (!m_glyphsToDraw)
529 m_glyphsToDraw = states.svgFont->toGlyphs(paragraphs.join(QLatin1Char('\n')));
530 if (isPainting) {
531 states.svgFont->draw(p, m_coord, m_glyphsToDraw.value(),
532 p->font().pointSizeF(), states.textAnchor);
533 } else {
534 *boundingRect = states.svgFont->boundingRect(p, m_coord, m_glyphsToDraw.value(),
535 p->font().pointSizeF(), states.textAnchor);
536 }
537 } else {
538 QRectF brect;
539 for (int i = 0; i < paragraphs.size(); ++i) {
540 QTextLayout tl(paragraphs[i]);
541 QTextOption op = tl.textOption();
542 op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
543 tl.setTextOption(op);
544 tl.setFormats(formatRanges[i]);
545 tl.beginLayout();
546
547 forever {
548 QTextLine line = tl.createLine();
549 if (!line.isValid())
550 break;
551 if (m_size.width() != 0)
552 line.setLineWidth(m_size.width());
553 }
554 tl.endLayout();
555
556 // A paragraph with an explicit position is drawn at that absolute user-space
557 // coordinate; otherwise it flows below the previous one from the text origin.
558 const bool positioned = paragraphPos[i].has_value();
559 const qreal originX = positioned ? paragraphPos[i]->x() : px;
560 const qreal originY = positioned ? paragraphPos[i]->y() : py;
561 qreal localY = 0; // baseline offset within a positioned paragraph
562
563 bool endOfBoundsReached = false;
564 for (int j = 0; j < tl.lineCount(); ++j) {
565 QTextLine line = tl.lineAt(j);
566
567 qreal x = 0;
568 if (alignment == Qt::AlignHCenter)
569 x -= 0.5 * line.naturalTextWidth();
570 else if (alignment == Qt::AlignRight)
571 x -= line.naturalTextWidth();
572
573 if (positioned) {
574 // The tspan's y is a baseline; QTextLine is placed by its top-left,
575 // so offset the first line up by its ascent.
576 if (j == 0)
577 localY -= line.ascent();
578 line.setPosition(QPointF(x, localY));
579 brect |= line.naturalTextRect().translated(originX, originY);
580 localY += 1.1 * line.height();
581 continue;
582 }
583
584 if (initial && m_type == Text)
585 y -= line.ascent();
586 initial = false;
587
588 line.setPosition(QPointF(x, y));
589 brect |= line.naturalTextRect().translated(originX, originY);
590
591 // Check if the current line fits into the bounding rectangle.
592 if ((m_size.width() != 0 && line.naturalTextWidth() > m_size.width())
593 || (m_size.height() != 0 && y + line.height() > m_size.height())) {
594 // I need to set the bounds height to 'y-epsilon' to avoid drawing the current
595 // line. Since the font is scaled to 100 units, 1 should be a safe epsilon.
596 bounds.setHeight(y - 1);
597 endOfBoundsReached = true;
598 break;
599 }
600
601 y += 1.1 * line.height();
602 }
603 if (isPainting)
604 tl.draw(p, QPointF(originX, originY), QList<QTextLayout::FormatRange>(), bounds);
605
606 if (endOfBoundsReached)
607 break;
608 }
609 if (boundingRect) {
610 if (bounds.height() > 0)
611 brect.setBottom(qMin(brect.bottom(), bounds.bottom()));
612 *boundingRect = brect;
613 }
614 }
615 }
616}
617
618void QSvgText::addText(QStringView text)
619{
620 m_tspans.append(new QSvgTspan(this, false));
621 m_tspans.back()->setWhitespaceMode(m_mode);
622 m_tspans.back()->addText(text);
623}
624
625QSvgTspan::~QSvgTspan()
626 = default;
627
628QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node)
629 : QSvgNode(parent), m_link(node), m_start(start), m_recursing(false)
630{
631
632}
633
635 = default;
636
637void QSvgUse::drawCommand(QPainter *p, QSvgExtraStates &states)
638{
639 if (Q_UNLIKELY(!m_link || isDescendantOf(m_link) || m_recursing))
640 return;
641
642 Q_ASSERT(states.nestedUseCount == 0 || states.nestedUseLevel > 0);
643 if (states.nestedUseLevel > 3 && states.nestedUseCount > (256 + states.nestedUseLevel * 2)) {
644 qCDebug(lcSvgDraw, "Too many nested use nodes at #%s!", qPrintable(m_linkId));
645 return;
646 }
647
648 QScopedValueRollback<bool> inUseGuard(states.inUse, true);
649
650 if (!m_start.isNull()) {
651 p->translate(m_start);
652 }
653 if (states.nestedUseLevel > 0)
654 ++states.nestedUseCount;
655 {
656 QScopedValueRollback<int> useLevelGuard(states.nestedUseLevel, states.nestedUseLevel + 1);
657 QScopedValueRollback<bool> recursingGuard(m_recursing, true);
658 m_link->draw(p, states);
659 }
660 if (states.nestedUseLevel == 0)
661 states.nestedUseCount = 0;
662
663 if (!m_start.isNull()) {
664 p->translate(-m_start);
665 }
666}
667
668QSvgNode::Type QSvgDummyNode::type() const
669{
670 return FeUnsupported;
671}
672
673
674QSvgCircle::~QSvgCircle()
675 = default;
676
677QSvgNode::Type QSvgCircle::type() const
678{
679 return Circle;
680}
681
682QSvgNode::Type QSvgEllipse::type() const
683{
684 return Ellipse;
685}
686
687QSvgNode::Type QSvgImage::type() const
688{
689 return Image;
690}
691
692QSvgNode::Type QSvgLine::type() const
693{
694 return Line;
695}
696
697QSvgNode::Type QSvgPath::type() const
698{
699 return Path;
700}
701
702QSvgNode::Type QSvgPolygon::type() const
703{
704 return Polygon;
705}
706
707QSvgNode::Type QSvgPolyline::type() const
708{
709 return Polyline;
710}
711
712QSvgNode::Type QSvgRect::type() const
713{
714 return Rect;
715}
716
717QSvgNode::Type QSvgText::type() const
718{
719 return m_type;
720}
721
723{
724 return Use;
725}
726
728 = default;
729
731{
732 return Video;
733}
734
735QRectF QSvgUse::internalBounds(QPainter *p, QSvgExtraStates &states) const
736{
737 QRectF bounds;
738 if (Q_LIKELY(m_link && !isDescendantOf(m_link) && !m_recursing)) {
739 QScopedValueRollback<bool> guard(m_recursing, true);
740 p->translate(m_start);
741 bounds = m_link->bounds(p, states);
742 p->translate(-m_start);
743 }
744 return bounds;
745}
746
747QRectF QSvgUse::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states) const
748{
749 QRectF bounds;
750 if (Q_LIKELY(m_link && !isDescendantOf(m_link) && !m_recursing)) {
751 QScopedValueRollback<bool> guard(m_recursing, true);
752 p->translate(m_start);
753 bounds = m_link->decoratedBounds(p, states);
754 p->translate(-m_start);
755 }
756 return bounds;
757}
758
759QRectF QSvgPolyline::internalFastBounds(QPainter *p, QSvgExtraStates &) const
760{
761 return p->transform().mapRect(m_poly.boundingRect());
762}
763
764QRectF QSvgPolyline::internalBounds(QPainter *p, QSvgExtraStates &s) const
765{
766 return internalBounds(p, s, BoundsMode::Simplistic);
767}
768
769QRectF QSvgPolyline::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
770{
771 QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
772 rect |= QSvgMarker::markersBoundsForNode(this, p, s);
773 return filterRegion(s.doc(), rect);
774}
775
776bool QSvgPolyline::requiresGroupRendering() const
777{
778 return hasAnyMarker();
779}
780
781QRectF QSvgPolyline::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
782{
783 qreal sw = strokeWidth(p);
784 if (qFuzzyIsNull(sw)) {
785 return p->transform().map(m_poly).boundingRect();
786 } else {
787 QPainterPath path;
788 path.addPolygon(m_poly);
789 return boundsOnStroke(p, path, sw, mode);
790 }
791}
792
793QRectF QSvgImage::internalBounds(QPainter *p, QSvgExtraStates &) const
794{
795 return p->transform().mapRect(m_bounds);
796}
797
798QRectF QSvgLine::internalFastBounds(QPainter *p, QSvgExtraStates &) const
799{
800 QPointF p1 = p->transform().map(m_line.p1());
801 QPointF p2 = p->transform().map(m_line.p2());
802 qreal minX = qMin(p1.x(), p2.x());
803 qreal minY = qMin(p1.y(), p2.y());
804 qreal maxX = qMax(p1.x(), p2.x());
805 qreal maxY = qMax(p1.y(), p2.y());
806 return QRectF(minX, minY, maxX - minX, maxY - minY);
807}
808
809QRectF QSvgLine::internalBounds(QPainter *p, QSvgExtraStates &s) const
810{
811 return internalBounds(p, s, BoundsMode::Simplistic);
812}
813
814QRectF QSvgLine::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
815{
816 QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
817 rect |= QSvgMarker::markersBoundsForNode(this, p, s);
818 return filterRegion(s.doc(), rect);
819}
820
821bool QSvgLine::requiresGroupRendering() const
822{
823 return hasAnyMarker();
824}
825
826QRectF QSvgLine::internalBounds(QPainter *p, QSvgExtraStates &s, BoundsMode mode) const
827{
828 qreal sw = strokeWidth(p);
829 if (qFuzzyIsNull(sw)) {
830 return internalFastBounds(p, s);
831 } else {
832 QPainterPath path;
833 path.moveTo(m_line.p1());
834 path.lineTo(m_line.p2());
835 return boundsOnStroke(p, path, sw, mode);
836 }
837}
838
839QT_END_NAMESPACE
friend class QPainter
QRectF decoratedInternalBounds(QPainter *p, QSvgExtraStates &states) const override
void drawCommand(QPainter *p, QSvgExtraStates &states) override
~QSvgUse() override
QRectF internalBounds(QPainter *p, QSvgExtraStates &states) const override
QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *link)
Type type() const override
~QSvgVideo() override
Type type() const override
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define qPrintable(string)
Definition qstring.h:1718
#define QT_SVG_MAX_LAYOUT_SIZE