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
qsvggenerator.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
4#include "qsvggenerator.h"
5
6#ifndef QT_NO_SVGGENERATOR
7
8#include "qpainterpath.h"
9
10#include "private/qpaintengine_p.h"
11#include "private/qtextengine_p.h"
12#include "private/qdrawhelper_p.h"
13
14#include "qfile.h"
15#include "qtextstream.h"
16#include "qbuffer.h"
17#include "qmath.h"
18#include "qbitmap.h"
19#include "qtransform.h"
20
21#include "qdebug.h"
22
23#include <optional>
24
26
27static void translate_color(const QColor &color, QString *color_string,
28 QString *opacity_string)
29{
30 Q_ASSERT(color_string);
31 Q_ASSERT(opacity_string);
32
33 *color_string =
34 QString::fromLatin1("#%1%2%3")
35 .arg(color.red(), 2, 16, QLatin1Char('0'))
36 .arg(color.green(), 2, 16, QLatin1Char('0'))
37 .arg(color.blue(), 2, 16, QLatin1Char('0'));
38 *opacity_string = QString::number(color.alphaF());
39}
40
41static void translate_dashPattern(const QList<qreal> &pattern, qreal width, QString *pattern_string)
42{
43 Q_ASSERT(pattern_string);
44
45 // Note that SVG operates in absolute lengths, whereas Qt uses a length/width ratio.
46 for (qreal entry : pattern)
47 *pattern_string += QString::fromLatin1("%1,").arg(entry * width);
48
49 pattern_string->chop(1);
50}
51
53{
54public:
56 : svgVersion(version)
57 {
58 size = QSize();
59 viewBox = QRectF();
60 outputDevice = nullptr;
61 resolution = 72;
62
63 attributes.document_title = QLatin1String("Qt SVG Document");
64 attributes.document_description = QLatin1String("Generated with Qt");
65 attributes.font_family = QLatin1String("serif");
66 attributes.font_size = QLatin1String("10pt");
67 attributes.font_style = QLatin1String("normal");
68 attributes.font_weight = QLatin1String("normal");
69
70 afterFirstUpdate = false;
71 numGradients = 0;
72 }
73
80
85
90
96
99
102
114
120
121 std::optional<QPainterPath> clipPath;
122 bool clipEnabled = false;
123 bool isClippingEffective() const {
124 return clipEnabled && clipPath.has_value();
125 }
129};
130
131static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures()
132{
133 return QPaintEngine::PaintEngineFeatures(
138}
139
140Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
141
143{
144 Q_DECLARE_PRIVATE(QSvgPaintEngine)
145public:
146
148 : QPaintEngine(*new QSvgPaintEnginePrivate(version),
150 {
151 }
152
153 bool begin(QPaintDevice *device) override;
154 bool end() override;
155
156 void updateState(const QPaintEngineState &state) override;
158 void popGroup();
159
160 void drawEllipse(const QRectF &r) override;
161 void drawPath(const QPainterPath &path) override;
162 void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
163 void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override;
164 void drawRects(const QRectF *rects, int rectCount) override;
165 void drawTextItem(const QPointF &pt, const QTextItem &item) override;
166 void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
167 Qt::ImageConversionFlags flags = Qt::AutoColor) override;
168
169 QPaintEngine::Type type() const override { return QPaintEngine::SVG; }
170
171 QSize size() const { return d_func()->size; }
172 void setSize(const QSize &size) {
173 Q_ASSERT(!isActive());
174 d_func()->size = size;
175 }
176
177 QRectF viewBox() const { return d_func()->viewBox; }
178 void setViewBox(const QRectF &viewBox) {
179 Q_ASSERT(!isActive());
180 d_func()->viewBox = viewBox;
181 }
182
183 QString documentTitle() const { return d_func()->attributes.document_title; }
185 d_func()->attributes.document_title = title;
186 }
187
188 QString documentDescription() const { return d_func()->attributes.document_description; }
189 void setDocumentDescription(const QString &description) {
190 d_func()->attributes.document_description = description;
191 }
192
193 QIODevice *outputDevice() const { return d_func()->outputDevice; }
195 Q_ASSERT(!isActive());
196 d_func()->outputDevice = device;
197 }
198
199 int resolution() const { return d_func()->resolution; }
201 Q_ASSERT(!isActive());
202 d_func()->resolution = resolution;
203 }
204
205 QSvgGenerator::SvgVersion svgVersion() const { return d_func()->svgVersion; }
206
208 {
209 QString maskId = QString(QStringLiteral("patternmask%1")).arg(style);
210 if (!d_func()->savedPatternMasks.contains(maskId)) {
211 QImage img = qt_imageForBrush(style, true);
212 QRegion reg(QBitmap::fromData(img.size(), img.constBits()));
213 QString rct(QStringLiteral("<rect x=\"%1\" y=\"%2\" width=\"%3\" height=\"%4\" />"));
214 QTextStream str(&d_func()->defs, QIODevice::Append);
215 str << "<mask id=\"" << maskId << "\" x=\"0\" y=\"0\" width=\"8\" height=\"8\" "
216 << "stroke=\"none\" fill=\"#ffffff\" patternUnits=\"userSpaceOnUse\" >" << Qt::endl;
217 for (QRect r : reg)
218 str << rct.arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height()) << Qt::endl;
219 str << QStringLiteral("</mask>") << Qt::endl << Qt::endl;
220 d_func()->savedPatternMasks.append(maskId);
221 }
222 return maskId;
223 }
224
226 {
227 QString patternId = QString(QStringLiteral("fillpattern%1_")).arg(brush.style()) + QStringView{color}.mid(1);
228 if (!d_func()->savedPatternBrushes.contains(patternId)) {
229 QString maskId = savePatternMask(brush.style());
230 QString geo(QStringLiteral("x=\"0\" y=\"0\" width=\"8\" height=\"8\""));
231 QTextStream str(&d_func()->defs, QIODevice::Append);
232 str << QString(QStringLiteral("<pattern id=\"%1\" %2 patternUnits=\"userSpaceOnUse\" >")).arg(patternId, geo) << Qt::endl;
233 str << QString(QStringLiteral("<rect %1 stroke=\"none\" fill=\"%2\" mask=\"url(#%3)\" />")).arg(geo, color, maskId) << Qt::endl;
234 str << QStringLiteral("</pattern>") << Qt::endl << Qt::endl;
235 d_func()->savedPatternBrushes.append(patternId);
236 }
237 return patternId;
238 }
239
241 {
242 QTextStream str(&d_func()->defs, QIODevice::Append);
243 const QLinearGradient *grad = static_cast<const QLinearGradient*>(g);
244 str << QLatin1String("<linearGradient ");
246 if (grad) {
247 str << QLatin1String("x1=\"") <<grad->start().x()<< QLatin1String("\" ")
248 << QLatin1String("y1=\"") <<grad->start().y()<< QLatin1String("\" ")
249 << QLatin1String("x2=\"") <<grad->finalStop().x() << QLatin1String("\" ")
250 << QLatin1String("y2=\"") <<grad->finalStop().y() << QLatin1String("\" ");
251 }
252
253 str << QLatin1String("id=\"") << d_func()->generateGradientName() << QLatin1String("\">\n");
255 str << QLatin1String("</linearGradient>") <<Qt::endl;
256 }
258 {
259 QTextStream str(&d_func()->defs, QIODevice::Append);
260 const QRadialGradient *grad = static_cast<const QRadialGradient*>(g);
261 str << QLatin1String("<radialGradient ");
263 if (grad) {
264 str << QLatin1String("cx=\"") <<grad->center().x()<< QLatin1String("\" ")
265 << QLatin1String("cy=\"") <<grad->center().y()<< QLatin1String("\" ")
266 << QLatin1String("r=\"") <<grad->radius() << QLatin1String("\" ")
267 << QLatin1String("fx=\"") <<grad->focalPoint().x() << QLatin1String("\" ")
268 << QLatin1String("fy=\"") <<grad->focalPoint().y() << QLatin1String("\" ");
269 }
270 str << QLatin1String("id=\"") <<d_func()->generateGradientName()<< QLatin1String("\">\n");
272 str << QLatin1String("</radialGradient>") << Qt::endl;
273 }
275 {
276 qWarning("svg's don't support conical gradients!");
277 }
278
280 QGradientStops stops = g->stops();
281
282 if (g->interpolationMode() == QGradient::ColorInterpolation) {
283 bool constantAlpha = true;
284 int alpha = stops.at(0).second.alpha();
285 for (int i = 1; i < stops.size(); ++i)
286 constantAlpha &= (stops.at(i).second.alpha() == alpha);
287
288 if (!constantAlpha) {
289 const qreal spacing = qreal(0.02);
290 QGradientStops newStops;
291 QRgb fromColor = qPremultiply(stops.at(0).second.rgba());
293 for (int i = 0; i + 1 < stops.size(); ++i) {
294 int parts = qCeil((stops.at(i + 1).first - stops.at(i).first) / spacing);
295 newStops.append(stops.at(i));
296 toColor = qPremultiply(stops.at(i + 1).second.rgba());
297
298 if (parts > 1) {
299 qreal step = (stops.at(i + 1).first - stops.at(i).first) / parts;
300 for (int j = 1; j < parts; ++j) {
301 QRgb color = qUnpremultiply(INTERPOLATE_PIXEL_256(fromColor, 256 - 256 * j / parts, toColor, 256 * j / parts));
302 newStops.append(QGradientStop(stops.at(i).first + j * step, QColor::fromRgba(color)));
303 }
304 }
306 }
307 newStops.append(stops.back());
308 stops = newStops;
309 }
310 }
311
312 for (const QGradientStop &stop : std::as_const(stops)) {
313 const QString color = stop.second.name(QColor::HexRgb);
314 str << QLatin1String(" <stop offset=\"")<< stop.first << QLatin1String("\" ")
315 << QLatin1String("stop-color=\"") << color << QLatin1String("\" ")
316 << QLatin1String("stop-opacity=\"") << stop.second.alphaF() <<QLatin1String("\" />\n");
317 }
318 }
319
321 {
322 str << QLatin1String("gradientUnits=\"");
323 if (gradient && (gradient->coordinateMode() == QGradient::ObjectBoundingMode || gradient->coordinateMode() == QGradient::ObjectMode))
324 str << QLatin1String("objectBoundingBox");
325 else
326 str << QLatin1String("userSpaceOnUse");
327 str << QLatin1String("\" ");
328 }
329
331 {
332 *d_func()->stream << QLatin1String("fill=\"none\" ");
333 *d_func()->stream << QLatin1String("stroke=\"black\" ");
334 *d_func()->stream << QLatin1String("stroke-width=\"1\" ");
335 *d_func()->stream << QLatin1String("fill-rule=\"evenodd\" ");
336 *d_func()->stream << QLatin1String("stroke-linecap=\"square\" ");
337 *d_func()->stream << QLatin1String("stroke-linejoin=\"bevel\" ");
338 *d_func()->stream << QLatin1String(">\n");
339 }
341 {
342 return *d_func()->stream;
343 }
344
345
346 void qpenToSvg(const QPen &spen)
347 {
348 d_func()->pen = spen;
349
350 switch (spen.style()) {
351 case Qt::NoPen:
352 stream() << QLatin1String("stroke=\"none\" ");
353
354 d_func()->attributes.stroke = QLatin1String("none");
355 d_func()->attributes.strokeOpacity = QString();
356 return;
357 break;
358 case Qt::SolidLine: {
359 QString color, colorOpacity;
360
361 translate_color(spen.color(), &color,
362 &colorOpacity);
363 d_func()->attributes.stroke = color;
364 d_func()->attributes.strokeOpacity = colorOpacity;
365
366 stream() << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
367 stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
368 }
369 break;
370 case Qt::DashLine:
371 case Qt::DotLine:
372 case Qt::DashDotLine:
374 case Qt::CustomDashLine: {
375 QString color, colorOpacity, dashPattern, dashOffset;
376
377 qreal penWidth = spen.width() == 0 ? qreal(1) : spen.widthF();
378
379 translate_color(spen.color(), &color, &colorOpacity);
380 translate_dashPattern(spen.dashPattern(), penWidth, &dashPattern);
381
382 // SVG uses absolute offset
383 dashOffset = QString::number(spen.dashOffset() * penWidth);
384
385 d_func()->attributes.stroke = color;
386 d_func()->attributes.strokeOpacity = colorOpacity;
387 d_func()->attributes.dashPattern = dashPattern;
388 d_func()->attributes.dashOffset = dashOffset;
389
390 stream() << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
391 stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
392 stream() << QLatin1String("stroke-dasharray=\"")<<dashPattern<< QLatin1String("\" ");
393 stream() << QLatin1String("stroke-dashoffset=\"")<<dashOffset<< QLatin1String("\" ");
394 break;
395 }
396 default:
397 qWarning("Unsupported pen style");
398 break;
399 }
400
401 if (spen.widthF() == 0)
402 stream() <<"stroke-width=\"1\" ";
403 else
404 stream() <<"stroke-width=\"" << spen.widthF() << "\" ";
405
406 switch (spen.capStyle()) {
407 case Qt::FlatCap:
408 stream() << "stroke-linecap=\"butt\" ";
409 break;
410 case Qt::SquareCap:
411 stream() << "stroke-linecap=\"square\" ";
412 break;
413 case Qt::RoundCap:
414 stream() << "stroke-linecap=\"round\" ";
415 break;
416 default:
417 qWarning("Unhandled cap style");
418 }
419 switch (spen.joinStyle()) {
420 case Qt::SvgMiterJoin:
421 case Qt::MiterJoin:
422 stream() << "stroke-linejoin=\"miter\" "
423 "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
424 break;
425 case Qt::BevelJoin:
426 stream() << "stroke-linejoin=\"bevel\" ";
427 break;
428 case Qt::RoundJoin:
429 stream() << "stroke-linejoin=\"round\" ";
430 break;
431 default:
432 qWarning("Unhandled join style");
433 }
434 }
435 void qbrushToSvg(const QBrush &sbrush)
436 {
437 d_func()->brush = sbrush;
438 switch (sbrush.style()) {
439 case Qt::SolidPattern: {
440 QString color, colorOpacity;
441 translate_color(sbrush.color(), &color, &colorOpacity);
442 stream() << "fill=\"" << color << "\" "
443 "fill-opacity=\""
444 << colorOpacity << "\" ";
445 d_func()->attributes.fill = color;
446 d_func()->attributes.fillOpacity = colorOpacity;
447 }
448 break;
456 case Qt::HorPattern:
457 case Qt::VerPattern:
458 case Qt::CrossPattern:
459 case Qt::BDiagPattern:
460 case Qt::FDiagPattern:
462 QString color, colorOpacity;
463 translate_color(sbrush.color(), &color, &colorOpacity);
464 QString patternId = savePatternBrush(color, sbrush);
465 QString patternRef = QString(QStringLiteral("url(#%1)")).arg(patternId);
466 stream() << "fill=\"" << patternRef << "\" fill-opacity=\"" << colorOpacity << "\" ";
467 d_func()->attributes.fill = patternRef;
468 d_func()->attributes.fillOpacity = colorOpacity;
469 break;
470 }
472 saveLinearGradientBrush(sbrush.gradient());
473 d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
474 d_func()->attributes.fillOpacity = QString();
475 stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
476 break;
478 saveRadialGradientBrush(sbrush.gradient());
479 d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
480 d_func()->attributes.fillOpacity = QString();
481 stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
482 break;
484 saveConicalGradientBrush(sbrush.gradient());
485 d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
486 d_func()->attributes.fillOpacity = QString();
487 stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
488 break;
489 case Qt::NoBrush:
490 stream() << QLatin1String("fill=\"none\" ");
491 d_func()->attributes.fill = QLatin1String("none");
492 d_func()->attributes.fillOpacity = QString();
493 return;
494 break;
495 default:
496 break;
497 }
498 }
499 void qfontToSvg(const QFont &sfont)
500 {
501 Q_D(QSvgPaintEngine);
502
503 d->font = sfont;
504
505 if (d->font.pixelSize() == -1)
506 d->attributes.font_size = QString::number(d->font.pointSizeF() * d->resolution / 72);
507 else
508 d->attributes.font_size = QString::number(d->font.pixelSize());
509
510 d->attributes.font_weight = QString::number(d->font.weight());
511 d->attributes.font_family = d->font.family();
512 d->attributes.font_style = d->font.italic() ? QLatin1String("italic") : QLatin1String("normal");
513
514 *d->stream << "font-family=\"" << d->attributes.font_family << "\" "
515 "font-size=\"" << d->attributes.font_size << "\" "
516 "font-weight=\"" << d->attributes.font_weight << "\" "
517 "font-style=\"" << d->attributes.font_style << "\" "
518 << Qt::endl;
519 }
520};
521
530
580QSvgGenerator::QSvgGenerator() // ### Qt 7: inline
581 : QSvgGenerator(SvgVersion::SvgTiny12)
582{
583}
584
591 : d_ptr(new QSvgGeneratorPrivate)
592{
593 Q_D(QSvgGenerator);
594
595 d->engine = new QSvgPaintEngine(version);
596 d->owns_iodevice = false;
597}
598
603{
604 Q_D(QSvgGenerator);
605 if (d->owns_iodevice)
606 delete d->engine->outputDevice();
607 delete d->engine;
608}
609
617{
618 Q_D(const QSvgGenerator);
619
620 return d->engine->documentTitle();
621}
622
624{
625 Q_D(QSvgGenerator);
626
627 d->engine->setDocumentTitle(title);
628}
629
637{
638 Q_D(const QSvgGenerator);
639
640 return d->engine->documentDescription();
641}
642
644{
645 Q_D(QSvgGenerator);
646
647 d->engine->setDocumentDescription(description);
648}
649
665{
666 Q_D(const QSvgGenerator);
667 return d->engine->size();
668}
669
671{
672 Q_D(QSvgGenerator);
673 if (d->engine->isActive()) {
674 qWarning("QSvgGenerator::setSize(), cannot set size while SVG is being generated");
675 return;
676 }
677 d->engine->setSize(size);
678}
679
695{
696 Q_D(const QSvgGenerator);
697 return d->engine->viewBox();
698}
699
708{
709 Q_D(const QSvgGenerator);
710 return d->engine->viewBox().toRect();
711}
712
714{
715 Q_D(QSvgGenerator);
716 if (d->engine->isActive()) {
717 qWarning("QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated");
718 return;
719 }
720 d->engine->setViewBox(viewBox);
721}
722
724{
726}
727
736{
737 Q_D(const QSvgGenerator);
738 return d->fileName;
739}
740
742{
743 Q_D(QSvgGenerator);
744 if (d->engine->isActive()) {
745 qWarning("QSvgGenerator::setFileName(), cannot set file name while SVG is being generated");
746 return;
747 }
748
749 if (d->owns_iodevice)
750 delete d->engine->outputDevice();
751
752 d->owns_iodevice = true;
753
754 d->fileName = fileName;
755 QFile *file = new QFile(fileName);
756 d->engine->setOutputDevice(file);
757}
758
770{
771 Q_D(const QSvgGenerator);
772 return d->engine->outputDevice();
773}
774
776{
777 Q_D(QSvgGenerator);
778 if (d->engine->isActive()) {
779 qWarning("QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated");
780 return;
781 }
782 d->owns_iodevice = false;
783 d->engine->setOutputDevice(outputDevice);
784 d->fileName = QString();
785}
786
798{
799 Q_D(const QSvgGenerator);
800 return d->engine->resolution();
801}
802
804{
805 Q_D(QSvgGenerator);
806 d->engine->setResolution(dpi);
807}
808
816{
817 Q_D(const QSvgGenerator);
818 return d->engine->svgVersion();
819}
820
826{
827 Q_D(const QSvgGenerator);
828 return d->engine;
829}
830
835{
836 Q_D(const QSvgGenerator);
837 switch (metric) {
839 return 32;
841 return d->engine->size().width();
843 return d->engine->size().height();
845 return d->engine->resolution();
847 return d->engine->resolution();
849 return qRound(d->engine->size().height() * 25.4 / d->engine->resolution());
851 return qRound(d->engine->size().width() * 25.4 / d->engine->resolution());
853 return 0xffffffff;
855 return d->engine->resolution();
857 return d->engine->resolution();
859 return 1;
862 default:
863 qWarning("QSvgGenerator::metric(), unhandled metric %d\n", metric);
864 break;
865 }
866 return 0;
867}
868
869/*****************************************************************************
870 * class QSvgPaintEngine
871 */
872
874{
875 Q_D(QSvgPaintEngine);
876 if (!d->outputDevice) {
877 qWarning("QSvgPaintEngine::begin(), no output device");
878 return false;
879 }
880
881 if (!d->outputDevice->isOpen()) {
882 if (!d->outputDevice->open(QIODevice::WriteOnly | QIODevice::Text)) {
883 qWarning("QSvgPaintEngine::begin(), could not open output device: '%s'",
884 qPrintable(d->outputDevice->errorString()));
885 return false;
886 }
887 } else if (!d->outputDevice->isWritable()) {
888 qWarning("QSvgPaintEngine::begin(), could not write to read-only output device: '%s'",
889 qPrintable(d->outputDevice->errorString()));
890 return false;
891 }
892
893 d->stream = new QTextStream(&d->header);
894
895 // stream out the header...
896 *d->stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << Qt::endl << "<svg";
897
898 if (d->size.isValid()) {
899 qreal wmm = d->size.width() * 25.4 / d->resolution;
900 qreal hmm = d->size.height() * 25.4 / d->resolution;
901 *d->stream << " width=\"" << wmm << "mm\" height=\"" << hmm << "mm\"" << Qt::endl;
902 }
903
904 if (d->viewBox.isValid()) {
905 *d->stream << " viewBox=\"" << d->viewBox.left() << ' ' << d->viewBox.top();
906 *d->stream << ' ' << d->viewBox.width() << ' ' << d->viewBox.height() << '\"' << Qt::endl;
907 }
908
909 *d->stream << " xmlns=\"http://www.w3.org/2000/svg\""
910 " xmlns:xlink=\"http://www.w3.org/1999/xlink\"";
911 switch (d->svgVersion) {
913 *d->stream << " version=\"1.2\" baseProfile=\"tiny\">";
914 break;
916 *d->stream << " version=\"1.1\">";
917 break;
918 }
919 *d->stream << Qt::endl;
920
921 if (!d->attributes.document_title.isEmpty()) {
922 *d->stream << "<title>" << d->attributes.document_title.toHtmlEscaped() << "</title>" << Qt::endl;
923 }
924
925 if (!d->attributes.document_description.isEmpty()) {
926 *d->stream << "<desc>" << d->attributes.document_description.toHtmlEscaped() << "</desc>" << Qt::endl;
927 }
928
929 d->stream->setString(&d->defs);
930 *d->stream << "<defs>\n";
931
932 d->stream->setString(&d->body);
933 // Start the initial graphics state...
934 *d->stream << "<g ";
936 *d->stream << Qt::endl;
937
938 return true;
939}
940
942{
943 Q_D(QSvgPaintEngine);
944
945 d->stream->setString(&d->defs);
946 *d->stream << "</defs>\n";
947
948 d->stream->setDevice(d->outputDevice);
949
950 *d->stream << d->header;
951 *d->stream << d->defs;
952 *d->stream << d->body;
953 if (d->hasEmittedClipGroup)
954 *d->stream << "</g>";
955 if (d->afterFirstUpdate)
956 *d->stream << "</g>" << Qt::endl; // close the updateState
957
958 *d->stream << "</g>" << Qt::endl // close the Qt defaults
959 << "</svg>" << Qt::endl;
960
961 delete d->stream;
962
963 return true;
964}
965
967 const QRectF &sr)
968{
969 drawImage(r, pm.toImage(), sr);
970}
971
973 const QRectF &sr,
974 Qt::ImageConversionFlags flags)
975{
976 //Q_D(QSvgPaintEngine);
977
978 Q_UNUSED(sr);
980 QString quality;
982 quality = QLatin1String("optimizeQuality");
983 } else {
984 quality = QLatin1String("optimizeSpeed");
985 }
986 stream() << "<image ";
987 stream() << "x=\""<<r.x()<<"\" "
988 "y=\""<<r.y()<<"\" "
989 "width=\""<<r.width()<<"\" "
990 "height=\""<<r.height()<<"\" "
991 "preserveAspectRatio=\"none\" "
992 "image-rendering=\""<<quality<<"\" ";
993
997 image.save(&buffer, "PNG");
998 buffer.close();
999 stream() << "xlink:href=\"data:image/png;base64,"
1000 << data.toBase64()
1001 <<"\" />\n";
1002}
1003
1005{
1006 Q_D(QSvgPaintEngine);
1007 // always stream full gstate, which is not required, but...
1008
1009 // close old state and start a new one...
1010 if (d->hasEmittedClipGroup)
1011 *d->stream << "</g>\n";
1012 if (d->afterFirstUpdate)
1013 *d->stream << "</g>\n\n";
1014
1016
1017 if (d->isClippingEffective()) {
1018 *d->stream << QStringLiteral("<g clip-path=\"url(#%1)\">").arg(d->currentClipPathName);
1019 d->hasEmittedClipGroup = true;
1020 } else {
1021 d->hasEmittedClipGroup = false;
1022 }
1023
1024 *d->stream << "<g ";
1025
1027 qpenToSvg(state.pen());
1028
1029 d->matrix = state.transform();
1030 *d->stream << "transform=\"matrix(" << d->matrix.m11() << ','
1031 << d->matrix.m12() << ','
1032 << d->matrix.m21() << ',' << d->matrix.m22() << ','
1033 << d->matrix.dx() << ',' << d->matrix.dy()
1034 << ")\""
1035 << Qt::endl;
1036
1038
1039 if (!qFuzzyIsNull(state.opacity() - 1))
1040 stream() << "opacity=\""<<state.opacity()<<"\" ";
1041
1042 *d->stream << '>' << Qt::endl;
1043
1044 d->afterFirstUpdate = true;
1045}
1046
1048{
1049 Q_D(QSvgPaintEngine);
1050 switch (d->svgVersion) {
1052 // no clip handling in Tiny 1.2
1053 return;
1055 break;
1056 }
1057
1058 const QPaintEngine::DirtyFlags flags = state.state();
1059
1060 const bool clippingChanged = flags.testAnyFlags(DirtyClipPath | DirtyClipRegion);
1061 if (clippingChanged) {
1062 switch (state.clipOperation()) {
1063 case Qt::NoClip:
1064 d->clipEnabled = false;
1065 d->clipPath.reset();
1066 break;
1067 case Qt::ReplaceClip:
1068 case Qt::IntersectClip:
1069 d->clipPath = painter()->transform().map(painter()->clipPath());
1070 break;
1071 }
1072 }
1073
1074 if (flags & DirtyClipEnabled)
1075 d->clipEnabled = state.isClipEnabled();
1076
1077 if (d->isClippingEffective() && clippingChanged) {
1078 d->stream->setString(&d->defs);
1079 *d->stream << QLatin1String("<clipPath id=\"%1\">\n").arg(d->generateClipPathName());
1080 drawPath(*d->clipPath);
1081 *d->stream << "</clipPath>\n";
1082 d->stream->setString(&d->body);
1083 }
1084}
1085
1087{
1088 Q_D(QSvgPaintEngine);
1089
1090 const bool isCircle = r.width() == r.height();
1091 *d->stream << '<' << (isCircle ? "circle" : "ellipse");
1092 if (state->pen().isCosmetic())
1093 *d->stream << " vector-effect=\"non-scaling-stroke\"";
1094 const QPointF c = r.center();
1095 *d->stream << " cx=\"" << c.x() << "\" cy=\"" << c.y();
1096 if (isCircle)
1097 *d->stream << "\" r=\"" << r.width() / qreal(2.0);
1098 else
1099 *d->stream << "\" rx=\"" << r.width() / qreal(2.0) << "\" ry=\"" << r.height() / qreal(2.0);
1100 *d->stream << "\"/>" << Qt::endl;
1101}
1102
1104{
1105 Q_D(QSvgPaintEngine);
1106
1107 *d->stream << "<path vector-effect=\""
1108 << (state->pen().isCosmetic() ? "non-scaling-stroke" : "none")
1109 << "\" fill-rule=\""
1110 << (p.fillRule() == Qt::OddEvenFill ? "evenodd" : "nonzero")
1111 << "\" d=\"";
1112
1113 for (int i=0; i<p.elementCount(); ++i) {
1114 const QPainterPath::Element &e = p.elementAt(i);
1115 switch (e.type) {
1117 *d->stream << 'M' << e.x << ',' << e.y;
1118 break;
1120 *d->stream << 'L' << e.x << ',' << e.y;
1121 break;
1123 *d->stream << 'C' << e.x << ',' << e.y;
1124 ++i;
1125 while (i < p.elementCount()) {
1126 const QPainterPath::Element &e = p.elementAt(i);
1128 --i;
1129 break;
1130 } else
1131 *d->stream << ' ';
1132 *d->stream << e.x << ',' << e.y;
1133 ++i;
1134 }
1135 break;
1136 default:
1137 break;
1138 }
1139 if (i != p.elementCount() - 1) {
1140 *d->stream << ' ';
1141 }
1142 }
1143
1144 *d->stream << "\"/>" << Qt::endl;
1145}
1146
1147void QSvgPaintEngine::drawPolygon(const QPointF *points, int pointCount,
1149{
1150 Q_ASSERT(pointCount >= 2);
1151
1152 //Q_D(QSvgPaintEngine);
1153
1155 for (int i=1; i<pointCount; ++i)
1156 path.lineTo(points[i]);
1157
1158 if (mode == PolylineMode) {
1159 stream() << "<polyline fill=\"none\" vector-effect=\""
1160 << (state->pen().isCosmetic() ? "non-scaling-stroke" : "none")
1161 << "\" points=\"";
1162 for (int i = 0; i < pointCount; ++i) {
1163 const QPointF &pt = points[i];
1164 stream() << pt.x() << ',' << pt.y() << ' ';
1165 }
1166 stream() << "\" />" <<Qt::endl;
1167 } else {
1168 path.closeSubpath();
1169 drawPath(path);
1170 }
1171}
1172
1173void QSvgPaintEngine::drawRects(const QRectF *rects, int rectCount)
1174{
1175 Q_D(QSvgPaintEngine);
1176
1177 for (int i=0; i < rectCount; ++i) {
1178 const QRectF &rect = rects[i].normalized();
1179 *d->stream << "<rect";
1180 if (state->pen().isCosmetic())
1181 *d->stream << " vector-effect=\"non-scaling-stroke\"";
1182 *d->stream << " x=\"" << rect.x() << "\" y=\"" << rect.y()
1183 << "\" width=\"" << rect.width() << "\" height=\"" << rect.height()
1184 << "\"/>" << Qt::endl;
1185 }
1186}
1187
1188void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &textItem)
1189{
1190 Q_D(QSvgPaintEngine);
1191 if (d->pen.style() == Qt::NoPen)
1192 return;
1193
1194 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1195 if (ti.chars == 0)
1196 QPaintEngine::drawTextItem(pt, ti); // Draw as path
1197 QString s = QString::fromRawData(ti.chars, ti.num_chars);
1198
1199 *d->stream << "<text "
1200 "fill=\"" << d->attributes.stroke << "\" "
1201 "fill-opacity=\"" << d->attributes.strokeOpacity << "\" "
1202 "stroke=\"none\" "
1203 "xml:space=\"preserve\" "
1204 "x=\"" << pt.x() << "\" y=\"" << pt.y() << "\" ";
1205 qfontToSvg(textItem.font());
1206 *d->stream << " >"
1207 << s.toHtmlEscaped()
1208 << "</text>"
1209 << Qt::endl;
1210}
1211
1213
1214#endif // QT_NO_SVGGENERATOR
IOBluetoothDevice * device
static QBitmap fromData(const QSize &size, const uchar *bits, QImage::Format monoFormat=QImage::Format_MonoLSB)
Constructs a bitmap with the given size, and sets the contents to the bits supplied.
Definition qbitmap.cpp:207
\inmodule QtGui
Definition qbrush.h:30
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QColor fromRgba(QRgb rgba) noexcept
Static convenience function that returns a QColor constructed from the given QRgb value rgba.
Definition qcolor.cpp:2385
@ HexRgb
Definition qcolor.h:36
\inmodule QtCore
Definition qfile.h:93
\reentrant
Definition qfont.h:22
\inmodule QtGui
Definition qbrush.h:135
@ ColorInterpolation
Definition qbrush.h:162
CoordinateMode coordinateMode() const
Definition qbrush.cpp:1672
@ ObjectBoundingMode
Definition qbrush.h:156
@ ObjectMode
Definition qbrush.h:157
\inmodule QtCore \reentrant
Definition qiodevice.h:34
\inmodule QtGui
Definition qimage.h:37
constexpr QLatin1Char first() const
QString arg(Args &&...args) const
\inmodule QtGui
Definition qbrush.h:394
QPointF start() const
Returns the start point of this linear gradient in logical coordinates.
Definition qbrush.cpp:1876
QPointF finalStop() const
Returns the final stop point of this linear gradient in logical coordinates.
Definition qbrush.cpp:1927
qsizetype size() const noexcept
Definition qlist.h:397
reference back()
Definition qlist.h:689
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
@ PdmDevicePixelRatioScaled
static qreal devicePixelRatioFScale()
The QPaintEngineState class provides information about the active paint engine's current state....
QTransform transform() const
Qt::ClipOperation clipOperation() const
Returns the clip operation in the current paint engine state.
qreal opacity() const
bool isClipEnabled() const
Returns whether clipping is enabled or not in the current paint engine state.
QBrush brush() const
Returns the brush in the current paint engine state.
QPainter::RenderHints renderHints() const
Returns the render hints in the current paint engine state.
QFont font() const
Returns the font in the current paint engine state.
QPen pen() const
Returns the pen in the current paint engine state.
QPaintEngine::DirtyFlags state() const
Returns a combination of flags identifying the set of properties that need to be updated when updatin...
\inmodule QtGui
virtual void drawTextItem(const QPointF &p, const QTextItem &textItem)
This function draws the text item textItem at position p.
PolygonDrawMode
\value OddEvenMode The polygon should be drawn using OddEven fill rule.
QPainter * painter() const
Returns the paint engine's painter.
QPaintEngineState * state
bool isActive() const
Returns true if the paint engine is actively drawing; otherwise returns false.
Type
\value X11 \value Windows \value MacPrinter \value CoreGraphics \macos's Quartz2D (CoreGraphics) \val...
\inmodule QtGui
\inmodule QtGui
@ SmoothPixmapTransform
Definition qpainter.h:54
const QTransform & transform() const
Alias for worldTransform().
\inmodule QtGui
Definition qpen.h:28
bool isCosmetic() const
Returns true if the pen is cosmetic; otherwise returns false.
Definition qpen.cpp:757
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
QImage toImage() const
Converts the pixmap to a QImage.
Definition qpixmap.cpp:408
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
\inmodule QtGui
Definition qbrush.h:412
QPointF center() const
Returns the center of this radial gradient in logical coordinates.
Definition qbrush.cpp:2157
QPointF focalPoint() const
Returns the focal point of this radial gradient in logical coordinates.
Definition qbrush.cpp:2279
qreal radius() const
Returns the radius of this radial gradient in logical coordinates.
Definition qbrush.cpp:2199
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:669
QRectF normalized() const noexcept
Returns a normalized rectangle; i.e., a rectangle that has a non-negative width and height.
Definition qrect.cpp:1522
\inmodule QtCore\reentrant
Definition qrect.h:30
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\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:6358
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
static QString fromRawData(const QChar *, qsizetype size)
Constructs a QString that uses the first size Unicode characters in the array unicode.
Definition qstring.cpp:9482
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:8084
QSvgPaintEngine * engine
\inmodule QtSvg
void setDescription(const QString &description)
void setResolution(int dpi)
QString fileName
the target filename for the generated SVG drawing
QSize size
the size of the generated SVG drawing
void setOutputDevice(QIODevice *outputDevice)
QString description
the description of the generated SVG drawing
SvgVersion svgVersion() const
void setViewBox(const QRect &viewBox)
int resolution
the resolution of the generated output
QRectF viewBoxF() const
QSvgGenerator()
Constructs a new generator using the SVG Tiny 1.2 profile.
~QSvgGenerator()
Destroys the generator.
QIODevice * outputDevice
the output device for the generated SVG drawing
QRectF viewBox
the viewBox of the generated SVG drawing
int metric(QPaintDevice::PaintDeviceMetric metric) const override
\reimp
void setFileName(const QString &fileName)
QString title
the title of the generated SVG drawing
QPaintEngine * paintEngine() const override
Returns the paint engine used to render graphics to be converted to SVG format information.
void setSize(const QSize &size)
void setTitle(const QString &title)
std::optional< QPainterPath > clipPath
bool isClippingEffective() const
QSvgGenerator::SvgVersion svgVersion
struct QSvgPaintEnginePrivate::_attributes attributes
QSvgPaintEnginePrivate(QSvgGenerator::SvgVersion version)
void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags=Qt::AutoColor) override
Reimplement this function to draw the part of the image specified by the sr rectangle in the given re...
bool end() override
Reimplement this function to finish painting on the current paint device.
void saveLinearGradientBrush(const QGradient *g)
QString savePatternMask(Qt::BrushStyle style)
void qfontToSvg(const QFont &sfont)
void qpenToSvg(const QPen &spen)
void setSize(const QSize &size)
QIODevice * outputDevice() const
int resolution() const
void saveGradientStops(QTextStream &str, const QGradient *g)
void drawEllipse(const QRectF &r) override
Reimplement this function to draw the largest ellipse that can be contained within rectangle rect.
void setDocumentTitle(const QString &title)
QString documentDescription() const
void drawPath(const QPainterPath &path) override
The default implementation ignores the path and does nothing.
void updateClipState(const QPaintEngineState &state)
void saveConicalGradientBrush(const QGradient *)
void saveRadialGradientBrush(const QGradient *g)
void qbrushToSvg(const QBrush &sbrush)
QString documentTitle() const
void drawTextItem(const QPointF &pt, const QTextItem &item) override
This function draws the text item textItem at position p.
void setViewBox(const QRectF &viewBox)
void setResolution(int resolution)
QSvgGenerator::SvgVersion svgVersion() const
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override
Reimplement this function to draw the part of the pm specified by the sr rectangle in the given r.
void setDocumentDescription(const QString &description)
QRectF viewBox() const
void setOutputDevice(QIODevice *device)
QTextStream & stream()
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override
Reimplement this virtual function to draw the polygon defined by the pointCount first points in point...
void updateState(const QPaintEngineState &state) override
Reimplement this function to update the state of a paint engine.
QPaintEngine::Type type() const override
Reimplement this function to return the paint engine \l{Type}.
bool begin(QPaintDevice *device) override
Reimplement this function to initialise your paint engine when painting is to start on the paint devi...
void drawRects(const QRectF *rects, int rectCount) override
Draws the first rectCount rectangles in the buffer rects.
QString savePatternBrush(const QString &color, const QBrush &brush)
QSize size() const
QSvgPaintEngine(QSvgGenerator::SvgVersion version)
void saveGradientUnits(QTextStream &str, const QGradient *gradient)
Internal QTextItem.
\inmodule QtGui
\inmodule QtCore
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QPoint map(const QPoint &p) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
qreal m11() const
Returns the horizontal scaling factor.
Definition qtransform.h:199
QString str
[2]
qreal spacing
rect
[4]
else opt state
[0]
Combined button and popup list for selecting options.
@ AutoColor
Definition qnamespace.h:478
@ ReplaceClip
@ IntersectClip
@ NoClip
@ CustomDashLine
@ DashDotDotLine
@ DotLine
@ SolidLine
@ DashDotLine
@ DashLine
@ NoPen
@ SvgMiterJoin
@ BevelJoin
@ MiterJoin
@ RoundJoin
BrushStyle
@ DiagCrossPattern
@ HorPattern
@ BDiagPattern
@ SolidPattern
@ Dense5Pattern
@ RadialGradientPattern
@ Dense1Pattern
@ Dense3Pattern
@ LinearGradientPattern
@ Dense4Pattern
@ NoBrush
@ CrossPattern
@ ConicalGradientPattern
@ FDiagPattern
@ Dense6Pattern
@ Dense7Pattern
@ VerPattern
@ Dense2Pattern
@ OddEvenFill
QTextStream & endl(QTextStream &stream)
Writes '\n' to the stream and flushes the stream.
@ SquareCap
@ RoundCap
@ FlatCap
Definition brush.cpp:5
Definition image.cpp:4
QPair< qreal, QColor > QGradientStop
Definition qbrush.h:131
static uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b)
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
QColor toColor(const QStringView &color)
Definition qgtk3json.cpp:84
QLatin1String fromColor(const QColor &color)
Definition qgtk3json.cpp:31
#define qWarning
Definition qlogging.h:166
int qCeil(T v)
Definition qmath.h:36
GLenum mode
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLint GLsizei width
GLuint color
[2]
GLbitfield flags
GLboolean GLboolean g
GLfixed GLfixed GLint GLint GLfixed points
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLuint entry
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
GLubyte * pattern
GLboolean invert
Definition qopenglext.h:226
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
QRgb qUnpremultiply(QRgb p)
Definition qrgb.h:60
constexpr QRgb qPremultiply(QRgb x)
Definition qrgb.h:45
SSL_CTX int void * arg
#define qPrintable(string)
Definition qstring.h:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert)
Definition qbrush.cpp:146
static void translate_dashPattern(const QList< qreal > &pattern, qreal width, QString *pattern_string)
static QPaintEngine::PaintEngineFeatures svgEngineFeatures()
static QT_BEGIN_NAMESPACE void translate_color(const QColor &color, QString *color_string, QString *opacity_string)
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
QFile file
[0]
QString title
[35]
QGraphicsItem * item
\inmodule QtCore \reentrant
Definition qchar.h:18