Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsvgvisitorimpl.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 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
4#include "qsvgvisitorimpl_p.h"
5#include "qquickgenerator_p.h"
6#include "qquicknodeinfo_p.h"
7
8#include <private/qsvgvisitor_p.h>
9
10#include <QString>
11#include <QPainter>
12#include <QTextDocument>
13#include <QTextLayout>
14#include <QMatrix4x4>
15#include <QQuickItem>
16
17#include <private/qquickshape_p.h>
18#include <private/qquicktext_p.h>
19#include <private/qquicktranslate_p.h>
20#include <private/qquickitem_p.h>
21
22#include <private/qquickimagebase_p_p.h>
23#include <private/qquickimage_p.h>
24#include <private/qsgcurveprocessor_p.h>
25
26#include <private/qquadpath_p.h>
27
28#include <QtCore/private/qstringiterator_p.h>
29
30#include "utils_p.h"
31#include <QtCore/qloggingcategory.h>
32
33#include <QtSvg/private/qsvgstyle_p.h>
34
36
37using namespace Qt::StringLiterals;
38
40{
41public:
51
56
59
61 {
65 }
66
67 QColor fillColor;
68 fillColor = m_dummyPainter.brush().color();
70
71 return fillColor;
72 }
73
75 {
77 }
78
80 {
82 if (brush.style() == Qt::LinearGradientPattern
84 || brush.style() == Qt::ConicalGradientPattern) {
85 return brush.gradient();
86 }
87 return nullptr;
88 }
89
96
101
103 {
107 }
108
109 QColor strokeColor;
110 strokeColor = m_dummyPainter.pen().brush().color();
112
113 return strokeColor;
114 }
115
116 static QGradient applyOpacityToGradient(const QGradient &gradient, float opacity)
117 {
118 QGradient grad = gradient;
119 QGradientStops stops;
120 for (auto &stop : grad.stops()) {
121 stop.second.setAlphaF(stop.second.alphaF() * opacity);
122 stops.append(stop);
123 }
124
125 grad.setStops(stops);
126
127 return grad;
128 }
129
130 float currentStrokeWidth() const
131 {
132 float penWidth = m_dummyPainter.pen().widthF();
133 return penWidth ? penWidth : 1;
134 }
135
137 {
138 return m_dummyPainter.pen();
139 }
140
141protected:
145};
146
148
149namespace {
150inline bool isPathContainer(const QSvgStructureNode *node)
151{
152 bool foundPath = false;
153 for (const auto *child : node->renderers()) {
154 switch (child->type()) {
155 // nodes that shouldn't go inside Shape{}
156 case QSvgNode::Switch:
157 case QSvgNode::Doc:
158 case QSvgNode::Group:
160 case QSvgNode::Use:
161 case QSvgNode::Video:
162 //qCDebug(lcQuickVectorGraphics) << "NOT path container because" << node->typeName() ;
163 return false;
164
165 // nodes that could go inside Shape{}
166 case QSvgNode::Defs:
167 case QSvgNode::Image:
169 case QSvgNode::Text:
170 case QSvgNode::Tspan:
171 break;
172
173 // nodes that are done as pure ShapePath{}
174 case QSvgNode::Rect:
175 case QSvgNode::Circle:
177 case QSvgNode::Line:
178 case QSvgNode::Path:
181 if (!child->style().transform.isDefault()) {
182 //qCDebug(lcQuickVectorGraphics) << "NOT path container because local transform";
183 return false;
184 }
185 foundPath = true;
186 break;
187 default:
188 qCDebug(lcQuickVectorImage) << "Unhandled type in switch" << child->type();
189 break;
190 }
191 }
192 //qCDebug(lcQuickVectorGraphics) << "Container" << node->nodeId() << node->typeName() << "is" << foundPath;
193 return foundPath;
194}
195
196static QString capStyleName(Qt::PenCapStyle style)
197{
198 QString styleName;
199
200 switch (style) {
201 case Qt::SquareCap:
202 styleName = QStringLiteral("squarecap");
203 break;
204 case Qt::FlatCap:
205 styleName = QStringLiteral("flatcap");
206 break;
207 case Qt::RoundCap:
208 styleName = QStringLiteral("roundcap");
209 break;
210 default:
211 break;
212 }
213
214 return styleName;
215}
216
217static QString joinStyleName(Qt::PenJoinStyle style)
218{
219 QString styleName;
220
221 switch (style) {
222 case Qt::MiterJoin:
223 styleName = QStringLiteral("miterjoin");
224 break;
225 case Qt::BevelJoin:
226 styleName = QStringLiteral("beveljoin");
227 break;
228 case Qt::RoundJoin:
229 styleName = QStringLiteral("roundjoin");
230 break;
231 case Qt::SvgMiterJoin:
232 styleName = QStringLiteral("svgmiterjoin");
233 break;
234 default:
235 break;
236 }
237
238 return styleName;
239}
240
241static QString dashArrayString(QList<qreal> dashArray)
242{
243 if (dashArray.isEmpty())
244 return QString();
245
246 QString dashArrayString;
247 QTextStream stream(&dashArrayString);
248
249 for (int i = 0; i < dashArray.length() - 1; i++) {
251 stream << value << ", ";
252 }
253
254 stream << dashArray.last();
255
256 return dashArrayString;
257}
258};
259
261 : m_svgFileName(svgFileName)
262 , m_generator(generator)
263{
264}
265
267{
268 if (!m_generator) {
269 qCDebug(lcQuickVectorImage) << "No valid QQuickGenerator is set. Genration will stop";
270 return false;
271 }
272
273 auto *doc = QSvgTinyDocument::load(m_svgFileName);
274 if (!doc) {
275 qCDebug(lcQuickVectorImage) << "Not a valid Svg File : " << m_svgFileName;
276 return false;
277 }
278
280 return true;
281}
282
284{
285 handleBaseNodeSetup(node);
286
288 fillCommonNodeInfo(node, info);
289
290 m_generator->generateNode(info);
291
292 handleBaseNodeEnd(node);
293}
294
296{
297 // TODO: this requires proper asset management.
298 handleBaseNodeSetup(node);
299
301 fillCommonNodeInfo(node, info);
302 info.image = node->image();
303 info.rect = node->rect();
304 info.externalFileReference = node->filename();
305
306 m_generator->generateImageNode(info);
307
308 handleBaseNodeEnd(node);
309}
310
312{
313 QRectF rect = node->rect();
314 QPointF rads = node->radius();
315 // This is using Qt::RelativeSize semantics: percentage of half rect size
316 qreal x1 = rect.left();
317 qreal x2 = rect.right();
318 qreal y1 = rect.top();
319 qreal y2 = rect.bottom();
320
321 qreal rx = rads.x() * rect.width() / 200;
322 qreal ry = rads.y() * rect.height() / 200;
324
325 p.moveTo(x1 + rx, y1);
326 p.lineTo(x2 - rx, y1);
327 // qCDebug(lcQuickVectorGraphics) << "Line1" << x2 - rx << y1;
328 p.arcTo(x2 - rx * 2, y1, rx * 2, ry * 2, 90, -90); // ARC to x2, y1 + ry
329 // qCDebug(lcQuickVectorGraphics) << "p1" << p;
330
331 p.lineTo(x2, y2 - ry);
332 p.arcTo(x2 - rx * 2, y2 - ry * 2, rx * 2, ry * 2, 0, -90); // ARC to x2 - rx, y2
333
334 p.lineTo(x1 + rx, y2);
335 p.arcTo(x1, y2 - ry * 2, rx * 2, ry * 2, 270, -90); // ARC to x1, y2 - ry
336
337 p.lineTo(x1, y1 + ry);
338 p.arcTo(x1, y1, rx * 2, ry * 2, 180, -90); // ARC to x1 + rx, y1
339
340 handlePathNode(node, p);
341}
342
344{
345 QRectF rect = node->rect();
346
349
350 handlePathNode(node, p);
351}
352
354{
355 handlePathNode(node, node->path());
356}
357
359{
361 p.moveTo(node->line().p1());
362 p.lineTo(node->line().p2());
363 handlePathNode(node, p);
364}
365
367{
369 handlePathNode(node, p);
370}
371
373{
375 handlePathNode(node, p);
376}
377
378QString QSvgVisitorImpl::gradientCssDescription(const QGradient *gradient)
379{
380 QString cssDescription;
381 if (gradient->type() == QGradient::LinearGradient) {
382 const QLinearGradient *linearGradient = static_cast<const QLinearGradient *>(gradient);
383
384 cssDescription += " -qt-foreground: qlineargradient("_L1;
385 cssDescription += "x1:"_L1 + QString::number(linearGradient->start().x()) + u',';
386 cssDescription += "y1:"_L1 + QString::number(linearGradient->start().y()) + u',';
387 cssDescription += "x2:"_L1 + QString::number(linearGradient->finalStop().x()) + u',';
388 cssDescription += "y2:"_L1 + QString::number(linearGradient->finalStop().y()) + u',';
389 } else if (gradient->type() == QGradient::RadialGradient) {
390 const QRadialGradient *radialGradient = static_cast<const QRadialGradient *>(gradient);
391
392 cssDescription += " -qt-foreground: qradialgradient("_L1;
393 cssDescription += "cx:"_L1 + QString::number(radialGradient->center().x()) + u',';
394 cssDescription += "cy:"_L1 + QString::number(radialGradient->center().y()) + u',';
395 cssDescription += "fx:"_L1 + QString::number(radialGradient->focalPoint().x()) + u',';
396 cssDescription += "fy:"_L1 + QString::number(radialGradient->focalPoint().y()) + u',';
397 cssDescription += "radius:"_L1 + QString::number(radialGradient->radius()) + u',';
398 } else {
399 const QConicalGradient *conicalGradient = static_cast<const QConicalGradient *>(gradient);
400
401 cssDescription += " -qt-foreground: qconicalgradient("_L1;
402 cssDescription += "cx:"_L1 + QString::number(conicalGradient->center().x()) + u',';
403 cssDescription += "cy:"_L1 + QString::number(conicalGradient->center().y()) + u',';
404 cssDescription += "angle:"_L1 + QString::number(conicalGradient->angle()) + u',';
405 }
406
407 const QStringList coordinateModes = { "logical"_L1, "stretchtodevice"_L1, "objectbounding"_L1, "object"_L1 };
408 cssDescription += "coordinatemode:"_L1;
409 cssDescription += coordinateModes.at(int(gradient->coordinateMode()));
410 cssDescription += u',';
411
412 const QStringList spreads = { "pad"_L1, "reflect"_L1, "repeat"_L1 };
413 cssDescription += "spread:"_L1;
414 cssDescription += spreads.at(int(gradient->spread()));
415
416 for (const QGradientStop &stop : gradient->stops()) {
417 cssDescription += ",stop:"_L1;
418 cssDescription += QString::number(stop.first);
419 cssDescription += u' ';
420 cssDescription += stop.second.name(QColor::HexArgb);
421 }
422
423 cssDescription += ");"_L1;
424
425 return cssDescription;
426}
427
428QString QSvgVisitorImpl::colorCssDescription(QColor color)
429{
430 QString cssDescription;
431 cssDescription += QStringLiteral("rgba(");
432 cssDescription += QString::number(color.red()) + QStringLiteral(",");
433 cssDescription += QString::number(color.green()) + QStringLiteral(",");
434 cssDescription += QString::number(color.blue()) + QStringLiteral(",");
435 cssDescription += QString::number(color.alphaF()) + QStringLiteral(")");
436
437 return cssDescription;
438}
439
440namespace {
441
442 // Simple class for representing the SVG font as a font engine
443 // We use the Proxy font engine type, which is currently unused and does not map to
444 // any specific font engine
445 // (The QSvgFont object must outlive the engine.)
446 class QSvgFontEngine : public QFontEngine
447 {
448 public:
449 QSvgFontEngine(const QSvgFont *font, qreal size);
450
451 QFontEngine *cloneWithSize(qreal size) const override;
452
453 glyph_t glyphIndex(uint ucs4) const override;
454 int stringToCMap(const QChar *str,
455 int len,
456 QGlyphLayout *glyphs,
457 int *nglyphs,
458 ShaperFlags flags) const override;
459
460 void addGlyphsToPath(glyph_t *glyphs,
462 int nGlyphs,
464 QTextItem::RenderFlags flags) override;
465
466 glyph_metrics_t boundingBox(glyph_t glyph) override;
467
468 void recalcAdvances(QGlyphLayout *, ShaperFlags) const override;
469 QFixed ascent() const override;
470 QFixed capHeight() const override;
471 QFixed descent() const override;
472 QFixed leading() const override;
473 qreal maxCharWidth() const override;
474 qreal minLeftBearing() const override;
475 qreal minRightBearing() const override;
476
477 QFixed emSquareSize() const override;
478
479 private:
480 const QSvgFont *m_font;
481 };
482
483 QSvgFontEngine::QSvgFontEngine(const QSvgFont *font, qreal size)
484 : QFontEngine(Proxy)
485 , m_font(font)
486 {
487 fontDef.pixelSize = size;
488 fontDef.families = QStringList(m_font->m_familyName);
489 }
490
491 QFixed QSvgFontEngine::emSquareSize() const
492 {
493 return QFixed::fromReal(m_font->m_unitsPerEm);
494 }
495
496 glyph_t QSvgFontEngine::glyphIndex(uint ucs4) const
497 {
498 if (ucs4 < USHRT_MAX && m_font->m_glyphs.contains(QChar(ushort(ucs4))))
499 return glyph_t(ucs4);
500
501 return 0;
502 }
503
504 int QSvgFontEngine::stringToCMap(const QChar *str,
505 int len,
506 QGlyphLayout *glyphs,
507 int *nglyphs,
508 ShaperFlags flags) const
509 {
510 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
511 if (*nglyphs < len) {
512 *nglyphs = len;
513 return -1;
514 }
515
516 int ucs4Length = 0;
518 while (it.hasNext()) {
519 char32_t ucs4 = it.next();
520 glyph_t index = glyphIndex(ucs4);
521 glyphs->glyphs[ucs4Length++] = index;
522 }
523
524 *nglyphs = ucs4Length;
525 glyphs->numGlyphs = ucs4Length;
526
527 if (!(flags & GlyphIndicesOnly))
528 recalcAdvances(glyphs, flags);
529
530 return *nglyphs;
531 }
532
533 void QSvgFontEngine::addGlyphsToPath(glyph_t *glyphs,
535 int nGlyphs,
537 QTextItem::RenderFlags flags)
538 {
540 const qreal scale = fontDef.pixelSize / m_font->m_unitsPerEm;
541 for (int i = 0; i < nGlyphs; ++i) {
542 glyph_t index = glyphs[i];
543 if (index > 0) {
544 QPointF position = positions[i].toPointF();
545 QPainterPath glyphPath = m_font->m_glyphs.value(QChar(ushort(index))).m_path;
546
550 glyphPath = xform.map(glyphPath);
551 path->addPath(glyphPath);
552 }
553 }
554 }
555
556 glyph_metrics_t QSvgFontEngine::boundingBox(glyph_t glyph)
557 {
559 ret.x = 0; // left bearing
560 ret.y = -ascent();
561 const qreal scale = fontDef.pixelSize / m_font->m_unitsPerEm;
562 const QSvgGlyph &svgGlyph = m_font->m_glyphs.value(QChar(ushort(glyph)));
563 ret.width = QFixed::fromReal(svgGlyph.m_horizAdvX * scale);
564 ret.height = ascent() + descent();
565 return ret;
566 }
567
568 QFontEngine *QSvgFontEngine::cloneWithSize(qreal size) const
569 {
570 QSvgFontEngine *otherEngine = new QSvgFontEngine(m_font, size);
571 return otherEngine;
572 }
573
574 void QSvgFontEngine::recalcAdvances(QGlyphLayout *glyphLayout, ShaperFlags) const
575 {
576 const qreal scale = fontDef.pixelSize / m_font->m_unitsPerEm;
577 for (int i = 0; i < glyphLayout->numGlyphs; i++) {
578 glyph_t glyph = glyphLayout->glyphs[i];
579 const QSvgGlyph &svgGlyph = m_font->m_glyphs.value(QChar(ushort(glyph)));
580 glyphLayout->advances[i] = QFixed::fromReal(svgGlyph.m_horizAdvX * scale);
581 }
582 }
583
584 QFixed QSvgFontEngine::ascent() const
585 {
586 return QFixed::fromReal(fontDef.pixelSize);
587 }
588
589 QFixed QSvgFontEngine::capHeight() const
590 {
591 return ascent();
592 }
593 QFixed QSvgFontEngine::descent() const
594 {
595 return QFixed{};
596 }
597
598 QFixed QSvgFontEngine::leading() const
599 {
600 return QFixed{};
601 }
602
603 qreal QSvgFontEngine::maxCharWidth() const
604 {
605 const qreal scale = fontDef.pixelSize / m_font->m_unitsPerEm;
606 return m_font->m_horizAdvX * scale;
607 }
608
609 qreal QSvgFontEngine::minLeftBearing() const
610 {
611 return 0.0;
612 }
613
614 qreal QSvgFontEngine::minRightBearing() const
615 {
616 return 0.0;
617 }
618}
619
621{
622 handleBaseNodeSetup(node);
623 const bool isTextArea = node->type() == QSvgNode::Textarea;
624
626 const QSvgFont *svgFont = styleResolver->states().svgFont;
627 bool needsRichText = false;
628 bool preserveWhiteSpace = node->whitespaceMode() == QSvgText::Preserve;
629 const QGradient *mainGradient = styleResolver->currentFillGradient();
630
631 QFontEngine *fontEngine = nullptr;
632 if (svgFont != nullptr) {
633 fontEngine = new QSvgFontEngine(svgFont, styleResolver->painter().font().pointSize());
634 fontEngine->ref.ref();
635 }
636
637#if QT_CONFIG(texthtmlparser)
638 bool needsPathNode = mainGradient != nullptr
639 || svgFont != nullptr
640 || styleResolver->currentStrokeGradient() != nullptr;
641#endif
642 for (const auto *tspan : node->tspans()) {
643 if (!tspan) {
644 text += QStringLiteral("<br>");
645 continue;
646 }
647
648 // Note: We cannot get the font directly from the style, since this does
649 // not apply the weight, since this is relative and depends on current state.
650 handleBaseNodeSetup(tspan);
651 QFont font = styleResolver->painter().font();
652
653 QString styleTagContent;
654
657 styleTagContent += QStringLiteral("font-family: %1;").arg(font.family());
658 }
659
662 && font.weight() != QFont::Bold) {
663 styleTagContent += QStringLiteral("font-weight: %1;").arg(int(font.weight()));
664 }
665
667 // Pixel size stored as point size in SVG parser
668 styleTagContent += QStringLiteral("font-size: %1px;").arg(int(font.pointSizeF()));
669 }
670
673 styleTagContent += QStringLiteral("font-variant: small-caps;");
674 }
675
676 if (styleResolver->currentFillGradient() != nullptr
677 && styleResolver->currentFillGradient() != mainGradient) {
678 const QGradient grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
679 styleTagContent += gradientCssDescription(&grad) + u';';
680#if QT_CONFIG(texthtmlparser)
681 needsPathNode = true;
682#endif
683 }
684
685 const QColor currentStrokeColor = styleResolver->currentStrokeColor();
686 if (currentStrokeColor.alpha() > 0) {
687 QString strokeColor = colorCssDescription(currentStrokeColor);
688 styleTagContent += QStringLiteral("-qt-stroke-color:%1;").arg(strokeColor);
689 styleTagContent += QStringLiteral("-qt-stroke-width:%1px;").arg(styleResolver->currentStrokeWidth());
690 styleTagContent += QStringLiteral("-qt-stroke-dasharray:%1;").arg(dashArrayString(styleResolver->currentStroke().dashPattern()));
691 styleTagContent += QStringLiteral("-qt-stroke-dashoffset:%1;").arg(styleResolver->currentStroke().dashOffset());
692 styleTagContent += QStringLiteral("-qt-stroke-lineCap:%1;").arg(capStyleName(styleResolver->currentStroke().capStyle()));
693 styleTagContent += QStringLiteral("-qt-stroke-lineJoin:%1;").arg(joinStyleName(styleResolver->currentStroke().joinStyle()));
694 if (styleResolver->currentStroke().joinStyle() == Qt::MiterJoin || styleResolver->currentStroke().joinStyle() == Qt::SvgMiterJoin)
695 styleTagContent += QStringLiteral("-qt-stroke-miterlimit:%1;").arg(styleResolver->currentStroke().miterLimit());
696#if QT_CONFIG(texthtmlparser)
697 needsPathNode = true;
698#endif
699 }
700
701 if (tspan->whitespaceMode() == QSvgText::Preserve && !preserveWhiteSpace)
702 styleTagContent += QStringLiteral("white-space: pre-wrap;");
703
704 QString content = tspan->text().toHtmlEscaped();
705 content.replace(QLatin1Char('\t'), QLatin1Char(' '));
706 content.replace(QLatin1Char('\n'), QLatin1Char(' '));
707
708 bool fontTag = false;
709 if (!tspan->style().fill.isDefault()) {
710 auto &b = tspan->style().fill->qbrush();
711 qCDebug(lcQuickVectorImage) << "tspan FILL:" << b;
712 if (b.style() != Qt::NoBrush)
713 {
714 if (qFuzzyCompare(b.color().alphaF() + 1.0, 2.0))
715 {
716 QString spanColor = b.color().name();
717 fontTag = !spanColor.isEmpty();
718 if (fontTag)
719 text += QStringLiteral("<font color=\"%1\">").arg(spanColor);
720 } else {
721 QString spanColor = colorCssDescription(b.color());
722 styleTagContent += QStringLiteral("color:%1").arg(spanColor);
723 }
724 }
725 }
726
727 needsRichText = needsRichText || !styleTagContent.isEmpty();
728 if (!styleTagContent.isEmpty())
729 text += QStringLiteral("<span style=\"%1\">").arg(styleTagContent);
730
732 text += QStringLiteral("<b>");
733
735 text += QStringLiteral("<i>");
736
738 switch (font.capitalization()) {
740 content = content.toLower();
741 break;
743 content = content.toUpper();
744 break;
746 // ### We need to iterate over the string and do the title case conversion,
747 // since this is not part of QString.
748 qCWarning(lcQuickVectorImage) << "Title case not implemented for tspan";
749 break;
750 default:
751 break;
752 }
753 }
754 text += content;
755 if (fontTag)
756 text += QStringLiteral("</font>");
757
759 text += QStringLiteral("</i>");
760
762 text += QStringLiteral("</b>");
763
764 if (!styleTagContent.isEmpty())
765 text += QStringLiteral("</span>");
766
767 handleBaseNodeEnd(tspan);
768 }
769
770 if (preserveWhiteSpace && (needsRichText || styleResolver->currentFillGradient() != nullptr))
771 text = QStringLiteral("<span style=\"white-space: pre-wrap\">") + text + QStringLiteral("</span>");
772
773 QFont font = styleResolver->painter().font();
774 if (font.pixelSize() <= 0 && font.pointSize() > 0)
775 font.setPixelSize(font.pointSize()); // Pixel size stored as point size by SVG parser
776
777#if QT_CONFIG(texthtmlparser)
778 if (needsPathNode) {
779 QTextDocument document;
780 document.setHtml(text);
781 if (isTextArea && node->size().width() > 0)
782 document.setTextWidth(node->size().width());
783 document.setDefaultFont(font);
784 document.pageCount(); // Force layout
785
786 QTextBlock block = document.firstBlock();
787 while (block.isValid()) {
788 QTextLayout *lout = block.layout();
789
790 if (lout != nullptr) {
791 // If this block has requested the current SVG font, we override it
792 // (note that this limits the text to one svg font, but this is also the case
793 // in the QPainter at the moment, and needs a more centralized solution in Qt Svg
794 // first)
795 QFont blockFont = block.charFormat().font();
796 if (svgFont != nullptr
797 && blockFont.family() == svgFont->m_familyName) {
798 QRawFont rawFont;
799 QRawFontPrivate *rawFontD = QRawFontPrivate::get(rawFont);
800 rawFontD->setFontEngine(fontEngine->cloneWithSize(blockFont.pixelSize()));
801
802 lout->setRawFont(rawFont);
803 }
804
805 auto addPathForFormat = [&](QPainterPath p, QTextCharFormat fmt) {
807 fillCommonNodeInfo(node, info);
808 auto fillStyle = node->style().fill;
809 if (fillStyle)
810 info.fillRule = fillStyle->fillRule();
811
812 if (fmt.hasProperty(QTextCharFormat::ForegroundBrush)) {
813 info.fillColor = fmt.foreground().color();
814 if (fmt.foreground().gradient() != nullptr && fmt.foreground().gradient()->type() != QGradient::NoGradient)
815 info.grad = *fmt.foreground().gradient();
816 } else {
817 info.fillColor = styleResolver->currentFillColor();
818 }
819
820 info.painterPath = p;
821
822 const QGradient *strokeGradient = styleResolver->currentStrokeGradient();
823 QPen pen;
824 if (fmt.hasProperty(QTextCharFormat::TextOutline)) {
825 pen = fmt.textOutline();
826 if (strokeGradient == nullptr) {
827 info.strokeStyle = StrokeStyle::fromPen(pen);
828 info.strokeStyle.color = pen.color();
829 }
830 } else {
831 pen = styleResolver->currentStroke();
832 if (strokeGradient == nullptr) {
833 info.strokeStyle = StrokeStyle::fromPen(pen);
834 info.strokeStyle.color = styleResolver->currentStrokeColor();
835 }
836 }
837
838 if (info.grad.type() == QGradient::NoGradient && styleResolver->currentFillGradient() != nullptr)
839 info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
840
841 info.fillTransform = styleResolver->currentFillTransform();
842
843 m_generator->generatePath(info);
844
845 if (strokeGradient != nullptr) {
846 PathNodeInfo strokeInfo;
847 fillCommonNodeInfo(node, strokeInfo);
848
849 strokeInfo.grad = *strokeGradient;
850
851 QPainterPathStroker stroker(pen);
852 strokeInfo.painterPath = stroker.createStroke(p);
853 m_generator->generatePath(strokeInfo);
854 }
855 };
856
857 qreal baselineOffset = -QFontMetricsF(font).ascent();
858 if (lout->lineCount() > 0 && lout->lineAt(0).isValid())
859 baselineOffset = -lout->lineAt(0).ascent();
860
861 const QPointF baselineTranslation(0.0, baselineOffset);
862 auto glyphsToPath = [&](QList<QGlyphRun> glyphRuns, qreal width) {
863 QList<QPainterPath> paths;
864 for (const QGlyphRun &glyphRun : glyphRuns) {
865 QRawFont font = glyphRun.rawFont();
866 QList<quint32> glyphIndexes = glyphRun.glyphIndexes();
867 QList<QPointF> positions = glyphRun.positions();
868
869 for (qsizetype j = 0; j < glyphIndexes.size(); ++j) {
870 quint32 glyphIndex = glyphIndexes.at(j);
871 const QPointF &pos = positions.at(j);
872
873 QPainterPath p = font.pathForGlyph(glyphIndex);
874 p.translate(pos + node->position() + baselineTranslation);
875 if (styleResolver->states().textAnchor == Qt::AlignHCenter)
876 p.translate(QPointF(-0.5 * width, 0));
877 else if (styleResolver->states().textAnchor == Qt::AlignRight)
878 p.translate(QPointF(-width, 0));
879 paths.append(p);
880 }
881 }
882
883 return paths;
884 };
885
886 QList<QTextLayout::FormatRange> formats = block.textFormats();
887 for (int i = 0; i < formats.size(); ++i) {
889
890 QList<QGlyphRun> glyphRuns = lout->glyphRuns(range.start, range.length);
891 QList<QPainterPath> paths = glyphsToPath(glyphRuns, lout->minimumWidth());
892 for (const QPainterPath &path : paths)
893 addPathForFormat(path, range.format);
894 }
895 }
896
897 block = block.next();
898 }
899 } else
900#endif
901 {
903 fillCommonNodeInfo(node, info);
904
905 info.position = node->position();
906 info.size = node->size();
907 info.font = font;
908 info.text = text;
909 info.isTextArea = isTextArea;
910 info.needsRichText = needsRichText;
911 info.fillColor = styleResolver->currentFillColor();
912 info.alignment = styleResolver->states().textAnchor;
913 info.strokeColor = styleResolver->currentStrokeColor();
914
915 m_generator->generateTextNode(info);
916 }
917
918 handleBaseNodeEnd(node);
919
920 if (fontEngine != nullptr) {
921 fontEngine->ref.deref();
922 Q_ASSERT(fontEngine->ref.loadRelaxed() == 0);
923 delete fontEngine;
924 }
925}
926
928{
929 QSvgNode *link = node->link();
930 if (!link)
931 return;
932
933 handleBaseNodeSetup(node);
935 fillCommonNodeInfo(node, info);
936
938 info.startPos = node->start();
939
940 m_generator->generateUseNode(info);
941
943
945 m_generator->generateUseNode(info);
946 handleBaseNodeEnd(node);
947}
948
950{
951 Q_UNUSED(node)
952
953 return m_generator->generateDefsNode(NodeInfo{});
954}
955
957{
958 constexpr bool forceSeparatePaths = false;
959 handleBaseNodeSetup(node);
960
962
963 fillCommonNodeInfo(node, info);
964 info.forceSeparatePaths = forceSeparatePaths;
965 info.isPathContainer = isPathContainer(node);
967
968 return m_generator->generateStructureNode(info);
969}
970
972{
973 handleBaseNodeEnd(node);
974 // qCDebug(lcQuickVectorGraphics) << "REVERT" << node->nodeId() << node->type() << (m_styleResolver->painter().pen().style() != Qt::NoPen) << m_styleResolver->painter().pen().color().name()
975 // << (m_styleResolver->painter().pen().brush().style() != Qt::NoBrush) << m_styleResolver->painter().pen().brush().color().name();
976
978 fillCommonNodeInfo(node, info);
980
981 m_generator->generateStructureNode(info);
982}
983
985{
986 handleBaseNodeSetup(node);
987
989 fillCommonNodeInfo(node, info);
990
991 const QSvgTinyDocument *doc = static_cast<const QSvgTinyDocument *>(node);
992 info.size = doc->size();
993 info.viewBox = doc->viewBox();
994 info.isPathContainer = isPathContainer(node);
996
997 return m_generator->generateRootNode(info);
998}
999
1001{
1002 handleBaseNodeEnd(node);
1003 qCDebug(lcQuickVectorImage) << "REVERT" << node->nodeId() << node->type() << (styleResolver->painter().pen().style() != Qt::NoPen)
1004 << styleResolver->painter().pen().color().name() << (styleResolver->painter().pen().brush().style() != Qt::NoBrush)
1005 << styleResolver->painter().pen().brush().color().name();
1006
1008 fillCommonNodeInfo(node, info);
1010
1011 m_generator->generateRootNode(info);
1012}
1013
1014void QSvgVisitorImpl::fillCommonNodeInfo(const QSvgNode *node, NodeInfo &info)
1015{
1016 info.nodeId = node->nodeId();
1017 info.typeName = node->typeName();
1018 info.isDefaultTransform = node->style().transform.isDefault();
1019 info.transform = !info.isDefaultTransform ? node->style().transform->qtransform() : QTransform();
1020 info.isDefaultOpacity = node->style().opacity.isDefault();
1021 info.opacity = !info.isDefaultOpacity ? node->style().opacity->opacity() : 1.0;
1022 info.isVisible = node->isVisible();
1023 info.isDisplayed = node->displayMode() != QSvgNode::DisplayMode::NoneMode;
1024}
1025
1026void QSvgVisitorImpl::handleBaseNodeSetup(const QSvgNode *node)
1027{
1028 qCDebug(lcQuickVectorImage) << "Before SETUP" << node << "fill" << styleResolver->currentFillColor()
1029 << "stroke" << styleResolver->currentStrokeColor() << styleResolver->currentStrokeWidth()
1030 << node->nodeId() << " type: " << node->typeName() << " " << node->type();
1031
1032 node->applyStyle(&styleResolver->painter(), styleResolver->states());
1033
1034 qCDebug(lcQuickVectorImage) << "After SETUP" << node << "fill" << styleResolver->currentFillColor()
1035 << "stroke" << styleResolver->currentStrokeColor()
1036 << styleResolver->currentStrokeWidth() << node->nodeId();
1037}
1038
1039void QSvgVisitorImpl::handleBaseNode(const QSvgNode *node)
1040{
1041 NodeInfo info;
1042 fillCommonNodeInfo(node, info);
1043
1044 m_generator->generateNodeBase(info);
1045}
1046
1047void QSvgVisitorImpl::handleBaseNodeEnd(const QSvgNode *node)
1048{
1049 node->revertStyle(&styleResolver->painter(), styleResolver->states());
1050
1051 qCDebug(lcQuickVectorImage) << "After END" << node << "fill" << styleResolver->currentFillColor()
1052 << "stroke" << styleResolver->currentStrokeColor() << styleResolver->currentStrokeWidth()
1053 << node->nodeId();
1054}
1055
1056void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &path)
1057{
1058 handleBaseNodeSetup(node);
1059
1061 fillCommonNodeInfo(node, info);
1062 auto fillStyle = node->style().fill;
1063 if (fillStyle)
1064 info.fillRule = fillStyle->fillRule();
1065
1066 const QGradient *strokeGradient = styleResolver->currentStrokeGradient();
1067
1068 info.painterPath = path;
1069 info.fillColor = styleResolver->currentFillColor();
1070 if (strokeGradient == nullptr) {
1071 info.strokeStyle = StrokeStyle::fromPen(styleResolver->currentStroke());
1072 info.strokeStyle.color = styleResolver->currentStrokeColor();
1073 }
1074 if (styleResolver->currentFillGradient() != nullptr)
1075 info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
1076 info.fillTransform = styleResolver->currentFillTransform();
1077
1078 m_generator->generatePath(info);
1079
1080 if (strokeGradient != nullptr) {
1081 PathNodeInfo strokeInfo;
1082 fillCommonNodeInfo(node, strokeInfo);
1083
1084 strokeInfo.grad = *strokeGradient;
1085
1086 QPainterPathStroker stroker(styleResolver->currentStroke());
1087 strokeInfo.painterPath = stroker.createStroke(path);
1088 m_generator->generatePath(strokeInfo);
1089 }
1090
1091 handleBaseNodeEnd(node);
1092}
1093
bool ref() noexcept
bool deref() noexcept
T loadRelaxed() const noexcept
\inmodule QtGui
Definition qbrush.h:30
const QGradient * gradient() const
Returns the gradient describing this brush.
Definition qbrush.cpp:791
const QColor & color() const
Returns the brush color.
Definition qbrush.h:121
Qt::BrushStyle style() const
Returns the brush style.
Definition qbrush.h:120
QTransform transform() const
Definition qbrush.h:122
\inmodule QtCore
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
void setAlphaF(float alpha)
Sets the alpha of this color to alpha.
Definition qcolor.cpp:1511
int alpha() const noexcept
Returns the alpha color component of this color.
Definition qcolor.cpp:1466
@ HexArgb
Definition qcolor.h:36
\inmodule QtGui
Definition qbrush.h:446
virtual QFontEngine * cloneWithSize(qreal) const
QAtomicInt ref
\reentrant \inmodule QtGui
qreal ascent() const
Returns the ascent of the font.
\reentrant
Definition qfont.h:22
QString family() const
Returns the requested font family name.
Definition qfont.cpp:817
int pixelSize() const
Returns the pixel size of the font if it was set with setPixelSize().
Definition qfont.cpp:1074
bool italic() const
Returns true if the style() of the font is not QFont::StyleNormal.
Definition qfont.h:377
@ AllLowercase
Definition qfont.h:101
@ AllUppercase
Definition qfont.h:100
@ Capitalize
Definition qfont.h:103
@ SmallCaps
Definition qfont.h:102
Capitalization capitalization() const
Definition qfont.cpp:1760
Weight weight() const
Returns the weight of the font, using the same scale as the \l{QFont::Weight} enumeration.
Definition qfont.cpp:1133
@ SizeResolved
Definition qfont.h:116
@ FamilyResolved
Definition qfont.h:115
@ WeightResolved
Definition qfont.h:119
@ CapitalizationResolved
Definition qfont.h:127
@ StyleResolved
Definition qfont.h:120
@ FamiliesResolved
Definition qfont.h:132
int pointSize() const
Returns the point size of the font.
Definition qfont.cpp:884
void setPixelSize(int)
Sets the font size to pixelSize pixels, with a maxiumum size of an unsigned 16-bit integer.
Definition qfont.cpp:1049
uint resolveMask() const
Definition qfont.h:313
bool bold() const
Returns true if weight() is a value greater than \l{Weight}{QFont::Medium}; otherwise returns false.
Definition qfont.h:370
qreal pointSizeF() const
Returns the point size of the font.
Definition qfont.cpp:1034
@ Bold
Definition qfont.h:71
@ Normal
Definition qfont.h:68
The QGlyphRun class provides direct access to the internal glyphs in a font.
Definition qglyphrun.h:20
\inmodule QtGui
Definition qbrush.h:135
Spread spread() const
Returns the spread method use by this gradient.
Definition qbrush.h:347
CoordinateMode coordinateMode() const
Definition qbrush.cpp:1672
void setStops(const QGradientStops &stops)
Replaces the current set of stop points with the given stopPoints.
Definition qbrush.cpp:1608
Type type() const
Returns the type of gradient.
Definition qbrush.h:344
QGradientStops stops() const
Returns the stop points for this gradient.
Definition qbrush.cpp:1631
@ LinearGradient
Definition qbrush.h:139
@ NoGradient
Definition qbrush.h:142
@ RadialGradient
Definition qbrush.h:140
T value(const Key &key) const noexcept
Definition qhash.h:1055
\inmodule QtGui
Definition qimage.h:37
@ Format_RGB32
Definition qimage.h:46
constexpr QPointF p1() const
Returns the line's start point.
Definition qline.h:317
constexpr QPointF p2() const
Returns the line's end point.
Definition qline.h:322
\inmodule QtGui
Definition qbrush.h:394
void append(parameter_type t)
Definition qlist.h:459
The QPainterPathStroker class is used to generate fillable outlines for a given painter path.
QPainterPath createStroke(const QPainterPath &path) const
Generates a new path that is a fillable area representing the outline of the given path.
\inmodule QtGui
void moveTo(const QPointF &p)
Moves the current point to the given point, implicitly starting a new subpath and closing the previou...
void addEllipse(const QRectF &rect)
Creates an ellipse within the specified boundingRectangle and adds it to the painter path as a closed...
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
const QPen & pen() const
Returns the painter's current pen.
void setPen(const QColor &color)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool begin(QPaintDevice *)
Begins painting the paint device and returns true if successful; otherwise returns false.
const QBrush & brush() const
Returns the painter's current brush.
void setBrush(const QBrush &brush)
Sets the painter's brush to the given brush.
bool end()
Ends painting.
\inmodule QtGui
Definition qpen.h:28
qreal widthF() const
Returns the pen width with floating point precision.
Definition qpen.cpp:572
QColor color() const
Returns the color of this pen's brush.
Definition qpen.cpp:692
void setMiterLimit(qreal limit)
Sets the miter limit of this pen to the given limit.
Definition qpen.cpp:545
QBrush brush() const
Returns the brush used to fill strokes generated with this pen.
Definition qpen.cpp:715
\inmodule QtCore\reentrant
Definition qpoint.h:217
virtual void generateNode(const NodeInfo &info)=0
virtual void generateImageNode(const ImageNodeInfo &info)=0
\inmodule QtGui
Definition qbrush.h:412
static QRawFontPrivate * get(const QRawFont &font)
Definition qrawfont_p.h:104
The QRawFont class provides access to a single physical instance of a font.
Definition qrawfont.h:24
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:332
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString & fill(QChar c, qsizetype size=-1)
Sets every character in the string to character ch.
Definition qstring.cpp:6369
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3829
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
QString toLower() const &
Definition qstring.h:435
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8095
QString toUpper() const &
Definition qstring.h:439
QString toHtmlEscaped() const
QRectF rect() const
qreal m_horizAdvX
Definition qsvgfont_p.h:55
QString m_familyName
Definition qsvgfont_p.h:53
qreal m_unitsPerEm
Definition qsvgfont_p.h:54
QHash< QChar, QSvgGlyph > m_glyphs
Definition qsvgfont_p.h:56
QPainterPath m_path
Definition qsvgfont_p.h:33
const QImage & image() const
QRectF rect() const
QString filename() const
QLineF line() const
void revertStyle(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:254
QString typeName() const
Definition qsvgnode.cpp:392
void applyStyle(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:233
DisplayMode displayMode() const
Definition qsvgnode.cpp:607
QString nodeId() const
Definition qsvgnode_p.h:213
const QSvgStyle & style() const
Definition qsvgnode_p.h:170
bool isVisible() const
Definition qsvgnode_p.h:208
virtual Type type() const =0
qreal opacity() const
const QPainterPath & path() const
const QPolygonF & polygon() const
const QPolygonF & polygon() const
QPointF radius() const
QRectF rect() const
bool isDefault() const
Definition qsvgstyle_p.h:79
QList< QSvgNode * > renderers() const
const QGradient * currentFillGradient() const
const QGradient * currentStrokeGradient() const
QColor currentFillColor() const
QSvgExtraStates & states()
static QGradient applyOpacityToGradient(const QGradient &gradient, float opacity)
QTransform currentFillTransform() const
QPen currentStroke() const
qreal currentFillOpacity() const
float currentStrokeWidth() const
QSvgExtraStates m_svgState
QColor currentStrokeColor() const
QSvgRefCounter< QSvgOpacityStyle > opacity
QSvgRefCounter< QSvgTransformStyle > transform
QSvgRefCounter< QSvgFillStyle > fill
QPointF position() const
QSizeF size() const
const QList< QSvgTspan * > tspans() const
WhitespaceMode whitespaceMode() const
Type type() const override
Type type() const override
static QSvgTinyDocument * load(const QString &file, QtSvg::Options options={})
QRectF viewBox() const
const QTransform & qtransform() const
QSvgNode * link() const
QPointF start() const
void visitPolygonNode(const QSvgPolygon *node) override
void visitEllipseNode(const QSvgEllipse *node) override
void visitPathNode(const QSvgPath *node) override
bool visitDefsNodeStart(const QSvgDefs *node) override
QSvgVisitorImpl(const QString svgFileName, QQuickGenerator *generator)
void visitDocumentNodeEnd(const QSvgTinyDocument *node) override
void visitStructureNodeEnd(const QSvgStructureNode *node) override
void visitRectNode(const QSvgRect *node) override
void visitLineNode(const QSvgLine *node) override
void visitNode(const QSvgNode *node) override
void visitPolylineNode(const QSvgPolyline *node) override
void visitUseNode(const QSvgUse *node) override
bool visitDocumentNodeStart(const QSvgTinyDocument *node) override
void visitImageNode(const QSvgImage *node) override
void visitTextNode(const QSvgText *node) override
bool visitStructureNodeStart(const QSvgStructureNode *node) override
void traverse(const QSvgStructureNode *node)
\reentrant
bool isValid() const
Returns true if this text block is valid; otherwise returns false.
QList< QTextLayout::FormatRange > textFormats() const
QTextBlock next() const
Returns the text block in the document after this block, or an empty text block if this is the last o...
QTextLayout * layout() const
Returns the QTextLayout that is used to lay out and display the block's contents.
QTextCharFormat charFormat() const
Returns the QTextCharFormat that describes the block's character format.
QFont font() const
Returns the font for this character format.
\reentrant \inmodule QtGui
\reentrant
Definition qtextlayout.h:70
qreal minimumWidth() const
The minimum width the layout needs.
QList< QGlyphRun > glyphRuns(int from=-1, int length=-1, GlyphRunRetrievalFlags flags=DefaultRetrievalFlags) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setRawFont(const QRawFont &rawFont)
int lineCount() const
Returns the number of lines in this text layout.
QTextLine lineAt(int i) const
Returns the {i}-th line of text in this text layout.
bool isValid() const
Returns true if this text line is valid; otherwise returns false.
qreal ascent() const
Returns the line's ascent.
\inmodule QtCore
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
QPoint map(const QPoint &p) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
QString str
[2]
QString text
QSet< QString >::iterator it
rect
[4]
EGLint EGLint * formats
constexpr QColor Transparent
Definition qcolor.h:310
QPainterPath polygonToPath(const QPolygonF &poly, bool closed)
Definition utils_p.h:66
Combined button and popup list for selecting options.
@ AlignRight
Definition qnamespace.h:146
@ AlignHCenter
Definition qnamespace.h:148
@ black
Definition qnamespace.h:30
@ SolidLine
@ NoPen
PenJoinStyle
@ SvgMiterJoin
@ BevelJoin
@ MiterJoin
@ RoundJoin
@ RadialGradientPattern
@ LinearGradientPattern
@ NoBrush
@ ConicalGradientPattern
PenCapStyle
@ SquareCap
@ RoundCap
@ FlatCap
Definition brush.cpp:5
QPair< qreal, QColor > QGradientStop
Definition qbrush.h:131
QList< QString > QStringList
Constructs a string list that contains the given string, str.
static const QCssKnownValue positions[NumKnownPositionModes - 1]
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
return ret
GLboolean GLboolean GLboolean b
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLfloat GLfloat GLfloat x1
GLsizei range
GLint GLsizei width
GLuint color
[2]
GLsizei const GLuint * paths
GLbitfield flags
GLsizei const GLfloat * dashArray
GLfixed GLfixed GLfixed y2
GLfixed GLfixed x2
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
GLenum GLsizei len
GLenum GLenum GLenum GLenum GLenum scale
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
unsigned int glyph_t
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
unsigned short ushort
Definition qtypes.h:33
double qreal
Definition qtypes.h:187
QVideoFrameFormat::PixelFormat fmt
QRandomGenerator generator(sseq)
p ry()++
p rx()++
QLayoutItem * child
[0]
QHostInfo info
[0]
static constexpr QFixed fromReal(qreal r)
Definition qfixed_p.h:35
glyph_t * glyphs
QFixed * advances
\inmodule QtCore \reentrant
Definition qchar.h:18
static StrokeStyle fromPen(const QPen &p)
QByteArray name
Definition moc.h:29