Browse code

vf_drawtext: fontconfig support.

Nicolas George authored on 2012/04/07 22:09:16
Showing 4 changed files
... ...
@@ -23,6 +23,7 @@ version next:
23 23
 - OpenEXR image decoder
24 24
 - removelogo filter
25 25
 - drop support for ffmpeg without libavfilter
26
+- drawtext video filter: fontconfig support
26 27
 
27 28
 
28 29
 version 0.10:
... ...
@@ -166,6 +166,7 @@ Individual component options:
166 166
 External library support:
167 167
   --enable-avisynth        enable reading of AVISynth script files [no]
168 168
   --enable-bzlib           enable bzlib [autodetect]
169
+  --enable-fontconfig      enable fontconfig
169 170
   --enable-frei0r          enable frei0r video filtering
170 171
   --enable-gnutls          enable gnutls [no]
171 172
   --enable-libaacplus      enable AAC+ encoding via libaacplus [no]
... ...
@@ -1022,6 +1023,7 @@ CONFIG_LIST="
1022 1022
     dxva2
1023 1023
     fastdiv
1024 1024
     fft
1025
+    fontconfig
1025 1026
     frei0r
1026 1027
     gnutls
1027 1028
     gpl
... ...
@@ -3164,6 +3166,7 @@ check_mathfunc truncf
3164 3164
 
3165 3165
 # these are off by default, so fail if requested and not available
3166 3166
 enabled avisynth   && require2 vfw32 "windows.h vfw.h" AVIFileInit -lavifil32
3167
+enabled fontconfig && require_pkg_config fontconfig "fontconfig/fontconfig.h" FcInit
3167 3168
 enabled frei0r     && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; }
3168 3169
 enabled gnutls     && require_pkg_config gnutls gnutls/gnutls.h gnutls_global_init
3169 3170
 enabled libaacplus && require  "libaacplus >= 2.0.0" aacplus.h aacplusEncOpen -laacplus
... ...
@@ -1414,6 +1414,9 @@ with or without text parameter. @var{rate} option must be specified.
1414 1414
 frame rate (timecode only)
1415 1415
 @end table
1416 1416
 
1417
+If libavfilter was built with @code{--enable-fontconfig}, then
1418
+@option{fontfile} can be a fontconfig pattern or omitted.
1419
+
1417 1420
 Some examples follow.
1418 1421
 
1419 1422
 @itemize
... ...
@@ -1467,11 +1470,20 @@ The glyph baseline is placed at half screen height.
1467 1467
 drawtext=fontsize=60:fontfile=FreeSerif.ttf:fontcolor=green:text=g:x=(w-max_glyph_w)/2:y=h/2-ascent
1468 1468
 @end example
1469 1469
 
1470
+@item
1471
+Use fontconfig to set the font. Note that the colons need to be escaped.
1472
+@example
1473
+drawtext='fontfile=Linux Libertine O-40\\:style=Semibold:text=FFmpeg'
1474
+@end example
1475
+
1470 1476
 @end itemize
1471 1477
 
1472 1478
 For more information about libfreetype, check:
