The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

text_helpers.c in libsvgbird

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libsvgbird/text_helpers.c.
Text extent
1 #include <stdlib.h> 2 #include <glib.h> 3 #include <stdio.h> 4 #include <math.h> 5 #include <harfbuzz/hb.h> 6 #include <harfbuzz/hb-ft.h> 7 #include <cairo.h> 8 #include <cairo-ft.h> 9 10 static GMutex font_config_lock; 11 FcConfig* font_config = NULL; 12 13 typedef struct svg_bird_font_item_t { 14 int font_size; 15 FT_Library ft_library; 16 FT_Face ft_face; 17 hb_font_t *hb_font; 18 } svg_bird_font_item; 19 20 gchar* svg_bird_find_font_file (const gchar* font_name); 21 void svg_bird_font_item_delete (svg_bird_font_item* item); 22 23 gboolean svg_bird_has_font_config () { 24 g_mutex_lock (&font_config_lock); 25 gboolean exists = font_config != NULL; 26 g_mutex_unlock (&font_config_lock); 27 return exists; 28 } 29 30 void svg_bird_set_font_config (FcConfig* f) { 31 g_mutex_lock (&font_config_lock); 32 font_config = f; 33 g_mutex_unlock (&font_config_lock); 34 } 35 36 svg_bird_font_item* svg_bird_font_item_create (const char* font_family, int font_size) { 37 FT_Error ft_error; 38 svg_bird_font_item* font = malloc (sizeof (svg_bird_font_item)); 39 memset (font, 0, sizeof (svg_bird_font_item)); 40 41 char* font_file = svg_bird_find_font_file (font_family); 42 43 FT_Library ft_library = 0; 44 FT_Face ft_face = 0; 45 46 font->font_size = font_size; 47 if ((ft_error = FT_Init_FreeType (&ft_library))) { 48 g_warning ("Can't init freetype"); 49 svg_bird_font_item_delete (font); 50 return NULL; 51 } 52 53 font->ft_library = ft_library; 54 55 if ((ft_error = FT_New_Face (font->ft_library, font_file, 0, &ft_face))) { 56 g_warning ("Can't find freetype font %s %s.", font_family, font_file); 57 svg_bird_font_item_delete (font); 58 return NULL; 59 } 60 61 font->ft_face = ft_face; 62 63 if ((ft_error = FT_Set_Char_Size (font->ft_face, font_size * 64, font_size * 64, 0, 0))) { 64 g_warning ("Can't set font size."); 65 svg_bird_font_item_delete (font); 66 return NULL; 67 } 68 69 font->hb_font = hb_ft_font_create (font->ft_face, NULL); 70 71 if (font->hb_font == NULL) { 72 g_warning ("Can't create harfbuzz font for %s", font_file); 73 svg_bird_font_item_delete (font); 74 return NULL; 75 } 76 77 free (font_file); 78 return font; 79 } 80 81 void svg_bird_font_item_delete (svg_bird_font_item* item) { 82 if (item) { 83 if (item->ft_face) { 84 FT_Done_Face (item->ft_face); 85 } 86 87 if (item->hb_font) { 88 hb_font_destroy (item->hb_font); 89 } 90 91 if (item->ft_library) { 92 FT_Done_FreeType (item->ft_library); 93 } 94 95 free (item); 96 } 97 } 98 99 void svg_bird_draw_text (cairo_t* cr, svg_bird_font_item* font, const char* text) { 100 cairo_save (cr); 101 102 hb_buffer_t *hb_buffer; 103 104 hb_buffer = hb_buffer_create (); 105 hb_buffer_add_utf8 (hb_buffer, text, -1, 0, -1); 106 hb_buffer_guess_segment_properties (hb_buffer); 107 108 hb_shape (font->hb_font, hb_buffer, NULL, 0); 109 110 unsigned int len = hb_buffer_get_length (hb_buffer); 111 hb_glyph_info_t *info = hb_buffer_get_glyph_infos (hb_buffer, NULL); 112 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (hb_buffer, NULL); 113 114 double current_x = 0; 115 double current_y = 0; 116 for (unsigned int i = 0; i < len; i++) { 117 hb_codepoint_t gid = info[i].codepoint; 118 unsigned int cluster = info[i].cluster; 119 double x_position = current_x + pos[i].x_offset / 64.; 120 double y_position = current_y + pos[i].y_offset / 64.; 121 122 current_x += pos[i].x_advance / 64.; 123 current_y += pos[i].y_advance / 64.; 124 } 125 126 double width = 0; 127 double height = 0; 128 for (unsigned int i = 0; i < len; i++) { 129 width += pos[i].x_advance / 64.; 130 height -= pos[i].y_advance / 64.; 131 } 132 133 if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer))) 134 height += font->font_size; 135 else 136 width += font->font_size; 137 138 cairo_font_face_t *cairo_face; 139 cairo_face = cairo_ft_font_face_create_for_ft_face (font->ft_face, 0); 140 cairo_set_font_face (cr, cairo_face); 141 cairo_set_font_size (cr, font->font_size); 142 143 if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer))) { 144 cairo_font_extents_t font_extents; 145 cairo_font_extents (cr, &font_extents); 146 double baseline = (font->font_size - font_extents.height) * .5 + font_extents.ascent; 147 cairo_translate (cr, 0, baseline); 148 } else { 149 cairo_translate (cr, font->font_size * .5, 0); 150 } 151 152 cairo_glyph_t *cairo_glyphs = cairo_glyph_allocate (len); 153 current_x = 0; 154 current_y = 0; 155 156 cairo_matrix_t matrix; 157 cairo_get_matrix (cr, &matrix); 158 cairo_matrix_invert (&matrix); 159 160 for (unsigned int i = 0; i < len; i++) { 161 cairo_glyphs[i].index = info[i].codepoint; 162 cairo_glyphs[i].x = current_x + pos[i].x_offset / 64.; 163 cairo_glyphs[i].y = -(current_y + pos[i].y_offset / 64.); 164 165 double dx = pos[i].x_advance / 64.0; 166 double dy = pos[i].y_advance / 64.0; 167 168 cairo_matrix_transform_distance (&matrix, &dx, &dy); 169 170 current_x += dx; 171 current_y += dy; 172 } 173 174 cairo_show_glyphs (cr, cairo_glyphs, len); 175 cairo_glyph_free (cairo_glyphs); 176 177 cairo_font_face_destroy (cairo_face); 178 179 hb_buffer_destroy (hb_buffer); 180 cairo_restore (cr); 181 } 182 183 gchar* svg_bird_find_font_file (const gchar* font_name) { 184 const FcChar8* name; 185 FcPattern* search_pattern; 186 FcPattern* font; 187 FcChar8* file; 188 gchar* path; 189 FcObjectSet* font_properties; 190 FcFontSet* fonts; 191 int i; 192 193 if (!svg_bird_has_font_config ()) { 194 g_warning("Font config not loaded."); 195 return NULL; 196 } 197 198 g_mutex_lock (&font_config_lock); 199 200 if (font_config == NULL) { 201 g_warning("Font config not loaded."); 202 return NULL; 203 } 204 205 path = NULL; 206 name = font_name; 207 208 // match any font as fallback 209 search_pattern = FcPatternCreate (); 210 font_properties = FcObjectSetBuild (FC_FILE, NULL); 211 212 fonts = FcFontList (font_config, search_pattern, font_properties); 213 214 if (fonts->nfont > 0) { 215 for (i = 0; i < fonts->nfont; i++) { 216 font = fonts->fonts[i]; 217 218 if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) { 219 path = g_strdup ((gchar*) file); 220 break; 221 } 222 223 FcPatternDestroy (font); 224 } 225 } 226 227 FcPatternDestroy (search_pattern); 228 229 // search for a family name 230 search_pattern = FcPatternCreate (); 231 FcPatternAddString (search_pattern, FC_FAMILY, name); 232 fonts = FcFontList (font_config, search_pattern, font_properties); 233 234 if (fonts->nfont > 0) { 235 for (i = 0; i < fonts->nfont; i++) { 236 font = fonts->fonts[i]; 237 238 if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) { 239 g_free (path); 240 path = g_strdup ((gchar*) file); 241 break; 242 } 243 244 FcPatternDestroy (font); 245 } 246 } 247 248 FcPatternDestroy (search_pattern); 249 250 g_mutex_unlock (&font_config_lock); 251 return path; 252 } 253