diff options
Diffstat (limited to 'src/svg/qsvggenerator.cpp')
-rw-r--r-- | src/svg/qsvggenerator.cpp | 226 |
1 files changed, 149 insertions, 77 deletions
diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp index f69e420..af39bb9 100644 --- a/src/svg/qsvggenerator.cpp +++ b/src/svg/qsvggenerator.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt SVG module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsvggenerator.h" @@ -56,6 +20,8 @@ #include "qdebug.h" +#include <optional> + QT_BEGIN_NAMESPACE static void translate_color(const QColor &color, QString *color_string, @@ -86,11 +52,12 @@ static void translate_dashPattern(const QList<qreal> &pattern, qreal width, QStr class QSvgPaintEnginePrivate : public QPaintEnginePrivate { public: - QSvgPaintEnginePrivate() + explicit QSvgPaintEnginePrivate(QSvgGenerator::SvgVersion version) + : svgVersion(version) { size = QSize(); viewBox = QRectF(); - outputDevice = 0; + outputDevice = nullptr; resolution = 72; attributes.document_title = QLatin1String("Qt SVG Document"); @@ -104,6 +71,7 @@ public: numGradients = 0; } + QSvgGenerator::SvgVersion svgVersion; QSize size; QRectF viewBox; QIODevice *outputDevice; @@ -143,6 +111,21 @@ public: QString dashPattern, dashOffset; QString fill, fillOpacity; } attributes; + + QString generateClipPathName() { + ++numClipPaths; + currentClipPathName = QStringLiteral("clipPath%1").arg(numClipPaths); + return currentClipPathName; + } + + std::optional<QPainterPath> clipPath; + bool clipEnabled = false; + bool isClippingEffective() const { + return clipEnabled && clipPath.has_value(); + } + QString currentClipPathName; + int numClipPaths = 0; + bool hasEmittedClipGroup = false; }; static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures() @@ -161,8 +144,8 @@ class QSvgPaintEngine : public QPaintEngine Q_DECLARE_PRIVATE(QSvgPaintEngine) public: - QSvgPaintEngine() - : QPaintEngine(*new QSvgPaintEnginePrivate, + explicit QSvgPaintEngine(QSvgGenerator::SvgVersion version) + : QPaintEngine(*new QSvgPaintEnginePrivate(version), svgEngineFeatures()) { } @@ -171,6 +154,7 @@ public: bool end() override; void updateState(const QPaintEngineState &state) override; + void updateClipState(const QPaintEngineState &state); void popGroup(); void drawEllipse(const QRectF &r) override; @@ -212,12 +196,14 @@ public: d_func()->outputDevice = device; } - int resolution() { return d_func()->resolution; } + int resolution() const { return d_func()->resolution; } void setResolution(int resolution) { Q_ASSERT(!isActive()); d_func()->resolution = resolution; } + QSvgGenerator::SvgVersion svgVersion() const { return d_func()->svgVersion; } + QString savePatternMask(Qt::BrushStyle style) { QString maskId = QString(QStringLiteral("patternmask%1")).arg(style); @@ -323,7 +309,7 @@ public: } } - for (const QGradientStop &stop : qAsConst(stops)) { + for (const QGradientStop &stop : std::as_const(stops)) { const QString color = stop.second.name(QColor::HexRgb); str << QLatin1String(" <stop offset=\"")<< stop.first << QLatin1String("\" ") << QLatin1String("stop-color=\"") << color << QLatin1String("\" ") @@ -581,14 +567,35 @@ public: */ /*! - Constructs a new generator. + \enum QSvgGenerator::SvgVersion + \since 6.5 + + This enumeration describes the version of the SVG output of the + generator. + + \value SvgTiny12 The generated document follows the SVG Tiny 1.2 specification. + \value Svg11 The generated document follows the SVG 1.1 specification. */ -QSvgGenerator::QSvgGenerator() + +/*! + Constructs a new generator using the SVG Tiny 1.2 profile. +*/ +QSvgGenerator::QSvgGenerator() // ### Qt 7: inline + : QSvgGenerator(SvgVersion::SvgTiny12) +{ +} + +/*! + \since 6.5 + + Constructs a new generator that uses the SVG version \a version. +*/ +QSvgGenerator::QSvgGenerator(SvgVersion version) : d_ptr(new QSvgGeneratorPrivate) { Q_D(QSvgGenerator); - d->engine = new QSvgPaintEngine; + d->engine = new QSvgPaintEngine(version); d->owns_iodevice = false; } @@ -803,6 +810,18 @@ void QSvgGenerator::setResolution(int dpi) } /*! + \since 6.5 + + Returns the version of the SVG document that this generator is + producing. +*/ +QSvgGenerator::SvgVersion QSvgGenerator::svgVersion() const +{ + Q_D(const QSvgGenerator); + return d->engine->svgVersion(); +} + +/*! Returns the paint engine used to render graphics to be converted to SVG format information. */ @@ -891,15 +910,23 @@ bool QSvgPaintEngine::begin(QPaintDevice *) } *d->stream << " xmlns=\"http://www.w3.org/2000/svg\"" - " xmlns:xlink=\"http://www.w3.org/1999/xlink\" " - " version=\"1.2\" baseProfile=\"tiny\">" << Qt::endl; + " xmlns:xlink=\"http://www.w3.org/1999/xlink\""; + switch (d->svgVersion) { + case QSvgGenerator::SvgVersion::SvgTiny12: + *d->stream << " version=\"1.2\" baseProfile=\"tiny\">"; + break; + case QSvgGenerator::SvgVersion::Svg11: + *d->stream << " version=\"1.1\">"; + break; + } + *d->stream << Qt::endl; if (!d->attributes.document_title.isEmpty()) { - *d->stream << "<title>" << d->attributes.document_title << "</title>" << Qt::endl; + *d->stream << "<title>" << d->attributes.document_title.toHtmlEscaped() << "</title>" << Qt::endl; } if (!d->attributes.document_description.isEmpty()) { - *d->stream << "<desc>" << d->attributes.document_description << "</desc>" << Qt::endl; + *d->stream << "<desc>" << d->attributes.document_description.toHtmlEscaped() << "</desc>" << Qt::endl; } d->stream->setString(&d->defs); @@ -926,6 +953,8 @@ bool QSvgPaintEngine::end() *d->stream << d->header; *d->stream << d->defs; *d->stream << d->body; + if (d->hasEmittedClipGroup) + *d->stream << "</g>"; if (d->afterFirstUpdate) *d->stream << "</g>" << Qt::endl; // close the updateState @@ -951,12 +980,19 @@ void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image, Q_UNUSED(sr); Q_UNUSED(flags); + QString quality; + if (state->renderHints() & QPainter::SmoothPixmapTransform) { + quality = QLatin1String("optimizeQuality"); + } else { + quality = QLatin1String("optimizeSpeed"); + } stream() << "<image "; stream() << "x=\""<<r.x()<<"\" " "y=\""<<r.y()<<"\" " "width=\""<<r.width()<<"\" " "height=\""<<r.height()<<"\" " - "preserveAspectRatio=\"none\" "; + "preserveAspectRatio=\"none\" " + "image-rendering=\""<<quality<<"\" "; QByteArray data; QBuffer buffer(&data); @@ -971,49 +1007,85 @@ void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image, void QSvgPaintEngine::updateState(const QPaintEngineState &state) { Q_D(QSvgPaintEngine); - QPaintEngine::DirtyFlags flags = state.state(); - // always stream full gstate, which is not required, but... - flags |= QPaintEngine::AllDirty; // close old state and start a new one... + if (d->hasEmittedClipGroup) + *d->stream << "</g>\n"; if (d->afterFirstUpdate) *d->stream << "</g>\n\n"; - *d->stream << "<g "; + updateClipState(state); - if (flags & QPaintEngine::DirtyBrush) { - qbrushToSvg(state.brush()); + if (d->isClippingEffective()) { + *d->stream << QStringLiteral("<g clip-path=\"url(#%1)\">").arg(d->currentClipPathName); + d->hasEmittedClipGroup = true; + } else { + d->hasEmittedClipGroup = false; } - if (flags & QPaintEngine::DirtyPen) { - qpenToSvg(state.pen()); - } + *d->stream << "<g "; - if (flags & QPaintEngine::DirtyTransform) { - d->matrix = state.transform(); - *d->stream << "transform=\"matrix(" << d->matrix.m11() << ',' - << d->matrix.m12() << ',' - << d->matrix.m21() << ',' << d->matrix.m22() << ',' - << d->matrix.dx() << ',' << d->matrix.dy() - << ")\"" - << Qt::endl; - } + qbrushToSvg(state.brush()); + qpenToSvg(state.pen()); - if (flags & QPaintEngine::DirtyFont) { - qfontToSvg(state.font()); - } + d->matrix = state.transform(); + *d->stream << "transform=\"matrix(" << d->matrix.m11() << ',' + << d->matrix.m12() << ',' + << d->matrix.m21() << ',' << d->matrix.m22() << ',' + << d->matrix.dx() << ',' << d->matrix.dy() + << ")\"" + << Qt::endl; - if (flags & QPaintEngine::DirtyOpacity) { - if (!qFuzzyIsNull(state.opacity() - 1)) - stream() << "opacity=\""<<state.opacity()<<"\" "; - } + qfontToSvg(state.font()); + + if (!qFuzzyIsNull(state.opacity() - 1)) + stream() << "opacity=\""<<state.opacity()<<"\" "; *d->stream << '>' << Qt::endl; d->afterFirstUpdate = true; } +void QSvgPaintEngine::updateClipState(const QPaintEngineState &state) +{ + Q_D(QSvgPaintEngine); + switch (d->svgVersion) { + case QSvgGenerator::SvgVersion::SvgTiny12: + // no clip handling in Tiny 1.2 + return; + case QSvgGenerator::SvgVersion::Svg11: + break; + } + + const QPaintEngine::DirtyFlags flags = state.state(); + + const bool clippingChanged = flags.testAnyFlags(DirtyClipPath | DirtyClipRegion); + if (clippingChanged) { + switch (state.clipOperation()) { + case Qt::NoClip: + d->clipEnabled = false; + d->clipPath.reset(); + break; + case Qt::ReplaceClip: + case Qt::IntersectClip: + d->clipPath = painter()->transform().map(painter()->clipPath()); + break; + } + } + + if (flags & DirtyClipEnabled) + d->clipEnabled = state.isClipEnabled(); + + if (d->isClippingEffective() && clippingChanged) { + d->stream->setString(&d->defs); + *d->stream << QLatin1String("<clipPath id=\"%1\">\n").arg(d->generateClipPathName()); + drawPath(*d->clipPath); + *d->stream << "</clipPath>\n"; + d->stream->setString(&d->body); + } +} + void QSvgPaintEngine::drawEllipse(const QRectF &r) { Q_D(QSvgPaintEngine); |