diff --git a/libs/flake/svg/SvgGraphicContext.cpp b/libs/flake/svg/SvgGraphicContext.cpp index e9fe97506f..aa3c1fdcec 100644 --- a/libs/flake/svg/SvgGraphicContext.cpp +++ b/libs/flake/svg/SvgGraphicContext.cpp @@ -1,62 +1,70 @@ /* This file is part of the KDE project * Copyright (C) 2003,2005 Rob Buis * Copyright (C) 2007,2009 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "SvgGraphicContext.h" #include "kis_pointer_utils.h" SvgGraphicsContext::SvgGraphicsContext() : stroke(toQShared(new KoShapeStroke())) , textProperties(KoSvgTextProperties::defaultProperties()) { stroke->setLineStyle(Qt::NoPen, QVector()); // default is no stroke stroke->setLineWidth(1.0); stroke->setCapStyle(Qt::FlatCap); stroke->setJoinStyle(Qt::MiterJoin); } +SvgGraphicsContext::SvgGraphicsContext(const SvgGraphicsContext &gc) + : stroke(toQShared(new KoShapeStroke(*(gc.stroke.data())))) +{ + KoShapeStrokeSP newStroke = stroke; + *this = gc; + this->stroke = newStroke; +} + void SvgGraphicsContext::workaroundClearInheritedFillProperties() { /** * HACK ALERT: according to SVG patterns, clip paths and clip masks * must not inherit any properties from the referencing element. * We still don't support it, therefore we reset only fill/stroke * properties to avoid cyclic fill inheritance, which may cause * infinite recursion. */ strokeType = None; stroke = toQShared(new KoShapeStroke()); stroke->setLineStyle(Qt::NoPen, QVector()); // default is no stroke stroke->setLineWidth(1.0); stroke->setCapStyle(Qt::FlatCap); stroke->setJoinStyle(Qt::MiterJoin); fillType = Solid; fillRule = Qt::WindingFill; fillColor = QColor(Qt::black); // default is black fill as per svg spec opacity = 1.0; currentColor = Qt::black; } diff --git a/libs/flake/svg/SvgGraphicContext.h b/libs/flake/svg/SvgGraphicContext.h index 5e8c12f869..d2badbfb26 100644 --- a/libs/flake/svg/SvgGraphicContext.h +++ b/libs/flake/svg/SvgGraphicContext.h @@ -1,84 +1,86 @@ /* This file is part of the KDE project * Copyright (C) 2003,2005 Rob Buis * Copyright (C) 2007,2009 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef SVGGRAPHICCONTEXT_H #define SVGGRAPHICCONTEXT_H #include "kritaflake_export.h" #include #include #include #include class KRITAFLAKE_EXPORT SvgGraphicsContext { public: // Fill/stroke styles enum StyleType { None, ///< no style Solid, ///< solid style Complex ///< gradient or pattern style }; SvgGraphicsContext(); + SvgGraphicsContext(const SvgGraphicsContext &gc); + void workaroundClearInheritedFillProperties(); StyleType fillType {Solid}; ///< the current fill type Qt::FillRule fillRule {Qt::WindingFill}; ///< the current fill rule QColor fillColor {QColor(Qt::black)}; ///< the current fill color. Default is black fill as per svg spec QString fillId; ///< the current fill id (used for gradient/pattern fills) StyleType strokeType {None};///< the current stroke type QString strokeId; ///< the current stroke id (used for gradient strokes) KoShapeStrokeSP stroke; ///< the current stroke QString filterId; ///< the current filter id QString clipPathId; ///< the current clip path id QString clipMaskId; ///< the current clip mask id Qt::FillRule clipRule {Qt::WindingFill}; ///< the current clip rule qreal opacity {1.0}; ///< the shapes opacity QTransform matrix; ///< the current transformation matrix QFont font; ///< the current font QStringList fontFamiliesList; ///< the full list of all the families to search glyphs in QColor currentColor {Qt::black}; ///< the current color QString xmlBaseDir; ///< the current base directory (used for loading external content) bool preserveWhitespace {false}; ///< preserve whitespace in element text QRectF currentBoundingBox; ///< the current bound box used for bounding box units bool forcePercentage {false}; ///< force parsing coordinates/length as percentages of currentBoundbox QTransform viewboxTransform; ///< view box transformation bool display {true}; ///< controls display of shape bool visible {true}; ///< controls visibility of the shape (inherited) bool isResolutionFrame {false}; qreal pixelsPerInch {72.0}; ///< controls the resolution of the image raster qreal forcedFontSizeCoeff {1.0}; ///< workaround for a Krita 3.3 odf-based files that use different resolution for font size. No workaround by default QString markerStartId; QString markerMidId; QString markerEndId; bool autoFillMarkers {false}; KoSvgTextProperties textProperties; }; #endif // SVGGRAPHICCONTEXT_H diff --git a/libs/flake/svg/SvgLoadingContext.cpp b/libs/flake/svg/SvgLoadingContext.cpp index bf38bec314..c956eafc6b 100644 --- a/libs/flake/svg/SvgLoadingContext.cpp +++ b/libs/flake/svg/SvgLoadingContext.cpp @@ -1,302 +1,303 @@ /* This file is part of the KDE project * Copyright (C) 2011 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "SvgLoadingContext.h" #include #include #include #include #include #include #include #include #include "SvgGraphicContext.h" #include "SvgUtil.h" #include "SvgCssHelper.h" #include "SvgStyleParser.h" #include "kis_debug.h" class Q_DECL_HIDDEN SvgLoadingContext::Private { public: Private() : zIndex(0), styleParser(0) { } ~Private() { if (! gcStack.isEmpty() && !gcStack.top()->isResolutionFrame) { // Resolution frame is usually the first and is not removed. warnFlake << "the context stack is not empty (current count" << gcStack.size() << ", expected 0)"; } qDeleteAll(gcStack); gcStack.clear(); delete styleParser; } QStack gcStack; QString initialXmlBaseDir; int zIndex; KoDocumentResourceManager *documentResourceManager; QHash loadedShapes; QHash definitions; QHash profiles; SvgCssHelper cssStyles; SvgStyleParser *styleParser; FileFetcherFunc fileFetcher; }; SvgLoadingContext::SvgLoadingContext(KoDocumentResourceManager *documentResourceManager) : d(new Private()) { d->documentResourceManager = documentResourceManager; d->styleParser = new SvgStyleParser(*this); Q_ASSERT(d->documentResourceManager); } SvgLoadingContext::~SvgLoadingContext() { delete d; } SvgGraphicsContext *SvgLoadingContext::currentGC() const { if (d->gcStack.isEmpty()) return 0; return d->gcStack.top(); } #include "parsers/SvgTransformParser.h" SvgGraphicsContext *SvgLoadingContext::pushGraphicsContext(const KoXmlElement &element, bool inherit) { - SvgGraphicsContext *gc = new SvgGraphicsContext; - + SvgGraphicsContext *gc; // copy data from current context if (! d->gcStack.isEmpty() && inherit) { - *gc = *(d->gcStack.top()); + gc = new SvgGraphicsContext(*d->gcStack.top()); + } else { + gc = new SvgGraphicsContext(); } gc->textProperties.resetNonInheritableToDefault(); // some of the text properties are not inherited gc->filterId.clear(); // filters are not inherited gc->clipPathId.clear(); // clip paths are not inherited gc->clipMaskId.clear(); // clip masks are not inherited gc->display = true; // display is not inherited gc->opacity = 1.0; // opacity is not inherited if (!element.isNull()) { if (element.hasAttribute("transform")) { SvgTransformParser p(element.attribute("transform")); if (p.isValid()) { QTransform mat = p.transform(); gc->matrix = mat * gc->matrix; } } if (element.hasAttribute("xml:base")) gc->xmlBaseDir = element.attribute("xml:base"); if (element.hasAttribute("xml:space")) gc->preserveWhitespace = element.attribute("xml:space") == "preserve"; } d->gcStack.push(gc); return gc; } void SvgLoadingContext::popGraphicsContext() { delete(d->gcStack.pop()); } void SvgLoadingContext::setInitialXmlBaseDir(const QString &baseDir) { d->initialXmlBaseDir = baseDir; } QString SvgLoadingContext::xmlBaseDir() const { SvgGraphicsContext *gc = currentGC(); return (gc && !gc->xmlBaseDir.isEmpty()) ? gc->xmlBaseDir : d->initialXmlBaseDir; } QString SvgLoadingContext::absoluteFilePath(const QString &href) { QFileInfo info(href); if (! info.isRelative()) return href; SvgGraphicsContext *gc = currentGC(); if (!gc) return d->initialXmlBaseDir; QString baseDir = d->initialXmlBaseDir; if (! gc->xmlBaseDir.isEmpty()) baseDir = absoluteFilePath(gc->xmlBaseDir); QFileInfo pathInfo(QFileInfo(baseDir).filePath()); QString relFile = href; while (relFile.startsWith(QLatin1String("../"))) { relFile.remove(0, 3); pathInfo.setFile(pathInfo.dir(), QString()); } QString absFile = pathInfo.absolutePath() + '/' + relFile; return absFile; } QString SvgLoadingContext::relativeFilePath(const QString &href) { const SvgGraphicsContext *gc = currentGC(); if (!gc) return href; QString result = href; if (!gc->xmlBaseDir.isEmpty()) { result = gc->xmlBaseDir + QDir::separator() + href; } else if (!d->initialXmlBaseDir.isEmpty()) { result = d->initialXmlBaseDir + QDir::separator() + href; } return QDir::cleanPath(result); } int SvgLoadingContext::nextZIndex() { return d->zIndex++; } KoImageCollection* SvgLoadingContext::imageCollection() { return d->documentResourceManager->imageCollection(); } void SvgLoadingContext::registerShape(const QString &id, KoShape *shape) { if (!id.isEmpty()) d->loadedShapes.insert(id, shape); } KoShape* SvgLoadingContext::shapeById(const QString &id) { return d->loadedShapes.value(id); } void SvgLoadingContext::addDefinition(const KoXmlElement &element) { const QString id = element.attribute("id"); if (id.isEmpty() || d->definitions.contains(id)) return; d->definitions.insert(id, element); } KoXmlElement SvgLoadingContext::definition(const QString &id) const { return d->definitions.value(id); } bool SvgLoadingContext::hasDefinition(const QString &id) const { return d->definitions.contains(id); } void SvgLoadingContext::addStyleSheet(const KoXmlElement &styleSheet) { d->cssStyles.parseStylesheet(styleSheet); } QStringList SvgLoadingContext::matchingCssStyles(const KoXmlElement &element) const { return d->cssStyles.matchStyles(element); } SvgStyleParser &SvgLoadingContext::styleParser() { return *d->styleParser; } void SvgLoadingContext::parseProfile(const KoXmlElement &element) { const QString href = element.attribute("xlink:href"); const QByteArray uniqueId = QByteArray::fromHex(element.attribute("local").toLatin1()); const QString name = element.attribute("name"); if (element.attribute("rendering-intent", "auto") != "auto") { // WARNING: Krita does *not* treat rendering intents attributes of the profile! debugFlake << "WARNING: we do *not* treat rendering intents attributes of the profile!"; } if (d->profiles.contains(name)) { debugFlake << "Profile already in the map!" << ppVar(name); return; } const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByUniqueId(uniqueId); if (!profile && d->fileFetcher) { KoColorSpaceEngine *engine = KoColorSpaceEngineRegistry::instance()->get("icc"); KIS_ASSERT(engine); if (engine) { const QString fileName = relativeFilePath(href); const QByteArray profileData = d->fileFetcher(fileName); if (!profileData.isEmpty()) { profile = engine->addProfile(profileData); if (profile->uniqueId() != uniqueId) { debugFlake << "WARNING: ProfileID of the attached profile doesn't match the one mentioned in SVG element"; debugFlake << " " << ppVar(profile->uniqueId().toHex()); debugFlake << " " << ppVar(uniqueId.toHex()); } } else { debugFlake << "WARNING: couldn't fetch the ICCprofile file!" << fileName; } } } if (profile) { d->profiles.insert(name, profile); } else { debugFlake << "WARNING: couldn't load SVG profile" << ppVar(name) << ppVar(href) << ppVar(uniqueId); } } bool SvgLoadingContext::isRootContext() const { KIS_ASSERT(!d->gcStack.isEmpty()); return d->gcStack.size() == 1; } void SvgLoadingContext::setFileFetcher(SvgLoadingContext::FileFetcherFunc func) { d->fileFetcher = func; } QByteArray SvgLoadingContext::fetchExternalFile(const QString &url) { return d->fileFetcher ? d->fileFetcher(url) : QByteArray(); }