Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgWriter.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2002 Lars Siebold <khandha5@gmx.net>
3 SPDX-FileCopyrightText: 2002-2003, 2005 Rob Buis <buis@kde.org>
4 SPDX-FileCopyrightText: 2002, 2005-2006 David Faure <faure@kde.org>
5 SPDX-FileCopyrightText: 2002 Werner Trobin <trobin@kde.org>
6 SPDX-FileCopyrightText: 2002 Lennart Kudling <kudling@kde.org>
7 SPDX-FileCopyrightText: 2004 Nicolas Goutte <nicolasg@snafu.de>
8 SPDX-FileCopyrightText: 2005 Boudewijn Rempt <boud@valdyas.org>
9 SPDX-FileCopyrightText: 2005 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
10 SPDX-FileCopyrightText: 2005 Thomas Zander <zander@kde.org>
11 SPDX-FileCopyrightText: 2005, 2007-2008 Jan Hambrecht <jaham@gmx.net>
12 SPDX-FileCopyrightText: 2006 Inge Wallin <inge@lysator.liu.se>
13 SPDX-FileCopyrightText: 2006 Martin Pfeiffer <hubipete@gmx.net>
14 SPDX-FileCopyrightText: 2006 Gábor Lehel <illissius@gmail.com>
15 SPDX-FileCopyrightText: 2006 Laurent Montel <montel@kde.org>
16 SPDX-FileCopyrightText: 2006 Christian Mueller <cmueller@gmx.de>
17 SPDX-FileCopyrightText: 2006 Ariya Hidayat <ariya@kde.org>
18 SPDX-FileCopyrightText: 2010 Thorsten Zachmann <zachmann@kde.org>
19
20 SPDX-License-Identifier: LGPL-2.0-or-later
21*/
22
23#include "SvgWriter.h"
24
25#include "SvgUtil.h"
26#include "SvgSavingContext.h"
27#include "SvgShape.h"
28#include "SvgStyleWriter.h"
29
30#include <KoShapeLayer.h>
31#include <KoShapeGroup.h>
32#include <KoPathShape.h>
33#include <KoXmlWriter.h>
34#include <KoShapePainter.h>
35#include <KoXmlNS.h>
36
37#include <QFile>
38#include <QString>
39#include <QTextStream>
40#include <QBuffer>
41#include <QPainter>
42#include <QSvgGenerator>
43
44#include <kis_debug.h>
45#include <KisPortingUtils.h>
46
48 : m_writeInlineImages(true)
49{
50 Q_FOREACH (KoShapeLayer *layer, layers)
51 m_toplevelShapes.append(layer);
52}
53
55 : m_toplevelShapes(toplevelShapes)
56 , m_writeInlineImages(true)
57{
58}
59
64
65bool SvgWriter::save(const QString &filename, const QSizeF &pageSize, bool writeInlineImages)
66{
67 QFile fileOut(filename);
68 if (!fileOut.open(QIODevice::WriteOnly))
69 return false;
70
71 m_writeInlineImages = writeInlineImages;
72
73 const bool success = save(fileOut, pageSize);
74
76
77 fileOut.close();
78
79 return success;
80}
81
82bool SvgWriter::save(QIODevice &outputDevice, const QSizeF &pageSize)
83{
84 if (m_toplevelShapes.isEmpty()) {
85 return false;
86 }
87
88 QTextStream svgStream(&outputDevice);
90
91 // standard header:
92 svgStream << "<?xml version=\"1.0\" standalone=\"no\"?>" << Qt::endl;
93 svgStream << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" ";
94 svgStream << "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">" << Qt::endl;
95
96 // add some PR. one line is more than enough.
97 svgStream << "<!-- Created using Krita: https://krita.org -->" << Qt::endl;
98
99 svgStream << "<svg xmlns=\"http://www.w3.org/2000/svg\" \n";
100 svgStream << " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n";
101 svgStream << QString(" xmlns:krita=\"%1\"\n").arg(KoXmlNS::krita);
102 svgStream << " xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n";
103 svgStream << " width=\"" << pageSize.width() << "pt\"\n";
104 svgStream << " height=\"" << pageSize.height() << "pt\"\n";
105 svgStream << " viewBox=\"0 0 "
106 << pageSize.width() << " " << pageSize.height()
107 << "\"";
108 svgStream << ">" << Qt::endl;
109
110 if (!m_documentTitle.isNull() && !m_documentTitle.isEmpty()) {
111 svgStream << "<title>" << m_documentTitle << "</title>" << Qt::endl;
112 }
113
114 if (!m_documentDescription.isNull() && !m_documentDescription.isEmpty()) {
115 svgStream << "<desc>" << m_documentDescription << "</desc>" << Qt::endl;
116 }
117
118 {
119 SvgSavingContext savingContext(outputDevice, m_writeInlineImages);
120 saveShapes(m_toplevelShapes, savingContext);
121 }
122
123 // end tag:
124 svgStream << Qt::endl << "</svg>" << Qt::endl;
125
126 return true;
127}
128
129bool SvgWriter::saveDetached(QIODevice &outputDevice)
130{
131 if (m_toplevelShapes.isEmpty())
132 return false;
133
134 SvgSavingContext savingContext(outputDevice, m_writeInlineImages);
135 saveShapes(m_toplevelShapes, savingContext);
136
137 return true;
138}
139
141{
142 if (m_toplevelShapes.isEmpty())
143 return false;
144
145 saveShapes(m_toplevelShapes, savingContext);
146
147 return true;
148}
149
151{
152 // top level shapes
153 Q_FOREACH (KoShape *shape, shapes) {
154 KoShapeLayer *layer = dynamic_cast<KoShapeLayer*>(shape);
155 if(layer) {
156 saveLayer(layer, savingContext);
157 } else {
158 KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
159 if (group)
160 saveGroup(group, savingContext);
161 else
162 saveShape(shape, savingContext);
163 }
164 }
165}
166
168{
169 context.shapeWriter().startElement("g");
170 context.shapeWriter().addAttribute("id", context.getID(layer));
171
172 QList<KoShape*> sortedShapes = layer->shapes();
173 std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
174
175 Q_FOREACH (KoShape * shape, sortedShapes) {
176 KoShapeGroup * group = dynamic_cast<KoShapeGroup*>(shape);
177 if (group)
178 saveGroup(group, context);
179 else
180 saveShape(shape, context);
181 }
182
183 context.shapeWriter().endElement();
184}
185
187{
188 context.shapeWriter().startElement("g");
189 context.shapeWriter().addAttribute("id", context.getID(group));
190
191 SvgUtil::writeTransformAttributeLazy("transform", group->transformation(), context.shapeWriter());
192
193
194 SvgStyleWriter::saveMetadata(group, context);
195
196 SvgStyleWriter::saveSvgStyle(group, context);
197
198 QList<KoShape*> sortedShapes = group->shapes();
199 std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
200
201 Q_FOREACH (KoShape * shape, sortedShapes) {
202 KoShapeGroup * childGroup = dynamic_cast<KoShapeGroup*>(shape);
203 if (childGroup)
204 saveGroup(childGroup, context);
205 else
206 saveShape(shape, context);
207 }
208
209 context.shapeWriter().endElement();
210}
211
213{
214 SvgShape *svgShape = dynamic_cast<SvgShape*>(shape);
215 if (svgShape && svgShape->saveSvg(context))
216 return;
217
218 KoPathShape * path = dynamic_cast<KoPathShape*>(shape);
219 if (path) {
220 savePath(path, context);
221 } else {
222 // generic saving of shape via a switch element
223 saveGeneric(shape, context);
224 }
225}
226
228{
229 context.shapeWriter().startElement("path");
230 context.shapeWriter().addAttribute("id", context.getID(path));
231
232 SvgUtil::writeTransformAttributeLazy("transform", path->transformation(), context.shapeWriter());
233 SvgStyleWriter::saveMetadata(path, context);
234
235 SvgStyleWriter::saveSvgStyle(path, context);
236
237 context.shapeWriter().addAttribute("d", path->toString(context.userSpaceTransform()));
238 context.shapeWriter().addAttribute("sodipodi:nodetypes", path->nodeTypes());
239 context.shapeWriter().endElement();
240}
241
243{
245
246 const QRectF bbox = shape->boundingRect();
247
248 // paint shape to the image
249 KoShapePainter painter;
250 painter.setShapes(QList<KoShape*>()<< shape);
251
252 // generate svg from shape
253 QBuffer svgBuffer;
254 QSvgGenerator svgGenerator;
255 svgGenerator.setOutputDevice(&svgBuffer);
256
266 if (shape->shapeId() == "TextShapeID") {
267 svgGenerator.setResolution(54);
268 }
269
270 QPainter svgPainter;
271 svgPainter.begin(&svgGenerator);
272 painter.paint(svgPainter, SvgUtil::toUserSpace(bbox).toRect(), bbox);
273 svgPainter.end();
274
275 // remove anything before the start of the svg element from the buffer
276 int startOfContent = svgBuffer.buffer().indexOf("<svg");
277 if(startOfContent>0) {
278 svgBuffer.buffer().remove(0, startOfContent);
279 }
280
281 // check if painting to svg produced any output
282 if (svgBuffer.buffer().isEmpty()) {
283 // prepare a transparent image, make it twice as big as the original size
284 QImage image(2*bbox.size().toSize(), QImage::Format_ARGB32);
285 image.fill(0);
286 painter.paint(image);
287
288 context.shapeWriter().startElement("image");
289 context.shapeWriter().addAttribute("id", context.getID(shape));
290 context.shapeWriter().addAttribute("x", bbox.x());
291 context.shapeWriter().addAttribute("y", bbox.y());
292 context.shapeWriter().addAttribute("width", bbox.width());
293 context.shapeWriter().addAttribute("height", bbox.height());
294 context.shapeWriter().addAttribute("xlink:href", context.saveImage(image));
295 context.shapeWriter().endElement(); // image
296
297 } else {
298 context.shapeWriter().addCompleteElement(&svgBuffer);
299 }
300
301 // TODO: once we support saving single (flat) odf files
302 // we can embed these here to have full support for generic shapes
303}
304
306{
307 m_documentTitle = title;
308}
309
310void SvgWriter::setDocumentDescription(QString description)
311{
312 m_documentDescription = description;
313}
314
The position of a path point within a path shape.
Definition KoPathShape.h:63
QList< KoShape * > shapes() const
void setShapes(const QList< KoShape * > &shapes)
void paint(QPainter &painter)
QString shapeId() const
Definition KoShape.cpp:1057
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:434
virtual QRectF boundingRect() const
Get the bounding box of the shape.
Definition KoShape.cpp:335
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:424
static const QString krita
Definition KoXmlNS.h:48
Context for saving svg files.
QTransform userSpaceTransform() const
Returns the transformation used to transform into user space.
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.
An interface providing svg loading and saving routines.
Definition SvgShape.h:18
virtual bool saveSvg(SvgSavingContext &context)
Saves data utilizing specified svg saving context.
Definition SvgShape.cpp:14
static void saveSvgStyle(KoShape *shape, SvgSavingContext &context)
Saves the style of the specified shape.
static void saveMetadata(const KoShape *shape, SvgSavingContext &context)
static double toUserSpace(double value)
Definition SvgUtil.cpp:34
static void writeTransformAttributeLazy(const QString &name, const QTransform &transform, KoXmlWriter &shapeWriter)
Writes a transform as an attribute name iff the transform is not empty.
Definition SvgUtil.cpp:124
void saveGroup(KoShapeGroup *group, SvgSavingContext &context)
SvgWriter(const QList< KoShapeLayer * > &layers)
Creates svg writer to export specified layers.
Definition SvgWriter.cpp:47
void saveGeneric(KoShape *shape, SvgSavingContext &context)
void saveShapes(const QList< KoShape * > shapes, SvgSavingContext &savingContext)
bool m_writeInlineImages
Definition SvgWriter.h:67
QString m_documentDescription
Definition SvgWriter.h:69
void saveShape(KoShape *shape, SvgSavingContext &context)
void savePath(KoPathShape *path, SvgSavingContext &context)
bool saveDetached(QIODevice &outputDevice)
void setDocumentDescription(QString description)
QString m_documentTitle
Definition SvgWriter.h:68
QList< KoShape * > m_toplevelShapes
Definition SvgWriter.h:66
bool save(QIODevice &outputDevice, const QSizeF &pageSize)
Writes svg to specified output device.
Definition SvgWriter.cpp:82
void saveLayer(KoShapeLayer *layer, SvgSavingContext &context)
virtual ~SvgWriter()
Destroys the svg writer.
Definition SvgWriter.cpp:60
void setDocumentTitle(QString title)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
void setUtf8OnStream(QTextStream &stream)