Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgSavingContext.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 "SvgSavingContext.h"
8#include "SvgUtil.h"
9
10#include <KoXmlWriter.h>
11#include <KoShape.h>
12#include <KoShapeGroup.h>
13#include <KoShapeLayer.h>
14
15#include <QTemporaryFile>
16
17#include <QImage>
18#include <QTransform>
19#include <QBuffer>
20#include <QHash>
21#include <QFile>
22#include <QFileInfo>
23#include <KisMimeDatabase.h>
24
25class Q_DECL_HIDDEN SvgSavingContext::Private
26{
27public:
28 Private(QIODevice *_mainDevice, QIODevice *_styleDevice)
29 : mainDevice(_mainDevice)
30 , styleDevice(_styleDevice)
31 , styleWriter(0)
32 , shapeWriter(0)
33 , saveInlineImages(true)
34 {
35 styleWriter.reset(new KoXmlWriter(&styleBuffer, 1));
36 styleWriter->startElement("defs");
37 shapeWriter.reset(new KoXmlWriter(&shapeBuffer, 1));
38
39 const qreal scaleToUserSpace = SvgUtil::toUserSpace(1.0);
40 userSpaceMatrix.scale(scaleToUserSpace, scaleToUserSpace);
41 }
42
44 {
45 }
46
47 QIODevice *mainDevice;
48 QIODevice *styleDevice;
49 QBuffer styleBuffer;
50 QBuffer shapeBuffer;
51 QScopedPointer<KoXmlWriter> styleWriter;
52 QScopedPointer<KoXmlWriter> shapeWriter;
53
54 QHash<QString, int> uniqueNames;
55 QHash<const KoShape*, QString> shapeIds;
56 QTransform userSpaceMatrix;
58 bool strippedTextMode = false;
59};
60
61SvgSavingContext::SvgSavingContext(QIODevice &outputDevice, bool saveInlineImages)
62 : d(new Private(&outputDevice, 0))
63{
64 d->saveInlineImages = saveInlineImages;
65}
66
67SvgSavingContext::SvgSavingContext(QIODevice &shapesDevice, QIODevice &styleDevice, bool saveInlineImages)
68 : d(new Private(&shapesDevice, &styleDevice))
69{
70 d->saveInlineImages = saveInlineImages;
71}
72
74{
75 d->styleWriter->endElement();
76
77 if (d->styleDevice) {
78 d->styleDevice->write(d->styleBuffer.data());
79 } else {
80 d->mainDevice->write(d->styleBuffer.data());
81 d->mainDevice->write("\n");
82 }
83
84 d->mainDevice->write(d->shapeBuffer.data());
85
86 delete d;
87}
88
90{
91 return *d->styleWriter;
92}
93
95{
96 return *d->shapeWriter;
97}
98
99QString SvgSavingContext::createUID(const QString &base)
100{
101 QString idBase = base.isEmpty() ? "defitem" : base;
102 int counter = d->uniqueNames.value(idBase);
103 QString res;
104 do {
105 res = idBase + QString::number(counter);
106 counter++;
107 } while (d->uniqueNames.contains(res));
108
109 d->uniqueNames.insert(idBase, counter);
110 d->uniqueNames.insert(res, 1);
111 return res;
112}
113
115{
116 QString id;
117 // do we have already an id for this object ?
118 if (d->shapeIds.contains(obj)) {
119 // use existing id
120 id = d->shapeIds[obj];
121 } else {
122 // initialize from object name
123 id = obj->name();
124 // if object name is not empty and was not used already
125 // we can use it as is
126 if (!id.isEmpty() && !d->uniqueNames.contains(id)) {
127 // add to unique names so it does not get reused
128 d->uniqueNames.insert(id, 1);
129 } else {
130 if (id.isEmpty()) {
131 // differentiate a little between shape types
132 if (dynamic_cast<const KoShapeGroup*>(obj))
133 id = "group";
134 else if (dynamic_cast<const KoShapeLayer*>(obj))
135 id = "layer";
136 else
137 id = "shape";
138 }
139 // create a completely new id based on object name
140 // or a generic name
141 id = createUID(id);
142 }
143 // record id for this shape
144 d->shapeIds.insert(obj, id);
145 }
146 return id;
147}
148
150{
151 return d->userSpaceMatrix;
152}
153
155{
156 return d->saveInlineImages;
157}
158
159QString SvgSavingContext::createFileName(const QString &extension)
160{
161 QFile *file = qobject_cast<QFile*>(d->mainDevice);
162 if (!file)
163 return QString();
164
165 QFileInfo fi(file->fileName());
166 QString path = fi.absolutePath();
167 QString dstBaseFilename = fi.completeBaseName();
168
169 // create a filename for the image file at the destination directory
170 QString fname = dstBaseFilename + '_' + createUID("file");
171
172 // check if file exists already
173 int i = 0;
174 QString counter;
175 // change filename as long as the filename already exists
176 while (QFile(path + fname + counter + extension).exists()) {
177 counter = QString("_%1").arg(++i);
178 }
179
180 return fname + counter + extension;
181}
182
183QString SvgSavingContext::saveImage(const QImage &image)
184{
185 if (isSavingInlineImages()) {
186 QBuffer buffer;
187 buffer.open(QIODevice::WriteOnly);
188 if (image.save(&buffer, "PNG")) {
189 const QString header("data:image/x-png;base64,");
190 return header + buffer.data().toBase64();
191 }
192 } else {
193 // write to a temp file first
194 QTemporaryFile imgFile;
195 if (image.save(&imgFile, "PNG")) {
196 QString dstFilename = createFileName(".png");
197 if (QFile::copy(imgFile.fileName(), dstFilename)) {
198 return dstFilename;
199 }
200 else {
201 QFile f(imgFile.fileName());
202 f.remove();
203 }
204 }
205 }
206
207 return QString();
208}
209
211{
212 d->strippedTextMode = value;
213}
214
216{
217 return d->strippedTextMode;
218}
float value(const T *src, size_t ch)
QString name() const
Definition KoShape.cpp:1150
Context for saving svg files.
QString createUID(const QString &base)
Create a unique id from the specified base text.
virtual ~SvgSavingContext()
Virtual destructor.
QTransform userSpaceTransform() const
Returns the transformation used to transform into user space.
QHash< const KoShape *, QString > shapeIds
QString saveImage(const QImage &image)
Saves given image and returns the href used.
QScopedPointer< KoXmlWriter > shapeWriter
QString getID(const KoShape *obj)
Returns the unique id for the given shape.
bool isSavingInlineImages() const
Returns if image should be saved inline.
Private *const d
QHash< QString, int > uniqueNames
void setStrippedTextMode(bool value)
QString createFileName(const QString &extension)
Create a filename suitable for saving external data.
SvgSavingContext(QIODevice &outputDevice, bool saveInlineImages=true)
Creates a new svg saving context on the specified output device.
Private(QIODevice *_mainDevice, QIODevice *_styleDevice)
QScopedPointer< KoXmlWriter > styleWriter
static double toUserSpace(double value)
Definition SvgUtil.cpp:34