src/svg/qsvggenerator.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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.
       
    15 **
       
    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.
       
    23 **
       
    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.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    60 QT_BEGIN_NAMESPACE
       
    61 
       
    62 static 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 
       
    76 static 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 
       
    87 class QSvgPaintEnginePrivate : public QPaintEnginePrivate
       
    88 {
       
    89 public:
       
    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 
       
   146 static 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 
       
   156 class QSvgPaintEngine : public QPaintEngine
       
   157 {
       
   158     Q_DECLARE_PRIVATE(QSvgPaintEngine)
       
   159 public:
       
   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 = 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());
       
   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::number(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                         "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");
       
   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                         "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 
       
   503 class QSvgGeneratorPrivate
       
   504 {
       
   505 public:
       
   506     QSvgPaintEngine *engine;
       
   507 
       
   508     uint owns_iodevice : 1;
       
   509     QString fileName;
       
   510 };
       
   511 
       
   512 /*!
       
   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
       
   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 */
       
   552 QSvgGenerator::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 */
       
   564 QSvgGenerator::~QSvgGenerator()
       
   565 {
       
   566     Q_D(QSvgGenerator);
       
   567     if (d->owns_iodevice)
       
   568         delete d->engine->outputDevice();
       
   569     delete d->engine;
       
   570 }
       
   571 
       
   572 /*!
       
   573     \property QSvgGenerator::title
       
   574     \brief the title of the generated SVG drawing
       
   575     \since 4.5
       
   576     \sa description
       
   577 */
       
   578 QString QSvgGenerator::title() const
       
   579 {
       
   580     Q_D(const QSvgGenerator);
       
   581 
       
   582     return d->engine->documentTitle();
       
   583 }
       
   584 
       
   585 void QSvgGenerator::setTitle(const QString &title)
       
   586 {
       
   587     Q_D(QSvgGenerator);
       
   588 
       
   589     d->engine->setDocumentTitle(title);
       
   590 }
       
   591 
       
   592 /*!
       
   593     \property QSvgGenerator::description
       
   594     \brief the description of the generated SVG drawing
       
   595     \since 4.5
       
   596     \sa title
       
   597 */
       
   598 QString QSvgGenerator::description() const
       
   599 {
       
   600     Q_D(const QSvgGenerator);
       
   601 
       
   602     return d->engine->documentDescription();
       
   603 }
       
   604 
       
   605 void QSvgGenerator::setDescription(const QString &description)
       
   606 {
       
   607     Q_D(QSvgGenerator);
       
   608 
       
   609     d->engine->setDocumentDescription(description);
       
   610 }
       
   611 
       
   612 /*!
       
   613     \property QSvgGenerator::size
       
   614     \brief the size of the generated SVG drawing
       
   615     \since 4.5
       
   616 
       
   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.
       
   620 
       
   621     \note It is not possible to change this property while a
       
   622     QPainter is active on the generator.
       
   623 
       
   624     \sa viewBox, resolution
       
   625 */
       
   626 QSize QSvgGenerator::size() const
       
   627 {
       
   628     Q_D(const QSvgGenerator);
       
   629     return d->engine->size();
       
   630 }
       
   631 
       
   632 void QSvgGenerator::setSize(const QSize &size)
       
   633 {
       
   634     Q_D(QSvgGenerator);
       
   635     if (d->engine->isActive()) {
       
   636         qWarning("QSvgGenerator::setSize(), cannot set size while SVG is being generated");
       
   637         return;
       
   638     }
       
   639     d->engine->setSize(size);
       
   640 }
       
   641 
       
   642 /*!
       
   643     \property QSvgGenerator::viewBox
       
   644     \brief the viewBox of the generated SVG drawing
       
   645     \since 4.5
       
   646 
       
   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.
       
   650 
       
   651     \note It is not possible to change this property while a
       
   652     QPainter is active on the generator.
       
   653 
       
   654     \sa viewBox(), size, resolution
       
   655 */
       
   656 QRectF QSvgGenerator::viewBoxF() const
       
   657 {
       
   658     Q_D(const QSvgGenerator);
       
   659     return d->engine->viewBox();
       
   660 }
       
   661 
       
   662 /*!
       
   663     \since 4.5
       
   664 
       
   665     Returns viewBoxF().toRect().
       
   666 
       
   667     \sa viewBoxF()
       
   668 */
       
   669 QRect QSvgGenerator::viewBox() const
       
   670 {
       
   671     Q_D(const QSvgGenerator);
       
   672     return d->engine->viewBox().toRect();
       
   673 }
       
   674 
       
   675 void QSvgGenerator::setViewBox(const QRectF &viewBox)
       
   676 {
       
   677     Q_D(QSvgGenerator);
       
   678     if (d->engine->isActive()) {
       
   679         qWarning("QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated");
       
   680         return;
       
   681     }
       
   682     d->engine->setViewBox(viewBox);
       
   683 }
       
   684 
       
   685 void QSvgGenerator::setViewBox(const QRect &viewBox)
       
   686 {
       
   687     setViewBox(QRectF(viewBox));
       
   688 }
       
   689 
       
   690 /*!
       
   691     \property QSvgGenerator::fileName
       
   692     \brief the target filename for the generated SVG drawing
       
   693     \since 4.5
       
   694 
       
   695     \sa outputDevice
       
   696 */
       
   697 QString QSvgGenerator::fileName() const
       
   698 {
       
   699     Q_D(const QSvgGenerator);
       
   700     return d->fileName;
       
   701 }
       
   702 
       
   703 void QSvgGenerator::setFileName(const QString &fileName)
       
   704 {
       
   705     Q_D(QSvgGenerator);
       
   706     if (d->engine->isActive()) {
       
   707         qWarning("QSvgGenerator::setFileName(), cannot set file name while SVG is being generated");
       
   708         return;
       
   709     }
       
   710 
       
   711     if (d->owns_iodevice)
       
   712         delete d->engine->outputDevice();
       
   713 
       
   714     d->owns_iodevice = true;
       
   715 
       
   716     d->fileName = fileName;
       
   717     QFile *file = new QFile(fileName);
       
   718     d->engine->setOutputDevice(file);
       
   719 }
       
   720 
       
   721 /*!
       
   722     \property QSvgGenerator::outputDevice
       
   723     \brief the output device for the generated SVG drawing
       
   724     \since 4.5
       
   725 
       
   726     If both output device and file name are specified, the output device
       
   727     will have precedence.
       
   728 
       
   729     \sa fileName
       
   730 */
       
   731 QIODevice *QSvgGenerator::outputDevice() const
       
   732 {
       
   733     Q_D(const QSvgGenerator);
       
   734     return d->engine->outputDevice();
       
   735 }
       
   736 
       
   737 void QSvgGenerator::setOutputDevice(QIODevice *outputDevice)
       
   738 {
       
   739     Q_D(QSvgGenerator);
       
   740     if (d->engine->isActive()) {
       
   741         qWarning("QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated");
       
   742         return;
       
   743     }
       
   744     d->owns_iodevice = false;
       
   745     d->engine->setOutputDevice(outputDevice);
       
   746     d->fileName = QString();
       
   747 }
       
   748 
       
   749 /*!
       
   750     \property QSvgGenerator::resolution
       
   751     \brief the resolution of the generated output
       
   752     \since 4.5
       
   753 
       
   754     The resolution is specified in dots per inch, and is used to
       
   755     calculate the physical size of an SVG drawing.
       
   756 
       
   757     \sa size, viewBox
       
   758 */
       
   759 int QSvgGenerator::resolution() const
       
   760 {
       
   761     Q_D(const QSvgGenerator);
       
   762     return d->engine->resolution();
       
   763 }
       
   764 
       
   765 void QSvgGenerator::setResolution(int dpi)
       
   766 {
       
   767     Q_D(QSvgGenerator);
       
   768     d->engine->setResolution(dpi);
       
   769 }
       
   770 
       
   771 /*!
       
   772     Returns the paint engine used to render graphics to be converted to SVG
       
   773     format information.
       
   774 */
       
   775 QPaintEngine *QSvgGenerator::paintEngine() const
       
   776 {
       
   777     Q_D(const QSvgGenerator);
       
   778     return d->engine;
       
   779 }
       
   780 
       
   781 /*!
       
   782     \reimp
       
   783 */
       
   784 int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric) const
       
   785 {
       
   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;
       
   811     }
       
   812     return 0;
       
   813 }
       
   814 
       
   815 /*****************************************************************************
       
   816  * class QSvgPaintEngine
       
   817  */
       
   818 
       
   819 bool QSvgPaintEngine::begin(QPaintDevice *)
       
   820 {
       
   821     Q_D(QSvgPaintEngine);
       
   822     if (!d->outputDevice) {
       
   823         qWarning("QSvgPaintEngine::begin(), no output device");
       
   824         return false;
       
   825     }
       
   826 
       
   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;
       
   832         }
       
   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;
       
   837     }
       
   838 
       
   839     d->stream = new QTextStream(&d->header);
       
   840 
       
   841     // stream out the header...
       
   842     *d->stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << endl << "<svg";
       
   843 
       
   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;
       
   848     }
       
   849 
       
   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;
       
   853     }
       
   854 
       
   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;
       
   858 
       
   859     if (!d->attributes.document_title.isEmpty()) {
       
   860         *d->stream << "<title>" << d->attributes.document_title << "</title>" << endl;
       
   861     }
       
   862 
       
   863     if (!d->attributes.document_description.isEmpty()) {
       
   864         *d->stream << "<desc>" << d->attributes.document_description << "</desc>" << endl;
       
   865     }
       
   866 
       
   867     d->stream->setString(&d->defs);
       
   868     *d->stream << "<defs>\n";
       
   869 
       
   870     d->stream->setString(&d->body);
       
   871     // Start the initial graphics state...
       
   872     *d->stream << "<g ";
       
   873     generateQtDefaults();
       
   874     *d->stream << endl;
       
   875 
       
   876     return true;
       
   877 }
       
   878 
       
   879 bool QSvgPaintEngine::end()
       
   880 {
       
   881     Q_D(QSvgPaintEngine);
       
   882 
       
   883     d->stream->setString(&d->defs);
       
   884     *d->stream << "</defs>\n";
       
   885 
       
   886     d->stream->setDevice(d->outputDevice);
       
   887 #ifndef QT_NO_TEXTCODEC
       
   888     d->stream->setCodec(QTextCodec::codecForName("UTF-8"));
       
   889 #endif
       
   890 
       
   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
       
   896 
       
   897     *d->stream << "</g>" << endl // close the Qt defaults
       
   898                << "</svg>" << endl;
       
   899 
       
   900     delete d->stream;
       
   901 
       
   902     return true;
       
   903 }
       
   904 
       
   905 void QSvgPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm,
       
   906                                  const QRectF &sr)
       
   907 {
       
   908     drawImage(r, pm.toImage(), sr);
       
   909 }
       
   910 
       
   911 void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image,
       
   912                                 const QRectF &sr,
       
   913                                 Qt::ImageConversionFlag flags)
       
   914 {
       
   915     //Q_D(QSvgPaintEngine);
       
   916 
       
   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\" ";
       
   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              <<"\" />\n";
       
   934 }
       
   935 
       
   936 void QSvgPaintEngine::updateState(const QPaintEngineState &state)
       
   937 {
       
   938     Q_D(QSvgPaintEngine);
       
   939     QPaintEngine::DirtyFlags flags = state.state();
       
   940 
       
   941     // always stream full gstate, which is not required, but...
       
   942     flags |= QPaintEngine::AllDirty;
       
   943 
       
   944     // close old state and start a new one...
       
   945     if (d->afterFirstUpdate)
       
   946         *d->stream << "</g>\n\n";
       
   947 
       
   948     *d->stream << "<g ";
       
   949 
       
   950     if (flags & QPaintEngine::DirtyBrush) {
       
   951         qbrushToSvg(state.brush());
       
   952     }
       
   953 
       
   954     if (flags & QPaintEngine::DirtyPen) {
       
   955         qpenToSvg(state.pen());
       
   956     }
       
   957 
       
   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;
       
   966     }
       
   967 
       
   968     if (flags & QPaintEngine::DirtyFont) {
       
   969         qfontToSvg(state.font());
       
   970     }
       
   971 
       
   972     if (flags & QPaintEngine::DirtyOpacity) {
       
   973         if (!qFuzzyIsNull(state.opacity() - 1))
       
   974             stream() << "opacity=\""<<state.opacity()<<"\" ";
       
   975     }
       
   976 
       
   977     *d->stream << '>' << endl;
       
   978 
       
   979     d->afterFirstUpdate = true;
       
   980 }
       
   981 
       
   982 void QSvgPaintEngine::drawPath(const QPainterPath &p)
       
   983 {
       
   984     Q_D(QSvgPaintEngine);
       
   985 
       
   986     *d->stream << "<path "
       
   987                   "fill-rule=";
       
   988     if (p.fillRule() == Qt::OddEvenFill)
       
   989         *d->stream << "\"evenodd\" ";
       
   990     else
       
   991         *d->stream << "\"nonzero\" ";
       
   992 
       
   993     *d->stream << "d=\"";
       
   994 
       
   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;
       
  1016             }
       
  1017             break;
       
  1018         default:
       
  1019             break;
       
  1020         }
       
  1021         if (i != p.elementCount() - 1) {
       
  1022             *d->stream << ' ';
       
  1023         }
       
  1024     }
       
  1025 
       
  1026     *d->stream << "\"/>" << endl;
       
  1027 }
       
  1028 
       
  1029 void QSvgPaintEngine::drawPolygon(const QPointF *points, int pointCount,
       
  1030                                   PolygonDrawMode mode)
       
  1031 {
       
  1032     Q_ASSERT(pointCount >= 2);
       
  1033 
       
  1034     //Q_D(QSvgPaintEngine);
       
  1035 
       
  1036     QPainterPath path(points[0]);
       
  1037     for (int i=1; i<pointCount; ++i)
       
  1038         path.lineTo(points[i]);
       
  1039 
       
  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() << ' ';
       
  1045         }
       
  1046         stream() << "\" />" <<endl;
       
  1047     } else {
       
  1048         path.closeSubpath();
       
  1049         drawPath(path);
       
  1050     }
       
  1051 }
       
  1052 
       
  1053 void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &textItem)
       
  1054 {
       
  1055     Q_D(QSvgPaintEngine);
       
  1056     if (d->pen.style() == Qt::NoPen)
       
  1057         return;
       
  1058 
       
  1059     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
       
  1060     QString s = QString::fromRawData(ti.chars, ti.num_chars);
       
  1061 
       
  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;
       
  1073 }
       
  1074 
       
  1075 QT_END_NAMESPACE
       
  1076 
       
  1077 #endif // QT_NO_SVGGENERATOR