From 899dee59261fb00a54a5bf9ca46393bc7eaa171d Mon Sep 17 00:00:00 2001 From: Idriss Fekir Date: Tue, 15 Aug 2023 18:57:52 +0100 Subject: [PATCH] (De)Serialize fonts in pango markup --- app/text/gimptext-parasite.c | 60 ++++++++++++- app/text/gimptext.c | 159 +++++++++++++++++++++++++++++++++++ app/tools/gimptexttool.c | 2 +- 3 files changed, 219 insertions(+), 2 deletions(-) diff --git a/app/text/gimptext-parasite.c b/app/text/gimptext-parasite.c index deb8b56ca2..f6933b6e4c 100644 --- a/app/text/gimptext-parasite.c +++ b/app/text/gimptext-parasite.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "libgimpbase/gimpbase.h" #include "libgimpcolor/gimpcolor.h" @@ -82,19 +83,76 @@ gimp_text_from_parasite (const GimpParasite *parasite, parasite_data = (gchar *) gimp_parasite_get_data (parasite, ¶site_data_size); if (parasite_data) { - gboolean is_old = !g_str_has_prefix (strstr (parasite_data, "\")\n(font"), "\")\n(font \"GimpFont\""); + gboolean is_old = strstr (parasite_data, "\"GimpFont\"") == NULL; + gboolean has_markup = g_str_has_prefix (parasite_data, "(markup "); GimpParasite *new_parasite = NULL; GString *new_data; /* This is for backward compatibility with older xcf files. * font used to be serialized as a string, but now it is serialized/deserialized as * GimpFont, so the object Type name is inserted for the GimpFont deserialization function to be called. + * And more importantly, fonts in the markup are extracted into their own fields for deserialization. */ if (is_old) { new_data = g_string_new (parasite_data); g_string_replace (new_data, "\")\n(font", "\")\n(font \"GimpFont\"", 1); + if (has_markup) + { + PangoAttrList *attr_list; + gchar *desc; + guint length; + GSList *list = NULL; + GSList *fonts = NULL; + GString *markup_fonts = g_string_new (NULL); + glong markup_start_pos = strstr (parasite_data, "\"<") - parasite_data + 1; + glong markup_end_pos = strstr (parasite_data, ">\")") - parasite_data + 1; + gchar *markup_str = g_utf8_substring (parasite_data, + markup_start_pos, + markup_end_pos); + GString *markup = g_string_new (markup_str); + + g_string_replace (markup, "\\\"", "\"", 0); + pango_parse_markup (markup->str, -1, 0, &attr_list, NULL, NULL, NULL); + + list = pango_attr_list_get_attributes (attr_list); + length = g_slist_length (list); + + for (guint i = 0; i < length; ++i) + { + PangoAttrFontDesc *attr_font_desc = pango_attribute_as_font_desc ((PangoAttribute*)g_slist_nth_data (list, i)); + + if (attr_font_desc != NULL) + { + desc = pango_font_description_to_string (attr_font_desc->desc); + + if (g_slist_find_custom (fonts, (gconstpointer) desc, g_str_equal) == NULL) + { + fonts = g_slist_prepend (fonts, (gpointer) desc); + /*duplicate font name to making parsing easier when deserializing*/ + g_string_append_printf (markup_fonts, + "\n\"%s\" \"%s\"", + desc, desc); + } + else + { + g_free (desc); + } + } + + } + g_slist_free_full (fonts, (GDestroyNotify) g_free); + g_slist_free_full (list, (GDestroyNotify) pango_attribute_destroy); + pango_attr_list_unref (attr_list); + + g_string_insert (new_data, markup_end_pos + 1, markup_fonts->str); + + g_free (markup_str); + g_string_free (markup_fonts, TRUE); + g_string_free (markup, TRUE); + } + new_parasite = gimp_parasite_new (gimp_parasite_get_name (parasite), gimp_parasite_get_flags (parasite), new_data->len+1, diff --git a/app/text/gimptext.c b/app/text/gimptext.c index 26c5fdb4aa..ad0e262188 100644 --- a/app/text/gimptext.c +++ b/app/text/gimptext.c @@ -781,6 +781,91 @@ gimp_text_serialize_property (GimpConfig *config, gimp_config_writer_close (writer); + return TRUE; + } + else if (property_id == PROP_MARKUP) + { + gchar *markup = (gchar*)g_value_get_string (value); + GRegex *regex; + GimpText *text; + GimpContainer *container; + PangoAttrList *attr_list; + GimpFont *font; + guint length; + GSList *list = NULL; + GSList *fonts = NULL; + + g_return_val_if_fail (GIMP_IS_TEXT (config), FALSE); + + if (markup == NULL) + return FALSE; + + text = GIMP_TEXT (config); + container = gimp_data_factory_get_container (text->gimp->font_factory); + + /*lookupname format is "gimpfont%d" we keep only the "font%d" part + * this is to avoid problems when deserializing + * e.g. if there are 2 fonts with lookupname gimpfont17 and gimpfont23 + * we might replace the first with a font whose lookupname is gimpfont23, + * and we might replace the original gimpfont23 with say gimpfont29 + * this means that all occurences of gimpfont23 turned into gimpfont29 + */ + regex = g_regex_new ("\"gimpfont(\\d+)\"", 0, 0, NULL); + markup = g_regex_replace (regex, markup, -1, 0, "\"font\\1\"", 0, NULL); + + gimp_config_writer_open (writer, "markup"); + gimp_config_writer_string (writer, markup); + + pango_parse_markup (markup, -1, 0, &attr_list, NULL, NULL, NULL); + + list = pango_attr_list_get_attributes (attr_list); + length = g_slist_length (list); + + for (guint i = 0; i < length; ++i) + { + PangoAttrFontDesc *attr_font_desc = pango_attribute_as_font_desc ((PangoAttribute*)g_slist_nth_data (list, i)); + + if (attr_font_desc != NULL) + { + gchar *altered_font_name = pango_font_description_to_string (attr_font_desc->desc); + gchar *font_name = g_strdup_printf ("gimp%s", altered_font_name); + + if (g_slist_find_custom (fonts, (gconstpointer) font_name, g_str_equal) == NULL) + { + fonts = g_slist_prepend (fonts, (gpointer) font_name); + + font = GIMP_FONT (gimp_container_search (container, + (GimpContainerSearchFunc) gimp_font_match_by_lookup_name, + (gpointer) font_name)); + + gimp_config_writer_open (writer, "markupfont"); + /*lookupname format is "font%d" we keep only the "font%d" (see the above comment)*/ + gimp_config_writer_string (writer, font_name+4); + + gimp_config_writer_open (writer, "font"); + GIMP_CONFIG_GET_IFACE (GIMP_CONFIG (font))->serialize (GIMP_CONFIG (font), + writer, + NULL); + gimp_config_writer_close (writer); + gimp_config_writer_close (writer); + } + + else + { + g_free (font_name); + } + g_free (altered_font_name); + } + } + + gimp_config_writer_close (writer); + + g_slist_free_full (fonts, (GDestroyNotify) g_free); + g_slist_free_full (list, (GDestroyNotify) pango_attribute_destroy); + pango_attr_list_unref (attr_list); + g_free (markup); + g_regex_unref (regex); + return TRUE; } @@ -836,6 +921,80 @@ gimp_text_deserialize_property (GimpConfig *object, return TRUE; } } + else if (property_id == PROP_MARKUP) + { + gchar *markup; + GString *markup_str; + GimpFont *dummy_object = g_object_new (GIMP_TYPE_FONT, NULL); + + gimp_scanner_parse_string (scanner, &markup); + + markup_str = g_string_new (markup); + + /* This is for backward compatibility with older xcf files.*/ + if (g_scanner_peek_next_token (scanner) == G_TOKEN_STRING) + while (g_scanner_peek_next_token (scanner) == G_TOKEN_STRING) + { + gchar *markup_fontname; + gchar *escaped_markup_fontname; + gchar *actual_font_lookupname; + GimpFont *font; + + gimp_scanner_parse_string (scanner, &markup_fontname); + + font = GIMP_FONT (GIMP_CONFIG_GET_IFACE (dummy_object)->deserialize_create (GIMP_TYPE_FONT, + scanner, + -1, + NULL)); + escaped_markup_fontname = g_strdup_printf ("\"%s\"", markup_fontname); + actual_font_lookupname = g_strdup_printf ("\"%s\"", gimp_font_get_lookup_name (font)); + g_string_replace (markup_str, escaped_markup_fontname, actual_font_lookupname, 0); + + g_free (markup_fontname); + g_free (escaped_markup_fontname); + g_free (actual_font_lookupname); + g_object_unref (font); + } + + else + while (g_scanner_peek_next_token (scanner) == G_TOKEN_LEFT_PAREN) + { + gchar *lookupname; + gchar *escaped_lookupname; + gchar *actual_font_lookupname; + GimpFont *font; + + g_scanner_get_next_token (scanner); /* ( */ + g_scanner_get_next_token (scanner); /* "lookupname" */ + gimp_scanner_parse_string (scanner, &lookupname); + + g_scanner_get_next_token (scanner); /* ) */ + g_scanner_get_next_token (scanner); /* font */ + + font = GIMP_FONT (GIMP_CONFIG_GET_IFACE (dummy_object)->deserialize_create (GIMP_TYPE_FONT, + scanner, + -1, + NULL)); + g_scanner_get_next_token (scanner); /* ) */ + g_scanner_get_next_token (scanner); /* ) */ + + escaped_lookupname = g_strdup_printf ("\"%s\"", lookupname); + actual_font_lookupname = g_strdup_printf ("\"%s\"", gimp_font_get_lookup_name (font)); + g_string_replace (markup_str, escaped_lookupname, actual_font_lookupname, 0); + + g_free (lookupname); + g_free (escaped_lookupname); + g_free (actual_font_lookupname); + g_object_unref (font); + } + + g_value_set_string (value, markup_str->str); + + g_object_unref (dummy_object); + g_string_free (markup_str, TRUE); + + return TRUE; + } return FALSE; } diff --git a/app/tools/gimptexttool.c b/app/tools/gimptexttool.c index 252706da79..6bbdc80dd0 100644 --- a/app/tools/gimptexttool.c +++ b/app/tools/gimptexttool.c @@ -290,7 +290,7 @@ gimp_text_tool_constructed (GObject *object) G_OBJECT_CLASS (parent_class)->constructed (object); - text_tool->proxy = g_object_new (GIMP_TYPE_TEXT, NULL); + text_tool->proxy = g_object_new (GIMP_TYPE_TEXT, "gimp", tool->tool_info->gimp, NULL); gimp_text_options_connect_text (options, text_tool->proxy);