| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the Qt SVG module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include "qsvggenerator.h" | 
| 41 |  | 
| 42 | #ifndef QT_NO_SVGGENERATOR | 
| 43 |  | 
| 44 | #include "qpainterpath.h" | 
| 45 |  | 
| 46 | #include "private/qpaintengine_p.h" | 
| 47 | #include "private/qtextengine_p.h" | 
| 48 | #include "private/qdrawhelper_p.h" | 
| 49 |  | 
| 50 | #include "qfile.h" | 
| 51 | #include "qtextcodec.h" | 
| 52 | #include "qtextstream.h" | 
| 53 | #include "qbuffer.h" | 
| 54 | #include "qmath.h" | 
| 55 | #include "qbitmap.h" | 
| 56 | #include "qtransform.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(str: "#%1%2%3" ) | 
| 70 |         .arg(a: color.red(), fieldWidth: 2, base: 16, fillChar: QLatin1Char('0')) | 
| 71 |         .arg(a: color.green(), fieldWidth: 2, base: 16, fillChar: QLatin1Char('0')) | 
| 72 |         .arg(a: color.blue(), fieldWidth: 2, base: 16, fillChar: QLatin1Char('0')); | 
| 73 |     *opacity_string = QString::number(color.alphaF()); | 
| 74 | } | 
| 75 |  | 
| 76 | static void translate_dashPattern(const QVector<qreal> &pattern, 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 |     for (qreal entry : pattern) | 
| 82 |         *pattern_string += QString::fromLatin1(str: "%1," ).arg(a: entry * width); | 
| 83 |  | 
| 84 |     pattern_string->chop(n: 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 ; | 
| 115 |     QString defs; | 
| 116 |     QString body; | 
| 117 |     bool    afterFirstUpdate; | 
| 118 |  | 
| 119 |     QBrush brush; | 
| 120 |     QPen pen; | 
| 121 |     QTransform matrix; | 
| 122 |     QFont font; | 
| 123 |  | 
| 124 |     QString generateGradientName() { | 
| 125 |         ++numGradients; | 
| 126 |         currentGradientName = QString::fromLatin1(str: "gradient%1" ).arg(a: numGradients); | 
| 127 |         return currentGradientName; | 
| 128 |     } | 
| 129 |  | 
| 130 |     QString currentGradientName; | 
| 131 |     int numGradients; | 
| 132 |  | 
| 133 |     QStringList savedPatternBrushes; | 
| 134 |     QStringList savedPatternMasks; | 
| 135 |  | 
| 136 |     struct _attributes { | 
| 137 |         QString document_title; | 
| 138 |         QString document_description; | 
| 139 |         QString font_weight; | 
| 140 |         QString font_size; | 
| 141 |         QString font_family; | 
| 142 |         QString font_style; | 
| 143 |         QString stroke, strokeOpacity; | 
| 144 |         QString dashPattern, dashOffset; | 
| 145 |         QString fill, fillOpacity; | 
| 146 |     } attributes; | 
| 147 | }; | 
| 148 |  | 
| 149 | static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures() | 
| 150 | { | 
| 151 |     return QPaintEngine::PaintEngineFeatures( | 
| 152 |         QPaintEngine::AllFeatures | 
| 153 |         & ~QPaintEngine::PerspectiveTransform | 
| 154 |         & ~QPaintEngine::ConicalGradientFill | 
| 155 |         & ~QPaintEngine::PorterDuff); | 
| 156 | } | 
| 157 |  | 
| 158 | Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); | 
| 159 |  | 
| 160 | class QSvgPaintEngine : public QPaintEngine | 
| 161 | { | 
| 162 |     Q_DECLARE_PRIVATE(QSvgPaintEngine) | 
| 163 | public: | 
| 164 |  | 
| 165 |     QSvgPaintEngine() | 
| 166 |         : QPaintEngine(*new QSvgPaintEnginePrivate, | 
| 167 |                        svgEngineFeatures()) | 
| 168 |     { | 
| 169 |     } | 
| 170 |  | 
| 171 |     bool begin(QPaintDevice *device) override; | 
| 172 |     bool end() override; | 
| 173 |  | 
| 174 |     void updateState(const QPaintEngineState &state) override; | 
| 175 |     void popGroup(); | 
| 176 |  | 
| 177 |     void drawEllipse(const QRectF &r) override; | 
| 178 |     void drawPath(const QPainterPath &path) override; | 
| 179 |     void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override; | 
| 180 |     void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override; | 
| 181 |     void drawRects(const QRectF *rects, int rectCount) override; | 
| 182 |     void drawTextItem(const QPointF &pt, const QTextItem &item) override; | 
| 183 |     void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, | 
| 184 |                    Qt::ImageConversionFlags flags = Qt::AutoColor) override; | 
| 185 |  | 
| 186 |     QPaintEngine::Type type() const override { return QPaintEngine::SVG; } | 
| 187 |  | 
| 188 |     QSize size() const { return d_func()->size; } | 
| 189 |     void setSize(const QSize &size) { | 
| 190 |         Q_ASSERT(!isActive()); | 
| 191 |         d_func()->size = size; | 
| 192 |     } | 
| 193 |  | 
| 194 |     QRectF viewBox() const { return d_func()->viewBox; } | 
| 195 |     void setViewBox(const QRectF &viewBox) { | 
| 196 |         Q_ASSERT(!isActive()); | 
| 197 |         d_func()->viewBox = viewBox; | 
| 198 |     } | 
| 199 |  | 
| 200 |     QString documentTitle() const { return d_func()->attributes.document_title; } | 
| 201 |     void setDocumentTitle(const QString &title) { | 
| 202 |         d_func()->attributes.document_title = title; | 
| 203 |     } | 
| 204 |  | 
| 205 |     QString documentDescription() const { return d_func()->attributes.document_description; } | 
| 206 |     void setDocumentDescription(const QString &description) { | 
| 207 |         d_func()->attributes.document_description = description; | 
| 208 |     } | 
| 209 |  | 
| 210 |     QIODevice *outputDevice() const { return d_func()->outputDevice; } | 
| 211 |     void setOutputDevice(QIODevice *device) { | 
| 212 |         Q_ASSERT(!isActive()); | 
| 213 |         d_func()->outputDevice = device; | 
| 214 |     } | 
| 215 |  | 
| 216 |     int resolution() { return d_func()->resolution; } | 
| 217 |     void setResolution(int resolution) { | 
| 218 |         Q_ASSERT(!isActive()); | 
| 219 |         d_func()->resolution = resolution; | 
| 220 |     } | 
| 221 |  | 
| 222 |     QString savePatternMask(Qt::BrushStyle style) | 
| 223 |     { | 
| 224 |         QString maskId = QString(QStringLiteral("patternmask%1" )).arg(a: style); | 
| 225 |         if (!d_func()->savedPatternMasks.contains(str: maskId)) { | 
| 226 |             QImage img = qt_imageForBrush(brushStyle: style, invert: true); | 
| 227 |             QRegion reg(QBitmap::fromData(size: img.size(), bits: img.constBits())); | 
| 228 |             QString rct(QStringLiteral("<rect x=\"%1\" y=\"%2\" width=\"%3\" height=\"%4\" />" )); | 
| 229 |             QTextStream str(&d_func()->defs, QIODevice::Append); | 
| 230 |             str << "<mask id=\""  << maskId << "\" x=\"0\" y=\"0\" width=\"8\" height=\"8\" "  | 
| 231 |                 << "stroke=\"none\" fill=\"#ffffff\" patternUnits=\"userSpaceOnUse\" >"  << Qt::endl; | 
| 232 |             for (QRect r : reg) | 
| 233 |                 str << rct.arg(a: r.x()).arg(a: r.y()).arg(a: r.width()).arg(a: r.height()) << Qt::endl; | 
| 234 |             str << QStringLiteral("</mask>" ) << Qt::endl << Qt::endl; | 
| 235 |             d_func()->savedPatternMasks.append(t: maskId); | 
| 236 |         } | 
| 237 |         return maskId; | 
| 238 |     } | 
| 239 |  | 
| 240 |     QString savePatternBrush(const QString &color, const QBrush &brush) | 
| 241 |     { | 
| 242 |         QString patternId = QString(QStringLiteral("fillpattern%1_" )).arg(a: brush.style()) + color.midRef(position: 1); | 
| 243 |         if (!d_func()->savedPatternBrushes.contains(str: patternId)) { | 
| 244 |             QString maskId = savePatternMask(style: brush.style()); | 
| 245 |             QString geo(QStringLiteral("x=\"0\" y=\"0\" width=\"8\" height=\"8\"" )); | 
| 246 |             QTextStream str(&d_func()->defs, QIODevice::Append); | 
| 247 |             str << QString(QStringLiteral("<pattern id=\"%1\" %2 patternUnits=\"userSpaceOnUse\" >" )).arg(args&: patternId, args&: geo) << Qt::endl; | 
| 248 |             str << QString(QStringLiteral("<rect %1 stroke=\"none\" fill=\"%2\" mask=\"url(#%3)\" />" )).arg(args&: geo, args: color, args&: maskId) << Qt::endl; | 
| 249 |             str << QStringLiteral("</pattern>" ) << Qt::endl << Qt::endl; | 
| 250 |             d_func()->savedPatternBrushes.append(t: patternId); | 
| 251 |         } | 
| 252 |         return patternId; | 
| 253 |     } | 
| 254 |  | 
| 255 |     void saveLinearGradientBrush(const QGradient *g) | 
| 256 |     { | 
| 257 |         QTextStream str(&d_func()->defs, QIODevice::Append); | 
| 258 |         const QLinearGradient *grad = static_cast<const QLinearGradient*>(g); | 
| 259 |         str << QLatin1String("<linearGradient " ); | 
| 260 |         saveGradientUnits(str, gradient: g); | 
| 261 |         if (grad) { | 
| 262 |             str << QLatin1String("x1=\"" ) <<grad->start().x()<< QLatin1String("\" " ) | 
| 263 |                 << QLatin1String("y1=\"" ) <<grad->start().y()<< QLatin1String("\" " ) | 
| 264 |                 << QLatin1String("x2=\"" ) <<grad->finalStop().x() << QLatin1String("\" " ) | 
| 265 |                 << QLatin1String("y2=\"" ) <<grad->finalStop().y() << QLatin1String("\" " ); | 
| 266 |         } | 
| 267 |  | 
| 268 |         str << QLatin1String("id=\"" ) << d_func()->generateGradientName() << QLatin1String("\">\n" ); | 
| 269 |         saveGradientStops(str, g); | 
| 270 |         str << QLatin1String("</linearGradient>" ) <<Qt::endl; | 
| 271 |     } | 
| 272 |     void saveRadialGradientBrush(const QGradient *g) | 
| 273 |     { | 
| 274 |         QTextStream str(&d_func()->defs, QIODevice::Append); | 
| 275 |         const QRadialGradient *grad = static_cast<const QRadialGradient*>(g); | 
| 276 |         str << QLatin1String("<radialGradient " ); | 
| 277 |         saveGradientUnits(str, gradient: g); | 
| 278 |         if (grad) { | 
| 279 |             str << QLatin1String("cx=\"" ) <<grad->center().x()<< QLatin1String("\" " ) | 
| 280 |                 << QLatin1String("cy=\"" ) <<grad->center().y()<< QLatin1String("\" " ) | 
| 281 |                 << QLatin1String("r=\"" ) <<grad->radius() << QLatin1String("\" " ) | 
| 282 |                 << QLatin1String("fx=\"" ) <<grad->focalPoint().x() << QLatin1String("\" " ) | 
| 283 |                 << QLatin1String("fy=\"" ) <<grad->focalPoint().y() << QLatin1String("\" " ); | 
| 284 |         } | 
| 285 |         str << QLatin1String("id=\"" ) <<d_func()->generateGradientName()<< QLatin1String("\">\n" ); | 
| 286 |         saveGradientStops(str, g); | 
| 287 |         str << QLatin1String("</radialGradient>" ) << Qt::endl; | 
| 288 |     } | 
| 289 |     void saveConicalGradientBrush(const QGradient *) | 
| 290 |     { | 
| 291 |         qWarning(msg: "svg's don't support conical gradients!" ); | 
| 292 |     } | 
| 293 |  | 
| 294 |     void saveGradientStops(QTextStream &str, const QGradient *g) { | 
| 295 |         QGradientStops stops = g->stops(); | 
| 296 |  | 
| 297 |         if (g->interpolationMode() == QGradient::ColorInterpolation) { | 
| 298 |             bool constantAlpha = true; | 
| 299 |             int alpha = stops.at(i: 0).second.alpha(); | 
| 300 |             for (int i = 1; i < stops.size(); ++i) | 
| 301 |                 constantAlpha &= (stops.at(i).second.alpha() == alpha); | 
| 302 |  | 
| 303 |             if (!constantAlpha) { | 
| 304 |                 const qreal spacing = qreal(0.02); | 
| 305 |                 QGradientStops newStops; | 
| 306 |                 QRgb fromColor = qPremultiply(x: stops.at(i: 0).second.rgba()); | 
| 307 |                 QRgb toColor; | 
| 308 |                 for (int i = 0; i + 1 < stops.size(); ++i) { | 
| 309 |                     int parts = qCeil(v: (stops.at(i: i + 1).first - stops.at(i).first) / spacing); | 
| 310 |                     newStops.append(t: stops.at(i)); | 
| 311 |                     toColor = qPremultiply(x: stops.at(i: i + 1).second.rgba()); | 
| 312 |  | 
| 313 |                     if (parts > 1) { | 
| 314 |                         qreal step = (stops.at(i: i + 1).first - stops.at(i).first) / parts; | 
| 315 |                         for (int j = 1; j < parts; ++j) { | 
| 316 |                             QRgb color = qUnpremultiply(p: INTERPOLATE_PIXEL_256(x: fromColor, a: 256 - 256 * j / parts, y: toColor, b: 256 * j / parts)); | 
| 317 |                             newStops.append(t: QGradientStop(stops.at(i).first + j * step, QColor::fromRgba(rgba: color))); | 
| 318 |                         } | 
| 319 |                     } | 
| 320 |                     fromColor = toColor; | 
| 321 |                 } | 
| 322 |                 newStops.append(t: stops.back()); | 
| 323 |                 stops = newStops; | 
| 324 |             } | 
| 325 |         } | 
| 326 |  | 
| 327 |         for (const QGradientStop &stop : qAsConst(t&: stops)) { | 
| 328 |             const QString color = stop.second.name(format: QColor::HexRgb); | 
| 329 |             str << QLatin1String("    <stop offset=\"" )<< stop.first << QLatin1String("\" " ) | 
| 330 |                 << QLatin1String("stop-color=\"" ) << color << QLatin1String("\" " ) | 
| 331 |                 << QLatin1String("stop-opacity=\"" ) << stop.second.alphaF() <<QLatin1String("\" />\n" ); | 
| 332 |         } | 
| 333 |     } | 
| 334 |  | 
| 335 |     void saveGradientUnits(QTextStream &str, const QGradient *gradient) | 
| 336 |     { | 
| 337 |         str << QLatin1String("gradientUnits=\"" ); | 
| 338 |         if (gradient && (gradient->coordinateMode() == QGradient::ObjectBoundingMode || gradient->coordinateMode() == QGradient::ObjectMode)) | 
| 339 |             str << QLatin1String("objectBoundingBox" ); | 
| 340 |         else | 
| 341 |             str << QLatin1String("userSpaceOnUse" ); | 
| 342 |         str << QLatin1String("\" " ); | 
| 343 |     } | 
| 344 |  | 
| 345 |     void generateQtDefaults() | 
| 346 |     { | 
| 347 |         *d_func()->stream << QLatin1String("fill=\"none\" " ); | 
| 348 |         *d_func()->stream << QLatin1String("stroke=\"black\" " ); | 
| 349 |         *d_func()->stream << QLatin1String("stroke-width=\"1\" " ); | 
| 350 |         *d_func()->stream << QLatin1String("fill-rule=\"evenodd\" " ); | 
| 351 |         *d_func()->stream << QLatin1String("stroke-linecap=\"square\" " ); | 
| 352 |         *d_func()->stream << QLatin1String("stroke-linejoin=\"bevel\" " ); | 
| 353 |         *d_func()->stream << QLatin1String(">\n" ); | 
| 354 |     } | 
| 355 |     inline QTextStream &stream() | 
| 356 |     { | 
| 357 |         return *d_func()->stream; | 
| 358 |     } | 
| 359 |  | 
| 360 |  | 
| 361 |     void qpenToSvg(const QPen &spen) | 
| 362 |     { | 
| 363 |         d_func()->pen = spen; | 
| 364 |  | 
| 365 |         switch (spen.style()) { | 
| 366 |         case Qt::NoPen: | 
| 367 |             stream() << QLatin1String("stroke=\"none\" " ); | 
| 368 |  | 
| 369 |             d_func()->attributes.stroke = QLatin1String("none" ); | 
| 370 |             d_func()->attributes.strokeOpacity = QString(); | 
| 371 |             return; | 
| 372 |             break; | 
| 373 |         case Qt::SolidLine: { | 
| 374 |             QString color, colorOpacity; | 
| 375 |  | 
| 376 |             translate_color(color: spen.color(), color_string: &color, | 
| 377 |                             opacity_string: &colorOpacity); | 
| 378 |             d_func()->attributes.stroke = color; | 
| 379 |             d_func()->attributes.strokeOpacity = colorOpacity; | 
| 380 |  | 
| 381 |             stream() << QLatin1String("stroke=\"" )<<color<< QLatin1String("\" " ); | 
| 382 |             stream() << QLatin1String("stroke-opacity=\"" )<<colorOpacity<< QLatin1String("\" " ); | 
| 383 |         } | 
| 384 |             break; | 
| 385 |         case Qt::DashLine: | 
| 386 |         case Qt::DotLine: | 
| 387 |         case Qt::DashDotLine: | 
| 388 |         case Qt::DashDotDotLine: | 
| 389 |         case Qt::CustomDashLine: { | 
| 390 |             QString color, colorOpacity, dashPattern, dashOffset; | 
| 391 |  | 
| 392 |             qreal penWidth = spen.width() == 0 ? qreal(1) : spen.widthF(); | 
| 393 |  | 
| 394 |             translate_color(color: spen.color(), color_string: &color, opacity_string: &colorOpacity); | 
| 395 |             translate_dashPattern(pattern: spen.dashPattern(), width: penWidth, pattern_string: &dashPattern); | 
| 396 |  | 
| 397 |             // SVG uses absolute offset | 
| 398 |             dashOffset = QString::number(spen.dashOffset() * penWidth); | 
| 399 |  | 
| 400 |             d_func()->attributes.stroke = color; | 
| 401 |             d_func()->attributes.strokeOpacity = colorOpacity; | 
| 402 |             d_func()->attributes.dashPattern = dashPattern; | 
| 403 |             d_func()->attributes.dashOffset = dashOffset; | 
| 404 |  | 
| 405 |             stream() << QLatin1String("stroke=\"" )<<color<< QLatin1String("\" " ); | 
| 406 |             stream() << QLatin1String("stroke-opacity=\"" )<<colorOpacity<< QLatin1String("\" " ); | 
| 407 |             stream() << QLatin1String("stroke-dasharray=\"" )<<dashPattern<< QLatin1String("\" " ); | 
| 408 |             stream() << QLatin1String("stroke-dashoffset=\"" )<<dashOffset<< QLatin1String("\" " ); | 
| 409 |             break; | 
| 410 |         } | 
| 411 |         default: | 
| 412 |             qWarning(msg: "Unsupported pen style" ); | 
| 413 |             break; | 
| 414 |         } | 
| 415 |  | 
| 416 |         if (spen.widthF() == 0) | 
| 417 |             stream() <<"stroke-width=\"1\" " ; | 
| 418 |         else | 
| 419 |             stream() <<"stroke-width=\""  << spen.widthF() << "\" " ; | 
| 420 |  | 
| 421 |         switch (spen.capStyle()) { | 
| 422 |         case Qt::FlatCap: | 
| 423 |             stream() << "stroke-linecap=\"butt\" " ; | 
| 424 |             break; | 
| 425 |         case Qt::SquareCap: | 
| 426 |             stream() << "stroke-linecap=\"square\" " ; | 
| 427 |             break; | 
| 428 |         case Qt::RoundCap: | 
| 429 |             stream() << "stroke-linecap=\"round\" " ; | 
| 430 |             break; | 
| 431 |         default: | 
| 432 |             qWarning(msg: "Unhandled cap style" ); | 
| 433 |         } | 
| 434 |         switch (spen.joinStyle()) { | 
| 435 |         case Qt::SvgMiterJoin: | 
| 436 |         case Qt::MiterJoin: | 
| 437 |             stream() << "stroke-linejoin=\"miter\" "  | 
| 438 |                         "stroke-miterlimit=\"" <<spen.miterLimit()<<"\" " ; | 
| 439 |             break; | 
| 440 |         case Qt::BevelJoin: | 
| 441 |             stream() << "stroke-linejoin=\"bevel\" " ; | 
| 442 |             break; | 
| 443 |         case Qt::RoundJoin: | 
| 444 |             stream() << "stroke-linejoin=\"round\" " ; | 
| 445 |             break; | 
| 446 |         default: | 
| 447 |             qWarning(msg: "Unhandled join style" ); | 
| 448 |         } | 
| 449 |     } | 
| 450 |     void qbrushToSvg(const QBrush &sbrush) | 
| 451 |     { | 
| 452 |         d_func()->brush = sbrush; | 
| 453 |         switch (sbrush.style()) { | 
| 454 |         case Qt::SolidPattern: { | 
| 455 |             QString color, colorOpacity; | 
| 456 |             translate_color(color: sbrush.color(), color_string: &color, opacity_string: &colorOpacity); | 
| 457 |             stream() << "fill=\""  << color << "\" "  | 
| 458 |                         "fill-opacity=\""  | 
| 459 |                      << colorOpacity << "\" " ; | 
| 460 |             d_func()->attributes.fill = color; | 
| 461 |             d_func()->attributes.fillOpacity = colorOpacity; | 
| 462 |         } | 
| 463 |             break; | 
| 464 |         case Qt::Dense1Pattern: | 
| 465 |         case Qt::Dense2Pattern: | 
| 466 |         case Qt::Dense3Pattern: | 
| 467 |         case Qt::Dense4Pattern: | 
| 468 |         case Qt::Dense5Pattern: | 
| 469 |         case Qt::Dense6Pattern: | 
| 470 |         case Qt::Dense7Pattern: | 
| 471 |         case Qt::HorPattern: | 
| 472 |         case Qt::VerPattern: | 
| 473 |         case Qt::CrossPattern: | 
| 474 |         case Qt::BDiagPattern: | 
| 475 |         case Qt::FDiagPattern: | 
| 476 |         case Qt::DiagCrossPattern: { | 
| 477 |             QString color, colorOpacity; | 
| 478 |             translate_color(color: sbrush.color(), color_string: &color, opacity_string: &colorOpacity); | 
| 479 |             QString patternId = savePatternBrush(color, brush: sbrush); | 
| 480 |             QString patternRef = QString(QStringLiteral("url(#%1)" )).arg(a: patternId); | 
| 481 |             stream() << "fill=\""  << patternRef << "\" fill-opacity=\""  << colorOpacity << "\" " ; | 
| 482 |             d_func()->attributes.fill = patternRef; | 
| 483 |             d_func()->attributes.fillOpacity = colorOpacity; | 
| 484 |             break; | 
| 485 |         } | 
| 486 |         case Qt::LinearGradientPattern: | 
| 487 |             saveLinearGradientBrush(g: sbrush.gradient()); | 
| 488 |             d_func()->attributes.fill = QString::fromLatin1(str: "url(#%1)" ).arg(a: d_func()->currentGradientName); | 
| 489 |             d_func()->attributes.fillOpacity = QString(); | 
| 490 |             stream() << QLatin1String("fill=\"url(#" ) << d_func()->currentGradientName << QLatin1String(")\" " ); | 
| 491 |             break; | 
| 492 |         case Qt::RadialGradientPattern: | 
| 493 |             saveRadialGradientBrush(g: sbrush.gradient()); | 
| 494 |             d_func()->attributes.fill = QString::fromLatin1(str: "url(#%1)" ).arg(a: d_func()->currentGradientName); | 
| 495 |             d_func()->attributes.fillOpacity = QString(); | 
| 496 |             stream() << QLatin1String("fill=\"url(#" ) << d_func()->currentGradientName << QLatin1String(")\" " ); | 
| 497 |             break; | 
| 498 |         case Qt::ConicalGradientPattern: | 
| 499 |             saveConicalGradientBrush(sbrush.gradient()); | 
| 500 |             d_func()->attributes.fill = QString::fromLatin1(str: "url(#%1)" ).arg(a: d_func()->currentGradientName); | 
| 501 |             d_func()->attributes.fillOpacity = QString(); | 
| 502 |             stream() << QLatin1String("fill=\"url(#" ) << d_func()->currentGradientName << QLatin1String(")\" " ); | 
| 503 |             break; | 
| 504 |         case Qt::NoBrush: | 
| 505 |             stream() << QLatin1String("fill=\"none\" " ); | 
| 506 |             d_func()->attributes.fill = QLatin1String("none" ); | 
| 507 |             d_func()->attributes.fillOpacity = QString(); | 
| 508 |             return; | 
| 509 |             break; | 
| 510 |         default: | 
| 511 |            break; | 
| 512 |         } | 
| 513 |     } | 
| 514 |     void qfontToSvg(const QFont &sfont) | 
| 515 |     { | 
| 516 |         Q_D(QSvgPaintEngine); | 
| 517 |  | 
| 518 |         d->font = sfont; | 
| 519 |  | 
| 520 |         if (d->font.pixelSize() == -1) | 
| 521 |             d->attributes.font_size = QString::number(d->font.pointSizeF() * d->resolution / 72); | 
| 522 |         else | 
| 523 |             d->attributes.font_size = QString::number(d->font.pixelSize()); | 
| 524 |  | 
| 525 |         int svgWeight = d->font.weight(); | 
| 526 |         switch (svgWeight) { | 
| 527 |         case QFont::Light: | 
| 528 |             svgWeight = 100; | 
| 529 |             break; | 
| 530 |         case QFont::Normal: | 
| 531 |             svgWeight = 400; | 
| 532 |             break; | 
| 533 |         case QFont::Bold: | 
| 534 |             svgWeight = 700; | 
| 535 |             break; | 
| 536 |         default: | 
| 537 |             svgWeight *= 10; | 
| 538 |         } | 
| 539 |  | 
| 540 |         d->attributes.font_weight = QString::number(svgWeight); | 
| 541 |         d->attributes.font_family = d->font.family(); | 
| 542 |         d->attributes.font_style = d->font.italic() ? QLatin1String("italic" ) : QLatin1String("normal" ); | 
| 543 |  | 
| 544 |         *d->stream << "font-family=\""  << d->attributes.font_family << "\" "  | 
| 545 |                       "font-size=\""  << d->attributes.font_size << "\" "  | 
| 546 |                       "font-weight=\""  << d->attributes.font_weight << "\" "  | 
| 547 |                       "font-style=\""  << d->attributes.font_style << "\" "  | 
| 548 |                    << Qt::endl; | 
| 549 |     } | 
| 550 | }; | 
| 551 |  | 
| 552 | class QSvgGeneratorPrivate | 
| 553 | { | 
| 554 | public: | 
| 555 |     QSvgPaintEngine *engine; | 
| 556 |  | 
| 557 |     uint owns_iodevice : 1; | 
| 558 |     QString fileName; | 
| 559 | }; | 
| 560 |  | 
| 561 | /*! | 
| 562 |     \class QSvgGenerator | 
| 563 |     \ingroup painting | 
| 564 |     \inmodule QtSvg | 
| 565 |     \since 4.3 | 
| 566 |     \brief The QSvgGenerator class provides a paint device that is used to create SVG drawings. | 
| 567 |     \reentrant | 
| 568 |  | 
| 569 |     This paint device represents a Scalable Vector Graphics (SVG) drawing. Like QPrinter, it is | 
| 570 |     designed as a write-only device that generates output in a specific format. | 
| 571 |  | 
| 572 |     To write an SVG file, you first need to configure the output by setting the \l fileName | 
| 573 |     or \l outputDevice properties. It is usually necessary to specify the size of the drawing | 
| 574 |     by setting the \l size property, and in some cases where the drawing will be included in | 
| 575 |     another, the \l viewBox property also needs to be set. | 
| 576 |  | 
| 577 |     \snippet svggenerator/window.cpp configure SVG generator | 
| 578 |  | 
| 579 |     Other meta-data can be specified by setting the \a title, \a description and \a resolution | 
| 580 |     properties. | 
| 581 |  | 
| 582 |     As with other QPaintDevice subclasses, a QPainter object is used to paint onto an instance | 
| 583 |     of this class: | 
| 584 |  | 
| 585 |     \snippet svggenerator/window.cpp begin painting | 
| 586 |     \dots | 
| 587 |     \snippet svggenerator/window.cpp end painting | 
| 588 |  | 
| 589 |     Painting is performed in the same way as for any other paint device. However, | 
| 590 |     it is necessary to use the QPainter::begin() and \l{QPainter::}{end()} to | 
| 591 |     explicitly begin and end painting on the device. | 
| 592 |  | 
| 593 |     The \l{SVG Generator Example} shows how the same painting commands can be used | 
| 594 |     for painting a widget and writing an SVG file. | 
| 595 |  | 
| 596 |     \sa QSvgRenderer, QSvgWidget, {Qt SVG C++ Classes} | 
| 597 | */ | 
| 598 |  | 
| 599 | /*! | 
| 600 |     Constructs a new generator. | 
| 601 | */ | 
| 602 | QSvgGenerator::QSvgGenerator() | 
| 603 |     : d_ptr(new QSvgGeneratorPrivate) | 
| 604 | { | 
| 605 |     Q_D(QSvgGenerator); | 
| 606 |  | 
| 607 |     d->engine = new QSvgPaintEngine; | 
| 608 |     d->owns_iodevice = false; | 
| 609 | } | 
| 610 |  | 
| 611 | /*! | 
| 612 |     Destroys the generator. | 
| 613 | */ | 
| 614 | QSvgGenerator::~QSvgGenerator() | 
| 615 | { | 
| 616 |     Q_D(QSvgGenerator); | 
| 617 |     if (d->owns_iodevice) | 
| 618 |         delete d->engine->outputDevice(); | 
| 619 |     delete d->engine; | 
| 620 | } | 
| 621 |  | 
| 622 | /*! | 
| 623 |     \property QSvgGenerator::title | 
| 624 |     \brief the title of the generated SVG drawing | 
| 625 |     \since 4.5 | 
| 626 |     \sa description | 
| 627 | */ | 
| 628 | QString QSvgGenerator::title() const | 
| 629 | { | 
| 630 |     Q_D(const QSvgGenerator); | 
| 631 |  | 
| 632 |     return d->engine->documentTitle(); | 
| 633 | } | 
| 634 |  | 
| 635 | void QSvgGenerator::setTitle(const QString &title) | 
| 636 | { | 
| 637 |     Q_D(QSvgGenerator); | 
| 638 |  | 
| 639 |     d->engine->setDocumentTitle(title); | 
| 640 | } | 
| 641 |  | 
| 642 | /*! | 
| 643 |     \property QSvgGenerator::description | 
| 644 |     \brief the description of the generated SVG drawing | 
| 645 |     \since 4.5 | 
| 646 |     \sa title | 
| 647 | */ | 
| 648 | QString QSvgGenerator::description() const | 
| 649 | { | 
| 650 |     Q_D(const QSvgGenerator); | 
| 651 |  | 
| 652 |     return d->engine->documentDescription(); | 
| 653 | } | 
| 654 |  | 
| 655 | void QSvgGenerator::setDescription(const QString &description) | 
| 656 | { | 
| 657 |     Q_D(QSvgGenerator); | 
| 658 |  | 
| 659 |     d->engine->setDocumentDescription(description); | 
| 660 | } | 
| 661 |  | 
| 662 | /*! | 
| 663 |     \property QSvgGenerator::size | 
| 664 |     \brief the size of the generated SVG drawing | 
| 665 |     \since 4.5 | 
| 666 |  | 
| 667 |     By default this property is set to \c{QSize(-1, -1)}, which | 
| 668 |     indicates that the generator should not output the width and | 
| 669 |     height attributes of the \c<svg> element. | 
| 670 |  | 
| 671 |     \note It is not possible to change this property while a | 
| 672 |     QPainter is active on the generator. | 
| 673 |  | 
| 674 |     \sa viewBox, resolution | 
| 675 | */ | 
| 676 | QSize QSvgGenerator::size() const | 
| 677 | { | 
| 678 |     Q_D(const QSvgGenerator); | 
| 679 |     return d->engine->size(); | 
| 680 | } | 
| 681 |  | 
| 682 | void QSvgGenerator::setSize(const QSize &size) | 
| 683 | { | 
| 684 |     Q_D(QSvgGenerator); | 
| 685 |     if (d->engine->isActive()) { | 
| 686 |         qWarning(msg: "QSvgGenerator::setSize(), cannot set size while SVG is being generated" ); | 
| 687 |         return; | 
| 688 |     } | 
| 689 |     d->engine->setSize(size); | 
| 690 | } | 
| 691 |  | 
| 692 | /*! | 
| 693 |     \property QSvgGenerator::viewBox | 
| 694 |     \brief the viewBox of the generated SVG drawing | 
| 695 |     \since 4.5 | 
| 696 |  | 
| 697 |     By default this property is set to \c{QRect(0, 0, -1, -1)}, which | 
| 698 |     indicates that the generator should not output the viewBox attribute | 
| 699 |     of the \c<svg> element. | 
| 700 |  | 
| 701 |     \note It is not possible to change this property while a | 
| 702 |     QPainter is active on the generator. | 
| 703 |  | 
| 704 |     \sa viewBox(), size, resolution | 
| 705 | */ | 
| 706 | QRectF QSvgGenerator::viewBoxF() const | 
| 707 | { | 
| 708 |     Q_D(const QSvgGenerator); | 
| 709 |     return d->engine->viewBox(); | 
| 710 | } | 
| 711 |  | 
| 712 | /*! | 
| 713 |     \since 4.5 | 
| 714 |  | 
| 715 |     Returns viewBoxF().toRect(). | 
| 716 |  | 
| 717 |     \sa viewBoxF() | 
| 718 | */ | 
| 719 | QRect QSvgGenerator::viewBox() const | 
| 720 | { | 
| 721 |     Q_D(const QSvgGenerator); | 
| 722 |     return d->engine->viewBox().toRect(); | 
| 723 | } | 
| 724 |  | 
| 725 | void QSvgGenerator::setViewBox(const QRectF &viewBox) | 
| 726 | { | 
| 727 |     Q_D(QSvgGenerator); | 
| 728 |     if (d->engine->isActive()) { | 
| 729 |         qWarning(msg: "QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated" ); | 
| 730 |         return; | 
| 731 |     } | 
| 732 |     d->engine->setViewBox(viewBox); | 
| 733 | } | 
| 734 |  | 
| 735 | void QSvgGenerator::setViewBox(const QRect &viewBox) | 
| 736 | { | 
| 737 |     setViewBox(QRectF(viewBox)); | 
| 738 | } | 
| 739 |  | 
| 740 | /*! | 
| 741 |     \property QSvgGenerator::fileName | 
| 742 |     \brief the target filename for the generated SVG drawing | 
| 743 |     \since 4.5 | 
| 744 |  | 
| 745 |     \sa outputDevice | 
| 746 | */ | 
| 747 | QString QSvgGenerator::fileName() const | 
| 748 | { | 
| 749 |     Q_D(const QSvgGenerator); | 
| 750 |     return d->fileName; | 
| 751 | } | 
| 752 |  | 
| 753 | void QSvgGenerator::setFileName(const QString &fileName) | 
| 754 | { | 
| 755 |     Q_D(QSvgGenerator); | 
| 756 |     if (d->engine->isActive()) { | 
| 757 |         qWarning(msg: "QSvgGenerator::setFileName(), cannot set file name while SVG is being generated" ); | 
| 758 |         return; | 
| 759 |     } | 
| 760 |  | 
| 761 |     if (d->owns_iodevice) | 
| 762 |         delete d->engine->outputDevice(); | 
| 763 |  | 
| 764 |     d->owns_iodevice = true; | 
| 765 |  | 
| 766 |     d->fileName = fileName; | 
| 767 |     QFile *file = new QFile(fileName); | 
| 768 |     d->engine->setOutputDevice(file); | 
| 769 | } | 
| 770 |  | 
| 771 | /*! | 
| 772 |     \property QSvgGenerator::outputDevice | 
| 773 |     \brief the output device for the generated SVG drawing | 
| 774 |     \since 4.5 | 
| 775 |  | 
| 776 |     If both output device and file name are specified, the output device | 
| 777 |     will have precedence. | 
| 778 |  | 
| 779 |     \sa fileName | 
| 780 | */ | 
| 781 | QIODevice *QSvgGenerator::outputDevice() const | 
| 782 | { | 
| 783 |     Q_D(const QSvgGenerator); | 
| 784 |     return d->engine->outputDevice(); | 
| 785 | } | 
| 786 |  | 
| 787 | void QSvgGenerator::setOutputDevice(QIODevice *outputDevice) | 
| 788 | { | 
| 789 |     Q_D(QSvgGenerator); | 
| 790 |     if (d->engine->isActive()) { | 
| 791 |         qWarning(msg: "QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated" ); | 
| 792 |         return; | 
| 793 |     } | 
| 794 |     d->owns_iodevice = false; | 
| 795 |     d->engine->setOutputDevice(outputDevice); | 
| 796 |     d->fileName = QString(); | 
| 797 | } | 
| 798 |  | 
| 799 | /*! | 
| 800 |     \property QSvgGenerator::resolution | 
| 801 |     \brief the resolution of the generated output | 
| 802 |     \since 4.5 | 
| 803 |  | 
| 804 |     The resolution is specified in dots per inch, and is used to | 
| 805 |     calculate the physical size of an SVG drawing. | 
| 806 |  | 
| 807 |     \sa size, viewBox | 
| 808 | */ | 
| 809 | int QSvgGenerator::resolution() const | 
| 810 | { | 
| 811 |     Q_D(const QSvgGenerator); | 
| 812 |     return d->engine->resolution(); | 
| 813 | } | 
| 814 |  | 
| 815 | void QSvgGenerator::setResolution(int dpi) | 
| 816 | { | 
| 817 |     Q_D(QSvgGenerator); | 
| 818 |     d->engine->setResolution(dpi); | 
| 819 | } | 
| 820 |  | 
| 821 | /*! | 
| 822 |     Returns the paint engine used to render graphics to be converted to SVG | 
| 823 |     format information. | 
| 824 | */ | 
| 825 | QPaintEngine *QSvgGenerator::paintEngine() const | 
| 826 | { | 
| 827 |     Q_D(const QSvgGenerator); | 
| 828 |     return d->engine; | 
| 829 | } | 
| 830 |  | 
| 831 | /*! | 
| 832 |     \reimp | 
| 833 | */ | 
| 834 | int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric) const | 
| 835 | { | 
| 836 |     Q_D(const QSvgGenerator); | 
| 837 |     switch (metric) { | 
| 838 |     case QPaintDevice::PdmDepth: | 
| 839 |         return 32; | 
| 840 |     case QPaintDevice::PdmWidth: | 
| 841 |         return d->engine->size().width(); | 
| 842 |     case QPaintDevice::PdmHeight: | 
| 843 |         return d->engine->size().height(); | 
| 844 |     case QPaintDevice::PdmDpiX: | 
| 845 |         return d->engine->resolution(); | 
| 846 |     case QPaintDevice::PdmDpiY: | 
| 847 |         return d->engine->resolution(); | 
| 848 |     case QPaintDevice::PdmHeightMM: | 
| 849 |         return qRound(d: d->engine->size().height() * 25.4 / d->engine->resolution()); | 
| 850 |     case QPaintDevice::PdmWidthMM: | 
| 851 |         return qRound(d: d->engine->size().width() * 25.4 / d->engine->resolution()); | 
| 852 |     case QPaintDevice::PdmNumColors: | 
| 853 |         return 0xffffffff; | 
| 854 |     case QPaintDevice::PdmPhysicalDpiX: | 
| 855 |         return d->engine->resolution(); | 
| 856 |     case QPaintDevice::PdmPhysicalDpiY: | 
| 857 |         return d->engine->resolution(); | 
| 858 |     case QPaintDevice::PdmDevicePixelRatio: | 
| 859 |         return 1; | 
| 860 |     case QPaintDevice::PdmDevicePixelRatioScaled: | 
| 861 |         return 1 * QPaintDevice::devicePixelRatioFScale(); | 
| 862 |     default: | 
| 863 |         qWarning(msg: "QSvgGenerator::metric(), unhandled metric %d\n" , metric); | 
| 864 |         break; | 
| 865 |     } | 
| 866 |     return 0; | 
| 867 | } | 
| 868 |  | 
| 869 | /***************************************************************************** | 
| 870 |  * class QSvgPaintEngine | 
| 871 |  */ | 
| 872 |  | 
| 873 | bool QSvgPaintEngine::begin(QPaintDevice *) | 
| 874 | { | 
| 875 |     Q_D(QSvgPaintEngine); | 
| 876 |     if (!d->outputDevice) { | 
| 877 |         qWarning(msg: "QSvgPaintEngine::begin(), no output device" ); | 
| 878 |         return false; | 
| 879 |     } | 
| 880 |  | 
| 881 |     if (!d->outputDevice->isOpen()) { | 
| 882 |         if (!d->outputDevice->open(mode: QIODevice::WriteOnly | QIODevice::Text)) { | 
| 883 |             qWarning(msg: "QSvgPaintEngine::begin(), could not open output device: '%s'" , | 
| 884 |                      qPrintable(d->outputDevice->errorString())); | 
| 885 |             return false; | 
| 886 |         } | 
| 887 |     } else if (!d->outputDevice->isWritable()) { | 
| 888 |         qWarning(msg: "QSvgPaintEngine::begin(), could not write to read-only output device: '%s'" , | 
| 889 |                  qPrintable(d->outputDevice->errorString())); | 
| 890 |         return false; | 
| 891 |     } | 
| 892 |  | 
| 893 |     d->stream = new QTextStream(&d->header); | 
| 894 |  | 
| 895 |     // stream out the header... | 
| 896 |     *d->stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"  << Qt::endl << "<svg" ; | 
| 897 |  | 
| 898 |     if (d->size.isValid()) { | 
| 899 |         qreal wmm = d->size.width() * 25.4 / d->resolution; | 
| 900 |         qreal hmm = d->size.height() * 25.4 / d->resolution; | 
| 901 |         *d->stream << " width=\""  << wmm << "mm\" height=\""  << hmm << "mm\""  << Qt::endl; | 
| 902 |     } | 
| 903 |  | 
| 904 |     if (d->viewBox.isValid()) { | 
| 905 |         *d->stream << " viewBox=\""  << d->viewBox.left() << ' ' << d->viewBox.top(); | 
| 906 |         *d->stream << ' ' << d->viewBox.width() << ' ' << d->viewBox.height() << '\"' << Qt::endl; | 
| 907 |     } | 
| 908 |  | 
| 909 |     *d->stream << " xmlns=\"http://www.w3.org/2000/svg\""  | 
| 910 |                   " xmlns:xlink=\"http://www.w3.org/1999/xlink\" "  | 
| 911 |                   " version=\"1.2\" baseProfile=\"tiny\">"  << Qt::endl; | 
| 912 |  | 
| 913 |     if (!d->attributes.document_title.isEmpty()) { | 
| 914 |         *d->stream << "<title>"  << d->attributes.document_title << "</title>"  << Qt::endl; | 
| 915 |     } | 
| 916 |  | 
| 917 |     if (!d->attributes.document_description.isEmpty()) { | 
| 918 |         *d->stream << "<desc>"  << d->attributes.document_description << "</desc>"  << Qt::endl; | 
| 919 |     } | 
| 920 |  | 
| 921 |     d->stream->setString(string: &d->defs); | 
| 922 |     *d->stream << "<defs>\n" ; | 
| 923 |  | 
| 924 |     d->stream->setString(string: &d->body); | 
| 925 |     // Start the initial graphics state... | 
| 926 |     *d->stream << "<g " ; | 
| 927 |     generateQtDefaults(); | 
| 928 |     *d->stream << Qt::endl; | 
| 929 |  | 
| 930 |     return true; | 
| 931 | } | 
| 932 |  | 
| 933 | bool QSvgPaintEngine::end() | 
| 934 | { | 
| 935 |     Q_D(QSvgPaintEngine); | 
| 936 |  | 
| 937 |     d->stream->setString(string: &d->defs); | 
| 938 |     *d->stream << "</defs>\n" ; | 
| 939 |  | 
| 940 |     d->stream->setDevice(d->outputDevice); | 
| 941 | #ifndef QT_NO_TEXTCODEC | 
| 942 |     d->stream->setCodec(QTextCodec::codecForName(name: "UTF-8" )); | 
| 943 | #endif | 
| 944 |  | 
| 945 |     *d->stream << d->header; | 
| 946 |     *d->stream << d->defs; | 
| 947 |     *d->stream << d->body; | 
| 948 |     if (d->afterFirstUpdate) | 
| 949 |         *d->stream << "</g>"  << Qt::endl; // close the updateState | 
| 950 |  | 
| 951 |     *d->stream << "</g>"  << Qt::endl // close the Qt defaults | 
| 952 |                << "</svg>"  << Qt::endl; | 
| 953 |  | 
| 954 |     delete d->stream; | 
| 955 |  | 
| 956 |     return true; | 
| 957 | } | 
| 958 |  | 
| 959 | void QSvgPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, | 
| 960 |                                  const QRectF &sr) | 
| 961 | { | 
| 962 |     drawImage(r, pm: pm.toImage(), sr); | 
| 963 | } | 
| 964 |  | 
| 965 | void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image, | 
| 966 |                                 const QRectF &sr, | 
| 967 |                                 Qt::ImageConversionFlags flags) | 
| 968 | { | 
| 969 |     //Q_D(QSvgPaintEngine); | 
| 970 |  | 
| 971 |     Q_UNUSED(sr); | 
| 972 |     Q_UNUSED(flags); | 
| 973 |     stream() << "<image " ; | 
| 974 |     stream() << "x=\"" <<r.x()<<"\" "  | 
| 975 |                 "y=\"" <<r.y()<<"\" "  | 
| 976 |                 "width=\"" <<r.width()<<"\" "  | 
| 977 |                 "height=\"" <<r.height()<<"\" "  | 
| 978 |                 "preserveAspectRatio=\"none\" " ; | 
| 979 |  | 
| 980 |     QByteArray data; | 
| 981 |     QBuffer buffer(&data); | 
| 982 |     buffer.open(openMode: QBuffer::ReadWrite); | 
| 983 |     image.save(device: &buffer, format: "PNG" ); | 
| 984 |     buffer.close(); | 
| 985 |     stream() << "xlink:href=\"data:image/png;base64,"  | 
| 986 |              << data.toBase64() | 
| 987 |              <<"\" />\n" ; | 
| 988 | } | 
| 989 |  | 
| 990 | void QSvgPaintEngine::updateState(const QPaintEngineState &state) | 
| 991 | { | 
| 992 |     Q_D(QSvgPaintEngine); | 
| 993 |     QPaintEngine::DirtyFlags flags = state.state(); | 
| 994 |  | 
| 995 |     // always stream full gstate, which is not required, but... | 
| 996 |     flags |= QPaintEngine::AllDirty; | 
| 997 |  | 
| 998 |     // close old state and start a new one... | 
| 999 |     if (d->afterFirstUpdate) | 
| 1000 |         *d->stream << "</g>\n\n" ; | 
| 1001 |  | 
| 1002 |     *d->stream << "<g " ; | 
| 1003 |  | 
| 1004 |     if (flags & QPaintEngine::DirtyBrush) { | 
| 1005 |         qbrushToSvg(sbrush: state.brush()); | 
| 1006 |     } | 
| 1007 |  | 
| 1008 |     if (flags & QPaintEngine::DirtyPen) { | 
| 1009 |         qpenToSvg(spen: state.pen()); | 
| 1010 |     } | 
| 1011 |  | 
| 1012 |     if (flags & QPaintEngine::DirtyTransform) { | 
| 1013 |         d->matrix = state.transform(); | 
| 1014 |         *d->stream << "transform=\"matrix("  << d->matrix.m11() << ',' | 
| 1015 |                    << d->matrix.m12() << ',' | 
| 1016 |                    << d->matrix.m21() << ',' << d->matrix.m22() << ',' | 
| 1017 |                    << d->matrix.dx() << ',' << d->matrix.dy() | 
| 1018 |                    << ")\""  | 
| 1019 |                    << Qt::endl; | 
| 1020 |     } | 
| 1021 |  | 
| 1022 |     if (flags & QPaintEngine::DirtyFont) { | 
| 1023 |         qfontToSvg(sfont: state.font()); | 
| 1024 |     } | 
| 1025 |  | 
| 1026 |     if (flags & QPaintEngine::DirtyOpacity) { | 
| 1027 |         if (!qFuzzyIsNull(d: state.opacity() - 1)) | 
| 1028 |             stream() << "opacity=\"" <<state.opacity()<<"\" " ; | 
| 1029 |     } | 
| 1030 |  | 
| 1031 |     *d->stream << '>' << Qt::endl; | 
| 1032 |  | 
| 1033 |     d->afterFirstUpdate = true; | 
| 1034 | } | 
| 1035 |  | 
| 1036 | void QSvgPaintEngine::drawEllipse(const QRectF &r) | 
| 1037 | { | 
| 1038 |     Q_D(QSvgPaintEngine); | 
| 1039 |  | 
| 1040 |     const bool isCircle = r.width() == r.height(); | 
| 1041 |     *d->stream << '<' << (isCircle ? "circle"  : "ellipse" ); | 
| 1042 |     if (state->pen().isCosmetic()) | 
| 1043 |         *d->stream << " vector-effect=\"non-scaling-stroke\"" ; | 
| 1044 |     const QPointF c = r.center(); | 
| 1045 |     *d->stream << " cx=\""  << c.x() << "\" cy=\""  << c.y(); | 
| 1046 |     if (isCircle) | 
| 1047 |         *d->stream << "\" r=\""  << r.width() / qreal(2.0); | 
| 1048 |     else | 
| 1049 |         *d->stream << "\" rx=\""  << r.width() / qreal(2.0) << "\" ry=\""  << r.height() / qreal(2.0); | 
| 1050 |     *d->stream << "\"/>"  << Qt::endl; | 
| 1051 | } | 
| 1052 |  | 
| 1053 | void QSvgPaintEngine::drawPath(const QPainterPath &p) | 
| 1054 | { | 
| 1055 |     Q_D(QSvgPaintEngine); | 
| 1056 |  | 
| 1057 |     *d->stream << "<path vector-effect=\""  | 
| 1058 |                << (state->pen().isCosmetic() ? "non-scaling-stroke"  : "none" ) | 
| 1059 |                << "\" fill-rule=\""  | 
| 1060 |                << (p.fillRule() == Qt::OddEvenFill ? "evenodd"  : "nonzero" ) | 
| 1061 |                << "\" d=\"" ; | 
| 1062 |  | 
| 1063 |     for (int i=0; i<p.elementCount(); ++i) { | 
| 1064 |         const QPainterPath::Element &e = p.elementAt(i); | 
| 1065 |         switch (e.type) { | 
| 1066 |         case QPainterPath::MoveToElement: | 
| 1067 |             *d->stream << 'M' << e.x << ',' << e.y; | 
| 1068 |             break; | 
| 1069 |         case QPainterPath::LineToElement: | 
| 1070 |             *d->stream << 'L' << e.x << ',' << e.y; | 
| 1071 |             break; | 
| 1072 |         case QPainterPath::CurveToElement: | 
| 1073 |             *d->stream << 'C' << e.x << ',' << e.y; | 
| 1074 |             ++i; | 
| 1075 |             while (i < p.elementCount()) { | 
| 1076 |                 const QPainterPath::Element &e = p.elementAt(i); | 
| 1077 |                 if (e.type != QPainterPath::CurveToDataElement) { | 
| 1078 |                     --i; | 
| 1079 |                     break; | 
| 1080 |                 } else | 
| 1081 |                     *d->stream << ' '; | 
| 1082 |                 *d->stream << e.x << ',' << e.y; | 
| 1083 |                 ++i; | 
| 1084 |             } | 
| 1085 |             break; | 
| 1086 |         default: | 
| 1087 |             break; | 
| 1088 |         } | 
| 1089 |         if (i != p.elementCount() - 1) { | 
| 1090 |             *d->stream << ' '; | 
| 1091 |         } | 
| 1092 |     } | 
| 1093 |  | 
| 1094 |     *d->stream << "\"/>"  << Qt::endl; | 
| 1095 | } | 
| 1096 |  | 
| 1097 | void QSvgPaintEngine::drawPolygon(const QPointF *points, int pointCount, | 
| 1098 |                                   PolygonDrawMode mode) | 
| 1099 | { | 
| 1100 |     Q_ASSERT(pointCount >= 2); | 
| 1101 |  | 
| 1102 |     //Q_D(QSvgPaintEngine); | 
| 1103 |  | 
| 1104 |     QPainterPath path(points[0]); | 
| 1105 |     for (int i=1; i<pointCount; ++i) | 
| 1106 |         path.lineTo(p: points[i]); | 
| 1107 |  | 
| 1108 |     if (mode == PolylineMode) { | 
| 1109 |         stream() << "<polyline fill=\"none\" vector-effect=\""  | 
| 1110 |                  << (state->pen().isCosmetic() ? "non-scaling-stroke"  : "none" ) | 
| 1111 |                  << "\" points=\"" ; | 
| 1112 |         for (int i = 0; i < pointCount; ++i) { | 
| 1113 |             const QPointF &pt = points[i]; | 
| 1114 |             stream() << pt.x() << ',' << pt.y() << ' '; | 
| 1115 |         } | 
| 1116 |         stream() << "\" />"  <<Qt::endl; | 
| 1117 |     } else { | 
| 1118 |         path.closeSubpath(); | 
| 1119 |         drawPath(p: path); | 
| 1120 |     } | 
| 1121 | } | 
| 1122 |  | 
| 1123 | void QSvgPaintEngine::drawRects(const QRectF *rects, int rectCount) | 
| 1124 | { | 
| 1125 |     Q_D(QSvgPaintEngine); | 
| 1126 |  | 
| 1127 |     for (int i=0; i < rectCount; ++i) { | 
| 1128 |         const QRectF &rect = rects[i].normalized(); | 
| 1129 |         *d->stream << "<rect" ; | 
| 1130 |         if (state->pen().isCosmetic()) | 
| 1131 |             *d->stream << " vector-effect=\"non-scaling-stroke\"" ; | 
| 1132 |         *d->stream << " x=\""  << rect.x() << "\" y=\""  << rect.y() | 
| 1133 |                    << "\" width=\""  << rect.width() << "\" height=\""  << rect.height() | 
| 1134 |                    << "\"/>"  << Qt::endl; | 
| 1135 |     } | 
| 1136 | } | 
| 1137 |  | 
| 1138 | void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &textItem) | 
| 1139 | { | 
| 1140 |     Q_D(QSvgPaintEngine); | 
| 1141 |     if (d->pen.style() == Qt::NoPen) | 
| 1142 |         return; | 
| 1143 |  | 
| 1144 |     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); | 
| 1145 |     if (ti.chars == 0) | 
| 1146 |         QPaintEngine::drawTextItem(p: pt, textItem: ti); // Draw as path | 
| 1147 |     QString s = QString::fromRawData(ti.chars, size: ti.num_chars); | 
| 1148 |  | 
| 1149 |     *d->stream << "<text "  | 
| 1150 |                   "fill=\""  << d->attributes.stroke << "\" "  | 
| 1151 |                   "fill-opacity=\""  << d->attributes.strokeOpacity << "\" "  | 
| 1152 |                   "stroke=\"none\" "  | 
| 1153 |                   "xml:space=\"preserve\" "  | 
| 1154 |                   "x=\""  << pt.x() << "\" y=\""  << pt.y() << "\" " ; | 
| 1155 |     qfontToSvg(sfont: textItem.font()); | 
| 1156 |     *d->stream << " >"  | 
| 1157 |                << s.toHtmlEscaped() | 
| 1158 |                << "</text>"  | 
| 1159 |                << Qt::endl; | 
| 1160 | } | 
| 1161 |  | 
| 1162 | QT_END_NAMESPACE | 
| 1163 |  | 
| 1164 | #endif // QT_NO_SVGGENERATOR | 
| 1165 |  |