source: trunk/src/svg/qsvggenerator.cpp@ 116

Last change on this file since 116 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 33.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtSvg module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
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.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsvggenerator.h"
43
44#ifndef QT_NO_SVGGENERATOR
45
46#include "qpainterpath.h"
47
48#include "private/qpaintengine_p.h"
49#include "private/qtextengine_p.h"
50#include "private/qdrawhelper_p.h"
51
52#include "qfile.h"
53#include "qtextcodec.h"
54#include "qtextstream.h"
55#include "qbuffer.h"
56#include "qmath.h"
57
58#include "qdebug.h"
59
60QT_BEGIN_NAMESPACE
61
62static void translate_color(const QColor &color, QString *color_string,
63 QString *opacity_string)
64{
65 Q_ASSERT(color_string);
66 Q_ASSERT(opacity_string);
67
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());
74}
75
76static void translate_dashPattern(QVector<qreal> pattern, const qreal& width, QString *pattern_string)
77{
78 Q_ASSERT(pattern_string);
79
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);
83
84 pattern_string->chop(1);
85}
86
87class QSvgPaintEnginePrivate : public QPaintEnginePrivate
88{
89public:
90 QSvgPaintEnginePrivate()
91 {
92 size = QSize();
93 viewBox = QRectF();
94 outputDevice = 0;
95 resolution = 72;
96
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");
103
104 afterFirstUpdate = false;
105 numGradients = 0;
106 }
107
108 QSize size;
109 QRectF viewBox;
110 QIODevice *outputDevice;
111 QTextStream *stream;
112 int resolution;
113
114 QString header;
115 QString defs;
116 QString body;
117 bool afterFirstUpdate;
118
119 QBrush brush;
120 QPen pen;
121 QMatrix matrix;
122 QFont font;
123
124 QString generateGradientName() {
125 ++numGradients;
126 currentGradientName = QString::fromLatin1("gradient%1").arg(numGradients);
127 return currentGradientName;
128 }
129
130 QString currentGradientName;
131 int numGradients;
132
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;
144};
145
146static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures()
147{
148 return QPaintEngine::PaintEngineFeatures(
149 QPaintEngine::AllFeatures
150 & ~QPaintEngine::PatternBrush
151 & ~QPaintEngine::PerspectiveTransform
152 & ~QPaintEngine::ConicalGradientFill
153 & ~QPaintEngine::PorterDuff);
154}
155
156class QSvgPaintEngine : public QPaintEngine
157{
158 Q_DECLARE_PRIVATE(QSvgPaintEngine)
159public:
160
161 QSvgPaintEngine()
162 : QPaintEngine(*new QSvgPaintEnginePrivate,
163 svgEngineFeatures())
164 {
165 }
166
167 bool begin(QPaintDevice *device);
168 bool end();
169
170 void updateState(const QPaintEngineState &state);
171 void popGroup();
172
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);
179
180 QPaintEngine::Type type() const { return QPaintEngine::SVG; }
181
182 QSize size() const { return d_func()->size; }
183 void setSize(const QSize &size) {
184 Q_ASSERT(!isActive());
185 d_func()->size = size;
186 }
187
188 QRectF viewBox() const { return d_func()->viewBox; }
189 void setViewBox(const QRectF &viewBox) {
190 Q_ASSERT(!isActive());
191 d_func()->viewBox = viewBox;
192 }
193
194 QString documentTitle() const { return d_func()->attributes.document_title; }
195 void setDocumentTitle(const QString &title) {
196 d_func()->attributes.document_title = title;
197 }
198
199 QString documentDescription() const { return d_func()->attributes.document_description; }
200 void setDocumentDescription(const QString &description) {
201 d_func()->attributes.document_description = description;
202 }
203
204 QIODevice *outputDevice() const { return d_func()->outputDevice; }
205 void setOutputDevice(QIODevice *device) {
206 Q_ASSERT(!isActive());
207 d_func()->outputDevice = device;
208 }
209
210 int resolution() { return d_func()->resolution; }
211 void setResolution(int resolution) {
212 Q_ASSERT(!isActive());
213 d_func()->resolution = resolution;
214 }
215 void saveLinearGradientBrush(const QGradient *g)
216 {
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("\" ");
226 }
227
228 str << QLatin1String("id=\"") << d_func()->generateGradientName() << QLatin1String("\">\n");
229 saveGradientStops(str, g);
230 str << QLatin1String("</linearGradient>") <<endl;
231 }
232 void saveRadialGradientBrush(const QGradient *g)
233 {
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("\" ");
244 }
245 str << QLatin1String("xml:id=\"") <<d_func()->generateGradientName()<< QLatin1String("\">\n");
246 saveGradientStops(str, g);
247 str << QLatin1String("</radialGradient>") << endl;
248 }
249 void saveConicalGradientBrush(const QGradient *)
250 {
251 qWarning("svg's don't support conical gradients!");
252 }
253
254 void saveGradientStops(QTextStream &str, const QGradient *g) {
255 QGradientStops stops = g->stops();
256
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);
262
263 if (!constantAlpha) {
264 const qreal spacing = 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());
272
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)));
278 }
279 }
280 fromColor = toColor;
281 }
282 newStops.append(stops.back());
283 stops = newStops;
284 }
285 }
286
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");
296 }
297 }
298
299 void saveGradientUnits(QTextStream &str, const QGradient *gradient)
300 {
301 str << QLatin1String("gradientUnits=\"");
302 if (gradient && gradient->coordinateMode() == QGradient::ObjectBoundingMode)
303 str << QLatin1String("objectBoundingBox");
304 else
305 str << QLatin1String("userSpaceOnUse");
306 str << QLatin1String("\" ");
307 }
308
309 void generateQtDefaults()
310 {
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");
319 }
320 inline QTextStream &stream()
321 {
322 return *d_func()->stream;
323 }
324
325
326 void qpenToSvg(const QPen &spen)
327 {
328 QString width;
329
330 d_func()->pen = spen;
331
332 switch (spen.style()) {
333 case Qt::NoPen:
334 stream() << QLatin1String("stroke=\"none\" ");
335
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;
342
343 translate_color(spen.color(), &color,
344 &colorOpacity);
345 d_func()->attributes.stroke = color;
346 d_func()->attributes.strokeOpacity = colorOpacity;
347
348 stream() << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
349 stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
350 }
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;
358
359 qreal penWidth = spen.width() == 0 ? qreal(1) : spen.widthF();
360
361 translate_color(spen.color(), &color, &colorOpacity);
362 translate_dashPattern(spen.dashPattern(), penWidth, &dashPattern);
363
364 // SVG uses absolute offset
365 dashOffset = QString::fromLatin1("%1").arg(spen.dashOffset() * penWidth);
366
367 d_func()->attributes.stroke = color;
368 d_func()->attributes.strokeOpacity = colorOpacity;
369 d_func()->attributes.dashPattern = dashPattern;
370 d_func()->attributes.dashOffset = dashOffset;
371
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;
377 }
378 default:
379 qWarning("Unsupported pen style");
380 break;
381 }
382
383 if (spen.widthF() == 0) {
384 width = QLatin1String("1");
385 stream() << "vector-effect=\"non-scaling-stroke\" ";
386 }
387 else
388 width = QString::number(spen.widthF());
389 stream() <<"stroke-width=\""<<width<<"\" ";
390
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");
403 }
404 switch (spen.joinStyle()) {
405 case Qt::MiterJoin:
406 stream() << "stroke-linejoin=\"miter\" ";
407 stream() << "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 stream() << "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
418 break;
419 default:
420 qWarning("Unhandled join style");
421 }
422 }
423 void qbrushToSvg(const QBrush &sbrush)
424 {
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;
435 }
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;
463 }
464 }
465 void qfontToSvg(const QFont &sfont)
466 {
467 Q_D(QSvgPaintEngine);
468
469 d->font = sfont;
470
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());
475
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;
489 }
490
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");
494
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;
500 }
501};
502
503class QSvgGeneratorPrivate
504{
505public:
506 QSvgPaintEngine *engine;
507
508 uint owns_iodevice : 1;
509 QString fileName;
510};
511
512/*!
513 \class QSvgGenerator
514 \ingroup multimedia
515 \since 4.3
516 \brief The QSvgGenerator class provides a paint device that is used to create SVG drawings.
517 \reentrant
518
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.
521
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.
526
527 \snippet examples/painting/svggenerator/window.cpp configure SVG generator
528
529 Other meta-data can be specified by setting the \a title, \a description and \a resolution
530 properties.
531
532 As with other QPaintDevice subclasses, a QPainter object is used to paint onto an instance
533 of this class:
534
535 \snippet examples/painting/svggenerator/window.cpp begin painting
536 \dots
537 \snippet examples/painting/svggenerator/window.cpp end painting
538
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.
542
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.
545
546 \sa QSvgRenderer, QSvgWidget, {About SVG}
547*/
548
549/*!
550 Constructs a new generator.
551*/
552QSvgGenerator::QSvgGenerator()
553 : d_ptr(new QSvgGeneratorPrivate)
554{
555 Q_D(QSvgGenerator);
556
557 d->engine = new QSvgPaintEngine;
558 d->owns_iodevice = false;
559}
560
561/*!
562 Destroys the generator.
563*/
564QSvgGenerator::~QSvgGenerator()
565{
566 Q_D(QSvgGenerator);
567 if (d->owns_iodevice)
568 delete d->engine->outputDevice();
569 delete d->engine;
570 delete d_ptr;
571}
572
573/*!
574 \property QSvgGenerator::title
575 \brief the title of the generated SVG drawing
576 \since 4.5
577 \sa description
578*/
579QString QSvgGenerator::title() const
580{
581 Q_D(const QSvgGenerator);
582
583 return d->engine->documentTitle();
584}
585
586void QSvgGenerator::setTitle(const QString &title)
587{
588 Q_D(QSvgGenerator);
589
590 d->engine->setDocumentTitle(title);
591}
592
593/*!
594 \property QSvgGenerator::description
595 \brief the description of the generated SVG drawing
596 \since 4.5
597 \sa title
598*/
599QString QSvgGenerator::description() const
600{
601 Q_D(const QSvgGenerator);
602
603 return d->engine->documentDescription();
604}
605
606void QSvgGenerator::setDescription(const QString &description)
607{
608 Q_D(QSvgGenerator);
609
610 d->engine->setDocumentDescription(description);
611}
612
613/*!
614 \property QSvgGenerator::size
615 \brief the size of the generated SVG drawing
616 \since 4.5
617
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.
621
622 \note It is not possible to change this property while a
623 QPainter is active on the generator.
624
625 \sa viewBox, resolution
626*/
627QSize QSvgGenerator::size() const
628{
629 Q_D(const QSvgGenerator);
630 return d->engine->size();
631}
632
633void QSvgGenerator::setSize(const QSize &size)
634{
635 Q_D(QSvgGenerator);
636 if (d->engine->isActive()) {
637 qWarning("QSvgGenerator::setSize(), cannot set size while SVG is being generated");
638 return;
639 }
640 d->engine->setSize(size);
641}
642
643/*!
644 \property QSvgGenerator::viewBox
645 \brief the viewBox of the generated SVG drawing
646 \since 4.5
647
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.
651
652 \note It is not possible to change this property while a
653 QPainter is active on the generator.
654
655 \sa viewBox(), size, resolution
656*/
657QRectF QSvgGenerator::viewBoxF() const
658{
659 Q_D(const QSvgGenerator);
660 return d->engine->viewBox();
661}
662
663/*!
664 \since 4.5
665
666 Returns viewBoxF().toRect().
667
668 \sa viewBoxF()
669*/
670QRect QSvgGenerator::viewBox() const
671{
672 Q_D(const QSvgGenerator);
673 return d->engine->viewBox().toRect();
674}
675
676void QSvgGenerator::setViewBox(const QRectF &viewBox)
677{
678 Q_D(QSvgGenerator);
679 if (d->engine->isActive()) {
680 qWarning("QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated");
681 return;
682 }
683 d->engine->setViewBox(viewBox);
684}
685
686void QSvgGenerator::setViewBox(const QRect &viewBox)
687{
688 setViewBox(QRectF(viewBox));
689}
690
691/*!
692 \property QSvgGenerator::fileName
693 \brief the target filename for the generated SVG drawing
694 \since 4.5
695
696 \sa outputDevice
697*/
698QString QSvgGenerator::fileName() const
699{
700 Q_D(const QSvgGenerator);
701 return d->fileName;
702}
703
704void QSvgGenerator::setFileName(const QString &fileName)
705{
706 Q_D(QSvgGenerator);
707 if (d->engine->isActive()) {
708 qWarning("QSvgGenerator::setFileName(), cannot set file name while SVG is being generated");
709 return;
710 }
711
712 if (d->owns_iodevice)
713 delete d->engine->outputDevice();
714
715 d->owns_iodevice = true;
716
717 d->fileName = fileName;
718 QFile *file = new QFile(fileName);
719 d->engine->setOutputDevice(file);
720}
721
722/*!
723 \property QSvgGenerator::outputDevice
724 \brief the output device for the generated SVG drawing
725 \since 4.5
726
727 If both output device and file name are specified, the output device
728 will have precedence.
729
730 \sa fileName
731*/
732QIODevice *QSvgGenerator::outputDevice() const
733{
734 Q_D(const QSvgGenerator);
735 return d->engine->outputDevice();
736}
737
738void QSvgGenerator::setOutputDevice(QIODevice *outputDevice)
739{
740 Q_D(QSvgGenerator);
741 if (d->engine->isActive()) {
742 qWarning("QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated");
743 return;
744 }
745 d->owns_iodevice = false;
746 d->engine->setOutputDevice(outputDevice);
747 d->fileName = QString();
748}
749
750/*!
751 \property QSvgGenerator::resolution
752 \brief the resolution of the generated output
753 \since 4.5
754
755 The resolution is specified in dots per inch, and is used to
756 calculate the physical size of an SVG drawing.
757
758 \sa size, viewBox
759*/
760int QSvgGenerator::resolution() const
761{
762 Q_D(const QSvgGenerator);
763 return d->engine->resolution();
764}
765
766void QSvgGenerator::setResolution(int dpi)
767{
768 Q_D(QSvgGenerator);
769 d->engine->setResolution(dpi);
770}
771
772/*!
773 Returns the paint engine used to render graphics to be converted to SVG
774 format information.
775*/
776QPaintEngine *QSvgGenerator::paintEngine() const
777{
778 Q_D(const QSvgGenerator);
779 return d->engine;
780}
781
782/*!
783 \reimp
784*/
785int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric) const
786{
787 Q_D(const QSvgGenerator);
788 switch (metric) {
789 case QPaintDevice::PdmDepth:
790 return 32;
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:
804 return 0xffffffff;
805 case QPaintDevice::PdmPhysicalDpiX:
806 return d->engine->resolution();
807 case QPaintDevice::PdmPhysicalDpiY:
808 return d->engine->resolution();
809 default:
810 qWarning("QSvgGenerator::metric(), unhandled metric %d\n", metric);
811 break;
812 }
813 return 0;
814}
815
816/*****************************************************************************
817 * class QSvgPaintEngine
818 */
819
820bool QSvgPaintEngine::begin(QPaintDevice *)
821{
822 Q_D(QSvgPaintEngine);
823 if (!d->outputDevice) {
824 qWarning("QSvgPaintEngine::begin(), no output device");
825 return false;
826 }
827
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()));
832 return false;
833 }
834 } else if (!d->outputDevice->isWritable()) {
835 qWarning("QSvgPaintEngine::begin(), could not write to read-only output device: '%s'",
836 qPrintable(d->outputDevice->errorString()));
837 return false;
838 }
839
840 d->stream = new QTextStream(&d->header);
841
842 // stream out the header...
843 *d->stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << endl << "<svg";
844
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;
849 }
850
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;
854 }
855
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;
859
860 if (!d->attributes.document_title.isEmpty()) {
861 *d->stream << "<title>" << d->attributes.document_title << "</title>" << endl;
862 }
863
864 if (!d->attributes.document_description.isEmpty()) {
865 *d->stream << "<desc>" << d->attributes.document_description << "</desc>" << endl;
866 }
867
868 d->stream->setString(&d->defs);
869 *d->stream << "<defs>\n";
870
871 d->stream->setString(&d->body);
872 // Start the initial graphics state...
873 *d->stream << "<g ";
874 generateQtDefaults();
875 *d->stream << endl;
876
877 return true;
878}
879
880bool QSvgPaintEngine::end()
881{
882 Q_D(QSvgPaintEngine);
883
884 d->stream->setString(&d->defs);
885 *d->stream << "</defs>\n";
886
887 d->stream->setDevice(d->outputDevice);
888#ifndef QT_NO_TEXTCODEC
889 d->stream->setCodec(QTextCodec::codecForName("UTF-8"));
890#endif
891
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
897
898 *d->stream << "</g>" << endl // close the Qt defaults
899 << "</svg>" << endl;
900
901 delete d->stream;
902
903 return true;
904}
905
906void QSvgPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm,
907 const QRectF &sr)
908{
909 drawImage(r, pm.toImage(), sr);
910}
911
912void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image,
913 const QRectF &sr,
914 Qt::ImageConversionFlag flags)
915{
916 //Q_D(QSvgPaintEngine);
917
918 Q_UNUSED(sr);
919 Q_UNUSED(flags);
920 stream() << "<image ";
921 stream() << "x=\""<<r.x()<<"\" ";
922 stream() << "y=\""<<r.y()<<"\" ";
923 stream() << "width=\""<<r.width()<<"\" ";
924 stream() << "height=\""<<r.height()<<"\" ";
925
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 <<"\" ";
934 stream() << "/>\n";
935}
936
937void QSvgPaintEngine::updateState(const QPaintEngineState &state)
938{
939 Q_D(QSvgPaintEngine);
940 QPaintEngine::DirtyFlags flags = state.state();
941
942 // always stream full gstate, which is not required, but...
943 flags |= QPaintEngine::AllDirty;
944
945 // close old state and start a new one...
946 if (d->afterFirstUpdate)
947 *d->stream << "</g>\n\n";
948
949 *d->stream << "<g ";
950
951 if (flags & QPaintEngine::DirtyBrush) {
952 qbrushToSvg(state.brush());
953 }
954
955 if (flags & QPaintEngine::DirtyPen) {
956 qpenToSvg(state.pen());
957 }
958
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()
965 << ")\""
966 << endl;
967 }
968
969 if (flags & QPaintEngine::DirtyFont) {
970 qfontToSvg(state.font());
971 }
972
973 if (flags & QPaintEngine::DirtyOpacity) {
974 if (!qFuzzyCompare(state.opacity(), 1))
975 stream() << "opacity=\""<<state.opacity()<<"\" ";
976 }
977
978 *d->stream << ">" << endl;
979
980 d->afterFirstUpdate = true;
981}
982
983void QSvgPaintEngine::drawPath(const QPainterPath &p)
984{
985 Q_D(QSvgPaintEngine);
986
987 *d->stream << "<path ";
988
989
990 *d->stream << "fill-rule=";
991 if (p.fillRule() == Qt::OddEvenFill)
992 *d->stream << "\"evenodd\" ";
993 else
994 *d->stream << "\"nonzero\" ";
995
996 *d->stream << "d=\"";
997
998 for (int i=0; i<p.elementCount(); ++i) {
999 const QPainterPath::Element &e = p.elementAt(i);
1000 switch (e.type) {
1001 case QPainterPath::MoveToElement:
1002 *d->stream << "M" << e.x << "," << e.y;
1003 break;
1004 case QPainterPath::LineToElement:
1005 *d->stream << "L" << e.x << "," << e.y;
1006 break;
1007 case QPainterPath::CurveToElement:
1008 *d->stream << "C" << e.x << "," << e.y;
1009 ++i;
1010 while (i < p.elementCount()) {
1011 const QPainterPath::Element &e = p.elementAt(i);
1012 if (e.type != QPainterPath::CurveToDataElement) {
1013 --i;
1014 break;
1015 } else
1016 *d->stream << " ";
1017 *d->stream << e.x << "," << e.y;
1018 ++i;
1019 }
1020 break;
1021 default:
1022 break;
1023 }
1024 if (i != p.elementCount() - 1) {
1025 *d->stream << " ";
1026 }
1027 }
1028
1029 *d->stream << "\"/>" << endl;
1030}
1031
1032void QSvgPaintEngine::drawPolygon(const QPointF *points, int pointCount,
1033 PolygonDrawMode mode)
1034{
1035 Q_ASSERT(pointCount >= 2);
1036
1037 //Q_D(QSvgPaintEngine);
1038
1039 QPainterPath path(points[0]);
1040 for (int i=1; i<pointCount; ++i)
1041 path.lineTo(points[i]);
1042
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() << " ";
1048 }
1049 stream() << "\" />" <<endl;
1050 } else {
1051 path.closeSubpath();
1052 drawPath(path);
1053 }
1054}
1055
1056void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &textItem)
1057{
1058 Q_D(QSvgPaintEngine);
1059 if (d->pen.style() == Qt::NoPen)
1060 return;
1061
1062 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1063 QString s = QString::fromRawData(ti.chars, ti.num_chars);
1064
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());
1071 *d->stream << " >"
1072 << Qt::escape(s)
1073 << "</text>"
1074 << endl;
1075}
1076
1077QT_END_NAMESPACE
1078
1079#endif // QT_NO_SVGGENERATOR
Note: See TracBrowser for help on using the repository browser.