Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into master-integration
[qt-netbsd.git] / src / svg / qsvggenerator.cpp
blob4a8fc0bd2915fc4a82260a70966a80091a934464
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtSvg module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
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 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
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"
52 #include "qfile.h"
53 #include "qtextcodec.h"
54 #include "qtextstream.h"
55 #include "qbuffer.h"
56 #include "qmath.h"
58 #include "qdebug.h"
60 QT_BEGIN_NAMESPACE
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);
68 *color_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
89 public:
90 QSvgPaintEnginePrivate()
92 size = QSize();
93 viewBox = QRectF();
94 outputDevice = 0;
95 resolution = 72;
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;
105 numGradients = 0;
108 QSize size;
109 QRectF viewBox;
110 QIODevice *outputDevice;
111 QTextStream *stream;
112 int resolution;
114 QString header;
115 QString defs;
116 QString body;
117 bool afterFirstUpdate;
119 QBrush brush;
120 QPen pen;
121 QMatrix matrix;
122 QFont font;
124 QString generateGradientName() {
125 ++numGradients;
126 currentGradientName = QString::fromLatin1("gradient%1").arg(numGradients);
127 return currentGradientName;
130 QString currentGradientName;
131 int numGradients;
133 struct _attributes {
134 QString document_title;
135 QString document_description;
136 QString font_weight;
137 QString font_size;
138 QString font_family;
139 QString font_style;
140 QString stroke, strokeOpacity;
141 QString dashPattern, dashOffset;
142 QString fill, fillOpacity;
143 } attributes;
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)
159 public:
161 QSvgPaintEngine()
162 : QPaintEngine(*new QSvgPaintEnginePrivate,
163 svgEngineFeatures())
167 bool begin(QPaintDevice *device);
168 bool end();
170 void updateState(const QPaintEngineState &state);
171 void popGroup();
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);
221 if (grad) {
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);
238 if (grad) {
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());
267 QRgb toColor;
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());
273 if (parts > 1) {
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)));
280 fromColor = toColor;
282 newStops.append(stops.back());
283 stops = newStops;
287 foreach(QGradientStop stop, stops) {
288 QString color =
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");
304 else
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)
328 QString width;
330 d_func()->pen = spen;
332 switch (spen.style()) {
333 case Qt::NoPen:
334 stream() << QLatin1String("stroke=\"none\" ");
336 d_func()->attributes.stroke = QLatin1String("none");
337 d_func()->attributes.strokeOpacity = QString();
338 return;
339 break;
340 case Qt::SolidLine: {
341 QString color, colorOpacity;
343 translate_color(spen.color(), &color,
344 &colorOpacity);
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("\" ");
351 break;
352 case Qt::DashLine:
353 case Qt::DotLine:
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::number(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("\" ");
376 break;
378 default:
379 qWarning("Unsupported pen style");
380 break;
383 if (spen.widthF() == 0) {
384 width = QLatin1String("1");
385 stream() << "vector-effect=\"non-scaling-stroke\" ";
387 else
388 width = QString::number(spen.widthF());
389 stream() <<"stroke-width=\""<<width<<"\" ";
391 switch (spen.capStyle()) {
392 case Qt::FlatCap:
393 stream() << "stroke-linecap=\"butt\" ";
394 break;
395 case Qt::SquareCap:
396 stream() << "stroke-linecap=\"square\" ";
397 break;
398 case Qt::RoundCap:
399 stream() << "stroke-linecap=\"round\" ";
400 break;
401 default:
402 qWarning("Unhandled cap style");
404 switch (spen.joinStyle()) {
405 case Qt::MiterJoin:
406 stream() << "stroke-linejoin=\"miter\" "
407 "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
408 break;
409 case Qt::BevelJoin:
410 stream() << "stroke-linejoin=\"bevel\" ";
411 break;
412 case Qt::RoundJoin:
413 stream() << "stroke-linejoin=\"round\" ";
414 break;
415 case Qt::SvgMiterJoin:
416 stream() << "stroke-linejoin=\"miter\" "
417 "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
418 break;
419 default:
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 "fill-opacity=\""
432 << colorOpacity << "\" ";
433 d_func()->attributes.fill = color;
434 d_func()->attributes.fillOpacity = colorOpacity;
436 break;
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(")\" ");
442 break;
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(")\" ");
448 break;
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(")\" ");
454 break;
455 case Qt::NoBrush:
456 stream() << QLatin1String("fill=\"none\" ");
457 d_func()->attributes.fill = QLatin1String("none");
458 d_func()->attributes.fillOpacity = QString();
459 return;
460 break;
461 default:
462 break;
465 void qfontToSvg(const QFont &sfont)
467 Q_D(QSvgPaintEngine);
469 d->font = sfont;
471 if (d->font.pixelSize() == -1)
472 d->attributes.font_size = QString::number(d->font.pointSizeF() * d->resolution / 72);
473 else
474 d->attributes.font_size = QString::number(d->font.pixelSize());
476 int svgWeight = d->font.weight();
477 switch (svgWeight) {
478 case QFont::Light:
479 svgWeight = 100;
480 break;
481 case QFont::Normal:
482 svgWeight = 400;
483 break;
484 case QFont::Bold:
485 svgWeight = 700;
486 break;
487 default:
488 svgWeight *= 10;
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 << "\" "
499 << endl;
503 class QSvgGeneratorPrivate
505 public:
506 QSvgPaintEngine *engine;
508 uint owns_iodevice : 1;
509 QString fileName;
513 \class QSvgGenerator
514 \ingroup painting
515 \since 4.3
516 \brief The QSvgGenerator class provides a paint device that is used to create SVG drawings.
517 \reentrant
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
530 properties.
532 As with other QPaintDevice subclasses, a QPainter object is used to paint onto an instance
533 of this class:
535 \snippet examples/painting/svggenerator/window.cpp begin painting
536 \dots
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)
555 Q_D(QSvgGenerator);
557 d->engine = new QSvgPaintEngine;
558 d->owns_iodevice = false;
562 Destroys the generator.
564 QSvgGenerator::~QSvgGenerator()
566 Q_D(QSvgGenerator);
567 if (d->owns_iodevice)
568 delete d->engine->outputDevice();
569 delete d->engine;
573 \property QSvgGenerator::title
574 \brief the title of the generated SVG drawing
575 \since 4.5
576 \sa description
578 QString QSvgGenerator::title() const
580 Q_D(const QSvgGenerator);
582 return d->engine->documentTitle();
585 void QSvgGenerator::setTitle(const QString &title)
587 Q_D(QSvgGenerator);
589 d->engine->setDocumentTitle(title);
593 \property QSvgGenerator::description
594 \brief the description of the generated SVG drawing
595 \since 4.5
596 \sa title
598 QString QSvgGenerator::description() const
600 Q_D(const QSvgGenerator);
602 return d->engine->documentDescription();
605 void QSvgGenerator::setDescription(const QString &description)
607 Q_D(QSvgGenerator);
609 d->engine->setDocumentDescription(description);
613 \property QSvgGenerator::size
614 \brief the size of the generated SVG drawing
615 \since 4.5
617 By default this property is set to \c{QSize(-1, -1)}, which
618 indicates that the generator should not output the width and
619 height attributes of the \c<svg> element.
621 \note It is not possible to change this property while a
622 QPainter is active on the generator.
624 \sa viewBox, resolution
626 QSize QSvgGenerator::size() const
628 Q_D(const QSvgGenerator);
629 return d->engine->size();
632 void QSvgGenerator::setSize(const QSize &size)
634 Q_D(QSvgGenerator);
635 if (d->engine->isActive()) {
636 qWarning("QSvgGenerator::setSize(), cannot set size while SVG is being generated");
637 return;
639 d->engine->setSize(size);
643 \property QSvgGenerator::viewBox
644 \brief the viewBox of the generated SVG drawing
645 \since 4.5
647 By default this property is set to \c{QRect(0, 0, -1, -1)}, which
648 indicates that the generator should not output the viewBox attribute
649 of the \c<svg> element.
651 \note It is not possible to change this property while a
652 QPainter is active on the generator.
654 \sa viewBox(), size, resolution
656 QRectF QSvgGenerator::viewBoxF() const
658 Q_D(const QSvgGenerator);
659 return d->engine->viewBox();
663 \since 4.5
665 Returns viewBoxF().toRect().
667 \sa viewBoxF()
669 QRect QSvgGenerator::viewBox() const
671 Q_D(const QSvgGenerator);
672 return d->engine->viewBox().toRect();
675 void QSvgGenerator::setViewBox(const QRectF &viewBox)
677 Q_D(QSvgGenerator);
678 if (d->engine->isActive()) {
679 qWarning("QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated");
680 return;
682 d->engine->setViewBox(viewBox);
685 void QSvgGenerator::setViewBox(const QRect &viewBox)
687 setViewBox(QRectF(viewBox));
691 \property QSvgGenerator::fileName
692 \brief the target filename for the generated SVG drawing
693 \since 4.5
695 \sa outputDevice
697 QString QSvgGenerator::fileName() const
699 Q_D(const QSvgGenerator);
700 return d->fileName;
703 void QSvgGenerator::setFileName(const QString &fileName)
705 Q_D(QSvgGenerator);
706 if (d->engine->isActive()) {
707 qWarning("QSvgGenerator::setFileName(), cannot set file name while SVG is being generated");
708 return;
711 if (d->owns_iodevice)
712 delete d->engine->outputDevice();
714 d->owns_iodevice = true;
716 d->fileName = fileName;
717 QFile *file = new QFile(fileName);
718 d->engine->setOutputDevice(file);
722 \property QSvgGenerator::outputDevice
723 \brief the output device for the generated SVG drawing
724 \since 4.5
726 If both output device and file name are specified, the output device
727 will have precedence.
729 \sa fileName
731 QIODevice *QSvgGenerator::outputDevice() const
733 Q_D(const QSvgGenerator);
734 return d->engine->outputDevice();
737 void QSvgGenerator::setOutputDevice(QIODevice *outputDevice)
739 Q_D(QSvgGenerator);
740 if (d->engine->isActive()) {
741 qWarning("QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated");
742 return;
744 d->owns_iodevice = false;
745 d->engine->setOutputDevice(outputDevice);
746 d->fileName = QString();
750 \property QSvgGenerator::resolution
751 \brief the resolution of the generated output
752 \since 4.5
754 The resolution is specified in dots per inch, and is used to
755 calculate the physical size of an SVG drawing.
757 \sa size, viewBox
759 int QSvgGenerator::resolution() const
761 Q_D(const QSvgGenerator);
762 return d->engine->resolution();
765 void QSvgGenerator::setResolution(int dpi)
767 Q_D(QSvgGenerator);
768 d->engine->setResolution(dpi);
772 Returns the paint engine used to render graphics to be converted to SVG
773 format information.
775 QPaintEngine *QSvgGenerator::paintEngine() const
777 Q_D(const QSvgGenerator);
778 return d->engine;
782 \reimp
784 int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric) const
786 Q_D(const QSvgGenerator);
787 switch (metric) {
788 case QPaintDevice::PdmDepth:
789 return 32;
790 case QPaintDevice::PdmWidth:
791 return d->engine->size().width();
792 case QPaintDevice::PdmHeight:
793 return d->engine->size().height();
794 case QPaintDevice::PdmDpiX:
795 return d->engine->resolution();
796 case QPaintDevice::PdmDpiY:
797 return d->engine->resolution();
798 case QPaintDevice::PdmHeightMM:
799 return qRound(d->engine->size().height() * 25.4 / d->engine->resolution());
800 case QPaintDevice::PdmWidthMM:
801 return qRound(d->engine->size().width() * 25.4 / d->engine->resolution());
802 case QPaintDevice::PdmNumColors:
803 return 0xffffffff;
804 case QPaintDevice::PdmPhysicalDpiX:
805 return d->engine->resolution();
806 case QPaintDevice::PdmPhysicalDpiY:
807 return d->engine->resolution();
808 default:
809 qWarning("QSvgGenerator::metric(), unhandled metric %d\n", metric);
810 break;
812 return 0;
815 /*****************************************************************************
816 * class QSvgPaintEngine
819 bool QSvgPaintEngine::begin(QPaintDevice *)
821 Q_D(QSvgPaintEngine);
822 if (!d->outputDevice) {
823 qWarning("QSvgPaintEngine::begin(), no output device");
824 return false;
827 if (!d->outputDevice->isOpen()) {
828 if (!d->outputDevice->open(QIODevice::WriteOnly | QIODevice::Text)) {
829 qWarning("QSvgPaintEngine::begin(), could not open output device: '%s'",
830 qPrintable(d->outputDevice->errorString()));
831 return false;
833 } else if (!d->outputDevice->isWritable()) {
834 qWarning("QSvgPaintEngine::begin(), could not write to read-only output device: '%s'",
835 qPrintable(d->outputDevice->errorString()));
836 return false;
839 d->stream = new QTextStream(&d->header);
841 // stream out the header...
842 *d->stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << endl << "<svg";
844 if (d->size.isValid()) {
845 qreal wmm = d->size.width() * 25.4 / d->resolution;
846 qreal hmm = d->size.height() * 25.4 / d->resolution;
847 *d->stream << " width=\"" << wmm << "mm\" height=\"" << hmm << "mm\"" << endl;
850 if (d->viewBox.isValid()) {
851 *d->stream << " viewBox=\"" << d->viewBox.left() << ' ' << d->viewBox.top();
852 *d->stream << ' ' << d->viewBox.width() << ' ' << d->viewBox.height() << '\"' << endl;
855 *d->stream << " xmlns=\"http://www.w3.org/2000/svg\""
856 " xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
857 " version=\"1.2\" baseProfile=\"tiny\">" << endl;
859 if (!d->attributes.document_title.isEmpty()) {
860 *d->stream << "<title>" << d->attributes.document_title << "</title>" << endl;
863 if (!d->attributes.document_description.isEmpty()) {
864 *d->stream << "<desc>" << d->attributes.document_description << "</desc>" << endl;
867 d->stream->setString(&d->defs);
868 *d->stream << "<defs>\n";
870 d->stream->setString(&d->body);
871 // Start the initial graphics state...
872 *d->stream << "<g ";
873 generateQtDefaults();
874 *d->stream << endl;
876 return true;
879 bool QSvgPaintEngine::end()
881 Q_D(QSvgPaintEngine);
883 d->stream->setString(&d->defs);
884 *d->stream << "</defs>\n";
886 d->stream->setDevice(d->outputDevice);
887 #ifndef QT_NO_TEXTCODEC
888 d->stream->setCodec(QTextCodec::codecForName("UTF-8"));
889 #endif
891 *d->stream << d->header;
892 *d->stream << d->defs;
893 *d->stream << d->body;
894 if (d->afterFirstUpdate)
895 *d->stream << "</g>" << endl; // close the updateState
897 *d->stream << "</g>" << endl // close the Qt defaults
898 << "</svg>" << endl;
900 delete d->stream;
902 return true;
905 void QSvgPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm,
906 const QRectF &sr)
908 drawImage(r, pm.toImage(), sr);
911 void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image,
912 const QRectF &sr,
913 Qt::ImageConversionFlag flags)
915 //Q_D(QSvgPaintEngine);
917 Q_UNUSED(sr);
918 Q_UNUSED(flags);
919 stream() << "<image ";
920 stream() << "x=\""<<r.x()<<"\" "
921 "y=\""<<r.y()<<"\" "
922 "width=\""<<r.width()<<"\" "
923 "height=\""<<r.height()<<"\" "
924 "preserveAspectRatio=\"none\" ";
926 QByteArray data;
927 QBuffer buffer(&data);
928 buffer.open(QBuffer::ReadWrite);
929 image.save(&buffer, "PNG");
930 buffer.close();
931 stream() << "xlink:href=\"data:image/png;base64,"
932 << data.toBase64()
933 <<"\" />\n";
936 void QSvgPaintEngine::updateState(const QPaintEngineState &state)
938 Q_D(QSvgPaintEngine);
939 QPaintEngine::DirtyFlags flags = state.state();
941 // always stream full gstate, which is not required, but...
942 flags |= QPaintEngine::AllDirty;
944 // close old state and start a new one...
945 if (d->afterFirstUpdate)
946 *d->stream << "</g>\n\n";
948 *d->stream << "<g ";
950 if (flags & QPaintEngine::DirtyBrush) {
951 qbrushToSvg(state.brush());
954 if (flags & QPaintEngine::DirtyPen) {
955 qpenToSvg(state.pen());
958 if (flags & QPaintEngine::DirtyTransform) {
959 d->matrix = state.matrix();
960 *d->stream << "transform=\"matrix(" << d->matrix.m11() << ','
961 << d->matrix.m12() << ','
962 << d->matrix.m21() << ',' << d->matrix.m22() << ','
963 << d->matrix.dx() << ',' << d->matrix.dy()
964 << ")\""
965 << endl;
968 if (flags & QPaintEngine::DirtyFont) {
969 qfontToSvg(state.font());
972 if (flags & QPaintEngine::DirtyOpacity) {
973 if (!qFuzzyIsNull(state.opacity() - 1))
974 stream() << "opacity=\""<<state.opacity()<<"\" ";
977 *d->stream << '>' << endl;
979 d->afterFirstUpdate = true;
982 void QSvgPaintEngine::drawPath(const QPainterPath &p)
984 Q_D(QSvgPaintEngine);
986 *d->stream << "<path "
987 "fill-rule=";
988 if (p.fillRule() == Qt::OddEvenFill)
989 *d->stream << "\"evenodd\" ";
990 else
991 *d->stream << "\"nonzero\" ";
993 *d->stream << "d=\"";
995 for (int i=0; i<p.elementCount(); ++i) {
996 const QPainterPath::Element &e = p.elementAt(i);
997 switch (e.type) {
998 case QPainterPath::MoveToElement:
999 *d->stream << 'M' << e.x << ',' << e.y;
1000 break;
1001 case QPainterPath::LineToElement:
1002 *d->stream << 'L' << e.x << ',' << e.y;
1003 break;
1004 case QPainterPath::CurveToElement:
1005 *d->stream << 'C' << e.x << ',' << e.y;
1006 ++i;
1007 while (i < p.elementCount()) {
1008 const QPainterPath::Element &e = p.elementAt(i);
1009 if (e.type != QPainterPath::CurveToDataElement) {
1010 --i;
1011 break;
1012 } else
1013 *d->stream << ' ';
1014 *d->stream << e.x << ',' << e.y;
1015 ++i;
1017 break;
1018 default:
1019 break;
1021 if (i != p.elementCount() - 1) {
1022 *d->stream << ' ';
1026 *d->stream << "\"/>" << endl;
1029 void QSvgPaintEngine::drawPolygon(const QPointF *points, int pointCount,
1030 PolygonDrawMode mode)
1032 Q_ASSERT(pointCount >= 2);
1034 //Q_D(QSvgPaintEngine);
1036 QPainterPath path(points[0]);
1037 for (int i=1; i<pointCount; ++i)
1038 path.lineTo(points[i]);
1040 if (mode == PolylineMode) {
1041 stream() << "<polyline fill=\"none\" points=\"";
1042 for (int i = 0; i < pointCount; ++i) {
1043 const QPointF &pt = points[i];
1044 stream() << pt.x() << ',' << pt.y() << ' ';
1046 stream() << "\" />" <<endl;
1047 } else {
1048 path.closeSubpath();
1049 drawPath(path);
1053 void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &textItem)
1055 Q_D(QSvgPaintEngine);
1056 if (d->pen.style() == Qt::NoPen)
1057 return;
1059 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1060 QString s = QString::fromRawData(ti.chars, ti.num_chars);
1062 *d->stream << "<text "
1063 "fill=\"" << d->attributes.stroke << "\" "
1064 "fill-opacity=\"" << d->attributes.strokeOpacity << "\" "
1065 "stroke=\"none\" "
1066 "xml:space=\"preserve\" "
1067 "x=\"" << pt.x() << "\" y=\"" << pt.y() << "\" ";
1068 qfontToSvg(textItem.font());
1069 *d->stream << " >"
1070 << Qt::escape(s)
1071 << "</text>"
1072 << endl;
1075 QT_END_NAMESPACE
1077 #endif // QT_NO_SVGGENERATOR