consistency cosmetics
[mplayer/greg.git] / libass / ass_fontconfig.c
blobaddcc826468920e21d4ebbc3bb616f3529c021ee
1 // -*- c-basic-offset: 8; indent-tabs-mode: t -*-
2 // vim:ts=8:sw=8:noet:ai:
3 /*
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
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <inttypes.h>
30 #include <ft2build.h>
31 #include FT_FREETYPE_H
33 #include "mputils.h"
34 #include "ass.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>
41 #endif
43 extern int font_fontconfig;
45 struct fc_instance_s {
46 #ifdef HAVE_FONTCONFIG
47 FcConfig* config;
48 #endif
49 char* family_default;
50 char* path_default;
51 int index_default;
54 #ifdef HAVE_FONTCONFIG
55 /**
56 * \brief Low-level font selection.
57 * \param priv private data
58 * \param family font family
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
64 */
65 static char* _select_font(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index,
66 uint32_t code)
68 FcBool rc;
69 FcResult result;
70 FcPattern *pat = 0, *rpat;
71 int val_i;
72 FcChar8* val_s;
73 FcBool val_b;
74 FcCharSet* val_cs;
75 FcFontSet* fset = 0;
76 int curf;
77 char* retval = 0;
79 *index = 0;
81 pat = FcPatternCreate();
82 if (!pat)
83 goto error;
85 FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)family);
86 FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
87 FcPatternAddInteger(pat, FC_SLANT, italic);
88 FcPatternAddInteger(pat, FC_WEIGHT, bold);
90 FcDefaultSubstitute(pat);
92 rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern);
93 if (!rc)
94 goto error;
96 fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result);
98 for (curf = 0; curf < fset->nfont; ++curf) {
99 rpat = fset->fonts[curf];
101 result = FcPatternGetBool(rpat, FC_OUTLINE, 0, &val_b);
102 if (result != FcResultMatch)
103 continue;
104 if (val_b != FcTrue)
105 continue;
106 if (!code)
107 break;
108 result = FcPatternGetCharSet(rpat, FC_CHARSET, 0, &val_cs);
109 if (result != FcResultMatch)
110 continue;
111 if (FcCharSetHasChar(val_cs, code))
112 break;
115 if (curf >= fset->nfont)
116 goto error;
118 rpat = fset->fonts[curf];
120 result = FcPatternGetInteger(rpat, FC_INDEX, 0, &val_i);
121 if (result != FcResultMatch)
122 goto error;
123 *index = val_i;
125 result = FcPatternGetString(rpat, FC_FAMILY, 0, &val_s);
126 if (result != FcResultMatch)
127 goto error;
129 if (strcasecmp((const char*)val_s, family) != 0)
130 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne,
131 (const char*)val_s, family);
133 result = FcPatternGetString(rpat, FC_FILE, 0, &val_s);
134 if (result != FcResultMatch)
135 goto error;
137 retval = strdup((const char*)val_s);
138 error:
139 if (pat) FcPatternDestroy(pat);
140 if (fset) FcFontSetDestroy(fset);
141 return retval;
145 * \brief Find a font. Use default family or path if necessary.
146 * \param priv_ private data
147 * \param family font family
148 * \param bold font weight value
149 * \param italic font slant value
150 * \param index out: font index inside a file
151 * \param code: the character that should be present in the font, can be 0
152 * \return font file path
154 char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index,
155 uint32_t code)
157 char* res = 0;
158 if (font_fontconfig < 0) {
159 *index = priv->index_default;
160 return priv->path_default;
162 if (family && *family)
163 res = _select_font(priv, family, bold, italic, index, code);
164 if (!res && priv->family_default) {
165 res = _select_font(priv, priv->family_default, bold, italic, index, code);
166 if (res)
167 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily,
168 family, bold, italic, res, *index);
170 if (!res && priv->path_default) {
171 res = priv->path_default;
172 *index = priv->index_default;
173 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont,
174 family, bold, italic, res, *index);
176 if (!res) {
177 res = _select_font(priv, "Arial", bold, italic, index, code);
178 if (res)
179 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily,
180 family, bold, italic, res, *index);
182 if (res)
183 mp_msg(MSGT_ASS, MSGL_V, "fontconfig_select: (%s, %d, %d) -> %s, %d\n",
184 family, bold, italic, res, *index);
185 return res;
188 #if (FC_VERSION < 20402)
189 static char* validate_fname(char* name)
191 char* fname;
192 char* p;
193 char* q;
194 unsigned code;
195 int sz = strlen(name);
197 q = fname = malloc(sz + 1);
198 p = name;
199 while (*p) {
200 code = utf8_get_char(&p);
201 if (code == 0)
202 break;
203 if ( (code > 0x7F) ||
204 (code == '\\') ||
205 (code == '/') ||
206 (code == ':') ||
207 (code == '*') ||
208 (code == '?') ||
209 (code == '<') ||
210 (code == '>') ||
211 (code == '|') ||
212 (code == 0))
214 *q++ = '_';
215 } else {
216 *q++ = code;
218 if (p - name > sz)
219 break;
221 *q = 0;
222 return fname;
224 #endif
227 * \brief Process memory font.
228 * \param priv private data
229 * \param library library object
230 * \param ftlibrary freetype library object
231 * \param idx index of the processed font in library->fontdata
232 * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
233 * With older FontConfig versions, save the font to ~/.mplayer/fonts.
235 static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Library ftlibrary, int idx)
237 int rc;
238 const char* name = library->fontdata[idx].name;
239 const char* data = library->fontdata[idx].data;
240 int data_size = library->fontdata[idx].size;
242 #if (FC_VERSION < 20402)
243 struct stat st;
244 char* fname;
245 const char* fonts_dir = library->fonts_dir;
246 char buf[1000];
247 FILE* fp = 0;
249 if (!fonts_dir)
250 return;
251 rc = stat(fonts_dir, &st);
252 if (rc) {
253 int res;
254 #ifndef __MINGW32__
255 res = mkdir(fonts_dir, 0700);
256 #else
257 res = mkdir(fonts_dir);
258 #endif
259 if (res) {
260 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FailedToCreateDirectory, fonts_dir);
262 } else if (!S_ISDIR(st.st_mode)) {
263 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir);
266 fname = validate_fname((char*)name);
268 snprintf(buf, 1000, "%s/%s", fonts_dir, fname);
269 free(fname);
271 fp = fopen(buf, "wb");
272 if (!fp) return;
274 fwrite(data, data_size, 1, fp);
275 fclose(fp);
277 #else // (FC_VERSION >= 20402)
278 FT_Face face;
279 FcPattern* pattern;
280 FcFontSet* fset;
281 FcBool res;
283 rc = FT_New_Memory_Face(ftlibrary, (unsigned char*)data, data_size, 0, &face);
284 if (rc) {
285 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, name);
286 return;
289 pattern = FcFreeTypeQueryFace(face, (unsigned char*)name, 0, FcConfigGetBlanks(priv->config));
290 if (!pattern) {
291 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFreeTypeQueryFace");
292 FT_Done_Face(face);
293 return;
296 fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication
297 if (!fset) {
298 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcConfigGetFonts");
299 FT_Done_Face(face);
300 return;
303 res = FcFontSetAdd(fset, pattern);
304 if (!res) {
305 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFontSetAdd");
306 FT_Done_Face(face);
307 return;
310 FT_Done_Face(face);
311 #endif
315 * \brief Init fontconfig.
316 * \param library libass library object
317 * \param ftlibrary freetype library object
318 * \param family default font family
319 * \param path default font path
320 * \return pointer to fontconfig private data
322 fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path)
324 int rc;
325 fc_instance_t* priv = calloc(1, sizeof(fc_instance_t));
326 const char* dir = library->fonts_dir;
327 int i;
329 if (font_fontconfig < 0) {
330 mp_msg(MSGT_ASS, MSGL_WARN,
331 MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed);
332 priv->path_default = strdup(path);
333 priv->index_default = 0;
334 return priv;
337 rc = FcInit();
338 assert(rc);
340 priv->config = FcConfigGetCurrent();
341 if (!priv->config) {
342 mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed);
343 return 0;
346 for (i = 0; i < library->num_fontdata; ++i)
347 process_fontdata(priv, library, ftlibrary, i);
349 if (FcDirCacheValid((const FcChar8 *)dir) == FcFalse)
351 mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache);
352 if (FcGetVersion() >= 20390 && FcGetVersion() < 20400)
353 mp_msg(MSGT_ASS, MSGL_WARN,
354 MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported);
355 // FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir()
356 if (FcGetVersion() < 20390) {
357 FcFontSet* fcs;
358 FcStrSet* fss;
359 fcs = FcFontSetCreate();
360 fss = FcStrSetCreate();
361 rc = FcStrSetAdd(fss, (const FcChar8*)dir);
362 if (!rc) {
363 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcStrSetAddFailed);
364 goto ErrorFontCache;
367 rc = FcDirScan(fcs, fss, NULL, FcConfigGetBlanks(priv->config), (const FcChar8 *)dir, FcFalse);
368 if (!rc) {
369 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirScanFailed);
370 goto ErrorFontCache;
373 rc = FcDirSave(fcs, fss, (const FcChar8 *)dir);
374 if (!rc) {
375 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirSave);
376 goto ErrorFontCache;
378 ErrorFontCache:
383 rc = FcConfigAppFontAddDir(priv->config, (const FcChar8*)dir);
384 if (!rc) {
385 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcConfigAppFontAddDirFailed);
388 priv->family_default = family ? strdup(family) : 0;
389 priv->path_default = path ? strdup(path) : 0;
390 priv->index_default = 0;
392 return priv;
395 #else // HAVE_FONTCONFIG
397 char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index,
398 uint32_t code)
400 *index = priv->index_default;
401 return priv->path_default;
404 fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path)
406 fc_instance_t* priv;
408 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed);
410 priv = calloc(1, sizeof(fc_instance_t));
412 priv->path_default = strdup(path);
413 priv->index_default = 0;
414 return priv;
417 #endif
419 void fontconfig_done(fc_instance_t* priv)
421 // don't call FcFini() here, library can still be used by some code
422 if (priv && priv->path_default) free(priv->path_default);
423 if (priv && priv->family_default) free(priv->family_default);
424 if (priv) free(priv);