[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