]> granicus.if.org Git - libass/blob - libass/ass_fontselect.c
fontselect: expose the freetype-provided family as well
[libass] / libass / ass_fontselect.c
1 /*
2  * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
3  * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
4  *
5  * This file is part of libass.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 #include "config.h"
21 #include "ass_compat.h"
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <assert.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <inttypes.h>
31 #include <limits.h>
32 #include <ft2build.h>
33 #include <sys/types.h>
34 #include <dirent.h>
35 #include FT_FREETYPE_H
36 #include FT_SFNT_NAMES_H
37 #include FT_TRUETYPE_IDS_H
38 #include FT_TYPE1_TABLES_H
39
40 #include "ass_utils.h"
41 #include "ass.h"
42 #include "ass_library.h"
43 #include "ass_fontselect.h"
44 #include "ass_fontconfig.h"
45 #include "ass_coretext.h"
46 #include "ass_directwrite.h"
47 #include "ass_font.h"
48 #include "ass_string.h"
49
50 #define ABS(x) ((x) < 0 ? -(x) : (x))
51 #define MAX_FULLNAME 100
52
53 // internal font database element
54 // all strings are utf-8
55 struct font_info {
56     int uid;            // unique font face id
57
58     char **families;    // family name
59     char **fullnames;   // list of localized fullnames (e.g. Arial Bold Italic)
60     int n_family;
61     int n_fullname;
62
63     int slant;
64     int weight;         // TrueType scale, 100-900
65     int width;
66
67     // how to access this face
68     char *path;            // absolute path
69     int index;             // font index inside font collections
70
71     char *postscript_name; // can be used as an alternative to index to
72                            // identify a font inside a collection
73
74     // font source
75     ASS_FontProvider *provider;
76
77     // private data for callbacks
78     void *priv;
79 };
80
81 struct font_selector {
82     // uid counter
83     int uid;
84
85     // fallbacks
86     char *family_default;
87     char *path_default;
88     int index_default;
89
90     // font database
91     int n_font;
92     int alloc_font;
93     ASS_FontInfo *font_infos;
94
95     ASS_FontProvider *default_provider;
96     ASS_FontProvider *embedded_provider;
97 };
98
99 struct font_provider {
100     ASS_FontSelector *parent;
101     ASS_FontProviderFuncs funcs;
102     void *priv;
103 };
104
105 typedef struct font_data_ft FontDataFT;
106 struct font_data_ft {
107     ASS_Library *lib;
108     FT_Face face;
109     int idx;
110 };
111
112 static bool check_postscript_ft(void *data)
113 {
114     FontDataFT *fd = (FontDataFT *)data;
115     PS_FontInfoRec postscript_info;
116     return !FT_Get_PS_Font_Info(fd->face, &postscript_info);
117 }
118
119 static bool check_glyph_ft(void *data, uint32_t codepoint)
120 {
121     FontDataFT *fd = (FontDataFT *)data;
122
123     if (!codepoint)
124         return true;
125
126     return !!FT_Get_Char_Index(fd->face, codepoint);
127 }
128
129 static void destroy_font_ft(void *data)
130 {
131     FontDataFT *fd = (FontDataFT *)data;
132
133     FT_Done_Face(fd->face);
134     free(fd);
135 }
136
137 static size_t
138 get_data_embedded(void *data, unsigned char *buf, size_t offset, size_t len)
139 {
140     FontDataFT *ft = (FontDataFT *)data;
141     ASS_Fontdata *fd = ft->lib->fontdata;
142     int i = ft->idx;
143
144     if (buf == NULL)
145         return fd[i].size;
146
147     if (offset >= fd[i].size)
148         return 0;
149
150     if (len > fd[i].size - offset)
151         len = fd[i].size - offset;
152
153     memcpy(buf, fd[i].data + offset, len);
154     return len;
155 }
156
157 static ASS_FontProviderFuncs ft_funcs = {
158     .get_data          = get_data_embedded,
159     .check_postscript  = check_postscript_ft,
160     .check_glyph       = check_glyph_ft,
161     .destroy_font      = destroy_font_ft,
162 };
163
164 static void load_fonts_from_dir(ASS_Library *library, const char *dir)
165 {
166     DIR *d = opendir(dir);
167     if (!d)
168         return;
169     while (1) {
170         struct dirent *entry = readdir(d);
171         if (!entry)
172             break;
173         if (entry->d_name[0] == '.')
174             continue;
175         char fullname[4096];
176         snprintf(fullname, sizeof(fullname), "%s/%s", dir, entry->d_name);
177         size_t bufsize = 0;
178         ass_msg(library, MSGL_INFO, "Loading font file '%s'", fullname);
179         void *data = read_file(library, fullname, &bufsize);
180         if (data) {
181             ass_add_font(library, entry->d_name, data, bufsize);
182             free(data);
183         }
184     }
185     closedir(d);
186 }
187
188 /**
189  * \brief Create a bare font provider.
190  * \param selector parent selector. The provider will be attached to it.
191  * \param funcs callback/destroy functions
192  * \param data private data of the provider
193  * \return the font provider
194  */
195 ASS_FontProvider *
196 ass_font_provider_new(ASS_FontSelector *selector, ASS_FontProviderFuncs *funcs,
197                       void *data)
198 {
199     ASS_FontProvider *provider = calloc(1, sizeof(ASS_FontProvider));
200     if (provider == NULL)
201         return NULL;
202
203     provider->parent   = selector;
204     provider->funcs    = *funcs;
205     provider->priv     = data;
206
207     return provider;
208 }
209
210 /**
211  * Free all data associated with a FontInfo struct. Handles FontInfo structs
212  * with incomplete allocations well.
213  *
214  * \param info FontInfo struct to free associated data from
215  */
216 static void ass_font_provider_free_fontinfo(ASS_FontInfo *info)
217 {
218     int j;
219
220     if (info->fullnames) {
221         for (j = 0; j < info->n_fullname; j++)
222             free(info->fullnames[j]);
223         free(info->fullnames);
224     }
225
226     if (info->families) {
227         for (j = 0; j < info->n_family; j++)
228             free(info->families[j]);
229         free(info->families);
230     }
231
232     if (info->path)
233         free(info->path);
234
235     if (info->postscript_name)
236         free(info->postscript_name);
237
238 }
239
240 /**
241  * \brief Add a font to a font provider.
242  * \param provider the font provider
243  * \param meta basic metadata of the font
244  * \param path path to the font file, or NULL
245  * \param index face index inside the file (-1 to look up by PostScript name)
246  * \param data private data for the font
247  * \return success
248  */
249 bool
250 ass_font_provider_add_font(ASS_FontProvider *provider,
251                            ASS_FontProviderMetaData *meta, const char *path,
252                            int index, void *data)
253 {
254     int i;
255     int weight, slant, width;
256     ASS_FontSelector *selector = provider->parent;
257     ASS_FontInfo *info;
258
259 #if 0
260     int j;
261     printf("new font:\n");
262     printf("  families: ");
263     for (j = 0; j < meta->n_family; j++)
264         printf("'%s' ", meta->families[j]);
265     printf("\n");
266     printf("  fullnames: ");
267     for (j = 0; j < meta->n_fullname; j++)
268         printf("'%s' ", meta->fullnames[j]);
269     printf("\n");
270     printf("  slant: %d\n", meta->slant);
271     printf("  weight: %d\n", meta->weight);
272     printf("  width: %d\n", meta->width);
273     printf("  path: %s\n", path);
274     printf("  index: %d\n", index);
275 #endif
276
277     weight = meta->weight;
278     slant  = meta->slant;
279     width  = meta->width;
280
281     // check slant/weight for validity, use defaults if they're invalid
282     if (weight < 100 || weight > 900)
283         weight = 400;
284     if (slant < 0 || slant > 110)
285         slant = 0;
286     if (width < 50 || width > 200)
287         width = 100;
288
289     // check size
290     if (selector->n_font >= selector->alloc_font) {
291         selector->alloc_font = FFMAX(1, 2 * selector->alloc_font);
292         selector->font_infos = realloc(selector->font_infos,
293                 selector->alloc_font * sizeof(ASS_FontInfo));
294     }
295
296     // copy over metadata
297     info = selector->font_infos + selector->n_font;
298     memset(info, 0, sizeof(ASS_FontInfo));
299
300     // set uid
301     info->uid = selector->uid++;
302
303     info->slant         = slant;
304     info->weight        = weight;
305     info->width         = width;
306     info->n_fullname    = meta->n_fullname;
307     info->n_family      = meta->n_family;
308
309     info->families = calloc(meta->n_family, sizeof(char *));
310     if (info->families == NULL)
311         goto error;
312
313     if (meta->n_fullname) {
314         info->fullnames = calloc(meta->n_fullname, sizeof(char *));
315         if (info->fullnames == NULL)
316             goto error;
317     }
318
319     for (i = 0; i < info->n_family; i++) {
320         info->families[i] = strdup(meta->families[i]);
321         if (info->families[i] == NULL)
322             goto error;
323     }
324
325     for (i = 0; i < info->n_fullname; i++) {
326         info->fullnames[i] = strdup(meta->fullnames[i]);
327         if (info->fullnames[i] == NULL)
328             goto error;
329     }
330
331     if (meta->postscript_name) {
332         info->postscript_name = strdup(meta->postscript_name);
333         if (info->postscript_name == NULL)
334             goto error;
335     }
336
337     if (path) {
338         info->path = strdup(path);
339         if (info->path == NULL)
340             goto error;
341     }
342
343     info->index = index;
344     info->priv  = data;
345     info->provider = provider;
346
347     selector->n_font++;
348     return true;
349
350 error:
351     ass_font_provider_free_fontinfo(info);
352
353     if (provider->funcs.destroy_font)
354         provider->funcs.destroy_font(data);
355
356     return false;
357 }
358
359 /**
360  * \brief Clean up font database. Deletes all fonts that have an invalid
361  * font provider (NULL).
362  * \param selector the font selector
363  */
364 static void ass_fontselect_cleanup(ASS_FontSelector *selector)
365 {
366     int i, w;
367
368     for (i = 0, w = 0; i < selector->n_font; i++) {
369         ASS_FontInfo *info = selector->font_infos + i;
370
371         // update write pointer
372         if (info->provider != NULL) {
373             // rewrite, if needed
374             if (w != i)
375                 memcpy(selector->font_infos + w, selector->font_infos + i,
376                         sizeof(ASS_FontInfo));
377             w++;
378         }
379
380     }
381
382     selector->n_font = w;
383 }
384
385 void ass_font_provider_free(ASS_FontProvider *provider)
386 {
387     int i;
388     ASS_FontSelector *selector = provider->parent;
389
390     // free all fonts and mark their entries
391     for (i = 0; i < selector->n_font; i++) {
392         ASS_FontInfo *info = selector->font_infos + i;
393
394         if (info->provider == provider) {
395             ass_font_provider_free_fontinfo(info);
396
397             if (info->provider->funcs.destroy_font)
398                 info->provider->funcs.destroy_font(info->priv);
399
400             info->provider = NULL;
401         }
402
403     }
404
405     // delete marked entries
406     ass_fontselect_cleanup(selector);
407
408     // free private data of the provider
409     if (provider->funcs.destroy_provider)
410         provider->funcs.destroy_provider(provider->priv);
411
412     free(provider);
413 }
414
415 static bool check_postscript(ASS_FontInfo *fi)
416 {
417     ASS_FontProvider *provider = fi->provider;
418     assert(provider && provider->funcs.check_postscript);
419
420     return provider->funcs.check_postscript(fi->priv);
421 }
422
423 /**
424  * \brief Return whether the given font is in the given family.
425  */
426 static bool matches_family_name(ASS_FontInfo *f, const char *family)
427 {
428     for (int i = 0; i < f->n_family; i++) {
429         if (ass_strcasecmp(f->families[i], family) == 0)
430             return true;
431     }
432     return false;
433 }
434
435 /**
436  * \brief Return whether the given font has the given fullname or
437  * PostScript name depending on whether it has PostScript outlines.
438  */
439 static bool matches_full_or_postscript_name(ASS_FontInfo *f,
440                                             const char *fullname)
441 {
442     bool matches_fullname = false;
443     bool matches_postscript_name = false;
444
445     for (int i = 0; i < f->n_fullname; i++) {
446         if (ass_strcasecmp(f->fullnames[i], fullname) == 0) {
447             matches_fullname = true;
448             break;
449         }
450     }
451
452     if (f->postscript_name != NULL &&
453         ass_strcasecmp(f->postscript_name, fullname) == 0)
454         matches_postscript_name = true;
455
456     if (matches_fullname == matches_postscript_name)
457         return matches_fullname;
458
459     if (check_postscript(f))
460         return matches_postscript_name;
461     else
462         return matches_fullname;
463 }
464
465 /**
466  * \brief Compare attributes of font (a) against a font request (req). Returns
467  * a matching score - the lower the better.
468  * Ignores font names/families!
469  * \param a font
470  * \param b font request
471  * \return matching score
472  */
473 static unsigned font_attributes_similarity(ASS_FontInfo *a, ASS_FontInfo *req)
474 {
475     unsigned similarity = 0;
476     similarity += ABS(a->weight - req->weight);
477     similarity += ABS(a->slant - req->slant);
478     similarity += ABS(a->width - req->width);
479
480     return similarity;
481 }
482
483 #if 0
484 // dump font information
485 static void font_info_dump(ASS_FontInfo *font_infos, size_t len)
486 {
487     int i, j;
488
489     // dump font infos
490     for (i = 0; i < len; i++) {
491         printf("font %d\n", i);
492         printf("  families: ");
493         for (j = 0; j < font_infos[i].n_family; j++)
494             printf("'%s' ", font_infos[i].families[j]);
495         printf("  fullnames: ");
496         for (j = 0; j < font_infos[i].n_fullname; j++)
497             printf("'%s' ", font_infos[i].fullnames[j]);
498         printf("\n");
499         printf("  slant: %d\n", font_infos[i].slant);
500         printf("  weight: %d\n", font_infos[i].weight);
501         printf("  width: %d\n", font_infos[i].width);
502         printf("  path: %s\n", font_infos[i].path);
503         printf("  index: %d\n", font_infos[i].index);
504         printf("  score: %d\n", font_infos[i].score);
505
506     }
507 }
508 #endif
509
510 static bool check_glyph(ASS_FontInfo *fi, uint32_t code)
511 {
512     ASS_FontProvider *provider = fi->provider;
513     assert(provider && provider->funcs.check_glyph);
514
515     return provider->funcs.check_glyph(fi->priv, code);
516 }
517
518 static char *
519 find_font(ASS_FontSelector *priv, ASS_Library *library,
520           ASS_FontProviderMetaData meta, unsigned bold, unsigned italic,
521           int *index, char **postscript_name, int *uid, ASS_FontStream *stream,
522           uint32_t code, bool *name_match)
523 {
524     ASS_FontInfo req = {0};
525     ASS_FontInfo *selected = NULL;
526
527     // do we actually have any fonts?
528     if (!priv->n_font)
529         return NULL;
530
531     // fill font request
532     req.slant   = italic;
533     req.weight  = bold;
534     req.width   = 100;
535
536     // Match font family name against font list
537     unsigned score_min = UINT_MAX;
538     for (int i = 0; i < meta.n_fullname; i++) {
539         const char *fullname = meta.fullnames[i];
540
541         for (int x = 0; x < priv->n_font; x++) {
542             ASS_FontInfo *font = &priv->font_infos[x];
543             unsigned score = UINT_MAX;
544
545             if (matches_family_name(font, fullname)) {
546                 // If there's a family match, compare font attributes
547                 // to determine best match in that particular family
548                 score = font_attributes_similarity(font, &req);
549                 *name_match = true;
550             } else if (matches_full_or_postscript_name(font, fullname)) {
551                 // If we don't have any match, compare fullnames against request
552                 // if there is a match now, assign lowest score possible. This means
553                 // the font should be chosen instantly, without further search.
554                 score = 0;
555                 *name_match = true;
556             }
557
558             // Consider updating idx if score is better than current minimum
559             if (score < score_min) {
560                 // Check if the font has the requested glyph.
561                 // We are doing this here, for every font face, because
562                 // coverage might differ between the variants of a font
563                 // family. In practice, it is common that the regular
564                 // style has the best coverage while bold/italic/etc
565                 // variants cover less (e.g. FreeSans family).
566                 // We want to be able to match even if the closest variant
567                 // does not have the requested glyph, but another member
568                 // of the family has the glyph.
569                 if (!check_glyph(font, code))
570                     continue;
571
572                 score_min = score;
573                 selected = font;
574             }
575
576             // Lowest possible score instantly matches; this is typical
577             // for fullname matches, but can also occur with family matches.
578             if (score == 0)
579                 break;
580         }
581
582         // The list of names is sorted by priority. If we matched anything,
583         // we can and should stop.
584         if (selected != NULL)
585             break;
586     }
587
588     // found anything?
589     char *result = NULL;
590     if (selected) {
591         ASS_FontProvider *provider = selected->provider;
592
593         // successfully matched, set up return values
594         *postscript_name = selected->postscript_name;
595         *uid   = selected->uid;
596
597         // use lazy evaluation for index if applicable
598         if (provider->funcs.get_font_index) {
599             *index = provider->funcs.get_font_index(selected->priv);
600         } else
601             *index = selected->index;
602
603         // set up memory stream if there is no path
604         if (selected->path == NULL) {
605             stream->func = provider->funcs.get_data;
606             stream->priv = selected->priv;
607             // Prefer PostScript name because it is unique. This is only
608             // used for display purposes so it doesn't matter that much,
609             // though.
610             if (selected->postscript_name)
611                 result = selected->postscript_name;
612             else
613                 result = selected->families[0];
614         } else
615             result = selected->path;
616
617     }
618
619     return result;
620 }
621
622 static char *select_font(ASS_FontSelector *priv, ASS_Library *library,
623                          const char *family, unsigned bold, unsigned italic,
624                          int *index, char **postscript_name, int *uid,
625                          ASS_FontStream *stream, uint32_t code)
626 {
627     ASS_FontProvider *default_provider = priv->default_provider;
628     ASS_FontProviderMetaData meta = {0};
629     char *result = NULL;
630     bool name_match = false;
631
632     if (family == NULL)
633         return NULL;
634
635     ASS_FontProviderMetaData default_meta = {
636         .n_fullname = 1,
637         .fullnames  = (char **)&family,
638     };
639
640     // Get a list of substitutes if applicable, and use it for matching.
641     if (default_provider && default_provider->funcs.get_substitutions) {
642         default_provider->funcs.get_substitutions(default_provider->priv,
643                                                   family, &meta);
644     }
645
646     if (!meta.n_fullname) {
647         meta = default_meta;
648     }
649
650     result = find_font(priv, library, meta, bold, italic, index,
651                        postscript_name, uid, stream, code, &name_match);
652
653     // If no matching font was found, it might not exist in the font list
654     // yet. Call the match_fonts callback to fill in the missing fonts
655     // on demand, and retry the search for a match.
656     if (result == NULL && name_match == false && default_provider &&
657             default_provider->funcs.match_fonts) {
658         // TODO: consider changing the API to make more efficient
659         // implementations possible.
660         for (int i = 0; i < meta.n_fullname; i++) {
661             default_provider->funcs.match_fonts(library, default_provider,
662                                                 meta.fullnames[i]);
663         }
664         result = find_font(priv, library, meta, bold, italic, index,
665                            postscript_name, uid, stream, code, &name_match);
666     }
667
668     // cleanup
669     if (meta.fullnames != default_meta.fullnames) {
670         for (int i = 0; i < meta.n_fullname; i++)
671             free(meta.fullnames[i]);
672         free(meta.fullnames);
673     }
674
675     return result;
676 }
677
678
679 /**
680  * \brief Find a font. Use default family or path if necessary.
681  * \param library ASS library handle
682  * \param family font family
683  * \param treat_family_as_pattern treat family as fontconfig pattern
684  * \param bold font weight value
685  * \param italic font slant value
686  * \param index out: font index inside a file
687  * \param code: the character that should be present in the font, can be 0
688  * \return font file path
689 */
690 char *ass_font_select(ASS_FontSelector *priv, ASS_Library *library,
691                       ASS_Font *font, int *index, char **postscript_name,
692                       int *uid, ASS_FontStream *data, uint32_t code)
693 {
694     char *res = 0;
695     const char *family = font->desc.family;
696     unsigned bold = font->desc.bold;
697     unsigned italic = font->desc.italic;
698     ASS_FontProvider *default_provider = priv->default_provider;
699
700     if (family && *family)
701         res = select_font(priv, library, family, bold, italic, index,
702                 postscript_name, uid, data, code);
703
704     if (!res && priv->family_default) {
705         res = select_font(priv, library, priv->family_default, bold,
706                 italic, index, postscript_name, uid, data, code);
707         if (res)
708             ass_msg(library, MSGL_WARN, "fontselect: Using default "
709                     "font family: (%s, %d, %d) -> %s, %d, %s",
710                     family, bold, italic, res, *index,
711                     *postscript_name ? *postscript_name : "(none)");
712     }
713
714     if (!res && default_provider && default_provider->funcs.get_fallback) {
715         const char *search_family = family;
716         if (!search_family || !*search_family)
717             search_family = "Arial";
718         char *fallback_family = default_provider->funcs.get_fallback(
719                 default_provider->priv, search_family, code);
720
721         if (fallback_family) {
722             res = select_font(priv, library, fallback_family, bold, italic,
723                     index, postscript_name, uid, data, code);
724             free(fallback_family);
725         }
726     }
727
728     if (!res && priv->path_default) {
729         res = priv->path_default;
730         *index = priv->index_default;
731         ass_msg(library, MSGL_WARN, "fontselect: Using default font: "
732                 "(%s, %d, %d) -> %s, %d, %s", family, bold, italic,
733                 priv->path_default, *index,
734                 *postscript_name ? *postscript_name : "(none)");
735     }
736
737     if (res)
738         ass_msg(library, MSGL_INFO,
739                 "fontselect: (%s, %d, %d) -> %s, %d, %s", family, bold,
740                 italic, res, *index, *postscript_name ? *postscript_name : "(none)");
741
742     return res;
743 }
744
745
746 /**
747  * \brief Read basic metadata (names, weight, slant) from a FreeType face,
748  * as required for the FontSelector for matching and sorting.
749  * \param lib FreeType library
750  * \param face FreeType face
751  * \param info metadata, returned here
752  * \return success
753  */
754 static bool
755 get_font_info(FT_Library lib, FT_Face face, ASS_FontProviderMetaData *info)
756 {
757     int i;
758     int num_fullname = 0;
759     int num_family   = 0;
760     int num_names = FT_Get_Sfnt_Name_Count(face);
761     int slant, weight;
762     char *fullnames[MAX_FULLNAME];
763     char *families[MAX_FULLNAME];
764
765     // we're only interested in outlines
766     if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
767         return false;
768
769     if (face->family_name) {
770         families[0] = strdup(face->family_name);
771         if (families[0] == NULL)
772             goto error;
773         num_family++;
774     }
775
776     for (i = 0; i < num_names; i++) {
777         FT_SfntName name;
778
779         if (FT_Get_Sfnt_Name(face, i, &name))
780             continue;
781
782         if (name.platform_id == TT_PLATFORM_MICROSOFT &&
783                 (name.name_id == TT_NAME_ID_FULL_NAME ||
784                  name.name_id == TT_NAME_ID_FONT_FAMILY)) {
785             char buf[1024];
786             ass_utf16be_to_utf8(buf, sizeof(buf), (uint8_t *)name.string,
787                                 name.string_len);
788
789             if (name.name_id == TT_NAME_ID_FULL_NAME) {
790                 fullnames[num_fullname] = strdup(buf);
791                 if (fullnames[num_fullname] == NULL)
792                     goto error;
793                 num_fullname++;
794             }
795
796             if (name.name_id == TT_NAME_ID_FONT_FAMILY) {
797                 families[num_family] = strdup(buf);
798                 if (families[num_family] == NULL)
799                     goto error;
800                 num_family++;
801             }
802         }
803     }
804
805     // we absolutely need a name
806     if (num_family == 0)
807         goto error;
808
809     // calculate sensible slant and weight from style attributes
810     slant  = 110 * !!(face->style_flags & FT_STYLE_FLAG_ITALIC);
811     weight = ass_face_get_weight(face);
812
813     // fill our struct
814     info->slant  = slant;
815     info->weight = weight;
816     info->width  = 100;     // FIXME, should probably query the OS/2 table
817
818     info->postscript_name = (char *)FT_Get_Postscript_Name(face);
819
820     info->families = calloc(sizeof(char *), num_family);
821     if (info->families == NULL)
822         goto error;
823     memcpy(info->families, &families, sizeof(char *) * num_family);
824     info->n_family = num_family;
825
826     if (num_fullname) {
827         info->fullnames = calloc(sizeof(char *), num_fullname);
828         if (info->fullnames == NULL)
829             goto error;
830         memcpy(info->fullnames, &fullnames, sizeof(char *) * num_fullname);
831         info->n_fullname = num_fullname;
832     }
833
834     return true;
835
836 error:
837     for (i = 0; i < num_family; i++)
838         free(families[i]);
839
840     for (i = 0; i < num_fullname; i++)
841         free(fullnames[i]);
842
843     free(info->families);
844     free(info->fullnames);
845
846     return false;
847 }
848
849 bool ass_get_font_info(ASS_Library *lib, FT_Library ftlib, const char *path,
850                        const char *postscript_name, int index,
851                        ASS_FontProviderMetaData *info)
852 {
853     bool ret = false;
854     FT_Face face = NULL;
855     int error = FT_New_Face(ftlib, path, index, &face);
856     if (error) {
857         ass_msg(lib, MSGL_WARN, "Error opening font: '%s', %d", path, index);
858         return false;
859     }
860
861     if (postscript_name && index < 0 && face->num_faces > 0) {
862         // The font provider gave us a postscript name and is not sure
863         // about the face index.. so use the postscript name to find the
864         // correct face_index in the collection!
865         for (int i = 0; i < face->num_faces; i++) {
866             FT_Done_Face(face);
867             error = FT_New_Face(ftlib, path, i, &face);
868             if (error) {
869                 ass_msg(lib, MSGL_WARN, "Error opening font: '%s', %d", path, i);
870                 return false;
871             }
872
873             const char *face_psname = FT_Get_Postscript_Name(face);
874             if (face_psname != NULL &&
875                 strcmp(face_psname, postscript_name) == 0)
876                 break;
877         }
878     }
879
880     if (face) {
881         ret = get_font_info(ftlib, face, info);
882         if (ret)
883             info->postscript_name = strdup(info->postscript_name);
884         FT_Done_Face(face);
885     }
886
887     return ret;
888 }
889
890 /**
891  * \brief Free the dynamically allocated fields of metadata
892  * created by get_font_info.
893  * \param meta metadata created by get_font_info
894  */
895 static void free_font_info(ASS_FontProviderMetaData *meta)
896 {
897     int i;
898
899     for (i = 0; i < meta->n_family; i++)
900         free(meta->families[i]);
901
902     for (i = 0; i < meta->n_fullname; i++)
903         free(meta->fullnames[i]);
904
905     free(meta->families);
906     free(meta->fullnames);
907 }
908
909 /**
910  * \brief Process memory font.
911  * \param priv private data
912  * \param library library object
913  * \param ftlibrary freetype library object
914  * \param idx index of the processed font in library->fontdata
915  *
916  * Builds a FontInfo with FreeType and some table reading.
917 */
918 static void process_fontdata(ASS_FontProvider *priv, ASS_Library *library,
919                              FT_Library ftlibrary, int idx)
920 {
921     int rc;
922     const char *name = library->fontdata[idx].name;
923     const char *data = library->fontdata[idx].data;
924     int data_size = library->fontdata[idx].size;
925
926     FT_Face face;
927     int face_index, num_faces = 1;
928
929     for (face_index = 0; face_index < num_faces; ++face_index) {
930         ASS_FontProviderMetaData info;
931         FontDataFT *ft;
932
933         rc = FT_New_Memory_Face(ftlibrary, (unsigned char *) data,
934                                 data_size, face_index, &face);
935         if (rc) {
936             ass_msg(library, MSGL_WARN, "Error opening memory font '%s'",
937                    name);
938             continue;
939         }
940
941         num_faces = face->num_faces;
942
943         charmap_magic(library, face);
944
945         memset(&info, 0, sizeof(ASS_FontProviderMetaData));
946         if (!get_font_info(ftlibrary, face, &info)) {
947             ass_msg(library, MSGL_WARN,
948                     "Error getting metadata for embedded font '%s'", name);
949             FT_Done_Face(face);
950             continue;
951         }
952
953         ft = calloc(1, sizeof(FontDataFT));
954
955         if (ft == NULL) {
956             free_font_info(&info);
957             FT_Done_Face(face);
958             continue;
959         }
960
961         ft->lib  = library;
962         ft->face = face;
963         ft->idx  = idx;
964
965         if (!ass_font_provider_add_font(priv, &info, NULL, face_index, ft)) {
966             ass_msg(library, MSGL_WARN, "Failed to add embedded font '%s'",
967                     name);
968         }
969
970         free_font_info(&info);
971     }
972 }
973
974 /**
975  * \brief Create font provider for embedded fonts. This parses the fonts known
976  * to the current ASS_Library and adds them to the selector.
977  * \param lib library
978  * \param selector font selector
979  * \param ftlib FreeType library - used for querying fonts
980  * \return font provider
981  */
982 static ASS_FontProvider *
983 ass_embedded_fonts_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
984                                 FT_Library ftlib)
985 {
986     int i;
987     ASS_FontProvider *priv = ass_font_provider_new(selector, &ft_funcs, NULL);
988     if (priv == NULL)
989         return NULL;
990
991     if (lib->fonts_dir && lib->fonts_dir[0]) {
992         load_fonts_from_dir(lib, lib->fonts_dir);
993     }
994
995     for (i = 0; i < lib->num_fontdata; ++i)
996         process_fontdata(priv, lib, ftlib, i);
997
998     return priv;
999 }
1000
1001 struct font_constructors {
1002     ASS_DefaultFontProvider id;
1003     ASS_FontProvider *(*constructor)(ASS_Library *, ASS_FontSelector *,
1004                                      const char *);
1005     const char *name;
1006 };
1007
1008 struct font_constructors font_constructors[] = {
1009 #ifdef CONFIG_CORETEXT
1010     { ASS_FONTPROVIDER_CORETEXT,        &ass_coretext_add_provider,     "coretext"},
1011 #endif
1012 #ifdef CONFIG_DIRECTWRITE
1013     { ASS_FONTPROVIDER_DIRECTWRITE,     &ass_directwrite_add_provider,  "directwrite"},
1014 #endif
1015 #ifdef CONFIG_FONTCONFIG
1016     { ASS_FONTPROVIDER_FONTCONFIG,      &ass_fontconfig_add_provider,   "fontconfig"},
1017 #endif
1018     { ASS_FONTPROVIDER_NONE, NULL, NULL },
1019 };
1020
1021 /**
1022  * \brief Init font selector.
1023  * \param library libass library object
1024  * \param ftlibrary freetype library object
1025  * \param family default font family
1026  * \param path default font path
1027  * \return newly created font selector
1028  */
1029 ASS_FontSelector *
1030 ass_fontselect_init(ASS_Library *library,
1031                     FT_Library ftlibrary, const char *family,
1032                     const char *path, const char *config,
1033                     ASS_DefaultFontProvider dfp)
1034 {
1035     ASS_FontSelector *priv = calloc(1, sizeof(ASS_FontSelector));
1036     if (priv == NULL)
1037         return NULL;
1038
1039     priv->uid = 1;
1040     priv->family_default = family ? strdup(family) : NULL;
1041     priv->path_default = path ? strdup(path) : NULL;
1042     priv->index_default = 0;
1043
1044     priv->embedded_provider = ass_embedded_fonts_add_provider(library, priv,
1045             ftlibrary);
1046
1047     if (priv->embedded_provider == NULL) {
1048         ass_msg(library, MSGL_WARN, "failed to create embedded font provider");
1049     }
1050
1051     if (dfp >= ASS_FONTPROVIDER_AUTODETECT) {
1052         for (int i = 0; font_constructors[i].constructor; i++ )
1053             if (dfp == font_constructors[i].id ||
1054                 dfp == ASS_FONTPROVIDER_AUTODETECT) {
1055                 priv->default_provider =
1056                     font_constructors[i].constructor(library, priv, config);
1057                 if (priv->default_provider) {
1058                     ass_msg(library, MSGL_INFO, "Using font provider %s",
1059                             font_constructors[i].name);
1060                     break;
1061                 }
1062             }
1063
1064         if (!priv->default_provider)
1065             ass_msg(library, MSGL_WARN, "can't find selected font provider");
1066
1067     }
1068
1069     return priv;
1070 }
1071
1072 void ass_get_available_font_providers(ASS_Library *priv,
1073                                       ASS_DefaultFontProvider **providers,
1074                                       size_t *size)
1075 {
1076     size_t offset = 2;
1077
1078     *size = offset;
1079     for (int i = 0; font_constructors[i].constructor; i++)
1080         (*size)++;
1081
1082     *providers = calloc(*size, sizeof(ASS_DefaultFontProvider));
1083
1084     if (*providers == NULL) {
1085         *size = (size_t)-1;
1086         return;
1087     }
1088
1089     (*providers)[0] = ASS_FONTPROVIDER_NONE;
1090     (*providers)[1] = ASS_FONTPROVIDER_AUTODETECT;
1091
1092     for (int i = offset; i < *size; i++)
1093         (*providers)[i] = font_constructors[i-offset].id;
1094 }
1095
1096 /**
1097  * \brief Free font selector and release associated data
1098  * \param the font selector
1099  */
1100 void ass_fontselect_free(ASS_FontSelector *priv)
1101 {
1102     if (priv->default_provider)
1103         ass_font_provider_free(priv->default_provider);
1104     if (priv->embedded_provider)
1105         ass_font_provider_free(priv->embedded_provider);
1106
1107     free(priv->font_infos);
1108     free(priv->path_default);
1109     free(priv->family_default);
1110
1111     free(priv);
1112 }
1113
1114 void ass_map_font(const ASS_FontMapping *map, int len, const char *name,
1115                   ASS_FontProviderMetaData *meta)
1116 {
1117     for (int i = 0; i < len; i++) {
1118         if (ass_strcasecmp(map[i].from, name) == 0) {
1119             meta->fullnames = calloc(1, sizeof(char *));
1120             if (meta->fullnames) {
1121                 meta->fullnames[0] = strdup(map[i].to);
1122                 if (meta->fullnames[0])
1123                     meta->n_fullname = 1;
1124             }
1125             return;
1126         }
1127     }
1128 }