blob: d6ffdb9475544cc145ce22c29a69379184fd18a6 [file] [log] [blame]
José Fonseca17a45412012-11-28 09:44:01 +00001/**************************************************************************
2 *
3 * Copyright 2011 Jose Fonseca
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 **************************************************************************/
25
26/*
27 * JSON writing functions.
28 */
29
30
31#include <assert.h>
32#include <string.h>
33
José Fonseca6083cfc2012-11-28 09:58:09 +000034#include <sstream>
35
36#include "image.hpp"
José Fonseca17a45412012-11-28 09:44:01 +000037#include "json.hpp"
38
39
40void
41JSONWriter::newline(void) {
42 os << "\n";
43 for (int i = 0; i < level; ++i)
44 os << " ";
45}
46
47void
48JSONWriter::separator(void) {
49 if (value) {
50 os << ",";
51 switch (space) {
52 case '\0':
53 break;
54 case '\n':
55 newline();
56 break;
57 default:
58 os << space;
59 break;
60 }
61 } else {
62 if (space == '\n') {
63 newline();
64 }
65 }
66}
67
68static void
69escapeAsciiString(std::ostream &os, const char *str) {
70 os << "\"";
71
72 const unsigned char *src = (const unsigned char *)str;
73 unsigned char c;
74 while ((c = *src++)) {
75 if ((c == '\"') ||
76 (c == '\\')) {
77 // escape character
78 os << '\\' << (unsigned char)c;
79 } else if ((c >= 0x20 && c <= 0x7e) ||
80 c == '\t' ||
81 c == '\r' ||
82 c == '\n') {
83 // pass-through character
84 os << (unsigned char)c;
85 } else {
86 assert(0);
87 os << "?";
88 }
89 }
90
91 os << "\"";
92}
93
94static void
95escapeUnicodeString(std::ostream &os, const char *str) {
96 os << "\"";
97
98 const char *locale = setlocale(LC_CTYPE, "");
99 const char *src = str;
100 mbstate_t state;
101
102 memset(&state, 0, sizeof state);
103
104 do {
105 // Convert characters one at a time in order to recover from
106 // conversion errors
107 wchar_t c;
108 size_t written = mbsrtowcs(&c, &src, 1, &state);
109 if (written == 0) {
110 // completed
111 break;
112 } if (written == (size_t)-1) {
113 // conversion error -- skip
114 os << "?";
115 do {
116 ++src;
117 } while (*src & 0x80);
118 } else if ((c == '\"') ||
119 (c == '\\')) {
120 // escape character
121 os << '\\' << (unsigned char)c;
122 } else if ((c >= 0x20 && c <= 0x7e) ||
123 c == '\t' ||
124 c == '\r' ||
125 c == '\n') {
126 // pass-through character
127 os << (unsigned char)c;
128 } else {
129 // unicode
José Fonsecadbfe0c42015-03-26 12:23:41 +0000130 os << "\\u" << std::hex << std::setfill('0') << std::setw(4) << (unsigned)c << std::setfill(' ') << std::dec;
José Fonseca17a45412012-11-28 09:44:01 +0000131 }
132 } while (src);
133
134 setlocale(LC_CTYPE, locale);
135
136 os << "\"";
137}
138
139static void
140encodeBase64String(std::ostream &os, const unsigned char *bytes, size_t size) {
141 const char *table64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
142 unsigned char c0, c1, c2, c3;
143 char buf[4];
144 unsigned written;
145
146 os << "\"";
147
148 written = 0;
149 while (size >= 3) {
150 c0 = bytes[0] >> 2;
151 c1 = ((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xf0) >> 4);
152 c2 = ((bytes[1] & 0x0f) << 2) | ((bytes[2] & 0xc0) >> 6);
153 c3 = bytes[2] & 0x3f;
154
155 buf[0] = table64[c0];
156 buf[1] = table64[c1];
157 buf[2] = table64[c2];
158 buf[3] = table64[c3];
159
160 os.write(buf, 4);
161
162 bytes += 3;
163 size -= 3;
164 ++written;
165
166 if (written >= 76/4 && size) {
167 os << "\n";
168 written = 0;
169 }
170 }
171
172 if (size > 0) {
173 c0 = bytes[0] >> 2;
174 c1 = ((bytes[0] & 0x03) << 4);
175 buf[2] = '=';
176 buf[3] = '=';
177
178 if (size > 1) {
179 c1 |= ((bytes[1] & 0xf0) >> 4);
180 c2 = ((bytes[1] & 0x0f) << 2);
181 if (size > 2) {
182 c2 |= ((bytes[2] & 0xc0) >> 6);
183 c3 = bytes[2] & 0x3f;
184 buf[3] = table64[c3];
185 }
186 buf[2] = table64[c2];
187 }
188 buf[1] = table64[c1];
189 buf[0] = table64[c0];
190
191 os.write(buf, 4);
192 }
193
194 os << "\"";
195}
196
197JSONWriter::JSONWriter(std::ostream &_os) :
198 os(_os),
199 level(0),
200 value(false),
201 space(0)
202{
203 beginObject();
204}
205
206JSONWriter::~JSONWriter() {
207 endObject();
208 newline();
209}
210
211void
212JSONWriter::beginObject() {
213 separator();
214 os << "{";
215 ++level;
216 value = false;
217}
218
219void
220JSONWriter::endObject() {
221 --level;
222 if (value)
223 newline();
224 os << "}";
225 value = true;
226 space = '\n';
227}
228
229void
230JSONWriter::beginMember(const char * name) {
231 space = 0;
232 separator();
233 newline();
234 escapeAsciiString(os, name);
235 os << ": ";
236 value = false;
237}
238
239void
240JSONWriter::endMember(void) {
241 assert(value);
242 value = true;
243 space = 0;
244}
245
246void
247JSONWriter::beginArray() {
248 separator();
249 os << "[";
250 ++level;
251 value = false;
252 space = 0;
253}
254
255void
256JSONWriter::endArray(void) {
257 --level;
258 if (space == '\n') {
259 newline();
260 }
261 os << "]";
262 value = true;
263 space = '\n';
264}
265
266void
267JSONWriter::writeString(const char *s) {
268 if (!s) {
269 writeNull();
270 return;
271 }
272
273 separator();
274 escapeUnicodeString(os, s);
275 value = true;
276 space = ' ';
277}
278
279void
280JSONWriter::writeBase64(const void *bytes, size_t size) {
281 separator();
282 encodeBase64String(os, (const unsigned char *)bytes, size);
283 value = true;
284 space = ' ';
285}
286
287void
288JSONWriter::writeNull(void) {
289 separator();
290 os << "null";
291 value = true;
292 space = ' ';
293}
294
295void
296JSONWriter::writeBool(bool b) {
297 separator();
298 os << (b ? "true" : "false");
299 value = true;
300 space = ' ';
301}
José Fonseca6083cfc2012-11-28 09:58:09 +0000302
303void
José Fonseca42551442014-12-29 18:10:08 +0000304JSONWriter::writeImage(image::Image *image,
305 const ImageDesc & desc)
José Fonseca6083cfc2012-11-28 09:58:09 +0000306{
José Fonseca1085a4b2014-08-26 22:32:18 +0100307 assert(image);
José Fonseca65ba4972012-11-28 12:56:01 +0000308 if (!image) {
309 writeNull();
310 return;
311 }
312
José Fonseca6083cfc2012-11-28 09:58:09 +0000313 beginObject();
314
315 // Tell the GUI this is no ordinary object, but an image
316 writeStringMember("__class__", "image");
317
318 writeIntMember("__width__", image->width);
José Fonseca42551442014-12-29 18:10:08 +0000319 writeIntMember("__height__", image->height / desc.depth);
320 writeIntMember("__depth__", desc.depth);
José Fonseca6083cfc2012-11-28 09:58:09 +0000321
José Fonseca42551442014-12-29 18:10:08 +0000322 writeStringMember("__format__", desc.format.c_str());
José Fonseca6083cfc2012-11-28 09:58:09 +0000323
José Fonseca42ec5632015-02-06 22:43:05 +0000324 if (!image->label.empty()) {
325 writeStringMember("__label__", image->label.c_str());
326 }
327
José Fonseca6083cfc2012-11-28 09:58:09 +0000328 beginMember("__data__");
329 std::stringstream ss;
José Fonsecad67cc372013-09-12 17:27:04 +0100330
331 if (image->channelType == image::TYPE_UNORM8) {
332 image->writePNG(ss);
333 } else {
334 image->writePNM(ss);
335 }
336
José Fonseca6083cfc2012-11-28 09:58:09 +0000337 const std::string & s = ss.str();
338 writeBase64(s.data(), s.size());
339 endMember(); // __data__
340
341 endObject();
342}