[FFmpeg-devel] [PATCH 2/2] vf_drawtext: fontconfig support.

Stefano Sabatini stefasab at gmail.com
Sun Apr 8 12:16:40 CEST 2012


On date Saturday 2012-04-07 16:46:49 +0200, Nicolas George encoded: 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  configure                 |    3 +
>  doc/filters.texi          |    9 ++++
>  libavfilter/vf_drawtext.c |  104 +++++++++++++++++++++++++++++++++++++++++---
>  3 files changed, 108 insertions(+), 8 deletions(-)
> 
> diff --git a/configure b/configure
> index 049708a..b4414d0 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 5d19fed..72a3e2b 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
> + at option{fontfile} can be a fontconfig pattern or omitted.
> +

A link to the doc/site of fontconfig may be useful somewhere.

>  Some examples follow.
>  
>  @itemize
> @@ -1467,6 +1470,12 @@ 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
>  
> + at item
> +Use fontconfig to set the font. Note that the colons need to be escaped.
> + at example
> +drawtext='fontfile=Linux Libertine O-40\\:style=Semibold:text=FFmpeg'
> + at end example
> +
>  @end itemize
>  
>  For more information about libfreetype, check:
> diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
> index 43b04e0..ba8b585 100644
> --- a/libavfilter/vf_drawtext.c
> +++ b/libavfilter/vf_drawtext.c
> @@ -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";

Nit: Capitalized, and use final dots consistently with the rest of drawtext.


> +        return AVERROR(EINVAL);
> +    }

> +    pattern = FcNameParse(dtext->fontfile ? dtext->fontfile :
> +                          (uint8_t *)(intptr_t)"default");

ugh, is this cast really required?

> +    if (!pattern) {
> +        *error = "error parsing fontconfig pattern";

1+i*nit: I'd avoid the term "error" in error messages, otherwise you
end up with log messages like this:
"Error occurred with fontconfig: error parsing fontconfig pattern";

which should rather be:
"Error occurred with fontconfig: could not parse fontconfig pattern";

which is less redundant and more specific.

> +        return AVERROR(EINVAL);
> +    }
> +    if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
> +        *error = "error substuting 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) {

nit+++: vertical align

> +        *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;

nit: s/err/ret/, if (ret >= 0) return ret;
more readable to me

> +#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;
> +}

I don't like very much the error message parsing but I can't see any
way to avoid that.

> +
>  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,17 +469,17 @@ 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));
>          return AVERROR(EINVAL);
>      }

> +    return 0;
>  
>      dtext->use_kerning = FT_HAS_KERNING(dtext->face);

stray return?
-- 
FFmpeg = Fabulous Free Magic Political Evil Ghost


More information about the ffmpeg-devel mailing list