move around - flatter.
[profile/ivi/evas.git] / src / lib / canvas / evas_font_dir.c
1 #include "evas_common.h"
2 #include "evas_private.h"
3 #ifdef BUILD_FONT_LOADER_EET
4 #include <Eet.h>
5 #endif
6 #ifdef HAVE_FONTCONFIG
7 #include <fontconfig/fontconfig.h>
8 #endif
9
10 /* font dir cache */
11 static Evas_Hash *font_dirs = NULL;
12 static Evas_List *fonts_cache = NULL;
13 static Evas_List *fonts_zero = NULL;
14
15 typedef struct _Fndat Fndat;
16
17 struct _Fndat
18 {
19    const char *name;
20    const char *source;
21    int         size;
22    void       *font;
23    int         ref;
24 };
25
26 /* private methods for font dir cache */
27 static Evas_Bool font_cache_dir_free(const Evas_Hash *hash, const char *key, void *data, void *fdata);
28 static Evas_Font_Dir *object_text_font_cache_dir_update(char *dir, Evas_Font_Dir *fd);
29 static Evas_Font *object_text_font_cache_font_find_x(Evas_Font_Dir *fd, char *font);
30 static Evas_Font *object_text_font_cache_font_find_file(Evas_Font_Dir *fd, char *font);
31 static Evas_Font *object_text_font_cache_font_find_alias(Evas_Font_Dir *fd, char *font);
32 static Evas_Font *object_text_font_cache_font_find(Evas_Font_Dir *fd, char *font);
33 static Evas_Font_Dir *object_text_font_cache_dir_add(char *dir);
34 static void object_text_font_cache_dir_del(char *dir, Evas_Font_Dir *fd);
35 static int evas_object_text_font_string_parse(char *buffer, char dest[14][256]);
36
37 void
38 evas_font_dir_cache_free(void)
39 {
40    if (!font_dirs) return;
41
42    evas_hash_foreach(font_dirs, font_cache_dir_free, NULL);
43    evas_hash_free(font_dirs);
44    font_dirs = NULL;
45 }
46
47 const char *
48 evas_font_dir_cache_find(char *dir, char *font)
49 {
50    Evas_Font_Dir *fd;
51
52    fd = evas_hash_find(font_dirs, dir);
53    fd = object_text_font_cache_dir_update(dir, fd);
54    if (fd)
55      {
56         Evas_Font *fn;
57
58         fn = object_text_font_cache_font_find(fd, font);
59         if (fn)
60           {
61              return fn->path;
62           }
63      }
64    return NULL;
65 }
66
67 static Evas_List *
68 evas_font_set_get(const char *name)
69 {
70    Evas_List *fonts = NULL;
71    char *p;
72
73    p = strchr(name, ',');
74    if (!p)
75      {
76         fonts = evas_list_append(fonts, evas_stringshare_add(name));
77      }
78    else
79      {
80         const char *pp;
81         char *nm;
82
83         pp = name;
84         while (p)
85           {
86              nm = alloca(p - pp + 1);
87              strncpy(nm, pp, p - pp);
88              nm[p - pp] = 0;
89              fonts = evas_list_append(fonts, evas_stringshare_add(nm));
90              pp = p + 1;
91              p = strchr(pp, ',');
92              if (!p) fonts = evas_list_append(fonts, evas_stringshare_add(pp));
93           }
94      }
95    return fonts;
96 }
97
98 void
99 evas_font_free(Evas *evas, void *font)
100 {
101    Evas_List *l;
102
103    for (l = fonts_cache; l; l = l->next)
104      {
105         Fndat *fd;
106
107         fd = l->data;
108         if (fd->font == font)
109           {
110              fd->ref--;
111              if (fd->ref == 0)
112                {
113                   fonts_cache = evas_list_remove_list(fonts_cache, l);
114                   fonts_zero = evas_list_append(fonts_zero, fd);
115                }
116              break;
117           }
118      }
119    while ((fonts_zero) &&
120           (evas_list_count(fonts_zero) > 4)) /* 4 is arbitrary */
121      {
122         Fndat *fd;
123
124         fd = evas_list_data(fonts_zero);
125         if (fd->ref != 0) break;
126         fonts_zero = evas_list_remove_list(fonts_zero, fonts_zero);
127         if (fd->name) evas_stringshare_del(fd->name);
128         if (fd->source) evas_stringshare_del(fd->source);
129         evas->engine.func->font_free(evas->engine.data.output, fd->font);
130         free(fd);
131      }
132 }
133
134 void *
135 evas_font_load(Evas *evas, const char *name, const char *source, int size)
136 {
137    void *font = NULL;
138    Evas_List *fonts, *l;
139    Fndat *fd;
140
141    if (!name) return NULL;
142    if (name[0] == 0) return NULL;
143
144    for (l = fonts_cache; l; l = l->next)
145      {
146         fd = l->data;
147         if (!strcmp(name, fd->name))
148           {
149              if (((!source) && (!fd->source)) ||
150                  ((source) && (fd->source) && (!strcmp(source, fd->source))))
151                {
152                   if (size == fd->size)
153                     {
154                        fonts_cache = evas_list_promote_list(fonts_cache, l);
155                        fd->ref++;
156                        return fd->font;
157                     }
158                }
159           }
160      }
161
162    for (l = fonts_zero; l; l = l->next)
163      {
164         fd = l->data;
165         if (!strcmp(name, fd->name))
166           {
167              if (((!source) && (!fd->source)) ||
168                  ((source) && (fd->source) && (!strcmp(source, fd->source))))
169                {
170                   if (size == fd->size)
171                     {
172                        fonts_zero = evas_list_remove_list(fonts_zero, l);
173                        fonts_cache = evas_list_prepend(fonts_cache, fd);
174                        fd->ref++;
175                        return fd->font;
176                     }
177                }
178           }
179      }
180    fonts = evas_font_set_get(name);
181    for (l = fonts; l; l = l->next) /* Load each font in append */
182      {
183         char *nm;
184
185         nm = l->data;
186         if ((l == fonts) || (!font)) /* First iteration OR no font */
187           {
188 #ifdef BUILD_FONT_LOADER_EET
189              if (source) /* Load Font from "eet" source */
190                {
191                   Eet_File *ef;
192                   char *fake_name;
193
194                   fake_name = evas_file_path_join(source, nm);
195                   if (fake_name)
196                     {
197                        font = evas->engine.func->font_load(evas->engine.data.output, fake_name, size);
198                        if (!font) /* Load from fake name failed, probably not cached */
199                          {
200                             /* read original!!! */
201                             ef = eet_open(source, EET_FILE_MODE_READ);
202                             if (ef)
203                               {
204                                  void *fdata;
205                                  int fsize = 0;
206
207                                  fdata = eet_read(ef, nm, &fsize);
208                                  if ((fdata) && (fsize > 0))
209                                    {
210                                       font = evas->engine.func->font_memory_load(evas->engine.data.output, fake_name, size, fdata, fsize);
211                                       free(fdata);
212                                    }
213                                  eet_close(ef);
214                               }
215                          }
216                        free(fake_name);
217                     }
218                }
219              if (!font) /* Source load failed */
220                {
221 #endif
222                   if (evas_file_path_is_full_path((char *)nm)) /* Try filename */
223                     font = evas->engine.func->font_load(evas->engine.data.output, (char *)nm, size);
224                   else /* search font path */
225                     {
226                        Evas_List *l;
227
228                        for (l = evas->font_path; l; l = l->next)
229                          {
230                             const char *f_file;
231
232                             f_file = evas_font_dir_cache_find(l->data, (char *)nm);
233                             if (f_file)
234                               {
235                                  font = evas->engine.func->font_load(evas->engine.data.output, f_file, size);
236                                  if (font) break;
237                               }
238                          }
239                     }
240 #ifdef BUILD_FONT_LOADER_EET
241                }
242 #endif
243           }
244         else /* Base font loaded, append others */
245           {
246 #ifdef BUILD_FONT_LOADER_EET
247              void *ok = NULL;
248
249              if (source)
250                {
251                   Eet_File *ef;
252                   char *fake_name;
253
254                   fake_name = evas_file_path_join(source, nm);
255                   if (fake_name)
256                     {
257                        /* FIXME: make an engine func */
258                        if (!evas->engine.func->font_add(evas->engine.data.output, font, fake_name, size))
259                          {
260                             /* read original!!! */
261                             ef = eet_open(source, EET_FILE_MODE_READ);
262                             if (ef)
263                               {
264                                  void *fdata;
265                                  int fsize = 0;
266
267                                  fdata = eet_read(ef, nm, &fsize);
268                                  if ((fdata) && (fsize > 0))
269                                    {
270                                       ok = evas->engine.func->font_memory_add(evas->engine.data.output, font, fake_name, size, fdata, fsize);
271                                       free(fdata);
272                                    }
273                                  eet_close(ef);
274                               }
275                          }
276                        else
277                          ok = (void *)1;
278                        free(fake_name);
279                     }
280                }
281              if (!ok)
282                {
283 #endif
284                   if (evas_file_path_is_full_path((char *)nm))
285                     evas->engine.func->font_add(evas->engine.data.output, font, (char *)nm, size);
286                   else
287                     {
288                        Evas_List *l;
289
290                        for (l = evas->font_path; l; l = l->next)
291                          {
292                             const char *f_file;
293
294                             f_file = evas_font_dir_cache_find(l->data, (char *)nm);
295                             if (f_file)
296                               {
297                                  if (evas->engine.func->font_add(evas->engine.data.output, font, f_file, size))
298                                    break;
299                               }
300                          }
301                     }
302 #ifdef BUILD_FONT_LOADER_EET
303                }
304 #endif
305           }
306         evas_stringshare_del(nm);
307      }
308    evas_list_free(fonts);
309
310 #ifdef HAVE_FONTCONFIG
311
312    if (!font) /* Search using fontconfig */
313      {
314         FcPattern *p_nm = NULL;
315         FcFontSet *set;
316         FcResult res;
317         int i;
318
319         p_nm = FcNameParse((FcChar8 *)name);
320         FcConfigSubstitute(NULL, p_nm, FcMatchPattern);
321         FcDefaultSubstitute(p_nm);
322
323         /* do matching */
324         set = FcFontSort(NULL, p_nm, FcTrue, NULL, &res);
325
326         /* Do loading for all in family */
327         for (i = 0; i < set->nfont; i++)
328           {
329              FcValue filename;
330
331              FcPatternGet(set->fonts[i], FC_FILE, 0, &filename);
332
333              if (font)
334                evas->engine.func->font_add(evas->engine.data.output, font, (char *)filename.u.s, size);           
335              else         
336                font = evas->engine.func->font_load(evas->engine.data.output, (char *)filename.u.s, size);
337           }
338
339         FcFontSetDestroy(set);
340         FcPatternDestroy(p_nm);
341      }
342 #endif
343
344    fd = calloc(1, sizeof(Fndat));
345    if (fd)
346      {
347         fd->name = evas_stringshare_add(name);
348         if (source) fd->source = evas_stringshare_add(source);
349         fd->size = size;
350         fd->font = font;
351         fd->ref = 1;
352         fonts_cache = evas_list_prepend(fonts_cache, fd);
353      }
354
355    if (font)
356      evas->engine.func->font_hinting_set(evas->engine.data.output, font,
357                                          evas->hinting);
358    return font;
359 }
360
361 void
362 evas_font_load_hinting_set(Evas *evas, void *font, int hinting)
363 {
364    evas->engine.func->font_hinting_set(evas->engine.data.output, font,
365                                        hinting);
366 }
367
368 Evas_List *
369 evas_font_dir_available_list(const Evas *evas)
370 {
371    Evas_List *l;
372    Evas_List *ll;
373    Evas_List *available = NULL;
374
375 #ifdef HAVE_FONTCONFIG
376    /* Add font config fonts */
377    FcPattern *p;
378    FcFontSet *set = NULL;
379    FcObjectSet *os;
380    int i;
381
382    p = FcPatternCreate();
383    os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, NULL);
384
385    if (p && os) set = FcFontList(NULL, p, os);
386
387    if (p) FcPatternDestroy(p);
388    if (os) FcObjectSetDestroy(os);
389
390    if (set)
391      {
392         for (i = 0; i < set->nfont; i++)
393           {
394              char *font;
395
396              font = (char *)FcNameUnparse(set->fonts[i]);
397              available = evas_list_append(available, evas_stringshare_add(font));
398              free(font);
399           }
400
401         FcFontSetDestroy(set);
402      }
403 #endif
404
405    /* Add fonts in evas font_path*/
406    if (!evas->font_path)
407      return available;
408
409    for (l = evas->font_path; l; l = l->next)
410      {
411         Evas_Font_Dir *fd;
412
413         fd = evas_hash_find(font_dirs, (char *)l->data);
414         fd = object_text_font_cache_dir_update((char *)l->data, fd);
415         if (fd && fd->aliases)
416           {
417              for (ll = fd->aliases; ll; ll = ll->next)
418                {
419                   Evas_Font_Alias *fa;
420
421                   fa = ll->data;
422                   available = evas_list_append(available, evas_stringshare_add((char *)fa->alias));
423                }
424           }
425      }
426
427    return available;
428 }
429
430 void
431 evas_font_dir_available_list_free(Evas_List *available)
432 {
433    while (available)
434      {
435         evas_stringshare_del(available->data);
436         available = evas_list_remove(available, available->data);
437      }
438 }
439
440 /* private stuff */
441 static Evas_Bool
442 font_cache_dir_free(const Evas_Hash *hash, const char *key, void *data, void *fdata)
443 {
444    object_text_font_cache_dir_del((char *) key, data);
445    return 1;
446 }
447
448 static Evas_Font_Dir *
449 object_text_font_cache_dir_update(char *dir, Evas_Font_Dir *fd)
450 {
451    DATA64 mt;
452    char *tmp;
453
454    if (fd)
455      {
456         mt = evas_file_modified_time(dir);
457         if (mt != fd->dir_mod_time)
458           {
459              object_text_font_cache_dir_del(dir, fd);
460              font_dirs = evas_hash_del(font_dirs, dir, fd);
461           }
462         else
463           {
464              tmp = evas_file_path_join(dir, "fonts.dir");
465              if (tmp)
466                {
467                   mt = evas_file_modified_time(tmp);
468                   free(tmp);
469                   if (mt != fd->fonts_dir_mod_time)
470                     {
471                        object_text_font_cache_dir_del(dir, fd);
472                        font_dirs = evas_hash_del(font_dirs, dir, fd);
473                     }
474                   else
475                     {
476                        tmp = evas_file_path_join(dir, "fonts.alias");
477                        if (tmp)
478                          {
479                             mt = evas_file_modified_time(tmp);
480                             free(tmp);
481                          }
482                        if (mt != fd->fonts_alias_mod_time)
483                          {
484                             object_text_font_cache_dir_del(dir, fd);
485                             font_dirs = evas_hash_del(font_dirs, dir, fd);
486                          }
487                        else
488                          return fd;
489                     }
490                }
491           }
492      }
493    return object_text_font_cache_dir_add(dir);
494 }
495
496 static Evas_Font *
497 object_text_font_cache_font_find_x(Evas_Font_Dir *fd, char *font)
498 {
499    Evas_List *l;
500    char font_prop[14][256];
501    int num;
502
503    num = evas_object_text_font_string_parse(font, font_prop);
504    if (num != 14) return NULL;
505    for (l = fd->fonts; l; l = l->next)
506      {
507         Evas_Font *fn;
508
509         fn = l->data;
510         if (fn->type == 1)
511           {
512              int i;
513              int match = 0;
514
515              for (i = 0; i < 14; i++)
516                {
517                   if ((font_prop[i][0] == '*') && (font_prop[i][1] == 0))
518                     match++;
519                   else
520                     {
521                        if (!strcasecmp(font_prop[i], fn->x.prop[i])) match++;
522                        else break;
523                     }
524                }
525              if (match == 14) return fn;
526           }
527      }
528    return NULL;
529 }
530
531 static Evas_Font *
532 object_text_font_cache_font_find_file(Evas_Font_Dir *fd, char *font)
533 {
534    Evas_List *l;
535
536    for (l = fd->fonts; l; l = l->next)
537      {
538         Evas_Font *fn;
539
540         fn = l->data;
541         if (fn->type == 0)
542           {
543              if (!strcasecmp(font, fn->simple.name)) return fn;
544           }
545      }
546    return NULL;
547 }
548
549 static Evas_Font *
550 object_text_font_cache_font_find_alias(Evas_Font_Dir *fd, char *font)
551 {
552    Evas_List *l;
553
554    for (l = fd->aliases; l; l = l->next)
555      {
556         Evas_Font_Alias *fa;
557
558         fa = l->data;
559         if (!strcasecmp(fa->alias, font)) return fa->fn;
560      }
561    return NULL;
562 }
563
564 static Evas_Font *
565 object_text_font_cache_font_find(Evas_Font_Dir *fd, char *font)
566 {
567    Evas_Font *fn;
568
569    fn = evas_hash_find(fd->lookup, font);
570    if (fn) return fn;
571    fn = object_text_font_cache_font_find_alias(fd, font);
572    if (!fn) fn = object_text_font_cache_font_find_x(fd, font);
573    if (!fn) fn = object_text_font_cache_font_find_file(fd, font);
574    if (!fn) return NULL;
575    fd->lookup = evas_hash_add(fd->lookup, font, fn);
576    return fn;
577 }
578
579 static Evas_Font_Dir *
580 object_text_font_cache_dir_add(char *dir)
581 {
582    Evas_Font_Dir *fd;
583    char *tmp, *tmp2;
584    Evas_List *fdir;
585
586    fd = calloc(1, sizeof(Evas_Font_Dir));
587    if (!fd) return NULL;
588    font_dirs = evas_hash_add(font_dirs, dir, fd);
589
590    /* READ fonts.alias, fonts.dir and directory listing */
591
592    /* fonts.dir */
593    tmp = evas_file_path_join(dir, "fonts.dir");
594    if (tmp)
595      {
596         FILE *f;
597
598         f = fopen(tmp, "r");
599         if (f)
600           {
601              int num;
602              char fname[4096], fdef[4096];
603
604              if (fscanf(f, "%i\n", &num) != 1) goto cant_read;
605              /* read font lines */
606              while (fscanf(f, "%4090s %[^\n]\n", fname, fdef) == 2)
607                {
608                   char font_prop[14][256];
609                   int i;
610
611                   /* skip comments */
612                   if ((fdef[0] == '!') || (fdef[0] == '#')) continue;
613                   /* parse font def */
614                   num = evas_object_text_font_string_parse((char *)fdef, font_prop);
615                   if (num == 14)
616                     {
617                        Evas_Font *fn;
618
619                        fn = calloc(1, sizeof(Evas_Font));
620                        if (fn)
621                          {
622                             fn->type = 1;
623                             for (i = 0; i < 14; i++)
624                               fn->x.prop[i] = evas_stringshare_add(font_prop[i]);
625                             tmp2 = evas_file_path_join(dir, fname);
626                             if (tmp2)
627                               {
628                                  fn->path = evas_stringshare_add(tmp2);
629                                  free(tmp2);
630                               }
631                             fd->fonts = evas_list_append(fd->fonts, fn);
632                          }
633                     }
634                }
635              cant_read: ;
636              fclose(f);
637           }
638         free(tmp);
639      }
640
641    /* directoy listing */
642    fdir = evas_file_path_list(dir, "*.ttf", 0);
643    while (fdir)
644      {
645         tmp = evas_file_path_join(dir, fdir->data);
646         if (tmp)
647           {
648              Evas_Font *fn;
649
650              fn = calloc(1, sizeof(Evas_Font));
651              if (fn)
652                {
653                   char *p;
654
655                   fn->type = 0;
656                   tmp2 = alloca(strlen(fdir->data) + 1);
657                   strcpy(tmp2, fdir->data);
658                   p = strrchr(tmp2, '.');
659                   if (p) *p = 0;
660                   fn->simple.name = evas_stringshare_add(tmp2);
661                   tmp2 = evas_file_path_join(dir, fdir->data);
662                   if (tmp2)
663                     {
664                        fn->path = evas_stringshare_add(tmp2);
665                        free(tmp2);
666                     }
667                   fd->fonts = evas_list_append(fd->fonts, fn);
668                }
669              free(tmp);
670           }
671         free(fdir->data);
672         fdir = evas_list_remove(fdir, fdir->data);
673      }
674
675    /* fonts.alias */
676    tmp = evas_file_path_join(dir, "fonts.alias");
677    if (tmp)
678      {
679         FILE *f;
680
681         f = fopen(tmp, "r");
682         if (f)
683           {
684              char fname[4096], fdef[4096];
685
686              /* read font alias lines */
687              while (fscanf(f, "%4090s %[^\n]\n", fname, fdef) == 2)
688                {
689                   Evas_Font_Alias *fa;
690
691                   /* skip comments */
692                   if ((fname[0] == '!') || (fname[0] == '#')) continue;
693                   fa = calloc(1, sizeof(Evas_Font_Alias));
694                   if (fa)
695                     {
696                        fa->alias = evas_stringshare_add(fname);
697                        fa->fn = object_text_font_cache_font_find_x(fd, fdef);
698                        if ((!fa->alias) || (!fa->fn))
699                          {
700                             if (fa->alias) evas_stringshare_del(fa->alias);
701                             free(fa);
702                          }
703                        else
704                          fd->aliases = evas_list_append(fd->aliases, fa);
705                     }
706                }
707              fclose(f);
708           }
709         free(tmp);
710      }
711
712    fd->dir_mod_time = evas_file_modified_time(dir);
713    tmp = evas_file_path_join(dir, "fonts.dir");
714    if (tmp)
715      {
716         fd->fonts_dir_mod_time = evas_file_modified_time(tmp);
717         free(tmp);
718      }
719    tmp = evas_file_path_join(dir, "fonts.alias");
720    if (tmp)
721      {
722         fd->fonts_alias_mod_time = evas_file_modified_time(tmp);
723         free(tmp);
724      }
725
726    return fd;
727 }
728
729 static void
730 object_text_font_cache_dir_del(char *dir, Evas_Font_Dir *fd)
731 {
732    if (fd->lookup) evas_hash_free(fd->lookup);
733    while (fd->fonts)
734      {
735         Evas_Font *fn;
736         int i;
737
738         fn = fd->fonts->data;
739         fd->fonts = evas_list_remove(fd->fonts, fn);
740         for (i = 0; i < 14; i++)
741           {
742              if (fn->x.prop[i]) evas_stringshare_del(fn->x.prop[i]);
743           }
744         if (fn->simple.name) evas_stringshare_del(fn->simple.name);
745         if (fn->path) evas_stringshare_del(fn->path);
746         free(fn);
747      }
748    while (fd->aliases)
749      {
750         Evas_Font_Alias *fa;
751
752         fa = fd->aliases->data;
753         fd->aliases = evas_list_remove(fd->aliases, fa);
754         if (fa->alias) evas_stringshare_del(fa->alias);
755         free(fa);
756      }
757    free(fd);
758 }
759
760 static int
761 evas_object_text_font_string_parse(char *buffer, char dest[14][256])
762 {
763    char *p;
764    int n, m, i;
765
766    n = 0;
767    m = 0;
768    p = buffer;
769    if (p[0] != '-') return 0;
770    i = 1;
771    while (p[i])
772      {
773         dest[n][m] = p[i];
774         if ((p[i] == '-') || (m == 256))
775           {
776              dest[n][m] = 0;
777              n++;
778              m = -1;
779           }
780         i++;
781         m++;
782         if (n == 14) return n;
783      }
784    dest[n][m] = 0;
785    n++;
786    return n;
787 }