1/* Pango
2 * serializer.c: Code to serialize various Pango objects
3 *
4 * Copyright (C) 2021 Red Hat, Inc
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22#include "config.h"
23
24#include <pango/pango-layout.h>
25#include <pango/pango-layout-private.h>
26#include <pango/pango-context-private.h>
27#include <pango/pango-enum-types.h>
28#include <pango/pango-font-private.h>
29
30#include <hb-ot.h>
31#include "pango/json/gtkjsonparserprivate.h"
32#include "pango/json/gtkjsonprinterprivate.h"
33
34/* {{{ Error handling */
35
36G_DEFINE_QUARK(pango-layout-deserialize-error-quark, pango_layout_deserialize_error)
37
38/* }}} */
39/* {{{ Enum names */
40
41static const char *style_names[] = {
42 "normal",
43 "oblique",
44 "italic",
45 NULL
46};
47
48static const char *variant_names[] = {
49 "normal",
50 "small-caps",
51 "all-small-caps",
52 "petite-caps",
53 "all-petite-caps",
54 "unicase",
55 "titlecase",
56 NULL
57};
58
59static const char *stretch_names[] = {
60 "ultra-condensed",
61 "extra-condensed",
62 "condensed",
63 "semi-condensed",
64 "normal",
65 "semi-expanded",
66 "expanded",
67 "extra-expanded",
68 "ultra-expanded",
69 NULL
70};
71
72static const char *underline_names[] = {
73 "none",
74 "single",
75 "double",
76 "low",
77 "error",
78 "single-line",
79 "double-line",
80 "error-line",
81 NULL
82};
83
84static const char *overline_names[] = {
85 "none",
86 "single",
87 NULL
88};
89
90static const char *gravity_names[] = {
91 "south",
92 "east",
93 "north",
94 "west",
95 "auto",
96 NULL
97};
98
99static const char *gravity_hint_names[] = {
100 "natural",
101 "strong",
102 "line",
103 NULL
104};
105
106static const char *text_transform_names[] = {
107 "none",
108 "lowercase",
109 "uppercase",
110 "capitalize",
111 NULL
112};
113
114static const char *baseline_shift_names[] = {
115 "none",
116 "superscript",
117 "subscript",
118 NULL
119};
120
121static const char *font_scale_names[] = {
122 "none",
123 "superscript",
124 "subscript",
125 "small-caps",
126 NULL
127};
128
129static const char *weight_names[] = {
130 "thin",
131 "ultralight",
132 "light",
133 "semilight",
134 "book",
135 "normal",
136 "medium",
137 "semibold",
138 "bold",
139 "ultrabold",
140 "heavy",
141 "ultraheavy",
142 NULL
143};
144
145static int named_weights[] = { 100, 200, 300, 350, 380, 400, 500, 600, 700, 800, 900, 1000 };
146
147static int
148get_weight (int pos)
149{
150 return named_weights[pos];
151}
152
153static const char *
154get_weight_name (int weight)
155{
156 for (int i = 0; i < G_N_ELEMENTS (named_weights); i++)
157 {
158 if (named_weights[i] == weight)
159 return weight_names[i];
160 }
161
162 return NULL;
163}
164
165static const char *attr_type_names[] = {
166 "invalid",
167 "language",
168 "family",
169 "style",
170 "weight",
171 "variant",
172 "stretch",
173 "size",
174 "font-desc",
175 "foreground",
176 "background",
177 "underline",
178 "strikethrough",
179 "rise",
180 "shape",
181 "scale",
182 "fallback",
183 "letter-spacing",
184 "underline-color",
185 "strikethrough-color",
186 "absolute-size",
187 "gravity",
188 "gravity-hint",
189 "font-features",
190 "foreground-alpha",
191 "background-alpha",
192 "allow-breaks",
193 "show",
194 "insert-hyphens",
195 "overline",
196 "overline-color",
197 "line-height",
198 "absolute-line-height",
199 "text-transform",
200 "word",
201 "sentence",
202 "baseline-shift",
203 "font-scale",
204 NULL
205};
206
207static const char *
208get_script_name (PangoScript script)
209{
210 GEnumClass *enum_class;
211 GEnumValue *enum_value;
212
213 enum_class = g_type_class_ref (type: PANGO_TYPE_SCRIPT);
214 enum_value = g_enum_get_value (enum_class, value: script);
215 g_type_class_unref (g_class: enum_class);
216
217 if (enum_value)
218 return enum_value->value_nick;
219
220 return NULL;
221}
222
223static const char *tab_align_names[] = {
224 "left",
225 "right",
226 "center",
227 "decimal",
228 NULL
229};
230
231static const char *direction_names[] = {
232 "ltr",
233 "rtl",
234 "ttb-ltr",
235 "ttb-rtl",
236 "weak-ltr",
237 "weak-rtl",
238 "neutral",
239 NULL
240};
241
242static const char *alignment_names[] = {
243 "left",
244 "center",
245 "right",
246 NULL
247};
248
249static const char *wrap_names[] = {
250 "word",
251 "char",
252 "word-char",
253 NULL
254};
255
256static const char *ellipsize_names[] = {
257 "none",
258 "start",
259 "middle",
260 "end",
261 NULL
262};
263
264/* }}} */
265/* {{{ Serialization */
266
267static void
268add_attribute (GtkJsonPrinter *printer,
269 PangoAttribute *attr)
270{
271 char *str;
272
273 gtk_json_printer_start_object (self: printer, NULL);
274
275 if (attr->start_index != PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING)
276 gtk_json_printer_add_integer (self: printer, name: "start", value: (int)attr->start_index);
277 if (attr->end_index != PANGO_ATTR_INDEX_TO_TEXT_END)
278 gtk_json_printer_add_integer (self: printer, name: "end", value: (int)attr->end_index);
279 gtk_json_printer_add_string (self: printer, name: "type", s: attr_type_names[attr->klass->type]);
280
281 switch (attr->klass->type)
282 {
283 default:
284 case PANGO_ATTR_INVALID:
285 g_assert_not_reached ();
286 case PANGO_ATTR_LANGUAGE:
287 gtk_json_printer_add_string (self: printer, name: "value", pango_language_to_string (((PangoAttrLanguage*)attr)->value));
288 break;
289 case PANGO_ATTR_FAMILY:
290 case PANGO_ATTR_FONT_FEATURES:
291 gtk_json_printer_add_string (self: printer, name: "value", s: ((PangoAttrString*)attr)->value);
292 break;
293 case PANGO_ATTR_STYLE:
294 gtk_json_printer_add_string (self: printer, name: "value", s: style_names[((PangoAttrInt*)attr)->value]);
295 break;
296
297 case PANGO_ATTR_VARIANT:
298 gtk_json_printer_add_string (self: printer, name: "value", s: variant_names[((PangoAttrInt*)attr)->value]);
299 break;
300
301 case PANGO_ATTR_STRETCH:
302 gtk_json_printer_add_string (self: printer, name: "value", s: stretch_names[((PangoAttrInt*)attr)->value]);
303 break;
304
305 case PANGO_ATTR_UNDERLINE:
306 gtk_json_printer_add_string (self: printer, name: "value", s: underline_names[((PangoAttrInt*)attr)->value]);
307 break;
308
309 case PANGO_ATTR_OVERLINE:
310 gtk_json_printer_add_string (self: printer, name: "value", s: overline_names[((PangoAttrInt*)attr)->value]);
311 break;
312
313 case PANGO_ATTR_GRAVITY:
314 gtk_json_printer_add_string (self: printer, name: "value", s: gravity_names[((PangoAttrInt*)attr)->value]);
315 break;
316
317 case PANGO_ATTR_GRAVITY_HINT:
318 gtk_json_printer_add_string (self: printer, name: "value", s: gravity_hint_names[((PangoAttrInt*)attr)->value]);
319 break;
320
321 case PANGO_ATTR_TEXT_TRANSFORM:
322 gtk_json_printer_add_string (self: printer, name: "value", s: text_transform_names[((PangoAttrInt*)attr)->value]);
323 break;
324
325 case PANGO_ATTR_FONT_SCALE:
326 gtk_json_printer_add_string (self: printer, name: "value", s: font_scale_names[((PangoAttrInt*)attr)->value]);
327 break;
328
329 case PANGO_ATTR_WEIGHT:
330 {
331 const char *name = get_weight_name (weight: ((PangoAttrInt*)attr)->value);
332 if (name)
333 gtk_json_printer_add_string (self: printer, name: "value", s: name);
334 else
335 gtk_json_printer_add_integer (self: printer, name: "value", value: ((PangoAttrInt*)attr)->value);
336 }
337 break;
338
339 case PANGO_ATTR_BASELINE_SHIFT:
340 gtk_json_printer_add_string (self: printer, name: "value", s: baseline_shift_names[((PangoAttrInt*)attr)->value]);
341 break;
342
343
344 case PANGO_ATTR_SIZE:
345 case PANGO_ATTR_RISE:
346 case PANGO_ATTR_LETTER_SPACING:
347 case PANGO_ATTR_ABSOLUTE_SIZE:
348 case PANGO_ATTR_FOREGROUND_ALPHA:
349 case PANGO_ATTR_BACKGROUND_ALPHA:
350 case PANGO_ATTR_SHOW:
351 case PANGO_ATTR_WORD:
352 case PANGO_ATTR_SENTENCE:
353 case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
354 gtk_json_printer_add_integer (self: printer, name: "value", value: ((PangoAttrInt*)attr)->value);
355 break;
356
357 case PANGO_ATTR_FONT_DESC:
358 str = pango_font_description_to_string (desc: ((PangoAttrFontDesc*)attr)->desc);
359 gtk_json_printer_add_string (self: printer, name: "value", s: str);
360 g_free (mem: str);
361 break;
362
363 case PANGO_ATTR_FOREGROUND:
364 case PANGO_ATTR_BACKGROUND:
365 case PANGO_ATTR_UNDERLINE_COLOR:
366 case PANGO_ATTR_OVERLINE_COLOR:
367 case PANGO_ATTR_STRIKETHROUGH_COLOR:
368 str = pango_color_to_string (color: &((PangoAttrColor*)attr)->color);
369 gtk_json_printer_add_string (self: printer, name: "value", s: str);
370 g_free (mem: str);
371 break;
372
373 case PANGO_ATTR_STRIKETHROUGH:
374 case PANGO_ATTR_FALLBACK:
375 case PANGO_ATTR_ALLOW_BREAKS:
376 case PANGO_ATTR_INSERT_HYPHENS:
377 gtk_json_printer_add_boolean (self: printer, name: "value", value: ((PangoAttrInt*)attr)->value != 0);
378 break;
379
380 case PANGO_ATTR_SHAPE:
381 gtk_json_printer_add_string (self: printer, name: "value", s: "shape");
382 break;
383
384 case PANGO_ATTR_SCALE:
385 case PANGO_ATTR_LINE_HEIGHT:
386 gtk_json_printer_add_number (self: printer, name: "value", value: ((PangoAttrFloat*)attr)->value);
387 }
388
389 gtk_json_printer_end (self: printer);
390}
391
392static void
393add_attr_list (GtkJsonPrinter *printer,
394 PangoAttrList *attrs)
395{
396 GSList *attributes, *l;
397
398 if (!attrs)
399 return;
400
401 attributes = pango_attr_list_get_attributes (list: attrs);
402
403 if (!attributes)
404 return;
405
406 gtk_json_printer_start_array (self: printer, name: "attributes");
407
408 for (l = attributes; l; l = l->next)
409 {
410 PangoAttribute *attr = l->data;
411 add_attribute (printer, attr);
412 }
413 g_slist_free_full (list: attributes, free_func: (GDestroyNotify) pango_attribute_destroy);
414
415 gtk_json_printer_end (self: printer);
416}
417
418static void
419add_tab_array (GtkJsonPrinter *printer,
420 PangoTabArray *tabs)
421{
422 if (!tabs || pango_tab_array_get_size (tab_array: tabs) == 0)
423 return;
424
425 gtk_json_printer_start_object (self: printer, name: "tabs");
426
427 gtk_json_printer_add_boolean (self: printer, name: "positions-in-pixels", value: pango_tab_array_get_positions_in_pixels (tab_array: tabs));
428 gtk_json_printer_start_array (self: printer, name: "positions");
429 for (int i = 0; i < pango_tab_array_get_size (tab_array: tabs); i++)
430 {
431 PangoTabAlign align;
432 int pos;
433 pango_tab_array_get_tab (tab_array: tabs, tab_index: i, alignment: &align, location: &pos);
434 gtk_json_printer_start_object (self: printer, NULL);
435 gtk_json_printer_add_integer (self: printer, name: "position", value: pos);
436 gtk_json_printer_add_string (self: printer, name: "alignment", s: tab_align_names[align]);
437 gtk_json_printer_add_integer (self: printer, name: "decimal-point", value: pango_tab_array_get_decimal_point (tab_array: tabs, tab_index: i));
438 gtk_json_printer_end (self: printer);
439 }
440 gtk_json_printer_end (self: printer);
441
442 gtk_json_printer_end (self: printer);
443}
444
445static void
446add_context (GtkJsonPrinter *printer,
447 PangoContext *context)
448{
449 char *str;
450 const PangoMatrix *matrix;
451 PangoMatrix identity = PANGO_MATRIX_INIT;
452
453 gtk_json_printer_start_object (self: printer, name: "context");
454
455 /* Note: since we don't create the context when deserializing,
456 * we don't strip out default values here to ensure that the
457 * context gets updated as expected.
458 */
459
460 str = pango_font_description_to_string (desc: context->font_desc);
461 gtk_json_printer_add_string (self: printer, name: "font", s: str);
462 g_free (mem: str);
463
464 if (context->set_language)
465 gtk_json_printer_add_string (self: printer, name: "language", pango_language_to_string (context->set_language));
466
467 gtk_json_printer_add_string (self: printer, name: "base-gravity", s: gravity_names[context->base_gravity]);
468 gtk_json_printer_add_string (self: printer, name: "gravity-hint", s: gravity_hint_names[context->gravity_hint]);
469 gtk_json_printer_add_string (self: printer, name: "base-dir", s: direction_names[context->base_dir]);
470 gtk_json_printer_add_boolean (self: printer, name: "round-glyph-positions", value: context->round_glyph_positions);
471
472 matrix = pango_context_get_matrix (context);
473 if (!matrix)
474 matrix = &identity;
475
476 gtk_json_printer_start_array (self: printer, name: "transform");
477 gtk_json_printer_add_number (self: printer, NULL, value: matrix->xx);
478 gtk_json_printer_add_number (self: printer, NULL, value: matrix->xy);
479 gtk_json_printer_add_number (self: printer, NULL, value: matrix->yx);
480 gtk_json_printer_add_number (self: printer, NULL, value: matrix->yy);
481 gtk_json_printer_add_number (self: printer, NULL, value: matrix->x0);
482 gtk_json_printer_add_number (self: printer, NULL, value: matrix->y0);
483 gtk_json_printer_end (self: printer);
484
485 gtk_json_printer_end (self: printer);
486}
487
488static void
489add_log_attrs (GtkJsonPrinter *printer,
490 PangoLayout *layout)
491{
492 const PangoLogAttr *log_attrs;
493 int n_attrs;
494
495 gtk_json_printer_start_array (self: printer, name: "log-attrs");
496
497 log_attrs = pango_layout_get_log_attrs_readonly (layout, n_attrs: &n_attrs);
498 for (int i = 0; i < n_attrs; i++)
499 {
500 gtk_json_printer_start_object (self: printer, NULL);
501 if (log_attrs[i].is_line_break)
502 gtk_json_printer_add_boolean (self: printer, name: "line-break", TRUE);
503 if (log_attrs[i].is_mandatory_break)
504 gtk_json_printer_add_boolean (self: printer, name: "mandatory-break", TRUE);
505 if (log_attrs[i].is_char_break)
506 gtk_json_printer_add_boolean (self: printer, name: "char-break", TRUE);
507 if (log_attrs[i].is_white)
508 gtk_json_printer_add_boolean (self: printer, name: "white", TRUE);
509 if (log_attrs[i].is_cursor_position)
510 gtk_json_printer_add_boolean (self: printer, name: "cursor-position", TRUE);
511 if (log_attrs[i].is_word_start)
512 gtk_json_printer_add_boolean (self: printer, name: "word-start", TRUE);
513 if (log_attrs[i].is_word_end)
514 gtk_json_printer_add_boolean (self: printer, name: "word-end", TRUE);
515 if (log_attrs[i].is_sentence_boundary)
516 gtk_json_printer_add_boolean (self: printer, name: "sentence-boundary", TRUE);
517 if (log_attrs[i].is_sentence_start)
518 gtk_json_printer_add_boolean (self: printer, name: "sentence-start", TRUE);
519 if (log_attrs[i].is_sentence_end)
520 gtk_json_printer_add_boolean (self: printer, name: "sentence-end", TRUE);
521 if (log_attrs[i].backspace_deletes_character)
522 gtk_json_printer_add_boolean (self: printer, name: "backspace-deletes-character", TRUE);
523 if (log_attrs[i].is_expandable_space)
524 gtk_json_printer_add_boolean (self: printer, name: "expandable-space", TRUE);
525 if (log_attrs[i].is_word_boundary)
526 gtk_json_printer_add_boolean (self: printer, name: "word-boundary", TRUE);
527 if (log_attrs[i].break_inserts_hyphen)
528 gtk_json_printer_add_boolean (self: printer, name: "break-inserts-hyphen", TRUE);
529 if (log_attrs[i].break_removes_preceding)
530 gtk_json_printer_add_boolean (self: printer, name: "break-removes-preceding", TRUE);
531 gtk_json_printer_end (self: printer);
532 }
533
534 gtk_json_printer_end (self: printer);
535}
536
537static void
538add_font (GtkJsonPrinter *printer,
539 const char *member,
540 PangoFont *font)
541{
542 PangoFontDescription *desc;
543 char *str;
544 hb_font_t *hb_font;
545 hb_face_t *face;
546 hb_blob_t *blob;
547 const char *data;
548 guint length;
549 const int *coords;
550 hb_feature_t features[32];
551 PangoMatrix matrix;
552
553 gtk_json_printer_start_object (self: printer, name: member);
554
555 desc = pango_font_describe (font);
556 str = pango_font_description_to_string (desc);
557 gtk_json_printer_add_string (self: printer, name: "description", s: str);
558 g_free (mem: str);
559 pango_font_description_free (desc);
560
561 hb_font = pango_font_get_hb_font (font);
562 face = hb_font_get_face (font: hb_font);
563 blob = hb_face_reference_blob (face);
564
565 data = hb_blob_get_data (blob, length: &length);
566 str = g_compute_checksum_for_data (checksum_type: G_CHECKSUM_SHA256, data: (const guchar *)data, length);
567
568 gtk_json_printer_add_string (self: printer, name: "checksum", s: str);
569
570 g_free (mem: str);
571 hb_blob_destroy (blob);
572
573 coords = hb_font_get_var_coords_normalized (font: hb_font, length: &length);
574 if (length > 0)
575 {
576 guint count;
577 hb_ot_var_axis_info_t *axes;
578
579 count = hb_ot_var_get_axis_count (face);
580 g_assert (count == length);
581
582 axes = g_alloca (count * sizeof (hb_ot_var_axis_info_t));
583 hb_ot_var_get_axis_infos (face, start_offset: 0, axes_count: &count, axes_array: axes);
584
585 gtk_json_printer_start_object (self: printer, name: "variations");
586
587 for (int i = 0; i < length; i++)
588 {
589 char buf[5] = { 0, };
590
591 hb_tag_to_string (tag: axes[i].tag, buf);
592 gtk_json_printer_add_integer (self: printer, name: buf, value: coords[i]);
593 }
594
595 gtk_json_printer_end (self: printer);
596 }
597
598 length = 0;
599 pango_font_get_features (font, features, G_N_ELEMENTS (features), num_features: &length);
600 if (length > 0)
601 {
602 gtk_json_printer_start_object (self: printer, name: "features");
603
604 for (int i = 0; i < length; i++)
605 {
606 char buf[5] = { 0, };
607
608 hb_tag_to_string (tag: features[i].tag, buf);
609 gtk_json_printer_add_integer (self: printer, name: buf, value: features[i].value);
610 }
611
612 gtk_json_printer_end (self: printer);
613 }
614
615 pango_font_get_matrix (font, matrix: &matrix);
616 if (memcmp (s1: &matrix, s2: &(PangoMatrix)PANGO_MATRIX_INIT, n: sizeof (PangoMatrix)) != 0)
617 {
618 gtk_json_printer_start_array (self: printer, name: "matrix");
619 gtk_json_printer_add_number (self: printer, NULL, value: matrix.xx);
620 gtk_json_printer_add_number (self: printer, NULL, value: matrix.xy);
621 gtk_json_printer_add_number (self: printer, NULL, value: matrix.yx);
622 gtk_json_printer_add_number (self: printer, NULL, value: matrix.yy);
623 gtk_json_printer_add_number (self: printer, NULL, value: matrix.x0);
624 gtk_json_printer_add_number (self: printer, NULL, value: matrix.y0);
625 gtk_json_printer_end (self: printer);
626 }
627
628 gtk_json_printer_end (self: printer);
629}
630
631#define ANALYSIS_FLAGS (PANGO_ANALYSIS_FLAG_CENTERED_BASELINE | \
632 PANGO_ANALYSIS_FLAG_IS_ELLIPSIS | \
633 PANGO_ANALYSIS_FLAG_NEED_HYPHEN)
634
635static void
636add_run (GtkJsonPrinter *printer,
637 PangoLayout *layout,
638 PangoLayoutRun *run)
639{
640 char *str;
641
642 gtk_json_printer_start_object (self: printer, NULL);
643
644 gtk_json_printer_add_integer (self: printer, name: "offset", value: run->item->offset);
645 gtk_json_printer_add_integer (self: printer, name: "length", value: run->item->length);
646
647 str = g_strndup (str: layout->text + run->item->offset, n: run->item->length);
648 gtk_json_printer_add_string (self: printer, name: "text", s: str);
649 g_free (mem: str);
650
651 gtk_json_printer_add_integer (self: printer, name: "bidi-level", value: run->item->analysis.level);
652 gtk_json_printer_add_string (self: printer, name: "gravity", s: gravity_names[run->item->analysis.gravity]);
653 gtk_json_printer_add_string (self: printer, name: "language", pango_language_to_string (run->item->analysis.language));
654 gtk_json_printer_add_string (self: printer, name: "script", s: get_script_name (script: run->item->analysis.script));
655
656 add_font (printer, member: "font", font: run->item->analysis.font);
657
658 gtk_json_printer_add_integer (self: printer, name: "flags", value: run->item->analysis.flags & ANALYSIS_FLAGS);
659
660 if (run->item->analysis.extra_attrs)
661 {
662 GSList *l;
663
664 gtk_json_printer_start_array (self: printer, name: "extra-attributes");
665 for (l = run->item->analysis.extra_attrs; l; l = l->next)
666 {
667 PangoAttribute *attr = l->data;
668 add_attribute (printer, attr);
669 }
670 gtk_json_printer_end (self: printer);
671 }
672
673 gtk_json_printer_add_integer (self: printer, name: "y-offset", value: run->y_offset);
674 gtk_json_printer_add_integer (self: printer, name: "start-x-offset", value: run->start_x_offset);
675 gtk_json_printer_add_integer (self: printer, name: "end-x-offset", value: run->end_x_offset);
676
677 gtk_json_printer_start_array (self: printer, name: "glyphs");
678 for (int i = 0; i < run->glyphs->num_glyphs; i++)
679 {
680 gtk_json_printer_start_object (self: printer, NULL);
681
682 gtk_json_printer_add_integer (self: printer, name: "glyph", value: run->glyphs->glyphs[i].glyph);
683 gtk_json_printer_add_integer (self: printer, name: "width", value: run->glyphs->glyphs[i].geometry.width);
684
685 if (run->glyphs->glyphs[i].geometry.x_offset != 0)
686 gtk_json_printer_add_integer (self: printer, name: "x-offset", value: run->glyphs->glyphs[i].geometry.x_offset);
687
688 if (run->glyphs->glyphs[i].geometry.y_offset != 0)
689 gtk_json_printer_add_integer (self: printer, name: "y-offset", value: run->glyphs->glyphs[i].geometry.y_offset);
690
691 if (run->glyphs->glyphs[i].attr.is_cluster_start)
692 gtk_json_printer_add_boolean (self: printer, name: "is-cluster-start", TRUE);
693
694 if (run->glyphs->glyphs[i].attr.is_color)
695 gtk_json_printer_add_boolean (self: printer, name: "is-color", TRUE);
696
697 gtk_json_printer_add_integer (self: printer, name: "log-cluster", value: run->glyphs->log_clusters[i]);
698
699 gtk_json_printer_end (self: printer);
700 }
701
702 gtk_json_printer_end (self: printer);
703
704 gtk_json_printer_end (self: printer);
705}
706
707#undef ANALYSIS_FLAGS
708
709static void
710add_line (GtkJsonPrinter *printer,
711 PangoLayoutLine *line)
712{
713 gtk_json_printer_start_object (self: printer, NULL);
714
715 gtk_json_printer_add_integer (self: printer, name: "start-index", value: line->start_index);
716 gtk_json_printer_add_integer (self: printer, name: "length", value: line->length);
717 gtk_json_printer_add_boolean (self: printer, name: "paragraph-start", value: line->is_paragraph_start);
718 gtk_json_printer_add_string (self: printer, name: "direction", s: direction_names[line->resolved_dir]);
719
720 gtk_json_printer_start_array (self: printer, name: "runs");
721 for (GSList *l = line->runs; l; l = l->next)
722 {
723 PangoLayoutRun *run = l->data;
724 add_run (printer, layout: line->layout, run);
725 }
726 gtk_json_printer_end (self: printer);
727
728 gtk_json_printer_end (self: printer);
729}
730
731static void
732add_output (GtkJsonPrinter *printer,
733 PangoLayout *layout)
734{
735 int width, height;
736
737 gtk_json_printer_start_object (self: printer, name: "output");
738
739 gtk_json_printer_add_boolean (self: printer, name: "is-wrapped", value: pango_layout_is_wrapped (layout));
740 gtk_json_printer_add_boolean (self: printer, name: "is-ellipsized", value: pango_layout_is_ellipsized (layout));
741 gtk_json_printer_add_integer (self: printer, name: "unknown-glyphs", value: pango_layout_get_unknown_glyphs_count (layout));
742
743 pango_layout_get_size (layout, width: &width, height: &height);
744 gtk_json_printer_add_integer (self: printer, name: "width", value: width);
745 gtk_json_printer_add_integer (self: printer, name: "height", value: height);
746
747 add_log_attrs (printer, layout);
748 gtk_json_printer_start_array (self: printer, name: "lines");
749 for (GSList *l = layout->lines; l; l = l->next)
750 {
751 PangoLayoutLine *line = l->data;
752 add_line (printer, line);
753 }
754 gtk_json_printer_end (self: printer);
755
756 gtk_json_printer_end (self: printer);
757}
758
759static void
760layout_to_json (GtkJsonPrinter *printer,
761 PangoLayout *layout,
762 PangoLayoutSerializeFlags flags)
763{
764 const char *str;
765
766 gtk_json_printer_start_object (self: printer, NULL);
767
768 if (flags & PANGO_LAYOUT_SERIALIZE_CONTEXT)
769 add_context (printer, context: layout->context);
770
771 str = (const char *) g_object_get_data (G_OBJECT (layout), key: "comment");
772 if (str)
773 gtk_json_printer_add_string (self: printer, name: "comment", s: str);
774
775 gtk_json_printer_add_string (self: printer, name: "text", s: layout->text);
776
777 add_attr_list (printer, attrs: layout->attrs);
778
779 if (layout->font_desc)
780 {
781 char *str = pango_font_description_to_string (desc: layout->font_desc);
782 gtk_json_printer_add_string (self: printer, name: "font", s: str);
783 g_free (mem: str);
784 }
785
786 add_tab_array (printer, tabs: layout->tabs);
787
788 if (layout->justify)
789 gtk_json_printer_add_boolean (self: printer, name: "justify", TRUE);
790
791 if (layout->justify_last_line)
792 gtk_json_printer_add_boolean (self: printer, name: "justify-last-line", TRUE);
793
794 if (layout->single_paragraph)
795 gtk_json_printer_add_boolean (self: printer, name: "single-paragraph", TRUE);
796
797 if (!layout->auto_dir)
798 gtk_json_printer_add_boolean (self: printer, name: "auto-dir", FALSE);
799
800 if (layout->alignment != PANGO_ALIGN_LEFT)
801 gtk_json_printer_add_string (self: printer, name: "alignment", s: alignment_names[layout->alignment]);
802
803 if (layout->wrap != PANGO_WRAP_WORD)
804 gtk_json_printer_add_string (self: printer, name: "wrap", s: wrap_names[layout->wrap]);
805
806 if (layout->ellipsize != PANGO_ELLIPSIZE_NONE)
807 gtk_json_printer_add_string (self: printer, name: "ellipsize", s: ellipsize_names[layout->ellipsize]);
808
809 if (layout->width != -1)
810 gtk_json_printer_add_integer (self: printer, name: "width", value: layout->width);
811
812 if (layout->height != -1)
813 gtk_json_printer_add_integer (self: printer, name: "height", value: layout->height);
814
815 if (layout->indent != 0)
816 gtk_json_printer_add_integer (self: printer, name: "indent", value: layout->indent);
817
818 if (layout->spacing != 0)
819 gtk_json_printer_add_integer (self: printer, name: "spacing", value: layout->spacing);
820
821 if (layout->line_spacing != 0.)
822 gtk_json_printer_add_number (self: printer, name: "line-spacing", value: layout->line_spacing);
823
824 if (flags & PANGO_LAYOUT_SERIALIZE_OUTPUT)
825 add_output (printer, layout);
826
827 gtk_json_printer_end (self: printer);
828}
829
830static void
831gstring_write (GtkJsonPrinter *printer,
832 const char *s,
833 gpointer data)
834{
835 GString *str = data;
836 g_string_append (string: str, val: s);
837}
838
839/* }}} */
840/* {{{ Deserialization */
841
842static int
843parser_select_string (GtkJsonParser *parser,
844 const char **options)
845{
846 int value;
847
848 value = gtk_json_parser_select_string (self: parser, options);
849 if (value == -1)
850 {
851 char *str = gtk_json_parser_get_string (self: parser);
852 char *opts = g_strjoinv (separator: ", ", str_array: (char **)options);
853
854 gtk_json_parser_value_error (self: parser,
855 format: "Failed to parse string: %s, valid options are: %s",
856 str, opts);
857
858 g_free (mem: opts);
859 g_free (mem: str);
860
861 value = 0;
862 }
863
864 return value;
865}
866
867static PangoFontDescription *
868parser_get_font_description (GtkJsonParser *parser)
869{
870 char *str = gtk_json_parser_get_string (self: parser);
871 PangoFontDescription *desc = pango_font_description_from_string (str);
872
873 if (!desc)
874 gtk_json_parser_value_error (self: parser,
875 format: "Failed to parse font: %s", str);
876 g_free (mem: str);
877
878 return desc;
879}
880
881static void
882parser_get_color (GtkJsonParser *parser,
883 PangoColor *color)
884{
885 char *str = gtk_json_parser_get_string (self: parser);
886 if (!pango_color_parse (color, spec: str))
887 {
888 gtk_json_parser_value_error (self: parser,
889 format: "Failed to parse color: %s", str);
890 color->red = color->green = color->blue = 0;
891 }
892
893 g_free (mem: str);
894}
895
896static PangoAttribute *
897attr_for_type (GtkJsonParser *parser,
898 PangoAttrType type,
899 int start,
900 int end)
901{
902 PangoAttribute *attr;
903 PangoFontDescription *desc;
904 PangoColor color;
905 char *str;
906
907 switch (type)
908 {
909 default:
910 g_assert_not_reached ();
911
912 case PANGO_ATTR_INVALID:
913 gtk_json_parser_schema_error (self: parser, format: "Missing attribute type");
914 return NULL;
915
916 case PANGO_ATTR_LANGUAGE:
917 str = gtk_json_parser_get_string (self: parser);
918 attr = pango_attr_language_new (language: pango_language_from_string (language: str));
919 g_free (mem: str);
920 break;
921
922 case PANGO_ATTR_FAMILY:
923 str = gtk_json_parser_get_string (self: parser);
924 attr = pango_attr_family_new (family: str);
925 g_free (mem: str);
926 break;
927
928 case PANGO_ATTR_STYLE:
929 attr = pango_attr_style_new (style: (PangoStyle) parser_select_string (parser, options: style_names));
930 break;
931
932 case PANGO_ATTR_WEIGHT:
933 if (gtk_json_parser_get_node (self: parser) == GTK_JSON_STRING)
934 attr = pango_attr_weight_new (weight: get_weight (pos: parser_select_string (parser, options: weight_names)));
935 else
936 attr = pango_attr_weight_new (weight: (int) gtk_json_parser_get_int (self: parser));
937 break;
938
939 case PANGO_ATTR_VARIANT:
940 attr = pango_attr_variant_new (variant: (PangoVariant) parser_select_string (parser, options: variant_names));
941 break;
942
943 case PANGO_ATTR_STRETCH:
944 attr = pango_attr_stretch_new (stretch: (PangoStretch) parser_select_string (parser, options: stretch_names));
945 break;
946
947 case PANGO_ATTR_SIZE:
948 attr = pango_attr_size_new (size: (int) gtk_json_parser_get_number (self: parser));
949 break;
950
951 case PANGO_ATTR_FONT_DESC:
952 desc = parser_get_font_description (parser);
953 attr = pango_attr_font_desc_new (desc);
954 pango_font_description_free (desc);
955 break;
956
957 case PANGO_ATTR_FOREGROUND:
958 parser_get_color (parser, color: &color);
959 attr = pango_attr_foreground_new (red: color.red, green: color.green, blue: color.blue);
960 break;
961
962 case PANGO_ATTR_BACKGROUND:
963 parser_get_color (parser, color: &color);
964 attr = pango_attr_background_new (red: color.red, green: color.green, blue: color.blue);
965 break;
966
967 case PANGO_ATTR_UNDERLINE:
968 attr = pango_attr_underline_new (underline: (PangoUnderline) parser_select_string (parser, options: underline_names));
969 break;
970
971 case PANGO_ATTR_STRIKETHROUGH:
972 attr = pango_attr_strikethrough_new (strikethrough: gtk_json_parser_get_boolean (self: parser));
973 break;
974
975 case PANGO_ATTR_RISE:
976 attr = pango_attr_rise_new (rise: (int) gtk_json_parser_get_number (self: parser));
977 break;
978
979 case PANGO_ATTR_SHAPE:
980 /* FIXME */
981 attr = pango_attr_shape_new (ink_rect: &(PangoRectangle) { 0, 0, 0, 0}, logical_rect: &(PangoRectangle) { 0, 0, 0, 0});
982 break;
983
984 case PANGO_ATTR_SCALE:
985 attr = pango_attr_scale_new (scale_factor: gtk_json_parser_get_number (self: parser));
986 break;
987
988 case PANGO_ATTR_FALLBACK:
989 attr = pango_attr_fallback_new (enable_fallback: gtk_json_parser_get_boolean (self: parser));
990 break;
991
992 case PANGO_ATTR_LETTER_SPACING:
993 attr = pango_attr_letter_spacing_new (letter_spacing: (int) gtk_json_parser_get_number (self: parser));
994 break;
995
996 case PANGO_ATTR_UNDERLINE_COLOR:
997 parser_get_color (parser, color: &color);
998 attr = pango_attr_underline_color_new (red: color.red, green: color.green, blue: color.blue);
999 break;
1000
1001 case PANGO_ATTR_STRIKETHROUGH_COLOR:
1002 parser_get_color (parser, color: &color);
1003 attr = pango_attr_strikethrough_color_new (red: color.red, green: color.green, blue: color.blue);
1004 break;
1005
1006 case PANGO_ATTR_ABSOLUTE_SIZE:
1007 attr = pango_attr_size_new_absolute (size: (int) gtk_json_parser_get_number (self: parser));
1008 break;
1009
1010 case PANGO_ATTR_GRAVITY:
1011 attr = pango_attr_gravity_new (gravity: (PangoGravity) parser_select_string (parser, options: gravity_names));
1012 break;
1013
1014 case PANGO_ATTR_GRAVITY_HINT:
1015 attr = pango_attr_gravity_hint_new (hint: (PangoGravityHint) parser_select_string (parser, options: gravity_hint_names));
1016 break;
1017
1018 case PANGO_ATTR_FONT_FEATURES:
1019 str = gtk_json_parser_get_string (self: parser);
1020 attr = pango_attr_font_features_new (features: str);
1021 g_free (mem: str);
1022 break;
1023
1024 case PANGO_ATTR_FOREGROUND_ALPHA:
1025 attr = pango_attr_foreground_alpha_new (alpha: (int) gtk_json_parser_get_number (self: parser));
1026 break;
1027
1028 case PANGO_ATTR_BACKGROUND_ALPHA:
1029 attr = pango_attr_background_alpha_new (alpha: (int) gtk_json_parser_get_number (self: parser));
1030 break;
1031
1032 case PANGO_ATTR_ALLOW_BREAKS:
1033 attr = pango_attr_allow_breaks_new (allow_breaks: gtk_json_parser_get_boolean (self: parser));
1034 break;
1035
1036 case PANGO_ATTR_SHOW:
1037 attr = pango_attr_show_new (flags: (int) gtk_json_parser_get_number (self: parser));
1038 break;
1039
1040 case PANGO_ATTR_INSERT_HYPHENS:
1041 attr = pango_attr_insert_hyphens_new (insert_hyphens: (int) gtk_json_parser_get_number (self: parser));
1042 break;
1043
1044 case PANGO_ATTR_OVERLINE:
1045 attr = pango_attr_overline_new (overline: (PangoOverline) parser_select_string (parser, options: overline_names));
1046 break;
1047
1048 case PANGO_ATTR_OVERLINE_COLOR:
1049 parser_get_color (parser, color: &color);
1050 attr = pango_attr_overline_color_new (red: color.red, green: color.green, blue: color.blue);
1051 break;
1052
1053 case PANGO_ATTR_LINE_HEIGHT:
1054 attr = pango_attr_line_height_new (factor: gtk_json_parser_get_number (self: parser));
1055 break;
1056
1057 case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
1058 attr = pango_attr_line_height_new_absolute (height: (int) gtk_json_parser_get_number (self: parser));
1059 break;
1060
1061 case PANGO_ATTR_TEXT_TRANSFORM:
1062 attr = pango_attr_text_transform_new (transform: (PangoTextTransform) parser_select_string (parser, options: text_transform_names));
1063 break;
1064
1065 case PANGO_ATTR_WORD:
1066 attr = pango_attr_word_new ();
1067 break;
1068
1069 case PANGO_ATTR_SENTENCE:
1070 attr = pango_attr_sentence_new ();
1071 break;
1072
1073 case PANGO_ATTR_BASELINE_SHIFT:
1074 attr = pango_attr_baseline_shift_new (shift: parser_select_string (parser, options: baseline_shift_names));
1075 break;
1076
1077 case PANGO_ATTR_FONT_SCALE:
1078 attr = pango_attr_font_scale_new (scale: (PangoFontScale) parser_select_string (parser, options: font_scale_names));
1079 break;
1080 }
1081
1082 attr->start_index = start;
1083 attr->end_index = end;
1084
1085 return attr;
1086}
1087
1088enum {
1089 ATTR_START,
1090 ATTR_END,
1091 ATTR_TYPE,
1092 ATTR_VALUE
1093};
1094
1095static const char *attr_members[] = {
1096 "start",
1097 "end",
1098 "type",
1099 "value",
1100 NULL
1101};
1102
1103static PangoAttribute *
1104json_to_attribute (GtkJsonParser *parser)
1105{
1106 PangoAttribute *attr = NULL;
1107 PangoAttrType type = PANGO_ATTR_INVALID;
1108 guint start = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
1109 guint end = PANGO_ATTR_INDEX_TO_TEXT_END;
1110
1111 gtk_json_parser_start_object (self: parser);
1112
1113 do
1114 {
1115 switch (gtk_json_parser_select_member (self: parser, options: attr_members))
1116 {
1117 case ATTR_START:
1118 start = (int)gtk_json_parser_get_number (self: parser);
1119 break;
1120
1121 case ATTR_END:
1122 end = (int)gtk_json_parser_get_number (self: parser);
1123 break;
1124
1125 case ATTR_TYPE:
1126 type = parser_select_string (parser, options: attr_type_names);
1127 break;
1128
1129 case ATTR_VALUE:
1130 attr = attr_for_type (parser, type, start, end);
1131 break;
1132
1133 default:
1134 break;
1135 }
1136 }
1137 while (gtk_json_parser_next (self: parser));
1138
1139 if (!attr && !gtk_json_parser_get_error (self: parser))
1140 gtk_json_parser_schema_error (self: parser, format: "Attribute missing \"value\"");
1141
1142 gtk_json_parser_end (self: parser);
1143
1144 return attr;
1145}
1146
1147static void
1148json_parser_fill_attr_list (GtkJsonParser *parser,
1149 PangoAttrList *attributes)
1150{
1151 gtk_json_parser_start_array (self: parser);
1152
1153 do
1154 {
1155 PangoAttribute *attr = json_to_attribute (parser);
1156 if (attr)
1157 pango_attr_list_insert (list: attributes, attr);
1158 }
1159 while (gtk_json_parser_next (self: parser));
1160
1161 gtk_json_parser_end (self: parser);
1162}
1163
1164enum {
1165 TAB_POSITION,
1166 TAB_ALIGNMENT,
1167 TAB_DECIMAL_POINT
1168};
1169
1170static const char *tab_members[] = {
1171 "position",
1172 "alignment",
1173 "decimal-point",
1174 NULL,
1175};
1176
1177
1178static void
1179json_parser_fill_tabs (GtkJsonParser *parser,
1180 PangoTabArray *tabs)
1181{
1182 int index;
1183
1184 gtk_json_parser_start_array (self: parser);
1185
1186 index = 0;
1187 do
1188 {
1189 int pos;
1190 PangoTabAlign align = PANGO_TAB_LEFT;
1191 gunichar ch = 0;
1192
1193 if (gtk_json_parser_get_node (self: parser) == GTK_JSON_OBJECT)
1194 {
1195 gtk_json_parser_start_object (self: parser);
1196 do
1197 {
1198 switch (gtk_json_parser_select_member (self: parser, options: tab_members))
1199 {
1200 case TAB_POSITION:
1201 pos = (int) gtk_json_parser_get_number (self: parser);
1202 break;
1203
1204 case TAB_ALIGNMENT:
1205 align = (PangoTabAlign) parser_select_string (parser, options: tab_align_names);
1206 break;
1207
1208 case TAB_DECIMAL_POINT:
1209 ch = (int) gtk_json_parser_get_number (self: parser);
1210 break;
1211
1212 default:
1213 break;
1214 }
1215 }
1216 while (gtk_json_parser_next (self: parser));
1217
1218 gtk_json_parser_end (self: parser);
1219 }
1220 else
1221 pos = (int) gtk_json_parser_get_number (self: parser);
1222
1223 pango_tab_array_set_tab (tab_array: tabs, tab_index: index, alignment: align, location: pos);
1224 pango_tab_array_set_decimal_point (tab_array: tabs, tab_index: index, decimal_point: ch);
1225 index++;
1226 }
1227 while (gtk_json_parser_next (self: parser));
1228
1229 gtk_json_parser_end (self: parser);
1230}
1231
1232enum {
1233 TABS_POSITIONS_IN_PIXELS,
1234 TABS_POSITIONS
1235};
1236
1237static const char *tabs_members[] = {
1238 "positions-in-pixels",
1239 "positions",
1240 NULL
1241};
1242
1243static void
1244json_parser_fill_tab_array (GtkJsonParser *parser,
1245 PangoTabArray *tabs)
1246{
1247 gtk_json_parser_start_object (self: parser);
1248
1249 do
1250 {
1251 switch (gtk_json_parser_select_member (self: parser, options: tabs_members))
1252 {
1253 case TABS_POSITIONS_IN_PIXELS:
1254 pango_tab_array_set_positions_in_pixels (tab_array: tabs, positions_in_pixels: gtk_json_parser_get_boolean (self: parser));
1255 break;
1256
1257 case TABS_POSITIONS:
1258 json_parser_fill_tabs (parser, tabs);
1259 break;
1260
1261 default:
1262 break;
1263 }
1264 }
1265 while (gtk_json_parser_next (self: parser));
1266
1267 gtk_json_parser_end (self: parser);
1268}
1269
1270enum {
1271 CONTEXT_LANGUAGE,
1272 CONTEXT_FONT,
1273 CONTEXT_BASE_GRAVITY,
1274 CONTEXT_GRAVITY_HINT,
1275 CONTEXT_BASE_DIR,
1276 CONTEXT_ROUND_GLYPH_POSITIONS,
1277 CONTEXT_TRANSFORM,
1278};
1279
1280static const char *context_members[] = {
1281 "language",
1282 "font",
1283 "base-gravity",
1284 "gravity-hint",
1285 "base-dir",
1286 "round-glyph-positions",
1287 "transform",
1288 NULL,
1289};
1290
1291static void
1292json_parser_fill_context (GtkJsonParser *parser,
1293 PangoContext *context)
1294{
1295 gtk_json_parser_start_object (self: parser);
1296
1297 do
1298 {
1299 char *str;
1300
1301 switch (gtk_json_parser_select_member (self: parser, options: context_members))
1302 {
1303 case CONTEXT_LANGUAGE:
1304 str = gtk_json_parser_get_string (self: parser);
1305 PangoLanguage *language = pango_language_from_string (language: str);
1306 pango_context_set_language (context, language);
1307 g_free (mem: str);
1308 break;
1309
1310 case CONTEXT_FONT:
1311 {
1312 PangoFontDescription *desc = parser_get_font_description (parser);
1313 pango_context_set_font_description (context, desc);
1314 pango_font_description_free (desc);
1315 }
1316 break;
1317
1318 case CONTEXT_BASE_GRAVITY:
1319 pango_context_set_base_gravity (context, gravity: (PangoGravity) parser_select_string (parser, options: gravity_names));
1320 break;
1321
1322 case CONTEXT_GRAVITY_HINT:
1323 pango_context_set_gravity_hint (context, hint: (PangoGravityHint) parser_select_string (parser, options: gravity_hint_names));
1324 break;
1325
1326 case CONTEXT_BASE_DIR:
1327 pango_context_set_base_dir (context, direction: (PangoDirection) parser_select_string (parser, options: direction_names));
1328 break;
1329
1330 case CONTEXT_ROUND_GLYPH_POSITIONS:
1331 pango_context_set_round_glyph_positions (context, round_positions: gtk_json_parser_get_boolean (self: parser));
1332 break;
1333
1334 case CONTEXT_TRANSFORM:
1335 {
1336 PangoMatrix m = PANGO_MATRIX_INIT;
1337
1338 gtk_json_parser_start_array (self: parser);
1339 m.xx = gtk_json_parser_get_number (self: parser);
1340 gtk_json_parser_next (self: parser);
1341 m.xy = gtk_json_parser_get_number (self: parser);
1342 gtk_json_parser_next (self: parser);
1343 m.yx = gtk_json_parser_get_number (self: parser);
1344 gtk_json_parser_next (self: parser);
1345 m.yy = gtk_json_parser_get_number (self: parser);
1346 gtk_json_parser_next (self: parser);
1347 m.x0 = gtk_json_parser_get_number (self: parser);
1348 gtk_json_parser_next (self: parser);
1349 m.y0 = gtk_json_parser_get_number (self: parser);
1350 gtk_json_parser_end (self: parser);
1351
1352 pango_context_set_matrix (context, matrix: &m);
1353 }
1354 break;
1355
1356 default:
1357 break;
1358 }
1359 }
1360 while (gtk_json_parser_next (self: parser));
1361
1362 gtk_json_parser_end (self: parser);
1363}
1364
1365enum {
1366 LAYOUT_CONTEXT,
1367 LAYOUT_COMMENT,
1368 LAYOUT_TEXT,
1369 LAYOUT_ATTRIBUTES,
1370 LAYOUT_FONT,
1371 LAYOUT_TABS,
1372 LAYOUT_JUSTIFY,
1373 LAYOUT_JUSTIFY_LAST_LINE,
1374 LAYOUT_SINGLE_PARAGRAPH,
1375 LAYOUT_AUTO_DIR,
1376 LAYOUT_ALIGNMENT,
1377 LAYOUT_WRAP,
1378 LAYOUT_ELLIPSIZE,
1379 LAYOUT_WIDTH,
1380 LAYOUT_HEIGHT,
1381 LAYOUT_INDENT,
1382 LAYOUT_SPACING,
1383 LAYOUT_LINE_SPACING,
1384 LAYOUT_OUTPUT
1385};
1386
1387static const char *layout_members[] = {
1388 "context",
1389 "comment",
1390 "text",
1391 "attributes",
1392 "font",
1393 "tabs",
1394 "justify",
1395 "justify-last-line",
1396 "single-paragraph",
1397 "auto-dir",
1398 "alignment",
1399 "wrap",
1400 "ellipsize",
1401 "width",
1402 "height",
1403 "indent",
1404 "spacing",
1405 "line-spacing",
1406 "output",
1407 NULL
1408};
1409
1410static void
1411json_parser_fill_layout (GtkJsonParser *parser,
1412 PangoLayout *layout,
1413 PangoLayoutDeserializeFlags flags)
1414{
1415 gtk_json_parser_start_object (self: parser);
1416
1417 do
1418 {
1419 char *str;
1420
1421 switch (gtk_json_parser_select_member (self: parser, options: layout_members))
1422 {
1423 case LAYOUT_CONTEXT:
1424 if (flags & PANGO_LAYOUT_DESERIALIZE_CONTEXT)
1425 json_parser_fill_context (parser, context: pango_layout_get_context (layout));
1426 break;
1427
1428 case LAYOUT_COMMENT:
1429 str = gtk_json_parser_get_string (self: parser);
1430 g_object_set_data_full (G_OBJECT (layout), key: "comment", data: str, destroy: g_free);
1431 break;
1432
1433 case LAYOUT_TEXT:
1434 str = gtk_json_parser_get_string (self: parser);
1435 pango_layout_set_text (layout, text: str, length: -1);
1436 g_free (mem: str);
1437 break;
1438
1439 case LAYOUT_ATTRIBUTES:
1440 {
1441 PangoAttrList *attributes = pango_attr_list_new ();
1442 json_parser_fill_attr_list (parser, attributes);
1443 pango_layout_set_attributes (layout, attrs: attributes);
1444 pango_attr_list_unref (list: attributes);
1445 }
1446 break;
1447
1448 case LAYOUT_FONT:
1449 {
1450 PangoFontDescription *desc = parser_get_font_description (parser);;
1451 pango_layout_set_font_description (layout, desc);
1452 pango_font_description_free (desc);
1453 }
1454 break;
1455
1456 case LAYOUT_TABS:
1457 {
1458 PangoTabArray *tabs = pango_tab_array_new (initial_size: 0, FALSE);
1459 json_parser_fill_tab_array (parser, tabs);
1460 pango_layout_set_tabs (layout, tabs);
1461 pango_tab_array_free (tab_array: tabs);
1462 }
1463 break;
1464
1465 case LAYOUT_JUSTIFY:
1466 pango_layout_set_justify (layout, justify: gtk_json_parser_get_boolean (self: parser));
1467 break;
1468
1469 case LAYOUT_JUSTIFY_LAST_LINE:
1470 pango_layout_set_justify_last_line (layout, justify: gtk_json_parser_get_boolean (self: parser));
1471 break;
1472
1473 case LAYOUT_SINGLE_PARAGRAPH:
1474 pango_layout_set_single_paragraph_mode (layout, setting: gtk_json_parser_get_boolean (self: parser));
1475 break;
1476
1477 case LAYOUT_AUTO_DIR:
1478 pango_layout_set_auto_dir (layout, auto_dir: gtk_json_parser_get_boolean (self: parser));
1479 break;
1480
1481 case LAYOUT_ALIGNMENT:
1482 pango_layout_set_alignment (layout, alignment: (PangoAlignment) parser_select_string (parser, options: alignment_names));
1483 break;
1484
1485 case LAYOUT_WRAP:
1486 pango_layout_set_wrap (layout, wrap: (PangoWrapMode) parser_select_string (parser, options: wrap_names));
1487 break;
1488
1489 case LAYOUT_ELLIPSIZE:
1490 pango_layout_set_ellipsize (layout, ellipsize: (PangoEllipsizeMode) parser_select_string (parser, options: ellipsize_names));
1491 break;
1492
1493 case LAYOUT_WIDTH:
1494 pango_layout_set_width (layout, width: (int) gtk_json_parser_get_number (self: parser));
1495 break;
1496
1497 case LAYOUT_HEIGHT:
1498 pango_layout_set_height (layout, height: (int) gtk_json_parser_get_number (self: parser));
1499 break;
1500
1501 case LAYOUT_INDENT:
1502 pango_layout_set_indent (layout, indent: (int) gtk_json_parser_get_number (self: parser));
1503 break;
1504
1505 case LAYOUT_SPACING:
1506 pango_layout_set_spacing (layout, spacing: (int) gtk_json_parser_get_number (self: parser));
1507 break;
1508
1509 case LAYOUT_LINE_SPACING:
1510 pango_layout_set_line_spacing (layout, factor: gtk_json_parser_get_number (self: parser));
1511 break;
1512
1513 case LAYOUT_OUTPUT:
1514 break;
1515
1516 default:
1517 break;
1518 }
1519 }
1520 while (gtk_json_parser_next (self: parser));
1521
1522 gtk_json_parser_end (self: parser);
1523}
1524
1525enum {
1526 FONT_DESCRIPTION,
1527 FONT_CHECKSUM,
1528 FONT_VARIATIONS,
1529 FONT_FEATURES,
1530 FONT_MATRIX
1531};
1532
1533static const char *font_members[] = {
1534 "description",
1535 "checksum",
1536 "variations",
1537 "features",
1538 "matrix",
1539 NULL
1540};
1541
1542static PangoFont *
1543json_parser_load_font (GtkJsonParser *parser,
1544 PangoContext *context,
1545 GError **error)
1546{
1547 PangoFont *font = NULL;
1548
1549 gtk_json_parser_start_object (self: parser);
1550
1551 switch (gtk_json_parser_select_member (self: parser, options: font_members))
1552 {
1553 case FONT_DESCRIPTION:
1554 {
1555 PangoFontDescription *desc = parser_get_font_description (parser);
1556 font = pango_context_load_font (context, desc);
1557 pango_font_description_free (desc);
1558 }
1559 break;
1560
1561 default:
1562 break;
1563 }
1564
1565 gtk_json_parser_end (self: parser);
1566
1567 return font;
1568}
1569
1570/* }}} */
1571/* {{{ Public API */
1572
1573/**
1574 * pango_layout_serialize:
1575 * @layout: a `PangoLayout`
1576 * @flags: `PangoLayoutSerializeFlags`
1577 *
1578 * Serializes the @layout for later deserialization via [func@Pango.Layout.deserialize].
1579 *
1580 * There are no guarantees about the format of the output across different
1581 * versions of Pango and [func@Pango.Layout.deserialize] will reject data
1582 * that it cannot parse.
1583 *
1584 * The intended use of this function is testing, benchmarking and debugging.
1585 * The format is not meant as a permanent storage format.
1586 *
1587 * Returns: a `GBytes` containing the serialized form of @layout
1588 *
1589 * Since: 1.50
1590 */
1591GBytes *
1592pango_layout_serialize (PangoLayout *layout,
1593 PangoLayoutSerializeFlags flags)
1594{
1595 GString *str;
1596 GtkJsonPrinter *printer;
1597 char *data;
1598 gsize size;
1599
1600 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
1601
1602 str = g_string_new (init: "");
1603
1604 printer = gtk_json_printer_new (write_func: gstring_write, data: str, NULL);
1605 gtk_json_printer_set_flags (self: printer, flags: GTK_JSON_PRINTER_PRETTY);
1606 layout_to_json (printer, layout, flags);
1607 gtk_json_printer_free (self: printer);
1608
1609 g_string_append_c (str, '\n');
1610
1611 size = str->len;
1612 data = g_string_free (string: str, FALSE);
1613
1614 return g_bytes_new_take (data, size);
1615}
1616
1617/**
1618 * pango_layout_write_to_file:
1619 * @layout: a `PangoLayout`
1620 * @flags: `PangoLayoutSerializeFlags`
1621 * @filename: (type filename): the file to save it to
1622 * @error: Return location for a potential error
1623 *
1624 * A convenience method to serialize a layout to a file.
1625 *
1626 * It is equivalent to calling [method@Pango.Layout.serialize]
1627 * followed by [func@GLib.file_set_contents].
1628 *
1629 * See those two functions for details on the arguments.
1630 *
1631 * It is mostly intended for use inside a debugger to quickly dump
1632 * a layout to a file for later inspection.
1633 *
1634 * Returns: %TRUE if saving was successful
1635 *
1636 * Since: 1.50
1637 */
1638gboolean
1639pango_layout_write_to_file (PangoLayout *layout,
1640 PangoLayoutSerializeFlags flags,
1641 const char *filename,
1642 GError **error)
1643{
1644 GBytes *bytes;
1645 gboolean result;
1646
1647 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
1648 g_return_val_if_fail (filename != NULL, FALSE);
1649 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1650
1651 bytes = pango_layout_serialize (layout, flags);
1652 result = g_file_set_contents (filename,
1653 contents: g_bytes_get_data (bytes, NULL),
1654 length: g_bytes_get_size (bytes),
1655 error);
1656 g_bytes_unref (bytes);
1657
1658 return result;
1659
1660}
1661
1662/**
1663 * pango_layout_deserialize:
1664 * @context: a `PangoContext`
1665 * @flags: `PangoLayoutDeserializeFlags`
1666 * @bytes: the bytes containing the data
1667 * @error: return location for an error
1668 *
1669 * Loads data previously created via [method@Pango.Layout.serialize].
1670 *
1671 * For a discussion of the supported format, see that function.
1672 *
1673 * Note: to verify that the returned layout is identical to
1674 * the one that was serialized, you can compare @bytes to the
1675 * result of serializing the layout again.
1676 *
1677 * Returns: (nullable) (transfer full): a new `PangoLayout`
1678 *
1679 * Since: 1.50
1680 */
1681PangoLayout *
1682pango_layout_deserialize (PangoContext *context,
1683 GBytes *bytes,
1684 PangoLayoutDeserializeFlags flags,
1685 GError **error)
1686{
1687 PangoLayout *layout;
1688 GtkJsonParser *parser;
1689 const GError *parser_error;
1690
1691 g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
1692
1693 layout = pango_layout_new (context);
1694
1695 parser = gtk_json_parser_new_for_bytes (bytes);
1696 json_parser_fill_layout (parser, layout, flags);
1697
1698 parser_error = gtk_json_parser_get_error (self: parser);
1699
1700 if (parser_error)
1701 {
1702 gsize start, end;
1703 int code;
1704
1705 gtk_json_parser_get_error_offset (self: parser, start: &start, end: &end);
1706
1707 if (g_error_matches (error: parser_error, GTK_JSON_ERROR, code: GTK_JSON_ERROR_VALUE))
1708 code = PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE;
1709 else if (g_error_matches (error: parser_error, GTK_JSON_ERROR, code: GTK_JSON_ERROR_SCHEMA))
1710 code = PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE;
1711 else
1712 code = PANGO_LAYOUT_DESERIALIZE_INVALID;
1713
1714 g_set_error (err: error, PANGO_LAYOUT_DESERIALIZE_ERROR, code,
1715 format: "%ld:%ld: %s", start, end, parser_error->message);
1716
1717 g_clear_object (&layout);
1718 }
1719
1720 gtk_json_parser_free (self: parser);
1721
1722 return layout;
1723}
1724
1725/**
1726 * pango_font_serialize:
1727 * @font: a `PangoFont`
1728 *
1729 * Serializes the @font in a way that can be uniquely identified.
1730 *
1731 * There are no guarantees about the format of the output across different
1732 * versions of Pango.
1733 *
1734 * The intended use of this function is testing, benchmarking and debugging.
1735 * The format is not meant as a permanent storage format.
1736 *
1737 * To recreate a font from its serialized form, use [func@Pango.Font.deserialize].
1738 *
1739 * Returns: a `GBytes` containing the serialized form of @font
1740 *
1741 * Since: 1.50
1742 */
1743GBytes *
1744pango_font_serialize (PangoFont *font)
1745{
1746 GString *str;
1747 GtkJsonPrinter *printer;
1748 char *data;
1749 gsize size;
1750
1751 g_return_val_if_fail (PANGO_IS_FONT (font), NULL);
1752
1753 str = g_string_new (init: "");
1754
1755 printer = gtk_json_printer_new (write_func: gstring_write, data: str, NULL);
1756 gtk_json_printer_set_flags (self: printer, flags: GTK_JSON_PRINTER_PRETTY);
1757 add_font (printer, NULL, font);
1758 gtk_json_printer_free (self: printer);
1759
1760 size = str->len;
1761 data = g_string_free (string: str, FALSE);
1762
1763 return g_bytes_new_take (data, size);
1764}
1765
1766/**
1767 * pango_font_deserialize:
1768 * @context: a `PangoContext`
1769 * @bytes: the bytes containing the data
1770 * @error: return location for an error
1771 *
1772 * Loads data previously created via [method@Pango.Font.serialize].
1773 *
1774 * For a discussion of the supported format, see that function.
1775 *
1776 * Note: to verify that the returned font is identical to
1777 * the one that was serialized, you can compare @bytes to the
1778 * result of serializing the font again.
1779 *
1780 * Returns: (nullable) (transfer full): a new `PangoFont`
1781 *
1782 * Since: 1.50
1783 */
1784PangoFont *
1785pango_font_deserialize (PangoContext *context,
1786 GBytes *bytes,
1787 GError **error)
1788{
1789 PangoFont *font;
1790 GtkJsonParser *parser;
1791
1792 g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
1793
1794 parser = gtk_json_parser_new_for_bytes (bytes);
1795 font = json_parser_load_font (parser, context, error);
1796 gtk_json_parser_free (self: parser);
1797
1798 return font;
1799}
1800
1801/* }}} */
1802
1803/* vim:set foldmethod=marker expandtab: */
1804

source code of gtk/subprojects/pango/pango/serializer.c