(De)Serialize fonts in pango markup

This commit is contained in:
Idriss Fekir 2023-08-15 18:57:52 +01:00 committed by Jehan
parent 7be3f56f5c
commit 899dee5926
3 changed files with 219 additions and 2 deletions

View file

@ -26,6 +26,7 @@
#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <pango/pango.h>
#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, &parasite_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,

View file

@ -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;
}

View file

@ -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);