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