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 | |
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
55 files changed, 1540 insertions, 344 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3b9f5200..6195501c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,7 @@ stages: variables: COMMON_MESON_FLAGS: "--fatal-meson-warnings --werror" MESON_TEST_TIMEOUT_MULTIPLIER: 2 - FEDORA_IMAGE: registry.gitlab.gnome.org/gnome/pango/fedora:v6 + FEDORA_IMAGE: registry.gitlab.gnome.org/gnome/pango/fedora:v7 .only-default: only: diff --git a/.gitlab-ci/fedora.Dockerfile b/.gitlab-ci/fedora.Dockerfile index ebb1e6b5..e905deee 100644 --- a/.gitlab-ci/fedora.Dockerfile +++ b/.gitlab-ci/fedora.Dockerfile @@ -26,6 +26,7 @@ RUN dnf -y install \ harfbuzz-devel \ hicolor-icon-theme \ itstool \ + json-glib-devel \ libasan \ lcov \ libthai-devel \ diff --git a/meson.build b/meson.build index 37bcf6cb..4a6b128c 100644 --- a/meson.build +++ b/meson.build @@ -232,6 +232,7 @@ harfbuzz_req_version = '>= 2.6.0' fontconfig_req_version = '>= 2.13.0' xft_req_version = '>= 2.0.0' cairo_req_version = '>= 1.12.10' +json_glib_version = '>= 1.6.0' # libm mathlib_dep = cc.find_library('m', required: false) @@ -251,6 +252,10 @@ fribidi_dep = dependency('fribidi', version: fribidi_req_version, default_options: ['docs=false']) pango_deps += fribidi_dep +json_glib_dep = dependency('json-glib-1.0', version: json_glib_version, + fallback: ['json-glib', 'json_glib_dep']) +pango_deps += json_glib_dep + thai_dep = dependency('libthai', version: libthai_req_version, required: get_option('libthai')) if thai_dep.found() pango_conf.set('HAVE_LIBTHAI', 1) @@ -19,6 +19,14 @@ fun:_cairo_ft_font_options_substitute } +{ + pango 3 + Memcheck:Leak + fun:calloc + ... + fun:pango_language_from_string +} + # Fontconfig { FcFontSetList @@ -84,6 +92,13 @@ ... fun:g_intern_static_string } +{ + glib GThread + Memcheck:Leak + fun:calloc + ... + fun:g_thread_new_internal +} # libthai { 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: */ diff --git a/subprojects/json-glib.wrap b/subprojects/json-glib.wrap new file mode 100644 index 00000000..b88acd72 --- /dev/null +++ b/subprojects/json-glib.wrap @@ -0,0 +1,5 @@ +[wrap-git] +directory=json-glib +url=https://gitlab.gnome.org/GNOME/json-glib.git +revision=master +depth=1 diff --git a/tests/layouts/valid-1.layout b/tests/layouts/valid-1.layout new file mode 100644 index 00000000..9289830b --- /dev/null +++ b/tests/layouts/valid-1.layout @@ -0,0 +1,20 @@ +{ + "text" : "This is a test of the automatic emergency brake!\n", + "attributes" : [ + { + "start" : 22, + "end" : 41, + "type" : "foreground", + "value" : "#00000000ffff" + }, + { + "start" : 22, + "end" : 41, + "type" : "underline", + "value" : "single" + } + ], + "font" : "Cantarell 11", + "ellipsize" : "end", + "width" : 225280 +} diff --git a/tests/layouts/valid-1.markup b/tests/layouts/valid-1.markup deleted file mode 100644 index 780e6831..00000000 --- a/tests/layouts/valid-1.markup +++ /dev/null @@ -1,2 +0,0 @@ -width=220,ellipsize=end -This is a test of the <span foreground="#0000ff" underline="single">automatic emergency</span> brake! diff --git a/tests/layouts/valid-10.layout b/tests/layouts/valid-10.layout new file mode 100644 index 00000000..0d36a38d --- /dev/null +++ b/tests/layouts/valid-10.layout @@ -0,0 +1,8 @@ +{ + "text" : "Hello שלום Γειά σας\n", + "attributes" : [], + "font" : "Cantarell 11", + "justify" : true, + "ellipsize" : "end", + "width" : 102400 +} diff --git a/tests/layouts/valid-10.markup b/tests/layouts/valid-10.markup deleted file mode 100644 index 15bf693d..00000000 --- a/tests/layouts/valid-10.markup +++ /dev/null @@ -1,2 +0,0 @@ -width=100,justify=true,ellipsize=end -Hello שלום Γειά σας diff --git a/tests/layouts/valid-11.layout b/tests/layouts/valid-11.layout new file mode 100644 index 00000000..a044cc53 --- /dev/null +++ b/tests/layouts/valid-11.layout @@ -0,0 +1,42 @@ +{ + "text" : "double low error\n", + "attributes" : [ + { + "end" : 6, + "type" : "underline", + "value" : "double" + }, + { + "end" : 6, + "type" : "overline", + "value" : "single" + }, + { + "start" : 7, + "end" : 10, + "type" : "underline", + "value" : "low" + }, + { + "start" : 7, + "end" : 10, + "type" : "strikethrough", + "value" : true + }, + { + "start" : 11, + "end" : 16, + "type" : "underline", + "value" : "error" + }, + { + "start" : 11, + "end" : 16, + "type" : "rise", + "value" : 1024 + } + ], + "font" : "Cantarell 11", + "wrap" : "char", + "width" : 225280 +} diff --git a/tests/layouts/valid-11.markup b/tests/layouts/valid-11.markup deleted file mode 100644 index fbf1abd2..00000000 --- a/tests/layouts/valid-11.markup +++ /dev/null @@ -1,2 +0,0 @@ -width=220,wrap=char -<span underline='double' overline='single'>double</span> <span underline='low' strikethrough='true'>low</span> <span underline='error' rise='1024'>error</span> diff --git a/tests/layouts/valid-12.layout b/tests/layouts/valid-12.layout new file mode 100644 index 00000000..1c9fa227 --- /dev/null +++ b/tests/layouts/valid-12.layout @@ -0,0 +1,15 @@ +{ + "text" : "a b c d
e f g h\n", + "attributes" : [], + "font" : "Cantarell 11", + "tabs" : { + "positions-in-pixels" : true, + "positions" : [ + 0, + 50, + 100, + 150, + 200 + ] + } +} diff --git a/tests/layouts/valid-12.markup b/tests/layouts/valid-12.markup deleted file mode 100644 index b7306cc6..00000000 --- a/tests/layouts/valid-12.markup +++ /dev/null @@ -1,2 +0,0 @@ -wrap=word,tabs=0 50 100 150 200 -a b c d
e f g h diff --git a/tests/layouts/valid-13.layout b/tests/layouts/valid-13.layout new file mode 100644 index 00000000..a9f10f86 --- /dev/null +++ b/tests/layouts/valid-13.layout @@ -0,0 +1,16 @@ +{ + "text" : "a b c d
e f g h\n", + "attributes" : [], + "font" : "Cantarell 11", + "tabs" : { + "positions-in-pixels" : true, + "positions" : [ + 0, + 50, + 100, + 150, + 200 + ] + }, + "single-paragraph" : true +} diff --git a/tests/layouts/valid-13.markup b/tests/layouts/valid-13.markup deleted file mode 100644 index be2345d1..00000000 --- a/tests/layouts/valid-13.markup +++ /dev/null @@ -1,2 +0,0 @@ -wrap=word,tabs=0 50 100 150 200,single_paragraph=true -a b c d
e f g h diff --git a/tests/layouts/valid-14.layout b/tests/layouts/valid-14.layout new file mode 100644 index 00000000..116cc94f --- /dev/null +++ b/tests/layouts/valid-14.layout @@ -0,0 +1,8 @@ +{ + "text" : "你好 Hello שלום Γειά σας\n", + "attributes" : [], + "font" : "Cantarell 11", + "ellipsize" : "start", + "width" : 161792, + "line-spacing" : 1.5 +} diff --git a/tests/layouts/valid-14.markup b/tests/layouts/valid-14.markup deleted file mode 100644 index 1d556d26..00000000 --- a/tests/layouts/valid-14.markup +++ /dev/null @@ -1,2 +0,0 @@ -line_spacing=1.5,width=158,ellipsize=start -你好 Hello שלום Γειά σας diff --git a/tests/layouts/valid-15.layout b/tests/layouts/valid-15.layout new file mode 100644 index 00000000..9f584ba6 --- /dev/null +++ b/tests/layouts/valid-15.layout @@ -0,0 +1,8 @@ +{ + "text" : "Lets see if this text is long enough to wrap due to height limitations. It might, or it might not.\n", + "attributes" : [], + "font" : "Cantarell 11", + "ellipsize" : "end", + "width" : 153600, + "height" : 40960 +} diff --git a/tests/layouts/valid-15.markup b/tests/layouts/valid-15.markup deleted file mode 100644 index e3081c81..00000000 --- a/tests/layouts/valid-15.markup +++ /dev/null @@ -1,2 +0,0 @@ -ellipsize=end,height=40,width=150 -Lets see if this text is long enough to wrap due to height limitations. It might, or it might not. diff --git a/tests/layouts/valid-16.layout b/tests/layouts/valid-16.layout new file mode 100644 index 00000000..e2a82d96 --- /dev/null +++ b/tests/layouts/valid-16.layout @@ -0,0 +1,9 @@ +{ + "text" : "Lets see if this text is long enough to wrap due to height limitations. It might, or it might not.\n", + "attributes" : [], + "font" : "Cantarell 11", + "wrap" : "word-char", + "ellipsize" : "end", + "width" : 153600, + "height" : -2 +} diff --git a/tests/layouts/valid-16.markup b/tests/layouts/valid-16.markup deleted file mode 100644 index 91a8ae37..00000000 --- a/tests/layouts/valid-16.markup +++ /dev/null @@ -1,2 +0,0 @@ -wrap=word-char,ellipsize=end,width=150,height=-2 -Lets see if this text is long enough to wrap due to height limitations. It might, or it might not. diff --git a/tests/layouts/valid-17.layout b/tests/layouts/valid-17.layout new file mode 100644 index 00000000..062cc241 --- /dev/null +++ b/tests/layouts/valid-17.layout @@ -0,0 +1,7 @@ +{ + "text" : "some|bla|bla|bla\n", + "attributes" : [], + "font" : "Cantarell 11", + "justify" : true, + "width" : 102400 +} diff --git a/tests/layouts/valid-17.markup b/tests/layouts/valid-17.markup deleted file mode 100644 index d804c20a..00000000 --- a/tests/layouts/valid-17.markup +++ /dev/null @@ -1,2 +0,0 @@ -justify=true,width=100 -some|bla|bla|bla diff --git a/tests/layouts/valid-18.layout b/tests/layouts/valid-18.layout new file mode 100644 index 00000000..85e9a7d1 --- /dev/null +++ b/tests/layouts/valid-18.layout @@ -0,0 +1,11 @@ +{ + "text" : "some line breaks
and miscellaneous ignorables\n", + "attributes" : [ + { + "end" : 57, + "type" : "show", + "value" : 7 + } + ], + "font" : "Cantarell 11" +} diff --git a/tests/layouts/valid-18.markup b/tests/layouts/valid-18.markup deleted file mode 100644 index ab5f5f35..00000000 --- a/tests/layouts/valid-18.markup +++ /dev/null @@ -1,2 +0,0 @@ - -<span show="spaces|line-breaks|ignorables">some line breaks
and miscellaneous ignorables</span> diff --git a/tests/layouts/valid-19.layout b/tests/layouts/valid-19.layout new file mode 100644 index 00000000..32ad1283 --- /dev/null +++ b/tests/layouts/valid-19.layout @@ -0,0 +1,5 @@ +{ + "text" : " a⃠ 😊︎ 😊️ 🇩🇪 ✊ ✋🏾 0 # 🏴 ©\n", + "attributes" : [], + "font" : "Cantarell 11" +} diff --git a/tests/layouts/valid-19.markup b/tests/layouts/valid-19.markup deleted file mode 100644 index e48c9f0e..00000000 --- a/tests/layouts/valid-19.markup +++ /dev/null @@ -1,2 +0,0 @@ -# various Emoji segmentation cases - a⃠ 😊︎ 😊️ 🇩🇪 ✊ ✋🏾 0 # 🏴 © diff --git a/tests/layouts/valid-2.layout b/tests/layouts/valid-2.layout new file mode 100644 index 00000000..303b2b67 --- /dev/null +++ b/tests/layouts/valid-2.layout @@ -0,0 +1,18 @@ +{ + "text" : "test the blue drink after dinner\n", + "attributes" : [ + { + "start" : 9, + "end" : 13, + "type" : "style", + "value" : "italic" + }, + { + "start" : 20, + "end" : 25, + "type" : "underline", + "value" : "single" + } + ], + "font" : "Cantarell 11" +} diff --git a/tests/layouts/valid-2.markup b/tests/layouts/valid-2.markup deleted file mode 100644 index 677f26f8..00000000 --- a/tests/layouts/valid-2.markup +++ /dev/null @@ -1,2 +0,0 @@ - -test the <i>blue</i> drink <u>after</u> dinner diff --git a/tests/layouts/valid-20.layout b/tests/layouts/valid-20.layout new file mode 100644 index 00000000..bde84b6f --- /dev/null +++ b/tests/layouts/valid-20.layout @@ -0,0 +1,22 @@ +{ + "text" : "abcdef\n", + "attributes" : [ + { + "end" : 3, + "type" : "gravity", + "value" : "east" + }, + { + "end" : 3, + "type" : "gravity-hint", + "value" : "strong" + }, + { + "start" : 3, + "end" : 6, + "type" : "gravity", + "value" : "south" + } + ], + "font" : "Cantarell 11" +} diff --git a/tests/layouts/valid-20.markup b/tests/layouts/valid-20.markup deleted file mode 100644 index fe6480d7..00000000 --- a/tests/layouts/valid-20.markup +++ /dev/null @@ -1,2 +0,0 @@ -# exercise gravity handling -<span gravity='east' gravity_hint='strong'>abc</span><span gravity='south'>def</span> diff --git a/tests/layouts/valid-21.expected b/tests/layouts/valid-21.expected deleted file mode 100644 index 5999056f..00000000 --- a/tests/layouts/valid-21.expected +++ /dev/null @@ -1,36 +0,0 @@ -有一位住在石室裏的詩人叫施氏,abc, 愛吃獅子,決心要吃十隻獅子。 - ---- parameters - -wrapped: 0 -ellipsized: 0 -lines: 2 - ---- attributes - -range 0 2147483647 - ---- directions - -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - ---- cursor positions - -0(0) 3(0) 6(0) 9(0) 12(0) 15(0) 18(0) 21(0) 24(0) 27(0) 30(0) 33(0) 36(0) 39(0) 42(0) 45(0) 46(0) 47(0) 48(0) 49(0) 50(0) 53(0) 56(0) 59(0) 62(0) 65(0) 68(0) 71(0) 74(0) 77(0) 80(0) 83(0) 86(0) 89(0) 89(1) 93(0) - ---- lines - -i=1, index=0, paragraph-start=1, dir=ltr '有一位住在石室裏的詩人叫施氏,abc, 愛吃獅子,決心要吃十隻獅子。 -' -i=2, index=93, paragraph-start=1, dir=ltr '' - ---- runs - -i=1, index=0, chars=14, level=0, gravity=east, flags=1, font=OMITTED, script=han, language=xx, '有一位住在石室裏的詩人叫施氏' -i=2, index=42, chars=1, level=0, gravity=east, flags=1, font=OMITTED, script=han, language=xx, ',' -i=3, index=45, chars=5, level=0, gravity=south, flags=1, font=OMITTED, script=latin, language=en-us, 'abc, ' -i=4, index=50, chars=4, level=0, gravity=east, flags=1, font=OMITTED, script=han, language=xx, '愛吃獅子' -i=5, index=62, chars=1, level=0, gravity=east, flags=1, font=OMITTED, script=han, language=xx, ',' -i=6, index=65, chars=9, level=0, gravity=east, flags=1, font=OMITTED, script=han, language=xx, '決心要吃十隻獅子。' -i=7, index=92, no run, line end -i=8, index=93, no run, line end diff --git a/tests/layouts/valid-21.markup b/tests/layouts/valid-21.markup deleted file mode 100644 index 676a5e56..00000000 --- a/tests/layouts/valid-21.markup +++ /dev/null @@ -1,2 +0,0 @@ -gravity=east -有一位住在石室裏的詩人叫施氏,abc, 愛吃獅子,決心要吃十隻獅子。 diff --git a/tests/layouts/valid-22.layout b/tests/layouts/valid-22.layout new file mode 100644 index 00000000..be57d26a --- /dev/null +++ b/tests/layouts/valid-22.layout @@ -0,0 +1,84 @@ +{ + "text" : "e0 = ooo...\n", + "attributes" : [ + { + "start" : 1, + "end" : 2, + "type" : "font-desc", + "value" : "Italic" + }, + { + "start" : 1, + "end" : 2, + "type" : "font-scale", + "value" : "subscript" + }, + { + "start" : 1, + "end" : 2, + "type" : "baseline-shift", + "value" : "subscript" + }, + { + "start" : 6, + "end" : 11, + "type" : "font-scale", + "value" : "superscript" + }, + { + "start" : 6, + "end" : 11, + "type" : "baseline-shift", + "value" : "superscript" + }, + { + "start" : 7, + "end" : 11, + "type" : "font-scale", + "value" : "superscript" + }, + { + "start" : 7, + "end" : 11, + "type" : "baseline-shift", + "value" : "superscript" + }, + { + "start" : 8, + "end" : 11, + "type" : "font-scale", + "value" : "superscript" + }, + { + "start" : 8, + "end" : 11, + "type" : "baseline-shift", + "value" : "superscript" + }, + { + "start" : 9, + "end" : 11, + "type" : "font-scale", + "value" : "superscript" + }, + { + "start" : 9, + "end" : 11, + "type" : "baseline-shift", + "value" : "superscript" + }, + { + "start" : 10, + "end" : 11, + "type" : "font-scale", + "value" : "superscript" + }, + { + "start" : 10, + "end" : 11, + "type" : "baseline-shift", + "value" : "superscript" + } + ], + "font" : "Cantarell 11" +} diff --git a/tests/layouts/valid-22.markup b/tests/layouts/valid-22.markup deleted file mode 100644 index d761749a..00000000 --- a/tests/layouts/valid-22.markup +++ /dev/null @@ -1,2 +0,0 @@ - -e<span font_desc="italic"><sub>0</sub></span> = o<sup>o<sup>o<sup>.<sup>.<sup>.</sup></sup></sup></sup></sup> diff --git a/tests/layouts/valid-3.layout b/tests/layouts/valid-3.layout new file mode 100644 index 00000000..ef1b1917 --- /dev/null +++ b/tests/layouts/valid-3.layout @@ -0,0 +1,5 @@ +{ + "text" : "ABC😀️D\n", + "attributes" : [], + "font" : "Cantarell 11" +} diff --git a/tests/layouts/valid-3.markup b/tests/layouts/valid-3.markup deleted file mode 100644 index 4e394a42..00000000 --- a/tests/layouts/valid-3.markup +++ /dev/null @@ -1,2 +0,0 @@ - -ABC😀️D diff --git a/tests/layouts/valid-4.layout b/tests/layouts/valid-4.layout new file mode 100644 index 00000000..5504107d --- /dev/null +++ b/tests/layouts/valid-4.layout @@ -0,0 +1,6 @@ +{ + "text" : "This paragraph should actually have multiple lines, unlike all the other wannabe äöü paragraph tests in this ugh test-case. Grow some lines!\n", + "attributes" : [], + "font" : "Cantarell 11", + "width" : 198656 +} diff --git a/tests/layouts/valid-4.markup b/tests/layouts/valid-4.markup deleted file mode 100644 index d8890baf..00000000 --- a/tests/layouts/valid-4.markup +++ /dev/null @@ -1,2 +0,0 @@ -width=194 -This paragraph should actually have multiple lines, unlike all the other wannabe äöü paragraph tests in this ugh test-case. Grow some lines! diff --git a/tests/layouts/valid-5.layout b/tests/layouts/valid-5.layout new file mode 100644 index 00000000..e5e96b9a --- /dev/null +++ b/tests/layouts/valid-5.layout @@ -0,0 +1,13 @@ +{ + "text" : "A test with multiple paragraphs and with no-break attributes, which might trigger a crash.\nIf it doesn't the fix has worked.\n", + "attributes" : [ + { + "start" : 21, + "end" : 31, + "type" : "font-features", + "value" : "tnum=1" + } + ], + "font" : "Cantarell 11", + "width" : 194560 +} diff --git a/tests/layouts/valid-5.markup b/tests/layouts/valid-5.markup deleted file mode 100644 index 2b2023ad..00000000 --- a/tests/layouts/valid-5.markup +++ /dev/null @@ -1,3 +0,0 @@ -width=190 -A test with multiple <span font_features="tnum=1">paragraphs</span> and with no-break attributes, which might trigger a crash. -If it doesn't the fix has worked. diff --git a/tests/layouts/valid-6.layout b/tests/layouts/valid-6.layout new file mode 100644 index 00000000..4423d068 --- /dev/null +++ b/tests/layouts/valid-6.layout @@ -0,0 +1,5 @@ +{ + "text" : " 0️⃣ Keycap Digit Zero\n", + "attributes" : [], + "font" : "Cantarell 11" +} diff --git a/tests/layouts/valid-6.markup b/tests/layouts/valid-6.markup deleted file mode 100644 index 92c53e28..00000000 --- a/tests/layouts/valid-6.markup +++ /dev/null @@ -1,2 +0,0 @@ - - 0️⃣ Keycap Digit Zero diff --git a/tests/layouts/valid-7.layout b/tests/layouts/valid-7.layout new file mode 100644 index 00000000..b49484c6 --- /dev/null +++ b/tests/layouts/valid-7.layout @@ -0,0 +1,21 @@ +{ + "text" : "This is a test of the automatic emergency brake!\n", + "attributes" : [ + { + "start" : 22, + "end" : 41, + "type" : "foreground", + "value" : "#00000000ffff" + }, + { + "start" : 22, + "end" : 41, + "type" : "underline", + "value" : "single" + } + ], + "font" : "Cantarell 11", + "ellipsize" : "middle", + "width" : 204800, + "indent" : 51200 +} diff --git a/tests/layouts/valid-7.markup b/tests/layouts/valid-7.markup deleted file mode 100644 index d5f9821e..00000000 --- a/tests/layouts/valid-7.markup +++ /dev/null @@ -1,2 +0,0 @@ -width=200,indent=50,ellipsize=middle -This is a test of the <span foreground="#0000ff" underline="single">automatic emergency</span> brake! diff --git a/tests/layouts/valid-8.layout b/tests/layouts/valid-8.layout new file mode 100644 index 00000000..063ec370 --- /dev/null +++ b/tests/layouts/valid-8.layout @@ -0,0 +1,8 @@ +{ + "text" : "Hello שלום Γειά σας\n", + "attributes" : [], + "font" : "Cantarell 11", + "auto-dir" : false, + "alignment" : "center", + "spacing" : 51200 +} diff --git a/tests/layouts/valid-8.markup b/tests/layouts/valid-8.markup deleted file mode 100644 index 3272ea2b..00000000 --- a/tests/layouts/valid-8.markup +++ /dev/null @@ -1,2 +0,0 @@ -spacing=50,auto_dir=false,alignment=center -Hello שלום Γειά σας diff --git a/tests/layouts/valid-9.layout b/tests/layouts/valid-9.layout new file mode 100644 index 00000000..e3dfa57b --- /dev/null +++ b/tests/layouts/valid-9.layout @@ -0,0 +1,12 @@ +{ + "text" : "Hello שלום Γειά σας\n", + "attributes" : [ + { + "end" : 30, + "type" : "letter-spacing", + "value" : 8888 + } + ], + "font" : "Cantarell 11", + "width" : 102400 +} diff --git a/tests/layouts/valid-9.markup b/tests/layouts/valid-9.markup deleted file mode 100644 index ced8f8e5..00000000 --- a/tests/layouts/valid-9.markup +++ /dev/null @@ -1,2 +0,0 @@ -width=100 -<span letter_spacing="8888">Hello שלום Γειά σας</span> diff --git a/tests/test-layout.c b/tests/test-layout.c index cd60450e..7415efe7 100644 --- a/tests/test-layout.c +++ b/tests/test-layout.c @@ -219,176 +219,14 @@ dump_cursor_positions (PangoLayout *layout, GString *string) g_string_append (string, "\n"); } -typedef struct { - int width; - int height; - int indent; - int spacing; - float line_spacing; - PangoEllipsizeMode ellipsize; - PangoWrapMode wrap; - PangoAlignment alignment; - gboolean justify; - gboolean auto_dir; - gboolean single_paragraph; - PangoTabArray *tabs; - PangoGravity gravity; -} LayoutParams; - -static void -init_params (LayoutParams *params) -{ - params->width = -1; - params->height = -1; - params->indent = 0; - params->spacing = 0; - params->line_spacing = 0.0; - params->ellipsize = PANGO_ELLIPSIZE_NONE; - params->wrap = PANGO_WRAP_WORD; - params->alignment = PANGO_ALIGN_LEFT; - params->justify = FALSE; - params->auto_dir = TRUE; - params->single_paragraph = FALSE; - params->tabs = NULL; - params->gravity = PANGO_GRAVITY_AUTO; -} - -static void -parse_params (const char *str, - LayoutParams *params) -{ - char **strings; - int i; - GEnumClass *eclass; - GEnumValue *ev; - - strings = g_strsplit (str, ",", -1); - for (i = 0; strings[i]; i++) - { - char **str2 = g_strsplit (strings[i], "=", -1); - if (strcmp (str2[0], "width") == 0) - { - params->width = (int) g_ascii_strtoll (str2[1], NULL, 10); - } - else if (strcmp (str2[0], "height") == 0) - { - params->height = (int) g_ascii_strtoll (str2[1], NULL, 10); - } - else if (strcmp (str2[0], "indent") == 0) - { - params->indent = (int) g_ascii_strtoll (str2[1], NULL, 10); - } - else if (strcmp (str2[0], "spacing") == 0) - { - params->spacing = (int) g_ascii_strtoll (str2[1], NULL, 10); - } - else if (strcmp (str2[0], "line_spacing") == 0) - { - params->line_spacing = (float) g_ascii_strtod (str2[1], NULL); - } - else if (strcmp (str2[0], "ellipsize") == 0) - { - eclass = g_type_class_ref (PANGO_TYPE_ELLIPSIZE_MODE); - ev = g_enum_get_value_by_name (eclass, str2[1]); - if (!ev) - ev = g_enum_get_value_by_nick (eclass, str2[1]); - if (ev) - params->ellipsize = ev->value; - g_type_class_unref (eclass); - } - else if (strcmp (str2[0], "wrap") == 0) - { - eclass = g_type_class_ref (PANGO_TYPE_WRAP_MODE); - ev = g_enum_get_value_by_name (eclass, str2[1]); - if (!ev) - ev = g_enum_get_value_by_nick (eclass, str2[1]); - if (ev) - params->wrap = ev->value; - g_type_class_unref (eclass); - } - else if (strcmp (str2[0], "alignment") == 0) - { - eclass = g_type_class_ref (PANGO_TYPE_ALIGNMENT); - ev = g_enum_get_value_by_name (eclass, str2[1]); - if (!ev) - ev = g_enum_get_value_by_nick (eclass, str2[1]); - if (ev) - params->alignment = ev->value; - g_type_class_unref (eclass); - } - else if (strcmp (str2[0], "justify") == 0) - { - params->justify = g_str_equal (str2[1], "true"); - } - else if (strcmp (str2[0], "auto_dir") == 0) - { - params->auto_dir = g_str_equal (str2[1], "true"); - } - else if (strcmp (str2[0], "single_paragraph") == 0) - { - params->single_paragraph = g_str_equal (str2[1], "true"); - } - else if (strcmp (str2[0], "tabs") == 0) - { - char **str3 = g_strsplit (strings[i], " ", -1); - params->tabs = pango_tab_array_new (g_strv_length (str3), TRUE); - for (int j = 0; str3[j]; j++) - { - int tab = (int) g_ascii_strtoll (str3[j], NULL, 10); - pango_tab_array_set_tab (params->tabs, j, PANGO_TAB_LEFT, tab); - } - g_strfreev (str3); - } - else if (strcmp (str2[0], "gravity") == 0) - { - eclass = g_type_class_ref (PANGO_TYPE_GRAVITY); - ev = g_enum_get_value_by_name (eclass, str2[1]); - if (!ev) - ev = g_enum_get_value_by_nick (eclass, str2[1]); - if (ev) - params->gravity = ev->value; - g_type_class_unref (eclass); - } - - g_strfreev (str2); - } - g_strfreev (strings); -} - -#define assert_layout_changed(layout) \ - g_assert_cmpuint (pango_layout_get_serial (layout), !=, serial); \ - serial = pango_layout_get_serial (layout); - -#define assert_rectangle_equal(r1, r2) \ - g_assert_true((r1)->x == (r2)->x && \ - (r1)->y == (r2)->y && \ - (r1)->width == (r2)->width && \ - (r1)->height == (r2)->height) - -#define assert_rectangle_contained(r1, r2) \ - g_assert_true ((r1)->x >= (r2)->x && \ - (r1)->y >= (r2)->y && \ - (r1)->x + (r1)->width <= (r2)->x + (r2)->width && \ - (r1)->y + (r1)->height <= (r2)->y + (r2)->height) - -#define assert_rectangle_size_contained(r1, r2) \ - g_assert_true ((r1)->width <= (r2)->width && \ - (r1)->height <= (r2)->height) - static void test_file (const char *filename, GString *string) { char *contents; - char *markup; gsize length; + GBytes *bytes; GError *error = NULL; PangoLayout *layout; - char *p; - LayoutParams params; - PangoFontDescription *desc; - const PangoFontDescription *desc2; - guint serial; - PangoTabArray *tabs; if (context == NULL) context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); @@ -396,85 +234,12 @@ test_file (const char *filename, GString *string) g_file_get_contents (filename, &contents, &length, &error); g_assert_no_error (error); - p = strchr (contents, '\n'); - g_assert (p); - markup = p + 1; - *p = '\0'; - length = strlen (markup); - - init_params (¶ms); - - layout = pango_layout_new (context); - - serial = pango_layout_get_serial (layout); - g_assert_cmpuint (serial, !=, 0); - - /* Check initial values */ - g_assert_cmpint (pango_layout_get_width (layout), ==, params.width); - g_assert_cmpint (pango_layout_get_height (layout), ==, params.height); - g_assert_cmpint (pango_layout_get_indent (layout), ==, params.indent); - g_assert_cmpint (pango_layout_get_spacing (layout), ==, params.spacing); - g_assert_cmpfloat (pango_layout_get_line_spacing (layout), ==, params.line_spacing); - g_assert_cmpint (pango_layout_get_ellipsize (layout), ==, params.ellipsize); - g_assert_cmpint (pango_layout_get_wrap (layout), ==, params.wrap); - g_assert_cmpint (pango_layout_get_alignment (layout), ==, params.alignment); - g_assert_cmpint (pango_layout_get_justify (layout), ==, params.justify); - g_assert_cmpint (pango_layout_get_auto_dir (layout), ==, params.auto_dir); - g_assert_cmpint (pango_layout_get_single_paragraph_mode (layout), ==, params.single_paragraph); - - g_assert_cmpstr (pango_layout_get_text (layout), ==, ""); - g_assert_null (pango_layout_get_attributes (layout)); - g_assert_null (pango_layout_get_tabs (layout)); - g_assert_null (pango_layout_get_font_description (layout)); - g_assert_cmpint (pango_layout_is_ellipsized (layout), ==, FALSE); - g_assert_cmpint (pango_layout_is_wrapped (layout), ==, FALSE); - - desc = pango_font_description_from_string ("Cantarell 11"); - pango_layout_set_font_description (layout, desc); - desc2 = pango_layout_get_font_description (layout); - g_assert_true (pango_font_description_equal (desc, desc2)); - pango_font_description_free (desc); - assert_layout_changed (layout); - - pango_layout_set_markup (layout, markup, length); - assert_layout_changed (layout); - - parse_params (contents, ¶ms); - - pango_context_set_base_gravity (context, params.gravity); - - pango_layout_set_width (layout, params.width > 0 ? params.width * PANGO_SCALE : -1); - pango_layout_set_height (layout, params.height > 0 ? params.height * PANGO_SCALE : params.height); - pango_layout_set_indent (layout, params.indent * PANGO_SCALE); - pango_layout_set_spacing (layout, params.spacing * PANGO_SCALE); - pango_layout_set_line_spacing (layout, params.line_spacing); - pango_layout_set_ellipsize (layout, params.ellipsize); - pango_layout_set_wrap (layout, params.wrap); - pango_layout_set_alignment (layout, params.alignment); - pango_layout_set_justify (layout, params.justify); - pango_layout_set_auto_dir (layout, params.auto_dir); - pango_layout_set_single_paragraph_mode (layout, params.single_paragraph); - pango_layout_set_tabs (layout, params.tabs); - - /* Check the values we set */ - g_assert_cmpint (pango_layout_get_width (layout), ==, params.width > 0 ? params.width * PANGO_SCALE : -1); - g_assert_cmpint (pango_layout_get_height (layout), ==, params.height > 0 ? params.height * PANGO_SCALE : params.height); - g_assert_cmpint (pango_layout_get_indent (layout), ==, params.indent * PANGO_SCALE); - g_assert_cmpint (pango_layout_get_spacing (layout), ==, params.spacing * PANGO_SCALE); - g_assert_cmpfloat (pango_layout_get_line_spacing (layout), ==, params.line_spacing); - g_assert_cmpint (pango_layout_get_ellipsize (layout), ==, params.ellipsize); - g_assert_cmpint (pango_layout_get_wrap (layout), ==, params.wrap); - g_assert_cmpint (pango_layout_get_alignment (layout), ==, params.alignment); - g_assert_cmpint (pango_layout_get_justify (layout), ==, params.justify); - g_assert_cmpint (pango_layout_get_auto_dir (layout), ==, params.auto_dir); - g_assert_cmpint (pango_layout_get_single_paragraph_mode (layout), ==, params.single_paragraph); - - tabs = pango_layout_get_tabs (layout); - g_assert_true ((tabs == NULL) == (params.tabs == NULL)); - if (tabs) - pango_tab_array_free (tabs); - - g_assert_cmpint (pango_layout_get_character_count (layout), ==, g_utf8_strlen (pango_layout_get_text (layout), -1)); + bytes = g_bytes_new_take (contents, length); + + layout = pango_layout_deserialize (context, bytes, &error); + g_assert_no_error (error); + + g_bytes_unref (bytes); /* generate the dumps */ g_string_append (string, pango_layout_get_text (layout)); @@ -484,13 +249,11 @@ test_file (const char *filename, GString *string) g_string_append_printf (string, "wrapped: %d\n", pango_layout_is_wrapped (layout)); g_string_append_printf (string, "ellipsized: %d\n", pango_layout_is_ellipsized (layout)); g_string_append_printf (string, "lines: %d\n", pango_layout_get_line_count (layout)); - if (params.width > 0) + if (pango_layout_get_width (layout) > 0) g_string_append_printf (string, "width: %d\n", pango_layout_get_width (layout)); - - if (params.height > 0) + if (pango_layout_get_height (layout) > 0) g_string_append_printf (string, "height: %d\n", pango_layout_get_height (layout)); - - if (params.indent != 0) + if (pango_layout_get_indent (layout) != 0) g_string_append_printf (string, "indent: %d\n", pango_layout_get_indent (layout)); g_string_append (string, "\n--- attributes\n\n"); @@ -509,19 +272,15 @@ test_file (const char *filename, GString *string) dump_runs (layout, string); g_object_unref (layout); - g_free (contents); - - if (params.tabs) - pango_tab_array_free (params.tabs); } static gchar * -get_expected_filename (const gchar *filename) +get_expected_filename (const char *filename) { - gchar *f, *p, *expected; + char *f, *p, *expected; f = g_strdup (filename); - p = strstr (f, ".markup"); + p = strstr (f, ".layout"); if (p) *p = 0; expected = g_strconcat (f, ".expected", NULL); @@ -534,11 +293,11 @@ get_expected_filename (const gchar *filename) static void test_layout (gconstpointer d) { - const gchar *filename = d; - gchar *expected_file; + const char *filename = d; + char *expected_file; GError *error = NULL; GString *dump; - gchar *diff; + char *diff; PangoFontFamily **families; int n_families; gboolean found_cantarell; @@ -616,7 +375,7 @@ main (int argc, char *argv[]) GDir *dir; GError *error = NULL; const gchar *name; - gchar *path; + char *path; GOptionContext *option_context; GOptionEntry entries[] = { { "show-fonts", '0', 0, G_OPTION_ARG_NONE, &opt_show_font, "Print font names in dumps", NULL }, @@ -659,7 +418,7 @@ main (int argc, char *argv[]) g_assert_no_error (error); while ((name = g_dir_read_name (dir)) != NULL) { - if (!strstr (name, "markup")) + if (!g_str_has_suffix (name, ".layout")) continue; path = g_strdup_printf ("/layout/%s", name); diff --git a/tests/testserialize.c b/tests/testserialize.c index cc6c67bc..5fb7543a 100644 --- a/tests/testserialize.c +++ b/tests/testserialize.c @@ -21,6 +21,7 @@ #include "config.h" #include <glib.h> #include <pango/pangocairo.h> +#include <gio/gio.h> static void test_serialize_attr_list (void) @@ -122,6 +123,191 @@ test_serialize_tab_array (void) } } +static void +test_serialize_layout_minimal (void) +{ + const char *test = + "{\n" + " \"text\" : \"Almost nothing\"\n" + "}"; + + PangoContext *context; + GBytes *bytes; + PangoLayout *layout; + GError *error = NULL; + GBytes *out_bytes; + const char *str; + + context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); + + bytes = g_bytes_new_static (test, -1); + + layout = pango_layout_deserialize (context, bytes, &error); + g_assert_no_error (error); + g_assert_true (PANGO_IS_LAYOUT (layout)); + g_assert_cmpstr (pango_layout_get_text (layout), ==, "Almost nothing"); + g_assert_null (pango_layout_get_attributes (layout)); + g_assert_null (pango_layout_get_tabs (layout)); + g_assert_null (pango_layout_get_font_description (layout)); + g_assert_cmpint (pango_layout_get_alignment (layout), ==, PANGO_ALIGN_LEFT); + g_assert_cmpint (pango_layout_get_width (layout), ==, -1); + + out_bytes = pango_layout_serialize (layout); + str = g_bytes_get_data (out_bytes, NULL); + + g_assert_cmpstr (str, ==, test); + + g_bytes_unref (out_bytes); + + g_object_unref (layout); + g_bytes_unref (bytes); + g_object_unref (context); +} + +static void +test_serialize_layout_valid (void) +{ + const char *test = + "{\n" + " \"text\" : \"Some fun with layouts!\",\n" + " \"attributes\" : [\n" + " {\n" + " \"end\" : 4,\n" + " \"type\" : \"foreground\",\n" + " \"value\" : \"#ffff00000000\"\n" + " },\n" + " {\n" + " \"start\" : 5,\n" + " \"end\" : 8,\n" + " \"type\" : \"foreground\",\n" + " \"value\" : \"#000080800000\"\n" + " },\n" + " {\n" + " \"start\" : 14,\n" + " \"type\" : \"foreground\",\n" + " \"value\" : \"#ffff0000ffff\"\n" + " }\n" + " ],\n" + " \"font\" : \"Sans Bold 32\",\n" + " \"tabs\" : {\n" + " \"positions-in-pixels\" : true,\n" + " \"positions\" : [\n" + " 0,\n" + " 50,\n" + " 100\n" + " ]\n" + " },\n" + " \"alignment\" : \"center\",\n" + " \"width\" : 350000,\n" + " \"line-spacing\" : 1.5\n" + "}"; + + PangoContext *context; + GBytes *bytes; + PangoLayout *layout; + PangoTabArray *tabs; + GError *error = NULL; + GBytes *out_bytes; + const char *str; + char *s; + + context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); + + bytes = g_bytes_new_static (test, -1); + + layout = pango_layout_deserialize (context, bytes, &error); + g_assert_no_error (error); + g_assert_true (PANGO_IS_LAYOUT (layout)); + g_assert_cmpstr (pango_layout_get_text (layout), ==, "Some fun with layouts!"); + g_assert_nonnull (pango_layout_get_attributes (layout)); + tabs = pango_layout_get_tabs (layout); + g_assert_nonnull (tabs); + pango_tab_array_free (tabs); + s = pango_font_description_to_string (pango_layout_get_font_description (layout)); + g_assert_cmpstr (s, ==, "Sans Bold 32"); + g_free (s); + g_assert_cmpint (pango_layout_get_alignment (layout), ==, PANGO_ALIGN_CENTER); + g_assert_cmpint (pango_layout_get_width (layout), ==, 350000); + g_assert_cmpfloat_with_epsilon (pango_layout_get_line_spacing (layout), 1.5, 0.0001); + + out_bytes = pango_layout_serialize (layout); + str = g_bytes_get_data (out_bytes, NULL); + + g_assert_cmpstr (str, ==, test); + + g_bytes_unref (out_bytes); + + g_object_unref (layout); + g_bytes_unref (bytes); + g_object_unref (context); +} + +static void +test_serialize_layout_invalid (void) +{ + struct { + const char *json; + int expected_error; + } test[] = { + { + "{\n" + " \"attributes\" : [\n" + " {\n" + " \"type\" : \"caramba\"\n" + " }\n" + " ]\n" + "}", + PANGO_LAYOUT_SERIALIZE_INVALID_VALUE + }, + { + "{\n" + " \"attributes\" : [\n" + " {\n" + " \"type\" : \"weight\"\n" + " }\n" + " ]\n" + "}", + PANGO_LAYOUT_SERIALIZE_MISSING_VALUE + }, + { + "{\n" + " \"attributes\" : [\n" + " {\n" + " \"type\" : \"alignment\",\n" + " \"value\" : \"nonsense\"\n" + " }\n" + " ]\n" + "}", + PANGO_LAYOUT_SERIALIZE_INVALID_VALUE + }, + { + "{\n" + " \"alignment\" : \"nonsense\"\n" + "}", + PANGO_LAYOUT_SERIALIZE_INVALID_VALUE + } + }; + + PangoContext *context; + + context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); + + for (int i = 0; i < G_N_ELEMENTS (test); i++) + { + GBytes *bytes; + PangoLayout *layout; + GError *error = NULL; + + bytes = g_bytes_new_static (test[i].json, -1); + layout = pango_layout_deserialize (context, bytes, &error); + g_assert_null (layout); + g_assert_error (error, PANGO_LAYOUT_SERIALIZE_ERROR, test[i].expected_error); + g_bytes_unref (bytes); + g_clear_error (&error); + } + + g_object_unref (context); +} int main (int argc, char *argv[]) { @@ -129,6 +315,9 @@ main (int argc, char *argv[]) g_test_add_func ("/serialize/attr-list", test_serialize_attr_list); g_test_add_func ("/serialize/tab-array", test_serialize_tab_array); + g_test_add_func ("/serialize/layout/minimal", test_serialize_layout_minimal); + g_test_add_func ("/serialize/layout/valid", test_serialize_layout_valid); + g_test_add_func ("/serialize/layout/invalid", test_serialize_layout_invalid); return g_test_run (); } |