Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_cos_writer.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2023 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "kis_cos_writer.h"
8
9#include <QTextCodec>
10#include <QVariantHash>
11#include <QVariant>
12#include <QVariantList>
13#include <QBuffer>
14
15const QMap<char, char> escape = {
16 // Turns out, Adobe text engine data only really escapes ( and ).
17 //{0x0a, 'n'},
18 //{0x0d, 'r'},
19 //{0x09, 't'},
20 //{0x08, 'b'}, // backspace
21 //{0x0c, 'f'},
22 {0x28, '('},
23 {0x29, ')'},
24 {0x5c, '\\'} // reverse solidus/backslash
25};
26
27// Because we can't differentiate between names and regular strings,
28// we'll just list the keys of the names whose value is also a name.
30 "/StreamTag",
31 "/ListStyle",
32 "/MojiKumiTable",
33 "/Kinsoku"
34};
35
36void writeString(QIODevice &dev, const QVariant val, const QString name) {
37 QString newString = val.toString();
38 if (nameKeys.contains(name)) {
39 dev.write((name+" "+newString).toLatin1());
40 } else {
41 newString.replace(QChar(0x0a), QChar(0x0d));
42 QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE");
43 dev.write((name+" (").toLatin1());
44 QByteArray unicode = Utf16Codec->fromUnicode(newString);
45 QByteArray escaped = QByteArray();
46
47 char *c = unicode.begin();
48 while(c < unicode.end()) {
49 if (escape.keys().contains(*c)) {
50 escaped.append('\\');
51 escaped.append(escape.value(*c));
52 } else {
53 escaped.append(*c);
54 }
55 c++;
56 }
57 dev.write(escaped);
58 dev.write(")");
59 }
60}
66void writeVariant(QIODevice &dev, const QVariantHash object, int indent, bool prettyPrint, bool writeBrackets = true) {
67 QByteArray indentStringOld(indent, QChar::Tabulation);
68 QString newLine = prettyPrint? "\n": " ";
69 dev.write(indentStringOld);
70 if (writeBrackets) {
71 dev.write(("<<"+newLine).toLatin1());
72 }
73 indent = prettyPrint? indent + 1: 0;
74 QByteArray indentString(indent, QChar::Tabulation);
75 Q_FOREACH(QString key, object.keys()) {
76 QVariant val = object[key];
77 QString name = QString(key);
78 if (val.type() == QVariant::Hash) {
79 dev.write(indentString);
80 dev.write((name+newLine).toLatin1());
81 writeVariant(dev, val.toHash(), indent, prettyPrint);
82 } else if (val.type() == QVariant::List) {
83 QVariantList array = val.toList();
84 if(array.isEmpty()) {
85 dev.write(indentString);
86 dev.write((name+" [ ]"+newLine).toLatin1());
87 } else if (array.at(0).type() == QVariant::Int) {
88 dev.write(indentString);
89 dev.write((name+" [").toLatin1());
90 for (int i=0; i<array.size(); i++) {
91 dev.write((" "+QString::number(array.at(i).toInt())).toLatin1());
92 }
93 dev.write((" ]"+newLine).toLatin1());
94 } else if (array.at(0).type() == QVariant::Double) {
95 dev.write(indentString);
96 dev.write((name+" [").toLatin1());
97 for (int i=0; i<array.size(); i++) {
98 dev.write((" "+QString::number(array.at(i).toDouble(), 'f', 5)).toLatin1());
99 }
100 dev.write((" ]"+newLine).toLatin1());
101 } else {
102 dev.write(indentString);
103 dev.write((name+" ["+newLine).toLatin1());
104 for (int i=0; i<val.toList().size(); i++) {
105 QVariant arrVal = val.toList().at(i);
106 if (arrVal.type() == QVariant::Hash) {
107 writeVariant(dev, arrVal.toHash(), indent, prettyPrint);
108 }
109 }
110 dev.write(indentString);
111 dev.write(("]"+newLine).toLatin1());
112 }
113 } else if (val.type() == QVariant::String) {
114
115 dev.write(indentString);
116 writeString(dev, val, name);
117 dev.write((newLine).toLatin1());
118 } else if (val.type() == QVariant::Bool) {
119 QString boolVal = val.toBool()? "true": "false";
120 dev.write(indentString);
121 dev.write((name+" "+boolVal+newLine).toLatin1());
122 } else {
123 if (val.type() == QVariant::Double) {
124 dev.write(indentString);
125 dev.write((name+" "+QString::number(val.toDouble(), 'f', 5)+newLine).toLatin1());
126 } else if (val.type() == QVariant::Int) {
127 dev.write(indentString);
128 dev.write((name+" "+QString::number(val.toInt())+newLine).toLatin1());
129 }
130 }
131 }
132 dev.write(indentStringOld);
133 if (writeBrackets) {
134 dev.write((">>"+newLine).toLatin1());
135 }
136}
137
138QByteArray KisCosWriter::writeCosFromVariantHash(const QVariantHash doc)
139{
140 QByteArray ba;
141 QBuffer dev(&ba);
142 if (dev.open(QIODevice::WriteOnly)) {
143 int indent = 0;
144 dev.write("\n\n");
145 bool prettyPrint = true;
146 writeVariant(dev, doc, indent, prettyPrint);
147 dev.close();
148 } else {
149 qWarning() << dev.errorString();
150 }
151 return ba;
152}
153
154QByteArray KisCosWriter::writeTxt2FromVariantHash(const QVariantHash doc)
155{
156 QByteArray ba;
157 QBuffer dev(&ba);
158 if (dev.open(QIODevice::WriteOnly)) {
159 dev.write(" ");
160 writeVariant(dev, doc, 0, false, false);
161 dev.close();
162 } else {
163 qWarning() << dev.errorString();
164 }
165 if (ba.endsWith(' ')) {
166 ba.chop(1);
167 }
168 return ba;
169}
static QByteArray writeCosFromVariantHash(const QVariantHash doc)
static QByteArray writeTxt2FromVariantHash(const QVariantHash doc)
const QMap< char, char > escaped
void writeVariant(QIODevice &dev, const QVariantHash object, int indent, bool prettyPrint, bool writeBrackets=true)
writeVariant
const QMap< char, char > escape
void writeString(QIODevice &dev, const QVariant val, const QString name)
static QStringList nameKeys