8#ifndef QT_NO_SVGGENERATOR
12#include "private/qdrawhelper_p.h"
13#include "private/qpaintengine_p.h"
14#include "private/qpainter_p.h"
15#include "private/qtextengine_p.h"
32 QString *opacity_string)
34 Q_ASSERT(color_string);
35 Q_ASSERT(opacity_string);
38 QString::fromLatin1(
"#%1%2%3")
39 .arg(color.red(), 2, 16, QLatin1Char(
'0'))
40 .arg(color.green(), 2, 16, QLatin1Char(
'0'))
41 .arg(color.blue(), 2, 16, QLatin1Char(
'0'));
42 *opacity_string = QString::number(color.alphaF());
47 Q_ASSERT(pattern_string);
50 for (qreal entry : pattern)
51 *pattern_string += QString::fromLatin1(
"%1,").arg(entry * width);
53 pattern_string->chop(1);
67 attributes.document_title = QLatin1String(
"Qt SVG Document");
68 attributes.document_description = QLatin1String(
"Generated with Qt");
69 attributes.font_families << QLatin1String(
"serif");
70 attributes.font_size = QLatin1String(
"10pt");
71 attributes.font_style = QLatin1String(
"normal");
72 attributes.font_weight = QLatin1String(
"normal");
97 currentGradientName = QString::fromLatin1(
"gradient%1").arg(numGradients);
98 return currentGradientName;
121 currentClipPathName = QStringLiteral(
"clipPath%1").arg(numClipPaths);
122 return currentClipPathName;
128 return clipEnabled && clipPath.has_value();
137 return QPaintEngine::PaintEngineFeatures(
138 QPaintEngine::AllFeatures
139 & ~QPaintEngine::PerspectiveTransform
140 & ~QPaintEngine::ConicalGradientFill
141 & ~QPaintEngine::PorterDuff);
144Q_GUI_EXPORT
QImage qt_imageForBrush(
int brushStyle,
bool invert);
167 void drawPixmap(
const QRectF &r,
const QPixmap &pm,
const QRectF &sr)
override;
168 void drawPolygon(
const QPointF *points,
int pointCount, PolygonDrawMode mode)
override;
169 void drawRects(
const QRectF *rects,
int rectCount)
override;
172 void drawImage(
const QRectF &r,
const QImage &pm,
const QRectF &sr,
173 Qt::ImageConversionFlags flags =
Qt::
AutoColor)
override;
179 Q_ASSERT(!isActive());
180 d_func()->size = size;
185 Q_ASSERT(!isActive());
186 d_func()->viewBox = viewBox;
189 QString
documentTitle()
const {
return d_func()->attributes.document_title; }
191 d_func()->attributes.document_title = title;
196 d_func()->attributes.document_description = description;
201 Q_ASSERT(!isActive());
202 d_func()->outputDevice = device;
207 Q_ASSERT(!isActive());
208 d_func()->resolution = resolution;
221 str <<
"<mask id=\"" <<
maskId <<
"\" x=\"0\" y=\"0\" width=\"8\" height=\"8\" "
222 <<
"stroke=\"none\" fill=\"#ffffff\" patternUnits=\"userSpaceOnUse\" >" <<
Qt::
endl;
304 qWarning(
"svg's don't support conical gradients!");
430 stream() <<
"stroke-width=\"1\" ";
436 stream() <<
"stroke-linecap=\"butt\" ";
439 stream() <<
"stroke-linecap=\"square\" ";
442 stream() <<
"stroke-linecap=\"round\" ";
450 stream() <<
"stroke-linejoin=\"miter\" "
454 stream() <<
"stroke-linejoin=\"bevel\" ";
457 stream() <<
"stroke-linejoin=\"round\" ";
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
591
592
593
594
595
596
597
598
599
600
601
602
603
604
607
608
609
610
611
612
613
614
615
618
619
620QSvgGenerator::QSvgGenerator()
621 : QSvgGenerator(SvgVersion::SvgTiny12)
626
627
628
629
630QSvgGenerator::QSvgGenerator(SvgVersion version)
631 : d_ptr(
new QSvgGeneratorPrivate)
635 d->engine =
new QSvgPaintEngine(version);
636 d->owns_iodevice =
false;
640
641
642QSvgGenerator::~QSvgGenerator()
645 if (d->owns_iodevice)
646 delete d->engine->outputDevice();
651
652
653
654
655
656QString QSvgGenerator::title()
const
658 Q_D(
const QSvgGenerator);
660 return d->engine->documentTitle();
663void QSvgGenerator::setTitle(
const QString &title)
667 d->engine->setDocumentTitle(title);
671
672
673
674
675
676QString QSvgGenerator::description()
const
678 Q_D(
const QSvgGenerator);
680 return d->engine->documentDescription();
683void QSvgGenerator::setDescription(
const QString &description)
687 d->engine->setDocumentDescription(description);
691
692
693
694
695
696
697
698
699
700
701
702
703
704QSize QSvgGenerator::size()
const
706 Q_D(
const QSvgGenerator);
707 return d->engine->size();
710void QSvgGenerator::setSize(
const QSize &size)
713 if (d->engine->isActive()) {
714 qWarning(
"QSvgGenerator::setSize(), cannot set size while SVG is being generated");
717 d->engine->setSize(size);
721
722
723
724
725
726
727
728
729
730
731
732
733
734QRectF QSvgGenerator::viewBoxF()
const
736 Q_D(
const QSvgGenerator);
737 return d->engine->viewBox();
741
742
743
744
745
746
747QRect QSvgGenerator::viewBox()
const
749 Q_D(
const QSvgGenerator);
750 return d->engine->viewBox().toRect();
753void QSvgGenerator::setViewBox(
const QRectF &viewBox)
756 if (d->engine->isActive()) {
757 qWarning(
"QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated");
760 d->engine->setViewBox(viewBox);
763void QSvgGenerator::setViewBox(
const QRect &viewBox)
765 setViewBox(QRectF(viewBox));
769
770
771
772
773
774
775QString QSvgGenerator::fileName()
const
777 Q_D(
const QSvgGenerator);
781void QSvgGenerator::setFileName(
const QString &fileName)
784 if (d->engine->isActive()) {
785 qWarning(
"QSvgGenerator::setFileName(), cannot set file name while SVG is being generated");
789 if (d->owns_iodevice)
790 delete d->engine->outputDevice();
792 d->owns_iodevice =
true;
794 d->fileName = fileName;
795 QFile *file =
new QFile(fileName);
796 d->engine->setOutputDevice(file);
800
801
802
803
804
805
806
807
808
809QIODevice *QSvgGenerator::outputDevice()
const
811 Q_D(
const QSvgGenerator);
812 return d->engine->outputDevice();
815void QSvgGenerator::setOutputDevice(QIODevice *outputDevice)
818 if (d->engine->isActive()) {
819 qWarning(
"QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated");
822 d->owns_iodevice =
false;
823 d->engine->setOutputDevice(outputDevice);
824 d->fileName = QString();
828
829
830
831
832
833
834
835
836
837int QSvgGenerator::resolution()
const
839 Q_D(
const QSvgGenerator);
840 return d->engine->resolution();
843void QSvgGenerator::setResolution(
int dpi)
846 d->engine->setResolution(dpi);
850
851
852
853
854
855QSvgGenerator::SvgVersion QSvgGenerator::svgVersion()
const
857 Q_D(
const QSvgGenerator);
858 return d->engine->svgVersion();
862
863
864
865QPaintEngine *QSvgGenerator::paintEngine()
const
867 Q_D(
const QSvgGenerator);
872
873
874int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric)
const
876 Q_D(
const QSvgGenerator);
878 case QPaintDevice::PdmDepth:
880 case QPaintDevice::PdmWidth:
881 return d->engine->size().width();
882 case QPaintDevice::PdmHeight:
883 return d->engine->size().height();
884 case QPaintDevice::PdmDpiX:
885 return d->engine->resolution();
886 case QPaintDevice::PdmDpiY:
887 return d->engine->resolution();
888 case QPaintDevice::PdmHeightMM:
889 return qRound(d->engine->size().height() * 25.4 / d->engine->resolution());
890 case QPaintDevice::PdmWidthMM:
891 return qRound(d->engine->size().width() * 25.4 / d->engine->resolution());
892 case QPaintDevice::PdmNumColors:
894 case QPaintDevice::PdmPhysicalDpiX:
895 return d->engine->resolution();
896 case QPaintDevice::PdmPhysicalDpiY:
897 return d->engine->resolution();
898 case QPaintDevice::PdmDevicePixelRatio:
900 case QPaintDevice::PdmDevicePixelRatioScaled:
901 return 1 * QPaintDevice::devicePixelRatioFScale();
903 qWarning(
"QSvgGenerator::metric(), unhandled metric %d\n", metric);
910
911
912
913void QSvgGenerator::initPainter(QPainter *painter)
const
915 QPainterPrivate *painterPrivate = QPainterPrivate::get(painter);
917 for (QFont *font : { &painterPrivate->state->deviceFont, &painterPrivate->state->font }) {
918 if (font->hintingPreference() == QFont::PreferDefaultHinting)
919 font->setHintingPreference(QFont::PreferNoHinting);
921 painterPrivate->setEngineDirtyFlags({ QPaintEngine::DirtyFont });
925
926
930 Q_D(QSvgPaintEngine);
931 if (!d->outputDevice) {
932 qWarning(
"QSvgPaintEngine::begin(), no output device");
936 if (!d->outputDevice->isOpen()) {
937 if (!d->outputDevice->open(QIODevice::WriteOnly | QIODevice::Text)) {
938 qWarning(
"QSvgPaintEngine::begin(), could not open output device: '%s'",
939 qPrintable(d->outputDevice->errorString()));
942 }
else if (!d->outputDevice->isWritable()) {
943 qWarning(
"QSvgPaintEngine::begin(), could not write to read-only output device: '%s'",
944 qPrintable(d->outputDevice->errorString()));
948 d->stream =
new QTextStream(&d->header);
951 *d->stream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << Qt::endl <<
"<svg";
953 if (d->size.isValid()) {
954 qreal wmm = d->size.width() * 25.4 / d->resolution;
955 qreal hmm = d->size.height() * 25.4 / d->resolution;
956 *d->stream <<
" width=\"" << wmm <<
"mm\" height=\"" << hmm <<
"mm\"" << Qt::endl;
959 if (d->viewBox.isValid()) {
960 *d->stream <<
" viewBox=\"" << d->viewBox.left() <<
' ' << d->viewBox.top();
961 *d->stream <<
' ' << d->viewBox.width() <<
' ' << d->viewBox.height() <<
'\"' << Qt::endl;
964 *d->stream <<
" xmlns=\"http://www.w3.org/2000/svg\""
965 " xmlns:xlink=\"http://www.w3.org/1999/xlink\"";
966 switch (d->svgVersion) {
967 case QSvgGenerator::SvgVersion::SvgTiny12:
968 *d->stream <<
" version=\"1.2\" baseProfile=\"tiny\">";
970 case QSvgGenerator::SvgVersion::Svg11:
971 *d->stream <<
" version=\"1.1\">";
974 *d->stream << Qt::endl;
976 if (!d->attributes.document_title.isEmpty()) {
977 *d->stream <<
"<title>" << d->attributes.document_title.toHtmlEscaped() <<
"</title>" << Qt::endl;
980 if (!d->attributes.document_description.isEmpty()) {
981 *d->stream <<
"<desc>" << d->attributes.document_description.toHtmlEscaped() <<
"</desc>" << Qt::endl;
984 d->stream->setString(&d->defs);
985 *d->stream <<
"<defs>\n";
987 d->stream->setString(&d->body);
990 generateQtDefaults();
991 *d->stream << Qt::endl;
998 Q_D(QSvgPaintEngine);
1000 d->stream->setString(&d->defs);
1001 *d->stream <<
"</defs>\n";
1003 d->stream->setDevice(d->outputDevice);
1005 *d->stream << d->header;
1006 *d->stream << d->defs;
1007 *d->stream << d->body;
1008 if (d->hasEmittedClipGroup)
1009 *d->stream <<
"</g>";
1010 if (d->afterFirstUpdate)
1011 *d->stream <<
"</g>" << Qt::endl;
1013 *d->stream <<
"</g>" << Qt::endl
1014 <<
"</svg>" << Qt::endl;
1024 drawImage(r, pm.toImage(), sr);
1027void QSvgPaintEngine::generateImage(QTextStream &stream,
const QRectF &r,
const QImage &image)
1030 if (state->renderHints() & QPainter::SmoothPixmapTransform) {
1031 quality = QLatin1String(
"optimizeQuality");
1033 quality = QLatin1String(
"optimizeSpeed");
1035 stream <<
"<image ";
1036 stream <<
"x=\""<<r.x()<<
"\" "
1037 "y=\""<<r.y()<<
"\" "
1038 "width=\""<<r.width()<<
"\" "
1039 "height=\""<<r.height()<<
"\" "
1040 "preserveAspectRatio=\"none\" "
1041 "image-rendering=\""<<quality<<
"\" ";
1044 QBuffer buffer(&data);
1045 buffer.open(QBuffer::ReadWrite);
1046 image.save(&buffer,
"PNG");
1048 stream <<
"xlink:href=\"data:image/png;base64,"
1055 Qt::ImageConversionFlags flags)
1059 generateImage(stream(), r, image);
1064 Q_D(QSvgPaintEngine);
1068 if (d->hasEmittedClipGroup)
1069 *d->stream <<
"</g>\n";
1070 if (d->afterFirstUpdate)
1071 *d->stream <<
"</g>\n\n";
1073 updateClipState(state);
1075 if (d->isClippingEffective()) {
1076 *d->stream << QStringLiteral(
"<g clip-path=\"url(#%1)\">").arg(d->currentClipPathName);
1077 d->hasEmittedClipGroup =
true;
1079 d->hasEmittedClipGroup =
false;
1082 *d->stream <<
"<g ";
1084 qbrushToSvg(state.brush());
1085 qpenToSvg(state.pen());
1087 d->matrix = state.transform();
1088 *d->stream <<
"transform=\"matrix(" << d->matrix.m11() <<
','
1089 << d->matrix.m12() <<
','
1090 << d->matrix.m21() <<
',' << d->matrix.m22() <<
','
1091 << d->matrix.dx() <<
',' << d->matrix.dy()
1095 qfontToSvg(state.font());
1097 if (!qFuzzyIsNull(state.opacity() - 1))
1098 stream() <<
"opacity=\""<<state.opacity()<<
"\" ";
1100 *d->stream <<
'>' << Qt::endl;
1102 d->afterFirstUpdate =
true;
1107 Q_D(QSvgPaintEngine);
1108 switch (d->svgVersion) {
1109 case QSvgGenerator::SvgVersion::SvgTiny12:
1112 case QSvgGenerator::SvgVersion::Svg11:
1116 const QPaintEngine::DirtyFlags flags = state.state();
1118 const bool clippingChanged = flags.testAnyFlags(DirtyClipPath | DirtyClipRegion);
1119 if (clippingChanged) {
1120 switch (state.clipOperation()) {
1122 d->clipEnabled =
false;
1123 d->clipPath.reset();
1125 case Qt::ReplaceClip:
1126 case Qt::IntersectClip:
1127 d->clipPath = painter()->transform().map(painter()->clipPath());
1132 if (flags & DirtyClipEnabled)
1133 d->clipEnabled = state.isClipEnabled();
1135 if (d->isClippingEffective() && clippingChanged) {
1136 d->stream->setString(&d->defs);
1137 *d->stream << QLatin1String(
"<clipPath id=\"%1\">\n").arg(d->generateClipPathName());
1139 *d->stream <<
"</clipPath>\n";
1140 d->stream->setString(&d->body);
1146 Q_D(QSvgPaintEngine);
1148 const bool isCircle = r.width() == r.height();
1149 *d->stream <<
'<' << (isCircle ?
"circle" :
"ellipse");
1150 if (state->pen().isCosmetic())
1151 *d->stream <<
" vector-effect=\"non-scaling-stroke\"";
1152 const QPointF c = r.center();
1153 *d->stream <<
" cx=\"" << c.x() <<
"\" cy=\"" << c.y();
1155 *d->stream <<
"\" r=\"" << r.width() / qreal(2.0);
1157 *d->stream <<
"\" rx=\"" << r.width() / qreal(2.0) <<
"\" ry=\"" << r.height() / qreal(2.0);
1158 *d->stream <<
"\"/>" << Qt::endl;
1163 Q_D(QSvgPaintEngine);
1165 *d->stream <<
"<path vector-effect=\""
1166 << (state->pen().isCosmetic() ?
"non-scaling-stroke" :
"none")
1167 <<
"\" fill-rule=\""
1168 << (p.fillRule() == Qt::OddEvenFill ?
"evenodd" :
"nonzero")
1172 QPointF subPathStart;
1173 auto endSubPath = [&]() {
1175 const QPointF subPathEnd = p.elementAt(i - 1);
1176 if (subPathEnd == subPathStart)
1181 bool inCurveToElement =
false;
1182 for (i = 0; i < p.elementCount(); ++i) {
1183 const QPainterPath::Element &e = p.elementAt(i);
1185 case QPainterPath::MoveToElement:
1188 *d->stream <<
'M' << e.x <<
',' << e.y;
1190 case QPainterPath::LineToElement:
1191 *d->stream <<
'L' << e.x <<
',' << e.y;
1193 case QPainterPath::CurveToElement:
1194 *d->stream <<
'C' << e.x <<
',' << e.y;
1196 case QPainterPath::CurveToDataElement:
1198 *d->stream << e.x <<
',' << e.y;
1204 if (e.type != QPainterPath::CurveToDataElement)
1205 inCurveToElement = (e.type == QPainterPath::CurveToElement);
1209 *d->stream <<
"\"/>" << Qt::endl;
1213 PolygonDrawMode mode)
1215 Q_ASSERT(pointCount >= 2);
1219 if (mode == PolylineMode)
1220 stream() <<
"<polyline fill=\"none\"";
1221 else if (mode == OddEvenMode)
1222 stream() <<
"<polygon fill-rule=\"evenodd\"";
1223 else if (mode == WindingMode || mode == ConvexMode)
1224 stream() <<
"<polygon fill-rule=\"nonzero\"";
1226 stream() <<
" vector-effect=\""
1227 << (state->pen().isCosmetic() ?
"non-scaling-stroke" :
"none")
1229 for (
int i = 0; i < pointCount; ++i) {
1230 const QPointF &pt = points[i];
1231 stream() << pt.x() <<
',' << pt.y() <<
' ';
1233 stream() <<
"\" />" <<Qt::endl;
1239 Q_D(QSvgPaintEngine);
1241 for (
int i=0; i < rectCount; ++i) {
1242 const QRectF &rect = rects[i].normalized();
1243 *d->stream <<
"<rect";
1244 if (state->pen().isCosmetic())
1245 *d->stream <<
" vector-effect=\"non-scaling-stroke\"";
1246 *d->stream <<
" x=\"" << rect.x() <<
"\" y=\"" << rect.y()
1247 <<
"\" width=\"" << rect.width() <<
"\" height=\"" << rect.height()
1248 <<
"\"/>" << Qt::endl;
1254 Q_D(QSvgPaintEngine);
1255 if (d->pen.style() == Qt::NoPen)
1258 const QTextItemInt &ti =
static_cast<
const QTextItemInt &>(textItem);
1260 QPaintEngine::drawTextItem(pt, ti);
1261 QString s = QString::fromRawData(ti.chars, ti.num_chars);
1263 *d->stream <<
"<text "
1264 "fill=\"" << d->attributes.stroke <<
"\" "
1265 "fill-opacity=\"" << d->attributes.strokeOpacity <<
"\" "
1267 "xml:space=\"preserve\" "
1268 "x=\"" << pt.x() <<
"\" y=\"" << pt.y() <<
"\" ";
1269 qfontToSvg(textItem.font());
1271 << 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.
Combined button and popup list for selecting options.
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)
QStringList font_families
QString document_description