Commit 0b81ac4
Fallback fonts in SVG drawings
Changed files (5)
modifiedlibbirdfont/TextRendering/FallbackFont.valamodifiedlibbirdfont/TextRendering/fontconfig.cmodifiedlibsvgbird/SvgFile.valamodifiedlibsvgbird/Text.valamodifiedlibsvgbird/text_helpers.c
Diff
@@ -14,32 +14,6 @@ using Gee; -[SimpleType]-[CCode (has_type_id = false)]-public extern struct FcConfig {-}--[CCode (cname = "FcInitLoadConfigAndFonts")]-public extern FcConfig* FcInitLoadConfigAndFonts ();--[CCode (cname = "FcConfigAppFontAddDir")]-public extern string* FcConfigAppFontAddDir (FcConfig* config, string path);--[CCode (cname = "FcConfigSetSysRoot")]-public extern void FcConfigSetSysRoot (FcConfig* config, string path);--[CCode (cname = "FcConfigParseAndLoad")]-public extern bool FcConfigParseAndLoad (FcConfig* config, string path, bool complain);--[CCode (cname = "FcConfigSetCurrent")]-public extern void FcConfigSetCurrent (FcConfig* config);--[CCode (cname = "FcConfigCreate")]-public extern FcConfig* FcConfigCreate ();--[CCode (cname = "FcConfigFilename")]-public extern string FcConfigFilename (string path);- [CCode (cname = "find_font")] public extern string? find_font (FcConfig* font_config, string characters); @@ -130,8 +130,9 @@ gchar* find_font_file (FcConfig* font_config, const gchar* font_name) { path = g_strdup ((gchar*) file); break; }+ + FcPatternDestroy (font); }- FcPatternDestroy (font); } FcPatternDestroy (search_pattern);@@ -614,11 +614,11 @@ public class SvgFile : GLib.Object { string name = attr.get_name (); if (name == "font-size") {- text.font_size = (int) parse_number (attr.get_content ());+ text.set_font_size ((int) parse_number (attr.get_content ())); } if (name == "font-family") {- text.font_family = attr.get_content ();+ text.set_font (attr.get_content ()); } if (name == "x") {@@ -15,31 +15,110 @@ using Cairo; using Math; -extern class svg_bird_font_item {}+[SimpleType]+[CCode (has_type_id = false)]+public extern struct FcConfig {+}++[SimpleType]+[CCode (has_type_id = false)]+extern struct svg_bird_font_item {+}+ extern void svg_bird_draw_text (Context cr, svg_bird_font_item* font, string text); extern void svg_bird_font_item_delete (svg_bird_font_item* item);-extern svg_bird_font_item svg_bird_font_item_create (string font_file, int font_size);+extern svg_bird_font_item* svg_bird_font_item_create (string font_file, int font_size);++extern bool svg_bird_has_font_config ();+extern void svg_bird_set_font_config (FcConfig* f);++[CCode (cname = "FcInitLoadConfigAndFonts")]+public extern FcConfig* FcInitLoadConfigAndFonts ();+ +[CCode (cname = "FcConfigAppFontAddDir")]+public extern string* FcConfigAppFontAddDir (FcConfig* config, string path);++[CCode (cname = "FcConfigSetSysRoot")]+public extern void FcConfigSetSysRoot (FcConfig* config, string path);++[CCode (cname = "FcConfigParseAndLoad")]+public extern bool FcConfigParseAndLoad (FcConfig* config, string path, bool complain);++[CCode (cname = "FcConfigSetCurrent")]+public extern void FcConfigSetCurrent (FcConfig* config);++[CCode (cname = "FcConfigCreate")]+public extern FcConfig* FcConfigCreate ();++[CCode (cname = "FcConfigFilename")]+public extern string FcConfigFilename (string path);+ namespace SvgBird { public class Text : Object {- public string font_family = "";- public int font_size = 12;+ string font_family = "";+ int font_size = 12;+ string content;+ public double x = 0; public double y = 0;- public string content;- - svg_bird_font_item* font;++ svg_bird_font_item* font = null; public Text () {- font = svg_bird_font_item_create ("resources/Roboto-Regular.ttf", 34);+ if (!svg_bird_has_font_config ()) {+ init_font_config ();+ }+ + set_font ("Roboto"); } ~Text () { svg_bird_font_item_delete (font); } + public void set_font_size (int s) {+ font_size = s;+ set_font (font_family);+ }+ + public void set_font (string font_family) {+ if (font != null) {+ svg_bird_font_item_delete (font);+ }+ + font = svg_bird_font_item_create (font_family, font_size);+ + if (font == null) {+ font = svg_bird_font_item_create ("sans-serif", font_size);+ }+ }++ public void init_font_config () {+ FcConfig* config;+ +#if MAC+ config = FcConfigCreate();+ + string bundle = (!) BirdFont.get_settings_directory ().get_path ();+ FcConfigSetSysRoot(config, bundle);+ + string path = FcConfigFilename((!) SearchPaths.search_file(null, "fontconfig.settings").get_path ());+ bool loaded = FcConfigParseAndLoad(config, path, true);+ + if (!loaded) {+ warning ("Fontconfig initialization failed.");+ }+ + FcConfigSetCurrent (config);+#else+ config = FcInitLoadConfigAndFonts ();+#endif+ svg_bird_set_font_config (config);+ }+ public void set_text (string t) { content = t.replace ("\n", " "); content = content.replace ("\t", " ");@@ -62,7 +141,13 @@ public class Text : Object { public override void draw_outline (Context cr) { cr.save (); cr.translate (x, y);- svg_bird_draw_text (cr, font, content);+ + if (font != null) {+ svg_bird_draw_text (cr, font, content);+ } else {+ warning ("No font.");+ }+ cr.restore (); } @@ -6,9 +6,9 @@ #include <harfbuzz/hb-ft.h> #include <cairo.h> #include <cairo-ft.h>--#define FONT_SIZE 36-#define MARGIN (FONT_SIZE * .5)+ +static GMutex font_config_lock;+FcConfig* font_config = NULL; typedef struct svg_bird_font_item_t { int font_size;@@ -17,37 +17,81 @@ typedef struct svg_bird_font_item_t { hb_font_t *hb_font; } svg_bird_font_item; -svg_bird_font_item* svg_bird_font_item_create (const char* font_file, int font_size) {- FT_Error ft_error;+gchar* svg_bird_find_font_file (const gchar* font_name);+void svg_bird_font_item_delete (svg_bird_font_item* item);++gboolean svg_bird_has_font_config () {+ g_mutex_lock (&font_config_lock);+ gboolean exists = font_config != NULL;+ g_mutex_unlock (&font_config_lock);+ return exists;+}++void svg_bird_set_font_config (FcConfig* f) {+ g_mutex_lock (&font_config_lock);+ font_config = f;+ g_mutex_unlock (&font_config_lock);+} +svg_bird_font_item* svg_bird_font_item_create (const char* font_family, int font_size) {+ FT_Error ft_error; svg_bird_font_item* font = malloc (sizeof (svg_bird_font_item));+ memset (font, 0, sizeof (svg_bird_font_item));+ + char* font_file = svg_bird_find_font_file (font_family); + FT_Library ft_library = 0;+ FT_Face ft_face = 0;+ font->font_size = font_size;-- if ((ft_error = FT_Init_FreeType (&font->ft_library))) {+ if ((ft_error = FT_Init_FreeType (&ft_library))) { g_warning ("Can't init freetype");+ svg_bird_font_item_delete (font); return NULL;- }- - if ((ft_error = FT_New_Face (font->ft_library, font_file, 0, &font->ft_face))) {- g_warning ("Can't init freetype font.");+ }+ + font->ft_library = ft_library;+ + if ((ft_error = FT_New_Face (font->ft_library, font_file, 0, &ft_face))) {+ g_warning ("Can't find freetype font %s %s.", font_family, font_file);+ svg_bird_font_item_delete (font); return NULL;- }- + }+ + font->ft_face = ft_face;+ if ((ft_error = FT_Set_Char_Size (font->ft_face, font_size * 64, font_size * 64, 0, 0))) { g_warning ("Can't set font size.");+ svg_bird_font_item_delete (font); return NULL; } font->hb_font = hb_ft_font_create (font->ft_face, NULL); + if (font->hb_font == NULL) {+ g_warning ("Can't create harfbuzz font for %s", font_file);+ svg_bird_font_item_delete (font);+ return NULL;+ }++ free (font_file);+ return font; } void svg_bird_font_item_delete (svg_bird_font_item* item) {- if (item != NULL) {- FT_Done_Face (item->ft_face);- FT_Done_FreeType (item->ft_library);- hb_font_destroy (item->hb_font);+ if (item) {+ if (item->ft_face) {+ FT_Done_Face (item->ft_face);+ }++ if (item->hb_font) {+ hb_font_destroy (item->hb_font);+ }+ + if (item->ft_library) {+ FT_Done_FreeType (item->ft_library);+ }+ free (item); } }@@ -74,7 +118,7 @@ void svg_bird_draw_text (cairo_t* cr, svg_bird_font_item* font, const char* text unsigned int cluster = info[i].cluster; double x_position = current_x + pos[i].x_offset / 64.; double y_position = current_y + pos[i].y_offset / 64.;- + current_x += pos[i].x_advance / 64.; current_y += pos[i].y_advance / 64.; }@@ -117,21 +161,92 @@ void svg_bird_draw_text (cairo_t* cr, svg_bird_font_item* font, const char* text cairo_glyphs[i].index = info[i].codepoint; cairo_glyphs[i].x = current_x + pos[i].x_offset / 64.; cairo_glyphs[i].y = -(current_y + pos[i].y_offset / 64.);- + double dx = pos[i].x_advance / 64.0; double dy = pos[i].y_advance / 64.0;- + cairo_matrix_transform_distance (&matrix, &dx, &dy); current_x += dx; current_y += dy;- }+ } cairo_show_glyphs (cr, cairo_glyphs, len); cairo_glyph_free (cairo_glyphs); cairo_font_face_destroy (cairo_face);-+ hb_buffer_destroy (hb_buffer); cairo_restore (cr);+ }++gchar* svg_bird_find_font_file (const gchar* font_name) {+ const FcChar8* name;+ FcPattern* search_pattern;+ FcPattern* font;+ FcChar8* file;+ gchar* path;+ FcObjectSet* font_properties;+ FcFontSet* fonts;+ int i;+ + if (!svg_bird_has_font_config ()) {+ g_warning("Font config not loaded.");+ return NULL;+ }++ g_mutex_lock (&font_config_lock);++ if (font_config == NULL) {+ g_warning("Font config not loaded.");+ return NULL;+ }+ + path = NULL;+ name = font_name;++ // match any font as fallback+ search_pattern = FcPatternCreate ();+ font_properties = FcObjectSetBuild (FC_FILE, NULL);++ fonts = FcFontList (font_config, search_pattern, font_properties);+ + if (fonts->nfont > 0) {+ for (i = 0; i < fonts->nfont; i++) {+ font = fonts->fonts[i];+ + if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) {+ path = g_strdup ((gchar*) file);+ break;+ }+ + FcPatternDestroy (font);+ }+ }+ + FcPatternDestroy (search_pattern);+ + // search for a family name+ search_pattern = FcPatternCreate ();+ FcPatternAddString (search_pattern, FC_FAMILY, name);+ fonts = FcFontList (font_config, search_pattern, font_properties);+ + if (fonts->nfont > 0) {+ for (i = 0; i < fonts->nfont; i++) {+ font = fonts->fonts[i];+ + if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) {+ g_free (path);+ path = g_strdup ((gchar*) file);+ break;+ }+ + FcPatternDestroy (font);+ }+ }++ FcPatternDestroy (search_pattern);+ + g_mutex_unlock (&font_config_lock);+ return path; }