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