diff options
Diffstat (limited to 'pango')
-rw-r--r-- | pango/fonts.c | 17 | ||||
-rw-r--r-- | pango/pango-font-private.h | 5 | ||||
-rw-r--r-- | pango/pango-font.h | 3 | ||||
-rw-r--r-- | pango/pango-layout.h | 78 | ||||
-rw-r--r-- | pango/pangofc-font.c | 22 | ||||
-rw-r--r-- | pango/serializer.c | 899 | ||||
-rw-r--r-- | pango/shape.c | 8 |
7 files changed, 915 insertions, 117 deletions
diff --git a/pango/fonts.c b/pango/fonts.c index 9d3c1bf3..d94feaf8 100644 --- a/pango/fonts.c +++ b/pango/fonts.c @@ -1752,6 +1752,13 @@ pango_font_default_get_face (PangoFont *font) } static void +pango_font_default_get_matrix (PangoFont *font, + PangoMatrix *matrix) +{ + *matrix = (PangoMatrix) PANGO_MATRIX_INIT; +} + +static void pango_font_class_init (PangoFontClass *class G_GNUC_UNUSED) { GObjectClass *object_class = G_OBJECT_CLASS (class); @@ -1766,6 +1773,7 @@ pango_font_class_init (PangoFontClass *class G_GNUC_UNUSED) pclass->get_scale_factors = pango_font_default_get_scale_factors; pclass->has_char = pango_font_default_has_char; pclass->get_face = pango_font_default_get_face; + pclass->get_matrix = pango_font_default_get_matrix; } static void @@ -2717,6 +2725,15 @@ pango_font_get_languages (PangoFont *font) return pclass->get_languages (font); } +void +pango_font_get_matrix (PangoFont *font, + PangoMatrix *matrix) +{ + PangoFontClassPrivate *pclass = PANGO_FONT_GET_CLASS_PRIVATE (font); + + pclass->get_matrix (font, matrix); +} + gboolean pango_font_is_hinted (PangoFont *font) { diff --git a/pango/pango-font-private.h b/pango/pango-font-private.h index 93ce27aa..1e38371d 100644 --- a/pango/pango-font-private.h +++ b/pango/pango-font-private.h @@ -45,12 +45,17 @@ typedef struct { gboolean (* has_char) (PangoFont *font, gunichar wc); PangoFontFace * (* get_face) (PangoFont *font); + void (* get_matrix) (PangoFont *font, + PangoMatrix *matrix); } PangoFontClassPrivate; gboolean pango_font_is_hinted (PangoFont *font); void pango_font_get_scale_factors (PangoFont *font, double *x_scale, double *y_scale); +void pango_font_get_matrix (PangoFont *font, + PangoMatrix *matrix); + G_END_DECLS diff --git a/pango/pango-font.h b/pango/pango-font.h index c56fb792..bfe4bc31 100644 --- a/pango/pango-font.h +++ b/pango/pango-font.h @@ -634,6 +634,9 @@ hb_font_t * pango_font_get_hb_font (PangoFont *font); PANGO_AVAILABLE_IN_1_50 PangoLanguage ** pango_font_get_languages (PangoFont *font); +PANGO_AVAILABLE_IN_1_50 +GBytes * pango_font_serialize (PangoFont *font); + /** * PANGO_GLYPH_EMPTY: * diff --git a/pango/pango-layout.h b/pango/pango-layout.h index 3b725adc..32dc16f0 100644 --- a/pango/pango-layout.h +++ b/pango/pango-layout.h @@ -351,30 +351,78 @@ 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 ()) - +/** + * PangoLayoutSerializeFlags: + * @PANGO_LAYOUT_SERIALIZE_DEFAULT: Default behavior + * @PANGO_LAYOUT_SERIALIZE_CONTEXT: Include context information + * @PANGO_LAYOUT_SERIALIZE_OUTPUT: Include information about the formatted output + * + * Flags that influence the behavior of [method@Pango.Layout.serialize]. + * + * New members may be added to this enumeration over time. + */ typedef enum { - PANGO_LAYOUT_SERIALIZE_INVALID, - PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, - PANGO_LAYOUT_SERIALIZE_INVALID_VALUE, - PANGO_LAYOUT_SERIALIZE_MISSING_VALUE, -} PangoLayoutSerializeError; + PANGO_LAYOUT_SERIALIZE_DEFAULT = 0, + PANGO_LAYOUT_SERIALIZE_CONTEXT = 1 << 0, + PANGO_LAYOUT_SERIALIZE_OUTPUT = 1 << 1, +} PangoLayoutSerializeFlags; PANGO_AVAILABLE_IN_1_50 -GQuark pango_layout_serialize_error_quark (void); +GBytes * pango_layout_serialize (PangoLayout *layout, + PangoLayoutSerializeFlags flags); PANGO_AVAILABLE_IN_1_50 -GBytes * pango_layout_serialize (PangoLayout *layout); +gboolean pango_layout_write_to_file (PangoLayout *layout, + PangoLayoutSerializeFlags flags, + + const char *filename, + GError **error); + +#define PANGO_LAYOUT_DESERIALIZE_ERROR (pango_layout_deserialize_error_quark ()) + +/** + * PangoLayoutDeserializeError: + * @PANGO_LAYOUT_SERIALIZE_INVALID: Unspecified error + * @PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX: The serialized data had + * the wrong structure (e.g. a member was expected to be a JSon object, + * but was an array) + * @PANGO_LAYOUT_SERIALIZE_INVALID_VALUE: A JSon value could not be + * interpreted + * @PANGO_LAYOUT_SERIALIZE_MISSING_VALUE: A required JSon member was + * not found + * + * Errors that can be returned by [func@Pango.Layout.deserialize]. + */ +typedef enum { + PANGO_LAYOUT_DESERIALIZE_INVALID, + PANGO_LAYOUT_DESERIALIZE_INVALID_SYNTAX, + PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE, + PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE, +} PangoLayoutDeserializeError; PANGO_AVAILABLE_IN_1_50 -PangoLayout * pango_layout_deserialize (PangoContext *context, - GBytes *bytes, - GError **error); +GQuark pango_layout_deserialize_error_quark (void); + +/** + * PangoLayoutDeserializeFlags: + * @PANGO_LAYOUT_DESERIALIZE_DEFAULT: Default behavior + * @PANGO_LAYOUT_DESERIALIZE_CONTEXT: Apply context information + * from the serialization to the `PangoContext` + * + * Flags that influence the behavior of [method@Pango.Layout.deserialize]. + * + * New members may be added to this enumeration over time. + */ +typedef enum { + PANGO_LAYOUT_DESERIALIZE_DEFAULT = 0, + PANGO_LAYOUT_DESERIALIZE_CONTEXT = 1 << 0, +} PangoLayoutDeserializeFlags; PANGO_AVAILABLE_IN_1_50 -gboolean pango_layout_write_to_file (PangoLayout *layout, - const char *filename, - GError **error); +PangoLayout * pango_layout_deserialize (PangoContext *context, + GBytes *bytes, + PangoLayoutDeserializeFlags flags, + GError **error); #define PANGO_TYPE_LAYOUT_LINE (pango_layout_line_get_type ()) diff --git a/pango/pangofc-font.c b/pango/pangofc-font.c index 74503b91..5635e0be 100644 --- a/pango/pangofc-font.c +++ b/pango/pangofc-font.c @@ -75,6 +75,8 @@ static gboolean _pango_fc_font_is_hinted (PangoFont *fon static void _pango_fc_font_get_scale_factors (PangoFont *font, double *x_scale, double *y_scale); +static void pango_fc_font_get_matrix (PangoFont *font, + PangoMatrix *matrix); #define PANGO_FC_FONT_LOCK_FACE(font) (PANGO_FC_FONT_GET_CLASS (font)->lock_face (font)) #define PANGO_FC_FONT_UNLOCK_FACE(font) (PANGO_FC_FONT_GET_CLASS (font)->unlock_face (font)) @@ -109,6 +111,7 @@ pango_fc_font_class_init (PangoFcFontClass *class) pclass->get_languages = _pango_fc_font_get_languages; pclass->is_hinted = _pango_fc_font_is_hinted; pclass->get_scale_factors = _pango_fc_font_get_scale_factors; + pclass->get_matrix = pango_fc_font_get_matrix; /** * PangoFcFont:pattern: @@ -1110,3 +1113,22 @@ _pango_fc_font_get_scale_factors (PangoFont *font, pango_matrix_get_font_scale_factors (&fcfont->matrix, x_scale, y_scale); } + +static void +pango_fc_font_get_matrix (PangoFont *font, + PangoMatrix *matrix) +{ + PangoFcFont *fcfont = PANGO_FC_FONT (font); + FcMatrix fc_matrix, *fc_matrix_val; + + FcMatrixInit (&fc_matrix); + for (int i = 0; FcPatternGetMatrix (fcfont->font_pattern, FC_MATRIX, i, &fc_matrix_val) == FcResultMatch; i++) + FcMatrixMultiply (&fc_matrix, &fc_matrix, fc_matrix_val); + + matrix->xx = fc_matrix.xx; + matrix->xy = - fc_matrix.xy; + matrix->yx = - fc_matrix.yx; + matrix->yy = fc_matrix.yy; + matrix->x0 = 0.; + matrix->y0 = 0.; +} diff --git a/pango/serializer.c b/pango/serializer.c index 576e6cf6..6e394a1c 100644 --- a/pango/serializer.c +++ b/pango/serializer.c @@ -23,13 +23,16 @@ #include <pango/pango-layout.h> #include <pango/pango-layout-private.h> +#include <pango/pango-context-private.h> #include <pango/pango-enum-types.h> +#include <pango/pango-font-private.h> +#include <hb-ot.h> #include <json-glib/json-glib.h> /* {{{ Error handling */ -G_DEFINE_QUARK(pango-layout-serialize-error-quark, pango_layout_serialize_error) +G_DEFINE_QUARK(pango-layout-deserialize-error-quark, pango_layout_deserialize_error) /* }}} */ /* {{{ Serialization */ @@ -177,9 +180,17 @@ add_attr_list (JsonBuilder *builder, { GSList *attributes, *l; - json_builder_begin_array (builder); + if (!attrs) + return; attributes = pango_attr_list_get_attributes (attrs); + + if (!attributes) + return; + + json_builder_set_member_name (builder, "attributes"); + json_builder_begin_array (builder); + for (l = attributes; l; l = l->next) { PangoAttribute *attr = l->data; @@ -194,6 +205,11 @@ static void add_tab_array (JsonBuilder *builder, PangoTabArray *tabs) { + if (!tabs || pango_tab_array_get_size (tabs) == 0) + return; + + json_builder_set_member_name (builder, "tabs"); + json_builder_begin_object (builder); json_builder_set_member_name (builder, "positions-in-pixels"); @@ -211,25 +227,479 @@ add_tab_array (JsonBuilder *builder, json_builder_end_object (builder); } +static void +add_context (JsonBuilder *builder, + PangoContext *context) +{ + char *str; + const PangoMatrix *matrix; + PangoMatrix identity = PANGO_MATRIX_INIT; + + json_builder_begin_object (builder); + + /* Note: since we don't create the context when deserializing, + * we don't strip out default values here to ensure that the + * context gets updated as expected. + */ + + str = pango_font_description_to_string (context->font_desc); + json_builder_set_member_name (builder, "font"); + json_builder_add_string_value (builder, str); + g_free (str); + + if (context->set_language) + { + json_builder_set_member_name (builder, "language"); + json_builder_add_string_value (builder, pango_language_to_string (context->set_language)); + } + + json_builder_set_member_name (builder, "base-gravity"); + add_enum_value (builder, PANGO_TYPE_GRAVITY, context->base_gravity, FALSE); + + json_builder_set_member_name (builder, "gravity-hint"); + add_enum_value (builder, PANGO_TYPE_GRAVITY_HINT, context->gravity_hint, FALSE); + + json_builder_set_member_name (builder, "base-dir"); + add_enum_value (builder, PANGO_TYPE_DIRECTION, context->base_dir, FALSE); + + json_builder_set_member_name (builder, "round-glyph-positions"); + json_builder_add_boolean_value (builder, context->round_glyph_positions); + + json_builder_set_member_name (builder, "transform"); + matrix = pango_context_get_matrix (context); + if (!matrix) + matrix = &identity; + + json_builder_begin_array (builder); + json_builder_add_double_value (builder, matrix->xx); + json_builder_add_double_value (builder, matrix->xy); + json_builder_add_double_value (builder, matrix->yx); + json_builder_add_double_value (builder, matrix->yy); + json_builder_add_double_value (builder, matrix->x0); + json_builder_add_double_value (builder, matrix->y0); + json_builder_end_array (builder); + + json_builder_end_object (builder); +} + +static void +add_log_attrs (JsonBuilder *builder, + PangoLayout *layout) +{ + const PangoLogAttr *log_attrs; + int n_attrs; + + json_builder_set_member_name (builder, "log-attrs"); + json_builder_begin_array (builder); + + log_attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); + for (int i = 0; i < n_attrs; i++) + { + json_builder_begin_object (builder); + if (log_attrs[i].is_line_break) + { + json_builder_set_member_name (builder, "line-break"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_mandatory_break) + { + json_builder_set_member_name (builder, "mandatory-break"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_char_break) + { + json_builder_set_member_name (builder, "char-break"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_white) + { + json_builder_set_member_name (builder, "white"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_cursor_position) + { + json_builder_set_member_name (builder, "cursor-position"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_word_start) + { + json_builder_set_member_name (builder, "word-start"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_word_end) + { + json_builder_set_member_name (builder, "word-end"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_sentence_boundary) + { + json_builder_set_member_name (builder, "sentence-boundary"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_sentence_start) + { + json_builder_set_member_name (builder, "sentence-start"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_sentence_end) + { + json_builder_set_member_name (builder, "sentence-end"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].backspace_deletes_character) + { + json_builder_set_member_name (builder, "backspace-deletes-character"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_expandable_space) + { + json_builder_set_member_name (builder, "expandable-space"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].is_word_boundary) + { + json_builder_set_member_name (builder, "word-boundary"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].break_inserts_hyphen) + { + json_builder_set_member_name (builder, "break-inserts-hyphen"); + json_builder_add_boolean_value (builder, TRUE); + } + if (log_attrs[i].break_removes_preceding) + { + json_builder_set_member_name (builder, "break-removes_preceding"); + json_builder_add_boolean_value (builder, TRUE); + } + json_builder_end_object (builder); + } + + json_builder_end_array (builder); +} + +static void +add_font (JsonBuilder *builder, + PangoFont *font) +{ + PangoFontDescription *desc; + char *str; + hb_font_t *hb_font; + hb_face_t *face; + hb_blob_t *blob; + const char *data; + guint length; + const int *coords; + hb_feature_t features[32]; + PangoMatrix matrix; + + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "description"); + + desc = pango_font_describe (font); + str = pango_font_description_to_string (desc); + json_builder_add_string_value (builder, str); + g_free (str); + pango_font_description_free (desc); + + hb_font = pango_font_get_hb_font (font); + face = hb_font_get_face (hb_font); + blob = hb_face_reference_blob (face); + + data = hb_blob_get_data (blob, &length); + str = g_compute_checksum_for_data (G_CHECKSUM_SHA256, (const guchar *)data, length); + + json_builder_set_member_name (builder, "checksum"); + json_builder_add_string_value (builder, str); + + g_free (str); + hb_blob_destroy (blob); + + coords = hb_font_get_var_coords_normalized (hb_font, &length); + if (length > 0) + { + guint count; + hb_ot_var_axis_info_t *axes; + + count = hb_ot_var_get_axis_count (face); + g_assert (count == length); + + axes = g_alloca (count * sizeof (hb_ot_var_axis_info_t)); + hb_ot_var_get_axis_infos (face, 0, &count, axes); + + json_builder_set_member_name (builder, "variations"); + json_builder_begin_object (builder); + + for (int i = 0; i < length; i++) + { + char buf[5] = { 0, }; + + hb_tag_to_string (axes[i].tag, buf); + json_builder_set_member_name (builder, buf); + json_builder_add_int_value (builder, coords[i]); + } + + json_builder_end_object (builder); + } + + length = 0; + pango_font_get_features (font, features, G_N_ELEMENTS (features), &length); + if (length > 0) + { + json_builder_set_member_name (builder, "features"); + json_builder_begin_object (builder); + + for (int i = 0; i < length; i++) + { + char buf[5] = { 0, }; + + hb_tag_to_string (features[i].tag, buf); + json_builder_set_member_name (builder, buf); + json_builder_add_int_value (builder, features[i].value); + } + + json_builder_end_object (builder); + } + + pango_font_get_matrix (font, &matrix); + if (memcmp (&matrix, &(PangoMatrix)PANGO_MATRIX_INIT, sizeof (PangoMatrix)) != 0) + { + json_builder_set_member_name (builder, "matrix"); + json_builder_begin_array (builder); + json_builder_add_double_value (builder, matrix.xx); + json_builder_add_double_value (builder, matrix.xy); + json_builder_add_double_value (builder, matrix.yx); + json_builder_add_double_value (builder, matrix.yy); + json_builder_add_double_value (builder, matrix.x0); + json_builder_add_double_value (builder, matrix.y0); + json_builder_end_array (builder); + } + + json_builder_end_object (builder); +} + +#define ANALYSIS_FLAGS (PANGO_ANALYSIS_FLAG_CENTERED_BASELINE | \ + PANGO_ANALYSIS_FLAG_IS_ELLIPSIS | \ + PANGO_ANALYSIS_FLAG_NEED_HYPHEN) + +static void +add_run (JsonBuilder *builder, + PangoLayout *layout, + PangoLayoutRun *run) +{ + json_builder_begin_object (builder); + char *str; + + json_builder_set_member_name (builder, "offset"); + json_builder_add_int_value (builder, run->item->offset); + + json_builder_set_member_name (builder, "length"); + json_builder_add_int_value (builder, run->item->length); + + str = g_strndup (layout->text + run->item->offset, run->item->length); + json_builder_set_member_name (builder, "text"); + json_builder_add_string_value (builder, str); + g_free (str); + + json_builder_set_member_name (builder, "bidi-level"); + json_builder_add_int_value (builder, run->item->analysis.level); + + json_builder_set_member_name (builder, "gravity"); + add_enum_value (builder, PANGO_TYPE_GRAVITY, run->item->analysis.gravity, FALSE); + + json_builder_set_member_name (builder, "language"); + json_builder_add_string_value (builder, pango_language_to_string (run->item->analysis.language)); + + json_builder_set_member_name (builder, "script"); + add_enum_value (builder, PANGO_TYPE_SCRIPT, run->item->analysis.script, FALSE); + + json_builder_set_member_name (builder, "font"); + add_font (builder, run->item->analysis.font); + + json_builder_set_member_name (builder, "flags"); + json_builder_add_int_value (builder, run->item->analysis.flags & ANALYSIS_FLAGS); + + if (run->item->analysis.extra_attrs) + { + GSList *l; + + json_builder_set_member_name (builder, "extra-attributes"); + + json_builder_begin_array (builder); + for (l = run->item->analysis.extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + add_attribute (builder, attr); + } + json_builder_end_array (builder); + } + + json_builder_set_member_name (builder, "y-offset"); + json_builder_add_int_value (builder, run->y_offset); + + json_builder_set_member_name (builder, "start-x-offset"); + json_builder_add_int_value (builder, run->start_x_offset); + + json_builder_set_member_name (builder, "end-x-offset"); + json_builder_add_int_value (builder, run->end_x_offset); + + json_builder_set_member_name (builder, "glyphs"); + json_builder_begin_array (builder); + for (int i = 0; i < run->glyphs->num_glyphs; i++) + { + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "glyph"); + json_builder_add_int_value (builder, run->glyphs->glyphs[i].glyph); + + json_builder_set_member_name (builder, "width"); + json_builder_add_int_value (builder, run->glyphs->glyphs[i].geometry.width); + + if (run->glyphs->glyphs[i].geometry.x_offset != 0) + { + json_builder_set_member_name (builder, "x-offset"); + json_builder_add_int_value (builder, run->glyphs->glyphs[i].geometry.x_offset); + } + + if (run->glyphs->glyphs[i].geometry.y_offset != 0) + { + json_builder_set_member_name (builder, "y-offset"); + json_builder_add_int_value (builder, run->glyphs->glyphs[i].geometry.y_offset); + } + + if (run->glyphs->glyphs[i].attr.is_cluster_start) + { + json_builder_set_member_name (builder, "is-cluster-start"); + json_builder_add_boolean_value (builder, TRUE); + } + + if (run->glyphs->glyphs[i].attr.is_color) + { + json_builder_set_member_name (builder, "is-color"); + json_builder_add_boolean_value (builder, TRUE); + } + + json_builder_set_member_name (builder, "log-cluster"); + json_builder_add_int_value (builder, run->glyphs->log_clusters[i]); + + json_builder_end_object (builder); + } + + json_builder_end_array (builder); + + json_builder_end_object (builder); +} + +#undef ANALYSIS_FLAGS + +static void +add_line (JsonBuilder *builder, + PangoLayoutLine *line) +{ + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "start-index"); + json_builder_add_int_value (builder, line->start_index); + + json_builder_set_member_name (builder, "length"); + json_builder_add_int_value (builder, line->length); + + json_builder_set_member_name (builder, "paragraph-start"); + json_builder_add_boolean_value (builder, line->is_paragraph_start); + + json_builder_set_member_name (builder, "direction"); + add_enum_value (builder, PANGO_TYPE_DIRECTION, line->resolved_dir, FALSE); + + json_builder_set_member_name (builder, "runs"); + json_builder_begin_array (builder); + for (GSList *l = line->runs; l; l = l->next) + { + PangoLayoutRun *run = l->data; + add_run (builder, line->layout, run); + } + json_builder_end_array (builder); + + json_builder_end_object (builder); +} + +static void +add_output (JsonBuilder *builder, + PangoLayout *layout) +{ + int width, height; + + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "is-wrapped"); + json_builder_add_boolean_value (builder, pango_layout_is_wrapped (layout)); + + json_builder_set_member_name (builder, "is-ellipsized"); + json_builder_add_boolean_value (builder, pango_layout_is_ellipsized (layout)); + + json_builder_set_member_name (builder, "unknown-glyphs"); + json_builder_add_int_value (builder, pango_layout_get_unknown_glyphs_count (layout)); + + pango_layout_get_size (layout, &width, &height); + json_builder_set_member_name (builder, "width"); + json_builder_add_int_value (builder, width); + json_builder_set_member_name (builder, "height"); + json_builder_add_int_value (builder, height); + + add_log_attrs (builder, layout); + json_builder_set_member_name (builder, "lines"); + json_builder_begin_array (builder); + for (GSList *l = layout->lines; l; l = l->next) + { + PangoLayoutLine *line = l->data; + add_line (builder, line); + } + json_builder_end_array (builder); + + json_builder_end_object (builder); +} + static JsonNode * -layout_to_json (PangoLayout *layout) +layout_to_json (PangoLayout *layout, + PangoLayoutSerializeFlags flags) { JsonBuilder *builder; JsonNode *root; + const char *str; 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 (flags & PANGO_LAYOUT_SERIALIZE_CONTEXT) + { + json_builder_set_member_name (builder, "context"); + add_context (builder, layout->context); + } - if (layout->attrs) + str = (const char *) g_object_get_data (G_OBJECT (layout), "comment"); + if (str) { - json_builder_set_member_name (builder, "attributes"); - add_attr_list (builder, layout->attrs); + json_builder_set_member_name (builder, "comment"); + if (strstr (str, "\n") != NULL) + { + char **strs = g_strsplit (str, "\n", -1); + + json_builder_begin_array (builder); + for (int i = 0; strs[i]; i++) + json_builder_add_string_value (builder, strs[i]); + json_builder_end_array (builder); + + g_strfreev (strs); + } + else + json_builder_add_string_value (builder, str); } + json_builder_set_member_name (builder, "text"); + json_builder_add_string_value (builder, layout->text); + + add_attr_list (builder, layout->attrs); + if (layout->font_desc) { char *str = pango_font_description_to_string (layout->font_desc); @@ -238,11 +708,7 @@ layout_to_json (PangoLayout *layout) g_free (str); } - if (layout->tabs) - { - json_builder_set_member_name (builder, "tabs"); - add_tab_array (builder, layout->tabs); - } + add_tab_array (builder, layout->tabs); if (layout->justify) { @@ -316,6 +782,12 @@ layout_to_json (PangoLayout *layout) json_builder_add_double_value (builder, layout->line_spacing); } + if (flags & PANGO_LAYOUT_SERIALIZE_OUTPUT) + { + json_builder_set_member_name (builder, "output"); + add_output (builder, layout); + } + json_builder_end_object (builder); root = json_builder_get_root (builder); @@ -324,13 +796,28 @@ layout_to_json (PangoLayout *layout) return root; } +static JsonNode * +font_to_json (PangoFont *font) +{ + JsonBuilder *builder; + JsonNode *root; + + builder = json_builder_new_immutable (); + add_font (builder, font); + 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) +get_enum_value (GType type, + const char *str, + gboolean allow_extra, + GError **error) { GEnumClass *enum_class; GEnumValue *enum_value; @@ -351,6 +838,13 @@ get_enum_value (GType type, return value; } + g_set_error (error, + PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE, + "Could not parse enum value of type %s: %s", + g_type_name (type), + str); + return -1; } @@ -364,11 +858,13 @@ json_to_attribute (JsonReader *reader, guint end = PANGO_ATTR_INDEX_TO_TEXT_END; PangoFontDescription *desc; PangoColor color; + int value; if (!json_reader_is_object (reader)) { g_set_error (error, - PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, + PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_INVALID_SYNTAX, "Attribute must be a Json object"); return NULL; } @@ -383,32 +879,26 @@ json_to_attribute (JsonReader *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); + type = get_enum_value (PANGO_TYPE_ATTR_TYPE, json_reader_get_string_value (reader), FALSE, error); + if (type == -1) + return NULL; } else { g_set_error (error, - PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_MISSING_VALUE, + PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE, "Attribute \"type\" missing"); - json_reader_end_member (reader); return NULL; } + json_reader_end_member (reader); if (!json_reader_read_member (reader, "value")) { g_set_error (error, - PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_MISSING_VALUE, + PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE, "Attribute \"value\" missing"); - json_reader_end_member (reader); return NULL; } @@ -425,16 +915,27 @@ json_to_attribute (JsonReader *reader, 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)); + value = get_enum_value (PANGO_TYPE_STYLE, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + return NULL; + attr = pango_attr_style_new ((PangoStyle)value); break; case PANGO_ATTR_WEIGHT: - attr = pango_attr_weight_new (get_enum_value (PANGO_TYPE_WEIGHT, json_reader_get_string_value (reader), TRUE)); + value = get_enum_value (PANGO_TYPE_WEIGHT, json_reader_get_string_value (reader), TRUE, error); + if (value == -1) + return NULL; + attr = pango_attr_weight_new (value); break; case PANGO_ATTR_VARIANT: - attr = pango_attr_variant_new (get_enum_value (PANGO_TYPE_VARIANT, json_reader_get_string_value (reader), FALSE)); + value = get_enum_value (PANGO_TYPE_VARIANT, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + return NULL; + attr = pango_attr_variant_new ((PangoVariant)value); break; case PANGO_ATTR_STRETCH: - attr = pango_attr_stretch_new (get_enum_value (PANGO_TYPE_STRETCH, json_reader_get_string_value (reader), FALSE)); + value = get_enum_value (PANGO_TYPE_STRETCH, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + attr = pango_attr_stretch_new ((PangoStretch)value); break; case PANGO_ATTR_SIZE: attr = pango_attr_size_new (json_reader_get_int_value (reader)); @@ -453,7 +954,10 @@ json_to_attribute (JsonReader *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)); + value = get_enum_value (PANGO_TYPE_UNDERLINE, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + return NULL; + attr = pango_attr_underline_new ((PangoUnderline)value); break; case PANGO_ATTR_STRIKETHROUGH: attr = pango_attr_strikethrough_new (json_reader_get_boolean_value (reader)); @@ -486,10 +990,16 @@ json_to_attribute (JsonReader *reader, 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)); + value = get_enum_value (PANGO_TYPE_GRAVITY, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + return NULL; + attr = pango_attr_gravity_new ((PangoGravity)value); 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)); + value = get_enum_value (PANGO_TYPE_GRAVITY_HINT, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + return NULL; + attr = pango_attr_gravity_hint_new ((PangoGravityHint)value); break; case PANGO_ATTR_FONT_FEATURES: attr = pango_attr_font_features_new (json_reader_get_string_value (reader)); @@ -510,7 +1020,10 @@ json_to_attribute (JsonReader *reader, 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)); + value = get_enum_value (PANGO_TYPE_OVERLINE, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + return NULL; + attr = pango_attr_overline_new ((PangoOverline)value); break; case PANGO_ATTR_OVERLINE_COLOR: pango_color_parse (&color, json_reader_get_string_value (reader)); @@ -523,7 +1036,10 @@ json_to_attribute (JsonReader *reader, 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)); + value = get_enum_value (PANGO_TYPE_TEXT_TRANSFORM, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + return NULL; + attr = pango_attr_text_transform_new ((PangoTextTransform)value); break; case PANGO_ATTR_WORD: attr = pango_attr_word_new (); @@ -532,10 +1048,16 @@ json_to_attribute (JsonReader *reader, 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)); + value = get_enum_value (PANGO_TYPE_BASELINE_SHIFT, json_reader_get_string_value (reader), TRUE, error); + if (value == -1) + return NULL; + attr = pango_attr_baseline_shift_new (value); 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)); + value = get_enum_value (PANGO_TYPE_FONT_SCALE, json_reader_get_string_value (reader), FALSE, error); + if (value == -1) + return NULL; + attr = pango_attr_font_scale_new ((PangoFontScale)value); break; } @@ -553,16 +1075,17 @@ json_to_attr_list (JsonReader *reader, { PangoAttrList *attributes; + attributes = pango_attr_list_new (); + if (!json_reader_is_array (reader)) { g_set_error (error, - PANGO_LAYOUT_SERIALIZE_ERROR, PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, + PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_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; @@ -600,8 +1123,8 @@ json_to_tab_array (JsonReader *reader, if (!json_reader_is_array (reader)) { g_set_error (error, - PANGO_LAYOUT_SERIALIZE_ERROR, - PANGO_LAYOUT_SERIALIZE_INVALID_SYNTAX, + PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_INVALID_SYNTAX, "Tab \"positions\" must be a Json array"); goto fail; } @@ -626,26 +1149,165 @@ fail: return NULL; } +static gboolean +apply_json_to_context (JsonReader *reader, + PangoContext *context, + GError **error) +{ + if (json_reader_read_member (reader, "language")) + { + const char *value; + PangoLanguage *language; + + value = json_reader_get_string_value (reader); + language = pango_language_from_string (value); + pango_context_set_language (context, language); + } + 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)); + pango_context_set_font_description (context, desc); + pango_font_description_free (desc); + } + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "base-gravity")) + { + PangoGravity gravity = get_enum_value (PANGO_TYPE_GRAVITY, + json_reader_get_string_value (reader), + FALSE, + error); + if (gravity == -1) + return FALSE; + + pango_context_set_base_gravity (context, gravity); + } + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "gravity-hint")) + { + PangoGravityHint gravity_hint = get_enum_value (PANGO_TYPE_GRAVITY_HINT, + json_reader_get_string_value (reader), + FALSE, + error); + if (gravity_hint == -1) + return FALSE; + + pango_context_set_gravity_hint (context, gravity_hint); + } + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "base-dir")) + { + PangoDirection direction = get_enum_value (PANGO_TYPE_DIRECTION, + json_reader_get_string_value (reader), + FALSE, + error); + if (direction == -1) + return FALSE; + + pango_context_set_base_dir (context, direction); + } + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "round-glyph-positions")) + { + pango_context_set_round_glyph_positions (context, json_reader_get_boolean_value (reader)); + } + json_reader_end_member (reader); + + if (json_reader_read_member (reader, "transform")) + { + PangoMatrix m; + + json_reader_read_element (reader, 0); + m.xx = json_reader_get_double_value (reader); + json_reader_end_element (reader); + json_reader_read_element (reader, 1); + m.xy = json_reader_get_double_value (reader); + json_reader_end_element (reader); + json_reader_read_element (reader, 2); + m.yx = json_reader_get_double_value (reader); + json_reader_end_element (reader); + json_reader_read_element (reader, 3); + m.yy = json_reader_get_double_value (reader); + json_reader_end_element (reader); + json_reader_read_element (reader, 4); + m.x0 = json_reader_get_double_value (reader); + json_reader_end_element (reader); + json_reader_read_element (reader, 5); + m.y0 = json_reader_get_double_value (reader); + json_reader_end_element (reader); + + pango_context_set_matrix (context, &m); + } + json_reader_end_member (reader); + + return TRUE; +} + static PangoLayout * -json_to_layout (PangoContext *context, - JsonNode *node, - GError **error) +json_to_layout (PangoContext *context, + JsonNode *node, + PangoLayoutDeserializeFlags flags, + 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, + PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_INVALID_SYNTAX, "Layout must be a Json object"); goto fail; } + if (flags & PANGO_LAYOUT_DESERIALIZE_CONTEXT) + { + if (json_reader_read_member (reader, "context")) + { + if (!apply_json_to_context (reader, context, error)) + goto fail; + } + json_reader_end_member (reader); + } + + layout = pango_layout_new (context); + + if (json_reader_read_member (reader, "comment")) + { + if (json_reader_is_array (reader)) + { + GString *s; + + s = g_string_new (""); + for (int i = 0; i < json_reader_count_elements (reader); i++) + { + json_reader_read_element (reader, i); + if (s->len > 0) + g_string_append_c (s, '\n'); + g_string_append (s, json_reader_get_string_value (reader)); + json_reader_end_element (reader); + } + + g_object_set_data_full (G_OBJECT (layout), "comment", + g_string_free (s, FALSE), + g_free); + } + else + g_object_set_data_full (G_OBJECT (layout), "comment", + g_strdup (json_reader_get_string_value (reader)), + g_free); + } + json_reader_end_member (reader); + if (json_reader_read_member (reader, "text")) pango_layout_set_text (layout, json_reader_get_string_value (reader), -1); json_reader_end_member (reader); @@ -672,9 +1334,10 @@ json_to_layout (PangoContext *context, if (!desc) { g_set_error (error, - PANGO_LAYOUT_SERIALIZE_ERROR, - PANGO_LAYOUT_SERIALIZE_INVALID_VALUE, - "Could not parse \"font\" value: %s", + PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE, + "Could not parse \"%s\" value: %s", + "font", json_reader_get_string_value (reader)); goto fail; } @@ -717,16 +1380,10 @@ json_to_layout (PangoContext *context, { PangoAlignment align = get_enum_value (PANGO_TYPE_ALIGNMENT, json_reader_get_string_value (reader), - FALSE); + FALSE, + error); 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; - } + goto fail; pango_layout_set_alignment (layout, align); } @@ -737,17 +1394,11 @@ json_to_layout (PangoContext *context, { PangoWrapMode wrap = get_enum_value (PANGO_TYPE_WRAP_MODE, json_reader_get_string_value (reader), - FALSE); + FALSE, + error); 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; - } + goto fail; pango_layout_set_wrap (layout, wrap); } @@ -758,17 +1409,11 @@ json_to_layout (PangoContext *context, { PangoEllipsizeMode ellipsize = get_enum_value (PANGO_TYPE_ELLIPSIZE_MODE, json_reader_get_string_value (reader), - FALSE); + FALSE, + error); 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; - } + goto fail; pango_layout_set_ellipsize (layout, ellipsize); } @@ -801,20 +1446,22 @@ json_to_layout (PangoContext *context, fail: g_object_unref (reader); - g_object_unref (layout); + if (layout) + g_object_unref (layout); return NULL; } - /* }}} */ - /* {{{ Public API */ +/* }}} */ +/* {{{ Public API */ /** * pango_layout_serialize: * @layout: a `PangoLayout` + * @flags: `PangoLayoutSerializeFlags` * * Serializes the @layout for later deserialization via [method@Pango.Layout.deserialize]. * - * There are no guarantees about the format of the output accross different + * There are no guarantees about the format of the output across different * versions of Pango and [method@Pango.Layout.deserialize] will reject data * that it cannot parse. * @@ -826,21 +1473,28 @@ fail: * Since: 1.50 */ GBytes * -pango_layout_serialize (PangoLayout *layout) +pango_layout_serialize (PangoLayout *layout, + PangoLayoutSerializeFlags flags) { JsonGenerator *generator; JsonNode *node; + GString *str; char *data; gsize size; - node = layout_to_json (layout); + g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL); + + node = layout_to_json (layout, flags); 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); + str = g_string_new (""); + g_string_append_c (json_generator_to_gstring (generator, str), '\n'); + size = str->len; + data = g_string_free (str, FALSE); json_node_free (node); g_object_unref (generator); @@ -851,6 +1505,7 @@ pango_layout_serialize (PangoLayout *layout) /** * pango_layout_write_to_file: * @layout: a `PangoLayout` + * @flags: `PangoLayoutSerializeFlags` * @filename: (type filename): the file to save it to * @error: Return location for a potential error * @@ -867,9 +1522,10 @@ pango_layout_serialize (PangoLayout *layout) * Since: 1.50 */ gboolean -pango_layout_write_to_file (PangoLayout *layout, - const char *filename, - GError **error) +pango_layout_write_to_file (PangoLayout *layout, + PangoLayoutSerializeFlags flags, + const char *filename, + GError **error) { GBytes *bytes; gboolean result; @@ -878,7 +1534,7 @@ pango_layout_write_to_file (PangoLayout *layout, g_return_val_if_fail (filename != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - bytes = pango_layout_serialize (layout); + bytes = pango_layout_serialize (layout, flags); result = g_file_set_contents (filename, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), @@ -892,6 +1548,7 @@ pango_layout_write_to_file (PangoLayout *layout, /** * pango_layout_deserialize: * @context: a `PangoContext` + * @flags: `PangoLayoutDeserializeFlags` * @bytes: the bytes containing the data * @error: return location for an error * @@ -904,14 +1561,17 @@ pango_layout_write_to_file (PangoLayout *layout, * Since: 1.50 */ PangoLayout * -pango_layout_deserialize (PangoContext *context, - GBytes *bytes, - GError **error) +pango_layout_deserialize (PangoContext *context, + GBytes *bytes, + PangoLayoutDeserializeFlags flags, + GError **error) { JsonParser *parser; JsonNode *node; PangoLayout *layout; + g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL); + parser = json_parser_new_immutable (); if (!json_parser_load_from_data (parser, g_bytes_get_data (bytes, NULL), @@ -923,13 +1583,54 @@ pango_layout_deserialize (PangoContext *context, } node = json_parser_get_root (parser); - layout = json_to_layout (context, node, error); + layout = json_to_layout (context, node, flags, error); g_object_unref (parser); return layout; } +/** + * pango_font_serialize: + * @font: a `PangoFont` + * + * Serializes the @font in a way that can be uniquely identified. + * + * There are no guarantees about the format of the output across different + * versions of Pango. + * + * 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 @font + * + * Since: 1.50 + */ +GBytes * +pango_font_serialize (PangoFont *font) +{ + JsonGenerator *generator; + JsonNode *node; + char *data; + gsize size; + + g_return_val_if_fail (PANGO_IS_FONT (font), NULL); + + node = font_to_json (font); + + 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); +} + /* }}} */ /* vim:set foldmethod=marker expandtab: */ diff --git a/pango/shape.c b/pango/shape.c index 02f0f059..afb1e1a3 100644 --- a/pango/shape.c +++ b/pango/shape.c @@ -102,10 +102,12 @@ pango_hb_font_get_nominal_glyph (hb_font_t *font, * don't draw a hex box for 0x20 */ if (unicode == 0x20) - *glyph = PANGO_GET_UNKNOWN_GLYPH (0x2423); + unicode = 0x2423; else - *glyph = PANGO_GET_UNKNOWN_GLYPH (unicode); - return TRUE; + { + *glyph = PANGO_GET_UNKNOWN_GLYPH (unicode); + return TRUE; + } } if ((context->show_flags & PANGO_SHOW_IGNORABLES) != 0 && |