1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-17 04:15:13 +00:00
mpv/libass/ass_fontconfig.c
eugeni 92256c07d1 Open embedded fonts directly from memory.
FontConfig 2.4.2 (released yesterday) supports scanning fonts with
FcFreeTypeQueryFace without writing them to disk. With earlier FontConfig
versions, the old mechanism is used.


git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@21476 b3059339-0415-0410-9bf9-f77b7e298cf2
2006-12-03 18:24:11 +00:00

420 lines
10 KiB
C

// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/*
Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "mputils.h"
#include "ass.h"
#include "ass_library.h"
#include "ass_fontconfig.h"
#ifdef HAVE_FONTCONFIG
#include <fontconfig/fontconfig.h>
#include <fontconfig/fcfreetype.h>
#endif
struct fc_instance_s {
#ifdef HAVE_FONTCONFIG
FcConfig* config;
#endif
char* family_default;
char* path_default;
int index_default;
};
#ifdef HAVE_FONTCONFIG
/**
* \brief Low-level font selection.
* \param priv private data
* \param family font family
* \param bold font weight value
* \param italic font slant value
* \param index out: font index inside a file
* \return font file path
*/
static char* _select_font(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index,
FcCharSet* charset)
{
FcBool rc;
FcResult result;
FcPattern *pat, *rpat;
int val_i;
FcChar8* val_s;
FcBool val_b;
FcCharSet* val_cs;
FcFontSet* fset;
int curf, bestf, bestdiff = 0;
*index = 0;
pat = FcPatternCreate();
if (!pat)
return 0;
FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)family);
FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
FcPatternAddInteger(pat, FC_SLANT, italic);
FcPatternAddInteger(pat, FC_WEIGHT, bold);
FcDefaultSubstitute(pat);
rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern);
if (!rc)
return 0;
fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result);
bestf = -1;
if (charset)
bestdiff = FcCharSetCount(charset) + 1;
for (curf = 0; curf < fset->nfont; ++curf) {
rpat = fset->fonts[curf];
result = FcPatternGetBool(rpat, FC_OUTLINE, 0, &val_b);
if (result != FcResultMatch)
continue;
if (val_b != FcTrue)
continue;
if (charset) {
int diff;
result = FcPatternGetCharSet(rpat, FC_CHARSET, 0, &val_cs);
if (result != FcResultMatch)
continue;
diff = FcCharSetSubtractCount(charset, val_cs);
if (diff < bestdiff) {
bestdiff = diff;
bestf = curf;
}
if (diff == 0)
break;
} else {
bestf = curf;
break;
}
}
if (bestf < 0)
return 0;
rpat = fset->fonts[bestf];
result = FcPatternGetInteger(rpat, FC_INDEX, 0, &val_i);
if (result != FcResultMatch)
return 0;
*index = val_i;
result = FcPatternGetString(rpat, FC_FAMILY, 0, &val_s);
if (result != FcResultMatch)
return 0;
if (strcasecmp((const char*)val_s, family) != 0)
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne,
(const char*)val_s, family);
result = FcPatternGetString(rpat, FC_FILE, 0, &val_s);
if (result != FcResultMatch)
return 0;
return strdup((const char*)val_s);
}
/**
* \brief Find a font. Use default family or path if necessary.
* \param priv_ private data
* \param family font family
* \param bold font weight value
* \param italic font slant value
* \param index out: font index inside a file
* \return font file path
*/
char* fontconfig_select_with_charset(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index,
FcCharSet* charset)
{
char* res = 0;
if (family && *family)
res = _select_font(priv, family, bold, italic, index, charset);
if (!res && priv->family_default) {
res = _select_font(priv, priv->family_default, bold, italic, index, charset);
if (res)
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily,
family, bold, italic, res, *index);
}
if (!res && priv->path_default) {
res = priv->path_default;
*index = priv->index_default;
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont,
family, bold, italic, res, *index);
}
if (!res) {
res = _select_font(priv, "Arial", bold, italic, index, charset);
if (res)
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily,
family, bold, italic, res, *index);
}
if (res)
mp_msg(MSGT_ASS, MSGL_V, "fontconfig_select: (%s, %d, %d) -> %s, %d\n",
family, bold, italic, res, *index);
return res;
}
char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index)
{
return fontconfig_select_with_charset(priv, family, bold, italic, index, 0);
}
static char* validate_fname(char* name)
{
char* fname;
char* p;
char* q;
unsigned code;
int sz = strlen(name);
q = fname = malloc(sz + 1);
p = name;
while (*p) {
code = utf8_get_char(&p);
if (code == 0)
break;
if ( (code > 0x7F) ||
(code == '\\') ||
(code == '/') ||
(code == ':') ||
(code == '*') ||
(code == '?') ||
(code == '<') ||
(code == '>') ||
(code == '|') ||
(code == 0))
{
*q++ = '_';
} else {
*q++ = code;
}
if (p - name > sz)
break;
}
*q = 0;
return fname;
}
/**
* \brief Process embedded matroska font. Saves it to ~/.mplayer/fonts.
* \param name attachment name
* \param data binary font data
* \param data_size data size
*/
static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Library ftlibrary, int idx)
{
char buf[1000];
FILE* fp = 0;
int rc;
struct stat st;
char* fname;
const char* name = library->fontdata[idx].name;
const char* data = library->fontdata[idx].data;
int data_size = library->fontdata[idx].size;
const char* fonts_dir = library->fonts_dir;
FT_Face face;
FcPattern* pattern;
FcFontSet* fset;
FcBool res;
#if (FC_VERSION < 20402)
if (!fonts_dir)
return;
rc = stat(fonts_dir, &st);
if (rc) {
int res;
#ifndef __MINGW32__
res = mkdir(fonts_dir, 0700);
#else
res = mkdir(fonts_dir);
#endif
if (res) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FailedToCreateDirectory, fonts_dir);
}
} else if (!S_ISDIR(st.st_mode)) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir);
}
fname = validate_fname((char*)name);
snprintf(buf, 1000, "%s/%s", fonts_dir, fname);
free(fname);
fp = fopen(buf, "wb");
if (!fp) return;
fwrite(data, data_size, 1, fp);
fclose(fp);
#else // (FC_VERSION >= 20402)
rc = FT_New_Memory_Face(ftlibrary, data, data_size, 0, &face);
if (rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, name);
return;
}
pattern = FcFreeTypeQueryFace(face, name, 0, FcConfigGetBlanks(priv->config));
if (!pattern) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFreeTypeQueryFace");
FT_Done_Face(face);
return;
}
fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication
if (!fset) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcConfigGetFonts");
FT_Done_Face(face);
return;
}
res = FcFontSetAdd(fset, pattern);
if (!res) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFontSetAdd");
FT_Done_Face(face);
return;
}
FT_Done_Face(face);
#endif
}
/**
* \brief Init fontconfig.
* \param dir additional directoryu for fonts
* \param family default font family
* \param path default font path
* \return pointer to fontconfig private data
*/
fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path)
{
int rc;
struct stat st;
fc_instance_t* priv = calloc(1, sizeof(fc_instance_t));
const char* dir = library->fonts_dir;
int i;
rc = FcInit();
assert(rc);
priv->config = FcConfigGetCurrent();
if (!priv->config) {
mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed);
return 0;
}
for (i = 0; i < library->num_fontdata; ++i)
process_fontdata(priv, library, ftlibrary, i);
if (FcDirCacheValid((const FcChar8 *)dir) == FcFalse)
{
mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache);
if (FcGetVersion() >= 20390 && FcGetVersion() < 20400)
mp_msg(MSGT_ASS, MSGL_WARN,
MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported);
// FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir()
if (FcGetVersion() < 20390) {
FcFontSet* fcs;
FcStrSet* fss;
fcs = FcFontSetCreate();
fss = FcStrSetCreate();
rc = FcStrSetAdd(fss, (const FcChar8*)dir);
if (!rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcStrSetAddFailed);
goto ErrorFontCache;
}
rc = FcDirScan(fcs, fss, NULL, FcConfigGetBlanks(priv->config), (const FcChar8 *)dir, FcFalse);
if (!rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirScanFailed);
goto ErrorFontCache;
}
rc = FcDirSave(fcs, fss, (const FcChar8 *)dir);
if (!rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirSave);
goto ErrorFontCache;
}
ErrorFontCache:
;
}
}
rc = FcConfigAppFontAddDir(priv->config, (const FcChar8*)dir);
if (!rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcConfigAppFontAddDirFailed);
}
priv->family_default = family ? strdup(family) : 0;
priv->index_default = 0;
rc = stat(path, &st);
if (!rc && S_ISREG(st.st_mode))
priv->path_default = path ? strdup(path) : 0;
else
priv->path_default = 0;
return priv;
}
#else
char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index)
{
*index = priv->index_default;
return priv->path_default;
}
fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path)
{
fc_instance_t* priv;
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed);
priv = calloc(1, sizeof(fc_instance_t));
priv->path_default = strdup(path);
priv->index_default = 0;
return priv;
}
#endif
void fontconfig_done(fc_instance_t* priv)
{
// don't call FcFini() here, library can still be used by some code
if (priv && priv->path_default) free(priv->path_default);
if (priv && priv->family_default) free(priv->family_default);
if (priv) free(priv);
}