/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * gimpfontfactory.c * Copyright (C) 2003-2018 Michael Natterer * * Partly based on code Copyright (C) Sven Neumann * Manish Singh * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #include #include #include #include "libgimpbase/gimpbase.h" #include "libgimpconfig/gimpconfig.h" #include "text-types.h" #include "core/gimp.h" #include "core/gimp-parallel.h" #include "core/gimpasync.h" #include "core/gimpasyncset.h" #include "core/gimpcancelable.h" #include "core/gimpcontainer.h" #include "gimpfont.h" #include "gimpfontfactory.h" #include "gimp-intl.h" #include #define CONF_FNAME "fonts.conf" struct _GimpFontFactoryPrivate { GSList *fonts_renaming_config; gchar *conf; gchar *sysconf; PangoContext *pango_context; }; #define GET_PRIVATE(obj) (((GimpFontFactory *) (obj))->priv) static void gimp_font_factory_data_init (GimpDataFactory *factory, GimpContext *context); static void gimp_font_factory_data_refresh (GimpDataFactory *factory, GimpContext *context); static void gimp_font_factory_data_save (GimpDataFactory *factory); static void gimp_font_factory_data_cancel (GimpDataFactory *factory); static GimpData * gimp_font_factory_data_duplicate (GimpDataFactory *factory, GimpData *data); static gboolean gimp_font_factory_data_delete (GimpDataFactory *factory, GimpData *data, gboolean delete_from_disk, GError **error); static void gimp_font_factory_finalize (GObject *object); static void gimp_font_factory_load (GimpFontFactory *factory, GError **error); static gboolean gimp_font_factory_load_fonts_conf (FcConfig *config, GFile *fonts_conf); static void gimp_font_factory_add_directories (GimpFontFactory *factory, FcConfig *config, GList *path, GError **error); static void gimp_font_factory_recursive_add_fontdir (FcConfig *config, GFile *file, GError **error); static int gimp_font_factory_load_names (GimpFontFactory *container); static void gimp_font_factory_load_aliases (GimpContainer *container, PangoContext *context); G_DEFINE_TYPE_WITH_PRIVATE (GimpFontFactory, gimp_font_factory, GIMP_TYPE_DATA_FACTORY) #define parent_class gimp_font_factory_parent_class static void gimp_font_factory_class_init (GimpFontFactoryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GimpDataFactoryClass *factory_class = GIMP_DATA_FACTORY_CLASS (klass); object_class->finalize = gimp_font_factory_finalize; factory_class->data_init = gimp_font_factory_data_init; factory_class->data_refresh = gimp_font_factory_data_refresh; factory_class->data_save = gimp_font_factory_data_save; factory_class->data_cancel = gimp_font_factory_data_cancel; factory_class->data_duplicate = gimp_font_factory_data_duplicate; factory_class->data_delete = gimp_font_factory_data_delete; } static void gimp_font_factory_init (GimpFontFactory *factory) { factory->priv = gimp_font_factory_get_instance_private (factory); } static void gimp_font_factory_data_init (GimpDataFactory *factory, GimpContext *context) { GError *error = NULL; gimp_font_factory_load (GIMP_FONT_FACTORY (factory), &error); if (error) { gimp_message_literal (gimp_data_factory_get_gimp (factory), NULL, GIMP_MESSAGE_INFO, error->message); g_error_free (error); } } static void gimp_font_factory_data_refresh (GimpDataFactory *factory, GimpContext *context) { GError *error = NULL; gimp_font_factory_load (GIMP_FONT_FACTORY (factory), &error); if (error) { gimp_message_literal (gimp_data_factory_get_gimp (factory), NULL, GIMP_MESSAGE_INFO, error->message); g_error_free (error); } } static void gimp_font_factory_data_save (GimpDataFactory *factory) { /* this is not "saving" but this functions is called at the right * time at exit to reset the config */ /* if font loading is in progress in another thread, do nothing. calling * FcInitReinitialize() while loading takes place is unsafe. */ if (! gimp_async_set_is_empty (gimp_data_factory_get_async_set (factory))) return; /* Reinit the library with defaults. */ FcInitReinitialize (); } static void gimp_font_factory_data_cancel (GimpDataFactory *factory) { GimpAsyncSet *async_set = gimp_data_factory_get_async_set (factory); /* we can't really cancel font loading, so we just clear the async set and * return without waiting for loading to finish. we also cancel the async * set beforehand, as a way to signal to * gimp_font_factory_load_async_callback() that loading was canceled and the * factory might be dead, and that it should just do nothing. */ gimp_cancelable_cancel (GIMP_CANCELABLE (async_set)); gimp_async_set_clear (async_set); } static GimpData * gimp_font_factory_data_duplicate (GimpDataFactory *factory, GimpData *data) { return NULL; } static gboolean gimp_font_factory_data_delete (GimpDataFactory *factory, GimpData *data, gboolean delete_from_disk, GError **error) { return TRUE; } static void gimp_font_factory_finalize (GObject *object) { GimpFontFactory *font_factory = GIMP_FONT_FACTORY(object); g_slist_free_full (GET_PRIVATE (font_factory)->fonts_renaming_config, (GDestroyNotify) g_free); g_free (GET_PRIVATE (font_factory)->sysconf); g_free (GET_PRIVATE (font_factory)->conf); g_object_unref (GET_PRIVATE (font_factory)->pango_context); FcConfigDestroy (FcConfigGetCurrent ()); G_OBJECT_CLASS (parent_class)->finalize (object); } /* public functions */ GimpDataFactory * gimp_font_factory_new (Gimp *gimp, const gchar *path_property_name) { g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); g_return_val_if_fail (path_property_name != NULL, NULL); return g_object_new (GIMP_TYPE_FONT_FACTORY, "gimp", gimp, "data-type", GIMP_TYPE_FONT, "path-property-name", path_property_name, "get-standard-func", gimp_font_get_standard, "unique-names", FALSE, NULL); } GList * gimp_font_factory_get_custom_fonts_dirs (GimpFontFactory *factory) { return gimp_data_factory_get_data_path (GIMP_DATA_FACTORY (factory)); } void gimp_font_factory_get_custom_config_path (GimpFontFactory *factory, gchar **conf, gchar **sysconf) { *conf = GET_PRIVATE (factory)->conf; *sysconf = GET_PRIVATE (factory)->sysconf; } GSList * gimp_font_factory_get_fonts_renaming_config (GimpFontFactory *factory) { return (GET_PRIVATE (factory))->fonts_renaming_config; } PangoContext * gimp_font_factory_get_pango_context (GimpFontFactory *factory) { return GET_PRIVATE (factory)->pango_context; } /* private functions */ static void gimp_font_factory_load_async (GimpAsync *async, GimpFontFactory *factory) { if (FcConfigBuildFonts (NULL)) { gimp_async_finish (async, GINT_TO_POINTER (gimp_font_factory_load_names (factory))); } else { FcConfigDestroy (FcConfigGetCurrent ()); gimp_async_abort (async); } } static void gimp_font_factory_load_async_callback (GimpAsync *async, GimpFontFactory *factory) { GimpContainer *container; /* the operation was canceled and the factory might be dead (see * gimp_font_factory_data_cancel()). bail. */ if (gimp_async_is_canceled (async)) return; container = gimp_data_factory_get_container (GIMP_DATA_FACTORY (factory)); if (gimp_async_is_finished (async)) { gint num_fonts = GPOINTER_TO_INT (gimp_async_get_result (async)); PangoFontMap *fontmap; PangoContext *context; fontmap = pango_cairo_font_map_new_for_font_type (CAIRO_FONT_TYPE_FT); if (! fontmap) g_error ("You are using a Pango that has been built against a cairo " "that lacks the Freetype font backend"); pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (fontmap), 72.0 /* FIXME */); context = pango_font_map_create_context (fontmap); GET_PRIVATE (factory)->pango_context = context; g_object_unref (fontmap); /* only create aliases if there is at least one font available */ if (num_fonts > 0) gimp_font_factory_load_aliases (container, context); } gimp_container_thaw (container); } static void gimp_font_factory_load (GimpFontFactory *factory, GError **error) { GimpContainer *container; Gimp *gimp; GimpAsyncSet *async_set; FcConfig *config; GFile *fonts_conf; GList *path; GimpAsync *async; async_set = gimp_data_factory_get_async_set (GIMP_DATA_FACTORY (factory)); if (! gimp_async_set_is_empty (async_set)) { /* font loading is already in progress */ return; } container = gimp_data_factory_get_container (GIMP_DATA_FACTORY (factory)); gimp = gimp_data_factory_get_gimp (GIMP_DATA_FACTORY (factory)); if (gimp->be_verbose) g_print ("Loading fonts\n"); config = FcInitLoadConfig (); if (! config) return; fonts_conf = gimp_directory_file (CONF_FNAME, NULL); if (! gimp_font_factory_load_fonts_conf (config, fonts_conf)) { g_printerr ("%s: failed to read '%s'.\n", G_STRFUNC, g_file_peek_path (fonts_conf)); } else { g_free (GET_PRIVATE (factory)->conf); GET_PRIVATE (factory)->conf = g_file_get_path (fonts_conf); } g_object_unref (fonts_conf); fonts_conf = gimp_sysconf_directory_file (CONF_FNAME, NULL); if (! gimp_font_factory_load_fonts_conf (config, fonts_conf)) { g_printerr ("%s: failed to read '%s'.\n", G_STRFUNC, g_file_peek_path (fonts_conf)); } else { g_free (GET_PRIVATE (factory)->sysconf); GET_PRIVATE (factory)->sysconf = g_file_get_path (fonts_conf); } g_object_unref (fonts_conf); path = gimp_data_factory_get_data_path (GIMP_DATA_FACTORY (factory)); if (! path) return; gimp_container_freeze (container); gimp_container_clear (container); gimp_font_factory_add_directories (factory, config, path, error); g_list_free_full (path, (GDestroyNotify) g_object_unref); FcConfigSetCurrent (config); /* We perform font cache initialization in a separate thread, so * in the case a cache rebuild is to be done it will not block * the UI. */ async = gimp_parallel_run_async_independent_full ( +10, (GimpRunAsyncFunc) gimp_font_factory_load_async, factory); gimp_async_add_callback_for_object ( async, (GimpAsyncCallback) gimp_font_factory_load_async_callback, factory, factory); gimp_async_set_add (async_set, async); g_object_unref (async); } static gboolean gimp_font_factory_load_fonts_conf (FcConfig *config, GFile *fonts_conf) { gchar *path = g_file_get_path (fonts_conf); gboolean ret = TRUE; if (! FcConfigParseAndLoad (config, (const guchar *) path, FcFalse)) ret = FALSE; g_free (path); return ret; } static void gimp_font_factory_add_directories (GimpFontFactory *factory, FcConfig *config, GList *path, GError **error) { GList *list; for (list = path; list; list = list->next) { /* The configured directories must exist or be created. */ g_file_make_directory_with_parents (list->data, NULL, NULL); /* Do not use FcConfigAppFontAddDir(). Instead use * FcConfigAppFontAddFile() with our own recursive loop. * Otherwise, when some fonts fail to load (e.g. permission * issues), we end up in weird situations where the fonts are in * the list, but are unusable and output many errors. * See bug 748553. */ gimp_font_factory_recursive_add_fontdir (config, list->data, error); } if (error && *error) { gchar *font_list = g_strdup ((*error)->message); g_clear_error (error); g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Some fonts failed to load:\n%s"), font_list); g_free (font_list); } } static void gimp_font_factory_recursive_add_fontdir (FcConfig *config, GFile *file, GError **error) { GFileEnumerator *enumerator; GError *file_error = NULL; g_return_if_fail (config != NULL); enumerator = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, &file_error); if (enumerator) { GFileInfo *info; while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL))) { GFileType file_type; GFile *child; if (g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)) { g_object_unref (info); continue; } file_type = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE); child = g_file_enumerator_get_child (enumerator, info); if (file_type == G_FILE_TYPE_DIRECTORY) { gimp_font_factory_recursive_add_fontdir (config, child, error); } else if (file_type == G_FILE_TYPE_REGULAR) { gchar *path = g_file_get_path (child); #ifdef G_OS_WIN32 gchar *tmp = g_win32_locale_filename_from_utf8 (path); g_free (path); /* XXX: g_win32_locale_filename_from_utf8() may return * NULL. So we need to check that path is not NULL before * trying to load with fontconfig. */ path = tmp; #endif if (! path || FcFalse == FcConfigAppFontAddFile (config, (const FcChar8 *) path)) { g_printerr ("%s: adding font file '%s' failed.\n", G_STRFUNC, path); if (error) { if (*error) { gchar *current_message = g_strdup ((*error)->message); g_clear_error (error); g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s\n- %s", current_message, path); g_free (current_message); } else { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "- %s", path); } } } g_free (path); } g_object_unref (child); g_object_unref (info); } g_object_unref (enumerator); } else { if (error) { gchar *path = g_file_get_path (file); /* The font directories are supposed to exist since we create * them in gimp_font_factory_add_directories() when they * aren't already there. * Yet there are cases where empty folders can be deleted and * system paths are read-only. This happens for instance for * MSIX (see #11401). * So as a special exception, we ignore the case where the * folders don't exist, but we still warn for all other types * of errors. */ if (! file_error || file_error->code != G_IO_ERROR_NOT_FOUND) { if (*error) { gchar *current_message = g_strdup ((*error)->message); g_clear_error (error); if (file_error != NULL) g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s\n- %s%s (%s)", current_message, path, G_DIR_SEPARATOR_S, file_error->message); else g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s\n- %s%s", current_message, path, G_DIR_SEPARATOR_S); g_free (current_message); } else { if (file_error != NULL) g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "- %s%s (%s)", path, G_DIR_SEPARATOR_S, file_error->message); else g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "- %s%s", path, G_DIR_SEPARATOR_S); } } g_free (path); } } g_clear_error (&file_error); } static void gimp_font_factory_add_font (GimpContainer *container, PangoFontDescription *desc, const gchar *full_name, const gchar *path, gpointer font_info[]) { gchar *name = (gchar *) full_name; if (! desc && ! full_name) return; if (! full_name) name = pango_font_description_to_string (desc); /* It doesn't look like pango_font_description_to_string() could ever * return NULL. But just to be double sure and avoid a segfault, I * check before validating the string. */ if (name && strlen (name) > 0 && g_utf8_validate (name, -1, NULL)) { GimpFont *font; font = g_object_new (GIMP_TYPE_FONT, "name", name, NULL); gimp_font_set_lookup_name (font, pango_font_description_to_string (desc)); if (font_info != NULL) gimp_font_set_font_info (font, font_info); if (path != NULL) { GFile *file; file = g_file_new_for_path (path); gimp_data_set_file (GIMP_DATA (font), file, FALSE, FALSE); g_object_unref (file); } else { gchar *collection; collection = g_strdup_printf ("gimp-font-standard-alias: %s", name); gimp_data_make_internal (GIMP_DATA (font), collection); g_free (collection); } gimp_container_add (container, GIMP_OBJECT (font)); g_object_unref (font); } if (!full_name) g_free (name); } /* We're really chummy here with the implementation. Oh well. */ /* This is copied straight from make_alias_description in pango, plus * the gimp_font_list_add_font bits. */ static void gimp_font_factory_make_alias (GimpContainer *container, PangoContext *context, const gchar *family, gboolean bold, gboolean italic) { FcPattern *fcpattern; PangoFontDescription *desc = pango_font_description_new (); gchar *desc_str = NULL; gchar *style = NULL; gchar *psname = NULL; gchar *fullname = NULL; gchar *file = NULL; gint index = -1; gint weight = -1; gint width = -1; gint slant = -1; gint fontversion = -1; gpointer font_info[PROPERTIES_COUNT]; pango_font_description_set_family (desc, family); pango_font_description_set_style (desc, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); pango_font_description_set_variant (desc, PANGO_VARIANT_NORMAL); pango_font_description_set_weight (desc, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); pango_font_description_set_stretch (desc, PANGO_STRETCH_NORMAL); fcpattern = pango_fc_font_get_pattern (PANGO_FC_FONT (pango_context_load_font (context, desc))); /* this is for backward compatibility*/ desc_str = pango_font_description_to_string (desc); FcPatternGetString (fcpattern, FC_FILE, 0, (FcChar8 **) &file); FcPatternGetString (fcpattern, FC_FULLNAME, 0, (FcChar8 **) &fullname); FcPatternGetString (fcpattern, FC_POSTSCRIPT_NAME, 0, (FcChar8 **) &psname); FcPatternGetString (fcpattern, FC_STYLE, 0, (FcChar8 **) &style); FcPatternGetInteger (fcpattern, FC_WEIGHT, 0, &weight); FcPatternGetInteger (fcpattern, FC_WIDTH, 0, &width); FcPatternGetInteger (fcpattern, FC_INDEX, 0, &index); FcPatternGetInteger (fcpattern, FC_SLANT, 0, &slant); FcPatternGetInteger (fcpattern, FC_FONTVERSION, 0, &fontversion); font_info[PROP_DESC] = (gpointer) desc_str; font_info[PROP_FULLNAME] = (gpointer) fullname; font_info[PROP_FAMILY] = (gpointer) family; font_info[PROP_STYLE] = (gpointer) style; font_info[PROP_PSNAME] = (gpointer) psname; font_info[PROP_WEIGHT] = (gpointer) &weight; font_info[PROP_WIDTH] = (gpointer) &width; font_info[PROP_INDEX] = (gpointer) &index; font_info[PROP_SLANT] = (gpointer) &slant; font_info[PROP_FONTVERSION] = (gpointer) &fontversion; font_info[PROP_FILE] = (gpointer) file; /* This might be the only valid time where a NULL path is valid. Though I do * wonder if really these aliases are the right thing to do. Generic aliases * are the best way to have differing text renders over time (and that's not * something to be wished for). XXX */ gimp_font_factory_add_font (container, desc, NULL, NULL, font_info); g_free (desc_str); pango_font_description_free (desc); } static void gimp_font_factory_load_aliases (GimpContainer *container, PangoContext *context) { const gchar *families[] = { "Sans-serif", "Serif", "Monospace" }; gint i; for (i = 0; i < 3; i++) { gimp_font_factory_make_alias (container, context, families[i], FALSE, FALSE); gimp_font_factory_make_alias (container, context, families[i], TRUE, FALSE); gimp_font_factory_make_alias (container, context, families[i], FALSE, TRUE); gimp_font_factory_make_alias (container, context, families[i], TRUE, TRUE); } } static gint gimp_font_factory_load_names (GimpFontFactory *factory) { GimpContainer *container; FcObjectSet *os; FcPattern *pat; FcFontSet *fontset; FT_Library ft; GSList *xml_configs_list; GString *xml; GString *xml_bold_variant; GString *xml_italic_variant; GString *xml_bold_italic_variant_and_global; GString *ignored_fonts; gint n_ignored = 0; gint i; gint num_fonts_in_current_config = 0; gint n_loaded_fonts = 0; container = gimp_data_factory_get_container (GIMP_DATA_FACTORY (factory)); os = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_POSTSCRIPT_NAME, FC_FULLNAME, FC_FILE, FC_WEIGHT, FC_SLANT, FC_WIDTH, FC_INDEX, FC_FONTVERSION, NULL); g_return_val_if_fail (os, -1); pat = FcPatternCreate (); if (! pat) { FcObjectSetDestroy (os); g_critical ("%s: FcPatternCreate() returned NULL.", G_STRFUNC); return -1; } if (FT_Init_FreeType (&ft)) { g_critical ("%s: FreeType Initialization Failed.", G_STRFUNC); return -1; } fontset = FcFontList (NULL, pat, os); FcPatternDestroy (pat); FcObjectSetDestroy (os); g_return_val_if_fail (fontset, -1); xml_configs_list = NULL; xml = g_string_new (NULL); xml_italic_variant = g_string_new (NULL); xml_bold_variant = g_string_new (NULL); xml_bold_italic_variant_and_global = g_string_new (""); ignored_fonts = g_string_new (NULL); #define MAX_NUM_FONTS_PER_CONFIG 1000 for (i = 0; i < fontset->nfont; i++) { PangoFontDescription *pfd; gchar *family = NULL; gchar *style = NULL; gchar *psname = NULL; gchar *newname = NULL; gchar *display_name = NULL; gchar *escaped_fullname = NULL; gchar *fullname = NULL; gchar *escaped_file = NULL; gchar *file = NULL; gint index = -1; gint weight = -1; gint width = -1; gint slant = -1; gint fontversion = -1; gpointer font_info[PROPERTIES_COUNT]; PangoFontDescription *pattern_pfd; gchar *pattern_pfd_desc; FT_Face face; FcPatternGetString (fontset->fonts[i], FC_FILE, 0, (FcChar8 **) &file); if (file == NULL || ! g_utf8_validate (file, -1, NULL)) { g_string_append_printf (ignored_fonts, "- %s (not a valid utf-8 file name)\n", file); n_ignored++; continue; } if (FT_New_Face (ft, file, 0, &face)) { g_string_append_printf (ignored_fonts, "- %s (Failed To Create A FreeType Face)\n", file); n_ignored++; continue; } /* * Pango doesn't support non SFNT fonts because harfbuzz doesn't support them. * woff and woff2, not supported by pango (because they are not yet supported by harfbuzz, * when using harfbuzz's default loader, which is how pango uses it). * pcf,pcf.gz are bitmap font formats, not supported by pango (because of harfbuzz). * afm, pfm, pfb are type1 font formats, not supported by pango (because of harfbuzz). */ if (face->face_flags & FT_FACE_FLAG_SFNT) { /* If this is an SFNT wrapper, read the first 4 bytes to see if it is a WOFF[2] font. */ char buf[4] = {0}; int fd = g_open ((gchar *) file, O_RDONLY, 0); read (fd, buf, 4); g_close (fd, NULL); FT_Done_Face (face); if (buf[0] == 'w' && buf[1] == 'O' && buf[2] == 'F' && (buf[3] == 'F' || buf[3] == '2')) { g_string_append_printf (ignored_fonts, "- %s (WOFF[2] font)\n", file); n_ignored++; continue; } } else { FT_Done_Face (face); g_string_append_printf (ignored_fonts, "- %s (NON SFNT font)\n", file); n_ignored++; continue; } /* Some variable fonts have only a family name and a font version. * But we also check in case there is no family name */ if (FcPatternGetString (fontset->fonts[i], FC_FULLNAME, 0, (FcChar8 **) &fullname) != FcResultMatch || FcPatternGetString (fontset->fonts[i], FC_FAMILY, 0, (FcChar8 **) &family) != FcResultMatch || ! g_utf8_validate (fullname, -1, NULL) || ! g_utf8_validate (family, -1, NULL)) { g_string_append_printf (ignored_fonts, "- %s (no or invalid full name and/or family)\n", file); n_ignored++; continue; } FcPatternGetString (fontset->fonts[i], FC_POSTSCRIPT_NAME, 0, (FcChar8 **) &psname); FcPatternGetString (fontset->fonts[i], FC_STYLE, 0, (FcChar8 **) &style); FcPatternGetInteger (fontset->fonts[i], FC_WEIGHT, 0, &weight); FcPatternGetInteger (fontset->fonts[i], FC_WIDTH, 0, &width); FcPatternGetInteger (fontset->fonts[i], FC_INDEX, 0, &index); FcPatternGetInteger (fontset->fonts[i], FC_SLANT, 0, &slant); FcPatternGetInteger (fontset->fonts[i], FC_FONTVERSION, 0, &fontversion); /* this is for backward compatibility*/ pattern_pfd = pango_fc_font_description_from_pattern (fontset->fonts[i], FALSE); pattern_pfd_desc = pango_font_description_to_string (pattern_pfd); font_info[PROP_DESC] = (gpointer) pattern_pfd_desc; font_info[PROP_FULLNAME] = (gpointer) fullname; font_info[PROP_FAMILY] = (gpointer) family; font_info[PROP_STYLE] = (gpointer) style; font_info[PROP_PSNAME] = (gpointer) psname; font_info[PROP_WEIGHT] = (gpointer) &weight; font_info[PROP_WIDTH] = (gpointer) &width; font_info[PROP_INDEX] = (gpointer) &index; font_info[PROP_SLANT] = (gpointer) &slant; font_info[PROP_FONTVERSION] = (gpointer) &fontversion; font_info[PROP_FILE] = (gpointer) file; newname = g_strdup_printf ("gimpfont%i", i); if (num_fonts_in_current_config == MAX_NUM_FONTS_PER_CONFIG) { xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, xml_italic_variant->str); xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, xml_bold_variant->str); xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, xml->str); xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, ""); FcConfigParseAndLoadFromMemory (FcConfigGetCurrent (), (const FcChar8 *) xml_bold_italic_variant_and_global->str, FcTrue); xml_configs_list = g_slist_append (xml_configs_list, g_string_free (xml_bold_italic_variant_and_global, FALSE)); g_string_free (xml, TRUE); g_string_free (xml_italic_variant, TRUE); g_string_free (xml_bold_variant, TRUE); xml = g_string_new (NULL); xml_italic_variant = g_string_new (NULL); xml_bold_variant = g_string_new (NULL); xml_bold_italic_variant_and_global = g_string_new (""); num_fonts_in_current_config = 0; } xml = g_string_append (xml, ""); /*We can't use faux bold (sometimes real bold) unless it is specified in fontconfig*/ xml_bold_variant = g_string_append (xml_bold_variant, ""); xml_italic_variant = g_string_append (xml_italic_variant, ""); xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, ""); g_string_append_printf (xml, "%s", newname); g_string_append_printf (xml_bold_variant, "%s", newname); g_string_append_printf (xml_italic_variant, "%s", newname); g_string_append_printf (xml_bold_italic_variant_and_global, "%s", newname); g_string_append (xml_bold_variant, "bold"); g_string_append (xml_italic_variant, "italic"); g_string_append (xml_bold_italic_variant_and_global, "bold"); g_string_append (xml_bold_italic_variant_and_global, "italic"); escaped_fullname = g_markup_escape_text (fullname, -1); g_string_append_printf (xml, "%s", escaped_fullname); family = g_markup_escape_text (family, -1); g_string_append_printf (xml, "%s", family); g_string_append_printf (xml_bold_variant, "%s", family); g_string_append_printf (xml_italic_variant, "%s", family); g_string_append_printf (xml_bold_italic_variant_and_global, "%s", family); g_string_append_printf (xml_bold_variant, "%s", escaped_fullname); g_string_append_printf (xml_italic_variant, "%s", escaped_fullname); g_string_append_printf (xml_bold_italic_variant_and_global, "%s", escaped_fullname); g_free (escaped_fullname); g_free (family); escaped_file = g_markup_escape_text (file, -1); g_string_append_printf (xml, "%s", escaped_file); g_free (escaped_file); if (psname != NULL && g_utf8_validate (psname, -1, NULL)) { psname = g_markup_escape_text (psname, -1); g_string_append_printf (xml, "%s", psname); g_free (psname); } if (style != NULL && g_utf8_validate (style, -1, NULL)) { display_name = g_strdup_printf ("%s %s", (gchar *)font_info[PROP_FAMILY], style); style = g_markup_escape_text (style, -1); g_string_append_printf (xml, "%s", style); g_free (style); } g_string_append (xml_bold_variant, "bold"); g_string_append (xml_bold_italic_variant_and_global, "bold"); if (weight != -1) { g_string_append_printf (xml, "%i", weight); g_string_append_printf (xml_italic_variant, "%i", weight); } if (width != -1) { g_string_append_printf (xml, "%i", width); g_string_append_printf (xml_bold_variant, "%i", width); g_string_append_printf (xml_italic_variant, "%i", width); g_string_append_printf (xml_bold_italic_variant_and_global, "%i", width); } g_string_append (xml_italic_variant, "italic"); g_string_append (xml_bold_italic_variant_and_global, "italic"); if (slant != -1) { g_string_append_printf (xml, "%i", slant); g_string_append_printf (xml_bold_variant, "%i", slant); } if (fontversion != -1) { g_string_append_printf (xml, "%i", fontversion); g_string_append_printf (xml_bold_variant, "%i", fontversion); g_string_append_printf (xml_italic_variant, "%i", fontversion); g_string_append_printf (xml_bold_italic_variant_and_global, "%i", fontversion); } if (index != -1) { g_string_append_printf (xml, "%i", index); } g_string_append (xml, ""); g_string_append (xml_bold_variant, ""); g_string_append (xml_italic_variant, ""); g_string_append (xml_bold_italic_variant_and_global, ""); pfd = pango_font_description_from_string (newname); if (display_name != NULL) { gimp_font_factory_add_font (container, pfd, display_name, (const gchar *) file, font_info); g_free (display_name); } else { gimp_font_factory_add_font (container, pfd, fullname, (const gchar *) file, font_info); } pango_font_description_free (pattern_pfd); g_free (pattern_pfd_desc); pango_font_description_free (pfd); g_free (newname); num_fonts_in_current_config++; } #undef MAX_NUM_FONTS_PER_CONFIG if (num_fonts_in_current_config > 0) { xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, xml_italic_variant->str); xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, xml_bold_variant->str); xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, xml->str); xml_bold_italic_variant_and_global = g_string_append (xml_bold_italic_variant_and_global, ""); FcConfigParseAndLoadFromMemory (FcConfigGetCurrent (), (const FcChar8 *) xml_bold_italic_variant_and_global->str, FcTrue); xml_configs_list = g_slist_append (xml_configs_list, g_string_free (xml_bold_italic_variant_and_global, FALSE)); g_string_free (xml, TRUE); g_string_free (xml_italic_variant, TRUE); g_string_free (xml_bold_variant, TRUE); } g_slist_free_full (GET_PRIVATE (factory)->fonts_renaming_config, (GDestroyNotify) g_free); GET_PRIVATE (factory)->fonts_renaming_config = xml_configs_list; if (n_ignored > 0) { if (g_getenv ("GIMP_DEBUG_FONTS") != NULL) g_printerr ("%s: %d unsupported fonts were ignored: \n%s", G_STRFUNC, n_ignored, ignored_fonts->str); #if defined (GIMP_UNSTABLE) || ! defined (GIMP_RELEASE) else g_printerr ("%s: %d unsupported fonts were ignored. Set the GIMP_DEBUG_FONTS environment variable for a listing.\n", G_STRFUNC, n_ignored); #endif } n_loaded_fonts = fontset->nfont - n_ignored; g_string_free (ignored_fonts, TRUE); FT_Done_FreeType (ft); FcFontSetDestroy (fontset); return n_loaded_fonts; }