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 bold font weight value
70 * \param italic font slant value
71 * \param index out: font index inside a file
72 * \param code: the character that should be present in the font, can be 0
73 * \return font file path
75 static char* _select_font(fc_instance_t
* priv
, const char* family
, unsigned bold
, unsigned italic
, int* index
,
80 FcPattern
*pat
= NULL
, *rpat
= NULL
;
81 int r_index
, r_slant
, r_weight
;
82 FcChar8
*r_family
, *r_style
, *r_file
, *r_fullname
;
83 FcBool r_outline
, r_embolden
;
85 FcFontSet
* fset
= NULL
;
92 pat
= FcPatternCreate();
96 FcPatternAddString(pat
, FC_FAMILY
, (const FcChar8
*)family
);
98 // In SSA/ASS fonts are sometimes referenced by their "full name",
99 // which is usually a concatenation of family name and font
100 // style (ex. Ottawa Bold). Full name is available from
101 // FontConfig pattern element FC_FULLNAME, but it is never
102 // used for font matching.
103 // Therefore, I'm removing words from the end of the name one
104 // by one, and adding shortened names to the pattern. It seems
105 // that the first value (full name in this case) has
106 // precedence in matching.
107 // An alternative approach could be to reimplement FcFontSort
108 // using FC_FULLNAME instead of FC_FAMILY.
111 char* s
= strdup(family
);
112 char* p
= s
+ strlen(s
);
114 if (*p
== ' ' || *p
== '-') {
116 FcPatternAddString(pat
, FC_FAMILY
, (const FcChar8
*)s
);
121 FcPatternAddBool(pat
, FC_OUTLINE
, FcTrue
);
122 FcPatternAddInteger(pat
, FC_SLANT
, italic
);
123 FcPatternAddInteger(pat
, FC_WEIGHT
, bold
);
125 FcDefaultSubstitute(pat
);
127 rc
= FcConfigSubstitute(priv
->config
, pat
, FcMatchPattern
);
131 fset
= FcFontSort(priv
->config
, pat
, FcTrue
, NULL
, &result
);
135 for (curf
= 0; curf
< fset
->nfont
; ++curf
) {
136 FcPattern
* curp
= fset
->fonts
[curf
];
138 result
= FcPatternGetBool(curp
, FC_OUTLINE
, 0, &r_outline
);
139 if (result
!= FcResultMatch
)
141 if (r_outline
!= FcTrue
)
145 result
= FcPatternGetCharSet(curp
, FC_CHARSET
, 0, &r_charset
);
146 if (result
!= FcResultMatch
)
148 if (FcCharSetHasChar(r_charset
, code
))
152 if (curf
>= fset
->nfont
)
155 #if (FC_VERSION >= 20297)
156 // Remove all extra family names from original pattern.
157 // After this, FcFontRenderPrepare will select the most relevant family
158 // name in case there are more than one of them.
159 for (; family_cnt
> 1; --family_cnt
)
160 FcPatternRemove(pat
, FC_FAMILY
, family_cnt
- 1);
163 rpat
= FcFontRenderPrepare(priv
->config
, pat
, fset
->fonts
[curf
]);
167 result
= FcPatternGetInteger(rpat
, FC_INDEX
, 0, &r_index
);
168 if (result
!= FcResultMatch
)
172 result
= FcPatternGetString(rpat
, FC_FILE
, 0, &r_file
);
173 if (result
!= FcResultMatch
)
175 retval
= strdup((const char*)r_file
);
177 result
= FcPatternGetString(rpat
, FC_FAMILY
, 0, &r_family
);
178 if (result
!= FcResultMatch
)
181 result
= FcPatternGetString(rpat
, FC_FULLNAME
, 0, &r_fullname
);
182 if (result
!= FcResultMatch
)
185 if (!(r_family
&& strcasecmp((const char*)r_family
, family
) == 0) &&
186 !(r_fullname
&& strcasecmp((const char*)r_fullname
, family
) == 0))
187 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne
,
188 (const char*)(r_fullname
? r_fullname
: r_family
), family
);
190 result
= FcPatternGetString(rpat
, FC_STYLE
, 0, &r_style
);
191 if (result
!= FcResultMatch
)
194 result
= FcPatternGetInteger(rpat
, FC_SLANT
, 0, &r_slant
);
195 if (result
!= FcResultMatch
)
198 result
= FcPatternGetInteger(rpat
, FC_WEIGHT
, 0, &r_weight
);
199 if (result
!= FcResultMatch
)
202 result
= FcPatternGetBool(rpat
, FC_EMBOLDEN
, 0, &r_embolden
);
203 if (result
!= FcResultMatch
)
206 mp_msg(MSGT_ASS
, MSGL_V
, "[ass] Font info: family '%s', style '%s', fullname '%s',"
207 " slant %d, weight %d%s\n",
208 (const char*)r_family
, (const char*)r_style
, (const char*)r_fullname
,
209 r_slant
, r_weight
, r_embolden
? ", embolden" : "");
212 if (pat
) FcPatternDestroy(pat
);
213 if (rpat
) FcPatternDestroy(rpat
);
214 if (fset
) FcFontSetDestroy(fset
);
219 * \brief Find a font. Use default family or path if necessary.
220 * \param priv_ private data
221 * \param family font family
222 * \param bold font weight value
223 * \param italic font slant value
224 * \param index out: font index inside a file
225 * \param code: the character that should be present in the font, can be 0
226 * \return font file path
228 char* fontconfig_select(fc_instance_t
* priv
, const char* family
, unsigned bold
, unsigned italic
, int* index
,
233 *index
= priv
->index_default
;
234 return priv
->path_default
;
236 if (family
&& *family
)
237 res
= _select_font(priv
, family
, bold
, italic
, index
, code
);
238 if (!res
&& priv
->family_default
) {
239 res
= _select_font(priv
, priv
->family_default
, bold
, italic
, index
, code
);
241 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingDefaultFontFamily
,
242 family
, bold
, italic
, res
, *index
);
244 if (!res
&& priv
->path_default
) {
245 res
= priv
->path_default
;
246 *index
= priv
->index_default
;
247 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingDefaultFont
,
248 family
, bold
, italic
, res
, *index
);
251 res
= _select_font(priv
, "Arial", bold
, italic
, index
, code
);
253 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingArialFontFamily
,
254 family
, bold
, italic
, res
, *index
);
257 mp_msg(MSGT_ASS
, MSGL_V
, "fontconfig_select: (%s, %d, %d) -> %s, %d\n",
258 family
, bold
, italic
, res
, *index
);
262 #if (FC_VERSION < 20402)
263 static char* validate_fname(char* name
)
269 int sz
= strlen(name
);
271 q
= fname
= malloc(sz
+ 1);
274 code
= utf8_get_char(&p
);
277 if ( (code
> 0x7F) ||
301 * \brief Process memory font.
302 * \param priv private data
303 * \param library library object
304 * \param ftlibrary freetype library object
305 * \param idx index of the processed font in library->fontdata
306 * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
307 * With older FontConfig versions, save the font to ~/.mplayer/fonts.
309 static void process_fontdata(fc_instance_t
* priv
, ass_library_t
* library
, FT_Library ftlibrary
, int idx
)
312 const char* name
= library
->fontdata
[idx
].name
;
313 const char* data
= library
->fontdata
[idx
].data
;
314 int data_size
= library
->fontdata
[idx
].size
;
316 #if (FC_VERSION < 20402)
319 const char* fonts_dir
= library
->fonts_dir
;
325 rc
= stat(fonts_dir
, &st
);
329 res
= mkdir(fonts_dir
, 0700);
331 res
= mkdir(fonts_dir
);
334 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FailedToCreateDirectory
, fonts_dir
);
336 } else if (!S_ISDIR(st
.st_mode
)) {
337 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_NotADirectory
, fonts_dir
);
340 fname
= validate_fname((char*)name
);
342 snprintf(buf
, 1000, "%s/%s", fonts_dir
, fname
);
345 fp
= fopen(buf
, "wb");
348 fwrite(data
, data_size
, 1, fp
);
351 #else // (FC_VERSION >= 20402)
356 int face_index
, num_faces
= 1;
358 for (face_index
= 0; face_index
< num_faces
; ++face_index
) {
359 rc
= FT_New_Memory_Face(ftlibrary
, (unsigned char*)data
, data_size
, face_index
, &face
);
361 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_ErrorOpeningMemoryFont
, name
);
364 num_faces
= face
->num_faces
;
366 pattern
= FcFreeTypeQueryFace(face
, (unsigned char*)name
, 0, FcConfigGetBlanks(priv
->config
));
368 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcFreeTypeQueryFace");
373 fset
= FcConfigGetFonts(priv
->config
, FcSetSystem
); // somehow it failes when asked for FcSetApplication
375 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcConfigGetFonts");
380 res
= FcFontSetAdd(fset
, pattern
);
382 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcFontSetAdd");
393 * \brief Init fontconfig.
394 * \param library libass library object
395 * \param ftlibrary freetype library object
396 * \param family default font family
397 * \param path default font path
398 * \return pointer to fontconfig private data
400 fc_instance_t
* fontconfig_init(ass_library_t
* library
, FT_Library ftlibrary
, const char* family
, const char* path
, int fc
)
403 fc_instance_t
* priv
= calloc(1, sizeof(fc_instance_t
));
404 const char* dir
= library
->fonts_dir
;
408 mp_msg(MSGT_ASS
, MSGL_WARN
,
409 MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed
);
416 priv
->config
= FcConfigGetCurrent();
418 mp_msg(MSGT_ASS
, MSGL_FATAL
, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed
);
422 for (i
= 0; i
< library
->num_fontdata
; ++i
)
423 process_fontdata(priv
, library
, ftlibrary
, i
);
426 if (FcDirCacheValid((const FcChar8
*)dir
) == FcFalse
)
428 mp_msg(MSGT_ASS
, MSGL_INFO
, MSGTR_LIBASS_UpdatingFontCache
);
429 if (FcGetVersion() >= 20390 && FcGetVersion() < 20400)
430 mp_msg(MSGT_ASS
, MSGL_WARN
,
431 MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported
);
432 // FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir()
433 if (FcGetVersion() < 20390) {
436 fcs
= FcFontSetCreate();
437 fss
= FcStrSetCreate();
438 rc
= FcStrSetAdd(fss
, (const FcChar8
*)dir
);
440 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcStrSetAddFailed
);
444 rc
= FcDirScan(fcs
, fss
, NULL
, FcConfigGetBlanks(priv
->config
),
445 (const FcChar8
*)dir
, FcFalse
);
447 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcDirScanFailed
);
451 rc
= FcDirSave(fcs
, fss
, (const FcChar8
*)dir
);
453 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcDirSave
);
461 rc
= FcConfigAppFontAddDir(priv
->config
, (const FcChar8
*)dir
);
463 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcConfigAppFontAddDirFailed
);
467 priv
->family_default
= family
? strdup(family
) : NULL
;
469 priv
->path_default
= path
? strdup(path
) : NULL
;
470 priv
->index_default
= 0;
475 #else /* CONFIG_FONTCONFIG */
477 char* fontconfig_select(fc_instance_t
* priv
, const char* family
, unsigned bold
, unsigned italic
, int* index
,
480 *index
= priv
->index_default
;
481 return priv
->path_default
;
484 fc_instance_t
* fontconfig_init(ass_library_t
* library
, FT_Library ftlibrary
, const char* family
, const char* path
, int fc
)
488 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed
);
490 priv
= calloc(1, sizeof(fc_instance_t
));
492 priv
->path_default
= strdup(path
);
493 priv
->index_default
= 0;
499 void fontconfig_done(fc_instance_t
* priv
)
501 // don't call FcFini() here, library can still be used by some code
502 if (priv
&& priv
->path_default
) free(priv
->path_default
);
503 if (priv
&& priv
->family_default
) free(priv
->family_default
);
504 if (priv
) free(priv
);