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 program 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 This program 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
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include <sys/types.h>
31 #include FT_FREETYPE_H
35 #include "ass_library.h"
36 #include "ass_fontconfig.h"
38 #ifdef HAVE_FONTCONFIG
39 #include <fontconfig/fontconfig.h>
40 #include <fontconfig/fcfreetype.h>
43 struct fc_instance_s
{
44 #ifdef HAVE_FONTCONFIG
52 #ifdef HAVE_FONTCONFIG
54 * \brief Low-level font selection.
55 * \param priv private data
56 * \param family font family
57 * \param bold font weight value
58 * \param italic font slant value
59 * \param index out: font index inside a file
60 * \param code: the character that should be present in the font, can be 0
61 * \return font file path
63 static char* _select_font(fc_instance_t
* priv
, const char* family
, unsigned bold
, unsigned italic
, int* index
,
68 FcPattern
*pat
= 0, *rpat
;
79 pat
= FcPatternCreate();
83 FcPatternAddString(pat
, FC_FAMILY
, (const FcChar8
*)family
);
84 FcPatternAddBool(pat
, FC_OUTLINE
, FcTrue
);
85 FcPatternAddInteger(pat
, FC_SLANT
, italic
);
86 FcPatternAddInteger(pat
, FC_WEIGHT
, bold
);
88 FcDefaultSubstitute(pat
);
90 rc
= FcConfigSubstitute(priv
->config
, pat
, FcMatchPattern
);
94 fset
= FcFontSort(priv
->config
, pat
, FcTrue
, NULL
, &result
);
96 for (curf
= 0; curf
< fset
->nfont
; ++curf
) {
97 rpat
= fset
->fonts
[curf
];
99 result
= FcPatternGetBool(rpat
, FC_OUTLINE
, 0, &val_b
);
100 if (result
!= FcResultMatch
)
106 result
= FcPatternGetCharSet(rpat
, FC_CHARSET
, 0, &val_cs
);
107 if (result
!= FcResultMatch
)
109 if (FcCharSetHasChar(val_cs
, code
))
113 if (curf
>= fset
->nfont
)
116 rpat
= fset
->fonts
[curf
];
118 result
= FcPatternGetInteger(rpat
, FC_INDEX
, 0, &val_i
);
119 if (result
!= FcResultMatch
)
123 result
= FcPatternGetString(rpat
, FC_FAMILY
, 0, &val_s
);
124 if (result
!= FcResultMatch
)
127 if (strcasecmp((const char*)val_s
, family
) != 0)
128 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne
,
129 (const char*)val_s
, family
);
131 result
= FcPatternGetString(rpat
, FC_FILE
, 0, &val_s
);
132 if (result
!= FcResultMatch
)
135 retval
= strdup((const char*)val_s
);
137 if (pat
) FcPatternDestroy(pat
);
138 if (fset
) FcFontSetDestroy(fset
);
143 * \brief Find a font. Use default family or path if necessary.
144 * \param priv_ private data
145 * \param family font family
146 * \param bold font weight value
147 * \param italic font slant value
148 * \param index out: font index inside a file
149 * \param code: the character that should be present in the font, can be 0
150 * \return font file path
152 char* fontconfig_select(fc_instance_t
* priv
, const char* family
, unsigned bold
, unsigned italic
, int* index
,
156 if (family
&& *family
)
157 res
= _select_font(priv
, family
, bold
, italic
, index
, code
);
158 if (!res
&& priv
->family_default
) {
159 res
= _select_font(priv
, priv
->family_default
, bold
, italic
, index
, code
);
161 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingDefaultFontFamily
,
162 family
, bold
, italic
, res
, *index
);
164 if (!res
&& priv
->path_default
) {
165 res
= priv
->path_default
;
166 *index
= priv
->index_default
;
167 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingDefaultFont
,
168 family
, bold
, italic
, res
, *index
);
171 res
= _select_font(priv
, "Arial", bold
, italic
, index
, code
);
173 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UsingArialFontFamily
,
174 family
, bold
, italic
, res
, *index
);
177 mp_msg(MSGT_ASS
, MSGL_V
, "fontconfig_select: (%s, %d, %d) -> %s, %d\n",
178 family
, bold
, italic
, res
, *index
);
182 #if (FC_VERSION < 20402)
183 static char* validate_fname(char* name
)
189 int sz
= strlen(name
);
191 q
= fname
= malloc(sz
+ 1);
194 code
= utf8_get_char(&p
);
197 if ( (code
> 0x7F) ||
221 * \brief Process memory font.
222 * \param priv private data
223 * \param library library object
224 * \param ftlibrary freetype library object
225 * \param idx index of the processed font in library->fontdata
226 * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
227 * With older FontConfig versions, save the font to ~/.mplayer/fonts.
229 static void process_fontdata(fc_instance_t
* priv
, ass_library_t
* library
, FT_Library ftlibrary
, int idx
)
232 const char* name
= library
->fontdata
[idx
].name
;
233 const char* data
= library
->fontdata
[idx
].data
;
234 int data_size
= library
->fontdata
[idx
].size
;
236 #if (FC_VERSION < 20402)
239 const char* fonts_dir
= library
->fonts_dir
;
245 rc
= stat(fonts_dir
, &st
);
249 res
= mkdir(fonts_dir
, 0700);
251 res
= mkdir(fonts_dir
);
254 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FailedToCreateDirectory
, fonts_dir
);
256 } else if (!S_ISDIR(st
.st_mode
)) {
257 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_NotADirectory
, fonts_dir
);
260 fname
= validate_fname((char*)name
);
262 snprintf(buf
, 1000, "%s/%s", fonts_dir
, fname
);
265 fp
= fopen(buf
, "wb");
268 fwrite(data
, data_size
, 1, fp
);
271 #else // (FC_VERSION >= 20402)
277 rc
= FT_New_Memory_Face(ftlibrary
, (unsigned char*)data
, data_size
, 0, &face
);
279 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_ErrorOpeningMemoryFont
, name
);
283 pattern
= FcFreeTypeQueryFace(face
, (unsigned char*)name
, 0, FcConfigGetBlanks(priv
->config
));
285 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcFreeTypeQueryFace");
290 fset
= FcConfigGetFonts(priv
->config
, FcSetSystem
); // somehow it failes when asked for FcSetApplication
292 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcConfigGetFonts");
297 res
= FcFontSetAdd(fset
, pattern
);
299 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FunctionCallFailed
, "FcFontSetAdd");
309 * \brief Init fontconfig.
310 * \param library libass library object
311 * \param ftlibrary freetype library object
312 * \param family default font family
313 * \param path default font path
314 * \return pointer to fontconfig private data
316 fc_instance_t
* fontconfig_init(ass_library_t
* library
, FT_Library ftlibrary
, const char* family
, const char* path
)
319 fc_instance_t
* priv
= calloc(1, sizeof(fc_instance_t
));
320 const char* dir
= library
->fonts_dir
;
326 priv
->config
= FcConfigGetCurrent();
328 mp_msg(MSGT_ASS
, MSGL_FATAL
, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed
);
332 for (i
= 0; i
< library
->num_fontdata
; ++i
)
333 process_fontdata(priv
, library
, ftlibrary
, i
);
335 if (FcDirCacheValid((const FcChar8
*)dir
) == FcFalse
)
337 mp_msg(MSGT_ASS
, MSGL_INFO
, MSGTR_LIBASS_UpdatingFontCache
);
338 if (FcGetVersion() >= 20390 && FcGetVersion() < 20400)
339 mp_msg(MSGT_ASS
, MSGL_WARN
,
340 MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported
);
341 // FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir()
342 if (FcGetVersion() < 20390) {
345 fcs
= FcFontSetCreate();
346 fss
= FcStrSetCreate();
347 rc
= FcStrSetAdd(fss
, (const FcChar8
*)dir
);
349 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcStrSetAddFailed
);
353 rc
= FcDirScan(fcs
, fss
, NULL
, FcConfigGetBlanks(priv
->config
), (const FcChar8
*)dir
, FcFalse
);
355 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcDirScanFailed
);
359 rc
= FcDirSave(fcs
, fss
, (const FcChar8
*)dir
);
361 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcDirSave
);
369 rc
= FcConfigAppFontAddDir(priv
->config
, (const FcChar8
*)dir
);
371 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FcConfigAppFontAddDirFailed
);
374 priv
->family_default
= family
? strdup(family
) : 0;
375 priv
->path_default
= path
? strdup(path
) : 0;
376 priv
->index_default
= 0;
381 #else // HAVE_FONTCONFIG
383 char* fontconfig_select(fc_instance_t
* priv
, const char* family
, unsigned bold
, unsigned italic
, int* index
,
386 *index
= priv
->index_default
;
387 return priv
->path_default
;
390 fc_instance_t
* fontconfig_init(ass_library_t
* library
, FT_Library ftlibrary
, const char* family
, const char* path
)
394 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed
);
396 priv
= calloc(1, sizeof(fc_instance_t
));
398 priv
->path_default
= strdup(path
);
399 priv
->index_default
= 0;
405 void fontconfig_done(fc_instance_t
* priv
)
407 // don't call FcFini() here, library can still be used by some code
408 if (priv
&& priv
->path_default
) free(priv
->path_default
);
409 if (priv
&& priv
->family_default
) free(priv
->family_default
);
410 if (priv
) free(priv
);