6#ifndef QT_NO_SVGGENERATOR
10#include "private/qpaintengine_p.h"
11#include "private/qtextengine_p.h"
12#include "private/qdrawhelper_p.h"
28 QString *opacity_string)
30 Q_ASSERT(color_string);
31 Q_ASSERT(opacity_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());
43 Q_ASSERT(pattern_string);
46 for (qreal entry : pattern)
47 *pattern_string += QString::fromLatin1(
"%1,").arg(entry * width);
49 pattern_string->chop(1);
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");
93 currentGradientName = QString::fromLatin1(
"gradient%1").arg(numGradients);
94 return currentGradientName;
117 currentClipPathName = QStringLiteral(
"clipPath%1").arg(numClipPaths);
118 return currentClipPathName;
124 return clipEnabled && clipPath.has_value();
133 return QPaintEngine::PaintEngineFeatures(
134 QPaintEngine::AllFeatures
135 & ~QPaintEngine::PerspectiveTransform
136 & ~QPaintEngine::ConicalGradientFill
137 & ~QPaintEngine::PorterDuff);
140Q_GUI_EXPORT
QImage qt_imageForBrush(
int brushStyle,
bool invert);
163 void drawPixmap(
const QRectF &r,
const QPixmap &pm,
const QRectF &sr)
override;
164 void drawPolygon(
const QPointF *points,
int pointCount, PolygonDrawMode mode)
override;
165 void drawRects(
const QRectF *rects,
int rectCount)
override;
168 void drawImage(
const QRectF &r,
const QImage &pm,
const QRectF &sr,
169 Qt::ImageConversionFlags flags =
Qt::
AutoColor)
override;
175 Q_ASSERT(!isActive());
176 d_func()->size = size;
181 Q_ASSERT(!isActive());
182 d_func()->viewBox = viewBox;
185 QString
documentTitle()
const {
return d_func()->attributes.document_title; }
187 d_func()->attributes.document_title = title;
192 d_func()->attributes.document_description = description;
197 Q_ASSERT(!isActive());
198 d_func()->outputDevice = device;
203 Q_ASSERT(!isActive());
204 d_func()->resolution = resolution;
217 str <<
"<mask id=\"" <<
maskId <<
"\" x=\"0\" y=\"0\" width=\"8\" height=\"8\" "
218 <<
"stroke=\"none\" fill=\"#ffffff\" patternUnits=\"userSpaceOnUse\" >" <<
Qt::
endl;
300 qWarning(
"svg's don't support conical gradients!");
426 stream() <<
"stroke-width=\"1\" ";
432 stream() <<
"stroke-linecap=\"butt\" ";
435 stream() <<
"stroke-linecap=\"square\" ";
438 stream() <<
"stroke-linecap=\"round\" ";
446 stream() <<
"stroke-linejoin=\"miter\" "
450 stream() <<
"stroke-linejoin=\"bevel\" ";
453 stream() <<
"stroke-linejoin=\"round\" ";
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
593
594
595
596
597
598
599
600
601
604
605
606QSvgGenerator::QSvgGenerator()
607 : QSvgGenerator(SvgVersion::SvgTiny12)
612
613
614
615
616QSvgGenerator::QSvgGenerator(SvgVersion version)
617 : d_ptr(
new QSvgGeneratorPrivate)
621 d->engine =
new QSvgPaintEngine(version);
622 d->owns_iodevice =
false;
626
627
628QSvgGenerator::~QSvgGenerator()
631 if (d->owns_iodevice)
632 delete d->engine->outputDevice();
637
638
639
640
641
642QString QSvgGenerator::title()
const
644 Q_D(
const QSvgGenerator);
646 return d->engine->documentTitle();
649void QSvgGenerator::setTitle(
const QString &title)
653 d->engine->setDocumentTitle(title);
657
658
659
660
661
662QString QSvgGenerator::description()
const
664 Q_D(
const QSvgGenerator);
666 return d->engine->documentDescription();
669void QSvgGenerator::setDescription(
const QString &description)
673 d->engine->setDocumentDescription(description);
677
678
679
680
681
682
683
684
685
686
687
688
689
690QSize QSvgGenerator::size()
const
692 Q_D(
const QSvgGenerator);
693 return d->engine->size();
696void QSvgGenerator::setSize(
const QSize &size)
699 if (d->engine->isActive()) {
700 qWarning(
"QSvgGenerator::setSize(), cannot set size while SVG is being generated");
703 d->engine->setSize(size);
707
708
709
710
711
712
713
714
715
716
717
718
719
720QRectF QSvgGenerator::viewBoxF()
const
722 Q_D(
const QSvgGenerator);
723 return d->engine->viewBox();
727
728
729
730
731
732
733QRect QSvgGenerator::viewBox()
const
735 Q_D(
const QSvgGenerator);
736 return d->engine->viewBox().toRect();
739void QSvgGenerator::setViewBox(
const QRectF &viewBox)
742 if (d->engine->isActive()) {
743 qWarning(
"QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated");
746 d->engine->setViewBox(viewBox);
749void QSvgGenerator::setViewBox(
const QRect &viewBox)
751 setViewBox(QRectF(viewBox));
755
756
757
758
759
760
761QString QSvgGenerator::fileName()
const
763 Q_D(
const QSvgGenerator);
767void QSvgGenerator::setFileName(
const QString &fileName)
770 if (d->engine->isActive()) {
771 qWarning(
"QSvgGenerator::setFileName(), cannot set file name while SVG is being generated");
775 if (d->owns_iodevice)
776 delete d->engine->outputDevice();
778 d->owns_iodevice =
true;
780 d->fileName = fileName;
781 QFile *file =
new QFile(fileName);
782 d->engine->setOutputDevice(file);
786
787
788
789
790
791
792
793
794
795QIODevice *QSvgGenerator::outputDevice()
const
797 Q_D(
const QSvgGenerator);
798 return d->engine->outputDevice();
801void QSvgGenerator::setOutputDevice(QIODevice *outputDevice)
804 if (d->engine->isActive()) {
805 qWarning(
"QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated");
808 d->owns_iodevice =
false;
809 d->engine->setOutputDevice(outputDevice);
810 d->fileName = QString();
814
815
816
817
818
819
820
821
822
823int QSvgGenerator::resolution()
const
825 Q_D(
const QSvgGenerator);
826 return d->engine->resolution();
829void QSvgGenerator::setResolution(
int dpi)
832 d->engine->setResolution(dpi);
836
837
838
839
840
841QSvgGenerator::SvgVersion QSvgGenerator::svgVersion()
const
843 Q_D(
const QSvgGenerator);
844 return d->engine->svgVersion();
848
849
850
851QPaintEngine *QSvgGenerator::paintEngine()
const
853 Q_D(
const QSvgGenerator);
858
859
860int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric)
const
862 Q_D(
const QSvgGenerator);
864 case QPaintDevice::PdmDepth:
866 case QPaintDevice::PdmWidth:
867 return d->engine->size().width();
868 case QPaintDevice::PdmHeight:
869 return d->engine->size().height();
870 case QPaintDevice::PdmDpiX:
871 return d->engine->resolution();
872 case QPaintDevice::PdmDpiY:
873 return d->engine->resolution();
874 case QPaintDevice::PdmHeightMM:
875 return qRound(d->engine->size().height() * 25.4 / d->engine->resolution());
876 case QPaintDevice::PdmWidthMM:
877 return qRound(d->engine->size().width() * 25.4 / d->engine->resolution());
878 case QPaintDevice::PdmNumColors:
880 case QPaintDevice::PdmPhysicalDpiX:
881 return d->engine->resolution();
882 case QPaintDevice::PdmPhysicalDpiY:
883 return d->engine->resolution();
884 case QPaintDevice::PdmDevicePixelRatio:
886 case QPaintDevice::PdmDevicePixelRatioScaled:
887 return 1 * QPaintDevice::devicePixelRatioFScale();
889 qWarning(
"QSvgGenerator::metric(), unhandled metric %d\n", metric);
896
897
901 Q_D(QSvgPaintEngine);
902 if (!d->outputDevice) {
903 qWarning(
"QSvgPaintEngine::begin(), no output device");
907 if (!d->outputDevice->isOpen()) {
908 if (!d->outputDevice->open(QIODevice::WriteOnly | QIODevice::Text)) {
909 qWarning(
"QSvgPaintEngine::begin(), could not open output device: '%s'",
910 qPrintable(d->outputDevice->errorString()));
913 }
else if (!d->outputDevice->isWritable()) {
914 qWarning(
"QSvgPaintEngine::begin(), could not write to read-only output device: '%s'",
915 qPrintable(d->outputDevice->errorString()));
919 d->stream =
new QTextStream(&d->header);
922 *d->stream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << Qt::endl <<
"<svg";
924 if (d->size.isValid()) {
925 qreal wmm = d->size.width() * 25.4 / d->resolution;
926 qreal hmm = d->size.height() * 25.4 / d->resolution;
927 *d->stream <<
" width=\"" << wmm <<
"mm\" height=\"" << hmm <<
"mm\"" << Qt::endl;
930 if (d->viewBox.isValid()) {
931 *d->stream <<
" viewBox=\"" << d->viewBox.left() <<
' ' << d->viewBox.top();
932 *d->stream <<
' ' << d->viewBox.width() <<
' ' << d->viewBox.height() <<
'\"' << Qt::endl;
935 *d->stream <<
" xmlns=\"http://www.w3.org/2000/svg\""
936 " xmlns:xlink=\"http://www.w3.org/1999/xlink\"";
937 switch (d->svgVersion) {
938 case QSvgGenerator::SvgVersion::SvgTiny12:
939 *d->stream <<
" version=\"1.2\" baseProfile=\"tiny\">";
941 case QSvgGenerator::SvgVersion::Svg11:
942 *d->stream <<
" version=\"1.1\">";
945 *d->stream << Qt::endl;
947 if (!d->attributes.document_title.isEmpty()) {
948 *d->stream <<
"<title>" << d->attributes.document_title.toHtmlEscaped() <<
"</title>" << Qt::endl;
951 if (!d->attributes.document_description.isEmpty()) {
952 *d->stream <<
"<desc>" << d->attributes.document_description.toHtmlEscaped() <<
"</desc>" << Qt::endl;
955 d->stream->setString(&d->defs);
956 *d->stream <<
"<defs>\n";
958 d->stream->setString(&d->body);
961 generateQtDefaults();
962 *d->stream << Qt::endl;
969 Q_D(QSvgPaintEngine);
971 d->stream->setString(&d->defs);
972 *d->stream <<
"</defs>\n";
974 d->stream->setDevice(d->outputDevice);
976 *d->stream << d->header;
977 *d->stream << d->defs;
978 *d->stream << d->body;
979 if (d->hasEmittedClipGroup)
980 *d->stream <<
"</g>";
981 if (d->afterFirstUpdate)
982 *d->stream <<
"</g>" << Qt::endl;
984 *d->stream <<
"</g>" << Qt::endl
985 <<
"</svg>" << Qt::endl;
995 drawImage(r, pm.toImage(), sr);
998void QSvgPaintEngine::generateImage(QTextStream &stream,
const QRectF &r,
const QImage &image)
1001 if (state->renderHints() & QPainter::SmoothPixmapTransform) {
1002 quality = QLatin1String(
"optimizeQuality");
1004 quality = QLatin1String(
"optimizeSpeed");
1006 stream <<
"<image ";
1007 stream <<
"x=\""<<r.x()<<
"\" "
1008 "y=\""<<r.y()<<
"\" "
1009 "width=\""<<r.width()<<
"\" "
1010 "height=\""<<r.height()<<
"\" "
1011 "preserveAspectRatio=\"none\" "
1012 "image-rendering=\""<<quality<<
"\" ";
1015 QBuffer buffer(&data);
1016 buffer.open(QBuffer::ReadWrite);
1017 image.save(&buffer,
"PNG");
1019 stream <<
"xlink:href=\"data:image/png;base64,"
1026 Qt::ImageConversionFlags flags)
1030 generateImage(stream(), r, image);
1035 Q_D(QSvgPaintEngine);
1039 if (d->hasEmittedClipGroup)
1040 *d->stream <<
"</g>\n";
1041 if (d->afterFirstUpdate)
1042 *d->stream <<
"</g>\n\n";
1044 updateClipState(state);
1046 if (d->isClippingEffective()) {
1047 *d->stream << QStringLiteral(
"<g clip-path=\"url(#%1)\">").arg(d->currentClipPathName);
1048 d->hasEmittedClipGroup =
true;
1050 d->hasEmittedClipGroup =
false;
1053 *d->stream <<
"<g ";
1055 qbrushToSvg(state.brush());
1056 qpenToSvg(state.pen());
1058 d->matrix = state.transform();
1059 *d->stream <<
"transform=\"matrix(" << d->matrix.m11() <<
','
1060 << d->matrix.m12() <<
','
1061 << d->matrix.m21() <<
',' << d->matrix.m22() <<
','
1062 << d->matrix.dx() <<
',' << d->matrix.dy()
1066 qfontToSvg(state.font());
1068 if (!qFuzzyIsNull(state.opacity() - 1))
1069 stream() <<
"opacity=\""<<state.opacity()<<
"\" ";
1071 *d->stream <<
'>' << Qt::endl;
1073 d->afterFirstUpdate =
true;
1078 Q_D(QSvgPaintEngine);
1079 switch (d->svgVersion) {
1080 case QSvgGenerator::SvgVersion::SvgTiny12:
1083 case QSvgGenerator::SvgVersion::Svg11:
1087 const QPaintEngine::DirtyFlags flags = state.state();
1089 const bool clippingChanged = flags.testAnyFlags(DirtyClipPath | DirtyClipRegion);
1090 if (clippingChanged) {
1091 switch (state.clipOperation()) {
1093 d->clipEnabled =
false;
1094 d->clipPath.reset();
1096 case Qt::ReplaceClip:
1097 case Qt::IntersectClip:
1098 d->clipPath = painter()->transform().map(painter()->clipPath());
1103 if (flags & DirtyClipEnabled)
1104 d->clipEnabled = state.isClipEnabled();
1106 if (d->isClippingEffective() && clippingChanged) {
1107 d->stream->setString(&d->defs);
1108 *d->stream << QLatin1String(
"<clipPath id=\"%1\">\n").arg(d->generateClipPathName());
1109 drawPath(*d->clipPath);
1110 *d->stream <<
"</clipPath>\n";
1111 d->stream->setString(&d->body);
1117 Q_D(QSvgPaintEngine);
1119 const bool isCircle = r.width() == r.height();
1120 *d->stream <<
'<' << (isCircle ?
"circle" :
"ellipse");
1121 if (state->pen().isCosmetic())
1122 *d->stream <<
" vector-effect=\"non-scaling-stroke\"";
1123 const QPointF c = r.center();
1124 *d->stream <<
" cx=\"" << c.x() <<
"\" cy=\"" << c.y();
1126 *d->stream <<
"\" r=\"" << r.width() / qreal(2.0);
1128 *d->stream <<
"\" rx=\"" << r.width() / qreal(2.0) <<
"\" ry=\"" << r.height() / qreal(2.0);
1129 *d->stream <<
"\"/>" << Qt::endl;
1134 Q_D(QSvgPaintEngine);
1136 *d->stream <<
"<path vector-effect=\""
1137 << (state->pen().isCosmetic() ?
"non-scaling-stroke" :
"none")
1138 <<
"\" fill-rule=\""
1139 << (p.fillRule() == Qt::OddEvenFill ?
"evenodd" :
"nonzero")
1142 for (
int i=0; i<p.elementCount(); ++i) {
1143 const QPainterPath::Element &e = p.elementAt(i);
1145 case QPainterPath::MoveToElement:
1146 *d->stream <<
'M' << e.x <<
',' << e.y;
1148 case QPainterPath::LineToElement:
1149 *d->stream <<
'L' << e.x <<
',' << e.y;
1151 case QPainterPath::CurveToElement:
1152 *d->stream <<
'C' << e.x <<
',' << e.y;
1154 while (i < p.elementCount()) {
1155 const QPainterPath::Element &e = p.elementAt(i);
1156 if (e.type != QPainterPath::CurveToDataElement) {
1161 *d->stream << e.x <<
',' << e.y;
1168 if (i != p.elementCount() - 1) {
1173 *d->stream <<
"\"/>" << Qt::endl;
1177 PolygonDrawMode mode)
1179 Q_ASSERT(pointCount >= 2);
1183 QPainterPath path(points[0]);
1184 for (
int i=1; i<pointCount; ++i)
1185 path.lineTo(points[i]);
1187 if (mode == PolylineMode) {
1188 stream() <<
"<polyline fill=\"none\" vector-effect=\""
1189 << (state->pen().isCosmetic() ?
"non-scaling-stroke" :
"none")
1191 for (
int i = 0; i < pointCount; ++i) {
1192 const QPointF &pt = points[i];
1193 stream() << pt.x() <<
',' << pt.y() <<
' ';
1195 stream() <<
"\" />" <<Qt::endl;
1197 path.closeSubpath();
1204 Q_D(QSvgPaintEngine);
1206 for (
int i=0; i < rectCount; ++i) {
1207 const QRectF &rect = rects[i].normalized();
1208 *d->stream <<
"<rect";
1209 if (state->pen().isCosmetic())
1210 *d->stream <<
" vector-effect=\"non-scaling-stroke\"";
1211 *d->stream <<
" x=\"" << rect.x() <<
"\" y=\"" << rect.y()
1212 <<
"\" width=\"" << rect.width() <<
"\" height=\"" << rect.height()
1213 <<
"\"/>" << Qt::endl;
1219 Q_D(QSvgPaintEngine);
1220 if (d->pen.style() == Qt::NoPen)
1223 const QTextItemInt &ti =
static_cast<
const QTextItemInt &>(textItem);
1225 QPaintEngine::drawTextItem(pt, ti);
1226 QString s = QString::fromRawData(ti.chars, ti.num_chars);
1228 *d->stream <<
"<text "
1229 "fill=\"" << d->attributes.stroke <<
"\" "
1230 "fill-opacity=\"" << d->attributes.strokeOpacity <<
"\" "
1232 "xml:space=\"preserve\" "
1233 "x=\"" << pt.x() <<
"\" y=\"" << pt.y() <<
"\" ";
1234 qfontToSvg(textItem.font());
1236 << s.toHtmlEscaped()
QString generateClipPathName()
bool isClippingEffective() const
QString currentGradientName
QStringList savedPatternMasks
QStringList savedPatternBrushes
QString currentClipPathName
QSvgPaintEnginePrivate(QSvgGenerator::SvgVersion version)
QString generateGradientName()
std::optional< QPainterPath > clipPath
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 generateImage(QTextStream &stream, const QRectF &r, const QImage &pm)
void setSize(const QSize &size)
QIODevice * outputDevice() const
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)
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)
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)
void setOutputDevice(QIODevice *device)
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}.
void drawRects(const QRectF *rects, int rectCount) override
Draws the first rectCount rectangles in the buffer rects.
bool qHasPixmapTexture(const QBrush &)
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)
QString document_description