Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgLoadingContext.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2011 Jan Hambrecht <jaham@gmx.net>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#include "SvgLoadingContext.h"
8
9#include <QStack>
10#include <QFileInfo>
11#include <QDir>
12
14#include <KoColorSpaceEngine.h>
15#include <KoColorProfile.h>
17
18#include <FlakeDebug.h>
19
20#include "SvgGraphicContext.h"
21#include "SvgUtil.h"
22#include "SvgCssHelper.h"
23#include "SvgStyleParser.h"
24#include "kis_debug.h"
25
26
27class Q_DECL_HIDDEN SvgLoadingContext::Private
28{
29public:
31 : zIndex(0)
32 , documentResourceManager(0)
33 , styleParser(0)
34 {
35
36 }
37
39 {
40 if (! gcStack.isEmpty() && !gcStack.top()->isResolutionFrame) {
41 // Resolution frame is usually the first and is not removed.
42 warnFlake << "the context stack is not empty (current count" << gcStack.size() << ", expected 0)";
43 }
44 qDeleteAll(gcStack);
45 gcStack.clear();
46 delete styleParser;
47 }
50 int zIndex;
52 QHash<QString, KoShape*> loadedShapes;
53 QHash<QString, QDomElement> definitions;
54 QHash<QString, const KoColorProfile*> profiles;
57 FileFetcherFunc fileFetcher;
58};
59
61 : d(new Private())
62{
63 d->documentResourceManager = documentResourceManager;
64 d->styleParser = new SvgStyleParser(*this);
65 Q_ASSERT(d->documentResourceManager);
66}
67
71
73{
74 if (d->gcStack.isEmpty())
75 return 0;
76
77 return d->gcStack.top();
78}
79
81
82SvgGraphicsContext *SvgLoadingContext::pushGraphicsContext(const QDomElement &element, bool inherit)
83{
85 // copy data from current context
86 if (! d->gcStack.isEmpty() && inherit) {
87 gc = new SvgGraphicsContext(*d->gcStack.top());
88 } else {
89 gc = new SvgGraphicsContext();
90 }
91
93
94 gc->filterId.clear(); // filters are not inherited
95 gc->clipPathId.clear(); // clip paths are not inherited
96 gc->clipMaskId.clear(); // clip masks are not inherited
97 gc->display = true; // display is not inherited
98 gc->opacity = 1.0; // opacity is not inherited
99 gc->paintOrder = QString(); //paint order is inherited by default
100
101 if (!element.isNull()) {
102 if (element.hasAttribute("transform")) {
103 SvgTransformParser p(element.attribute("transform"));
104 if (p.isValid()) {
105 QTransform mat = p.transform();
106 gc->matrix = mat * gc->matrix;
107 }
108 }
109 if (element.hasAttribute("xml:base"))
110 gc->xmlBaseDir = element.attribute("xml:base");
111 if (element.hasAttribute("xml:space"))
112 gc->preserveWhitespace = element.attribute("xml:space") == "preserve";
113 }
114
115 d->gcStack.push(gc);
116
117 return gc;
118}
119
121{
122 delete(d->gcStack.pop());
123}
124
125void SvgLoadingContext::setInitialXmlBaseDir(const QString &baseDir)
126{
127 d->initialXmlBaseDir = baseDir;
128}
129
131{
133 return (gc && !gc->xmlBaseDir.isEmpty()) ? gc->xmlBaseDir : d->initialXmlBaseDir;
134}
135
136QString SvgLoadingContext::absoluteFilePath(const QString &href)
137{
138 QFileInfo info(href);
139 if (! info.isRelative())
140 return href;
141
143 if (!gc)
144 return d->initialXmlBaseDir;
145
146 QString baseDir = d->initialXmlBaseDir;
147 if (! gc->xmlBaseDir.isEmpty())
148 baseDir = absoluteFilePath(gc->xmlBaseDir);
149
150 QFileInfo pathInfo(QFileInfo(baseDir).filePath());
151
152 QString relFile = href;
153 while (relFile.startsWith(QLatin1String("../"))) {
154 relFile.remove(0, 3);
155 pathInfo.setFile(pathInfo.dir(), QString());
156 }
157
158 QString absFile = pathInfo.absolutePath() + '/' + relFile;
159
160 return absFile;
161}
162
163QString SvgLoadingContext::relativeFilePath(const QString &href)
164{
165 const SvgGraphicsContext *gc = currentGC();
166 if (!gc) return href;
167
168 QString result = href;
169
170 QFileInfo info(href);
171 if (info.isRelative())
172 return href;
173
174
175 if (!gc->xmlBaseDir.isEmpty()) {
176 result = QDir(gc->xmlBaseDir).relativeFilePath(href);
177 } else if (!d->initialXmlBaseDir.isEmpty()) {
178 result = QDir(d->initialXmlBaseDir).relativeFilePath(href);
179 }
180
181 return QDir::cleanPath(result);
182}
183
185{
186 return d->zIndex++;
187}
188
189void SvgLoadingContext::registerShape(const QString &id, KoShape *shape)
190{
191 if (!id.isEmpty())
192 d->loadedShapes.insert(id, shape);
193}
194
196{
197 return d->loadedShapes.value(id);
198}
199
200void SvgLoadingContext::addDefinition(const QDomElement &element)
201{
202 const QString id = element.attribute("id");
203 if (id.isEmpty() || d->definitions.contains(id))
204 return;
205 d->definitions.insert(id, element);
206}
207
208QDomElement SvgLoadingContext::definition(const QString &id) const
209{
210 return d->definitions.value(id);
211}
212
213bool SvgLoadingContext::hasDefinition(const QString &id) const
214{
215 return d->definitions.contains(id);
216}
217
218void SvgLoadingContext::addStyleSheet(const QDomElement &styleSheet)
219{
220 d->cssStyles.parseStylesheet(styleSheet);
221}
222
223QStringList SvgLoadingContext::matchingCssStyles(const QDomElement &element) const
224{
225 return d->cssStyles.matchStyles(element);
226}
227
229{
230 return *d->styleParser;
231}
232
233void SvgLoadingContext::parseProfile(const QDomElement &element)
234{
235 const QString href = element.attribute("xlink:href");
236 const QByteArray uniqueId = QByteArray::fromHex(element.attribute("local").toLatin1());
237 const QString name = element.attribute("name");
238
239 if (element.attribute("rendering-intent", "auto") != "auto") {
240 // WARNING: Krita does *not* treat rendering intents attributes of the profile!
241 warnFlake << "WARNING: we do *not* treat rendering intents attributes of the profile!";
242 }
243
244 if (d->profiles.contains(name)) {
245 debugFlake << "Profile already in the map!" << ppVar(name);
246 return;
247 }
248
249 const KoColorProfile *profile =
251
252 if (!profile && d->fileFetcher) {
254 KIS_ASSERT(engine);
255 if (engine) {
256 const QString fileName = relativeFilePath(href);
257 const QByteArray profileData = d->fileFetcher(fileName);
258 if (!profileData.isEmpty()) {
259 profile = engine->addProfile(profileData);
260
261 if (profile->uniqueId() != uniqueId) {
262 warnFlake << "WARNING: ProfileID of the attached profile doesn't match the one mentioned in SVG element";
263 warnFlake << " " << ppVar(profile->uniqueId().toHex());
264 warnFlake << " " << ppVar(uniqueId.toHex());
265 }
266 } else {
267 warnFlake << "WARNING: couldn't fetch the ICCprofile file!" << fileName;
268 }
269 }
270 }
271
272 if (profile) {
273 d->profiles.insert(name, profile);
274 } else {
275 warnFlake << "WARNING: couldn't load SVG profile" << ppVar(name) << ppVar(href) << ppVar(uniqueId);
276 }
277}
278
279QHash<QString, const KoColorProfile *> SvgLoadingContext::profiles()
280{
281 return d->profiles;
282}
283
285{
287 for (auto it = d->gcStack.begin(); it != d->gcStack.end(); it++) {
288 SvgGraphicsContext *gc = *it;
291 props2.inheritFrom(props, true);
292 props = props2;
293 }
294 return props;
295}
296
298{
299 KIS_ASSERT(!d->gcStack.isEmpty());
300 return d->gcStack.size() == 1;
301}
302
304{
305 d->fileFetcher = func;
306}
307
308QByteArray SvgLoadingContext::fetchExternalFile(const QString &url)
309{
310 return d->fileFetcher ? d->fileFetcher(url) : QByteArray();
311}
#define warnFlake
Definition FlakeDebug.h:16
#define debugFlake
Definition FlakeDebug.h:15
const Params2D p
static KoColorSpaceEngineRegistry * instance()
T get(const QString &id) const
void inheritFrom(const KoSvgTextProperties &parentProperties, bool resolve=false)
QString clipPathId
the current clip path id
QString xmlBaseDir
the current base directory (used for loading external content)
bool preserveWhitespace
preserve whitespace in element text
KoSvgTextProperties textProperties
Stores textProperties.
bool display
controls display of shape
QTransform matrix
the current transformation matrix
QString filterId
the current filter id
QString paintOrder
String list indicating paint order;.
QString clipMaskId
the current clip mask id
qreal opacity
the shapes opacity
Contains data used for loading svg.
void addDefinition(const QDomElement &element)
Adds a definition for later use.
void setInitialXmlBaseDir(const QString &baseDir)
Sets the initial xml base dir, i.e. the directory the svg file is read from.
SvgGraphicsContext * pushGraphicsContext(const QDomElement &element=QDomElement(), bool inherit=true)
Pushes a new graphics context to the stack.
QString relativeFilePath(const QString &href)
QByteArray fetchExternalFile(const QString &url)
std::function< QByteArray(const QString &) FileFetcherFunc)
void registerShape(const QString &id, KoShape *shape)
Registers a shape so it can be referenced later.
QStack< SvgGraphicsContext * > gcStack
KoDocumentResourceManager * documentResourceManager
void popGraphicsContext()
Pops the current graphics context from the stack.
void addStyleSheet(const QDomElement &styleSheet)
Adds a css style sheet.
QString xmlBaseDir() const
Returns the current xml base dir.
QHash< QString, KoShape * > loadedShapes
QScopedPointer< Private > d
QStringList matchingCssStyles(const QDomElement &element) const
Returns list of css styles matching to the specified element.
KoShape * shapeById(const QString &id)
Returns shape with specified id.
QDomElement definition(const QString &id) const
Returns the definition with the specified id.
FileFetcherFunc fileFetcher
QString absoluteFilePath(const QString &href)
Constructs an absolute file path from the given href and current xml base directory.
QHash< QString, const KoColorProfile * > profiles
SvgGraphicsContext * currentGC() const
Returns the current graphics context.
KoSvgTextProperties resolvedProperties() const
These are the text properties, completely resolved, ensuring that everything is inherited and the siz...
void parseProfile(const QDomElement &element)
parses 'color-profile' tag and saves it in the context
SvgLoadingContext(KoDocumentResourceManager *documentResourceManager)
void setFileFetcher(FileFetcherFunc func)
SvgStyleParser * styleParser
bool hasDefinition(const QString &id) const
Checks if a definition with the specified id exists.
int nextZIndex()
Returns the next z-index.
QHash< QString, QDomElement > definitions
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define ppVar(var)
Definition kis_debug.h:155
virtual QByteArray uniqueId() const =0
virtual const KoColorProfile * addProfile(const QString &filename)=0
static KoColorSpaceRegistry * instance()
const KoColorProfile * profileByUniqueId(const QByteArray &id) const