diff options
Diffstat (limited to 'pango')
-rw-r--r-- | pango/json/gtkjsonparser.c | 1142 | ||||
-rw-r--r-- | pango/json/gtkjsonparserprivate.h | 69 | ||||
-rw-r--r-- | pango/json/gtkjsonprinter.c | 405 | ||||
-rw-r--r-- | pango/json/gtkjsonprinterprivate.h | 79 | ||||
-rw-r--r-- | pango/meson.build | 2 | ||||
-rw-r--r-- | pango/pango-font.h | 5 | ||||
-rw-r--r-- | pango/pango-layout.h | 10 | ||||
-rw-r--r-- | pango/pango-tabs.c | 19 | ||||
-rw-r--r-- | pango/pango-tabs.h | 4 | ||||
-rw-r--r-- | pango/serializer.c | 1714 |
10 files changed, 2540 insertions, 909 deletions
diff --git a/pango/json/gtkjsonparser.c b/pango/json/gtkjsonparser.c new file mode 100644 index 00000000..91048fec --- /dev/null +++ b/pango/json/gtkjsonparser.c @@ -0,0 +1,1142 @@ +/* + * Copyright © 2021 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + + +#include "config.h" + +#include "gtkjsonparserprivate.h" + +typedef struct _GtkJsonBlock GtkJsonBlock; + +typedef enum { + GTK_JSON_BLOCK_TOPLEVEL, + GTK_JSON_BLOCK_OBJECT, + GTK_JSON_BLOCK_ARRAY, +} GtkJsonBlockType; + +struct _GtkJsonBlock +{ + GtkJsonBlockType type; + const guchar *value; /* start of current value to be consumed by external code */ + const guchar *member_name; /* name of current value, only used for object types */ + gsize index; /* index of the current element */ +}; + +struct _GtkJsonParser +{ + GBytes *bytes; + const guchar *reader; /* current read head, pointing as far as we've read */ + const guchar *end; /* pointer after end of data we're reading */ + + GError *error; /* if an error has happened, it's stored here. Errors aren't recoverable. */ + + GtkJsonBlock *block; /* current block */ + GtkJsonBlock *blocks; /* blocks array */ + GtkJsonBlock *blocks_end; /* blocks array */ + GtkJsonBlock blocks_preallocated[128]; /* preallocated */ +}; + +typedef enum { + WHITESPACE = (1 << 4), + STRING_ELEMENT = (1 << 5), + STRING_MARKER = (1 << 6), +} JsonCharacterType; + +#define JSON_CHARACTER_NODE_MASK ((1 << 4) - 1) + +static const guchar json_character_table[256] = { + ['\t'] = WHITESPACE, + ['\r'] = WHITESPACE, + ['\n'] = WHITESPACE, + [' '] = WHITESPACE | STRING_ELEMENT, + ['!'] = STRING_ELEMENT, + ['"'] = GTK_JSON_STRING | STRING_MARKER, + ['#'] = STRING_ELEMENT, + ['$'] = STRING_ELEMENT, + ['%'] = STRING_ELEMENT, + ['&'] = STRING_ELEMENT, + ['\''] = STRING_ELEMENT, + ['('] = STRING_ELEMENT, + [')'] = STRING_ELEMENT, + ['*'] = STRING_ELEMENT, + ['+'] = STRING_ELEMENT, + [','] = STRING_ELEMENT, + ['-'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['.'] = STRING_ELEMENT, + ['/'] = STRING_ELEMENT, + ['0'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['1'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['2'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['3'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['4'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['5'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['6'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['7'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['8'] = GTK_JSON_NUMBER | STRING_ELEMENT, + ['9'] = GTK_JSON_NUMBER | STRING_ELEMENT, + [':'] = STRING_ELEMENT, + [';'] = STRING_ELEMENT, + ['<'] = STRING_ELEMENT, + ['='] = STRING_ELEMENT, + ['>'] = STRING_ELEMENT, + ['?'] = STRING_ELEMENT, + ['@'] = STRING_ELEMENT, + ['A'] = STRING_ELEMENT, + ['B'] = STRING_ELEMENT, + ['C'] = STRING_ELEMENT, + ['D'] = STRING_ELEMENT, + ['E'] = STRING_ELEMENT, + ['F'] = STRING_ELEMENT, + ['G'] = STRING_ELEMENT, + ['H'] = STRING_ELEMENT, + ['I'] = STRING_ELEMENT, + ['J'] = STRING_ELEMENT, + ['K'] = STRING_ELEMENT, + ['L'] = STRING_ELEMENT, + ['M'] = STRING_ELEMENT, + ['N'] = STRING_ELEMENT, + ['O'] = STRING_ELEMENT, + ['P'] = STRING_ELEMENT, + ['Q'] = STRING_ELEMENT, + ['R'] = STRING_ELEMENT, + ['S'] = STRING_ELEMENT, + ['T'] = STRING_ELEMENT, + ['U'] = STRING_ELEMENT, + ['V'] = STRING_ELEMENT, + ['W'] = STRING_ELEMENT, + ['X'] = STRING_ELEMENT, + ['Y'] = STRING_ELEMENT, + ['Z'] = STRING_ELEMENT, + ['['] = GTK_JSON_ARRAY | STRING_ELEMENT, + ['\\'] = STRING_MARKER, + [']'] = STRING_ELEMENT, + ['^'] = STRING_ELEMENT, + ['_'] = STRING_ELEMENT, + ['`'] = STRING_ELEMENT, + ['a'] = STRING_ELEMENT, + ['b'] = STRING_ELEMENT, + ['c'] = STRING_ELEMENT, + ['d'] = STRING_ELEMENT, + ['e'] = STRING_ELEMENT, + ['f'] = GTK_JSON_BOOLEAN | STRING_ELEMENT, + ['g'] = STRING_ELEMENT, + ['h'] = STRING_ELEMENT, + ['i'] = STRING_ELEMENT, + ['j'] = STRING_ELEMENT, + ['k'] = STRING_ELEMENT, + ['l'] = STRING_ELEMENT, + ['m'] = STRING_ELEMENT, + ['n'] = GTK_JSON_NULL | STRING_ELEMENT, + ['o'] = STRING_ELEMENT, + ['p'] = STRING_ELEMENT, + ['q'] = STRING_ELEMENT, + ['r'] = STRING_ELEMENT, + ['s'] = STRING_ELEMENT, + ['t'] = GTK_JSON_BOOLEAN | STRING_ELEMENT, + ['u'] = STRING_ELEMENT, + ['v'] = STRING_ELEMENT, + ['w'] = STRING_ELEMENT, + ['x'] = STRING_ELEMENT, + ['y'] = STRING_ELEMENT, + ['z'] = STRING_ELEMENT, + ['{'] = GTK_JSON_OBJECT | STRING_ELEMENT, + ['|'] = STRING_ELEMENT, + ['}'] = STRING_ELEMENT, + ['~'] = STRING_ELEMENT, + [127] = STRING_ELEMENT, +}; + +static const guchar * +json_skip_characters (const guchar *start, + const guchar *end, + JsonCharacterType type) +{ + const guchar *s; + + for (s = start; s < end; s++) + { + if (!(json_character_table[*s] & type)) + break; + } + return s; +} + +static const guchar * +json_find_character (const guchar *start, + JsonCharacterType type) +{ + const guchar *s; + + for (s = start; ; s++) + { + if ((json_character_table[*s] & type)) + break; + } + return s; +} + +static void +gtk_json_parser_value_error (GtkJsonParser *self, + const char *format, + ...) G_GNUC_PRINTF(2, 3); +static void +gtk_json_parser_value_error (GtkJsonParser *self, + const char *format, + ...) +{ + va_list args; + + if (self->error) + return; + + va_start (args, format); + self->error = g_error_new_valist (G_FILE_ERROR, + G_FILE_ERROR_FAILED, + format, args); + va_end (args); +} + +static void +gtk_json_parser_syntax_error (GtkJsonParser *self, + const char *format, + ...) G_GNUC_PRINTF(2, 3); +static void +gtk_json_parser_syntax_error (GtkJsonParser *self, + const char *format, + ...) +{ + va_list args; + + if (self->error) + return; + + va_start (args, format); + self->error = g_error_new_valist (G_FILE_ERROR, + G_FILE_ERROR_FAILED, + format, args); + va_end (args); +} + +static gboolean +gtk_json_parser_is_eof (GtkJsonParser *self) +{ + return self->reader >= self->end || *self->reader == '\0'; +} + +static gsize +gtk_json_parser_remaining (GtkJsonParser *self) +{ + g_return_val_if_fail (self->reader <= self->end, 0); + + return self->end - self->reader; +} + +static void +gtk_json_parser_skip_whitespace (GtkJsonParser *self) +{ + self->reader = json_skip_characters (self->reader, self->end, WHITESPACE); +} + +static gboolean +gtk_json_parser_has_char (GtkJsonParser *self, + char c) +{ + return gtk_json_parser_remaining (self) && *self->reader == c; +} + +static gboolean +gtk_json_parser_try_char (GtkJsonParser *self, + char c) +{ + if (!gtk_json_parser_has_char (self, c)) + return FALSE; + + self->reader++; + return TRUE; +} + +static gboolean +gtk_json_parser_try_identifier_len (GtkJsonParser *self, + const char *ident, + gsize len) +{ + if (gtk_json_parser_remaining (self) < len) + return FALSE; + + if (memcmp (self->reader, ident, len) != 0) + return FALSE; + + self->reader += len; + return TRUE; +} + +#define gtk_json_parser_try_identifier(parser, ident) gtk_json_parser_try_identifier_len(parser, ident, strlen(ident)) + +/* + * decode_utf16_surrogate_pair: + * @first: the first UTF-16 code point + * @second: the second UTF-16 code point + * + * Decodes a surrogate pair of UTF-16 code points into the equivalent + * Unicode code point. + * + * If the code points are not valid, 0 is returned. + * + * Returns: the Unicode code point equivalent to the surrogate pair + */ +static inline gunichar +decode_utf16_surrogate_pair (gunichar first, + gunichar second) +{ + if (0xd800 > first || first > 0xdbff || + 0xdc00 > second || second > 0xdfff) + return 0; + + return 0x10000 + | (first & 0x3ff) << 10 + | (second & 0x3ff); +} + +static gsize +gtk_json_unescape_char (const guchar *json_escape, + char out_data[6], + gsize *out_len) +{ + switch (json_escape[1]) + { + case '"': + case '\\': + case '/': + out_data[0] = json_escape[1]; + *out_len = 1; + return 2; + case 'b': + out_data[0] = '\b'; + *out_len = 1; + return 2; + case 'f': + out_data[0] = '\f'; + *out_len = 1; + return 2; + case 'n': + out_data[0] = '\n'; + *out_len = 1; + return 2; + case 'r': + out_data[0] = '\r'; + *out_len = 1; + return 2; + case 't': + out_data[0] = '\t'; + *out_len = 1; + return 2; + case 'u': + { + gunichar unichar = (g_ascii_xdigit_value (json_escape[2]) << 12) | + (g_ascii_xdigit_value (json_escape[3]) << 8) | + (g_ascii_xdigit_value (json_escape[4]) << 4) | + (g_ascii_xdigit_value (json_escape[5])); + gsize result = 6; + + /* resolve UTF-16 surrogates for Unicode characters not in the BMP, + * as per ECMA 404, § 9, "String" + */ + if (g_unichar_type (unichar) == G_UNICODE_SURROGATE) + { + unichar = decode_utf16_surrogate_pair (unichar, + (g_ascii_xdigit_value (json_escape[8]) << 12) | + (g_ascii_xdigit_value (json_escape[9]) << 8) | + (g_ascii_xdigit_value (json_escape[10]) << 4) | + (g_ascii_xdigit_value (json_escape[11]))); + result += 6; + } + *out_len = g_unichar_to_utf8 (unichar, out_data); + return result; + } + default: + g_assert_not_reached (); + return 0; + } +} + +/* The escaped string MUST be valid json, so it must begin + * with " and end with " and must not contain any invalid + * escape codes. + * This function is meant to be fast + */ +static char * +gtk_json_unescape_string (const guchar *escaped) +{ + char buf[6]; + gsize buf_size; + GString *string; + const guchar *last, *s; + + string = NULL; + + g_assert (*escaped == '"'); + last = escaped + 1; + for (s = json_find_character (last, STRING_MARKER); + *s != '"'; + s = json_find_character (last, STRING_MARKER)) + { + g_assert (*s == '\\'); + if (string == NULL) + string = g_string_new (NULL); + g_string_append_len (string, (const char *) last, s - last); + last = s + gtk_json_unescape_char (s, buf, &buf_size); + g_string_append_len (string, buf, buf_size); + } + + if (string) + { + g_string_append_len (string, (const char *) last, s - last); + return g_string_free (string, FALSE); + } + else + { + return g_strndup ((const char *) last, s - last); + } +} + +static gboolean +gtk_json_parser_parse_string (GtkJsonParser *self) +{ + if (!gtk_json_parser_try_char (self, '"')) + { + gtk_json_parser_syntax_error (self, "Not a string"); + return FALSE; + } + + self->reader = json_skip_characters (self->reader, self->end, STRING_ELEMENT); + + while (gtk_json_parser_remaining (self)) + { + if (*self->reader < 0x20) + { + gtk_json_parser_syntax_error (self, "Disallowed control character in string literal"); + return FALSE; + } + else if (*self->reader > 127) + { + gunichar c = g_utf8_get_char_validated ((const char *) self->reader, gtk_json_parser_remaining (self)); + if (c == (gunichar) -2 || c == (gunichar) -1) + { + gtk_json_parser_syntax_error (self, "Invalid UTF-8"); + return FALSE; + } + self->reader = (const guchar *) g_utf8_next_char ((const char *) self->reader); + } + else if (*self->reader == '"') + { + self->reader++; + return TRUE; + } + else if (*self->reader == '\\') + { + if (gtk_json_parser_remaining (self) < 2) + goto end; + self->reader++; + switch (*self->reader) + { + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + break; + + case 'u': + /* lots of work necessary to validate the unicode escapes here */ + if (gtk_json_parser_remaining (self) < 5 || + !g_ascii_isxdigit (self->reader[1]) || + !g_ascii_isxdigit (self->reader[2]) || + !g_ascii_isxdigit (self->reader[3]) || + !g_ascii_isxdigit (self->reader[4])) + { + gtk_json_parser_syntax_error (self, "Invalid Unicode escape sequence"); + return FALSE; + } + else + { + gunichar unichar = (g_ascii_xdigit_value (self->reader[1]) << 12) | + (g_ascii_xdigit_value (self->reader[2]) << 8) | + (g_ascii_xdigit_value (self->reader[3]) << 4) | + (g_ascii_xdigit_value (self->reader[4])); + + self->reader += 4; + /* resolve UTF-16 surrogates for Unicode characters not in the BMP, + * as per ECMA 404, § 9, "String" + */ + if (g_unichar_type (unichar) == G_UNICODE_SURROGATE) + { + if (gtk_json_parser_remaining (self) >= 7 && + self->reader[1] == '\\' && + self->reader[2] == 'u' && + g_ascii_isxdigit (self->reader[3]) && + g_ascii_isxdigit (self->reader[4]) && + g_ascii_isxdigit (self->reader[5]) && + g_ascii_isxdigit (self->reader[6])) + { + unichar = decode_utf16_surrogate_pair (unichar, + (g_ascii_xdigit_value (self->reader[3]) << 12) | + (g_ascii_xdigit_value (self->reader[4]) << 8) | + (g_ascii_xdigit_value (self->reader[5]) << 4) | + (g_ascii_xdigit_value (self->reader[6]))); + self->reader += 6; + } + else + { + unichar = 0; + } + } + + if (unichar == 0) + { + gtk_json_parser_syntax_error (self, "Invalid UTF-16 surrogate pair"); + return FALSE; + } + } + break; + default: + gtk_json_parser_syntax_error (self, "Unknown escape sequence"); + return FALSE; + } + self->reader++; + } + + self->reader = json_skip_characters (self->reader, self->end, STRING_ELEMENT); + } + +end: + gtk_json_parser_syntax_error (self, "Unterminated string literal"); + return FALSE; +} + +static gboolean +gtk_json_parser_parse_number (GtkJsonParser *self) +{ + /* sign */ + gtk_json_parser_try_char (self, '-'); + + /* integer part */ + if (!gtk_json_parser_try_char (self, '0')) + { + if (gtk_json_parser_is_eof (self) || + !g_ascii_isdigit (*self->reader)) + goto out; + + self->reader++; + + while (!gtk_json_parser_is_eof (self) && g_ascii_isdigit (*self->reader)) + self->reader++; + } + + /* fractional part */ + if (gtk_json_parser_remaining (self) >= 2 && *self->reader == '.' && g_ascii_isdigit (self->reader[1])) + { + self->reader += 2; + + while (!gtk_json_parser_is_eof (self) && g_ascii_isdigit (*self->reader)) + self->reader++; + } + + /* exponent */ + if (gtk_json_parser_remaining (self) >= 2 && (self->reader[0] == 'e' || self->reader[0] == 'E') && + (g_ascii_isdigit (self->reader[1]) || + (gtk_json_parser_remaining (self) >= 3 && (self->reader[1] == '+' || self->reader[1] == '-') && g_ascii_isdigit (self->reader[2])))) + { + self->reader += 2; + + while (!gtk_json_parser_is_eof (self) && g_ascii_isdigit (*self->reader)) + self->reader++; + } + return TRUE; + +out: + gtk_json_parser_syntax_error (self, "Not a valid number"); + return FALSE; +} + +static gboolean +gtk_json_parser_parse_value (GtkJsonParser *self) +{ + if (gtk_json_parser_is_eof (self)) + { + gtk_json_parser_syntax_error (self, "Unexpected end of document"); + return FALSE; + } + + switch (json_character_table[*self->block->value] & JSON_CHARACTER_NODE_MASK) + { + case GTK_JSON_STRING: + return gtk_json_parser_parse_string (self); + + case GTK_JSON_NUMBER: + return gtk_json_parser_parse_number (self); + + case GTK_JSON_NULL: + if (gtk_json_parser_try_identifier (self, "null")) + return TRUE; + break; + + case GTK_JSON_BOOLEAN: + if (gtk_json_parser_try_identifier (self, "true") || + gtk_json_parser_try_identifier (self, "false")) + return TRUE; + break; + + case GTK_JSON_OBJECT: + case GTK_JSON_ARRAY: + /* don't preparse objects */ + return TRUE; + + default: + break; + } + + gtk_json_parser_syntax_error (self, "Expected a value"); + return FALSE; +} + +static void +gtk_json_parser_push_block (GtkJsonParser *self, + GtkJsonBlockType type) +{ + self->block++; + if (self->block == self->blocks_end) + { + gsize old_size = self->blocks_end - self->blocks; + gsize new_size = old_size + 128; + + if (self->blocks == self->blocks_preallocated) + { + self->blocks = g_new (GtkJsonBlock, new_size); + memcpy (self->blocks, self->blocks_preallocated, sizeof (GtkJsonBlock) * G_N_ELEMENTS (self->blocks_preallocated)); + } + else + { + self->blocks = g_renew (GtkJsonBlock, self->blocks, new_size); + } + self->blocks_end = self->blocks + new_size; + self->block = self->blocks + old_size; + } + + self->block->type = type; + self->block->member_name = 0; + self->block->value = 0; + self->block->index = 0; +} + +static void +gtk_json_parser_pop_block (GtkJsonParser *self) +{ + g_assert (self->block > self->blocks); + self->block--; +} + +GtkJsonParser * +gtk_json_parser_new_for_string (const char *string, + gssize size) +{ + GtkJsonParser *self; + GBytes *bytes; + + bytes = g_bytes_new (string, size >= 0 ? size : strlen (string)); + + self = gtk_json_parser_new_for_bytes (bytes); + + g_bytes_unref (bytes); + + return self; +} + +GtkJsonParser * +gtk_json_parser_new_for_bytes (GBytes *bytes) +{ + GtkJsonParser *self; + gsize size; + + g_return_val_if_fail (bytes != NULL, NULL); + + self = g_slice_new0 (GtkJsonParser); + + self->bytes = g_bytes_ref (bytes); + self->reader = g_bytes_get_data (bytes, &size); + self->end = self->reader + size; + + self->blocks = self->blocks_preallocated; + self->blocks_end = self->blocks + G_N_ELEMENTS (self->blocks_preallocated); + self->block = self->blocks; + self->block->type = GTK_JSON_BLOCK_TOPLEVEL; + + gtk_json_parser_skip_whitespace (self); + self->block->value = self->reader; + gtk_json_parser_parse_value (self); + + return self; +} + +void +gtk_json_parser_free (GtkJsonParser *self) +{ + if (self == NULL) + return; + + g_clear_error (&self->error); + + g_bytes_unref (self->bytes); + + if (self->blocks != self->blocks_preallocated) + g_free (self->blocks); + + g_slice_free (GtkJsonParser, self); +} + +static gboolean +gtk_json_parser_skip_block (GtkJsonParser *self) +{ + if (self->reader != self->block->value) + return TRUE; + + if (*self->reader == '{') + { + return gtk_json_parser_start_object (self) && + gtk_json_parser_end (self); + } + else if (*self->reader == '[') + { + return gtk_json_parser_start_array (self) && + gtk_json_parser_end (self); + } + else + { + g_assert_not_reached (); + return FALSE; + } +} + +gboolean +gtk_json_parser_next (GtkJsonParser *self) +{ + if (self->error) + return FALSE; + + if (self->block->value == NULL) + return FALSE; + + if (!gtk_json_parser_skip_block (self)) + { + g_assert (self->error); + return FALSE; + } + + switch (self->block->type) + { + case GTK_JSON_BLOCK_TOPLEVEL: + gtk_json_parser_skip_whitespace (self); + if (gtk_json_parser_is_eof (self)) + { + self->block->value = NULL; + if (gtk_json_parser_remaining (self)) + { + gtk_json_parser_syntax_error (self, "Unexpected nul byte in document"); + } + } + else + { + gtk_json_parser_syntax_error (self, "Data at end of document"); + } + return FALSE; + + case GTK_JSON_BLOCK_OBJECT: + gtk_json_parser_skip_whitespace (self); + if (gtk_json_parser_is_eof (self)) + { + gtk_json_parser_syntax_error (self, "Unexpected end of document"); + self->block->member_name = NULL; + self->block->value = NULL; + } + if (gtk_json_parser_has_char (self, '}')) + { + self->block->member_name = NULL; + self->block->value = NULL; + return FALSE; + } + if (!gtk_json_parser_try_char (self, ',')) + { + gtk_json_parser_syntax_error (self, "Expected a ',' to separate object members"); + return FALSE; + } + gtk_json_parser_skip_whitespace (self); + self->block->member_name = self->reader; + + if (!gtk_json_parser_parse_string (self)) + return FALSE; + gtk_json_parser_skip_whitespace (self); + if (!gtk_json_parser_try_char (self, ':')) + { + gtk_json_parser_syntax_error (self, "Missing ':' after member name"); + return FALSE; + } + + gtk_json_parser_skip_whitespace (self); + self->block->value = self->reader; + if (!gtk_json_parser_parse_value (self)) + return FALSE; + break; + + case GTK_JSON_BLOCK_ARRAY: + gtk_json_parser_skip_whitespace (self); + if (gtk_json_parser_is_eof (self)) + { + gtk_json_parser_syntax_error (self, "Unexpected end of document"); + self->block->member_name = NULL; + self->block->value = NULL; + } + if (gtk_json_parser_has_char (self, ']')) + { + self->block->value = NULL; + return FALSE; + } + + if (!gtk_json_parser_try_char (self, ',')) + { + gtk_json_parser_syntax_error (self, "Expected a ',' to separate array members"); + return FALSE; + } + + gtk_json_parser_skip_whitespace (self); + self->block->value = self->reader; + if (!gtk_json_parser_parse_value (self)) + return FALSE; + break; + + default: + g_assert_not_reached (); + break; + } + + return TRUE; +} + +GtkJsonNode +gtk_json_parser_get_node (GtkJsonParser *self) +{ + if (self->error) + return GTK_JSON_NONE; + + if (self->block->value == NULL) + return GTK_JSON_NONE; + + return (json_character_table[*self->block->value] & JSON_CHARACTER_NODE_MASK); +} + +const GError * +gtk_json_parser_get_error (GtkJsonParser *self) +{ + return self->error; +} + +char * +gtk_json_parser_get_member_name (GtkJsonParser *self) +{ + if (self->error) + return NULL; + + if (self->block->type != GTK_JSON_BLOCK_OBJECT) + return NULL; + + if (self->block->member_name == NULL) + return NULL; + + return gtk_json_unescape_string (self->block->member_name); +} + +gssize +gtk_json_parser_select_member (GtkJsonParser *self, + const char * const *options) +{ + char *member_name; + gssize i; + + member_name = gtk_json_parser_get_member_name (self); + if (member_name == NULL) + return -1; + + for (i = 0; options[i]; i++) + { + if (strcmp (member_name, options[i]) == 0) + break; + } + if (options[i] == NULL) + i = -1; + + g_free (member_name); + + return i; +} + +gboolean +gtk_json_parser_get_boolean (GtkJsonParser *self) +{ + if (self->error) + return FALSE; + + if (self->block->value == NULL) + return FALSE; + + if (*self->block->value == 't') + return TRUE; + else if (*self->block->value == 'f') + return FALSE; + + gtk_json_parser_value_error (self, "Expected a boolean value"); + return FALSE; +} + +double +gtk_json_parser_get_number (GtkJsonParser *self) +{ + double result; + + if (self->error) + return 0; + + if (self->block->value == NULL) + return 0; + + if (!strchr ("-0123456789", *self->block->value)) + { + gtk_json_parser_value_error (self, "Expected a number"); + return 0; + } + + errno = 0; + if (gtk_json_parser_remaining (self) == 0) + { + /* need terminated string here */ + char *s = g_strndup ((const char *) self->block->value, self->reader - self->block->value); + result = g_ascii_strtod (s, NULL); + g_free (s); + } + else + { + result = g_ascii_strtod ((const char *) self->block->value, NULL); + } + + if (errno) + { + if (errno == ERANGE) + gtk_json_parser_value_error (self, "Number out of range"); + else + gtk_json_parser_value_error (self, "%s", g_strerror (errno)); + + return 0; + } + + return result; +} + +int +gtk_json_parser_get_int (GtkJsonParser *self) +{ + int result; + + if (self->error) + return 0; + + if (self->block->value == NULL) + return 0; + + if (!strchr ("-0123456789", *self->block->value)) + { + gtk_json_parser_value_error (self, "Expected a number"); + return 0; + } + + errno = 0; + if (gtk_json_parser_remaining (self) == 0) + { + /* need terminated string here */ + char *s = g_strndup ((const char *) self->block->value, self->reader - self->block->value); + result = g_ascii_strtod (s, NULL); + g_free (s); + } + else + { + result = (int) g_ascii_strtoll ((const char *) self->block->value, NULL, 10); + } + + if (errno) + { + if (errno == ERANGE) + gtk_json_parser_value_error (self, "Number out of range"); + else + gtk_json_parser_value_error (self, "%s", g_strerror (errno)); + + return 0; + } + + return result; +} + +#if 0 +int gtk_json_parser_get_int (GtkJsonParser *self); +guint gtk_json_parser_get_uint (GtkJsonParser *self); +#endif + +char * +gtk_json_parser_get_string (GtkJsonParser *self) +{ + if (self->error) + return g_strdup (""); + + if (self->block->value == NULL) + return g_strdup (""); + + if (*self->block->value != '"') + { + gtk_json_parser_value_error (self, "Expected a string"); + return g_strdup (""); + } + + return gtk_json_unescape_string (self->block->value); +} + +gboolean +gtk_json_parser_start_object (GtkJsonParser *self) +{ + if (self->error) + return FALSE; + + if (!gtk_json_parser_try_char (self, '{')) + { + gtk_json_parser_value_error (self, "Expected an object"); + return FALSE; + } + + gtk_json_parser_push_block (self, GTK_JSON_BLOCK_OBJECT); + + gtk_json_parser_skip_whitespace (self); + if (gtk_json_parser_is_eof (self)) + { + gtk_json_parser_syntax_error (self, "Unexpected end of document"); + return FALSE; + } + if (gtk_json_parser_has_char (self, '}')) + return TRUE; + self->block->member_name = self->reader; + + if (!gtk_json_parser_parse_string (self)) + return FALSE; + gtk_json_parser_skip_whitespace (self); + if (!gtk_json_parser_try_char (self, ':')) + { + gtk_json_parser_syntax_error (self, "Missing ':' after member name"); + return FALSE; + } + + gtk_json_parser_skip_whitespace (self); + self->block->value = self->reader; + if (!gtk_json_parser_parse_value (self)) + return FALSE; + + return TRUE; +} + +gboolean +gtk_json_parser_start_array (GtkJsonParser *self) +{ + if (self->error) + return FALSE; + + if (!gtk_json_parser_try_char (self, '[')) + { + gtk_json_parser_value_error (self, "Expected an array"); + return FALSE; + } + + gtk_json_parser_push_block (self, GTK_JSON_BLOCK_ARRAY); + gtk_json_parser_skip_whitespace (self); + if (gtk_json_parser_is_eof (self)) + { + gtk_json_parser_syntax_error (self, "Unexpected end of document"); + return FALSE; + } + if (gtk_json_parser_has_char (self, ']')) + { + self->block->value = NULL; + return TRUE; + } + self->block->value = self->reader; + if (!gtk_json_parser_parse_value (self)) + return FALSE; + + return TRUE; +} + +gboolean +gtk_json_parser_end (GtkJsonParser *self) +{ + char bracket; + + g_return_val_if_fail (self != NULL, FALSE); + + while (gtk_json_parser_next (self)); + + if (self->error) + return FALSE; + + switch (self->block->type) + { + case GTK_JSON_BLOCK_OBJECT: + bracket = '}'; + break; + case GTK_JSON_BLOCK_ARRAY: + bracket = ']'; + break; + case GTK_JSON_BLOCK_TOPLEVEL: + default: + g_return_val_if_reached (FALSE); + } + + if (!gtk_json_parser_try_char (self, bracket)) + { + gtk_json_parser_syntax_error (self, "No terminating '%c'", bracket); + return FALSE; + } + + gtk_json_parser_pop_block (self); + + return TRUE; +} + +void +gtk_json_parser_set_error (GtkJsonParser *self, + GError *error) +{ + if (self->error) + g_error_free (error); + else + self->error = error; +} diff --git a/pango/json/gtkjsonparserprivate.h b/pango/json/gtkjsonparserprivate.h new file mode 100644 index 00000000..81c001cc --- /dev/null +++ b/pango/json/gtkjsonparserprivate.h @@ -0,0 +1,69 @@ +/* + * Copyright © 2021 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + + +#ifndef __GTK_JSON_PARSER_H__ +#define __GTK_JSON_PARSER_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef enum { + GTK_JSON_NONE, + GTK_JSON_NULL, + GTK_JSON_BOOLEAN, + GTK_JSON_NUMBER, + GTK_JSON_STRING, + GTK_JSON_OBJECT, + GTK_JSON_ARRAY +} GtkJsonNode; + +typedef struct _GtkJsonParser GtkJsonParser; + +GtkJsonParser * gtk_json_parser_new_for_bytes (GBytes *bytes); +GtkJsonParser * gtk_json_parser_new_for_string (const char *string, + gssize size); + +void gtk_json_parser_free (GtkJsonParser *self); + +gboolean gtk_json_parser_next (GtkJsonParser *self); +GtkJsonNode gtk_json_parser_get_node (GtkJsonParser *self); +const GError * gtk_json_parser_get_error (GtkJsonParser *self) G_GNUC_PURE; +char * gtk_json_parser_get_member_name (GtkJsonParser *self); +gssize gtk_json_parser_select_member (GtkJsonParser *self, + const char * const *options); + +gboolean gtk_json_parser_get_boolean (GtkJsonParser *self); +double gtk_json_parser_get_number (GtkJsonParser *self); +int gtk_json_parser_get_int (GtkJsonParser *self); +guint gtk_json_parser_get_uint (GtkJsonParser *self); +char * gtk_json_parser_get_string (GtkJsonParser *self); + +gboolean gtk_json_parser_start_object (GtkJsonParser *self); +gboolean gtk_json_parser_start_array (GtkJsonParser *self); +gboolean gtk_json_parser_end (GtkJsonParser *self); + +void gtk_json_parser_set_error (GtkJsonParser *self, + GError *error); + + +G_END_DECLS + +#endif /* __GTK_JSON_PARSER_H__ */ diff --git a/pango/json/gtkjsonprinter.c b/pango/json/gtkjsonprinter.c new file mode 100644 index 00000000..f2f1e273 --- /dev/null +++ b/pango/json/gtkjsonprinter.c @@ -0,0 +1,405 @@ +/* + * Copyright © 2021 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + + +#include "config.h" + +#include "gtkjsonprinterprivate.h" + +typedef struct _GtkJsonBlock GtkJsonBlock; + +typedef enum { + GTK_JSON_BLOCK_TOPLEVEL, + GTK_JSON_BLOCK_OBJECT, + GTK_JSON_BLOCK_ARRAY, +} GtkJsonBlockType; + +struct _GtkJsonBlock +{ + GtkJsonBlockType type; + gsize n_elements; /* number of elements already written */ +}; + +struct _GtkJsonPrinter +{ + GtkJsonPrinterFlags flags; + char *indentation; + + GtkJsonPrinterWriteFunc write_func; + gpointer user_data; + GDestroyNotify user_destroy; + + GtkJsonBlock *block; /* current block */ + GtkJsonBlock *blocks; /* blocks array */ + GtkJsonBlock *blocks_end; /* blocks array */ + GtkJsonBlock blocks_preallocated[128]; /* preallocated */ +}; + +static void +gtk_json_printer_push_block (GtkJsonPrinter *self, + GtkJsonBlockType type) +{ + self->block++; + if (self->block == self->blocks_end) + { + gsize old_size = self->blocks_end - self->blocks; + gsize new_size = old_size + 128; + + if (self->blocks == self->blocks_preallocated) + { + self->blocks = g_new (GtkJsonBlock, new_size); + memcpy (self->blocks, self->blocks_preallocated, sizeof (GtkJsonBlock) * G_N_ELEMENTS (self->blocks_preallocated)); + } + else + { + self->blocks = g_renew (GtkJsonBlock, self->blocks, new_size); + } + self->blocks_end = self->blocks + new_size; + self->block = self->blocks + old_size; + } + + self->block->type = type; + self->block->n_elements = 0; +} + +static void +gtk_json_printer_pop_block (GtkJsonPrinter *self) +{ + g_assert (self->block > self->blocks); + self->block--; +} + +GtkJsonPrinter * +gtk_json_printer_new (GtkJsonPrinterWriteFunc write_func, + gpointer data, + GDestroyNotify destroy) +{ + GtkJsonPrinter *self; + + g_return_val_if_fail (write_func, NULL); + + self = g_slice_new0 (GtkJsonPrinter); + + self->flags = 0; + self->indentation = g_strdup (" "); + + self->write_func = write_func; + self->user_data = data; + self->user_destroy = destroy; + + self->blocks = self->blocks_preallocated; + self->blocks_end = self->blocks + G_N_ELEMENTS (self->blocks_preallocated); + self->block = self->blocks; + self->block->type = GTK_JSON_BLOCK_TOPLEVEL; + + return self; +} + +void +gtk_json_printer_free (GtkJsonPrinter *self) +{ + g_return_if_fail (self != NULL); + + g_free (self->indentation); + + if (self->user_destroy) + self->user_destroy (self->user_data); + + if (self->blocks != self->blocks_preallocated) + g_free (self->blocks); + + g_slice_free (GtkJsonPrinter, self); +} + +static gboolean +gtk_json_printer_has_flag (GtkJsonPrinter *self, + GtkJsonPrinterFlags flag) +{ + return (self->flags & flag) ? TRUE : FALSE; +} + +gsize +gtk_json_printer_get_depth (GtkJsonPrinter *self) +{ + return self->block - self->blocks; +} + +gsize +gtk_json_printer_get_n_elements (GtkJsonPrinter *self) +{ + return self->block->n_elements; +} + +void +gtk_json_printer_set_flags (GtkJsonPrinter *self, + GtkJsonPrinterFlags flags) +{ + g_return_if_fail (self != NULL); + + self->flags = flags; +} + +GtkJsonPrinterFlags +gtk_json_printer_get_flags (GtkJsonPrinter *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return self->flags; +} + +void +gtk_json_printer_set_indentation (GtkJsonPrinter *self, + gsize amount) +{ + g_return_if_fail (self != NULL); + + g_free (self->indentation); + + self->indentation = g_malloc (amount + 1); + memset (self->indentation, ' ', amount); + self->indentation[amount] = 0; +} + +gsize +gtk_json_printer_get_indentation (GtkJsonPrinter *self) +{ + g_return_val_if_fail (self != NULL, 2); + + return strlen (self->indentation); +} + +static void +gtk_json_printer_write (GtkJsonPrinter *self, + const char *s) +{ + self->write_func (self, s, self->user_data); +} + +static char * +gtk_json_printer_escape_string (GtkJsonPrinter *self, + const char *str) +{ + GString *string; + + string = g_string_new (NULL); + string = g_string_append_c (string, '"'); + + for (; *str != '\0'; str = g_utf8_next_char (str)) + { + switch (*str) + { + case '"': + g_string_append (string, "\\\""); + break; + case '\\': + g_string_append (string, "\\\\"); + break; + case '\b': + g_string_append (string, "\\b"); + break; + case '\f': + g_string_append (string, "\\f"); + break; + case '\n': + g_string_append (string, "\\n"); + break; + case '\r': + g_string_append (string, "\\r"); + break; + case '\t': + g_string_append (string, "\\t"); + break; + default: + if ((int) *str < 0x20) + { + if ((guint) *str < 0x20 || gtk_json_printer_has_flag (self, GTK_JSON_PRINTER_ASCII)) + g_string_append_printf (string, "\\u%04x", g_utf8_get_char (str)); + else + g_string_append_unichar (string, g_utf8_get_char (str)); + } + else + g_string_append_c (string, *str); + } + } + + string = g_string_append_c (string, '"'); + return g_string_free (string, FALSE); +} + +static void +gtk_json_printer_newline (GtkJsonPrinter *self) +{ + gsize depth; + + if (!gtk_json_printer_has_flag (self, GTK_JSON_PRINTER_PRETTY)) + return; + + gtk_json_printer_write (self, "\n"); + for (depth = gtk_json_printer_get_depth (self); depth-->0;) + gtk_json_printer_write (self, self->indentation); +} + +static void +gtk_json_printer_begin_member (GtkJsonPrinter *self, + const char *name) +{ + if (gtk_json_printer_get_n_elements (self) > 0) + gtk_json_printer_write (self, ","); + if (self->block->type != GTK_JSON_BLOCK_TOPLEVEL || gtk_json_printer_get_n_elements (self) > 0) + gtk_json_printer_newline (self); + + self->block->n_elements++; + + if (name) + { + char *escaped = gtk_json_printer_escape_string (self, name); + gtk_json_printer_write (self, escaped); + g_free (escaped); + if (gtk_json_printer_has_flag (self, GTK_JSON_PRINTER_PRETTY)) + gtk_json_printer_write (self, " : "); + else + gtk_json_printer_write (self, ":"); + } +} + +void +gtk_json_printer_add_boolean (GtkJsonPrinter *self, + const char *name, + gboolean value) +{ + g_return_if_fail (self != NULL); + g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); + + gtk_json_printer_begin_member (self, name); + gtk_json_printer_write (self, value ? "true" : "false"); +} + +void +gtk_json_printer_add_number (GtkJsonPrinter *self, + const char *name, + double value) +{ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + g_return_if_fail (self != NULL); + g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); + + gtk_json_printer_begin_member (self, name); + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, value); + gtk_json_printer_write (self, buf); +} + +void +gtk_json_printer_add_integer (GtkJsonPrinter *self, + const char *name, + int value) +{ + char buf[128]; + + g_return_if_fail (self != NULL); + g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); + + gtk_json_printer_begin_member (self, name); + g_snprintf (buf, sizeof (buf), "%d", value); + gtk_json_printer_write (self, buf); +} + +void +gtk_json_printer_add_string (GtkJsonPrinter *self, + const char *name, + const char *s) +{ + char *escaped; + + g_return_if_fail (self != NULL); + g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); + g_return_if_fail (s != NULL); + + gtk_json_printer_begin_member (self, name); + escaped = gtk_json_printer_escape_string (self, s); + gtk_json_printer_write (self, escaped); + g_free (escaped); +} + +void +gtk_json_printer_add_null (GtkJsonPrinter *self, + const char *name) +{ + g_return_if_fail (self != NULL); + g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); + + gtk_json_printer_begin_member (self, name); + gtk_json_printer_write (self, "null"); +} + +void +gtk_json_printer_start_object (GtkJsonPrinter *self, + const char *name) +{ + g_return_if_fail (self != NULL); + g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); + + gtk_json_printer_begin_member (self, name); + gtk_json_printer_write (self, "{"); + gtk_json_printer_push_block (self, GTK_JSON_BLOCK_OBJECT); +} + +void +gtk_json_printer_start_array (GtkJsonPrinter *self, + const char *name) +{ + g_return_if_fail (self != NULL); + g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); + + gtk_json_printer_begin_member (self, name); + gtk_json_printer_write (self, "["); + gtk_json_printer_push_block (self, GTK_JSON_BLOCK_ARRAY); +} + +void +gtk_json_printer_end (GtkJsonPrinter *self) +{ + const char *bracket; + gboolean empty; + + g_return_if_fail (self != NULL); + + switch (self->block->type) + { + case GTK_JSON_BLOCK_OBJECT: + bracket = "}"; + break; + case GTK_JSON_BLOCK_ARRAY: + bracket = "]"; + break; + case GTK_JSON_BLOCK_TOPLEVEL: + default: + g_return_if_reached (); + } + + empty = gtk_json_printer_get_n_elements (self) == 0; + gtk_json_printer_pop_block (self); + + if (!empty) + { + gtk_json_printer_newline (self); + } + gtk_json_printer_write (self, bracket); +} + diff --git a/pango/json/gtkjsonprinterprivate.h b/pango/json/gtkjsonprinterprivate.h new file mode 100644 index 00000000..e25a1b1d --- /dev/null +++ b/pango/json/gtkjsonprinterprivate.h @@ -0,0 +1,79 @@ +/* + * Copyright © 2021 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + + +#ifndef __GTK_JSON_PRINTER_H__ +#define __GTK_JSON_PRINTER_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _GtkJsonPrinter GtkJsonPrinter; + +typedef enum { + GTK_JSON_PRINTER_PRETTY = (1 << 0), + GTK_JSON_PRINTER_ASCII = (1 << 1), +} GtkJsonPrinterFlags; + +typedef void (* GtkJsonPrinterWriteFunc) (GtkJsonPrinter *printer, + const char *s, + gpointer user_data); + + +GtkJsonPrinter * gtk_json_printer_new (GtkJsonPrinterWriteFunc write_func, + gpointer data, + GDestroyNotify destroy); +void gtk_json_printer_free (GtkJsonPrinter *self); + +void gtk_json_printer_set_flags (GtkJsonPrinter *self, + GtkJsonPrinterFlags flags); +GtkJsonPrinterFlags gtk_json_printer_get_flags (GtkJsonPrinter *self); +void gtk_json_printer_set_indentation (GtkJsonPrinter *self, + gsize amount); +gsize gtk_json_printer_get_indentation (GtkJsonPrinter *self); + +gsize gtk_json_printer_get_depth (GtkJsonPrinter *self); +gsize gtk_json_printer_get_n_elements (GtkJsonPrinter *self); + +void gtk_json_printer_add_boolean (GtkJsonPrinter *self, + const char *name, + gboolean value); +void gtk_json_printer_add_number (GtkJsonPrinter *self, + const char *name, + double value); +void gtk_json_printer_add_integer (GtkJsonPrinter *self, + const char *name, + int value); +void gtk_json_printer_add_string (GtkJsonPrinter *self, + const char *name, + const char *s); +void gtk_json_printer_add_null (GtkJsonPrinter *self, + const char *name); + +void gtk_json_printer_start_object (GtkJsonPrinter *self, + const char *name); +void gtk_json_printer_start_array (GtkJsonPrinter *self, + const char *name); +void gtk_json_printer_end (GtkJsonPrinter *self); + + +G_END_DECLS + +#endif /* __GTK_JSON_PRINTER_H__ */ diff --git a/pango/meson.build b/pango/meson.build index 7c7bb280..4e6f746e 100644 --- a/pango/meson.build +++ b/pango/meson.build @@ -28,6 +28,8 @@ pango_sources = [ 'reorder-items.c', 'shape.c', 'serializer.c', + 'json/gtkjsonparser.c', + 'json/gtkjsonprinter.c', ] pango_headers = [ diff --git a/pango/pango-font.h b/pango/pango-font.h index bfe4bc31..f4e619a1 100644 --- a/pango/pango-font.h +++ b/pango/pango-font.h @@ -637,6 +637,11 @@ PangoLanguage ** pango_font_get_languages (PangoFont *font); PANGO_AVAILABLE_IN_1_50 GBytes * pango_font_serialize (PangoFont *font); +PANGO_AVAILABLE_IN_1_50 +PangoFont * pango_font_deserialize (PangoContext *context, + GBytes *bytes, + GError **error); + /** * PANGO_GLYPH_EMPTY: * diff --git a/pango/pango-layout.h b/pango/pango-layout.h index 32dc16f0..7b3c6ca6 100644 --- a/pango/pango-layout.h +++ b/pango/pango-layout.h @@ -382,20 +382,16 @@ gboolean pango_layout_write_to_file (PangoLayout /** * 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 + * @PANGO_LAYOUT_DESERIALIZE_INVALID: Unspecified error + * @PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE: A JSon value could not be * interpreted - * @PANGO_LAYOUT_SERIALIZE_MISSING_VALUE: A required JSon member was + * @PANGO_LAYOUT_DESERIALIZE_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; diff --git a/pango/pango-tabs.c b/pango/pango-tabs.c index 0665161c..ab01cfe5 100644 --- a/pango/pango-tabs.c +++ b/pango/pango-tabs.c @@ -370,6 +370,25 @@ pango_tab_array_get_positions_in_pixels (PangoTabArray *tab_array) } /** + * pango_tab_array_set_positions_in_pixels: + * @tab_array: a `PangoTabArray` + * @positions_in_pixels: whether positions are in pixels + * + * Sets whether positions in this array are specified in + * pixels. + * + * Since: 1.50 + */ +void +pango_tab_array_set_positions_in_pixels (PangoTabArray *tab_array, + gboolean positions_in_pixels) +{ + g_return_if_fail (tab_array != NULL); + + tab_array->positions_in_pixels = positions_in_pixels; +} + +/** * pango_tab_array_to_string: * @tab_array: a `PangoTabArray` * diff --git a/pango/pango-tabs.h b/pango/pango-tabs.h index 8ca94fdb..a804977b 100644 --- a/pango/pango-tabs.h +++ b/pango/pango-tabs.h @@ -94,6 +94,10 @@ PANGO_AVAILABLE_IN_ALL gboolean pango_tab_array_get_positions_in_pixels (PangoTabArray *tab_array); PANGO_AVAILABLE_IN_1_50 +void pango_tab_array_set_positions_in_pixels (PangoTabArray *tab_array, + gboolean positions_in_pixels); + +PANGO_AVAILABLE_IN_1_50 char * pango_tab_array_to_string (PangoTabArray *tab_array); PANGO_AVAILABLE_IN_1_50 PangoTabArray * pango_tab_array_from_string (const char *text); diff --git a/pango/serializer.c b/pango/serializer.c index fe0fc25c..86e6b004 100644 --- a/pango/serializer.c +++ b/pango/serializer.c @@ -28,7 +28,8 @@ #include <pango/pango-font-private.h> #include <hb-ot.h> -#include <json-glib/json-glib.h> +#include "pango/json/gtkjsonparserprivate.h" +#include "pango/json/gtkjsonprinterprivate.h" /* {{{ Error handling */ @@ -58,10 +59,11 @@ get_enum_type (PangoAttrType attr_type) } static void -add_enum_value (JsonBuilder *builder, - GType type, - int value, - gboolean allow_extra) +add_enum_value (GtkJsonPrinter *printer, + const char *member, + GType type, + int value, + gboolean allow_extra) { GEnumClass *enum_class; GEnumValue *enum_value; @@ -70,51 +72,38 @@ add_enum_value (JsonBuilder *builder, enum_value = g_enum_get_value (enum_class, value); if (enum_value) - json_builder_add_string_value (builder, enum_value->value_nick); + gtk_json_printer_add_string (printer, member, enum_value->value_nick); else if (allow_extra) - { - char buf[128]; - g_snprintf (buf, 128, "%d", value); - json_builder_add_string_value (builder, buf); - } + gtk_json_printer_add_integer (printer, member, value); else - json_builder_add_string_value (builder, "ERROR"); - + gtk_json_printer_add_string (printer, member, "ERROR"); } static void -add_attribute (JsonBuilder *builder, +add_attribute (GtkJsonPrinter *printer, PangoAttribute *attr) { char *str; - json_builder_begin_object (builder); + gtk_json_printer_start_object (printer, NULL); 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); - } + gtk_json_printer_add_integer (printer, "start", (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); + gtk_json_printer_add_integer (printer, "end", (int)attr->end_index); + add_enum_value (printer, "type", 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)); + gtk_json_printer_add_string (printer, "value", 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); + gtk_json_printer_add_string (printer, "value", ((PangoAttrString*)attr)->value); break; case PANGO_ATTR_STYLE: case PANGO_ATTR_VARIANT: @@ -125,11 +114,11 @@ add_attribute (JsonBuilder *builder, 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); + add_enum_value (printer, "value", 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); + add_enum_value (printer, "value", get_enum_type (attr->klass->type), ((PangoAttrInt*)attr)->value, TRUE); break; case PANGO_ATTR_SIZE: case PANGO_ATTR_RISE: @@ -141,11 +130,11 @@ add_attribute (JsonBuilder *builder, case PANGO_ATTR_WORD: case PANGO_ATTR_SENTENCE: case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: - json_builder_add_int_value (builder, ((PangoAttrInt*)attr)->value); + gtk_json_printer_add_integer (printer, "value", ((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); + gtk_json_printer_add_string (printer, "value", str); g_free (str); break; case PANGO_ATTR_FOREGROUND: @@ -154,29 +143,29 @@ add_attribute (JsonBuilder *builder, 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); + gtk_json_printer_add_string (printer, "value", 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); + gtk_json_printer_add_boolean (printer, "value", ((PangoAttrInt*)attr)->value != 0); break; case PANGO_ATTR_SHAPE: - json_builder_add_string_value (builder, "shape"); + gtk_json_printer_add_string (printer, "value", "shape"); break; case PANGO_ATTR_SCALE: case PANGO_ATTR_LINE_HEIGHT: - json_builder_add_double_value (builder, ((PangoAttrFloat*)attr)->value); + gtk_json_printer_add_number (printer, "value", ((PangoAttrFloat*)attr)->value); } - json_builder_end_object (builder); + gtk_json_printer_end (printer); } static void -add_attr_list (JsonBuilder *builder, - PangoAttrList *attrs) +add_attr_list (GtkJsonPrinter *printer, + PangoAttrList *attrs) { GSList *attributes, *l; @@ -188,62 +177,54 @@ add_attr_list (JsonBuilder *builder, if (!attributes) return; - json_builder_set_member_name (builder, "attributes"); - json_builder_begin_array (builder); + gtk_json_printer_start_array (printer, "attributes"); for (l = attributes; l; l = l->next) { PangoAttribute *attr = l->data; - add_attribute (builder, attr); + add_attribute (printer, attr); } g_slist_free_full (attributes, (GDestroyNotify) pango_attribute_destroy); - json_builder_end_array (builder); + gtk_json_printer_end (printer); } static void -add_tab_array (JsonBuilder *builder, - PangoTabArray *tabs) +add_tab_array (GtkJsonPrinter *printer, + PangoTabArray *tabs) { if (!tabs || pango_tab_array_get_size (tabs) == 0) return; - json_builder_set_member_name (builder, "tabs"); - - json_builder_begin_object (builder); + gtk_json_printer_start_object (printer, "tabs"); - 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); + gtk_json_printer_add_boolean (printer, "positions-in-pixels", pango_tab_array_get_positions_in_pixels (tabs)); + gtk_json_printer_start_array (printer, "positions"); for (int i = 0; i < pango_tab_array_get_size (tabs); i++) { PangoTabAlign align; int pos; pango_tab_array_get_tab (tabs, i, &align, &pos); - json_builder_begin_object (builder); - json_builder_set_member_name (builder, "position"); - json_builder_add_int_value (builder, pos); - json_builder_set_member_name (builder, "alignment"); - add_enum_value (builder, PANGO_TYPE_TAB_ALIGN, align, FALSE); - json_builder_set_member_name (builder, "decimal-point"); - json_builder_add_int_value (builder, pango_tab_array_get_decimal_point (tabs, i)); - json_builder_end_object (builder); + gtk_json_printer_start_object (printer, NULL); + gtk_json_printer_add_integer (printer, "position", pos); + add_enum_value (printer, "alignment", PANGO_TYPE_TAB_ALIGN, align, FALSE); + gtk_json_printer_add_integer (printer, "decimal-point", pango_tab_array_get_decimal_point (tabs, i)); + gtk_json_printer_end (printer); } - json_builder_end_array (builder); + gtk_json_printer_end (printer); - json_builder_end_object (builder); + gtk_json_printer_end (printer); } static void -add_context (JsonBuilder *builder, - PangoContext *context) +add_context (GtkJsonPrinter *printer, + PangoContext *context) { char *str; const PangoMatrix *matrix; PangoMatrix identity = PANGO_MATRIX_INIT; - json_builder_begin_object (builder); + gtk_json_printer_start_object (printer, "context"); /* Note: since we don't create the context when deserializing, * we don't strip out default values here to ensure that the @@ -251,143 +232,86 @@ add_context (JsonBuilder *builder, */ str = pango_font_description_to_string (context->font_desc); - json_builder_set_member_name (builder, "font"); - json_builder_add_string_value (builder, str); + gtk_json_printer_add_string (printer, "font", 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); + gtk_json_printer_add_string (printer, "language", pango_language_to_string (context->set_language)); - json_builder_set_member_name (builder, "gravity-hint"); - add_enum_value (builder, PANGO_TYPE_GRAVITY_HINT, context->gravity_hint, FALSE); + add_enum_value (printer, "base-gravity", PANGO_TYPE_GRAVITY, context->base_gravity, FALSE); + add_enum_value (printer, "gravity-hint", PANGO_TYPE_GRAVITY_HINT, context->gravity_hint, FALSE); + add_enum_value (printer, "base-dir", PANGO_TYPE_DIRECTION, context->base_dir, FALSE); + gtk_json_printer_add_boolean (printer, "round-glyph-positions", context->round_glyph_positions); - 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); + gtk_json_printer_start_array (printer, "transform"); + gtk_json_printer_add_number (printer, NULL, matrix->xx); + gtk_json_printer_add_number (printer, NULL, matrix->xy); + gtk_json_printer_add_number (printer, NULL, matrix->yx); + gtk_json_printer_add_number (printer, NULL, matrix->yy); + gtk_json_printer_add_number (printer, NULL, matrix->x0); + gtk_json_printer_add_number (printer, NULL, matrix->y0); + gtk_json_printer_end (printer); - json_builder_end_object (builder); + gtk_json_printer_end (printer); } static void -add_log_attrs (JsonBuilder *builder, - PangoLayout *layout) +add_log_attrs (GtkJsonPrinter *printer, + PangoLayout *layout) { const PangoLogAttr *log_attrs; int n_attrs; - json_builder_set_member_name (builder, "log-attrs"); - json_builder_begin_array (builder); + gtk_json_printer_start_array (printer, "log-attrs"); log_attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); for (int i = 0; i < n_attrs; i++) { - json_builder_begin_object (builder); + gtk_json_printer_start_object (printer, NULL); if (log_attrs[i].is_line_break) - { - json_builder_set_member_name (builder, "line-break"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "line-break", TRUE); if (log_attrs[i].is_mandatory_break) - { - json_builder_set_member_name (builder, "mandatory-break"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "mandatory-break", TRUE); if (log_attrs[i].is_char_break) - { - json_builder_set_member_name (builder, "char-break"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "char-break", TRUE); if (log_attrs[i].is_white) - { - json_builder_set_member_name (builder, "white"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "white", TRUE); if (log_attrs[i].is_cursor_position) - { - json_builder_set_member_name (builder, "cursor-position"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "cursor-position", TRUE); if (log_attrs[i].is_word_start) - { - json_builder_set_member_name (builder, "word-start"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "word-start", TRUE); if (log_attrs[i].is_word_end) - { - json_builder_set_member_name (builder, "word-end"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "word-end", TRUE); if (log_attrs[i].is_sentence_boundary) - { - json_builder_set_member_name (builder, "sentence-boundary"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "sentence-boundary", TRUE); if (log_attrs[i].is_sentence_start) - { - json_builder_set_member_name (builder, "sentence-start"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "sentence-start", TRUE); if (log_attrs[i].is_sentence_end) - { - json_builder_set_member_name (builder, "sentence-end"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "sentence-end", TRUE); if (log_attrs[i].backspace_deletes_character) - { - json_builder_set_member_name (builder, "backspace-deletes-character"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "backspace-deletes-character", TRUE); if (log_attrs[i].is_expandable_space) - { - json_builder_set_member_name (builder, "expandable-space"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "expandable-space", TRUE); if (log_attrs[i].is_word_boundary) - { - json_builder_set_member_name (builder, "word-boundary"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "word-boundary", TRUE); if (log_attrs[i].break_inserts_hyphen) - { - json_builder_set_member_name (builder, "break-inserts-hyphen"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "break-inserts-hyphen", 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); + gtk_json_printer_add_boolean (printer, "break-removes-preceding", TRUE); + gtk_json_printer_end (printer); } - json_builder_end_array (builder); + gtk_json_printer_end (printer); } static void -add_font (JsonBuilder *builder, - PangoFont *font) +add_font (GtkJsonPrinter *printer, + const char *member, + PangoFont *font) { PangoFontDescription *desc; char *str; @@ -400,13 +324,11 @@ add_font (JsonBuilder *builder, hb_feature_t features[32]; PangoMatrix matrix; - json_builder_begin_object (builder); - - json_builder_set_member_name (builder, "description"); + gtk_json_printer_start_object (printer, member); desc = pango_font_describe (font); str = pango_font_description_to_string (desc); - json_builder_add_string_value (builder, str); + gtk_json_printer_add_string (printer, "description", str); g_free (str); pango_font_description_free (desc); @@ -417,8 +339,7 @@ add_font (JsonBuilder *builder, 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); + gtk_json_printer_add_string (printer, "checksum", str); g_free (str); hb_blob_destroy (blob); @@ -435,55 +356,50 @@ add_font (JsonBuilder *builder, 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); + gtk_json_printer_start_object (printer, "variations"); 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]); + gtk_json_printer_add_integer (printer, buf, coords[i]); } - json_builder_end_object (builder); + gtk_json_printer_end (printer); } 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); + gtk_json_printer_start_object (printer, "features"); 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); + gtk_json_printer_add_integer (printer, buf, features[i].value); } - json_builder_end_object (builder); + gtk_json_printer_end (printer); } 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); + gtk_json_printer_start_array (printer, "matrix"); + gtk_json_printer_add_number (printer, NULL, matrix.xx); + gtk_json_printer_add_number (printer, NULL, matrix.xy); + gtk_json_printer_add_number (printer, NULL, matrix.yx); + gtk_json_printer_add_number (printer, NULL, matrix.yy); + gtk_json_printer_add_number (printer, NULL, matrix.x0); + gtk_json_printer_add_number (printer, NULL, matrix.y0); + gtk_json_printer_end (printer); } - json_builder_end_object (builder); + gtk_json_printer_end (printer); } #define ANALYSIS_FLAGS (PANGO_ANALYSIS_FLAG_CENTERED_BASELINE | \ @@ -491,998 +407,962 @@ add_font (JsonBuilder *builder, PANGO_ANALYSIS_FLAG_NEED_HYPHEN) static void -add_run (JsonBuilder *builder, +add_run (GtkJsonPrinter *printer, 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); + gtk_json_printer_start_object (printer, NULL); - json_builder_set_member_name (builder, "length"); - json_builder_add_int_value (builder, run->item->length); + 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); - json_builder_set_member_name (builder, "text"); - json_builder_add_string_value (builder, str); + gtk_json_printer_add_string (printer, "text", 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); + gtk_json_printer_add_integer (printer, "bidi-level", run->item->analysis.level); + add_enum_value (printer, "gravity", PANGO_TYPE_GRAVITY, run->item->analysis.gravity, FALSE); + gtk_json_printer_add_string (printer, "language", pango_language_to_string (run->item->analysis.language)); + add_enum_value (printer, "script", PANGO_TYPE_SCRIPT, run->item->analysis.script, FALSE); - json_builder_set_member_name (builder, "font"); - add_font (builder, run->item->analysis.font); + add_font (printer, "font", run->item->analysis.font); - json_builder_set_member_name (builder, "flags"); - json_builder_add_int_value (builder, run->item->analysis.flags & ANALYSIS_FLAGS); + gtk_json_printer_add_integer (printer, "flags", 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); + gtk_json_printer_start_array (printer, "extra-attributes"); for (l = run->item->analysis.extra_attrs; l; l = l->next) { PangoAttribute *attr = l->data; - add_attribute (builder, attr); + add_attribute (printer, attr); } - json_builder_end_array (builder); + gtk_json_printer_end (printer); } - 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); + gtk_json_printer_add_integer (printer, "y-offset", run->y_offset); + gtk_json_printer_add_integer (printer, "start-x-offset", run->start_x_offset); + gtk_json_printer_add_integer (printer, "end-x-offset", run->end_x_offset); - json_builder_set_member_name (builder, "glyphs"); - json_builder_begin_array (builder); + gtk_json_printer_start_array (printer, "glyphs"); for (int i = 0; i < run->glyphs->num_glyphs; i++) { - json_builder_begin_object (builder); + gtk_json_printer_start_object (printer, NULL); - 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); + gtk_json_printer_add_integer (printer, "glyph", run->glyphs->glyphs[i].glyph); + gtk_json_printer_add_integer (printer, "width", 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); - } + gtk_json_printer_add_integer (printer, "x-offset", 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); - } + gtk_json_printer_add_integer (printer, "y-offset", 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); - } + gtk_json_printer_add_boolean (printer, "is-cluster-start", TRUE); if (run->glyphs->glyphs[i].attr.is_color) - { - json_builder_set_member_name (builder, "is-color"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "is-color", TRUE); - json_builder_set_member_name (builder, "log-cluster"); - json_builder_add_int_value (builder, run->glyphs->log_clusters[i]); + gtk_json_printer_add_integer (printer, "log-cluster", run->glyphs->log_clusters[i]); - json_builder_end_object (builder); + gtk_json_printer_end (printer); } - json_builder_end_array (builder); + gtk_json_printer_end (printer); - json_builder_end_object (builder); + gtk_json_printer_end (printer); } #undef ANALYSIS_FLAGS static void -add_line (JsonBuilder *builder, +add_line (GtkJsonPrinter *printer, 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); + gtk_json_printer_start_object (printer, NULL); - json_builder_set_member_name (builder, "direction"); - add_enum_value (builder, PANGO_TYPE_DIRECTION, line->resolved_dir, FALSE); + gtk_json_printer_add_integer (printer, "start-index", line->start_index); + gtk_json_printer_add_integer (printer, "length", line->length); + gtk_json_printer_add_boolean (printer, "paragraph-start", line->is_paragraph_start); + add_enum_value (printer, "direction", PANGO_TYPE_DIRECTION, line->resolved_dir, FALSE); - json_builder_set_member_name (builder, "runs"); - json_builder_begin_array (builder); + gtk_json_printer_start_array (printer, "runs"); for (GSList *l = line->runs; l; l = l->next) { PangoLayoutRun *run = l->data; - add_run (builder, line->layout, run); + add_run (printer, line->layout, run); } - json_builder_end_array (builder); + gtk_json_printer_end (printer); - json_builder_end_object (builder); + gtk_json_printer_end (printer); } static void -add_output (JsonBuilder *builder, - PangoLayout *layout) +add_output (GtkJsonPrinter *printer, + PangoLayout *layout) { int width, height; - json_builder_begin_object (builder); + gtk_json_printer_start_object (printer, "output"); - 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)); + gtk_json_printer_add_boolean (printer, "is-wrapped", pango_layout_is_wrapped (layout)); + gtk_json_printer_add_boolean (printer, "is-ellipsized", pango_layout_is_ellipsized (layout)); + gtk_json_printer_add_integer (printer, "unknown-glyphs", 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); + gtk_json_printer_add_integer (printer, "width", width); + gtk_json_printer_add_integer (printer, "height", width); + + add_log_attrs (printer, layout); + gtk_json_printer_start_array (printer, "lines"); for (GSList *l = layout->lines; l; l = l->next) { PangoLayoutLine *line = l->data; - add_line (builder, line); + add_line (printer, line); } - json_builder_end_array (builder); + gtk_json_printer_end (printer); - json_builder_end_object (builder); + gtk_json_printer_end (printer); } -static JsonNode * -layout_to_json (PangoLayout *layout, +static void +layout_to_json (GtkJsonPrinter *printer, + PangoLayout *layout, PangoLayoutSerializeFlags flags) { - JsonBuilder *builder; - JsonNode *root; const char *str; - builder = json_builder_new_immutable (); - - json_builder_begin_object (builder); + gtk_json_printer_start_object (printer, NULL); if (flags & PANGO_LAYOUT_SERIALIZE_CONTEXT) - { - json_builder_set_member_name (builder, "context"); - add_context (builder, layout->context); - } + add_context (printer, layout->context); str = (const char *) g_object_get_data (G_OBJECT (layout), "comment"); if (str) - { - 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); + gtk_json_printer_add_string (printer, "comment", str); - g_strfreev (strs); - } - else - json_builder_add_string_value (builder, str); - } + gtk_json_printer_add_string (printer, "text", layout->text); - json_builder_set_member_name (builder, "text"); - json_builder_add_string_value (builder, layout->text); - - add_attr_list (builder, layout->attrs); + add_attr_list (printer, 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); + gtk_json_printer_add_string (printer, "font", str); g_free (str); } - add_tab_array (builder, layout->tabs); + add_tab_array (printer, layout->tabs); if (layout->justify) - { - json_builder_set_member_name (builder, "justify"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "justify", TRUE); if (layout->justify_last_line) - { - json_builder_set_member_name (builder, "justify-last-line"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "justify-last-line", TRUE); if (layout->single_paragraph) - { - json_builder_set_member_name (builder, "single-paragraph"); - json_builder_add_boolean_value (builder, TRUE); - } + gtk_json_printer_add_boolean (printer, "single-paragraph", TRUE); if (!layout->auto_dir) - { - json_builder_set_member_name (builder, "auto-dir"); - json_builder_add_boolean_value (builder, FALSE); - } + gtk_json_printer_add_boolean (printer, "auto-dir", FALSE); if (layout->alignment != PANGO_ALIGN_LEFT) - { - json_builder_set_member_name (builder, "alignment"); - add_enum_value (builder, PANGO_TYPE_ALIGNMENT, layout->alignment, FALSE); - } + add_enum_value (printer, "alignment", 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); - } + add_enum_value (printer, "wrap", 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); - } + add_enum_value (printer, "ellipsize", 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); - } + gtk_json_printer_add_integer (printer, "width", layout->width); if (layout->height != -1) - { - json_builder_set_member_name (builder, "height"); - json_builder_add_int_value (builder, layout->height); - } + gtk_json_printer_add_integer (printer, "height", layout->height); if (layout->indent != 0) - { - json_builder_set_member_name (builder, "indent"); - json_builder_add_int_value (builder, layout->indent); - } + gtk_json_printer_add_integer (printer, "indent", layout->indent); if (layout->spacing != 0) - { - json_builder_set_member_name (builder, "spacing"); - json_builder_add_int_value (builder, layout->spacing); - } + gtk_json_printer_add_integer (printer, "spacing", layout->spacing); if (layout->line_spacing != 0.) - { - json_builder_set_member_name (builder, "line-spacing"); - json_builder_add_double_value (builder, layout->line_spacing); - } + gtk_json_printer_add_number (printer, "line-spacing", 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); + add_output (printer, layout); - root = json_builder_get_root (builder); - g_object_unref (builder); - - return root; + gtk_json_printer_end (printer); } -static JsonNode * -font_to_json (PangoFont *font) +static void +gstring_write (GtkJsonPrinter *printer, + const char *s, + gpointer data) { - 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; + GString *str = data; + g_string_append (str, s); } /* }}} */ /* {{{ Deserialization */ static int -get_enum_value (GType type, - const char *str, - gboolean allow_extra, - GError **error) +parser_get_enum_value (GtkJsonParser *parser, + GType type, + gboolean allow_extra) { GEnumClass *enum_class; GEnumValue *enum_value; + char *str = gtk_json_parser_get_string (parser); 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; + { + g_free (str); + return enum_value->value; + } if (allow_extra) { - gint64 value; + gint64 v; char *endp; - value = g_ascii_strtoll (str, &endp, 10); + v = g_ascii_strtoll (str, &endp, 10); if (*endp == '\0') - return value; + { + g_free (str); + return (int)v; + } } - 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); + gtk_json_parser_set_error (parser, + g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE, + "Could not parse enum value of type %s: %s", + g_type_name (type), + str)); + + g_free (str); - return -1; + return 0; } -static PangoAttribute * -json_to_attribute (JsonReader *reader, - GError **error) +static PangoFontDescription * +parser_get_font_description (GtkJsonParser *parser) { - 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; - int value; - - if (!json_reader_is_object (reader)) - { - g_set_error (error, - PANGO_LAYOUT_DESERIALIZE_ERROR, - PANGO_LAYOUT_DESERIALIZE_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); + char *str = gtk_json_parser_get_string (parser); + PangoFontDescription *desc = pango_font_description_from_string (str); + + if (!desc) + gtk_json_parser_set_error (parser, + g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE, + "Failed to parse font: %s", str)); + g_free (str); - if (json_reader_read_member (reader, "end")) - end = json_reader_get_int_value (reader); - json_reader_end_member (reader); + return desc; +} - if (json_reader_read_member (reader, "type")) - { - type = get_enum_value (PANGO_TYPE_ATTR_TYPE, json_reader_get_string_value (reader), FALSE, error); - if (type == -1) - return NULL; - } - else +static void +parser_get_color (GtkJsonParser *parser, + PangoColor *color) +{ + char *str = gtk_json_parser_get_string (parser); + if (!pango_color_parse (color, str)) { - g_set_error (error, - PANGO_LAYOUT_DESERIALIZE_ERROR, - PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE, - "Attribute \"type\" missing"); - return NULL; + gtk_json_parser_set_error (parser, + g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE, + "Failed to parse color: %s", str)); + color->red = color->green = color->blue = 0; } - json_reader_end_member (reader); - if (!json_reader_read_member (reader, "value")) - { - g_set_error (error, - PANGO_LAYOUT_DESERIALIZE_ERROR, - PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE, - "Attribute \"value\" missing"); - return NULL; - } + g_free (str); +} + +static PangoAttribute * +attr_for_type (GtkJsonParser *parser, + PangoAttrType type, + int start, + int end) +{ + PangoAttribute *attr; + PangoFontDescription *desc; + PangoColor color; + char *str; switch (type) { default: - case PANGO_ATTR_INVALID: g_assert_not_reached (); + case PANGO_ATTR_INVALID: + gtk_json_parser_set_error (parser, + g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE, + "Missing attribute type")); + return NULL; + case PANGO_ATTR_LANGUAGE: - attr = pango_attr_language_new (pango_language_from_string (json_reader_get_string_value (reader))); + str = gtk_json_parser_get_string (parser); + attr = pango_attr_language_new (pango_language_from_string (str)); + g_free (str); break; + case PANGO_ATTR_FAMILY: - attr = pango_attr_family_new (json_reader_get_string_value (reader)); + str = gtk_json_parser_get_string (parser); + attr = pango_attr_family_new (str); + g_free (str); break; + case PANGO_ATTR_STYLE: - 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); + attr = pango_attr_style_new ((PangoStyle)parser_get_enum_value (parser, PANGO_TYPE_STYLE, FALSE)); break; + case PANGO_ATTR_WEIGHT: - 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); + attr = pango_attr_weight_new (parser_get_enum_value (parser, PANGO_TYPE_WEIGHT, TRUE)); break; + case PANGO_ATTR_VARIANT: - 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); + attr = pango_attr_variant_new ((PangoVariant)parser_get_enum_value (parser, PANGO_TYPE_VARIANT, FALSE)); break; + case PANGO_ATTR_STRETCH: - 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); + attr = pango_attr_stretch_new ((PangoStretch)parser_get_enum_value (parser, PANGO_TYPE_STRETCH, FALSE)); break; + case PANGO_ATTR_SIZE: - attr = pango_attr_size_new (json_reader_get_int_value (reader)); + attr = pango_attr_size_new (gtk_json_parser_get_int (parser)); break; + case PANGO_ATTR_FONT_DESC: - desc = pango_font_description_from_string (json_reader_get_string_value (reader)); + desc = parser_get_font_description (parser); 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)); + parser_get_color (parser, &color); 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)); + parser_get_color (parser, &color); attr = pango_attr_background_new (color.red, color.green, color.blue); break; + case PANGO_ATTR_UNDERLINE: - 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); + attr = pango_attr_underline_new ((PangoUnderline)parser_get_enum_value (parser, PANGO_TYPE_UNDERLINE, FALSE)); break; + case PANGO_ATTR_STRIKETHROUGH: - attr = pango_attr_strikethrough_new (json_reader_get_boolean_value (reader)); + attr = pango_attr_strikethrough_new (gtk_json_parser_get_boolean (parser)); break; + case PANGO_ATTR_RISE: - attr = pango_attr_rise_new (json_reader_get_int_value (reader)); + attr = pango_attr_rise_new (gtk_json_parser_get_int (parser)); 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)); + attr = pango_attr_scale_new (gtk_json_parser_get_number (parser)); break; + case PANGO_ATTR_FALLBACK: - attr = pango_attr_fallback_new (json_reader_get_boolean_value (reader)); + attr = pango_attr_fallback_new (gtk_json_parser_get_boolean (parser)); break; + case PANGO_ATTR_LETTER_SPACING: - attr = pango_attr_letter_spacing_new (json_reader_get_int_value (reader)); + attr = pango_attr_letter_spacing_new (gtk_json_parser_get_int (parser)); break; + case PANGO_ATTR_UNDERLINE_COLOR: - pango_color_parse (&color, json_reader_get_string_value (reader)); + parser_get_color (parser, &color); 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)); + parser_get_color (parser, &color); 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)); + attr = pango_attr_size_new_absolute (gtk_json_parser_get_int (parser)); break; + case PANGO_ATTR_GRAVITY: - 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); + attr = pango_attr_gravity_new ((PangoGravity)parser_get_enum_value (parser, PANGO_TYPE_GRAVITY, FALSE)); break; + case PANGO_ATTR_GRAVITY_HINT: - 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); + attr = pango_attr_gravity_hint_new ((PangoGravityHint)parser_get_enum_value (parser, PANGO_TYPE_GRAVITY_HINT, FALSE)); break; + case PANGO_ATTR_FONT_FEATURES: - attr = pango_attr_font_features_new (json_reader_get_string_value (reader)); + str = gtk_json_parser_get_string (parser); + attr = pango_attr_font_features_new (str); + g_free (str); break; + case PANGO_ATTR_FOREGROUND_ALPHA: - attr = pango_attr_foreground_alpha_new (json_reader_get_int_value (reader)); + attr = pango_attr_foreground_alpha_new (gtk_json_parser_get_int (parser)); break; + case PANGO_ATTR_BACKGROUND_ALPHA: - attr = pango_attr_background_alpha_new (json_reader_get_int_value (reader)); + attr = pango_attr_background_alpha_new (gtk_json_parser_get_int (parser)); break; + case PANGO_ATTR_ALLOW_BREAKS: - attr = pango_attr_allow_breaks_new (json_reader_get_boolean_value (reader)); + attr = pango_attr_allow_breaks_new (gtk_json_parser_get_boolean (parser)); break; + case PANGO_ATTR_SHOW: - attr = pango_attr_show_new (json_reader_get_int_value (reader)); + attr = pango_attr_show_new (gtk_json_parser_get_int (parser)); break; + case PANGO_ATTR_INSERT_HYPHENS: - attr = pango_attr_insert_hyphens_new (json_reader_get_boolean_value (reader)); + attr = pango_attr_insert_hyphens_new (gtk_json_parser_get_int (parser)); break; + case PANGO_ATTR_OVERLINE: - 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); + attr = pango_attr_overline_new ((PangoOverline)parser_get_enum_value (parser, PANGO_TYPE_OVERLINE, FALSE)); break; + case PANGO_ATTR_OVERLINE_COLOR: - pango_color_parse (&color, json_reader_get_string_value (reader)); + parser_get_color (parser, &color); 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)); + attr = pango_attr_line_height_new (gtk_json_parser_get_number (parser)); break; + case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: - attr = pango_attr_line_height_new_absolute (json_reader_get_int_value (reader)); + attr = pango_attr_line_height_new_absolute (gtk_json_parser_get_int (parser)); break; + case PANGO_ATTR_TEXT_TRANSFORM: - 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); + attr = pango_attr_text_transform_new ((PangoTextTransform)parser_get_enum_value (parser, PANGO_TYPE_TEXT_TRANSFORM, 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: - 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); + attr = pango_attr_baseline_shift_new (parser_get_enum_value (parser, PANGO_TYPE_BASELINE_SHIFT, FALSE)); break; + case PANGO_ATTR_FONT_SCALE: - 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); + attr = pango_attr_font_scale_new ((PangoFontScale)parser_get_enum_value (parser, PANGO_TYPE_FONT_SCALE, 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; - - attributes = pango_attr_list_new (); - - if (!json_reader_is_array (reader)) - { - g_set_error (error, - PANGO_LAYOUT_DESERIALIZE_ERROR, - PANGO_LAYOUT_DESERIALIZE_INVALID_SYNTAX, - "\"attributes\" must be a Json array"); - goto fail; - } - - 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; +enum { + ATTR_START, + ATTR_END, + ATTR_TYPE, + ATTR_VALUE +}; + +static const char *attr_members[] = { + "start", + "end", + "type", + "value", + NULL +}; -fail: - if (attributes) - pango_attr_list_unref (attributes); - return NULL; -} - -static PangoTabArray * -json_to_tab_array (JsonReader *reader, - GError **error) +static PangoAttribute * +json_to_attribute (GtkJsonParser *parser) { - 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); + PangoAttribute *attr = NULL; + PangoAttrType type = PANGO_ATTR_INVALID; + guint start = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING; + guint end = PANGO_ATTR_INDEX_TO_TEXT_END; - tabs = pango_tab_array_new (0, positions_in_pixels); + gtk_json_parser_start_object (parser); - if (json_reader_read_member (reader, "positions")) + do { - if (!json_reader_is_array (reader)) + switch (gtk_json_parser_select_member (parser, attr_members)) { - g_set_error (error, - PANGO_LAYOUT_DESERIALIZE_ERROR, - PANGO_LAYOUT_DESERIALIZE_INVALID_SYNTAX, - "Tab \"positions\" must be a Json array"); - goto fail; - } + case ATTR_START: + start = gtk_json_parser_get_int (parser); + break; - pango_tab_array_resize (tabs, json_reader_count_elements (reader)); - for (int i = 0; i < json_reader_count_elements (reader); i++) - { - int pos; - PangoTabAlign align = PANGO_TAB_LEFT; - gunichar ch = 0; + case ATTR_END: + end = gtk_json_parser_get_int (parser); + break; - json_reader_read_element (reader, i); - if (json_reader_is_object (reader)) - { - json_reader_read_member (reader, "position"); - pos = json_reader_get_int_value (reader); - json_reader_end_member (reader); - json_reader_read_member (reader, "alignment"); - - align = get_enum_value (PANGO_TYPE_TAB_ALIGN, - json_reader_get_string_value (reader), - FALSE, - error); - if (align == -1) - goto fail; - json_reader_end_member (reader); - json_reader_read_member (reader, "decimal-point"); - ch = json_reader_get_int_value (reader); - json_reader_end_member (reader); - } - else - { - pos = json_reader_get_int_value (reader); - } + case ATTR_TYPE: + type = parser_get_enum_value (parser, PANGO_TYPE_ATTR_TYPE, FALSE); + break; - pango_tab_array_set_tab (tabs, i, align, pos); - pango_tab_array_set_decimal_point (tabs, i, ch); - json_reader_end_element (reader); + case ATTR_VALUE: + attr = attr_for_type (parser, type, start, end); + break; + + default: + break; } } - json_reader_end_member (reader); + while (gtk_json_parser_next (parser)); + + if (!attr && !gtk_json_parser_get_error (parser)) + gtk_json_parser_set_error (parser, + g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR, + PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE, + "Attribute missing \"value\"")); - return tabs; + gtk_json_parser_end (parser); -fail: - if (tabs) - pango_tab_array_free (tabs); - return NULL; + return attr; } -static gboolean -apply_json_to_context (JsonReader *reader, - PangoContext *context, - GError **error) +static void +json_parser_fill_attr_list (GtkJsonParser *parser, + PangoAttrList *attributes) { - 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); + gtk_json_parser_start_array (parser); - if (json_reader_read_member (reader, "base-gravity")) + do { - 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); + PangoAttribute *attr = json_to_attribute (parser); + if (attr) + pango_attr_list_insert (attributes, attr); } - json_reader_end_member (reader); + while (gtk_json_parser_next (parser)); - 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); + gtk_json_parser_end (parser); +} - 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); +enum { + TAB_POSITION, + TAB_ALIGNMENT, + TAB_DECIMAL_POINT +}; - return TRUE; -} +static const char *tab_members[] = { + "position", + "alignment", + "decimal-point", + NULL, +}; -static PangoLayout * -json_to_layout (PangoContext *context, - JsonNode *node, - PangoLayoutDeserializeFlags flags, - GError **error) +static void +json_parser_fill_tabs (GtkJsonParser *parser, + PangoTabArray *tabs) { - JsonReader *reader; - PangoLayout *layout; + int index; - reader = json_reader_new (node); - if (!json_reader_is_object (reader)) - { - g_set_error (error, - PANGO_LAYOUT_DESERIALIZE_ERROR, - PANGO_LAYOUT_DESERIALIZE_INVALID_SYNTAX, - "Layout must be a Json object"); - goto fail; - } + gtk_json_parser_start_array (parser); - if (flags & PANGO_LAYOUT_DESERIALIZE_CONTEXT) + index = 0; + do { - 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); + int pos; + PangoTabAlign align = PANGO_TAB_LEFT; + gunichar ch = 0; - if (json_reader_read_member (reader, "comment")) - { - if (json_reader_is_array (reader)) + if (gtk_json_parser_get_node (parser) == GTK_JSON_OBJECT) { - GString *s; - - s = g_string_new (""); - for (int i = 0; i < json_reader_count_elements (reader); i++) + gtk_json_parser_start_object (parser); + do { - 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); + switch (gtk_json_parser_select_member (parser, tab_members)) + { + case TAB_POSITION: + pos = gtk_json_parser_get_int (parser); + break; + + case TAB_ALIGNMENT: + align = parser_get_enum_value (parser, PANGO_TYPE_TAB_ALIGN, FALSE); + break; + + case TAB_DECIMAL_POINT: + ch = gtk_json_parser_get_int (parser); + break; + + default: + break; + } } + while (gtk_json_parser_next (parser)); - g_object_set_data_full (G_OBJECT (layout), "comment", - g_string_free (s, FALSE), - g_free); + gtk_json_parser_end (parser); } 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); + pos = gtk_json_parser_get_int (parser); - if (json_reader_read_member (reader, "text")) - pango_layout_set_text (layout, json_reader_get_string_value (reader), -1); - json_reader_end_member (reader); + pango_tab_array_set_tab (tabs, index, align, pos); + pango_tab_array_set_decimal_point (tabs, index, ch); + index++; + } + while (gtk_json_parser_next (parser)); - if (json_reader_read_member (reader, "attributes")) - { - PangoAttrList *attributes; + gtk_json_parser_end (parser); +} - attributes = json_to_attr_list (reader, error); +enum { + TABS_POSITIONS_IN_PIXELS, + TABS_POSITIONS +}; - if (!attributes) - goto fail; +static const char *tabs_members[] = { + "positions-in-pixels", + "positions", + NULL +}; - pango_layout_set_attributes (layout, attributes); - pango_attr_list_unref (attributes); - } - json_reader_end_member (reader); +static void +json_parser_fill_tab_array (GtkJsonParser *parser, + PangoTabArray *tabs) +{ + gtk_json_parser_start_object (parser); - if (json_reader_read_member (reader, "font")) + do { - PangoFontDescription *desc; - - desc = pango_font_description_from_string ( json_reader_get_string_value (reader)); - if (!desc) + switch (gtk_json_parser_select_member (parser, tabs_members)) { - g_set_error (error, - PANGO_LAYOUT_DESERIALIZE_ERROR, - PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE, - "Could not parse \"%s\" value: %s", - "font", - 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); + case TABS_POSITIONS_IN_PIXELS: + pango_tab_array_set_positions_in_pixels (tabs, gtk_json_parser_get_boolean (parser)); + break; - if (json_reader_read_member (reader, "tabs")) - { - PangoTabArray *tabs; - - tabs = json_to_tab_array (reader, error); - - if (!tabs) - goto fail; + case TABS_POSITIONS: + json_parser_fill_tabs (parser, tabs); + break; - pango_layout_set_tabs (layout, tabs); - pango_tab_array_free (tabs); + default: + break; + } } - 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); + while (gtk_json_parser_next (parser)); - 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); + gtk_json_parser_end (parser); +} - 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); +enum { + CONTEXT_LANGUAGE, + CONTEXT_FONT, + CONTEXT_BASE_GRAVITY, + CONTEXT_GRAVITY_HINT, + CONTEXT_BASE_DIR, + CONTEXT_ROUND_GLYPH_POSITIONS, + CONTEXT_TRANSFORM, +}; + +static const char *context_members[] = { + "language", + "font", + "base-gravity", + "gravity-hint", + "base-dir", + "round-glyph-positions", + "transform", + NULL, +}; - 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); +static void +json_parser_fill_context (GtkJsonParser *parser, + PangoContext *context) +{ + gtk_json_parser_start_object (parser); - if (json_reader_read_member (reader, "alignment")) + do { - PangoAlignment align = get_enum_value (PANGO_TYPE_ALIGNMENT, - json_reader_get_string_value (reader), - FALSE, - error); - if (align == -1) - goto fail; - - pango_layout_set_alignment (layout, align); - } - - json_reader_end_member (reader); + char *str; - if (json_reader_read_member (reader, "wrap")) - { - PangoWrapMode wrap = get_enum_value (PANGO_TYPE_WRAP_MODE, - json_reader_get_string_value (reader), - FALSE, - error); + switch (gtk_json_parser_select_member (parser, context_members)) + { + case CONTEXT_LANGUAGE: + str = gtk_json_parser_get_string (parser); + PangoLanguage *language = pango_language_from_string (str); + pango_context_set_language (context, language); + g_free (str); + break; + + case CONTEXT_FONT: + { + PangoFontDescription *desc = parser_get_font_description (parser); + pango_context_set_font_description (context, desc); + pango_font_description_free (desc); + } + break; + + case CONTEXT_BASE_GRAVITY: + pango_context_set_base_gravity (context, (PangoGravity)parser_get_enum_value (parser, PANGO_TYPE_GRAVITY, FALSE)); + break; + + case CONTEXT_GRAVITY_HINT: + pango_context_set_gravity_hint (context, (PangoGravityHint)parser_get_enum_value (parser, PANGO_TYPE_GRAVITY_HINT, FALSE)); + break; + + case CONTEXT_BASE_DIR: + pango_context_set_base_dir (context, (PangoDirection)parser_get_enum_value (parser, PANGO_TYPE_DIRECTION, FALSE)); + break; + + case CONTEXT_ROUND_GLYPH_POSITIONS: + pango_context_set_round_glyph_positions (context, gtk_json_parser_get_boolean (parser)); + break; + + case CONTEXT_TRANSFORM: + { + PangoMatrix m = PANGO_MATRIX_INIT; + + gtk_json_parser_start_array (parser); + m.xx = gtk_json_parser_get_number (parser); + gtk_json_parser_next (parser); + m.xy = gtk_json_parser_get_number (parser); + gtk_json_parser_next (parser); + m.yx = gtk_json_parser_get_number (parser); + gtk_json_parser_next (parser); + m.yy = gtk_json_parser_get_number (parser); + gtk_json_parser_next (parser); + m.x0 = gtk_json_parser_get_number (parser); + gtk_json_parser_next (parser); + m.y0 = gtk_json_parser_get_number (parser); + gtk_json_parser_end (parser); + + pango_context_set_matrix (context, &m); + } + break; + + default: + break; + } + } + while (gtk_json_parser_next (parser)); - if (wrap == -1) - goto fail; + gtk_json_parser_end (parser); +} - pango_layout_set_wrap (layout, wrap); - } +enum { + LAYOUT_CONTEXT, + LAYOUT_COMMENT, + LAYOUT_TEXT, + LAYOUT_ATTRIBUTES, + LAYOUT_FONT, + LAYOUT_TABS, + LAYOUT_JUSTIFY, + LAYOUT_JUSTIFY_LAST_LINE, + LAYOUT_SINGLE_PARAGRAPH, + LAYOUT_AUTO_DIR, + LAYOUT_ALIGNMENT, + LAYOUT_WRAP, + LAYOUT_ELLIPSIZE, + LAYOUT_WIDTH, + LAYOUT_HEIGHT, + LAYOUT_INDENT, + LAYOUT_SPACING, + LAYOUT_LINE_SPACING, + LAYOUT_OUTPUT +}; + +static const char *layout_members[] = { + "context", + "comment", + "text", + "attributes", + "font", + "tabs", + "justify", + "justify-last-line", + "single-paragraph", + "auto-dir", + "alignment", + "wrap", + "ellipsize", + "width", + "height", + "indent", + "spacing", + "line-spacing", + "output", + NULL +}; - json_reader_end_member (reader); +static void +json_parser_fill_layout (GtkJsonParser *parser, + PangoLayout *layout, + PangoLayoutDeserializeFlags flags) +{ + gtk_json_parser_start_object (parser); - if (json_reader_read_member (reader, "ellipsize")) + do { - PangoEllipsizeMode ellipsize = get_enum_value (PANGO_TYPE_ELLIPSIZE_MODE, - json_reader_get_string_value (reader), - FALSE, - error); + char *str; - if (ellipsize == -1) - goto fail; - - pango_layout_set_ellipsize (layout, ellipsize); + switch (gtk_json_parser_select_member (parser, layout_members)) + { + case LAYOUT_CONTEXT: + if (flags & PANGO_LAYOUT_DESERIALIZE_CONTEXT) + json_parser_fill_context (parser, pango_layout_get_context (layout)); + break; + + case LAYOUT_COMMENT: + str = gtk_json_parser_get_string (parser); + g_object_set_data_full (G_OBJECT (layout), "comment", str, g_free); + break; + + case LAYOUT_TEXT: + str = gtk_json_parser_get_string (parser); + pango_layout_set_text (layout, str, -1); + g_free (str); + break; + + case LAYOUT_ATTRIBUTES: + { + PangoAttrList *attributes = pango_attr_list_new (); + json_parser_fill_attr_list (parser, attributes); + pango_layout_set_attributes (layout, attributes); + pango_attr_list_unref (attributes); + } + break; + + case LAYOUT_FONT: + { + PangoFontDescription *desc = parser_get_font_description (parser);; + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + } + break; + + case LAYOUT_TABS: + { + PangoTabArray *tabs = pango_tab_array_new (0, FALSE); + json_parser_fill_tab_array (parser, tabs); + pango_layout_set_tabs (layout, tabs); + pango_tab_array_free (tabs); + } + break; + + case LAYOUT_JUSTIFY: + pango_layout_set_justify (layout, gtk_json_parser_get_boolean (parser)); + break; + + case LAYOUT_JUSTIFY_LAST_LINE: + pango_layout_set_justify_last_line (layout, gtk_json_parser_get_boolean (parser)); + break; + + case LAYOUT_SINGLE_PARAGRAPH: + pango_layout_set_single_paragraph_mode (layout, gtk_json_parser_get_boolean (parser)); + break; + + case LAYOUT_AUTO_DIR: + pango_layout_set_auto_dir (layout, gtk_json_parser_get_boolean (parser)); + break; + + case LAYOUT_ALIGNMENT: + pango_layout_set_alignment (layout, (PangoAlignment)parser_get_enum_value (parser, PANGO_TYPE_ALIGNMENT, FALSE)); + break; + + case LAYOUT_WRAP: + pango_layout_set_wrap (layout, (PangoWrapMode)parser_get_enum_value (parser, PANGO_TYPE_WRAP_MODE, FALSE)); + break; + + case LAYOUT_ELLIPSIZE: + pango_layout_set_ellipsize (layout, (PangoEllipsizeMode)parser_get_enum_value (parser, PANGO_TYPE_ELLIPSIZE_MODE, FALSE)); + break; + + case LAYOUT_WIDTH: + pango_layout_set_width (layout, gtk_json_parser_get_int (parser)); + break; + + case LAYOUT_HEIGHT: + pango_layout_set_height (layout, gtk_json_parser_get_int (parser)); + break; + + case LAYOUT_INDENT: + pango_layout_set_indent (layout, gtk_json_parser_get_int (parser)); + break; + + case LAYOUT_SPACING: + pango_layout_set_spacing (layout, gtk_json_parser_get_int (parser)); + break; + + case LAYOUT_LINE_SPACING: + pango_layout_set_line_spacing (layout, gtk_json_parser_get_number (parser)); + break; + + case LAYOUT_OUTPUT: + break; + + default: + break; + } } + while (gtk_json_parser_next (parser)); - 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); + gtk_json_parser_end (parser); +} - if (json_reader_read_member (reader, "indent")) - pango_layout_set_indent (layout, json_reader_get_int_value (reader)); - json_reader_end_member (reader); +enum { + FONT_DESCRIPTION, + FONT_CHECKSUM, + FONT_VARIATIONS, + FONT_FEATURES, + FONT_MATRIX +}; + +static const char *font_members[] = { + "description", + "checksum", + "variations", + "features", + "matrix", + NULL +}; + +static PangoFont * +json_parser_load_font (GtkJsonParser *parser, + PangoContext *context, + GError **error) +{ + PangoFont *font = NULL; - if (json_reader_read_member (reader, "spacing")) - pango_layout_set_spacing (layout, json_reader_get_int_value (reader)); - json_reader_end_member (reader); + gtk_json_parser_start_object (parser); - 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); + switch (gtk_json_parser_select_member (parser, font_members)) + { + case FONT_DESCRIPTION: + { + PangoFontDescription *desc = parser_get_font_description (parser); + font = pango_context_load_font (context, desc); + pango_font_description_free (desc); + } + break; - g_object_unref (reader); + default: + break; + } - return layout; + gtk_json_parser_end (parser); -fail: - g_object_unref (reader); - if (layout) - g_object_unref (layout); - return NULL; + return font; } /* }}} */ @@ -1510,29 +1390,25 @@ GBytes * pango_layout_serialize (PangoLayout *layout, PangoLayoutSerializeFlags flags) { - JsonGenerator *generator; - JsonNode *node; GString *str; + GtkJsonPrinter *printer; char *data; gsize size; g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL); - node = layout_to_json (layout, flags); + str = g_string_new (""); - generator = json_generator_new (); - json_generator_set_pretty (generator, TRUE); - json_generator_set_indent (generator, 2); + printer = gtk_json_printer_new (gstring_write, str, NULL); + gtk_json_printer_set_flags (printer, GTK_JSON_PRINTER_PRETTY); + layout_to_json (printer, layout, flags); + gtk_json_printer_free (printer); + + g_string_append_c (str, '\n'); - json_generator_set_root (generator, node); - 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); - return g_bytes_new_take (data, size); } @@ -1590,6 +1466,10 @@ pango_layout_write_to_file (PangoLayout *layout, * * 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 `PangoLayout` * * Since: 1.50 @@ -1600,26 +1480,23 @@ pango_layout_deserialize (PangoContext *context, PangoLayoutDeserializeFlags flags, GError **error) { - JsonParser *parser; - JsonNode *node; PangoLayout *layout; + GtkJsonParser *parser; 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), - g_bytes_get_size (bytes), - error)) + layout = pango_layout_new (context); + + parser = gtk_json_parser_new_for_bytes (bytes); + json_parser_fill_layout (parser, layout, flags); + + if (gtk_json_parser_get_error (parser)) { - g_object_unref (parser); - return NULL; + g_propagate_error (error, g_error_copy (gtk_json_parser_get_error (parser))); + g_clear_object (&layout); } - node = json_parser_get_root (parser); - layout = json_to_layout (context, node, flags, error); - - g_object_unref (parser); + gtk_json_parser_free (parser); return layout; } @@ -1643,28 +1520,61 @@ pango_layout_deserialize (PangoContext *context, GBytes * pango_font_serialize (PangoFont *font) { - JsonGenerator *generator; - JsonNode *node; + GString *str; + GtkJsonPrinter *printer; 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); + str = g_string_new (""); - json_generator_set_root (generator, node); - data = json_generator_to_data (generator, &size); + printer = gtk_json_printer_new (gstring_write, str, NULL); + gtk_json_printer_set_flags (printer, GTK_JSON_PRINTER_PRETTY); + add_font (printer, NULL, font); + gtk_json_printer_free (printer); - json_node_free (node); - g_object_unref (generator); + size = str->len; + data = g_string_free (str, FALSE); return g_bytes_new_take (data, size); } +/** + * pango_font_deserialize: + * @context: a `PangoContext` + * @bytes: the bytes containing the data + * @error: return location for an error + * + * Loads data previously created via [method@Pango.Font.serialize]. + * + * For a discussion of the supported format, see that function. + * + * Note: to verify that the returned font is identical to + * the one that was serialized, you can compare @bytes to the + * result of serializing the font again. + * + * Returns: (nullable) (transfer full): a new `PangoFont` + * + * Since: 1.50 + */ +PangoFont * +pango_font_deserialize (PangoContext *context, + GBytes *bytes, + GError **error) +{ + PangoFont *font; + GtkJsonParser *parser; + + g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL); + + parser = gtk_json_parser_new_for_bytes (bytes); + font = json_parser_load_font (parser, context, error); + gtk_json_parser_free (parser); + + return font; +} + /* }}} */ /* vim:set foldmethod=marker expandtab: */ |