vf_drawtext: fontconfig support.
authorNicolas George <nicolas.george@normalesup.org>
Sat, 7 Apr 2012 13:09:16 +0000 (15:09 +0200)
committerNicolas George <nicolas.george@normalesup.org>
Tue, 10 Apr 2012 13:16:47 +0000 (15:16 +0200)
Changelog
configure
doc/filters.texi
libavfilter/vf_drawtext.c

index 78c8219..f03600b 100644 (file)
--- 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:
index 5ff5292..419d252 100755 (executable)
--- 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
index 5d19fed..d166445 100644 (file)
@@ -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.
index 43b04e0..5cfe6bf 100644 (file)
@@ -48,6 +48,9 @@
 #include <freetype/config/ftheader.h>
 #include FT_FREETYPE_H
 #include FT_GLYPH_H
+#if CONFIG_FONTCONFIG
+#include <fontconfig/fontconfig.h>
+#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));