[pango/line-breaker: 12/16] Serialization




commit edb75623dbdb558d7e420a39884151c775350646
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Jan 14 22:13:01 2022 -0500

    Serialization

 pango/serializer.c    | 415 +++++++++++++++++++++++++++++++++++++++++++++++++-
 tests/testserialize.c |  39 +++++
 2 files changed, 450 insertions(+), 4 deletions(-)
---
diff --git a/pango/serializer.c b/pango/serializer.c
index 2dab93b4..eadb9742 100644
--- a/pango/serializer.c
+++ b/pango/serializer.c
@@ -26,6 +26,9 @@
 #include <pango/pango-context-private.h>
 #include <pango/pango-enum-types.h>
 #include <pango/pango-font-private.h>
+#include <pango/pango-line-private.h>
+#include <pango/pango-lines.h>
+#include <pango/pango-simple-layout.h>
 
 #include <hb-ot.h>
 #include "pango/json/gtkjsonparserprivate.h"
@@ -246,6 +249,15 @@ static const char *alignment_names[] = {
   NULL
 };
 
+static const char *alignment_names2[] = {
+  "left",
+  "center",
+  "right",
+  "justify",
+  "justify-all",
+  NULL
+};
+
 static const char *wrap_names[] = {
   "word",
   "char",
@@ -634,7 +646,7 @@ add_font (GtkJsonPrinter *printer,
 
 static void
 add_run (GtkJsonPrinter *printer,
-         PangoLayout    *layout,
+         const char     *text,
          PangoLayoutRun *run)
 {
   char *str;
@@ -644,7 +656,7 @@ add_run (GtkJsonPrinter *printer,
   gtk_json_printer_add_integer (printer, "offset", run->item->offset);
   gtk_json_printer_add_integer (printer, "length", run->item->length);
 
-  str = g_strndup (layout->text + run->item->offset, run->item->length);
+  str = g_strndup (text + run->item->offset, run->item->length);
   gtk_json_printer_add_string (printer, "text", str);
   g_free (str);
 
@@ -721,7 +733,7 @@ add_line (GtkJsonPrinter  *printer,
   for (GSList *l = line->runs; l; l = l->next)
     {
       PangoLayoutRun *run = l->data;
-      add_run (printer, line->layout, run);
+      add_run (printer, line->layout->text, run);
     }
   gtk_json_printer_end (printer);
 
@@ -827,6 +839,125 @@ layout_to_json (GtkJsonPrinter            *printer,
   gtk_json_printer_end (printer);
 }
 
+static void
+line_to_json (GtkJsonPrinter *printer,
+              PangoLine      *line,
+              int             x,
+              int             y)
+{
+  gtk_json_printer_start_object (printer, NULL);
+
+  gtk_json_printer_start_array (printer, "position");
+  gtk_json_printer_add_number (printer, NULL, x);
+  gtk_json_printer_add_number (printer, NULL, y);
+  gtk_json_printer_end (printer);
+
+  gtk_json_printer_start_object (printer, "line");
+
+  gtk_json_printer_add_integer (printer, "start-index", line->start_index);
+  gtk_json_printer_add_integer (printer, "length", line->length);
+  gtk_json_printer_add_integer (printer, "start-offset", line->start_offset);
+  gtk_json_printer_add_integer (printer, "n-chars", line->n_chars);
+
+  gtk_json_printer_add_boolean (printer, "wrapped", line->wrapped);
+  gtk_json_printer_add_boolean (printer, "ellipsized", line->ellipsized);
+  gtk_json_printer_add_boolean (printer, "hyphenated", line->hyphenated);
+  gtk_json_printer_add_boolean (printer, "justified", line->justified);
+  gtk_json_printer_add_boolean (printer, "paragraph-start", line->starts_paragraph);
+  gtk_json_printer_add_boolean (printer, "paragraph-end", line->ends_paragraph);
+  gtk_json_printer_add_string (printer, "direction", direction_names[line->direction]);
+
+  gtk_json_printer_start_array (printer, "runs");
+  for (GSList *l = line->runs; l; l = l->next)
+    {
+      PangoLayoutRun *run = l->data;
+      add_run (printer, line->data->text, run);
+    }
+  gtk_json_printer_end (printer);
+
+  gtk_json_printer_end (printer);
+
+  gtk_json_printer_end (printer);
+}
+
+static void
+lines_to_json (GtkJsonPrinter  *printer,
+               PangoLines      *lines,
+               const char      *name)
+{
+  gtk_json_printer_start_array (printer, name);
+
+  for (int i = 0; i < pango_lines_get_n_lines (lines); i++)
+    {
+      PangoLine *line;
+      int x, y;
+      pango_lines_get_line (lines, i, &line, &x, &y);
+      line_to_json (printer, line, x, y);
+    }
+
+  gtk_json_printer_end (printer);
+}
+
+static void
+simple_layout_to_json (GtkJsonPrinter                  *printer,
+                       PangoSimpleLayout               *layout,
+                       PangoSimpleLayoutSerializeFlags  flags)
+{
+  const char *str;
+
+  gtk_json_printer_start_object (printer, NULL);
+
+  if (flags & PANGO_SIMPLE_LAYOUT_SERIALIZE_CONTEXT)
+    add_context (printer, pango_simple_layout_get_context (layout));
+
+  str = (const char *) g_object_get_data (G_OBJECT (layout), "comment");
+  if (str)
+    gtk_json_printer_add_string (printer, "comment", str);
+
+  gtk_json_printer_add_string (printer, "text", pango_simple_layout_get_text (layout));
+
+  add_attr_list (printer, pango_simple_layout_get_attributes (layout));
+
+  if (pango_simple_layout_get_font_description (layout))
+    {
+      char *str = pango_font_description_to_string (pango_simple_layout_get_font_description (layout));
+      gtk_json_printer_add_string (printer, "font", str);
+      g_free (str);
+    }
+
+  if (pango_simple_layout_get_tabs (layout))
+    add_tab_array (printer, pango_simple_layout_get_tabs (layout));
+
+  if (!pango_simple_layout_get_auto_dir (layout))
+    gtk_json_printer_add_boolean (printer, "auto-dir", FALSE);
+
+  if (pango_simple_layout_get_alignment (layout) != PANGO_ALIGNMENT_LEFT)
+    gtk_json_printer_add_string (printer, "alignment", alignment_names2[pango_simple_layout_get_alignment 
(layout)]);
+
+  if (pango_simple_layout_get_wrap (layout))
+    gtk_json_printer_add_string (printer, "wrap", wrap_names[pango_simple_layout_get_wrap (layout)]);
+
+  if (pango_simple_layout_get_ellipsize (layout) != PANGO_ELLIPSIZE_NONE)
+    gtk_json_printer_add_string (printer, "ellipsize", ellipsize_names[pango_simple_layout_get_ellipsize 
(layout)]);
+
+  if (pango_simple_layout_get_width (layout) != -1)
+    gtk_json_printer_add_integer (printer, "width", pango_simple_layout_get_width (layout));
+
+  if (pango_simple_layout_get_height (layout) != -1)
+    gtk_json_printer_add_integer (printer, "height", pango_simple_layout_get_height (layout));
+
+  if (pango_simple_layout_get_indent (layout) != 0)
+    gtk_json_printer_add_integer (printer, "indent", pango_simple_layout_get_indent (layout));
+
+  if (pango_simple_layout_get_line_spacing (layout) != 0.)
+    gtk_json_printer_add_number (printer, "line-spacing", pango_simple_layout_get_line_spacing (layout));
+
+  if (flags & PANGO_LAYOUT_SERIALIZE_OUTPUT)
+    lines_to_json (printer, pango_simple_layout_get_lines (layout), "lines");
+
+  gtk_json_printer_end (printer);
+}
+
 static void
 gstring_write (GtkJsonPrinter *printer,
                const char     *s,
@@ -1522,6 +1653,142 @@ json_parser_fill_layout (GtkJsonParser               *parser,
   gtk_json_parser_end (parser);
 }
 
+enum {
+  SIMPLE_LAYOUT_CONTEXT,
+  SIMPLE_LAYOUT_COMMENT,
+  SIMPLE_LAYOUT_TEXT,
+  SIMPLE_LAYOUT_ATTRIBUTES,
+  SIMPLE_LAYOUT_FONT,
+  SIMPLE_LAYOUT_TABS,
+  SIMPLE_LAYOUT_AUTO_DIR,
+  SIMPLE_LAYOUT_ALIGNMENT,
+  SIMPLE_LAYOUT_WRAP,
+  SIMPLE_LAYOUT_ELLIPSIZE,
+  SIMPLE_LAYOUT_WIDTH,
+  SIMPLE_LAYOUT_HEIGHT,
+  SIMPLE_LAYOUT_INDENT,
+  SIMPLE_LAYOUT_LINE_SPACING,
+  SIMPLE_LAYOUT_LINES
+};
+
+static const char *simple_layout_members[] = {
+  "context",
+  "comment",
+  "text",
+  "attributes",
+  "font",
+  "tabs",
+  "auto-dir",
+  "alignment",
+  "wrap",
+  "ellipsize",
+  "width",
+  "height",
+  "indent",
+  "line-spacing"
+  "lines",
+  NULL
+};
+
+static void
+json_parser_fill_simple_layout (GtkJsonParser                     *parser,
+                                PangoSimpleLayout                 *layout,
+                                PangoSimpleLayoutDeserializeFlags  flags)
+{
+  gtk_json_parser_start_object (parser);
+
+  do
+    {
+      char *str;
+
+      switch (gtk_json_parser_select_member (parser, simple_layout_members))
+        {
+        case SIMPLE_LAYOUT_CONTEXT:
+          if (flags & PANGO_LAYOUT_DESERIALIZE_CONTEXT)
+            json_parser_fill_context (parser, pango_simple_layout_get_context (layout));
+          break;
+
+        case SIMPLE_LAYOUT_COMMENT:
+          str = gtk_json_parser_get_string (parser);
+          g_object_set_data_full (G_OBJECT (layout), "comment", str, g_free);
+          break;
+
+        case SIMPLE_LAYOUT_TEXT:
+          str = gtk_json_parser_get_string (parser);
+          pango_simple_layout_set_text (layout, str, -1);
+          g_free (str);
+          break;
+
+        case SIMPLE_LAYOUT_ATTRIBUTES:
+          {
+            PangoAttrList *attributes = pango_attr_list_new ();
+            json_parser_fill_attr_list (parser, attributes);
+            pango_simple_layout_set_attributes (layout, attributes);
+            pango_attr_list_unref (attributes);
+          }
+          break;
+
+        case SIMPLE_LAYOUT_FONT:
+          {
+            PangoFontDescription *desc = parser_get_font_description (parser);;
+            pango_simple_layout_set_font_description (layout, desc);
+            pango_font_description_free (desc);
+          }
+          break;
+
+        case SIMPLE_LAYOUT_AUTO_DIR:
+          pango_simple_layout_set_auto_dir (layout, gtk_json_parser_get_boolean (parser));
+          break;
+
+        case SIMPLE_LAYOUT_LINE_SPACING:
+          pango_simple_layout_set_line_spacing (layout, gtk_json_parser_get_number (parser));
+          break;
+
+        case SIMPLE_LAYOUT_TABS:
+          {
+            PangoTabArray *tabs = pango_tab_array_new (0, FALSE);
+            json_parser_fill_tab_array (parser, tabs);
+            pango_simple_layout_set_tabs (layout, tabs);
+            pango_tab_array_free (tabs);
+          }
+          break;
+
+        case SIMPLE_LAYOUT_ALIGNMENT:
+          pango_simple_layout_set_alignment (layout, (PangoAlignment) parser_select_string (parser, 
alignment_names2));
+          break;
+
+        case SIMPLE_LAYOUT_WRAP:
+          pango_simple_layout_set_wrap (layout, (PangoWrapMode) parser_select_string (parser, wrap_names));
+          break;
+
+        case SIMPLE_LAYOUT_ELLIPSIZE:
+          pango_simple_layout_set_ellipsize (layout, (PangoEllipsizeMode) parser_select_string (parser, 
ellipsize_names));
+          break;
+
+        case SIMPLE_LAYOUT_WIDTH:
+          pango_simple_layout_set_width (layout, (int) gtk_json_parser_get_number (parser));
+          break;
+
+        case SIMPLE_LAYOUT_HEIGHT:
+          pango_simple_layout_set_height (layout, (int) gtk_json_parser_get_number (parser));
+          break;
+
+        case SIMPLE_LAYOUT_INDENT:
+          pango_simple_layout_set_indent (layout, (int) gtk_json_parser_get_number (parser));
+          break;
+
+        case SIMPLE_LAYOUT_LINES:
+          break;
+
+        default:
+          break;
+        }
+    }
+  while (gtk_json_parser_next (parser));
+
+  gtk_json_parser_end (parser);
+}
+
 enum {
   FONT_DESCRIPTION,
   FONT_CHECKSUM,
@@ -1749,7 +2016,7 @@ pango_font_serialize (PangoFont *font)
   gsize size;
 
   g_return_val_if_fail (PANGO_IS_FONT (font), NULL);
-
+ 
   str = g_string_new ("");
 
   printer = gtk_json_printer_new (gstring_write, str, NULL);
@@ -1798,6 +2065,146 @@ pango_font_deserialize (PangoContext  *context,
   return font;
 }
 
+/**
+ * pango_lines_serialize:
+ * @lines: a `PangoLines` object
+ *
+ * Serializes the @lines.
+ *
+ * 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 @lines
+ */
+GBytes *
+pango_lines_serialize (PangoLines *lines)
+{
+  GString *str;
+  GtkJsonPrinter *printer;
+  char *data;
+  gsize size;
+
+  str = g_string_new ("");
+
+  printer = gtk_json_printer_new (gstring_write, str, NULL);
+  gtk_json_printer_set_flags (printer, GTK_JSON_PRINTER_PRETTY);
+  lines_to_json (printer, lines, NULL);
+  gtk_json_printer_free (printer);
+
+  g_string_append_c (str, '\n');
+
+  size = str->len;
+  data = g_string_free (str, FALSE);
+
+  return g_bytes_new_take (data, size);
+}
+
+/**
+ * pango_simple_layout_serialize:
+ * @layout: a `PangoSimpleLayout`
+ * @flags: `PangoSipleLayoutSerializeFlags`
+ *
+ * Serializes the @layout for later deserialization via [func@Pango.SimpleLayout.deserialize].
+ *
+ * There are no guarantees about the format of the output across different
+ * versions of Pango and [func@Pango.SimpleLayout.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
+ */
+GBytes *
+pango_simple_layout_serialize (PangoSimpleLayout               *layout,
+                               PangoSimpleLayoutSerializeFlags  flags)
+{
+  GString *str;
+  GtkJsonPrinter *printer;
+  char *data;
+  gsize size;
+
+  g_return_val_if_fail (PANGO_IS_SIMPLE_LAYOUT (layout), NULL);
+
+  str = g_string_new ("");
+
+  printer = gtk_json_printer_new (gstring_write, str, NULL);
+  gtk_json_printer_set_flags (printer, GTK_JSON_PRINTER_PRETTY);
+  simple_layout_to_json (printer, layout, flags);
+  gtk_json_printer_free (printer);
+
+  g_string_append_c (str, '\n');
+
+  size = str->len;
+  data = g_string_free (str, FALSE);
+
+  return g_bytes_new_take (data, size);
+}
+
+/**
+ * pango_simple_layout_deserialize:
+ * @context: a `PangoContext`
+ * @flags: `PangoSimpleLayoutDeserializeFlags`
+ * @bytes: the bytes containing the data
+ * @error: return location for an error
+ *
+ * Loads data previously created via [method@Pango.SimpleLayout.serialize].
+ *
+ * For a discussion of the supported format, see that function.
+ *
+ * Note: to verify that the returned layout is identical to
+ * the one that was serialized, you can compare @bytes to the
+ * result of serializing the layout again.
+ *
+ * Returns: (nullable) (transfer full): a new `PangoSimpleLayout`
+ */
+PangoSimpleLayout *
+pango_simple_layout_deserialize (PangoContext                       *context,
+                                 GBytes                             *bytes,
+                                 PangoSimpleLayoutDeserializeFlags   flags,
+                                 GError                            **error)
+{
+  PangoSimpleLayout *layout;
+  GtkJsonParser *parser;
+  const GError *parser_error;
+
+  g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
+
+  layout = pango_simple_layout_new (context);
+
+  parser = gtk_json_parser_new_for_bytes (bytes);
+  json_parser_fill_simple_layout (parser, layout, flags);
+
+  parser_error = gtk_json_parser_get_error (parser);
+
+  if (parser_error)
+    {
+      gsize start, end;
+      int code;
+
+      gtk_json_parser_get_error_offset (parser, &start, &end);
+
+      if (g_error_matches (parser_error, GTK_JSON_ERROR, GTK_JSON_ERROR_VALUE))
+        code = PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE;
+      else if (g_error_matches (parser_error, GTK_JSON_ERROR, GTK_JSON_ERROR_SCHEMA))
+        code = PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE;
+      else
+        code = PANGO_LAYOUT_DESERIALIZE_INVALID;
+
+      g_set_error (error, PANGO_LAYOUT_DESERIALIZE_ERROR, code,
+                   "%ld:%ld: %s", start, end, parser_error->message);
+
+      g_clear_object (&layout);
+    }
+
+  gtk_json_parser_free (parser);
+
+  return layout;
+}
+
 /* }}} */
 
 /* vim:set foldmethod=marker expandtab: */
diff --git a/tests/testserialize.c b/tests/testserialize.c
index e480da31..ecaa537b 100644
--- a/tests/testserialize.c
+++ b/tests/testserialize.c
@@ -465,6 +465,44 @@ install_fonts (void)
   g_free (dir);
 }
 
+static void
+test_serialize_linebreaker (void)
+{
+  PangoContext *context;
+  PangoLineBreaker *breaker;
+  const char *text =
+    "This is a long paragraph that should get "
+    "broken over at least two lines, if not more.";
+  int x, y, width;
+  PangoLines *lines;
+  GBytes *bytes;
+
+  context = pango_font_map_create_context (pango_cairo_font_map_get_default ());
+
+  breaker = pango_line_breaker_new (context);
+
+  pango_line_breaker_add_text (breaker, text, -1, NULL);
+
+  lines = pango_lines_new ();
+
+  x = y = 0;
+  width = 200;
+  while (!pango_line_breaker_done (breaker))
+    {
+      PangoLine *line = pango_line_breaker_next_line (breaker, x, width,
+                                                      PANGO_WRAP_WORD,
+                                                      PANGO_ELLIPSIZE_NONE);
+      pango_lines_add_line (lines, line, 0, 0);
+    }
+
+  bytes = pango_lines_serialize (lines);
+  g_print ("%s\n", (char *)g_bytes_get_data (bytes, NULL));
+
+  g_object_unref (lines);
+
+  g_object_unref (context);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -479,6 +517,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/serialize/layout/valid", test_serialize_layout_valid);
   g_test_add_func ("/serialize/layout/context", test_serialize_layout_context);
   g_test_add_func ("/serialize/layout/invalid", test_serialize_layout_invalid);
+  g_test_add_func ("/serialize/linebreaker", test_serialize_linebreaker);
 
   return g_test_run ();
 }


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