diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-11-18 19:31:16 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-11-18 19:31:16 +0000 |
commit | f82c7d11f098d3e4d26c24bc7decf475aed2109d (patch) | |
tree | 4a249ef9f4b350dc8a3990041021164a82f6bc4f /pango | |
parent | 97300279f62855dc4ef286ed8bd5d363a8b8f710 (diff) | |
parent | 43f78996eef19a5b141a604937c36e0121187000 (diff) | |
download | pango-f82c7d11f098d3e4d26c24bc7decf475aed2109d.tar.gz |
Merge branch 'serializer2' into 'main'
Add layout serialization api
See merge request GNOME/pango!513
Diffstat (limited to 'pango')
-rw-r--r-- | pango/meson.build | 1 | ||||
-rw-r--r-- | pango/pango-attributes.c | 5 | ||||
-rw-r--r-- | pango/pango-layout.h | 25 | ||||
-rw-r--r-- | pango/serializer.c | 935 |
4 files changed, 963 insertions, 3 deletions
diff --git a/pango/meson.build b/pango/meson.build index 084de229..7c7bb280 100644 --- a/pango/meson.build +++ b/pango/meson.build @@ -27,6 +27,7 @@ pango_sources = [ 'pango-utils.c', 'reorder-items.c', 'shape.c', + 'serializer.c', ] pango_headers = [ diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c index 13468963..cfaf9b17 100644 --- a/pango/pango-attributes.c +++ b/pango/pango-attributes.c @@ -2802,7 +2802,7 @@ pango_attr_list_from_string (const char *text) p = endp + strspn (endp, " "); - endp = (char *)strpbrk (p, " "); + endp = (char *)p + strcspn (p, " "); attr_type = get_attr_type_by_nick (p, endp - p); p = endp + strspn (endp, " "); @@ -2929,8 +2929,7 @@ pango_attr_list_from_string (const char *text) break; case PANGO_ATTR_SHAPE: - endp = (char *)strpbrk (p, ",\n"); - p = endp + strspn (endp, " "); + endp = (char *)p + strcspn (p, ",\n"); continue; /* FIXME */ case PANGO_ATTR_SCALE: diff --git a/pango/pango-layout.h b/pango/pango-layout.h index e28f9295..502f5e29 100644 --- a/pango/pango-layout.h +++ b/pango/pango-layout.h @@ -351,6 +351,31 @@ GSList * pango_layout_get_lines (PangoLayout *layout); PANGO_AVAILABLE_IN_1_16 GSList * pango_layout_get_lines_readonly (PangoLayout *layout); +#define PANGO_LAYOUT_SERIALIZE_ERROR (pango_layout_serialize_error_quark ()) + +typedef enum { + PANGO_LAYOUT_SERIALIZE_INVALID, + PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, + PANGO_LAYOUT_SERIALIZE_INVALID_VALUE, + PANGO_LAYOUT_SERIALIZE_MISSING_VALUE, +} PangoLayoutSerializeError; + +PANGO_AVAILABLE_IN_1_50 +GQuark pango_layout_serialize_error_quark (void); + +PANGO_AVAILABLE_IN_1_50 +GBytes * pango_layout_serialize (PangoLayout *layout); + +PANGO_AVAILABLE_IN_1_50 +PangoLayout * pango_layout_deserialize (PangoContext *context, + GBytes *bytes, + GError **error); + +PANGO_AVAILABLE_IN_1_50 +gboolean pango_layout_write_to_file (PangoLayout *layout, + const char *filename, + GError **error); + #define PANGO_TYPE_LAYOUT_LINE (pango_layout_line_get_type ()) diff --git a/pango/serializer.c b/pango/serializer.c new file mode 100644 index 00000000..576e6cf6 --- /dev/null +++ b/pango/serializer.c @@ -0,0 +1,935 @@ +/* Pango + * serializer.c: Code to serialize various Pango objects + * + * Copyright (C) 2021 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <pango/pango-layout.h> +#include <pango/pango-layout-private.h> +#include <pango/pango-enum-types.h> + +#include <json-glib/json-glib.h> + +/* {{{ Error handling */ + +G_DEFINE_QUARK(pango-layout-serialize-error-quark, pango_layout_serialize_error) + +/* }}} */ +/* {{{ Serialization */ + +static GType +get_enum_type (PangoAttrType attr_type) +{ + switch ((int)attr_type) + { + case PANGO_ATTR_STYLE: return PANGO_TYPE_STYLE; + case PANGO_ATTR_WEIGHT: return PANGO_TYPE_WEIGHT; + case PANGO_ATTR_VARIANT: return PANGO_TYPE_VARIANT; + case PANGO_ATTR_STRETCH: return PANGO_TYPE_STRETCH; + case PANGO_ATTR_GRAVITY: return PANGO_TYPE_GRAVITY; + case PANGO_ATTR_GRAVITY_HINT: return PANGO_TYPE_GRAVITY_HINT; + case PANGO_ATTR_UNDERLINE: return PANGO_TYPE_UNDERLINE; + case PANGO_ATTR_OVERLINE: return PANGO_TYPE_OVERLINE; + case PANGO_ATTR_BASELINE_SHIFT: return PANGO_TYPE_BASELINE_SHIFT; + case PANGO_ATTR_FONT_SCALE: return PANGO_TYPE_FONT_SCALE; + case PANGO_ATTR_TEXT_TRANSFORM: return PANGO_TYPE_TEXT_TRANSFORM; + default: return G_TYPE_INVALID; + } +} + +static void +add_enum_value (JsonBuilder *builder, + GType type, + int value, + gboolean allow_extra) +{ + GEnumClass *enum_class; + GEnumValue *enum_value; + + enum_class = g_type_class_ref (type); + enum_value = g_enum_get_value (enum_class, value); + + if (enum_value) + json_builder_add_string_value (builder, enum_value->value_nick); + else if (allow_extra) + { + char buf[128]; + g_snprintf (buf, 128, "%d", value); + json_builder_add_string_value (builder, buf); + } + else + json_builder_add_string_value (builder, "ERROR"); + +} + +static void +add_attribute (JsonBuilder *builder, + PangoAttribute *attr) +{ + char *str; + + json_builder_begin_object (builder); + + if (attr->start_index != PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING) + { + json_builder_set_member_name (builder, "start"); + json_builder_add_int_value (builder, (int)attr->start_index); + } + if (attr->end_index != PANGO_ATTR_INDEX_TO_TEXT_END) + { + json_builder_set_member_name (builder, "end"); + json_builder_add_int_value (builder, (int)attr->end_index); + } + json_builder_set_member_name (builder, "type"); + add_enum_value (builder, PANGO_TYPE_ATTR_TYPE, attr->klass->type, FALSE); + + json_builder_set_member_name (builder, "value"); + switch (attr->klass->type) + { + default: + case PANGO_ATTR_INVALID: + g_assert_not_reached (); + case PANGO_ATTR_LANGUAGE: + json_builder_add_string_value (builder, pango_language_to_string (((PangoAttrLanguage*)attr)->value)); + break; + case PANGO_ATTR_FAMILY: + case PANGO_ATTR_FONT_FEATURES: + json_builder_add_string_value (builder, ((PangoAttrString*)attr)->value); + break; + case PANGO_ATTR_STYLE: + case PANGO_ATTR_VARIANT: + case PANGO_ATTR_STRETCH: + case PANGO_ATTR_UNDERLINE: + case PANGO_ATTR_OVERLINE: + case PANGO_ATTR_GRAVITY: + case PANGO_ATTR_GRAVITY_HINT: + case PANGO_ATTR_TEXT_TRANSFORM: + case PANGO_ATTR_FONT_SCALE: + add_enum_value (builder, get_enum_type (attr->klass->type), ((PangoAttrInt*)attr)->value, FALSE); + break; + case PANGO_ATTR_WEIGHT: + case PANGO_ATTR_BASELINE_SHIFT: + add_enum_value (builder, get_enum_type (attr->klass->type), ((PangoAttrInt*)attr)->value, TRUE); + break; + case PANGO_ATTR_SIZE: + case PANGO_ATTR_RISE: + case PANGO_ATTR_LETTER_SPACING: + case PANGO_ATTR_ABSOLUTE_SIZE: + case PANGO_ATTR_FOREGROUND_ALPHA: + case PANGO_ATTR_BACKGROUND_ALPHA: + case PANGO_ATTR_SHOW: + case PANGO_ATTR_WORD: + case PANGO_ATTR_SENTENCE: + case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: + json_builder_add_int_value (builder, ((PangoAttrInt*)attr)->value); + break; + case PANGO_ATTR_FONT_DESC: + str = pango_font_description_to_string (((PangoAttrFontDesc*)attr)->desc); + json_builder_add_string_value (builder, str); + g_free (str); + break; + case PANGO_ATTR_FOREGROUND: + case PANGO_ATTR_BACKGROUND: + case PANGO_ATTR_UNDERLINE_COLOR: + case PANGO_ATTR_OVERLINE_COLOR: + case PANGO_ATTR_STRIKETHROUGH_COLOR: + str = pango_color_to_string (&((PangoAttrColor*)attr)->color); + json_builder_add_string_value (builder, str); + g_free (str); + break; + case PANGO_ATTR_STRIKETHROUGH: + case PANGO_ATTR_FALLBACK: + case PANGO_ATTR_ALLOW_BREAKS: + case PANGO_ATTR_INSERT_HYPHENS: + json_builder_add_boolean_value (builder, ((PangoAttrInt*)attr)->value != 0); + break; + case PANGO_ATTR_SHAPE: + json_builder_add_string_value (builder, "shape"); + break; + case PANGO_ATTR_SCALE: + case PANGO_ATTR_LINE_HEIGHT: + json_builder_add_double_value (builder, ((PangoAttrFloat*)attr)->value); + } + + json_builder_end_object (builder); +} + +static void +add_attr_list (JsonBuilder *builder, + PangoAttrList *attrs) +{ + GSList *attributes, *l; + + json_builder_begin_array (builder); + + attributes = pango_attr_list_get_attributes (attrs); + for (l = attributes; l; l = l->next) + { + PangoAttribute *attr = l->data; + add_attribute (builder, attr); + } + g_slist_free_full (attributes, (GDestroyNotify) pango_attribute_destroy); + + json_builder_end_array (builder); +} + +static void +add_tab_array (JsonBuilder *builder, + PangoTabArray *tabs) +{ + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "positions-in-pixels"); + json_builder_add_boolean_value (builder, pango_tab_array_get_positions_in_pixels (tabs)); + json_builder_set_member_name (builder, "positions"); + json_builder_begin_array (builder); + for (int i = 0; i < pango_tab_array_get_size (tabs); i++) + { + int pos; + pango_tab_array_get_tab (tabs, i, NULL, &pos); + json_builder_add_int_value (builder, pos); + } + json_builder_end_array (builder); + + json_builder_end_object (builder); +} + +static JsonNode * +layout_to_json (PangoLayout *layout) +{ + JsonBuilder *builder; + JsonNode *root; + + builder = json_builder_new_immutable (); + + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "text"); + json_builder_add_string_value (builder, layout->text); + + if (layout->attrs) + { + json_builder_set_member_name (builder, "attributes"); + add_attr_list (builder, layout->attrs); + } + + if (layout->font_desc) + { + char *str = pango_font_description_to_string (layout->font_desc); + json_builder_set_member_name (builder, "font"); + json_builder_add_string_value (builder, str); + g_free (str); + } + + if (layout->tabs) + { + json_builder_set_member_name (builder, "tabs"); + add_tab_array (builder, layout->tabs); + } + + if (layout->justify) + { + json_builder_set_member_name (builder, "justify"); + json_builder_add_boolean_value (builder, TRUE); + } + + if (layout->justify_last_line) + { + json_builder_set_member_name (builder, "justify-last-line"); + json_builder_add_boolean_value (builder, TRUE); + } + + if (layout->single_paragraph) + { + json_builder_set_member_name (builder, "single-paragraph"); + json_builder_add_boolean_value (builder, TRUE); + } + + if (!layout->auto_dir) + { + json_builder_set_member_name (builder, "auto-dir"); + json_builder_add_boolean_value (builder, FALSE); + } + + if (layout->alignment != PANGO_ALIGN_LEFT) + { + json_builder_set_member_name (builder, "alignment"); + add_enum_value (builder, PANGO_TYPE_ALIGNMENT, layout->alignment, FALSE); + } + + if (layout->wrap != PANGO_WRAP_WORD) + { + json_builder_set_member_name (builder, "wrap"); + add_enum_value (builder, PANGO_TYPE_WRAP_MODE, layout->wrap, FALSE); + } + + if (layout->ellipsize != PANGO_ELLIPSIZE_NONE) + { + json_builder_set_member_name (builder, "ellipsize"); + add_enum_value (builder, PANGO_TYPE_ELLIPSIZE_MODE, layout->ellipsize, FALSE); + } + + if (layout->width != -1) + { + json_builder_set_member_name (builder, "width"); + json_builder_add_int_value (builder, layout->width); + } + + if (layout->height != -1) + { + json_builder_set_member_name (builder, "height"); + json_builder_add_int_value (builder, layout->height); + } + + if (layout->indent != 0) + { + json_builder_set_member_name (builder, "indent"); + json_builder_add_int_value (builder, layout->indent); + } + + if (layout->spacing != 0) + { + json_builder_set_member_name (builder, "spacing"); + json_builder_add_int_value (builder, layout->spacing); + } + + if (layout->line_spacing != 0.) + { + json_builder_set_member_name (builder, "line-spacing"); + json_builder_add_double_value (builder, layout->line_spacing); + } + + json_builder_end_object (builder); + + root = json_builder_get_root (builder); + g_object_unref (builder); + + return root; +} + +/* }}} */ +/* {{{ Deserialization */ + +static int +get_enum_value (GType type, + const char *str, + gboolean allow_extra) +{ + GEnumClass *enum_class; + GEnumValue *enum_value; + + enum_class = g_type_class_ref (type); + enum_value = g_enum_get_value_by_nick (enum_class, str); + + if (enum_value) + return enum_value->value; + + if (allow_extra) + { + gint64 value; + char *endp; + + value = g_ascii_strtoll (str, &endp, 10); + if (*endp == '\0') + return value; + } + + return -1; +} + +static PangoAttribute * +json_to_attribute (JsonReader *reader, + GError **error) +{ + PangoAttribute *attr = NULL; + PangoAttrType type = PANGO_ATTR_INVALID; + guint start = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING; + guint end = PANGO_ATTR_INDEX_TO_TEXT_END; + PangoFontDescription *desc; + PangoColor color; + + if (!json_reader_is_object (reader)) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, + "Attribute must be a Json object"); + return NULL; + } + + if (json_reader_read_member (reader, "start")) + start = json_reader_get_int_value (reader); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "end")) + end = json_reader_get_int_value (reader); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "type")) + { + type = get_enum_value (PANGO_TYPE_ATTR_TYPE, json_reader_get_string_value (reader), FALSE); + if (type == -1 || type == PANGO_ATTR_INVALID) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_INVALID_VALUE, + "Attribute \"type\" invalid: %s", + json_reader_get_string_value (reader)); + return NULL; + } + json_reader_end_member (reader); + } + else + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_MISSING_VALUE, + "Attribute \"type\" missing"); + json_reader_end_member (reader); + return NULL; + } + + if (!json_reader_read_member (reader, "value")) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_MISSING_VALUE, + "Attribute \"value\" missing"); + json_reader_end_member (reader); + return NULL; + } + + switch (type) + { + default: + case PANGO_ATTR_INVALID: + g_assert_not_reached (); + + case PANGO_ATTR_LANGUAGE: + attr = pango_attr_language_new (pango_language_from_string (json_reader_get_string_value (reader))); + break; + case PANGO_ATTR_FAMILY: + attr = pango_attr_family_new (json_reader_get_string_value (reader)); + break; + case PANGO_ATTR_STYLE: + attr = pango_attr_style_new (get_enum_value (PANGO_TYPE_STYLE, json_reader_get_string_value (reader), FALSE)); + break; + case PANGO_ATTR_WEIGHT: + attr = pango_attr_weight_new (get_enum_value (PANGO_TYPE_WEIGHT, json_reader_get_string_value (reader), TRUE)); + break; + case PANGO_ATTR_VARIANT: + attr = pango_attr_variant_new (get_enum_value (PANGO_TYPE_VARIANT, json_reader_get_string_value (reader), FALSE)); + break; + case PANGO_ATTR_STRETCH: + attr = pango_attr_stretch_new (get_enum_value (PANGO_TYPE_STRETCH, json_reader_get_string_value (reader), FALSE)); + break; + case PANGO_ATTR_SIZE: + attr = pango_attr_size_new (json_reader_get_int_value (reader)); + break; + case PANGO_ATTR_FONT_DESC: + desc = pango_font_description_from_string (json_reader_get_string_value (reader)); + attr = pango_attr_font_desc_new (desc); + pango_font_description_free (desc); + break; + case PANGO_ATTR_FOREGROUND: + pango_color_parse (&color, json_reader_get_string_value (reader)); + attr = pango_attr_foreground_new (color.red, color.green, color.blue); + break; + case PANGO_ATTR_BACKGROUND: + pango_color_parse (&color, json_reader_get_string_value (reader)); + attr = pango_attr_background_new (color.red, color.green, color.blue); + break; + case PANGO_ATTR_UNDERLINE: + attr = pango_attr_underline_new (get_enum_value (PANGO_TYPE_UNDERLINE, json_reader_get_string_value (reader), FALSE)); + break; + case PANGO_ATTR_STRIKETHROUGH: + attr = pango_attr_strikethrough_new (json_reader_get_boolean_value (reader)); + break; + case PANGO_ATTR_RISE: + attr = pango_attr_rise_new (json_reader_get_int_value (reader)); + break; + case PANGO_ATTR_SHAPE: + /* FIXME */ + attr = pango_attr_shape_new (&(PangoRectangle) { 0, 0, 0, 0}, &(PangoRectangle) { 0, 0, 0, 0}); + break; + case PANGO_ATTR_SCALE: + attr = pango_attr_scale_new (json_reader_get_double_value (reader)); + break; + case PANGO_ATTR_FALLBACK: + attr = pango_attr_fallback_new (json_reader_get_boolean_value (reader)); + break; + case PANGO_ATTR_LETTER_SPACING: + attr = pango_attr_letter_spacing_new (json_reader_get_int_value (reader)); + break; + case PANGO_ATTR_UNDERLINE_COLOR: + pango_color_parse (&color, json_reader_get_string_value (reader)); + attr = pango_attr_underline_color_new (color.red, color.green, color.blue); + break; + case PANGO_ATTR_STRIKETHROUGH_COLOR: + pango_color_parse (&color, json_reader_get_string_value (reader)); + attr = pango_attr_strikethrough_color_new (color.red, color.green, color.blue); + break; + case PANGO_ATTR_ABSOLUTE_SIZE: + attr = pango_attr_size_new_absolute (json_reader_get_int_value (reader)); + break; + case PANGO_ATTR_GRAVITY: + attr = pango_attr_gravity_new (get_enum_value (PANGO_TYPE_GRAVITY, json_reader_get_string_value (reader), FALSE)); + break; + case PANGO_ATTR_GRAVITY_HINT: + attr = pango_attr_gravity_hint_new (get_enum_value (PANGO_TYPE_GRAVITY_HINT, json_reader_get_string_value (reader), FALSE)); + break; + case PANGO_ATTR_FONT_FEATURES: + attr = pango_attr_font_features_new (json_reader_get_string_value (reader)); + break; + case PANGO_ATTR_FOREGROUND_ALPHA: + attr = pango_attr_foreground_alpha_new (json_reader_get_int_value (reader)); + break; + case PANGO_ATTR_BACKGROUND_ALPHA: + attr = pango_attr_background_alpha_new (json_reader_get_int_value (reader)); + break; + case PANGO_ATTR_ALLOW_BREAKS: + attr = pango_attr_allow_breaks_new (json_reader_get_boolean_value (reader)); + break; + case PANGO_ATTR_SHOW: + attr = pango_attr_show_new (json_reader_get_int_value (reader)); + break; + case PANGO_ATTR_INSERT_HYPHENS: + attr = pango_attr_insert_hyphens_new (json_reader_get_boolean_value (reader)); + break; + case PANGO_ATTR_OVERLINE: + attr = pango_attr_overline_new (get_enum_value (PANGO_TYPE_OVERLINE, json_reader_get_string_value (reader), FALSE)); + break; + case PANGO_ATTR_OVERLINE_COLOR: + pango_color_parse (&color, json_reader_get_string_value (reader)); + attr = pango_attr_overline_color_new (color.red, color.green, color.blue); + break; + case PANGO_ATTR_LINE_HEIGHT: + attr = pango_attr_line_height_new (json_reader_get_double_value (reader)); + break; + case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: + attr = pango_attr_line_height_new_absolute (json_reader_get_int_value (reader)); + break; + case PANGO_ATTR_TEXT_TRANSFORM: + attr = pango_attr_text_transform_new (get_enum_value (PANGO_TYPE_TEXT_TRANSFORM, json_reader_get_string_value (reader), FALSE)); + break; + case PANGO_ATTR_WORD: + attr = pango_attr_word_new (); + break; + case PANGO_ATTR_SENTENCE: + attr = pango_attr_sentence_new (); + break; + case PANGO_ATTR_BASELINE_SHIFT: + attr = pango_attr_baseline_shift_new (get_enum_value (PANGO_TYPE_BASELINE_SHIFT, json_reader_get_string_value (reader), TRUE)); + break; + case PANGO_ATTR_FONT_SCALE: + attr = pango_attr_font_scale_new (get_enum_value (PANGO_TYPE_BASELINE_SHIFT, json_reader_get_string_value (reader), FALSE)); + break; + } + + attr->start_index = start; + attr->end_index = end; + + json_reader_end_member (reader); + + return attr; +} + +static PangoAttrList * +json_to_attr_list (JsonReader *reader, + GError **error) +{ + PangoAttrList *attributes; + + if (!json_reader_is_array (reader)) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, + "\"attributes\" must be a Json array"); + goto fail; + } + + attributes = pango_attr_list_new (); + + for (int i = 0; i < json_reader_count_elements (reader); i++) + { + PangoAttribute *attr; + json_reader_read_element (reader, i); + attr = json_to_attribute (reader, error); + if (!attr) + goto fail; + pango_attr_list_insert (attributes, attr); + json_reader_end_element (reader); + } + + return attributes; + +fail: + if (attributes) + pango_attr_list_unref (attributes); + return NULL; +} + +static PangoTabArray * +json_to_tab_array (JsonReader *reader, + GError **error) +{ + PangoTabArray *tabs; + gboolean positions_in_pixels = FALSE; + + if (json_reader_read_member (reader, "positions-in-pixels")) + positions_in_pixels = json_reader_get_boolean_value (reader); + json_reader_end_member (reader); + + tabs = pango_tab_array_new (0, positions_in_pixels); + + if (json_reader_read_member (reader, "positions")) + { + if (!json_reader_is_array (reader)) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, + PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, + "Tab \"positions\" must be a Json array"); + goto fail; + } + + pango_tab_array_resize (tabs, json_reader_count_elements (reader)); + for (int i = 0; i < json_reader_count_elements (reader); i++) + { + int pos; + json_reader_read_element (reader, i); + pos = json_reader_get_int_value (reader); + pango_tab_array_set_tab (tabs, i, PANGO_TAB_LEFT, pos); + json_reader_end_element (reader); + } + } + json_reader_end_member (reader); + + return tabs; + +fail: + if (tabs) + pango_tab_array_free (tabs); + return NULL; +} + +static PangoLayout * +json_to_layout (PangoContext *context, + JsonNode *node, + GError **error) +{ + JsonReader *reader; + PangoLayout *layout; + + layout = pango_layout_new (context); + + reader = json_reader_new (node); + if (!json_reader_is_object (reader)) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, + PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, + "Layout must be a Json object"); + goto fail; + } + + if (json_reader_read_member (reader, "text")) + pango_layout_set_text (layout, json_reader_get_string_value (reader), -1); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "attributes")) + { + PangoAttrList *attributes; + + attributes = json_to_attr_list (reader, error); + + if (!attributes) + goto fail; + + pango_layout_set_attributes (layout, attributes); + pango_attr_list_unref (attributes); + } + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "font")) + { + PangoFontDescription *desc; + + desc = pango_font_description_from_string ( json_reader_get_string_value (reader)); + if (!desc) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, + PANGO_LAYOUT_SERIALIZE_INVALID_VALUE, + "Could not parse \"font\" value: %s", + json_reader_get_string_value (reader)); + goto fail; + } + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + } + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "tabs")) + { + PangoTabArray *tabs; + + tabs = json_to_tab_array (reader, error); + + if (!tabs) + goto fail; + + pango_layout_set_tabs (layout, tabs); + pango_tab_array_free (tabs); + } + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "justify")) + pango_layout_set_justify (layout, json_reader_get_boolean_value (reader)); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "justify-last-line")) + pango_layout_set_justify_last_line (layout, json_reader_get_boolean_value (reader)); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "single-paragraph")) + pango_layout_set_single_paragraph_mode (layout, json_reader_get_boolean_value (reader)); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "auto-dir")) + pango_layout_set_auto_dir (layout, json_reader_get_boolean_value (reader)); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "alignment")) + { + PangoAlignment align = get_enum_value (PANGO_TYPE_ALIGNMENT, + json_reader_get_string_value (reader), + FALSE); + if (align == -1) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, + PANGO_LAYOUT_SERIALIZE_INVALID_VALUE, + "Could not parse \"alignment\" value: %s", + json_reader_get_string_value (reader)); + goto fail; + } + + pango_layout_set_alignment (layout, align); + } + + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "wrap")) + { + PangoWrapMode wrap = get_enum_value (PANGO_TYPE_WRAP_MODE, + json_reader_get_string_value (reader), + FALSE); + + if (wrap == -1) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, + PANGO_LAYOUT_SERIALIZE_INVALID_VALUE, + "Could not parse \"wrap\" value: %s", + json_reader_get_string_value (reader)); + goto fail; + } + + pango_layout_set_wrap (layout, wrap); + } + + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "ellipsize")) + { + PangoEllipsizeMode ellipsize = get_enum_value (PANGO_TYPE_ELLIPSIZE_MODE, + json_reader_get_string_value (reader), + FALSE); + + if (ellipsize == -1) + { + g_set_error (error, + PANGO_LAYOUT_SERIALIZE_ERROR, + PANGO_LAYOUT_SERIALIZE_INVALID_VALUE, + "Could not parse \"ellipsize\" value: %s", + json_reader_get_string_value (reader)); + goto fail; + } + + pango_layout_set_ellipsize (layout, ellipsize); + } + + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "width")) + pango_layout_set_width (layout, json_reader_get_int_value (reader)); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "height")) + pango_layout_set_height (layout, json_reader_get_int_value (reader)); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "indent")) + pango_layout_set_indent (layout, json_reader_get_int_value (reader)); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "spacing")) + pango_layout_set_spacing (layout, json_reader_get_int_value (reader)); + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "line-spacing")) + pango_layout_set_line_spacing (layout, json_reader_get_double_value (reader)); + json_reader_end_member (reader); + + g_object_unref (reader); + + return layout; + +fail: + g_object_unref (reader); + g_object_unref (layout); + return NULL; +} + + /* }}} */ + /* {{{ Public API */ + +/** + * pango_layout_serialize: + * @layout: a `PangoLayout` + * + * Serializes the @layout for later deserialization via [method@Pango.Layout.deserialize]. + * + * There are no guarantees about the format of the output accross different + * versions of Pango and [method@Pango.Layout.deserialize] will reject data + * that it cannot parse. + * + * The intended use of this function is testing, benchmarking and debugging. + * The format is not meant as a permanent storage format. + * + * Returns: a `GBytes` containing the serialized form of @layout + * + * Since: 1.50 + */ +GBytes * +pango_layout_serialize (PangoLayout *layout) +{ + JsonGenerator *generator; + JsonNode *node; + char *data; + gsize size; + + node = layout_to_json (layout); + + generator = json_generator_new (); + json_generator_set_pretty (generator, TRUE); + json_generator_set_indent (generator, 2); + + json_generator_set_root (generator, node); + data = json_generator_to_data (generator, &size); + + json_node_free (node); + g_object_unref (generator); + + return g_bytes_new_take (data, size); +} + +/** + * pango_layout_write_to_file: + * @layout: a `PangoLayout` + * @filename: (type filename): the file to save it to + * @error: Return location for a potential error + * + * This function is equivalent to calling [method@Pango.Layout.serialize] + * followed by g_file_set_contents(). + * + * See those two functions for details on the arguments. + * + * It is mostly intended for use inside a debugger to quickly dump + * a layout to a file for later inspection. + * + * Returns: %TRUE if saving was successful + * + * Since: 1.50 + */ +gboolean +pango_layout_write_to_file (PangoLayout *layout, + const char *filename, + GError **error) +{ + GBytes *bytes; + gboolean result; + + g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + bytes = pango_layout_serialize (layout); + result = g_file_set_contents (filename, + g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes), + error); + g_bytes_unref (bytes); + + return result; + +} + +/** + * pango_layout_deserialize: + * @context: a `PangoContext` + * @bytes: the bytes containing the data + * @error: return location for an error + * + * Loads data previously created via [method@Pango.Layout.serialize]. + * + * For a discussion of the supported format, see that function. + * + * Returns: (nullable) (transfer full): a new `PangoLayout` + * + * Since: 1.50 + */ +PangoLayout * +pango_layout_deserialize (PangoContext *context, + GBytes *bytes, + GError **error) +{ + JsonParser *parser; + JsonNode *node; + PangoLayout *layout; + + parser = json_parser_new_immutable (); + if (!json_parser_load_from_data (parser, + g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes), + error)) + { + g_object_unref (parser); + return NULL; + } + + node = json_parser_get_root (parser); + layout = json_to_layout (context, node, error); + + g_object_unref (parser); + + return layout; +} + +/* }}} */ + +/* vim:set foldmethod=marker expandtab: */ |