]> git.sesse.net Git - ffmpeg/blobdiff - libavfilter/vf_drawtext.c
Merge commit '6ef96292d99302a59f824713fc763f6abd3754df'
[ffmpeg] / libavfilter / vf_drawtext.c
index 1690b3896dcbc4ffda4456e67e1823cbf1a0e92c..280abb8a8211ab5671ee5dff83183b5b5532678b 100644 (file)
  */
 
 #include "config.h"
+
 #if HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <time.h>
+#include <unistd.h>
+
+#if CONFIG_LIBFONTCONFIG
+#include <fontconfig/fontconfig.h>
+#endif
 
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
@@ -53,9 +61,6 @@
 #include FT_FREETYPE_H
 #include FT_GLYPH_H
 #include FT_STROKER_H
-#if CONFIG_FONTCONFIG
-#include <fontconfig/fontconfig.h>
-#endif
 
 static const char *const var_names[] = {
     "dar",
@@ -121,10 +126,13 @@ enum expansion_mode {
     EXP_STRFTIME,
 };
 
-typedef struct {
+typedef struct DrawTextContext {
     const AVClass *class;
     enum expansion_mode exp_mode;   ///< expansion mode to use for the text
     int reinit;                     ///< tells if the filter is being reinited
+#if CONFIG_LIBFONTCONFIG
+    uint8_t *font;              ///< font to be used
+#endif
     uint8_t *fontfile;              ///< font to be used
     uint8_t *text;                  ///< text to be drawn
     AVBPrint expanded_text;         ///< used to contain the expanded text
@@ -198,6 +206,9 @@ static const AVOption drawtext_options[]= {
 #if FF_API_DRAWTEXT_OLD_TIMELINE
     {"draw",        "if false do not draw (deprecated)", OFFSET(draw_expr), AV_OPT_TYPE_STRING, {.str=NULL},   CHAR_MIN, CHAR_MAX, FLAGS},
 #endif
+#if CONFIG_LIBFONTCONFIG
+    { "font",        "Font name",            OFFSET(font),               AV_OPT_TYPE_STRING, { .str = "Sans" },           .flags = FLAGS },
+#endif
 
     {"expansion", "set the expansion mode", OFFSET(exp_mode), AV_OPT_TYPE_INT, {.i64=EXP_NORMAL}, 0, 2, FLAGS, "expansion"},
         {"none",     "set no expansion",                    OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NONE},     0, 0, FLAGS, "expansion"},
@@ -249,7 +260,7 @@ struct ft_error
 
 #define FT_ERRMSG(e) ft_errors[e].err_msg
 
-typedef struct {
+typedef struct Glyph {
     FT_Glyph *glyph;
     uint32_t code;
     FT_Bitmap bitmap; ///< array holding bitmaps of font
@@ -337,68 +348,90 @@ error:
     return ret;
 }
 
-static int load_font_file(AVFilterContext *ctx, const char *path, int index,
-                          const char **error)
+static int load_font_file(AVFilterContext *ctx, const char *path, int index)
 {
     DrawTextContext *s = ctx->priv;
     int err;
 
     err = FT_New_Face(s->library, path, index, &s->face);
     if (err) {
-        *error = FT_ERRMSG(err);
+        av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
+               s->fontfile, FT_ERRMSG(err));
         return AVERROR(EINVAL);
     }
     return 0;
 }
 
-#if CONFIG_FONTCONFIG
-static int load_font_fontconfig(AVFilterContext *ctx, const char **error)
+#if CONFIG_LIBFONTCONFIG
+static int load_font_fontconfig(AVFilterContext *ctx)
 {
     DrawTextContext *s = ctx->priv;
     FcConfig *fontconfig;
-    FcPattern *pattern, *fpat;
+    FcPattern *pat, *best;
     FcResult result = FcResultMatch;
     FcChar8 *filename;
-    int err, index;
+    int index;
     double size;
+    int err = AVERROR(ENOENT);
 
     fontconfig = FcInitLoadConfigAndFonts();
     if (!fontconfig) {
-        *error = "impossible to init fontconfig\n";
-        return AVERROR(EINVAL);
+        av_log(ctx, AV_LOG_ERROR, "impossible to init fontconfig\n");
+        return AVERROR_UNKNOWN;
     }
-    pattern = FcNameParse(s->fontfile ? s->fontfile :
+    pat = FcNameParse(s->fontfile ? s->fontfile :
                           (uint8_t *)(intptr_t)"default");
-    if (!pattern) {
-        *error = "could not parse fontconfig pattern";
+    if (!pat) {
+        av_log(ctx, AV_LOG_ERROR, "could not parse fontconfig pat");
         return AVERROR(EINVAL);
     }
-    if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
-        *error = "could not substitue fontconfig options"; /* very unlikely */
-        return AVERROR(EINVAL);
+
+    FcPatternAddString(pat, FC_FAMILY, s->font);
+    if (s->fontsize)
+        FcPatternAddDouble(pat, FC_SIZE, (double)s->fontsize);
+
+    FcDefaultSubstitute(pat);
+
+    if (!FcConfigSubstitute(fontconfig, pat, FcMatchPattern)) {
+        av_log(ctx, AV_LOG_ERROR, "could not substitue fontconfig options"); /* very unlikely */
+        FcPatternDestroy(pat);
+        return AVERROR(ENOMEM);
     }
-    FcDefaultSubstitute(pattern);
-    fpat = FcFontMatch(fontconfig, pattern, &result);
-    if (!fpat || result != FcResultMatch) {
-        *error = "impossible to find a matching font";
-        return AVERROR(EINVAL);
+
+    best = FcFontMatch(fontconfig, pat, &result);
+    FcPatternDestroy(pat);
+
+    if (!best || result != FcResultMatch) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Cannot find a valid font for the family %s\n",
+               s->font);
+        goto fail;
     }
-    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";
+
+    if (
+        FcPatternGetInteger(best, FC_INDEX, 0, &index   ) != FcResultMatch ||
+        FcPatternGetDouble (best, FC_SIZE,  0, &size    ) != FcResultMatch) {
+        av_log(ctx, AV_LOG_ERROR, "impossible to find font information");
         return AVERROR(EINVAL);
     }
+
+    if (FcPatternGetString(best, FC_FILE, 0, &filename) != FcResultMatch) {
+        av_log(ctx, AV_LOG_ERROR, "No file path for %s\n",
+               s->font);
+        goto fail;
+    }
+
     av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
     if (!s->fontsize)
         s->fontsize = size + 0.5;
-    err = load_font_file(ctx, filename, index, error);
+
+    err = load_font_file(ctx, filename, index);
     if (err)
         return err;
-    FcPatternDestroy(fpat);
-    FcPatternDestroy(pattern);
     FcConfigDestroy(fontconfig);
-    return 0;
+fail:
+    FcPatternDestroy(best);
+    return err;
 }
 #endif
 
@@ -406,19 +439,16 @@ static int load_font(AVFilterContext *ctx)
 {
     DrawTextContext *s = 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, s->fontfile, 0, &error);
+    err = load_font_file(ctx, s->fontfile, 0);
     if (!err)
         return 0;
-#if CONFIG_FONTCONFIG
-    err = load_font_fontconfig(ctx, &error);
+#if CONFIG_LIBFONTCONFIG
+    err = load_font_fontconfig(ctx);
     if (!err)
         return 0;
 #endif
-    av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
-           s->fontfile, error);
     return err;
 }
 
@@ -457,7 +487,7 @@ static av_cold int init(AVFilterContext *ctx)
                "you are encouraged to use the generic timeline support through the 'enable' option\n");
 #endif
 
-    if (!s->fontfile && !CONFIG_FONTCONFIG) {
+    if (!s->fontfile && !CONFIG_LIBFONTCONFIG) {
         av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
         return AVERROR(EINVAL);
     }