[pango/serialization-improvements: 11/19] Optionally serialize output




commit 3f6f2887f02ee6cf98d115085b0dcff681696e0e
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Nov 22 20:33:11 2021 -0500

    Optionally serialize output
    
    If requested, serialize lines, runs, and log attrs.
    This will let us use the serialization format to
    record not just the test inputs, but outputs as
    well.

 pango/pango-layout.h            |   2 +
 pango/serializer.c              | 383 +++++++++++++++++++++++++++++++++++++++-
 tests/layouts/valid-1.expected  |  43 -----
 tests/layouts/valid-10.expected |  37 ----
 tests/layouts/valid-11.expected |  53 ------
 tests/layouts/valid-12.expected |  35 ----
 tests/layouts/valid-13.expected |  35 ----
 tests/layouts/valid-14.expected |  38 ----
 tests/layouts/valid-15.expected |  36 ----
 tests/layouts/valid-16.expected |  37 ----
 tests/layouts/valid-17.expected |  35 ----
 tests/layouts/valid-18.expected |  40 -----
 tests/layouts/valid-19.expected |  42 -----
 tests/layouts/valid-2.expected  |  42 -----
 tests/layouts/valid-20.expected |  40 -----
 tests/layouts/valid-22.expected | 111 ------------
 tests/layouts/valid-3.expected  |  33 ----
 tests/layouts/valid-4.expected  |  44 -----
 tests/layouts/valid-5.expected  |  56 ------
 tests/layouts/valid-6.expected  |  33 ----
 tests/layouts/valid-7.expected  |  43 -----
 tests/layouts/valid-8.expected  |  34 ----
 tests/layouts/valid-9.expected  |  49 -----
 23 files changed, 384 insertions(+), 917 deletions(-)
---
diff --git a/pango/pango-layout.h b/pango/pango-layout.h
index 3c43c202..32dc16f0 100644
--- a/pango/pango-layout.h
+++ b/pango/pango-layout.h
@@ -355,6 +355,7 @@ GSList *         pango_layout_get_lines_readonly   (PangoLayout    *layout);
  * 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].
  *
@@ -363,6 +364,7 @@ GSList *         pango_layout_get_lines_readonly   (PangoLayout    *layout);
 typedef enum {
   PANGO_LAYOUT_SERIALIZE_DEFAULT = 0,
   PANGO_LAYOUT_SERIALIZE_CONTEXT = 1 << 0,
+  PANGO_LAYOUT_SERIALIZE_OUTPUT = 1 << 1,
 } PangoLayoutSerializeFlags;
 
 PANGO_AVAILABLE_IN_1_50
diff --git a/pango/serializer.c b/pango/serializer.c
index fdaba728..855fafea 100644
--- a/pango/serializer.c
+++ b/pango/serializer.c
@@ -25,7 +25,9 @@
 #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 */
@@ -257,7 +259,7 @@ add_context (JsonBuilder  *builder,
   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, "direction");
+  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");
@@ -280,6 +282,379 @@ add_context (JsonBuilder  *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));
+
+  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,
                 PangoLayoutSerializeFlags  flags)
@@ -384,6 +759,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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]