1 | /* |
2 | * Copyright © 2012,2013 Google, Inc. |
3 | * |
4 | * This is part of HarfBuzz, a text shaping library. |
5 | * |
6 | * Permission is hereby granted, without written agreement and without |
7 | * license or royalty fees, to use, copy, modify, and distribute this |
8 | * software and its documentation for any purpose, provided that the |
9 | * above copyright notice and the following two paragraphs appear in |
10 | * all copies of this software. |
11 | * |
12 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | * DAMAGE. |
17 | * |
18 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | * |
24 | * Google Author(s): Behdad Esfahbod |
25 | */ |
26 | |
27 | #include "hb.hh" |
28 | |
29 | #ifndef HB_NO_BUFFER_SERIALIZE |
30 | |
31 | #include "hb-buffer.hh" |
32 | |
33 | |
34 | static const char *serialize_formats[] = { |
35 | "text" , |
36 | "json" , |
37 | nullptr |
38 | }; |
39 | |
40 | /** |
41 | * hb_buffer_serialize_list_formats: |
42 | * |
43 | * Returns a list of supported buffer serialization formats. |
44 | * |
45 | * Return value: (transfer none): |
46 | * A string array of buffer serialization formats. Should not be freed. |
47 | * |
48 | * Since: 0.9.7 |
49 | **/ |
50 | const char ** |
51 | hb_buffer_serialize_list_formats () |
52 | { |
53 | return serialize_formats; |
54 | } |
55 | |
56 | /** |
57 | * hb_buffer_serialize_format_from_string: |
58 | * @str: (array length=len) (element-type uint8_t): a string to parse |
59 | * @len: length of @str, or -1 if string is %NULL terminated |
60 | * |
61 | * Parses a string into an #hb_buffer_serialize_format_t. Does not check if |
62 | * @str is a valid buffer serialization format, use |
63 | * hb_buffer_serialize_list_formats() to get the list of supported formats. |
64 | * |
65 | * Return value: |
66 | * The parsed #hb_buffer_serialize_format_t. |
67 | * |
68 | * Since: 0.9.7 |
69 | **/ |
70 | hb_buffer_serialize_format_t |
71 | hb_buffer_serialize_format_from_string (const char *str, int len) |
72 | { |
73 | /* Upper-case it. */ |
74 | return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020u); |
75 | } |
76 | |
77 | /** |
78 | * hb_buffer_serialize_format_to_string: |
79 | * @format: an #hb_buffer_serialize_format_t to convert. |
80 | * |
81 | * Converts @format to the string corresponding it, or %NULL if it is not a valid |
82 | * #hb_buffer_serialize_format_t. |
83 | * |
84 | * Return value: (transfer none): |
85 | * A %NULL terminated string corresponding to @format. Should not be freed. |
86 | * |
87 | * Since: 0.9.7 |
88 | **/ |
89 | const char * |
90 | hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) |
91 | { |
92 | switch ((unsigned) format) |
93 | { |
94 | case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; |
95 | case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; |
96 | default: |
97 | case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr; |
98 | } |
99 | } |
100 | |
101 | static unsigned int |
102 | _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, |
103 | unsigned int start, |
104 | unsigned int end, |
105 | char *buf, |
106 | unsigned int buf_size, |
107 | unsigned int *buf_consumed, |
108 | hb_font_t *font, |
109 | hb_buffer_serialize_flags_t flags) |
110 | { |
111 | hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); |
112 | hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? |
113 | nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); |
114 | |
115 | *buf_consumed = 0; |
116 | hb_position_t x = 0, y = 0; |
117 | for (unsigned int i = start; i < end; i++) |
118 | { |
119 | char b[1024]; |
120 | char *p = b; |
121 | |
122 | /* In the following code, we know b is large enough that no overflow can happen. */ |
123 | |
124 | #define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END |
125 | |
126 | if (i) |
127 | *p++ = ','; |
128 | |
129 | *p++ = '{'; |
130 | |
131 | APPEND ("\"g\":" ); |
132 | if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) |
133 | { |
134 | char g[128]; |
135 | hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g)); |
136 | *p++ = '"'; |
137 | for (char *q = g; *q; q++) { |
138 | if (*q == '"') |
139 | *p++ = '\\'; |
140 | *p++ = *q; |
141 | } |
142 | *p++ = '"'; |
143 | } |
144 | else |
145 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u" , info[i].codepoint)); |
146 | |
147 | if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { |
148 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u" , info[i].cluster)); |
149 | } |
150 | |
151 | if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) |
152 | { |
153 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d" , |
154 | x+pos[i].x_offset, y+pos[i].y_offset)); |
155 | if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) |
156 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d" , |
157 | pos[i].x_advance, pos[i].y_advance)); |
158 | } |
159 | |
160 | if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) |
161 | { |
162 | if (info[i].mask & HB_GLYPH_FLAG_DEFINED) |
163 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u" , info[i].mask & HB_GLYPH_FLAG_DEFINED)); |
164 | } |
165 | |
166 | if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) |
167 | { |
168 | hb_glyph_extents_t extents; |
169 | hb_font_get_glyph_extents(font, info[i].codepoint, &extents); |
170 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d" , |
171 | extents.x_bearing, extents.y_bearing)); |
172 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d" , |
173 | extents.width, extents.height)); |
174 | } |
175 | |
176 | *p++ = '}'; |
177 | |
178 | unsigned int l = p - b; |
179 | if (buf_size > l) |
180 | { |
181 | memcpy (buf, b, l); |
182 | buf += l; |
183 | buf_size -= l; |
184 | *buf_consumed += l; |
185 | *buf = '\0'; |
186 | } else |
187 | return i - start; |
188 | |
189 | if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) |
190 | { |
191 | x += pos[i].x_advance; |
192 | y += pos[i].y_advance; |
193 | } |
194 | } |
195 | |
196 | return end - start; |
197 | } |
198 | |
199 | static unsigned int |
200 | _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, |
201 | unsigned int start, |
202 | unsigned int end, |
203 | char *buf, |
204 | unsigned int buf_size, |
205 | unsigned int *buf_consumed, |
206 | hb_font_t *font, |
207 | hb_buffer_serialize_flags_t flags) |
208 | { |
209 | hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); |
210 | hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? |
211 | nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); |
212 | |
213 | *buf_consumed = 0; |
214 | hb_position_t x = 0, y = 0; |
215 | for (unsigned int i = start; i < end; i++) |
216 | { |
217 | char b[1024]; |
218 | char *p = b; |
219 | |
220 | /* In the following code, we know b is large enough that no overflow can happen. */ |
221 | |
222 | if (i) |
223 | *p++ = '|'; |
224 | |
225 | if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) |
226 | { |
227 | hb_font_glyph_to_string (font, info[i].codepoint, p, 128); |
228 | p += strlen (p); |
229 | } |
230 | else |
231 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u" , info[i].codepoint)); |
232 | |
233 | if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { |
234 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u" , info[i].cluster)); |
235 | } |
236 | |
237 | if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) |
238 | { |
239 | if (x+pos[i].x_offset || y+pos[i].y_offset) |
240 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d" , x+pos[i].x_offset, y+pos[i].y_offset)); |
241 | |
242 | if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) |
243 | { |
244 | *p++ = '+'; |
245 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d" , pos[i].x_advance)); |
246 | if (pos[i].y_advance) |
247 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d" , pos[i].y_advance)); |
248 | } |
249 | } |
250 | |
251 | if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) |
252 | { |
253 | if (info[i].mask & HB_GLYPH_FLAG_DEFINED) |
254 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X" , info[i].mask &HB_GLYPH_FLAG_DEFINED)); |
255 | } |
256 | |
257 | if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) |
258 | { |
259 | hb_glyph_extents_t extents; |
260 | hb_font_get_glyph_extents(font, info[i].codepoint, &extents); |
261 | p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>" , extents.x_bearing, extents.y_bearing, extents.width, extents.height)); |
262 | } |
263 | |
264 | unsigned int l = p - b; |
265 | if (buf_size > l) |
266 | { |
267 | memcpy (buf, b, l); |
268 | buf += l; |
269 | buf_size -= l; |
270 | *buf_consumed += l; |
271 | *buf = '\0'; |
272 | } else |
273 | return i - start; |
274 | |
275 | if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) |
276 | { |
277 | x += pos[i].x_advance; |
278 | y += pos[i].y_advance; |
279 | } |
280 | } |
281 | |
282 | return end - start; |
283 | } |
284 | |
285 | /** |
286 | * hb_buffer_serialize_glyphs: |
287 | * @buffer: an #hb_buffer_t buffer. |
288 | * @start: the first item in @buffer to serialize. |
289 | * @end: the last item in @buffer to serialize. |
290 | * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to |
291 | * write serialized buffer into. |
292 | * @buf_size: the size of @buf. |
293 | * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf. |
294 | * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to |
295 | * read glyph names and extents. If %NULL, and empty font will be used. |
296 | * @format: the #hb_buffer_serialize_format_t to use for formatting the output. |
297 | * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties |
298 | * to serialize. |
299 | * |
300 | * Serializes @buffer into a textual representation of its glyph content, |
301 | * useful for showing the contents of the buffer, for example during debugging. |
302 | * There are currently two supported serialization formats: |
303 | * |
304 | * ## text |
305 | * A human-readable, plain text format. |
306 | * The serialized glyphs will look something like: |
307 | * |
308 | * ``` |
309 | * [uni0651=0@518,0+0|uni0628=0+1897] |
310 | * ``` |
311 | * - The serialized glyphs are delimited with `[` and `]`. |
312 | * - Glyphs are separated with `|` |
313 | * - Each glyph starts with glyph name, or glyph index if |
314 | * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. Then, |
315 | * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, `=` then #hb_glyph_info_t.cluster. |
316 | * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format: |
317 | * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then, |
318 | * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then, |
319 | * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the |
320 | * #hb_glyph_extents_t in the format |
321 | * `<x_bearing,y_bearing,width,height>` |
322 | * |
323 | * ## json |
324 | * TODO. |
325 | * |
326 | * Return value: |
327 | * The number of serialized items. |
328 | * |
329 | * Since: 0.9.7 |
330 | **/ |
331 | unsigned int |
332 | hb_buffer_serialize_glyphs (hb_buffer_t *buffer, |
333 | unsigned int start, |
334 | unsigned int end, |
335 | char *buf, |
336 | unsigned int buf_size, |
337 | unsigned int *buf_consumed, |
338 | hb_font_t *font, |
339 | hb_buffer_serialize_format_t format, |
340 | hb_buffer_serialize_flags_t flags) |
341 | { |
342 | assert (start <= end && end <= buffer->len); |
343 | |
344 | unsigned int sconsumed; |
345 | if (!buf_consumed) |
346 | buf_consumed = &sconsumed; |
347 | *buf_consumed = 0; |
348 | if (buf_size) |
349 | *buf = '\0'; |
350 | |
351 | assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || |
352 | buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); |
353 | |
354 | if (!buffer->have_positions) |
355 | flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; |
356 | |
357 | if (unlikely (start == end)) |
358 | return 0; |
359 | |
360 | if (!font) |
361 | font = hb_font_get_empty (); |
362 | |
363 | switch (format) |
364 | { |
365 | case HB_BUFFER_SERIALIZE_FORMAT_TEXT: |
366 | return _hb_buffer_serialize_glyphs_text (buffer, start, end, |
367 | buf, buf_size, buf_consumed, |
368 | font, flags); |
369 | |
370 | case HB_BUFFER_SERIALIZE_FORMAT_JSON: |
371 | return _hb_buffer_serialize_glyphs_json (buffer, start, end, |
372 | buf, buf_size, buf_consumed, |
373 | font, flags); |
374 | |
375 | default: |
376 | case HB_BUFFER_SERIALIZE_FORMAT_INVALID: |
377 | return 0; |
378 | |
379 | } |
380 | } |
381 | |
382 | static bool |
383 | parse_int (const char *pp, const char *end, int32_t *pv) |
384 | { |
385 | int v; |
386 | const char *p = pp; |
387 | if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */))) |
388 | return false; |
389 | |
390 | *pv = v; |
391 | return true; |
392 | } |
393 | |
394 | static bool |
395 | parse_uint (const char *pp, const char *end, uint32_t *pv) |
396 | { |
397 | unsigned int v; |
398 | const char *p = pp; |
399 | if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */))) |
400 | return false; |
401 | |
402 | *pv = v; |
403 | return true; |
404 | } |
405 | |
406 | #include "hb-buffer-deserialize-json.hh" |
407 | #include "hb-buffer-deserialize-text.hh" |
408 | |
409 | /** |
410 | * hb_buffer_deserialize_glyphs: |
411 | * @buffer: an #hb_buffer_t buffer. |
412 | * @buf: (array length=buf_len): |
413 | * @buf_len: |
414 | * @end_ptr: (out): |
415 | * @font: |
416 | * @format: |
417 | * |
418 | * |
419 | * |
420 | * Return value: |
421 | * |
422 | * Since: 0.9.7 |
423 | **/ |
424 | hb_bool_t |
425 | hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, |
426 | const char *buf, |
427 | int buf_len, /* -1 means nul-terminated */ |
428 | const char **end_ptr, /* May be NULL */ |
429 | hb_font_t *font, /* May be NULL */ |
430 | hb_buffer_serialize_format_t format) |
431 | { |
432 | const char *end; |
433 | if (!end_ptr) |
434 | end_ptr = &end; |
435 | *end_ptr = buf; |
436 | |
437 | assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || |
438 | buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); |
439 | |
440 | if (buf_len == -1) |
441 | buf_len = strlen (buf); |
442 | |
443 | if (!buf_len) |
444 | { |
445 | *end_ptr = buf; |
446 | return false; |
447 | } |
448 | |
449 | hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); |
450 | |
451 | if (!font) |
452 | font = hb_font_get_empty (); |
453 | |
454 | switch (format) |
455 | { |
456 | case HB_BUFFER_SERIALIZE_FORMAT_TEXT: |
457 | return _hb_buffer_deserialize_glyphs_text (buffer, |
458 | buf, buf_len, end_ptr, |
459 | font); |
460 | |
461 | case HB_BUFFER_SERIALIZE_FORMAT_JSON: |
462 | return _hb_buffer_deserialize_glyphs_json (buffer, |
463 | buf, buf_len, end_ptr, |
464 | font); |
465 | |
466 | default: |
467 | case HB_BUFFER_SERIALIZE_FORMAT_INVALID: |
468 | return false; |
469 | |
470 | } |
471 | } |
472 | |
473 | |
474 | #endif |
475 | |