1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtSvg module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qsvggenerator.h"
44 #ifndef QT_NO_SVGGENERATOR
46 #include "qpainterpath.h"
48 #include "private/qpaintengine_p.h"
49 #include "private/qtextengine_p.h"
50 #include "private/qdrawhelper_p.h"
53 #include "qtextcodec.h"
54 #include "qtextstream.h"
62 static void translate_color(const QColor
&color
, QString
*color_string
,
63 QString
*opacity_string
)
65 Q_ASSERT(color_string
);
66 Q_ASSERT(opacity_string
);
69 QString::fromLatin1("#%1%2%3")
70 .arg(color
.red(), 2, 16, QLatin1Char('0'))
71 .arg(color
.green(), 2, 16, QLatin1Char('0'))
72 .arg(color
.blue(), 2, 16, QLatin1Char('0'));
73 *opacity_string
= QString::number(color
.alphaF());
76 static void translate_dashPattern(QVector
<qreal
> pattern
, const qreal
& width
, QString
*pattern_string
)
78 Q_ASSERT(pattern_string
);
80 // Note that SVG operates in absolute lengths, whereas Qt uses a length/width ratio.
81 foreach (qreal entry
, pattern
)
82 *pattern_string
+= QString::fromLatin1("%1,").arg(entry
* width
);
84 pattern_string
->chop(1);
87 class QSvgPaintEnginePrivate
: public QPaintEnginePrivate
90 QSvgPaintEnginePrivate()
97 attributes
.document_title
= QLatin1String("Qt Svg Document");
98 attributes
.document_description
= QLatin1String("Generated with Qt");
99 attributes
.font_family
= QLatin1String("serif");
100 attributes
.font_size
= QLatin1String("10pt");
101 attributes
.font_style
= QLatin1String("normal");
102 attributes
.font_weight
= QLatin1String("normal");
104 afterFirstUpdate
= false;
110 QIODevice
*outputDevice
;
117 bool afterFirstUpdate
;
124 QString
generateGradientName() {
126 currentGradientName
= QString::fromLatin1("gradient%1").arg(numGradients
);
127 return currentGradientName
;
130 QString currentGradientName
;
134 QString document_title
;
135 QString document_description
;
140 QString stroke
, strokeOpacity
;
141 QString dashPattern
, dashOffset
;
142 QString fill
, fillOpacity
;
146 static inline QPaintEngine::PaintEngineFeatures
svgEngineFeatures()
148 return QPaintEngine::PaintEngineFeatures(
149 QPaintEngine::AllFeatures
150 & ~QPaintEngine::PatternBrush
151 & ~QPaintEngine::PerspectiveTransform
152 & ~QPaintEngine::ConicalGradientFill
153 & ~QPaintEngine::PorterDuff
);
156 class QSvgPaintEngine
: public QPaintEngine
158 Q_DECLARE_PRIVATE(QSvgPaintEngine
)
162 : QPaintEngine(*new QSvgPaintEnginePrivate
,
167 bool begin(QPaintDevice
*device
);
170 void updateState(const QPaintEngineState
&state
);
173 void drawPath(const QPainterPath
&path
);
174 void drawPixmap(const QRectF
&r
, const QPixmap
&pm
, const QRectF
&sr
);
175 void drawPolygon(const QPointF
*points
, int pointCount
, PolygonDrawMode mode
);
176 void drawTextItem(const QPointF
&pt
, const QTextItem
&item
);
177 void drawImage(const QRectF
&r
, const QImage
&pm
, const QRectF
&sr
,
178 Qt::ImageConversionFlag
= Qt::AutoColor
);
180 QPaintEngine::Type
type() const { return QPaintEngine::SVG
; }
182 QSize
size() const { return d_func()->size
; }
183 void setSize(const QSize
&size
) {
184 Q_ASSERT(!isActive());
185 d_func()->size
= size
;
188 QRectF
viewBox() const { return d_func()->viewBox
; }
189 void setViewBox(const QRectF
&viewBox
) {
190 Q_ASSERT(!isActive());
191 d_func()->viewBox
= viewBox
;
194 QString
documentTitle() const { return d_func()->attributes
.document_title
; }
195 void setDocumentTitle(const QString
&title
) {
196 d_func()->attributes
.document_title
= title
;
199 QString
documentDescription() const { return d_func()->attributes
.document_description
; }
200 void setDocumentDescription(const QString
&description
) {
201 d_func()->attributes
.document_description
= description
;
204 QIODevice
*outputDevice() const { return d_func()->outputDevice
; }
205 void setOutputDevice(QIODevice
*device
) {
206 Q_ASSERT(!isActive());
207 d_func()->outputDevice
= device
;
210 int resolution() { return d_func()->resolution
; }
211 void setResolution(int resolution
) {
212 Q_ASSERT(!isActive());
213 d_func()->resolution
= resolution
;
215 void saveLinearGradientBrush(const QGradient
*g
)
217 QTextStream
str(&d_func()->defs
, QIODevice::Append
);
218 const QLinearGradient
*grad
= static_cast<const QLinearGradient
*>(g
);
219 str
<< QLatin1String("<linearGradient ");
220 saveGradientUnits(str
, g
);
222 str
<< QLatin1String("x1=\"") <<grad
->start().x()<< QLatin1String("\" ")
223 << QLatin1String("y1=\"") <<grad
->start().y()<< QLatin1String("\" ")
224 << QLatin1String("x2=\"") <<grad
->finalStop().x() << QLatin1String("\" ")
225 << QLatin1String("y2=\"") <<grad
->finalStop().y() << QLatin1String("\" ");
228 str
<< QLatin1String("id=\"") << d_func()->generateGradientName() << QLatin1String("\">\n");
229 saveGradientStops(str
, g
);
230 str
<< QLatin1String("</linearGradient>") <<endl
;
232 void saveRadialGradientBrush(const QGradient
*g
)
234 QTextStream
str(&d_func()->defs
, QIODevice::Append
);
235 const QRadialGradient
*grad
= static_cast<const QRadialGradient
*>(g
);
236 str
<< QLatin1String("<radialGradient ");
237 saveGradientUnits(str
, g
);
239 str
<< QLatin1String("cx=\"") <<grad
->center().x()<< QLatin1String("\" ")
240 << QLatin1String("cy=\"") <<grad
->center().y()<< QLatin1String("\" ")
241 << QLatin1String("r=\"") <<grad
->radius() << QLatin1String("\" ")
242 << QLatin1String("fx=\"") <<grad
->focalPoint().x() << QLatin1String("\" ")
243 << QLatin1String("fy=\"") <<grad
->focalPoint().y() << QLatin1String("\" ");
245 str
<< QLatin1String("xml:id=\"") <<d_func()->generateGradientName()<< QLatin1String("\">\n");
246 saveGradientStops(str
, g
);
247 str
<< QLatin1String("</radialGradient>") << endl
;
249 void saveConicalGradientBrush(const QGradient
*)
251 qWarning("svg's don't support conical gradients!");
254 void saveGradientStops(QTextStream
&str
, const QGradient
*g
) {
255 QGradientStops stops
= g
->stops();
257 if (g
->interpolationMode() == QGradient::ColorInterpolation
) {
258 bool constantAlpha
= true;
259 int alpha
= stops
.at(0).second
.alpha();
260 for (int i
= 1; i
< stops
.size(); ++i
)
261 constantAlpha
&= (stops
.at(i
).second
.alpha() == alpha
);
263 if (!constantAlpha
) {
264 const qreal spacing
= qreal(0.02);
265 QGradientStops newStops
;
266 QRgb fromColor
= PREMUL(stops
.at(0).second
.rgba());
268 for (int i
= 0; i
+ 1 < stops
.size(); ++i
) {
269 int parts
= qCeil((stops
.at(i
+ 1).first
- stops
.at(i
).first
) / spacing
);
270 newStops
.append(stops
.at(i
));
271 toColor
= PREMUL(stops
.at(i
+ 1).second
.rgba());
274 qreal step
= (stops
.at(i
+ 1).first
- stops
.at(i
).first
) / parts
;
275 for (int j
= 1; j
< parts
; ++j
) {
276 QRgb color
= INV_PREMUL(INTERPOLATE_PIXEL_256(fromColor
, 256 - 256 * j
/ parts
, toColor
, 256 * j
/ parts
));
277 newStops
.append(QGradientStop(stops
.at(i
).first
+ j
* step
, QColor::fromRgba(color
)));
282 newStops
.append(stops
.back());
287 foreach(QGradientStop stop
, stops
) {
289 QString::fromLatin1("#%1%2%3")
290 .arg(stop
.second
.red(), 2, 16, QLatin1Char('0'))
291 .arg(stop
.second
.green(), 2, 16, QLatin1Char('0'))
292 .arg(stop
.second
.blue(), 2, 16, QLatin1Char('0'));
293 str
<< QLatin1String(" <stop offset=\"")<< stop
.first
<< QLatin1String("\" ")
294 << QLatin1String("stop-color=\"") << color
<< QLatin1String("\" ")
295 << QLatin1String("stop-opacity=\"") << stop
.second
.alphaF() <<QLatin1String("\" />\n");
299 void saveGradientUnits(QTextStream
&str
, const QGradient
*gradient
)
301 str
<< QLatin1String("gradientUnits=\"");
302 if (gradient
&& gradient
->coordinateMode() == QGradient::ObjectBoundingMode
)
303 str
<< QLatin1String("objectBoundingBox");
305 str
<< QLatin1String("userSpaceOnUse");
306 str
<< QLatin1String("\" ");
309 void generateQtDefaults()
311 *d_func()->stream
<< QLatin1String("fill=\"none\" ");
312 *d_func()->stream
<< QLatin1String("stroke=\"black\" ");
313 *d_func()->stream
<< QLatin1String("vector-effect=\"non-scaling-stroke\" ");
314 *d_func()->stream
<< QLatin1String("stroke-width=\"1\" ");
315 *d_func()->stream
<< QLatin1String("fill-rule=\"evenodd\" ");
316 *d_func()->stream
<< QLatin1String("stroke-linecap=\"square\" ");
317 *d_func()->stream
<< QLatin1String("stroke-linejoin=\"bevel\" ");
318 *d_func()->stream
<< QLatin1String(">\n");
320 inline QTextStream
&stream()
322 return *d_func()->stream
;
326 void qpenToSvg(const QPen
&spen
)
330 d_func()->pen
= spen
;
332 switch (spen
.style()) {
334 stream() << QLatin1String("stroke=\"none\" ");
336 d_func()->attributes
.stroke
= QLatin1String("none");
337 d_func()->attributes
.strokeOpacity
= QString();
340 case Qt::SolidLine
: {
341 QString color
, colorOpacity
;
343 translate_color(spen
.color(), &color
,
345 d_func()->attributes
.stroke
= color
;
346 d_func()->attributes
.strokeOpacity
= colorOpacity
;
348 stream() << QLatin1String("stroke=\"")<<color
<< QLatin1String("\" ");
349 stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity
<< QLatin1String("\" ");
354 case Qt::DashDotLine
:
355 case Qt::DashDotDotLine
:
356 case Qt::CustomDashLine
: {
357 QString color
, colorOpacity
, dashPattern
, dashOffset
;
359 qreal penWidth
= spen
.width() == 0 ? qreal(1) : spen
.widthF();
361 translate_color(spen
.color(), &color
, &colorOpacity
);
362 translate_dashPattern(spen
.dashPattern(), penWidth
, &dashPattern
);
364 // SVG uses absolute offset
365 dashOffset
= QString::fromLatin1("%1").arg(spen
.dashOffset() * penWidth
);
367 d_func()->attributes
.stroke
= color
;
368 d_func()->attributes
.strokeOpacity
= colorOpacity
;
369 d_func()->attributes
.dashPattern
= dashPattern
;
370 d_func()->attributes
.dashOffset
= dashOffset
;
372 stream() << QLatin1String("stroke=\"")<<color
<< QLatin1String("\" ");
373 stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity
<< QLatin1String("\" ");
374 stream() << QLatin1String("stroke-dasharray=\"")<<dashPattern
<< QLatin1String("\" ");
375 stream() << QLatin1String("stroke-dashoffset=\"")<<dashOffset
<< QLatin1String("\" ");
379 qWarning("Unsupported pen style");
383 if (spen
.widthF() == 0) {
384 width
= QLatin1String("1");
385 stream() << "vector-effect=\"non-scaling-stroke\" ";
388 width
= QString::number(spen
.widthF());
389 stream() <<"stroke-width=\""<<width
<<"\" ";
391 switch (spen
.capStyle()) {
393 stream() << "stroke-linecap=\"butt\" ";
396 stream() << "stroke-linecap=\"square\" ";
399 stream() << "stroke-linecap=\"round\" ";
402 qWarning("Unhandled cap style");
404 switch (spen
.joinStyle()) {
406 stream() << "stroke-linejoin=\"miter\" ";
407 stream() << "stroke-miterlimit=\""<<spen
.miterLimit()<<"\" ";
410 stream() << "stroke-linejoin=\"bevel\" ";
413 stream() << "stroke-linejoin=\"round\" ";
415 case Qt::SvgMiterJoin
:
416 stream() << "stroke-linejoin=\"miter\" ";
417 stream() << "stroke-miterlimit=\""<<spen
.miterLimit()<<"\" ";
420 qWarning("Unhandled join style");
423 void qbrushToSvg(const QBrush
&sbrush
)
425 d_func()->brush
= sbrush
;
426 switch (sbrush
.style()) {
427 case Qt::SolidPattern
: {
428 QString color
, colorOpacity
;
429 translate_color(sbrush
.color(), &color
, &colorOpacity
);
430 stream() << "fill=\"" << color
<< "\" ";
431 stream() << "fill-opacity=\""
432 << colorOpacity
<< "\" ";
433 d_func()->attributes
.fill
= color
;
434 d_func()->attributes
.fillOpacity
= colorOpacity
;
437 case Qt::LinearGradientPattern
:
438 saveLinearGradientBrush(sbrush
.gradient());
439 d_func()->attributes
.fill
= QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName
);
440 d_func()->attributes
.fillOpacity
= QString();
441 stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName
<< QLatin1String(")\" ");
443 case Qt::RadialGradientPattern
:
444 saveRadialGradientBrush(sbrush
.gradient());
445 d_func()->attributes
.fill
= QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName
);
446 d_func()->attributes
.fillOpacity
= QString();
447 stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName
<< QLatin1String(")\" ");
449 case Qt::ConicalGradientPattern
:
450 saveConicalGradientBrush(sbrush
.gradient());
451 d_func()->attributes
.fill
= QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName
);
452 d_func()->attributes
.fillOpacity
= QString();
453 stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName
<< QLatin1String(")\" ");
456 stream() << QLatin1String("fill=\"none\" ");
457 d_func()->attributes
.fill
= QLatin1String("none");
458 d_func()->attributes
.fillOpacity
= QString();
465 void qfontToSvg(const QFont
&sfont
)
467 Q_D(QSvgPaintEngine
);
471 if (d
->font
.pixelSize() == -1)
472 d
->attributes
.font_size
= QString::number(d
->font
.pointSizeF() * d
->resolution
/ 72);
474 d
->attributes
.font_size
= QString::number(d
->font
.pixelSize());
476 int svgWeight
= d
->font
.weight();
491 d
->attributes
.font_weight
= QString::number(svgWeight
);
492 d
->attributes
.font_family
= d
->font
.family();
493 d
->attributes
.font_style
= d
->font
.italic() ? QLatin1String("italic") : QLatin1String("normal");
495 *d
->stream
<< "font-family=\"" << d
->attributes
.font_family
<< "\" "
496 << "font-size=\"" << d
->attributes
.font_size
<< "\" "
497 << "font-weight=\"" << d
->attributes
.font_weight
<< "\" "
498 << "font-style=\"" << d
->attributes
.font_style
<< "\" "
503 class QSvgGeneratorPrivate
506 QSvgPaintEngine
*engine
;
508 uint owns_iodevice
: 1;
516 \brief The QSvgGenerator class provides a paint device that is used to create SVG drawings.
519 This paint device represents a Scalable Vector Graphics (SVG) drawing. Like QPrinter, it is
520 designed as a write-only device that generates output in a specific format.
522 To write an SVG file, you first need to configure the output by setting the \l fileName
523 or \l outputDevice properties. It is usually necessary to specify the size of the drawing
524 by setting the \l size property, and in some cases where the drawing will be included in
525 another, the \l viewBox property also needs to be set.
527 \snippet examples/painting/svggenerator/window.cpp configure SVG generator
529 Other meta-data can be specified by setting the \a title, \a description and \a resolution
532 As with other QPaintDevice subclasses, a QPainter object is used to paint onto an instance
535 \snippet examples/painting/svggenerator/window.cpp begin painting
537 \snippet examples/painting/svggenerator/window.cpp end painting
539 Painting is performed in the same way as for any other paint device. However,
540 it is necessary to use the QPainter::begin() and \l{QPainter::}{end()} to
541 explicitly begin and end painting on the device.
543 The \l{SVG Generator Example} shows how the same painting commands can be used
544 for painting a widget and writing an SVG file.
546 \sa QSvgRenderer, QSvgWidget, {About SVG}
550 Constructs a new generator.
552 QSvgGenerator::QSvgGenerator()
553 : d_ptr(new QSvgGeneratorPrivate
)
557 d
->engine
= new QSvgPaintEngine
;
558 d
->owns_iodevice
= false;
562 Destroys the generator.
564 QSvgGenerator::~QSvgGenerator()
567 if (d
->owns_iodevice
)
568 delete d
->engine
->outputDevice();
574 \property QSvgGenerator::title
575 \brief the title of the generated SVG drawing
579 QString
QSvgGenerator::title() const
581 Q_D(const QSvgGenerator
);
583 return d
->engine
->documentTitle();
586 void QSvgGenerator::setTitle(const QString
&title
)
590 d
->engine
->setDocumentTitle(title
);
594 \property QSvgGenerator::description
595 \brief the description of the generated SVG drawing
599 QString
QSvgGenerator::description() const
601 Q_D(const QSvgGenerator
);
603 return d
->engine
->documentDescription();
606 void QSvgGenerator::setDescription(const QString
&description
)
610 d
->engine
->setDocumentDescription(description
);
614 \property QSvgGenerator::size
615 \brief the size of the generated SVG drawing
618 By default this property is set to \c{QSize(-1, -1)}, which
619 indicates that the generator should not output the width and
620 height attributes of the \c<svg> element.
622 \note It is not possible to change this property while a
623 QPainter is active on the generator.
625 \sa viewBox, resolution
627 QSize
QSvgGenerator::size() const
629 Q_D(const QSvgGenerator
);
630 return d
->engine
->size();
633 void QSvgGenerator::setSize(const QSize
&size
)
636 if (d
->engine
->isActive()) {
637 qWarning("QSvgGenerator::setSize(), cannot set size while SVG is being generated");
640 d
->engine
->setSize(size
);
644 \property QSvgGenerator::viewBox
645 \brief the viewBox of the generated SVG drawing
648 By default this property is set to \c{QRect(0, 0, -1, -1)}, which
649 indicates that the generator should not output the viewBox attribute
650 of the \c<svg> element.
652 \note It is not possible to change this property while a
653 QPainter is active on the generator.
655 \sa viewBox(), size, resolution
657 QRectF
QSvgGenerator::viewBoxF() const
659 Q_D(const QSvgGenerator
);
660 return d
->engine
->viewBox();
666 Returns viewBoxF().toRect().
670 QRect
QSvgGenerator::viewBox() const
672 Q_D(const QSvgGenerator
);
673 return d
->engine
->viewBox().toRect();
676 void QSvgGenerator::setViewBox(const QRectF
&viewBox
)
679 if (d
->engine
->isActive()) {
680 qWarning("QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated");
683 d
->engine
->setViewBox(viewBox
);
686 void QSvgGenerator::setViewBox(const QRect
&viewBox
)
688 setViewBox(QRectF(viewBox
));
692 \property QSvgGenerator::fileName
693 \brief the target filename for the generated SVG drawing
698 QString
QSvgGenerator::fileName() const
700 Q_D(const QSvgGenerator
);
704 void QSvgGenerator::setFileName(const QString
&fileName
)
707 if (d
->engine
->isActive()) {
708 qWarning("QSvgGenerator::setFileName(), cannot set file name while SVG is being generated");
712 if (d
->owns_iodevice
)
713 delete d
->engine
->outputDevice();
715 d
->owns_iodevice
= true;
717 d
->fileName
= fileName
;
718 QFile
*file
= new QFile(fileName
);
719 d
->engine
->setOutputDevice(file
);
723 \property QSvgGenerator::outputDevice
724 \brief the output device for the generated SVG drawing
727 If both output device and file name are specified, the output device
728 will have precedence.
732 QIODevice
*QSvgGenerator::outputDevice() const
734 Q_D(const QSvgGenerator
);
735 return d
->engine
->outputDevice();
738 void QSvgGenerator::setOutputDevice(QIODevice
*outputDevice
)
741 if (d
->engine
->isActive()) {
742 qWarning("QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated");
745 d
->owns_iodevice
= false;
746 d
->engine
->setOutputDevice(outputDevice
);
747 d
->fileName
= QString();
751 \property QSvgGenerator::resolution
752 \brief the resolution of the generated output
755 The resolution is specified in dots per inch, and is used to
756 calculate the physical size of an SVG drawing.
760 int QSvgGenerator::resolution() const
762 Q_D(const QSvgGenerator
);
763 return d
->engine
->resolution();
766 void QSvgGenerator::setResolution(int dpi
)
769 d
->engine
->setResolution(dpi
);
773 Returns the paint engine used to render graphics to be converted to SVG
776 QPaintEngine
*QSvgGenerator::paintEngine() const
778 Q_D(const QSvgGenerator
);
785 int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric
) const
787 Q_D(const QSvgGenerator
);
789 case QPaintDevice::PdmDepth
:
791 case QPaintDevice::PdmWidth
:
792 return d
->engine
->size().width();
793 case QPaintDevice::PdmHeight
:
794 return d
->engine
->size().height();
795 case QPaintDevice::PdmDpiX
:
796 return d
->engine
->resolution();
797 case QPaintDevice::PdmDpiY
:
798 return d
->engine
->resolution();
799 case QPaintDevice::PdmHeightMM
:
800 return qRound(d
->engine
->size().height() * 25.4 / d
->engine
->resolution());
801 case QPaintDevice::PdmWidthMM
:
802 return qRound(d
->engine
->size().width() * 25.4 / d
->engine
->resolution());
803 case QPaintDevice::PdmNumColors
:
805 case QPaintDevice::PdmPhysicalDpiX
:
806 return d
->engine
->resolution();
807 case QPaintDevice::PdmPhysicalDpiY
:
808 return d
->engine
->resolution();
810 qWarning("QSvgGenerator::metric(), unhandled metric %d\n", metric
);
816 /*****************************************************************************
817 * class QSvgPaintEngine
820 bool QSvgPaintEngine::begin(QPaintDevice
*)
822 Q_D(QSvgPaintEngine
);
823 if (!d
->outputDevice
) {
824 qWarning("QSvgPaintEngine::begin(), no output device");
828 if (!d
->outputDevice
->isOpen()) {
829 if (!d
->outputDevice
->open(QIODevice::WriteOnly
| QIODevice::Text
)) {
830 qWarning("QSvgPaintEngine::begin(), could not open output device: '%s'",
831 qPrintable(d
->outputDevice
->errorString()));
834 } else if (!d
->outputDevice
->isWritable()) {
835 qWarning("QSvgPaintEngine::begin(), could not write to read-only output device: '%s'",
836 qPrintable(d
->outputDevice
->errorString()));
840 d
->stream
= new QTextStream(&d
->header
);
842 // stream out the header...
843 *d
->stream
<< "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << endl
<< "<svg";
845 if (d
->size
.isValid()) {
846 qreal wmm
= d
->size
.width() * 25.4 / d
->resolution
;
847 qreal hmm
= d
->size
.height() * 25.4 / d
->resolution
;
848 *d
->stream
<< " width=\"" << wmm
<< "mm\" height=\"" << hmm
<< "mm\"" << endl
;
851 if (d
->viewBox
.isValid()) {
852 *d
->stream
<< " viewBox=\"" << d
->viewBox
.left() << " " << d
->viewBox
.top();
853 *d
->stream
<< " " << d
->viewBox
.width() << " " << d
->viewBox
.height() << "\"" << endl
;
856 *d
->stream
<< " xmlns=\"http://www.w3.org/2000/svg\""
857 << " xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
858 << " version=\"1.2\" baseProfile=\"tiny\">" << endl
;
860 if (!d
->attributes
.document_title
.isEmpty()) {
861 *d
->stream
<< "<title>" << d
->attributes
.document_title
<< "</title>" << endl
;
864 if (!d
->attributes
.document_description
.isEmpty()) {
865 *d
->stream
<< "<desc>" << d
->attributes
.document_description
<< "</desc>" << endl
;
868 d
->stream
->setString(&d
->defs
);
869 *d
->stream
<< "<defs>\n";
871 d
->stream
->setString(&d
->body
);
872 // Start the initial graphics state...
874 generateQtDefaults();
880 bool QSvgPaintEngine::end()
882 Q_D(QSvgPaintEngine
);
884 d
->stream
->setString(&d
->defs
);
885 *d
->stream
<< "</defs>\n";
887 d
->stream
->setDevice(d
->outputDevice
);
888 #ifndef QT_NO_TEXTCODEC
889 d
->stream
->setCodec(QTextCodec::codecForName("UTF-8"));
892 *d
->stream
<< d
->header
;
893 *d
->stream
<< d
->defs
;
894 *d
->stream
<< d
->body
;
895 if (d
->afterFirstUpdate
)
896 *d
->stream
<< "</g>" << endl
; // close the updateState
898 *d
->stream
<< "</g>" << endl
// close the Qt defaults
906 void QSvgPaintEngine::drawPixmap(const QRectF
&r
, const QPixmap
&pm
,
909 drawImage(r
, pm
.toImage(), sr
);
912 void QSvgPaintEngine::drawImage(const QRectF
&r
, const QImage
&image
,
914 Qt::ImageConversionFlag flags
)
916 //Q_D(QSvgPaintEngine);
920 stream() << "<image ";
921 stream() << "x=\""<<r
.x()<<"\" ";
922 stream() << "y=\""<<r
.y()<<"\" ";
923 stream() << "width=\""<<r
.width()<<"\" ";
924 stream() << "height=\""<<r
.height()<<"\" ";
927 QBuffer
buffer(&data
);
928 buffer
.open(QBuffer::ReadWrite
);
929 image
.save(&buffer
, "PNG");
931 stream() << "xlink:href=\"data:image/png;base64,"
937 void QSvgPaintEngine::updateState(const QPaintEngineState
&state
)
939 Q_D(QSvgPaintEngine
);
940 QPaintEngine::DirtyFlags flags
= state
.state();
942 // always stream full gstate, which is not required, but...
943 flags
|= QPaintEngine::AllDirty
;
945 // close old state and start a new one...
946 if (d
->afterFirstUpdate
)
947 *d
->stream
<< "</g>\n\n";
951 if (flags
& QPaintEngine::DirtyBrush
) {
952 qbrushToSvg(state
.brush());
955 if (flags
& QPaintEngine::DirtyPen
) {
956 qpenToSvg(state
.pen());
959 if (flags
& QPaintEngine::DirtyTransform
) {
960 d
->matrix
= state
.matrix();
961 *d
->stream
<< "transform=\"matrix(" << d
->matrix
.m11() << ","
962 << d
->matrix
.m12() << ","
963 << d
->matrix
.m21() << "," << d
->matrix
.m22() << ","
964 << d
->matrix
.dx() << "," << d
->matrix
.dy()
969 if (flags
& QPaintEngine::DirtyFont
) {
970 qfontToSvg(state
.font());
973 if (flags
& QPaintEngine::DirtyOpacity
) {
974 if (!qFuzzyCompare(state
.opacity(), 1))
975 stream() << "opacity=\""<<state
.opacity()<<"\" ";
978 *d
->stream
<< ">" << endl
;
980 d
->afterFirstUpdate
= true;
983 void QSvgPaintEngine::drawPath(const QPainterPath
&p
)
985 Q_D(QSvgPaintEngine
);
987 *d
->stream
<< "<path ";
990 *d
->stream
<< "fill-rule=";
991 if (p
.fillRule() == Qt::OddEvenFill
)
992 *d
->stream
<< "\"evenodd\" ";
994 *d
->stream
<< "\"nonzero\" ";
996 *d
->stream
<< "d=\"";
998 for (int i
=0; i
<p
.elementCount(); ++i
) {
999 const QPainterPath::Element
&e
= p
.elementAt(i
);
1001 case QPainterPath::MoveToElement
:
1002 *d
->stream
<< "M" << e
.x
<< "," << e
.y
;
1004 case QPainterPath::LineToElement
:
1005 *d
->stream
<< "L" << e
.x
<< "," << e
.y
;
1007 case QPainterPath::CurveToElement
:
1008 *d
->stream
<< "C" << e
.x
<< "," << e
.y
;
1010 while (i
< p
.elementCount()) {
1011 const QPainterPath::Element
&e
= p
.elementAt(i
);
1012 if (e
.type
!= QPainterPath::CurveToDataElement
) {
1017 *d
->stream
<< e
.x
<< "," << e
.y
;
1024 if (i
!= p
.elementCount() - 1) {
1029 *d
->stream
<< "\"/>" << endl
;
1032 void QSvgPaintEngine::drawPolygon(const QPointF
*points
, int pointCount
,
1033 PolygonDrawMode mode
)
1035 Q_ASSERT(pointCount
>= 2);
1037 //Q_D(QSvgPaintEngine);
1039 QPainterPath
path(points
[0]);
1040 for (int i
=1; i
<pointCount
; ++i
)
1041 path
.lineTo(points
[i
]);
1043 if (mode
== PolylineMode
) {
1044 stream() << "<polyline fill=\"none\" points=\"";
1045 for (int i
= 0; i
< pointCount
; ++i
) {
1046 const QPointF
&pt
= points
[i
];
1047 stream() << pt
.x() << "," << pt
.y() << " ";
1049 stream() << "\" />" <<endl
;
1051 path
.closeSubpath();
1056 void QSvgPaintEngine::drawTextItem(const QPointF
&pt
, const QTextItem
&textItem
)
1058 Q_D(QSvgPaintEngine
);
1059 if (d
->pen
.style() == Qt::NoPen
)
1062 const QTextItemInt
&ti
= static_cast<const QTextItemInt
&>(textItem
);
1063 QString s
= QString::fromRawData(ti
.chars
, ti
.num_chars
);
1065 *d
->stream
<< "<text "
1066 << "fill=\"" << d
->attributes
.stroke
<< "\" "
1067 << "fill-opacity=\"" << d
->attributes
.strokeOpacity
<< "\" "
1068 << "stroke=\"none\" "
1069 << "x=\"" << pt
.x() << "\" y=\"" << pt
.y() << "\" ";
1070 qfontToSvg(textItem
.font());
1079 #endif // QT_NO_SVGGENERATOR