1473 1479
 @url{http://www.freetype.org/}.
1474 1480
 
1481
+For more information about fontconfig, check:
1482
+@url{http://freedesktop.org/software/fontconfig/fontconfig-user.html}.
1483
+
1475 1484
 @section fade
1476 1485
 
1477 1486
 Apply fade-in/out effect to input video.
... ...
@@ -48,6 +48,9 @@
48 48
 #include <freetype/config/ftheader.h>
49 49
 #include FT_FREETYPE_H
50 50
 #include FT_GLYPH_H
51
+#if CONFIG_FONTCONFIG
52
+#include <fontconfig/fontconfig.h>
53
+#endif
51 54
 
52 55
 static const char *const var_names[] = {
53 56
     "main_w", "w", "W",       ///< width  of the input video
... ...
@@ -167,7 +170,7 @@ static const AVOption drawtext_options[]= {
167 167
 {"boxcolor",    "set box color",        OFFSET(boxcolor_string),    AV_OPT_TYPE_STRING, {.str="white"}, CHAR_MIN, CHAR_MAX },
168 168
 {"shadowcolor", "set shadow color",     OFFSET(shadowcolor_string), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX },
169 169
 {"box",      "set box",              OFFSET(draw_box),           AV_OPT_TYPE_INT,    {.dbl=0},     0,        1        },
170
-{"fontsize", "set font size",        OFFSET(fontsize),           AV_OPT_TYPE_INT,    {.dbl=16},    1,        INT_MAX  },
170
+{"fontsize", "set font size",        OFFSET(fontsize),           AV_OPT_TYPE_INT,    {.dbl=0},     0,        INT_MAX  },
171 171
 {"x",        "set x expression",     OFFSET(x_expr),             AV_OPT_TYPE_STRING, {.str="0"},   CHAR_MIN, CHAR_MAX },
172 172
 {"y",        "set y expression",     OFFSET(y_expr),             AV_OPT_TYPE_STRING, {.str="0"},   CHAR_MIN, CHAR_MAX },
173 173
 {"shadowx",  "set x",                OFFSET(shadowx),            AV_OPT_TYPE_INT,    {.dbl=0},     INT_MIN,  INT_MAX  },
... ...
@@ -298,6 +301,91 @@ error:
298 298
     return ret;
299 299
 }
300 300
 
301
+static int load_font_file(AVFilterContext *ctx, const char *path, int index,
302
+                          const char **error)
303
+{
304
+    DrawTextContext *dtext = ctx->priv;
305
+    int err;
306
+
307
+    err = FT_New_Face(dtext->library, path, index, &dtext->face);
308
+    if (err) {
309
+        *error = FT_ERRMSG(err);
310
+        return AVERROR(EINVAL);
311
+    }
312
+    return 0;
313
+}
314
+
315
+#if CONFIG_FONTCONFIG
316
+static int load_font_fontconfig(AVFilterContext *ctx, const char **error)
317
+{
318
+    DrawTextContext *dtext = ctx->priv;
319
+    FcConfig *fontconfig;
320
+    FcPattern *pattern, *fpat;
321
+    FcResult result = FcResultMatch;
322
+    FcChar8 *filename;
323
+    int err, index;
324
+    double size;
325
+
326
+    fontconfig = FcInitLoadConfigAndFonts();
327
+    if (!fontconfig) {
328
+        *error = "impossible to init fontconfig\n";
329
+        return AVERROR(EINVAL);
330
+    }
331
+    pattern = FcNameParse(dtext->fontfile ? dtext->fontfile :
332
+                          (uint8_t *)(intptr_t)"default");
333
+    if (!pattern) {
334
+        *error = "could not parse fontconfig pattern";
335
+        return AVERROR(EINVAL);
336
+    }
337
+    if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
338
+        *error = "could not substitue fontconfig options"; /* very unlikely */
339
+        return AVERROR(EINVAL);
340
+    }
341
+    FcDefaultSubstitute(pattern);
342
+    fpat = FcFontMatch(fontconfig, pattern, &result);
343
+    if (!fpat || result != FcResultMatch) {
344
+        *error = "impossible to find a matching font";
345
+        return AVERROR(EINVAL);
346
+    }
347
+    if (FcPatternGetString (fpat, FC_FILE,  0, &filename) != FcResultMatch ||
348
+        FcPatternGetInteger(fpat, FC_INDEX, 0, &index   ) != FcResultMatch ||
349
+        FcPatternGetDouble (fpat, FC_SIZE,  0, &size    ) != FcResultMatch) {
350
+        *error = "impossible to find font information";
351
+        return AVERROR(EINVAL);
352
+    }
353
+    av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
354
+    if (!dtext->fontsize)
355
+        dtext->fontsize = size + 0.5;
356
+    err = load_font_file(ctx, filename, index, error);
357
+    if (err)
358
+        return err;
359
+    FcPatternDestroy(fpat);
360
+    FcPatternDestroy(pattern);
361
+    FcConfigDestroy(fontconfig);
362
+    return 0;
363
+}
364
+#endif
365
+
366
+static int load_font(AVFilterContext *ctx)
367
+{
368
+    DrawTextContext *dtext = ctx->priv;
369
+    int err;
370
+    const char *error = "unknown error\n";
371
+
372
+    /* load the face, and set up the encoding, which is by default UTF-8 */
373
+    err = load_font_file(ctx, dtext->fontfile, 0, &error);
374
+    if (!err)
375
+        return 0;
376
+#if CONFIG_FONTCONFIG
377
+    err = load_font_fontconfig(ctx, &error);
378
+    if (!err)
379
+        return 0;
380
+#endif
381
+    av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
382
+           dtext->fontfile, error);
383
+    return err;
384
+}
385
+
301 386
 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
302 387
 {
303 388
     int err;
... ...
@@ -312,7 +400,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
312 312
         return err;
313 313
     }
314 314
 
315
-    if (!dtext->fontfile) {
315
+    if (!dtext->fontfile && !CONFIG_FONTCONFIG) {
316 316
         av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
317 317
         return AVERROR(EINVAL);
318 318
     }
... ...
@@ -381,12 +469,11 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
381 381
         return AVERROR(EINVAL);
382 382
     }
383 383
 
384
-    /* load the face, and set up the encoding, which is by default UTF-8 */
385
-    if ((err = FT_New_Face(dtext->library, dtext->fontfile, 0, &dtext->face))) {
386
-        av_log(ctx, AV_LOG_ERROR, "Could not load fontface from file '%s': %s\n",
387
-               dtext->fontfile, FT_ERRMSG(err));
388
-        return AVERROR(EINVAL);
389
-    }
384
+    err = load_font(ctx);
385
+    if (err)
386
+        return err;
387
+    if (!dtext->fontsize)
388
+        dtext->fontsize = 16;
390 389
     if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) {
391 390
         av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
392 391
                dtext->fontsize, FT_ERRMSG(err));