The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

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