2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
4 * This file is part of libass.
6 * libass is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * libass is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with libass; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <sys/types.h>
31 #include FT_FREETYPE_H
33 #include "ass_utils.h"
35 #include "ass_library.h"
36 #include "ass_fontconfig.h"
38 #ifdef CONFIG_FONTCONFIG
39 #include <fontconfig/fontconfig.h>
40 #include <fontconfig/fcfreetype.h>
44 #ifdef CONFIG_FONTCONFIG
52 #ifdef CONFIG_FONTCONFIG
55 * \brief Low-level font selection.
56 * \param priv private data
57 * \param family font family
58 * \param treat_family_as_pattern treat family as fontconfig pattern
59 * \param bold font weight value
60 * \param italic font slant value
61 * \param index out: font index inside a file
62 * \param code: the character that should be present in the font, can be 0
63 * \return font file path
65 static char *_select_font(ASS_Library
*library
, FCInstance
*priv
,
66 const char *family
, int treat_family_as_pattern
,
67 unsigned bold
, unsigned italic
, int *index
,
72 FcPattern
*pat
= NULL
, *rpat
= NULL
;
73 int r_index
, r_slant
, r_weight
;
74 FcChar8
*r_family
, *r_style
, *r_file
, *r_fullname
;
75 FcBool r_outline
, r_embolden
;
77 FcFontSet
*fset
= NULL
;
84 if (treat_family_as_pattern
)
85 pat
= FcNameParse((const FcChar8
*) family
);
87 pat
= FcPatternCreate();
92 if (!treat_family_as_pattern
) {
93 FcPatternAddString(pat
, FC_FAMILY
, (const FcChar8
*) family
);
95 // In SSA/ASS fonts are sometimes referenced by their "full name",
96 // which is usually a concatenation of family name and font
97 // style (ex. Ottawa Bold). Full name is available from
98 // FontConfig pattern element FC_FULLNAME, but it is never
99 // used for font matching.
100 // Therefore, I'm removing words from the end of the name one
101 // by one, and adding shortened names to the pattern. It seems
102 // that the first value (full name in this case) has
103 // precedence in matching.
104 // An alternative approach could be to reimplement FcFontSort
105 // using FC_FULLNAME instead of FC_FAMILY.
108 char *s
= strdup(family
);
109 char *p
= s
+ strlen(s
);
111 if (*p
== ' ' || *p
== '-') {
113 FcPatternAddString(pat
, FC_FAMILY
, (const FcChar8
*) s
);
119 FcPatternAddBool(pat
, FC_OUTLINE
, FcTrue
);
120 FcPatternAddInteger(pat
, FC_SLANT
, italic
);
121 FcPatternAddInteger(pat
, FC_WEIGHT
, bold
);
123 FcDefaultSubstitute(pat
);
125 rc
= FcConfigSubstitute(priv
->config
, pat
, FcMatchPattern
);
129 fset
= FcFontSort(priv
->config
, pat
, FcTrue
, NULL
, &result
);
133 for (curf
= 0; curf
< fset
->nfont
; ++curf
) {
134 FcPattern
*curp
= fset
->fonts
[curf
];
136 result
= FcPatternGetBool(curp
, FC_OUTLINE
, 0, &r_outline
);
137 if (result
!= FcResultMatch
)
139 if (r_outline
!= FcTrue
)
143 result
= FcPatternGetCharSet(curp
, FC_CHARSET
, 0, &r_charset
);
144 if (result
!= FcResultMatch
)
146 if (FcCharSetHasChar(r_charset
, code
))
150 if (curf
>= fset
->nfont
)
153 if (!treat_family_as_pattern
) {
154 // Remove all extra family names from original pattern.
155 // After this, FcFontRenderPrepare will select the most relevant family
156 // name in case there are more than one of them.
157 for (; family_cnt
> 1; --family_cnt
)
158 FcPatternRemove(pat
, FC_FAMILY
, family_cnt
- 1);
161 rpat
= FcFontRenderPrepare(priv
->config
, pat
, fset
->fonts
[curf
]);
165 result
= FcPatternGetInteger(rpat
, FC_INDEX
, 0, &r_index
);
166 if (result
!= FcResultMatch
)
170 result
= FcPatternGetString(rpat
, FC_FILE
, 0, &r_file
);
171 if (result
!= FcResultMatch
)
173 retval
= strdup((const char *) r_file
);
175 result
= FcPatternGetString(rpat
, FC_FAMILY
, 0, &r_family
);
176 if (result
!= FcResultMatch
)
179 result
= FcPatternGetString(rpat
, FC_FULLNAME
, 0, &r_fullname
);
180 if (result
!= FcResultMatch
)
183 if (!treat_family_as_pattern
&&
184 !(r_family
&& strcasecmp((const char *) r_family
, family
) == 0) &&
185 !(r_fullname
&& strcasecmp((const char *) r_fullname
, family
) == 0))
186 ass_msg(library
, MSGL_WARN
,
187 "fontconfig: Selected font is not the requested one: "
189 (const char *) (r_fullname
? r_fullname
: r_family
), family
);
191 result
= FcPatternGetString(rpat
, FC_STYLE
, 0, &r_style
);
192 if (result
!= FcResultMatch
)
195 result
= FcPatternGetInteger(rpat
, FC_SLANT
, 0, &r_slant
);
196 if (result
!= FcResultMatch
)
199 result
= FcPatternGetInteger(rpat
, FC_WEIGHT
, 0, &r_weight
);
200 if (result
!= FcResultMatch
)
203 result
= FcPatternGetBool(rpat
, FC_EMBOLDEN
, 0, &r_embolden
);
204 if (result
!= FcResultMatch
)
207 ass_msg(library
, MSGL_V
,
208 "Font info: family '%s', style '%s', fullname '%s',"
209 " slant %d, weight %d%s", (const char *) r_family
,
210 (const char *) r_style
, (const char *) r_fullname
, r_slant
,
211 r_weight
, r_embolden
? ", embolden" : "");
215 FcPatternDestroy(pat
);
217 FcPatternDestroy(rpat
);
219 FcFontSetDestroy(fset
);
224 * \brief Find a font. Use default family or path if necessary.
225 * \param priv_ private data
226 * \param family font family
227 * \param treat_family_as_pattern treat family as fontconfig pattern
228 * \param bold font weight value
229 * \param italic font slant value
230 * \param index out: font index inside a file
231 * \param code: the character that should be present in the font, can be 0
232 * \return font file path
234 char *fontconfig_select(ASS_Library
*library
, FCInstance
*priv
,
235 const char *family
, int treat_family_as_pattern
,
236 unsigned bold
, unsigned italic
, int *index
,
241 *index
= priv
->index_default
;
242 res
= priv
->path_default
? strdup(priv
->path_default
) : 0;
245 if (family
&& *family
)
247 _select_font(library
, priv
, family
, treat_family_as_pattern
,
248 bold
, italic
, index
, code
);
249 if (!res
&& priv
->family_default
) {
251 _select_font(library
, priv
, priv
->family_default
, 0, bold
,
252 italic
, index
, code
);
254 ass_msg(library
, MSGL_WARN
, "fontconfig_select: Using default "
255 "font family: (%s, %d, %d) -> %s, %d",
256 family
, bold
, italic
, res
, *index
);
258 if (!res
&& priv
->path_default
) {
259 res
= strdup(priv
->path_default
);
260 *index
= priv
->index_default
;
261 ass_msg(library
, MSGL_WARN
, "fontconfig_select: Using default font: "
262 "(%s, %d, %d) -> %s, %d", family
, bold
, italic
,
266 res
= _select_font(library
, priv
, "Arial", 0, bold
, italic
,
269 ass_msg(library
, MSGL_WARN
, "fontconfig_select: Using 'Arial' "
270 "font family: (%s, %d, %d) -> %s, %d", family
, bold
,
271 italic
, res
, *index
);
274 ass_msg(library
, MSGL_V
,
275 "fontconfig_select: (%s, %d, %d) -> %s, %d", family
, bold
,
276 italic
, res
, *index
);
281 * \brief Process memory font.
282 * \param priv private data
283 * \param library library object
284 * \param ftlibrary freetype library object
285 * \param idx index of the processed font in library->fontdata
286 * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
287 * With older FontConfig versions, save the font to ~/.mplayer/fonts.
289 static void process_fontdata(FCInstance
*priv
, ASS_Library
*library
,
290 FT_Library ftlibrary
, int idx
)
293 const char *name
= library
->fontdata
[idx
].name
;
294 const char *data
= library
->fontdata
[idx
].data
;
295 int data_size
= library
->fontdata
[idx
].size
;
301 int face_index
, num_faces
= 1;
303 for (face_index
= 0; face_index
< num_faces
; ++face_index
) {
304 rc
= FT_New_Memory_Face(ftlibrary
, (unsigned char *) data
,
305 data_size
, face_index
, &face
);
307 ass_msg(library
, MSGL_WARN
, "Error opening memory font: %s",
311 num_faces
= face
->num_faces
;
314 FcFreeTypeQueryFace(face
, (unsigned char *) name
, 0,
315 FcConfigGetBlanks(priv
->config
));
317 ass_msg(library
, MSGL_WARN
, "%s failed", "FcFreeTypeQueryFace");
322 fset
= FcConfigGetFonts(priv
->config
, FcSetSystem
); // somehow it failes when asked for FcSetApplication
324 ass_msg(library
, MSGL_WARN
, "%s failed", "FcConfigGetFonts");
329 res
= FcFontSetAdd(fset
, pattern
);
331 ass_msg(library
, MSGL_WARN
, "%s failed", "FcFontSetAdd");
341 * \brief Init fontconfig.
342 * \param library libass library object
343 * \param ftlibrary freetype library object
344 * \param family default font family
345 * \param path default font path
346 * \param fc whether fontconfig should be used
347 * \param config path to a fontconfig configuration file, or NULL
348 * \param update whether the fontconfig cache should be built/updated
349 * \return pointer to fontconfig private data
351 FCInstance
*fontconfig_init(ASS_Library
*library
,
352 FT_Library ftlibrary
, const char *family
,
353 const char *path
, int fc
, const char *config
,
357 FCInstance
*priv
= calloc(1, sizeof(FCInstance
));
358 const char *dir
= library
->fonts_dir
;
362 ass_msg(library
, MSGL_WARN
,
363 "Fontconfig disabled, only default font will be used.");
367 priv
->config
= FcConfigCreate();
368 rc
= FcConfigParseAndLoad(priv
->config
, (unsigned char *) config
, FcTrue
);
370 ass_msg(library
, MSGL_WARN
, "No usable fontconfig configuration "
371 "file found, using fallback.");
372 FcConfigDestroy(priv
->config
);
373 priv
->config
= FcInitLoadConfig();
377 FcConfigBuildFonts(priv
->config
);
380 if (!rc
|| !priv
->config
) {
381 ass_msg(library
, MSGL_FATAL
,
382 "No valid fontconfig configuration found!");
383 FcConfigDestroy(priv
->config
);
387 for (i
= 0; i
< library
->num_fontdata
; ++i
)
388 process_fontdata(priv
, library
, ftlibrary
, i
);
391 ass_msg(library
, MSGL_INFO
, "Updating font cache");
393 rc
= FcConfigAppFontAddDir(priv
->config
, (const FcChar8
*) dir
);
395 ass_msg(library
, MSGL_WARN
, "%s failed", "FcConfigAppFontAddDir");
399 priv
->family_default
= family
? strdup(family
) : NULL
;
401 priv
->path_default
= path
? strdup(path
) : NULL
;
402 priv
->index_default
= 0;
407 int fontconfig_update(FCInstance
*priv
)
409 return FcConfigBuildFonts(priv
->config
);
412 #else /* CONFIG_FONTCONFIG */
414 char *fontconfig_select(ASS_Library
*library
, FCInstance
*priv
,
415 const char *family
, int treat_family_as_pattern
,
416 unsigned bold
, unsigned italic
, int *index
,
419 *index
= priv
->index_default
;
420 char* res
= priv
->path_default
? strdup(priv
->path_default
) : 0;
424 FCInstance
*fontconfig_init(ASS_Library
*library
,
425 FT_Library ftlibrary
, const char *family
,
426 const char *path
, int fc
, const char *config
,
431 ass_msg(library
, MSGL_WARN
,
432 "Fontconfig disabled, only default font will be used.");
434 priv
= calloc(1, sizeof(FCInstance
));
436 priv
->path_default
= path
? strdup(path
) : 0;
437 priv
->index_default
= 0;
441 int fontconfig_update(FCInstance
*priv
)
449 void fontconfig_done(FCInstance
*priv
)
451 #ifdef CONFIG_FONTCONFIG
452 if (priv
&& priv
->config
)
453 FcConfigDestroy(priv
->config
);
455 if (priv
&& priv
->path_default
)
456 free(priv
->path_default
);
457 if (priv
&& priv
->family_default
)
458 free(priv
->family_default
);