34 #include <sys/types.h>
41 #if CONFIG_LIBFONTCONFIG
42 #include <fontconfig/fontconfig.h>
67 #include FT_FREETYPE_H
77 "max_glyph_a",
"ascent",
78 "max_glyph_d",
"descent",
96 static double drand(
void *opaque,
double min,
double max)
139 #if CONFIG_LIBFONTCONFIG
177 #if FF_API_DRAWTEXT_OLD_TIMELINE
189 #if CONFIG_LIBFRIBIDI
195 #define OFFSET(x) offsetof(DrawTextContext, x)
196 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
215 #if FF_API_DRAWTEXT_OLD_TIMELINE
218 #if CONFIG_LIBFONTCONFIG
233 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
OFFSET(fix_bounds),
AV_OPT_TYPE_INT, {.i64=1}, 0, 1,
FLAGS},
234 {
"start_number",
"start frame number for n/frame_num variable",
OFFSET(start_number),
AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,
FLAGS},
236 #if CONFIG_LIBFRIBIDI
241 {
"ft_load_flags",
"set font loading flags for libfreetype",
OFFSET(ft_load_flags),
AV_OPT_TYPE_FLAGS, { .i64 = FT_LOAD_DEFAULT }, 0, INT_MAX,
FLAGS,
"ft_load_flags" },
242 {
"default", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
243 {
"no_scale", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
244 {
"no_hinting", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING }, .flags =
FLAGS, .unit =
"ft_load_flags" },
245 {
"render", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_RENDER }, .flags =
FLAGS, .unit =
"ft_load_flags" },
246 {
"no_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
247 {
"vertical_layout", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
248 {
"force_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_FORCE_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
249 {
"crop_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_CROP_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
250 {
"pedantic", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_PEDANTIC }, .flags =
FLAGS, .unit =
"ft_load_flags" },
251 {
"ignore_global_advance_width", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags =
FLAGS, .unit =
"ft_load_flags" },
252 {
"no_recurse", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_RECURSE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
253 {
"ignore_transform", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags =
FLAGS, .unit =
"ft_load_flags" },
254 {
"monochrome", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_MONOCHROME }, .flags =
FLAGS, .unit =
"ft_load_flags" },
255 {
"linear_design", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_LINEAR_DESIGN }, .flags =
FLAGS, .unit =
"ft_load_flags" },
256 {
"no_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
262 #undef __FTERRORS_H__
263 #define FT_ERROR_START_LIST {
264 #define FT_ERRORDEF(e, v, s) { (e), (s) },
265 #define FT_ERROR_END_LIST { 0, NULL } };
274 #define FT_ERRMSG(e) ft_errors[e].err_msg
276 typedef struct Glyph {
278 FT_Glyph border_glyph;
281 FT_Bitmap border_bitmap;
290 const Glyph *
a = key, *bb =
b;
291 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
292 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
301 FT_BitmapGlyph bitmapglyph;
317 if (FT_Get_Glyph(s->
face->glyph, &glyph->glyph)) {
322 glyph->border_glyph = glyph->glyph;
323 if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->
stroker, 0, 0) ||
324 FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
328 bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph;
329 glyph->border_bitmap = bitmapglyph->bitmap;
331 if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
335 bitmapglyph = (FT_BitmapGlyph) glyph->glyph;
337 glyph->bitmap = bitmapglyph->bitmap;
338 glyph->bitmap_left = bitmapglyph->left;
339 glyph->bitmap_top = bitmapglyph->top;
340 glyph->advance = s->
face->glyph->advance.x >> 6;
343 FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
370 err = FT_New_Face(s->
library, path, index, &s->
face);
379 #if CONFIG_LIBFONTCONFIG
383 FcConfig *fontconfig;
384 FcPattern *pat, *best;
385 FcResult result = FcResultMatch;
391 fontconfig = FcInitLoadConfigAndFonts();
397 (
uint8_t *)(intptr_t)
"default");
403 FcPatternAddString(pat, FC_FAMILY, s->font);
405 FcPatternAddDouble(pat, FC_SIZE, (
double)s->
fontsize);
407 FcDefaultSubstitute(pat);
409 if (!FcConfigSubstitute(fontconfig, pat, FcMatchPattern)) {
411 FcPatternDestroy(pat);
415 best = FcFontMatch(fontconfig, pat, &result);
416 FcPatternDestroy(pat);
418 if (!best || result != FcResultMatch) {
420 "Cannot find a valid font for the family %s\n",
426 FcPatternGetInteger(best, FC_INDEX, 0, &index ) != FcResultMatch ||
427 FcPatternGetDouble (best, FC_SIZE, 0, &size ) != FcResultMatch) {
432 if (FcPatternGetString(best, FC_FILE, 0, &filename) != FcResultMatch) {
445 FcConfigDestroy(fontconfig);
447 FcPatternDestroy(best);
461 #if CONFIG_LIBFONTCONFIG
462 err = load_font_fontconfig(ctx);
479 "The text file '%s' could not be read or is empty\n",
489 memcpy(s->
text, textbuf, textbuf_size);
490 s->
text[textbuf_size] = 0;
498 return c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
501 #if CONFIG_LIBFRIBIDI
507 static const FriBidiFlags
flags = FRIBIDI_FLAGS_DEFAULT |
508 FRIBIDI_FLAGS_ARABIC;
509 FriBidiChar *unicodestr = NULL;
511 FriBidiParType direction = FRIBIDI_PAR_LTR;
512 FriBidiStrIndex line_start = 0;
513 FriBidiStrIndex line_end = 0;
514 FriBidiLevel *embedding_levels = NULL;
515 FriBidiArabicProp *ar_props = NULL;
516 FriBidiCharType *bidi_types = NULL;
519 len = strlen(s->
text);
523 len = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8,
524 s->
text, len, unicodestr);
531 fribidi_get_bidi_types(unicodestr, len, bidi_types);
534 if (!embedding_levels) {
538 if (!fribidi_get_par_embedding_levels(bidi_types, len, &direction,
548 fribidi_get_joining_types(unicodestr, len, ar_props);
549 fribidi_join_arabic(bidi_types, len, embedding_levels, ar_props);
550 fribidi_shape(flags, embedding_levels, len, ar_props, unicodestr);
552 for (line_end = 0, line_start = 0; line_end <
len; line_end++) {
553 if (
is_newline(unicodestr[line_end]) || line_end == len - 1) {
554 if (!fribidi_reorder_line(flags, bidi_types,
555 line_end - line_start + 1, line_start,
556 direction, embedding_levels, unicodestr,
560 line_start = line_end + 1;
565 for (i = 0, j = 0; i <
len; i++)
566 if (unicodestr[i] != FRIBIDI_CHAR_FILL)
567 unicodestr[j++] = unicodestr[i];
576 len = fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8,
577 unicodestr, len, s->
text);
595 #if FF_API_DRAWTEXT_OLD_TIMELINE
598 "you are encouraged to use the generic timeline support through the 'enable' option\n");
601 if (!s->
fontfile && !CONFIG_LIBFONTCONFIG) {
609 "Both text and text file provided. Please provide only one\n");
616 #if CONFIG_LIBFRIBIDI
618 if ((err = shape_text(ctx)) < 0)
638 "Either text, a valid file or a timecode must be provided\n");
642 if ((err = FT_Init_FreeType(&(s->
library)))) {
644 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
653 if ((err = FT_Set_Pixel_Sizes(s->
face, 0, s->
fontsize))) {
664 FT_Stroker_Set(s->
stroker, s->
borderw << 6, FT_STROKER_LINECAP_ROUND,
665 FT_STROKER_LINEJOIN_ROUND, 0);
674 if ((err =
load_glyph(ctx, &glyph,
' ')) < 0) {
681 (strchr(s->
text,
'%') || strchr(s->
text,
'\\')))
699 FT_Done_Glyph(glyph->glyph);
700 FT_Done_Glyph(glyph->border_glyph);
711 #if FF_API_DRAWTEXT_OLD_TIMELINE
723 FT_Done_Face(s->
face);
756 #if FF_API_DRAWTEXT_OLD_TIMELINE
769 #if FF_API_DRAWTEXT_OLD_TIMELINE
783 if (!strcmp(cmd,
"reinit")) {
789 if ((ret =
init(ctx)) < 0)
798 char *fct,
unsigned argc,
char **argv,
int tag)
807 char *fct,
unsigned argc,
char **argv,
int tag)
814 fmt = argc >= 1 ? argv[0] :
"flt";
823 if (!strcmp(fmt,
"flt")) {
825 }
else if (!strcmp(fmt,
"hms")) {
829 int64_t ms =
round(pts * 1000);
836 (
int)(ms / (60 * 60 * 1000)),
837 (
int)(ms / (60 * 1000)) % 60,
838 (
int)(ms / 1000) % 60,
849 char *fct,
unsigned argc,
char **argv,
int tag)
858 char *fct,
unsigned argc,
char **argv,
int tag)
868 #if !HAVE_LOCALTIME_R
876 char *fct,
unsigned argc,
char **argv,
int tag)
878 const char *
fmt = argc ? argv[0] :
"%Y-%m-%d %H:%M:%S";
892 char *fct,
unsigned argc,
char **argv,
int tag)
903 "Expression '%s' for the expr text expansion function is not valid\n",
929 unsigned argc,
char **argv)
957 const char *text = *rtext;
958 char *argv[16] = { NULL };
959 unsigned argc = 0, i;
984 if ((ret =
eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
987 *rtext = (
char *)text + 1;
990 for (i = 0; i < argc; i++)
998 char *text = s->
text;
1004 if (*text ==
'\\' && text[1]) {
1007 }
else if (*text ==
'%') {
1029 Glyph *glyph = NULL;
1031 for (i = 0, p = text; *p; i++) {
1033 Glyph
dummy = { 0 };
1037 if (code ==
'\n' || code ==
'\r' || code ==
'\t')
1043 bitmap = borderw ? glyph->border_bitmap : glyph->bitmap;
1045 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
1046 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
1054 bitmap.buffer, bitmap.pitch,
1055 bitmap.width, bitmap.rows,
1056 bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
1069 uint32_t code = 0, prev_code = 0;
1070 int x = 0,
y = 0, i = 0,
ret;
1071 int max_text_line_w = 0,
len;
1075 int y_min = 32000, y_max = -32000;
1076 int x_min = 32000, x_max = -32000;
1078 Glyph *glyph = NULL, *prev_glyph = NULL;
1079 Glyph
dummy = { 0 };
1081 time_t now = time(0);
1125 for (i = 0, p = text; *p; i++) {
1135 y_min =
FFMIN(glyph->bbox.yMin, y_min);
1136 y_max =
FFMAX(glyph->bbox.yMax, y_max);
1137 x_min =
FFMIN(glyph->bbox.xMin, x_min);
1138 x_max =
FFMAX(glyph->bbox.xMax, x_max);
1145 for (i = 0, p = text; *p; i++) {
1149 if (prev_code ==
'\r' && code ==
'\n')
1155 max_text_line_w =
FFMAX(max_text_line_w, x);
1167 if (s->
use_kerning && prev_glyph && glyph->code) {
1168 FT_Get_Kerning(s->
face, prev_glyph->code, glyph->code,
1169 ft_kerning_default, &delta);
1174 s->
positions[i].x = x + glyph->bitmap_left;
1175 s->
positions[i].y =
y - glyph->bitmap_top + y_max;
1177 else x += glyph->advance;
1180 max_text_line_w =
FFMAX(x, max_text_line_w);
1195 #if FF_API_DRAWTEXT_OLD_TIMELINE
1206 box_w =
FFMIN(width - 1 , max_text_line_w);
1213 s->
x, s->
y, box_w, box_h);
1243 #if CONFIG_LIBFRIBIDI
1244 if (s->text_shaping)
1245 if ((ret = shape_text(ctx)) < 0)
1273 .needs_writable = 1,
1288 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
1290 .priv_class = &drawtext_class,
1294 .
inputs = avfilter_vf_drawtext_inputs,
1295 .
outputs = avfilter_vf_drawtext_outputs,