diff --git a/Changelog b/Changelog index 78c8219032..f03600bac7 100644 --- a/Changelog +++ b/Changelog @@ -23,6 +23,7 @@ version next: - OpenEXR image decoder - removelogo filter - drop support for ffmpeg without libavfilter +- drawtext video filter: fontconfig support version 0.10: diff --git a/configure b/configure index 5ff5292d2f..419d2526e1 100755 --- a/configure +++ b/configure @@ -166,6 +166,7 @@ Individual component options: External library support: --enable-avisynth enable reading of AVISynth script files [no] --enable-bzlib enable bzlib [autodetect] + --enable-fontconfig enable fontconfig --enable-frei0r enable frei0r video filtering --enable-gnutls enable gnutls [no] --enable-libaacplus enable AAC+ encoding via libaacplus [no] @@ -1022,6 +1023,7 @@ CONFIG_LIST=" dxva2 fastdiv fft + fontconfig frei0r gnutls gpl @@ -3164,6 +3166,7 @@ check_mathfunc truncf # these are off by default, so fail if requested and not available enabled avisynth && require2 vfw32 "windows.h vfw.h" AVIFileInit -lavifil32 +enabled fontconfig && require_pkg_config fontconfig "fontconfig/fontconfig.h" FcInit enabled frei0r && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; } enabled gnutls && require_pkg_config gnutls gnutls/gnutls.h gnutls_global_init enabled libaacplus && require "libaacplus >= 2.0.0" aacplus.h aacplusEncOpen -laacplus diff --git a/doc/filters.texi b/doc/filters.texi index 5d19fed00d..d16644582b 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -1414,6 +1414,9 @@ with or without text parameter. @var{rate} option must be specified. frame rate (timecode only) @end table +If libavfilter was built with @code{--enable-fontconfig}, then +@option{fontfile} can be a fontconfig pattern or omitted. + Some examples follow. @itemize @@ -1467,11 +1470,20 @@ The glyph baseline is placed at half screen height. drawtext=fontsize=60:fontfile=FreeSerif.ttf:fontcolor=green:text=g:x=(w-max_glyph_w)/2:y=h/2-ascent @end example +@item +Use fontconfig to set the font. Note that the colons need to be escaped. +@example +drawtext='fontfile=Linux Libertine O-40\\:style=Semibold:text=FFmpeg' +@end example + @end itemize For more information about libfreetype, check: @url{http://www.freetype.org/}. +For more information about fontconfig, check: +@url{http://freedesktop.org/software/fontconfig/fontconfig-user.html}. + @section fade Apply fade-in/out effect to input video. diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 43b04e01e6..5cfe6bf262 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -48,6 +48,9 @@ #include #include FT_FREETYPE_H #include FT_GLYPH_H +#if CONFIG_FONTCONFIG +#include +#endif static const char *const var_names[] = { "main_w", "w", "W", ///< width of the input video @@ -167,7 +170,7 @@ static const AVOption drawtext_options[]= { {"boxcolor", "set box color", OFFSET(boxcolor_string), AV_OPT_TYPE_STRING, {.str="white"}, CHAR_MIN, CHAR_MAX }, {"shadowcolor", "set shadow color", OFFSET(shadowcolor_string), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX }, {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 }, -{"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.dbl=16}, 1, INT_MAX }, +{"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.dbl=0}, 0, INT_MAX }, {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX }, {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX }, {"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX }, @@ -298,6 +301,91 @@ error: return ret; } +static int load_font_file(AVFilterContext *ctx, const char *path, int index, + const char **error) +{ + DrawTextContext *dtext = ctx->priv; + int err; + + err = FT_New_Face(dtext->library, path, index, &dtext->face); + if (err) { + *error = FT_ERRMSG(err); + return AVERROR(EINVAL); + } + return 0; +} + +#if CONFIG_FONTCONFIG +static int load_font_fontconfig(AVFilterContext *ctx, const char **error) +{ + DrawTextContext *dtext = ctx->priv; + FcConfig *fontconfig; + FcPattern *pattern, *fpat; + FcResult result = FcResultMatch; + FcChar8 *filename; + int err, index; + double size; + + fontconfig = FcInitLoadConfigAndFonts(); + if (!fontconfig) { + *error = "impossible to init fontconfig\n"; + return AVERROR(EINVAL); + } + pattern = FcNameParse(dtext->fontfile ? dtext->fontfile : + (uint8_t *)(intptr_t)"default"); + if (!pattern) { + *error = "could not parse fontconfig pattern"; + return AVERROR(EINVAL); + } + if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) { + *error = "could not substitue fontconfig options"; /* very unlikely */ + return AVERROR(EINVAL); + } + FcDefaultSubstitute(pattern); + fpat = FcFontMatch(fontconfig, pattern, &result); + if (!fpat || result != FcResultMatch) { + *error = "impossible to find a matching font"; + return AVERROR(EINVAL); + } + if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch || + FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch || + FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) { + *error = "impossible to find font information"; + return AVERROR(EINVAL); + } + av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename); + if (!dtext->fontsize) + dtext->fontsize = size + 0.5; + err = load_font_file(ctx, filename, index, error); + if (err) + return err; + FcPatternDestroy(fpat); + FcPatternDestroy(pattern); + FcConfigDestroy(fontconfig); + return 0; +} +#endif + +static int load_font(AVFilterContext *ctx) +{ + DrawTextContext *dtext = ctx->priv; + int err; + const char *error = "unknown error\n"; + + /* load the face, and set up the encoding, which is by default UTF-8 */ + err = load_font_file(ctx, dtext->fontfile, 0, &error); + if (!err) + return 0; +#if CONFIG_FONTCONFIG + err = load_font_fontconfig(ctx, &error); + if (!err) + return 0; +#endif + av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n", + dtext->fontfile, error); + return err; +} + static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) { int err; @@ -312,7 +400,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) return err; } - if (!dtext->fontfile) { + if (!dtext->fontfile && !CONFIG_FONTCONFIG) { av_log(ctx, AV_LOG_ERROR, "No font filename provided\n"); return AVERROR(EINVAL); } @@ -381,12 +469,11 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) return AVERROR(EINVAL); } - /* load the face, and set up the encoding, which is by default UTF-8 */ - if ((err = FT_New_Face(dtext->library, dtext->fontfile, 0, &dtext->face))) { - av_log(ctx, AV_LOG_ERROR, "Could not load fontface from file '%s': %s\n", - dtext->fontfile, FT_ERRMSG(err)); - return AVERROR(EINVAL); - } + err = load_font(ctx); + if (err) + return err; + if (!dtext->fontsize) + dtext->fontsize = 16; if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) { av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n", dtext->fontsize, FT_ERRMSG(err));