1 #include "config.h"
2
3 #include <ft2build.h>
4 #include FT_FREETYPE_H
5 #include FT_GLYPH_H
6 #include <math.h>
7 #include <string.h>
8 #include <sys/types.h>
9
10 #include "blend.h"
11 #include "colormod.h"
12 #include "common.h"
13 #include "file.h"
14 #include "font.h"
15 #include "image.h"
16 #include "rgbadraw.h"
17 #include "rotate.h"
18
19 extern FT_Library ft_lib;
20
21 static int font_cache_usage = 0;
22 static int font_cache = 0;
23 static char **fpath = NULL;
24 static int fpath_num = 0;
25 static Imlib_Object_List *fonts = NULL;
26
27 static ImlibFont *__imlib_font_load(const char *name, int faceidx, int size);
28 static int font_modify_cache_cb(Imlib_Hash * hash, const char *key,
29 void *data, void *fdata);
30 static int font_flush_free_glyph_cb(Imlib_Hash * hash, const char *key,
31 void *data, void *fdata);
32
33 /* FIXME now! listdir() from evas_object_text.c */
34
35 /* separate fontname and size, find font file, start __imlib_font_load() then */
36 ImlibFont *
__imlib_font_load_joined(const char * fontname)37 __imlib_font_load_joined(const char *fontname)
38 {
39 int j, k, size, faceidx, namelen;
40 char *name, *file = NULL, *tmp;
41 ImlibFont *fn;
42
43 /* split font name (in format name[:faceidx]/size) */
44 for (j = strlen(fontname) - 1; (j >= 0) && (fontname[j] != '/'); j--)
45 ;
46
47 /* no "/" in font after the first char */
48 if (j <= 0)
49 return NULL;
50
51 /* get size */
52 size = atoi(&(fontname[j + 1]));
53
54 /* split font faceidx index (in format name[:faceidx]/size) */
55 faceidx = 0;
56 for (k = j - 1; k > 0; k--)
57 {
58 if (fontname[k] >= '0' && fontname[k] <= '9')
59 continue;
60 if (fontname[k] != ':')
61 break;
62 faceidx = atoi(&(fontname[k + 1]));
63 if (faceidx < 0)
64 faceidx = 0;
65 j = k;
66 break;
67 }
68
69 namelen = j;
70 /* split name in front off */
71 name = malloc(namelen + 1);
72 memcpy(name, fontname, namelen);
73 name[namelen] = 0;
74
75 /* find file if it exists */
76 tmp = malloc(namelen + 4 + 1);
77 if (!tmp)
78 goto done;
79
80 sprintf(tmp, "%s.ttf", name);
81 if (__imlib_FileIsFile(tmp))
82 file = strdup(tmp);
83 else
84 {
85 sprintf(tmp, "%s.TTF", name);
86 if (__imlib_FileIsFile(tmp))
87 file = strdup(tmp);
88 else
89 {
90 sprintf(tmp, "%s", name);
91 if (__imlib_FileIsFile(tmp))
92 file = strdup(tmp);
93 }
94 }
95 free(tmp);
96
97 if (!file)
98 {
99 for (j = 0; (j < fpath_num) && (!file); j++)
100 {
101 tmp = malloc(strlen(fpath[j]) + 1 + namelen + 4 + 1);
102 if (!tmp)
103 goto done;
104
105 sprintf(tmp, "%s/%s.ttf", fpath[j], name);
106 if (__imlib_FileIsFile(tmp))
107 file = strdup(tmp);
108 else
109 {
110 sprintf(tmp, "%s/%s.TTF", fpath[j], name);
111 if (__imlib_FileIsFile(tmp))
112 file = strdup(tmp);
113 else
114 {
115 sprintf(tmp, "%s/%s", fpath[j], name);
116 if (__imlib_FileIsFile(tmp))
117 file = strdup(tmp);
118 }
119 }
120 free(tmp);
121 }
122 }
123
124 done:
125 free(name);
126
127 /* didn't find a file? abort */
128 if (!file)
129 return NULL;
130
131 fn = __imlib_font_load(file, faceidx, size);
132 free(file);
133 return fn;
134 }
135
136 static ImlibFont *
__imlib_font_load(const char * name,int faceidx,int size)137 __imlib_font_load(const char *name, int faceidx, int size)
138 {
139 int error;
140 ImlibFont *fn;
141 char *file;
142
143 fn = __imlib_font_find(name, size);
144 if (fn)
145 return fn;
146
147 __imlib_font_init();
148
149 fn = malloc(sizeof(ImlibFont));
150 file = (char *)name;
151
152 error = FT_New_Face(ft_lib, file, faceidx, &(fn->ft.face));
153 if (error)
154 {
155 free(fn);
156 return NULL;
157 }
158 error = FT_Set_Char_Size(fn->ft.face, 0, (size * 64), 96, 96);
159 if (error)
160 error = FT_Set_Pixel_Sizes(fn->ft.face, 0, size);
161 if (error)
162 {
163 int i;
164 int chosen_size = 0;
165 int chosen_width = 0;
166
167 for (i = 0; i < fn->ft.face->num_fixed_sizes; i++)
168 {
169 int s;
170 int d, cd;
171
172 s = fn->ft.face->available_sizes[i].height;
173 cd = chosen_size - size;
174 if (cd < 0)
175 cd = -cd;
176 d = s - size;
177 if (d < 0)
178 d = -d;
179 if (d < cd)
180 {
181 chosen_width = fn->ft.face->available_sizes[i].width;
182 chosen_size = s;
183 }
184 if (d == 0)
185 break;
186 }
187 error = FT_Set_Pixel_Sizes(fn->ft.face, chosen_width, chosen_size);
188 if (error)
189 {
190 /* couldn't choose the size anyway... what now? */
191 }
192 }
193
194 error = FT_Select_Charmap(fn->ft.face, ft_encoding_unicode);
195 if (error)
196 {
197 }
198
199 fn->file = strdup(file);
200 fn->name = strdup(file);
201 fn->size = size;
202
203 fn->glyphs = NULL;
204
205 fn->usage = 0;
206
207 fn->references = 1;
208
209 fn->fallback_prev = NULL;
210 fn->fallback_next = NULL;
211
212 fonts = __imlib_object_list_prepend(fonts, fn);
213 return fn;
214 }
215
216 void
__imlib_font_free(ImlibFont * fn)217 __imlib_font_free(ImlibFont * fn)
218 {
219 fn->references--;
220 if (fn->references == 0)
221 {
222 __imlib_font_modify_cache_by(fn, 1);
223 __imlib_font_flush();
224 }
225 }
226
227 int
__imlib_font_insert_into_fallback_chain_imp(ImlibFont * fn,ImlibFont * fallback)228 __imlib_font_insert_into_fallback_chain_imp(ImlibFont * fn,
229 ImlibFont * fallback)
230 {
231 /* avoid infinite recursion */
232 if (fn == fallback)
233 return 1;
234
235 /* now remove the given fallback font from any chain it's already in */
236 __imlib_font_remove_from_fallback_chain_imp(fallback);
237
238 /* insert fallback into fn's font chain */
239 ImlibFont *tmp = fn->fallback_next;
240
241 fn->fallback_next = fallback;
242 fallback->fallback_prev = fn;
243 fallback->fallback_next = tmp;
244 if (tmp)
245 tmp->fallback_prev = fallback;
246 return 0;
247 }
248
249 void
__imlib_font_remove_from_fallback_chain_imp(ImlibFont * fn)250 __imlib_font_remove_from_fallback_chain_imp(ImlibFont * fn)
251 {
252 /* if fn has a previous font in its font chain, then make its fallback_next fn's fallback_next since fn is going away */
253 if (fn->fallback_prev)
254 fn->fallback_prev->fallback_next = fn->fallback_next;
255 fn->fallback_prev = NULL;
256 fn->fallback_next = NULL;
257 }
258
259 static int
font_modify_cache_cb(Imlib_Hash * hash,const char * key,void * data,void * fdata)260 font_modify_cache_cb(Imlib_Hash * hash, const char *key, void *data,
261 void *fdata)
262 {
263 int *dir;
264 Imlib_Font_Glyph *fg;
265
266 fg = data;
267 dir = fdata;
268 font_cache_usage += (*dir) * ((fg->glyph_out->bitmap.width * fg->glyph_out->bitmap.rows) + sizeof(Imlib_Font_Glyph) + sizeof(Imlib_Object_List) + 400); /* fudge values */
269 return 1;
270 }
271
272 void
__imlib_font_modify_cache_by(ImlibFont * fn,int dir)273 __imlib_font_modify_cache_by(ImlibFont * fn, int dir)
274 {
275 int sz_name = 0, sz_file = 0, sz_hash = 0;
276
277 if (fn->name)
278 sz_name = strlen(fn->name);
279 if (fn->file)
280 sz_file = strlen(fn->file);
281 if (fn->glyphs)
282 sz_hash = sizeof(Imlib_Hash);
283 __imlib_hash_foreach(fn->glyphs, font_modify_cache_cb, &dir);
284 font_cache_usage += dir * (sizeof(ImlibFont) + sz_name + sz_file + sz_hash + sizeof(FT_FaceRec) + 16384); /* fudge values */
285 }
286
287 int
__imlib_font_cache_get(void)288 __imlib_font_cache_get(void)
289 {
290 return font_cache;
291 }
292
293 void
__imlib_font_cache_set(int size)294 __imlib_font_cache_set(int size)
295 {
296 font_cache = size;
297 __imlib_font_flush();
298 }
299
300 void
__imlib_font_flush(void)301 __imlib_font_flush(void)
302 {
303 if (font_cache_usage < font_cache)
304 return;
305 while (font_cache_usage > font_cache)
306 __imlib_font_flush_last();
307 }
308
309 static int
font_flush_free_glyph_cb(Imlib_Hash * hash,const char * key,void * data,void * fdata)310 font_flush_free_glyph_cb(Imlib_Hash * hash, const char *key, void *data,
311 void *fdata)
312 {
313 Imlib_Font_Glyph *fg;
314
315 fg = data;
316 FT_Done_Glyph(fg->glyph);
317 free(fg);
318 return 1;
319 }
320
321 void
__imlib_font_flush_last(void)322 __imlib_font_flush_last(void)
323 {
324 Imlib_Object_List *l;
325 ImlibFont *fn = NULL;
326
327 for (l = fonts; l; l = l->next)
328 {
329 ImlibFont *fn_tmp;
330
331 fn_tmp = (ImlibFont *) l;
332 if (fn_tmp->references == 0)
333 fn = fn_tmp;
334 }
335 if (!fn)
336 return;
337
338 fonts = __imlib_object_list_remove(fonts, fn);
339 __imlib_font_modify_cache_by(fn, -1);
340
341 __imlib_hash_foreach(fn->glyphs, font_flush_free_glyph_cb, NULL);
342 __imlib_hash_free(fn->glyphs);
343
344 if (fn->file)
345 free(fn->file);
346 if (fn->name)
347 free(fn->name);
348 FT_Done_Face(fn->ft.face);
349 free(fn);
350 }
351
352 ImlibFont *
__imlib_font_find(const char * name,int size)353 __imlib_font_find(const char *name, int size)
354 {
355 Imlib_Object_List *l;
356
357 for (l = fonts; l; l = l->next)
358 {
359 ImlibFont *fn;
360
361 fn = (ImlibFont *) l;
362 if ((fn->size == size) && (!strcmp(name, fn->name)))
363 {
364 if (fn->references == 0)
365 __imlib_font_modify_cache_by(fn, -1);
366 fn->references++;
367 fonts = __imlib_object_list_remove(fonts, fn);
368 fonts = __imlib_object_list_prepend(fonts, fn);
369 return fn;
370 }
371 }
372 return NULL;
373 }
374
375 /* font pathes */
376 void
__imlib_font_add_font_path(const char * path)377 __imlib_font_add_font_path(const char *path)
378 {
379 fpath_num++;
380 if (!fpath)
381 fpath = malloc(sizeof(char *));
382 else
383 fpath = realloc(fpath, (fpath_num * sizeof(char *)));
384 fpath[fpath_num - 1] = strdup(path);
385 }
386
387 void
__imlib_font_del_font_path(const char * path)388 __imlib_font_del_font_path(const char *path)
389 {
390 int i, j;
391
392 for (i = 0; i < fpath_num; i++)
393 {
394 if (!strcmp(path, fpath[i]))
395 {
396 if (fpath[i])
397 free(fpath[i]);
398 fpath_num--;
399 for (j = i; j < fpath_num; j++)
400 fpath[j] = fpath[j + 1];
401 if (fpath_num > 0)
402 fpath = realloc(fpath, fpath_num * sizeof(char *));
403 else
404 {
405 free(fpath);
406 fpath = NULL;
407 }
408 }
409 }
410 }
411
412 int
__imlib_font_path_exists(const char * path)413 __imlib_font_path_exists(const char *path)
414 {
415 int i;
416
417 for (i = 0; i < fpath_num; i++)
418 {
419 if (!strcmp(path, fpath[i]))
420 return 1;
421 }
422 return 0;
423 }
424
425 char **
__imlib_font_list_font_path(int * num_ret)426 __imlib_font_list_font_path(int *num_ret)
427 {
428 *num_ret = fpath_num;
429 return fpath;
430 }
431
432 /* fonts list */
433 char **
__imlib_font_list_fonts(int * num_ret)434 __imlib_font_list_fonts(int *num_ret)
435 {
436 int i, j, d, l = 0;
437 char **list = NULL, **dir, *path;
438 FT_Error error;
439 char *p;
440
441 __imlib_font_init();
442
443 for (i = 0; i < fpath_num; i++)
444 {
445 dir = __imlib_FileDir(fpath[i], &d);
446 if (dir)
447 {
448 for (j = 0; j < d; j++)
449 {
450 path = malloc(strlen(fpath[i]) + strlen(dir[j]) + 2);
451 sprintf(path, "%s/%s", fpath[i], dir[j]);
452 /* trim .ttf if it is there */
453 if ((p = strrchr(dir[j], '.')))
454 *p = '\0';
455 if (!__imlib_ItemInList(list, l, dir[j]))
456 {
457 if (__imlib_FileIsFile(path))
458 {
459 FT_Face f;
460
461 error = FT_New_Face(ft_lib, path, 0, &f);
462 if (!error)
463 {
464 FT_Done_Face(f);
465 l++;
466 if (list)
467 list = realloc(list, sizeof(char *) * l);
468 else
469 list = malloc(sizeof(char *));
470 list[l - 1] = strdup(dir[j]);
471 }
472 }
473 }
474 free(path);
475 }
476 __imlib_FileFreeDirList(dir, d);
477 }
478 }
479 *num_ret = l;
480 return list;
481 }
482