1 // -*- c-basic-offset: 8; indent-tabs-mode: t -*-
2 // vim:ts=8:sw=8:noet:ai:
4 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
6 * This file is part of libass.
8 * libass is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * libass is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with libass; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <sys/types.h>
33 #include FT_FREETYPE_H
37 #include "ass_library.h"
38 #include "ass_fontconfig.h"
40 #ifdef CONFIG_FONTCONFIG
41 #include <fontconfig/fontconfig.h>
42 #include <fontconfig/fcfreetype.h>
45 struct fc_instance_s
{
46 #ifdef CONFIG_FONTCONFIG
54 #ifdef CONFIG_FONTCONFIG
56 // 4yo fontconfig does not have these.
57 // They are only needed for debug output, anyway.
59 #define FC_FULLNAME "fullname"
62 #define FC_EMBOLDEN "embolden"
66 * \brief Low-level font selection.
67 * \param priv private data
68 * \param family font family
69 * \param treat_family_as_pattern treat family as fontconfig pattern
70 * \param bold font weight value
71 * \param italic font slant value
72 * \param index out: font index inside a file
73 * \param code: the character that should be present in the font, can be 0
74 * \return font file path
76 static char* _select_font(fc_instance_t
* priv
, const char* family
, int treat_family_as_pattern
,
77 unsigned bold
, unsigned italic
, int* index
, uint32_t code
)
81 FcPattern
*pat
= NULL
, *rpat
= NULL
;
82 int r_index
, r_slant
, r_weight
;
83 FcChar8
*r_family
, *r_style
, *r_file
, *r_fullname
;
84 FcBool r_outline
, r_embolden
;
86 FcFontSet
* fset
= NULL
;
93 if (treat_family_as_pattern
)
94 pat
= FcNameParse((const FcChar8
*)family
);
96 pat
= FcPatternCreate();
101 if (!treat_family_as_pattern
) {
102 FcPatternAddString(pat
, FC_FAMILY
, (const FcChar8
*)family
);
104 // In SSA/ASS fonts are sometimes referenced by their "full name",
105 // which is usually a concatenation of family name and font
106 // style (ex. Ottawa Bold). Full name is available from
107 // FontConfig pattern element FC_FULLNAME, but it is never
108 // used for font matching.
109 // Therefore, I'm removing words from the end of the name one
110 // by one, and adding shortened names to the pattern. It seems
111 // that the first value (full name in this case) has
112 // precedence in matching.
113 // An alternative approach could be to reimplement FcFontSort
114 // using FC_FULLNAME instead of FC_FAMILY.
117 char* s
= strdup(family
);
118 char* p
= s
+ strlen(s
);
120 if (*p
== ' ' || *p
== '-') {
122 FcPatternAddString(pat
, FC_FAMILY
, (const FcChar8
*)s
);
128 FcPatternAddBool(pat
, FC_OUTLINE
, FcTrue
);
129 FcPatternAddInteger(pat
, FC_SLANT
, italic
);
130 FcPatternAddInteger(pat
, FC_WEIGHT
, bold
);
132 FcDefaultSubstitute(pat
);
134 rc
= FcConfigSubstitute(priv
->config
, pat
, FcMatchPattern
);
138 fset
= FcFontSort(priv
->config
, pat
, FcTrue
, NULL
, &result
);
142 for (curf
= 0; curf
< fset
->nfont
; ++curf
) {
143 FcPattern
* curp
= fset
->fonts
[curf
];
145 result
= FcPatternGetBool(curp
, FC_OUTLINE
, 0, &r_outline
);
146 if (result
!= FcResultMatch
)
148 if (r_outline
!= FcTrue
)
152 result
= FcPatternGetCharSet(curp
, FC_CHARSET
, 0, &r_charset
);
153 if (result
!= FcResultMatch
)
155 if (FcCharSetHasChar(r_charset
, code
))
159 if (curf
>= fset
->nfont
)
162 #if (FC_VERSION >= 20297)
163 if (!treat_family_as_pattern
) {
164 // Remove all extra family names from original pattern.
165 // After this, FcFontRenderPrepare will select the most relevant family
166 // name in case there are more than one of them.
167 for (; family_cnt
> 1; --family_cnt
)
168 FcPatternRemove(pat
, FC_FAMILY
, family_cnt
- 1);
172 rpat
= FcFontRenderPrepare(priv
->config
, pat
, fset
->fonts
[curf
]);
176 result
= FcPatternGetInteger(rpat
, FC_INDEX
, 0, &r_index
);
177 if (result
!= FcResultMatch
)
181 result
= FcPatternGetString(rpat
, FC_FILE
, 0, &r_file
);
182 if (result
!= FcResultMatch
)
184 retval
= strdup((const char*)r_file
);
186 result
= FcPatternGetString(rpat
, FC_FAMILY
, 0, &r_family
);
187 if (result
!= FcResultMatch
)
190 result
= FcPatternGetString(rpat
, FC_FULLNAME
, 0, &r_fullname
);
191 if (result
!= FcResultMatch
)
194 if (!treat_family_as_pattern
&&
195 !(r_family
&& strcasecmp((const char*)r_family
, family
) == 0) &&
196 !(r_fullname
&& strcasecmp((const char*)r_fullname
, family
) == 0))
197 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne
,
198 (const char*)(r_fullname
? r_fullname
: r_family
), family
);
200 result
= FcPatternGetString(rpat
, FC_STYLE
, 0, &r_style
);
201 if (result
!= FcResultMatch
)
204 result
= FcPatternGetInteger(rpat
, FC_SLANT
, 0, &r_slant
);
205 if (result
!= FcResultMatch
)
208 result
= FcPatternGetInteger(rpat
, FC_WEIGHT
, 0, &r_weight
);
209 if (result
!= FcResultMatch
)
212 result
= FcPatternGetBool(rpat
, FC_EMBOLDEN
, 0, &r_embolden
);
213 if (result
!= FcResultMatch
)
216 mp_msg(MSGT_ASS
, MSGL_V
, "[ass] Font info: family '%s', style '%s', fullname '%s',"
217 " slant %d, weight %d%s\n",
218 (const char*)r_family
, (const char*)r_style
, (const char*)r_fullname
,
219 r_slant
, r_weight
, r_embolden
? ", embolden" : "");
222 if (pat
) FcPatternDestroy(pat
);
223 if (rpat
) FcPatternDestroy(rpat
);
224 if (fset
) FcFontSetDestroy(fset
);
229 * \brief Find a font. Use default family or path if necessary.
230 * \param priv_ private data
231 * \param family font family
232 * \param treat_family_as_pattern treat family as fontconfig pattern
233 * \param bold font weight value
234 * \param italic font slant value
235 * \param index out: font index inside a file
236 * \param code: the character that should be present in the font, can be 0
237 * \return font file path
239 char* fontconfig_select(fc_instance_t
* priv
, const char* family
, int treat_family_as_pattern
,
240 unsigned bold
, unsigned italic
, int* index
, uint32_t code
)
244 *index
= priv
->index_default
;
245 return priv
->path_default
;
247 if (family
&& *family
)
248 res
= _select_font(priv
, family
, treat_family_as_pattern
, bold
, italic
, index
, code
);
249 if (!res
&& priv
->family_default
) {
250 res
= _select_font(priv
, priv
->family_default
, 0, bold
, italic
, index
, code
);
252 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingDefaultFontFamily
,
253 family
, bold
, italic
, res
, *index
);
255 if (!res
&& priv
->path_default
) {
256 res
= priv
->path_default
;
257 *index
= priv
->index_default
;
258 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingDefaultFont
,
259 family
, bold
, italic
, res
, *index
);
262 res
= _select_font(priv
, "Arial", 0, bold
, italic
, index
, code
);
264 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingArialFontFamily
,
265 family
, bold
, italic
, res
, *index
);
268 mp_msg(MSGT_ASS
, MSGL_V
, "fontconfig_select: (%s, %d, %d) -> %s, %d\n",
269 family
, bold
, italic
, res
, *index
);
273 #if (FC_VERSION < 20402)
274 static char* validate_fname(char* name
)
280 int sz
= strlen(name
);
282 q
= fname
= malloc(sz
+ 1);
285 code
= utf8_get_char(&p
);
288 if ( (code
> 0x7F) ||
312 * \brief Process memory font.
313 * \param priv private data
314 * \param library library object
315 * \param ftlibrary freetype library object
316 * \param idx index of the processed font in library->fontdata
317 * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
318 * With older FontConfig versions, save the font to ~/.mplayer/fonts.
320 static void process_fontdata(fc_instance_t
* priv
, ass_library_t
* library
, FT_Library ftlibrary
, int idx
)
323 const char* name
= library
->fontdata
[idx
].name
;
324 const char* data
= library
->fontdata
[idx
].data
;
325 int data_size
= library
->fontdata
[idx
].size
;
327 #if (FC_VERSION < 20402)
330 const char* fonts_dir
= library
->fonts_dir
;
336 rc
= stat(fonts_dir
, &st
);
340 res
= mkdir(fonts_dir
, 0700);
342 res
= mkdir(fonts_dir
);
345 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FailedToCreateDirectory
, fonts_dir
);
347 } else if (!S_ISDIR(st
.st_mode
)) {
348 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_NotADirectory
, fonts_dir
);
351 fname
= validate_fname((char*)name
);
353 snprintf(buf
, 1000, "%s/%s", fonts_dir
, fname
);
356 fp
= fopen(buf
, "wb");
359 fwrite(data
, data_size
, 1, fp
);
362 #else // (FC_VERSION >= 20402)
367 int face_index
, num_faces
= 1;
369 for (face_index
= 0; face_index
< num_faces
; ++face_index
) {
370 rc
= FT_New_Memory_Face(ftlibrary
, (unsigned char*)data
, data_size
, face_index
, &face
);
372 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_ErrorOpeningMemoryFont
, name
);
375 num_faces
= face
->num_faces
;
377 pattern
= FcFreeTypeQueryFace(face
, (unsigned char*)name
, 0, FcConfigGetBlanks(priv
->config
));
379 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcFreeTypeQueryFace");
384 fset
= FcConfigGetFonts(priv
->config
, FcSetSystem
); // somehow it failes when asked for FcSetApplication
386 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcConfigGetFonts");
391 res
= FcFontSetAdd(fset
, pattern
);
393 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcFontSetAdd");
404 * \brief Init fontconfig.
405 * \param library libass library object
406 * \param ftlibrary freetype library object
407 * \param family default font family
408 * \param path default font path
409 * \return pointer to fontconfig private data
411 fc_instance_t
* fontconfig_init(ass_library_t
* library
, FT_Library ftlibrary
, const char* family
, const char* path
, int fc
)
414 fc_instance_t
* priv
= calloc(1, sizeof(fc_instance_t
));
415 const char* dir
= library
->fonts_dir
;
419 mp_msg(MSGT_ASS
, MSGL_WARN
,
420 MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed
);
427 priv
->config
= FcConfigGetCurrent();
429 mp_msg(MSGT_ASS
, MSGL_FATAL
, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed
);
433 for (i
= 0; i
< library
->num_fontdata
; ++i
)
434 process_fontdata(priv
, library
, ftlibrary
, i
);
437 if (FcDirCacheValid((const FcChar8
*)dir
) == FcFalse
)
439 mp_msg(MSGT_ASS
, MSGL_INFO
, MSGTR_LIBASS_UpdatingFontCache
);
440 if (FcGetVersion() >= 20390 && FcGetVersion() < 20400)
441 mp_msg(MSGT_ASS
, MSGL_WARN
,
442 MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported
);
443 // FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir()
444 if (FcGetVersion() < 20390) {
447 fcs
= FcFontSetCreate();
448 fss
= FcStrSetCreate();
449 rc
= FcStrSetAdd(fss
, (const FcChar8
*)dir
);
451 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcStrSetAddFailed
);
455 rc
= FcDirScan(fcs
, fss
, NULL
, FcConfigGetBlanks(priv
->config
),
456 (const FcChar8
*)dir
, FcFalse
);
458 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcDirScanFailed
);
462 rc
= FcDirSave(fcs
, fss
, (const FcChar8
*)dir
);
464 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcDirSave
);
472 rc
= FcConfigAppFontAddDir(priv
->config
, (const FcChar8
*)dir
);
474 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcConfigAppFontAddDirFailed
);
478 priv
->family_default
= family
? strdup(family
) : NULL
;
480 priv
->path_default
= path
? strdup(path
) : NULL
;
481 priv
->index_default
= 0;
486 #else /* CONFIG_FONTCONFIG */
488 char* fontconfig_select(fc_instance_t
* priv
, const char* family
, int treat_family_as_pattern
,
489 unsigned bold
, unsigned italic
, int* index
, uint32_t code
)
491 *index
= priv
->index_default
;
492 return priv
->path_default
;
495 fc_instance_t
* fontconfig_init(ass_library_t
* library
, FT_Library ftlibrary
, const char* family
, const char* path
, int fc
)
499 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed
);
501 priv
= calloc(1, sizeof(fc_instance_t
));
503 priv
->path_default
= strdup(path
);
504 priv
->index_default
= 0;
510 void fontconfig_done(fc_instance_t
* priv
)
512 // don't call FcFini() here, library can still be used by some code
513 if (priv
&& priv
->path_default
) free(priv
->path_default
);
514 if (priv
&& priv
->family_default
) free(priv
->family_default
);
515 if (priv
) free(priv
);