Krita Source Code Documentation
Loading...
Searching...
No Matches
KoXmlWriter.cpp
Go to the documentation of this file.
1/* This file is part of the KDE projectz
2 SPDX-FileCopyrightText: 2004 David Faure <faure@kde.org>
3 SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "KoXmlWriter.h"
9
10#include <StoreDebug.h>
11#include <QByteArray>
12#include <QStack>
13#include <float.h>
15
16static const int s_indentBufferLength = 100;
17static const int s_escapeBufferLen = 10000;
18
19class Q_DECL_HIDDEN KoXmlWriter::Private
20{
21public:
22 Private(QIODevice* dev_, int indentLevel = 0)
23 : dev(dev_)
24 , baseIndentLevel(indentLevel)
25 {}
26
28 delete[] indentBuffer;
29 delete[] escapeBuffer;
30 //TODO: look at if we must delete "dev". For me we must delete it otherwise we will leak it
31 }
32
33 QIODevice* dev;
36
37 char* indentBuffer; // maybe make it static, but then it needs a K_GLOBAL_STATIC
38 // and would eat 1K all the time... Maybe refcount it :)
39 char* escapeBuffer; // can't really be static if we want to be thread-safe
40};
41
42KoXmlWriter::KoXmlWriter(QIODevice* dev, int indentLevel)
43 : d(new Private(dev, indentLevel))
44{
45 d->indentBuffer = new char[ s_indentBufferLength ];
46 memset(d->indentBuffer, ' ', s_indentBufferLength);
47 *d->indentBuffer = '\n'; // write newline before indentation, in one go
48
49 d->escapeBuffer = new char[s_escapeBufferLen];
50 if (!d->dev->isOpen())
51 d->dev->open(QIODevice::WriteOnly);
52
53}
54
56{
57 delete d;
58}
59
60void KoXmlWriter::startDocument(const char* rootElemName, const char* publicId, const char* systemId)
61{
62 Q_ASSERT(d->tags.isEmpty());
63 writeCString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
64 // There isn't much point in a doctype if there's no DTD to refer to
65 // (I'm told that files that are validated by a RelaxNG schema cannot refer to the schema)
66 if (publicId) {
67 writeCString("<!DOCTYPE ");
68 writeCString(rootElemName);
69 writeCString(" PUBLIC \"");
70 writeCString(publicId);
71 writeCString("\" \"");
72 writeCString(systemId);
73 writeCString("\"");
74 writeCString(">\n");
75 }
76}
77
79{
80 // just to do exactly like QDom does (newline at end of file).
81 writeChar('\n');
82 Q_ASSERT(d->tags.isEmpty());
83}
84
85// returns the value of indentInside of the parent
86bool KoXmlWriter::prepareForChild(bool indentInside)
87{
88 if (!d->tags.isEmpty()) {
89 Tag& parent = d->tags.top();
90 if (!parent.hasChildren) {
91 closeStartElement(parent);
92 parent.hasChildren = true;
93 parent.lastChildIsText = false;
94 }
95 if (parent.indentInside && indentInside) {
97 }
98 return parent.indentInside && indentInside;
99 }
100 return indentInside;
101}
102
104{
105 if (d->tags.isEmpty())
106 return;
107 Tag& parent = d->tags.top();
108 if (!parent.hasChildren) {
109 closeStartElement(parent);
110 parent.hasChildren = true;
111 parent.lastChildIsText = true;
112 }
113}
114
115void KoXmlWriter::startElement(const char* tagName, bool indentInside)
116{
117 Q_ASSERT(tagName != 0);
118
119 // Tell parent that it has children
120 indentInside = prepareForChild(indentInside);
121
122 d->tags.push(Tag(tagName, indentInside));
123 writeChar('<');
124 writeCString(tagName);
125 //kDebug(s_area) << tagName;
126}
127
129{
131 const bool wasOpen = indev->isOpen();
132 // Always (re)open the device in readonly mode, it might be
133 // already open but for writing, and we need to rewind.
134 const bool openOk = indev->open(QIODevice::ReadOnly);
135 Q_ASSERT(openOk);
136 if (!openOk) {
137 warnStore << "Failed to re-open the device! wasOpen=" << wasOpen;
138 return;
139 }
140
141 QString indentString;
142 indentString.fill((' '), d->tags.size() + d->baseIndentLevel);
143 QByteArray indentBuf(indentString.toUtf8());
144
145 QByteArray buffer;
146 while (!indev->atEnd()) {
147 buffer = indev->readLine();
148
149 d->dev->write(indentBuf);
150 d->dev->write(buffer);
151 }
152
153 if (!wasOpen) {
154 // Restore initial state
155 indev->close();
156 }
157}
158
160{
161 if (d->tags.isEmpty())
162 warnStore << "EndElement() was called more times than startElement(). "
163 "The generated XML will be invalid! "
164 "Please report this bug (by saving the document to another format...)" << Qt::endl;
165
166 Tag tag = d->tags.pop();
167
168 if (!tag.hasChildren) {
169 writeCString("/>");
170 } else {
171 if (tag.indentInside && !tag.lastChildIsText) {
172 writeIndent();
173 }
174 writeCString("</");
175 Q_ASSERT(tag.tagName != 0);
177 writeChar('>');
178 }
179}
180
181void KoXmlWriter::addTextNode(const QByteArray& cstr)
182{
183 // Same as the const char* version below, but here we know the size
185 char* escaped = escapeForXML(cstr.constData(), cstr.size());
187 if (escaped != d->escapeBuffer)
188 delete[] escaped;
189}
190
191void KoXmlWriter::addTextNode(const char* cstr)
192{
194 char* escaped = escapeForXML(cstr, -1);
196 if (escaped != d->escapeBuffer)
197 delete[] escaped;
198}
199
200void KoXmlWriter::addAttribute(const char* attrName, const QByteArray& value)
201{
202 // Same as the const char* one, but here we know the size
203 writeChar(' ');
204 writeCString(attrName);
205 writeCString("=\"");
206 char* escaped = escapeForXML(value.constData(), value.size());
208 if (escaped != d->escapeBuffer)
209 delete[] escaped;
210 writeChar('"');
211}
212
213void KoXmlWriter::addAttribute(const char* attrName, const char* value)
214{
215 writeChar(' ');
216 writeCString(attrName);
217 writeCString("=\"");
218 char* escaped = escapeForXML(value, -1);
220 if (escaped != d->escapeBuffer)
221 delete[] escaped;
222 writeChar('"');
223}
224
225void KoXmlWriter::addAttribute(const char* attrName, double value)
226{
228}
229
230void KoXmlWriter::addAttribute(const char* attrName, float value)
231{
233}
234
236{
237 // +1 because of the leading '\n'
238 d->dev->write(d->indentBuffer, qMin(d->tags.size() + d->baseIndentLevel + 1,
240}
241
242
243// In case of a reallocation (ret value != d->buffer), the caller owns the return value,
244// it must delete it (with [])
245char* KoXmlWriter::escapeForXML(const char* source, int length = -1) const
246{
247 // we're going to be pessimistic on char length; so lets make the outputLength less
248 // the amount one char can take: 6
249 char* destBoundary = d->escapeBuffer + s_escapeBufferLen - 6;
250 char* destination = d->escapeBuffer;
251 char* output = d->escapeBuffer;
252 const char* src = source; // src moves, source remains
253 for (;;) {
254 if (destination >= destBoundary) {
255 // When we come to realize that our escaped string is going to
256 // be bigger than the escape buffer (this shouldn't happen very often...),
257 // we drop the idea of using it, and we allocate a bigger buffer.
258 // Note that this if() can only be hit once per call to the method.
259 if (length == -1)
260 length = qstrlen(source); // expensive...
261 uint newLength = length * 6 + 1; // worst case. 6 is due to &quot; and &apos;
262 char* buffer = new char[ newLength ];
263 destBoundary = buffer + newLength;
264 uint amountOfCharsAlreadyCopied = destination - d->escapeBuffer;
265 memcpy(buffer, d->escapeBuffer, amountOfCharsAlreadyCopied);
266 output = buffer;
267 destination = buffer + amountOfCharsAlreadyCopied;
268 }
269 switch (*src) {
270 case 60: // <
271 memcpy(destination, "&lt;", 4);
272 destination += 4;
273 break;
274 case 62: // >
275 memcpy(destination, "&gt;", 4);
276 destination += 4;
277 break;
278 case 34: // "
279 memcpy(destination, "&quot;", 6);
280 destination += 6;
281 break;
282#if 0 // needed?
283 case 39: // '
284 memcpy(destination, "&apos;", 6);
285 destination += 6;
286 break;
287#endif
288 case 38: // &
289 memcpy(destination, "&amp;", 5);
290 destination += 5;
291 break;
292 case 0:
293 *destination = '\0';
294 return output;
295 // Control codes accepted in XML 1.0 documents.
296 case 9:
297 case 10:
298 case 13:
299 *destination++ = *src++;
300 continue;
301 default:
302 // Don't add control codes not accepted in XML 1.0 documents.
303 if (*src > 0 && *src < 32) {
304 ++src;
305 } else {
306 *destination++ = *src++;
307 }
308 continue;
309 }
310 ++src;
311 }
312 // NOTREACHED (see case 0)
313 return output;
314}
315
316void KoXmlWriter::addManifestEntry(const QString& fullPath, const QString& mediaType)
317{
318 startElement("manifest:file-entry");
319 addAttribute("manifest:media-type", mediaType);
320 addAttribute("manifest:full-path", fullPath);
321 endElement();
322}
323
324// TODO check return value!!!
325void KoXmlWriter::writeCString(const char* cstr) {
326 d->dev->write(cstr, qstrlen(cstr));
327}
328
329// TODO check return value!!!
331 d->dev->putChar(c);
332}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
unsigned int uint
static const int s_escapeBufferLen
static const int s_indentBufferLength
#define warnStore
Definition StoreDebug.h:16
~KoXmlWriter()
Destructor.
void addCompleteElement(QIODevice *dev)
void addManifestEntry(const QString &fullPath, const QString &mediaType)
bool prepareForChild(bool indentInside=true)
Private *const d
void startElement(const char *tagName, bool indentInside=true)
char * escapeForXML(const char *source, int length) const
char * escapeBuffer
KoXmlWriter(QIODevice *dev, int indentLevel=0)
void endDocument()
Call this to terminate an XML document.
char * indentBuffer
void writeCString(const char *cstr)
Private(QIODevice *dev_, int indentLevel=0)
QIODevice * dev
void startDocument(const char *rootElemName, const char *publicId=0, const char *systemId=0)
QStack< Tag > tags
void prepareForTextNode()
void addTextNode(const QString &str)
void closeStartElement(Tag &tag)
void endElement()
void writeIndent()
Write out followed by the number of spaces required.
void addAttribute(const char *attrName, const QString &value)
Definition KoXmlWriter.h:61
void writeChar(char c)
const QMap< char, char > escaped
QString toString(const QString &value)
bool indentInside
whether to indent the contents of this tag
bool lastChildIsText
last child is a text node
bool hasChildren
element or text children