From 59f6c578f30ee49c8147e718a64bbfae7651a756 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Nov 2021 11:01:55 -0500 Subject: Implement pango_layout_deserialize --- pango/serializer.c | 319 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 317 insertions(+), 2 deletions(-) diff --git a/pango/serializer.c b/pango/serializer.c index a7747f38..55b17eac 100644 --- a/pango/serializer.c +++ b/pango/serializer.c @@ -25,6 +25,13 @@ #include #include +#include "pango/css/gtkcss.h" +#include "pango/css/gtkcssdataurlprivate.h" +#include "pango/css/gtkcssparserprivate.h" +#include "pango/css/gtkcssserializerprivate.h" + +/* {{{ Printer */ + typedef struct { int indentation_level; @@ -118,6 +125,9 @@ out: g_string_append_c (str, '"'); } +/* }}} */ +/* {{{ Serialization */ + static void append_string_param (Printer *p, const char *param_name, @@ -166,7 +176,6 @@ append_enum_param (Printer *p, } else { - char *v = g_strdup_printf ("%d", value); append_simple_string (p, param_name, v); g_free (v); @@ -366,6 +375,288 @@ layout_print (Printer *p, end_node (p); } +/* }}} */ +/* {{{ Deserialization */ + +static PangoContext *parser_context; /* FIXME */ + +typedef struct +{ + const char *name; + gboolean (* parse_func) (GtkCssParser *parser, gpointer result); + void (* clear_func) (gpointer data); + gpointer result; +} Declaration; + +static guint +parse_declarations (GtkCssParser *parser, + const Declaration *declarations, + guint n_declarations) +{ + guint parsed = 0; + guint i; + + g_assert (n_declarations < 8 * sizeof (guint)); + + while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) + { + gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_OPEN_CURLY); + + for (i = 0; i < n_declarations; i++) + { + if (gtk_css_parser_try_ident (parser, declarations[i].name)) + { + if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON)) + { + gtk_css_parser_error_syntax (parser, "Expected ':' after variable declaration"); + } + else + { + if (parsed & (1 << i)) + { + gtk_css_parser_warn_syntax (parser, "Variable \"%s\" defined multiple times", declarations[i].name); + /* Unset, just to be sure */ + parsed &= ~(1 << i); + if (declarations[i].clear_func) + declarations[i].clear_func (declarations[i].result); + } + if (!declarations[i].parse_func (parser, declarations[i].result)) + { + /* nothing to do */ + } + else if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) + { + gtk_css_parser_error_syntax (parser, "Expected ';' at end of statement"); + if (declarations[i].clear_func) + declarations[i].clear_func (declarations[i].result); + } + else + { + parsed |= (1 << i); + } + } + break; + } + } + if (i == n_declarations) + { + if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_IDENT)) + gtk_css_parser_error_syntax (parser, "No variable named \"%s\"", + gtk_css_parser_get_token (parser)->string.string); + else + gtk_css_parser_error_syntax (parser, "Expected a variable name"); + } + + gtk_css_parser_end_block (parser); + } + + return parsed; +} + +static gboolean +parse_text (GtkCssParser *parser, + gpointer out_data) +{ + *(char **) out_data = gtk_css_parser_consume_string (parser); + return TRUE; +} + +static gboolean +parse_font (GtkCssParser *parser, + gpointer out_data) +{ + char *string; + PangoFontDescription *desc; + + string = gtk_css_parser_consume_string (parser); + desc = pango_font_description_from_string (string); + g_free (string); + + *(PangoFontDescription **) out_data = desc; + + return desc != NULL; +} + +static gboolean +parse_enum_value (GtkCssParser *parser, + GType enum_type, + gpointer out_data) +{ + char *string; + GEnumClass *enum_class; + GEnumValue *enum_value; + + string = gtk_css_parser_consume_ident (parser); + enum_class = g_type_class_ref (enum_type); + enum_value = g_enum_get_value_by_nick (enum_class, string); + g_type_class_unref (enum_class); + g_free (string); + + if (enum_value) + *(int *) out_data = enum_value->value; + + return enum_value != NULL; +} + +static gboolean +parse_alignment (GtkCssParser *parser, + gpointer out_data) +{ + return parse_enum_value (parser, PANGO_TYPE_ALIGNMENT, out_data); +} + +static gboolean +parse_wrap (GtkCssParser *parser, + gpointer out_data) +{ + return parse_enum_value (parser, PANGO_TYPE_WRAP_MODE, out_data); +} + +static gboolean +parse_ellipsize (GtkCssParser *parser, + gpointer out_data) +{ + return parse_enum_value (parser, PANGO_TYPE_ELLIPSIZE_MODE, out_data); +} + +static gboolean +parse_boolean (GtkCssParser *parser, + gpointer out_data) +{ + if (gtk_css_parser_try_ident (parser, "true")) + *(gboolean *) out_data = TRUE; + else if (gtk_css_parser_try_ident (parser, "false")) + *(gboolean *) out_data = FALSE; + else + return FALSE; + return TRUE; +} + +static gboolean +parse_int (GtkCssParser *parser, + gpointer out_data) +{ + return gtk_css_parser_consume_integer (parser, (int *)out_data); +} + +static gboolean +parse_double (GtkCssParser *parser, + gpointer out_data) +{ + return gtk_css_parser_consume_number (parser, (double *)out_data); +} + +static PangoLayout * +parse_layout (GtkCssParser *parser) +{ + char *text = NULL; + PangoAttrList *attrs = NULL; + PangoFontDescription *desc = NULL; + PangoTabArray *tabs = NULL; + gboolean justify = FALSE; + gboolean justify_last_line = FALSE; + gboolean single_paragraph = FALSE; + gboolean auto_dir = TRUE; + PangoAlignment align = PANGO_ALIGN_LEFT; + PangoWrapMode wrap = PANGO_WRAP_WORD; + PangoEllipsizeMode ellipsize = PANGO_ELLIPSIZE_NONE; + int width = -1; + int height = -1; + int indent = 0; + int spacing = 0; + double line_spacing = 0.; + const Declaration declarations[] = { + { "text", parse_text, g_free, &text }, + { "font", parse_font, (GDestroyNotify)pango_font_description_free, &desc }, + { "alignment", parse_alignment, NULL, &align }, + { "wrap", parse_wrap, NULL, &wrap }, + { "ellipsize", parse_ellipsize, NULL, &ellipsize }, + { "justify", parse_boolean, NULL, &justify }, + { "justify-last-line", parse_boolean, NULL, &justify_last_line }, + { "single-paragraph", parse_boolean, NULL, &single_paragraph }, + { "auto-dir", parse_boolean, NULL, &auto_dir }, + { "width", parse_int, NULL, &width }, + { "height", parse_int, NULL, &height }, + { "indent", parse_int, NULL, &indent }, + { "spacing", parse_int, NULL, &spacing }, + { "line-spacing", parse_double, NULL, &line_spacing }, + }; + PangoLayout *layout; + + parse_declarations (parser, declarations, G_N_ELEMENTS (declarations)); + + layout = pango_layout_new (parser_context); + + if (text) + { + pango_layout_set_text (layout, text, -1); + g_free (text); + } + if (attrs) + { + pango_layout_set_attributes (layout, attrs); + pango_attr_list_unref (attrs); + } + if (desc) + { + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + } + if (tabs) + { + pango_layout_set_tabs (layout, tabs); + pango_tab_array_free (tabs); + } + + pango_layout_set_justify (layout, justify); + pango_layout_set_justify_last_line (layout, justify_last_line); + pango_layout_set_single_paragraph_mode (layout, single_paragraph); + pango_layout_set_auto_dir (layout, auto_dir); + pango_layout_set_alignment (layout, align); + pango_layout_set_wrap (layout, wrap); + pango_layout_set_ellipsize (layout,ellipsize); + pango_layout_set_width (layout, width); + pango_layout_set_height (layout, height); + pango_layout_set_indent (layout, indent); + pango_layout_set_spacing (layout, spacing); + pango_layout_set_line_spacing (layout, line_spacing); + + return layout; +} + +static PangoLayout * +parse_toplevel_layout (GtkCssParser *parser) +{ + PangoLayout *layout = NULL; + + gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_OPEN_CURLY); + + if (gtk_css_parser_try_ident (parser, "layout")) + { + + if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) + { + gtk_css_parser_error_syntax (parser, "Expected '{' after node name"); + return FALSE; + } + + gtk_css_parser_end_block_prelude (parser); + + layout = parse_layout (parser); + + if (layout) + { + if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) + gtk_css_parser_error_syntax (parser, "Expected '}' at end of node definition"); + } + } + + gtk_css_parser_end_block (parser); + + return layout; +} + +/* }}} */ /* {{{ Public API */ /** @@ -396,6 +687,19 @@ pango_layout_serialize (PangoLayout *layout) return g_string_free_to_bytes (p.str); } +static void +parser_error (GtkCssParser *parser, + const GtkCssLocation *start, + const GtkCssLocation *end, + const GError *error, + gpointer user_data) +{ + g_print ("from line %ld:%ld to %ld:%ld: %s\n", + start->lines, start->line_chars, + end->lines, end->line_chars, + error->message); +} + /** * pango_layout_deserialize: * @context: a `PangoContext` @@ -413,7 +717,18 @@ PangoLayout * pango_layout_deserialize (PangoContext *context, GBytes *bytes) { - return NULL; + PangoLayout *layout = NULL; + GtkCssParser *parser; + + parser = gtk_css_parser_new_for_bytes (bytes, NULL, parser_error, NULL, NULL); + + parser_context = context; + layout = parse_toplevel_layout (parser); + parser_context = NULL; + + gtk_css_parser_unref (parser); + + return layout; } /* }}} */ -- cgit v1.2.1