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 | |
36 | G_DEFINE_QUARK(pango-layout-deserialize-error-quark, pango_layout_deserialize_error) |
37 | |
38 | /* }}} */ |
39 | /* {{{ Enum names */ |
40 | |
41 | static const char *style_names[] = { |
42 | "normal" , |
43 | "oblique" , |
44 | "italic" , |
45 | NULL |
46 | }; |
47 | |
48 | static 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 | |
59 | static 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 | |
72 | static 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 | |
84 | static const char *overline_names[] = { |
85 | "none" , |
86 | "single" , |
87 | NULL |
88 | }; |
89 | |
90 | static const char *gravity_names[] = { |
91 | "south" , |
92 | "east" , |
93 | "north" , |
94 | "west" , |
95 | "auto" , |
96 | NULL |
97 | }; |
98 | |
99 | static const char *gravity_hint_names[] = { |
100 | "natural" , |
101 | "strong" , |
102 | "line" , |
103 | NULL |
104 | }; |
105 | |
106 | static const char *text_transform_names[] = { |
107 | "none" , |
108 | "lowercase" , |
109 | "uppercase" , |
110 | "capitalize" , |
111 | NULL |
112 | }; |
113 | |
114 | static const char *baseline_shift_names[] = { |
115 | "none" , |
116 | "superscript" , |
117 | "subscript" , |
118 | NULL |
119 | }; |
120 | |
121 | static const char *font_scale_names[] = { |
122 | "none" , |
123 | "superscript" , |
124 | "subscript" , |
125 | "small-caps" , |
126 | NULL |
127 | }; |
128 | |
129 | static 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 | |
145 | static int named_weights[] = { 100, 200, 300, 350, 380, 400, 500, 600, 700, 800, 900, 1000 }; |
146 | |
147 | static int |
148 | get_weight (int pos) |
149 | { |
150 | return named_weights[pos]; |
151 | } |
152 | |
153 | static const char * |
154 | get_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 | |
165 | static 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 | |
207 | static const char * |
208 | get_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 | |
223 | static const char *tab_align_names[] = { |
224 | "left" , |
225 | "right" , |
226 | "center" , |
227 | "decimal" , |
228 | NULL |
229 | }; |
230 | |
231 | static 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 | |
242 | static const char *alignment_names[] = { |
243 | "left" , |
244 | "center" , |
245 | "right" , |
246 | NULL |
247 | }; |
248 | |
249 | static const char *wrap_names[] = { |
250 | "word" , |
251 | "char" , |
252 | "word-char" , |
253 | NULL |
254 | }; |
255 | |
256 | static const char *ellipsize_names[] = { |
257 | "none" , |
258 | "start" , |
259 | "middle" , |
260 | "end" , |
261 | NULL |
262 | }; |
263 | |
264 | /* }}} */ |
265 | /* {{{ Serialization */ |
266 | |
267 | static void |
268 | add_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 | |
392 | static void |
393 | add_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 | |
418 | static void |
419 | add_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 | |
445 | static void |
446 | add_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 | |
488 | static void |
489 | add_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 | |
537 | static void |
538 | add_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 | |
635 | static void |
636 | add_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 | |
709 | static void |
710 | add_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 | |
731 | static void |
732 | add_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 | |
759 | static void |
760 | layout_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 | |
830 | static void |
831 | gstring_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 | |
842 | static int |
843 | parser_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 | |
867 | static PangoFontDescription * |
868 | parser_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 | |
881 | static void |
882 | parser_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 | |
896 | static PangoAttribute * |
897 | attr_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 | |
1088 | enum { |
1089 | ATTR_START, |
1090 | ATTR_END, |
1091 | ATTR_TYPE, |
1092 | ATTR_VALUE |
1093 | }; |
1094 | |
1095 | static const char *attr_members[] = { |
1096 | "start" , |
1097 | "end" , |
1098 | "type" , |
1099 | "value" , |
1100 | NULL |
1101 | }; |
1102 | |
1103 | static PangoAttribute * |
1104 | json_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 | |
1147 | static void |
1148 | json_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 | |
1164 | enum { |
1165 | TAB_POSITION, |
1166 | TAB_ALIGNMENT, |
1167 | TAB_DECIMAL_POINT |
1168 | }; |
1169 | |
1170 | static const char *tab_members[] = { |
1171 | "position" , |
1172 | "alignment" , |
1173 | "decimal-point" , |
1174 | NULL, |
1175 | }; |
1176 | |
1177 | |
1178 | static void |
1179 | json_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 | |
1232 | enum { |
1233 | TABS_POSITIONS_IN_PIXELS, |
1234 | TABS_POSITIONS |
1235 | }; |
1236 | |
1237 | static const char *tabs_members[] = { |
1238 | "positions-in-pixels" , |
1239 | "positions" , |
1240 | NULL |
1241 | }; |
1242 | |
1243 | static void |
1244 | json_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 | |
1270 | enum { |
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 | |
1280 | static 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 | |
1291 | static void |
1292 | json_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 | |
1365 | enum { |
1366 | LAYOUT_CONTEXT, |
1367 | , |
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 | |
1387 | static 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 | |
1410 | static void |
1411 | json_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 | |
1525 | enum { |
1526 | FONT_DESCRIPTION, |
1527 | FONT_CHECKSUM, |
1528 | FONT_VARIATIONS, |
1529 | FONT_FEATURES, |
1530 | FONT_MATRIX |
1531 | }; |
1532 | |
1533 | static const char *font_members[] = { |
1534 | "description" , |
1535 | "checksum" , |
1536 | "variations" , |
1537 | "features" , |
1538 | "matrix" , |
1539 | NULL |
1540 | }; |
1541 | |
1542 | static PangoFont * |
1543 | json_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 | */ |
1591 | GBytes * |
1592 | pango_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 | */ |
1638 | gboolean |
1639 | pango_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 | */ |
1681 | PangoLayout * |
1682 | pango_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 | */ |
1743 | GBytes * |
1744 | pango_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 | */ |
1784 | PangoFont * |
1785 | pango_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 | |