2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
3 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
5 * This file is part of libass.
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.
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.
21 #include "ass_compat.h"
28 #include <sys/types.h>
33 #include <sys/types.h>
35 #include FT_FREETYPE_H
36 #include FT_SFNT_NAMES_H
37 #include FT_TRUETYPE_IDS_H
38 #include FT_TYPE1_TABLES_H
40 #include "ass_utils.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"
48 #include "ass_string.h"
50 #define ABS(x) ((x) < 0 ? -(x) : (x))
51 #define MAX_FULLNAME 100
53 // internal font database element
54 // all strings are utf-8
56 int uid; // unique font face id
58 char **families; // family name
59 char **fullnames; // list of localized fullnames (e.g. Arial Bold Italic)
64 int weight; // TrueType scale, 100-900
67 // how to access this face
68 char *path; // absolute path
69 int index; // font index inside font collections
71 char *postscript_name; // can be used as an alternative to index to
72 // identify a font inside a collection
75 ASS_FontProvider *provider;
77 // private data for callbacks
81 struct font_selector {
93 ASS_FontInfo *font_infos;
95 ASS_FontProvider *default_provider;
96 ASS_FontProvider *embedded_provider;
99 struct font_provider {
100 ASS_FontSelector *parent;
101 ASS_FontProviderFuncs funcs;
105 typedef struct font_data_ft FontDataFT;
106 struct font_data_ft {
112 static bool check_postscript_ft(void *data)
114 FontDataFT *fd = (FontDataFT *)data;
115 PS_FontInfoRec postscript_info;
116 return !FT_Get_PS_Font_Info(fd->face, &postscript_info);
119 static bool check_glyph_ft(void *data, uint32_t codepoint)
121 FontDataFT *fd = (FontDataFT *)data;
126 return !!FT_Get_Char_Index(fd->face, codepoint);
129 static void destroy_font_ft(void *data)
131 FontDataFT *fd = (FontDataFT *)data;
133 FT_Done_Face(fd->face);
138 get_data_embedded(void *data, unsigned char *buf, size_t offset, size_t len)
140 FontDataFT *ft = (FontDataFT *)data;
141 ASS_Fontdata *fd = ft->lib->fontdata;
147 if (offset >= fd[i].size)
150 if (len > fd[i].size - offset)
151 len = fd[i].size - offset;
153 memcpy(buf, fd[i].data + offset, len);
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,
164 static void load_fonts_from_dir(ASS_Library *library, const char *dir)
166 DIR *d = opendir(dir);
170 struct dirent *entry = readdir(d);
173 if (entry->d_name[0] == '.')
176 snprintf(fullname, sizeof(fullname), "%s/%s", dir, entry->d_name);
178 ass_msg(library, MSGL_INFO, "Loading font file '%s'", fullname);
179 void *data = read_file(library, fullname, &bufsize);
181 ass_add_font(library, entry->d_name, data, bufsize);
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
196 ass_font_provider_new(ASS_FontSelector *selector, ASS_FontProviderFuncs *funcs,
199 ASS_FontProvider *provider = calloc(1, sizeof(ASS_FontProvider));
200 if (provider == NULL)
203 provider->parent = selector;
204 provider->funcs = *funcs;
205 provider->priv = data;
211 * Free all data associated with a FontInfo struct. Handles FontInfo structs
212 * with incomplete allocations well.
214 * \param info FontInfo struct to free associated data from
216 static void ass_font_provider_free_fontinfo(ASS_FontInfo *info)
220 if (info->fullnames) {
221 for (j = 0; j < info->n_fullname; j++)
222 free(info->fullnames[j]);
223 free(info->fullnames);
226 if (info->families) {
227 for (j = 0; j < info->n_family; j++)
228 free(info->families[j]);
229 free(info->families);
235 if (info->postscript_name)
236 free(info->postscript_name);
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
250 ass_font_provider_add_font(ASS_FontProvider *provider,
251 ASS_FontProviderMetaData *meta, const char *path,
252 int index, void *data)
255 int weight, slant, width;
256 ASS_FontSelector *selector = provider->parent;
261 printf("new font:\n");
262 printf(" families: ");
263 for (j = 0; j < meta->n_family; j++)
264 printf("'%s' ", meta->families[j]);
266 printf(" fullnames: ");
267 for (j = 0; j < meta->n_fullname; j++)
268 printf("'%s' ", meta->fullnames[j]);
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);
277 weight = meta->weight;
281 // check slant/weight for validity, use defaults if they're invalid
282 if (weight < 100 || weight > 900)
284 if (slant < 0 || slant > 110)
286 if (width < 50 || width > 200)
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));
296 // copy over metadata
297 info = selector->font_infos + selector->n_font;
298 memset(info, 0, sizeof(ASS_FontInfo));
301 info->uid = selector->uid++;
304 info->weight = weight;
306 info->n_fullname = meta->n_fullname;
307 info->n_family = meta->n_family;
309 info->families = calloc(meta->n_family, sizeof(char *));
310 if (info->families == NULL)
313 if (meta->n_fullname) {
314 info->fullnames = calloc(meta->n_fullname, sizeof(char *));
315 if (info->fullnames == NULL)
319 for (i = 0; i < info->n_family; i++) {
320 info->families[i] = strdup(meta->families[i]);
321 if (info->families[i] == NULL)
325 for (i = 0; i < info->n_fullname; i++) {
326 info->fullnames[i] = strdup(meta->fullnames[i]);
327 if (info->fullnames[i] == NULL)
331 if (meta->postscript_name) {
332 info->postscript_name = strdup(meta->postscript_name);
333 if (info->postscript_name == NULL)
338 info->path = strdup(path);
339 if (info->path == NULL)
345 info->provider = provider;
351 ass_font_provider_free_fontinfo(info);
353 if (provider->funcs.destroy_font)
354 provider->funcs.destroy_font(data);
360 * \brief Clean up font database. Deletes all fonts that have an invalid
361 * font provider (NULL).
362 * \param selector the font selector
364 static void ass_fontselect_cleanup(ASS_FontSelector *selector)
368 for (i = 0, w = 0; i < selector->n_font; i++) {
369 ASS_FontInfo *info = selector->font_infos + i;
371 // update write pointer
372 if (info->provider != NULL) {
373 // rewrite, if needed
375 memcpy(selector->font_infos + w, selector->font_infos + i,
376 sizeof(ASS_FontInfo));
382 selector->n_font = w;
385 void ass_font_provider_free(ASS_FontProvider *provider)
388 ASS_FontSelector *selector = provider->parent;
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;
394 if (info->provider == provider) {
395 ass_font_provider_free_fontinfo(info);
397 if (info->provider->funcs.destroy_font)
398 info->provider->funcs.destroy_font(info->priv);
400 info->provider = NULL;
405 // delete marked entries
406 ass_fontselect_cleanup(selector);
408 // free private data of the provider
409 if (provider->funcs.destroy_provider)
410 provider->funcs.destroy_provider(provider->priv);
415 static bool check_postscript(ASS_FontInfo *fi)
417 ASS_FontProvider *provider = fi->provider;
418 assert(provider && provider->funcs.check_postscript);
420 return provider->funcs.check_postscript(fi->priv);
424 * \brief Return whether the given font is in the given family.
426 static bool matches_family_name(ASS_FontInfo *f, const char *family)
428 for (int i = 0; i < f->n_family; i++) {
429 if (ass_strcasecmp(f->families[i], family) == 0)
436 * \brief Return whether the given font has the given fullname or
437 * PostScript name depending on whether it has PostScript outlines.
439 static bool matches_full_or_postscript_name(ASS_FontInfo *f,
440 const char *fullname)
442 bool matches_fullname = false;
443 bool matches_postscript_name = false;
445 for (int i = 0; i < f->n_fullname; i++) {
446 if (ass_strcasecmp(f->fullnames[i], fullname) == 0) {
447 matches_fullname = true;
452 if (f->postscript_name != NULL &&
453 ass_strcasecmp(f->postscript_name, fullname) == 0)
454 matches_postscript_name = true;
456 if (matches_fullname == matches_postscript_name)
457 return matches_fullname;
459 if (check_postscript(f))
460 return matches_postscript_name;
462 return matches_fullname;
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!
470 * \param b font request
471 * \return matching score
473 static unsigned font_attributes_similarity(ASS_FontInfo *a, ASS_FontInfo *req)
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);
484 // dump font information
485 static void font_info_dump(ASS_FontInfo *font_infos, size_t len)
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]);
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);
510 static bool check_glyph(ASS_FontInfo *fi, uint32_t code)
512 ASS_FontProvider *provider = fi->provider;
513 assert(provider && provider->funcs.check_glyph);
515 return provider->funcs.check_glyph(fi->priv, code);
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)
524 ASS_FontInfo req = {0};
525 ASS_FontInfo *selected = NULL;
527 // do we actually have any fonts?
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];
541 for (int x = 0; x < priv->n_font; x++) {
542 ASS_FontInfo *font = &priv->font_infos[x];
543 unsigned score = UINT_MAX;
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);
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.
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))
576 // Lowest possible score instantly matches; this is typical
577 // for fullname matches, but can also occur with family matches.
582 // The list of names is sorted by priority. If we matched anything,
583 // we can and should stop.
584 if (selected != NULL)
591 ASS_FontProvider *provider = selected->provider;
593 // successfully matched, set up return values
594 *postscript_name = selected->postscript_name;
595 *uid = selected->uid;
597 // use lazy evaluation for index if applicable
598 if (provider->funcs.get_font_index) {
599 *index = provider->funcs.get_font_index(selected->priv);
601 *index = selected->index;
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,
610 if (selected->postscript_name)
611 result = selected->postscript_name;
613 result = selected->families[0];
615 result = selected->path;
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)
627 ASS_FontProvider *default_provider = priv->default_provider;
628 ASS_FontProviderMetaData meta = {0};
630 bool name_match = false;
635 ASS_FontProviderMetaData default_meta = {
637 .fullnames = (char **)&family,
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,
646 if (!meta.n_fullname) {
650 result = find_font(priv, library, meta, bold, italic, index,
651 postscript_name, uid, stream, code, &name_match);
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,
664 result = find_font(priv, library, meta, bold, italic, index,
665 postscript_name, uid, stream, code, &name_match);
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);
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
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)
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;
700 if (family && *family)
701 res = select_font(priv, library, family, bold, italic, index,
702 postscript_name, uid, data, code);
704 if (!res && priv->family_default) {
705 res = select_font(priv, library, priv->family_default, bold,
706 italic, index, postscript_name, uid, data, code);
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)");
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);
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);
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)");
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)");
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
755 get_font_info(FT_Library lib, FT_Face face, ASS_FontProviderMetaData *info)
758 int num_fullname = 0;
760 int num_names = FT_Get_Sfnt_Name_Count(face);
762 char *fullnames[MAX_FULLNAME];
763 char *families[MAX_FULLNAME];
765 // we're only interested in outlines
766 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
769 if (face->family_name) {
770 families[0] = strdup(face->family_name);
771 if (families[0] == NULL)
776 for (i = 0; i < num_names; i++) {
779 if (FT_Get_Sfnt_Name(face, i, &name))
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)) {
786 ass_utf16be_to_utf8(buf, sizeof(buf), (uint8_t *)name.string,
789 if (name.name_id == TT_NAME_ID_FULL_NAME) {
790 fullnames[num_fullname] = strdup(buf);
791 if (fullnames[num_fullname] == NULL)
796 if (name.name_id == TT_NAME_ID_FONT_FAMILY) {
797 families[num_family] = strdup(buf);
798 if (families[num_family] == NULL)
805 // we absolutely need a name
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);
815 info->weight = weight;
816 info->width = 100; // FIXME, should probably query the OS/2 table
818 info->postscript_name = (char *)FT_Get_Postscript_Name(face);
820 info->families = calloc(sizeof(char *), num_family);
821 if (info->families == NULL)
823 memcpy(info->families, &families, sizeof(char *) * num_family);
824 info->n_family = num_family;
827 info->fullnames = calloc(sizeof(char *), num_fullname);
828 if (info->fullnames == NULL)
830 memcpy(info->fullnames, &fullnames, sizeof(char *) * num_fullname);
831 info->n_fullname = num_fullname;
837 for (i = 0; i < num_family; i++)
840 for (i = 0; i < num_fullname; i++)
843 free(info->families);
844 free(info->fullnames);
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)
855 int error = FT_New_Face(ftlib, path, index, &face);
857 ass_msg(lib, MSGL_WARN, "Error opening font: '%s', %d", path, index);
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++) {
867 error = FT_New_Face(ftlib, path, i, &face);
869 ass_msg(lib, MSGL_WARN, "Error opening font: '%s', %d", path, i);
873 const char *face_psname = FT_Get_Postscript_Name(face);
874 if (face_psname != NULL &&
875 strcmp(face_psname, postscript_name) == 0)
881 ret = get_font_info(ftlib, face, info);
883 info->postscript_name = strdup(info->postscript_name);
891 * \brief Free the dynamically allocated fields of metadata
892 * created by get_font_info.
893 * \param meta metadata created by get_font_info
895 static void free_font_info(ASS_FontProviderMetaData *meta)
899 for (i = 0; i < meta->n_family; i++)
900 free(meta->families[i]);
902 for (i = 0; i < meta->n_fullname; i++)
903 free(meta->fullnames[i]);
905 free(meta->families);
906 free(meta->fullnames);
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
916 * Builds a FontInfo with FreeType and some table reading.
918 static void process_fontdata(ASS_FontProvider *priv, ASS_Library *library,
919 FT_Library ftlibrary, int idx)
922 const char *name = library->fontdata[idx].name;
923 const char *data = library->fontdata[idx].data;
924 int data_size = library->fontdata[idx].size;
927 int face_index, num_faces = 1;
929 for (face_index = 0; face_index < num_faces; ++face_index) {
930 ASS_FontProviderMetaData info;
933 rc = FT_New_Memory_Face(ftlibrary, (unsigned char *) data,
934 data_size, face_index, &face);
936 ass_msg(library, MSGL_WARN, "Error opening memory font '%s'",
941 num_faces = face->num_faces;
943 charmap_magic(library, face);
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);
953 ft = calloc(1, sizeof(FontDataFT));
956 free_font_info(&info);
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'",
970 free_font_info(&info);
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.
978 * \param selector font selector
979 * \param ftlib FreeType library - used for querying fonts
980 * \return font provider
982 static ASS_FontProvider *
983 ass_embedded_fonts_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
987 ASS_FontProvider *priv = ass_font_provider_new(selector, &ft_funcs, NULL);
991 if (lib->fonts_dir && lib->fonts_dir[0]) {
992 load_fonts_from_dir(lib, lib->fonts_dir);
995 for (i = 0; i < lib->num_fontdata; ++i)
996 process_fontdata(priv, lib, ftlib, i);
1001 struct font_constructors {
1002 ASS_DefaultFontProvider id;
1003 ASS_FontProvider *(*constructor)(ASS_Library *, ASS_FontSelector *,
1008 struct font_constructors font_constructors[] = {
1009 #ifdef CONFIG_CORETEXT
1010 { ASS_FONTPROVIDER_CORETEXT, &ass_coretext_add_provider, "coretext"},
1012 #ifdef CONFIG_DIRECTWRITE
1013 { ASS_FONTPROVIDER_DIRECTWRITE, &ass_directwrite_add_provider, "directwrite"},
1015 #ifdef CONFIG_FONTCONFIG
1016 { ASS_FONTPROVIDER_FONTCONFIG, &ass_fontconfig_add_provider, "fontconfig"},
1018 { ASS_FONTPROVIDER_NONE, NULL, NULL },
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
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)
1035 ASS_FontSelector *priv = calloc(1, sizeof(ASS_FontSelector));
1040 priv->family_default = family ? strdup(family) : NULL;
1041 priv->path_default = path ? strdup(path) : NULL;
1042 priv->index_default = 0;
1044 priv->embedded_provider = ass_embedded_fonts_add_provider(library, priv,
1047 if (priv->embedded_provider == NULL) {
1048 ass_msg(library, MSGL_WARN, "failed to create embedded font provider");
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);
1064 if (!priv->default_provider)
1065 ass_msg(library, MSGL_WARN, "can't find selected font provider");
1072 void ass_get_available_font_providers(ASS_Library *priv,
1073 ASS_DefaultFontProvider **providers,
1079 for (int i = 0; font_constructors[i].constructor; i++)
1082 *providers = calloc(*size, sizeof(ASS_DefaultFontProvider));
1084 if (*providers == NULL) {
1089 (*providers)[0] = ASS_FONTPROVIDER_NONE;
1090 (*providers)[1] = ASS_FONTPROVIDER_AUTODETECT;
1092 for (int i = offset; i < *size; i++)
1093 (*providers)[i] = font_constructors[i-offset].id;
1097 * \brief Free font selector and release associated data
1098 * \param the font selector
1100 void ass_fontselect_free(ASS_FontSelector *priv)
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);
1107 free(priv->font_infos);
1108 free(priv->path_default);
1109 free(priv->family_default);
1114 void ass_map_font(const ASS_FontMapping *map, int len, const char *name,
1115 ASS_FontProviderMetaData *meta)
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;