33 #include "libavutil/avstring.h"
34 #include "libavutil/bprint.h"
35 #include "libavutil/common.h"
36 #include "libavutil/file.h"
37 #include "libavutil/eval.h"
38 #include "libavutil/opt.h"
39 #include "libavutil/random_seed.h"
40 #include "libavutil/parseutils.h"
41 #include "libavutil/timecode.h"
43 #include "libavutil/lfg.h"
51 #include <config/ftheader.h>
52 #include FT_FREETYPE_H
55 #include <fontconfig/fontconfig.h>
64 "max_glyph_a",
"ascent",
65 "max_glyph_d",
"descent",
83 static double drand(
void *opaque,
double min,
double max)
158 #if FF_API_DRAWTEXT_OLD_TIMELINE
173 #define OFFSET(x) offsetof(DrawTextContext, x)
174 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
191 #if FF_API_DRAWTEXT_OLD_TIMELINE
206 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
OFFSET(fix_bounds),
AV_OPT_TYPE_INT, {.i64=1}, 0, 1,
FLAGS},
207 {
"start_number",
"start frame number for n/frame_num variable",
OFFSET(start_number),
AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,
FLAGS},
210 {
"ft_load_flags",
"set font loading flags for libfreetype",
OFFSET(ft_load_flags),
AV_OPT_TYPE_FLAGS, { .i64 = FT_LOAD_DEFAULT | FT_LOAD_RENDER}, 0, INT_MAX,
FLAGS,
"ft_load_flags" },
211 {
"default", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
212 {
"no_scale", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
213 {
"no_hinting", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING }, .flags =
FLAGS, .unit =
"ft_load_flags" },
214 {
"render", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_RENDER }, .flags =
FLAGS, .unit =
"ft_load_flags" },
215 {
"no_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
216 {
"vertical_layout", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
217 {
"force_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_FORCE_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
218 {
"crop_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_CROP_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
219 {
"pedantic", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_PEDANTIC }, .flags =
FLAGS, .unit =
"ft_load_flags" },
220 {
"ignore_global_advance_width", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags =
FLAGS, .unit =
"ft_load_flags" },
221 {
"no_recurse", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_RECURSE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
222 {
"ignore_transform", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags =
FLAGS, .unit =
"ft_load_flags" },
223 {
"monochrome", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_MONOCHROME }, .flags =
FLAGS, .unit =
"ft_load_flags" },
224 {
"linear_design", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_LINEAR_DESIGN }, .flags =
FLAGS, .unit =
"ft_load_flags" },
225 {
"no_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
231 #undef __FTERRORS_H__
232 #define FT_ERROR_START_LIST {
233 #define FT_ERRORDEF(e, v, s) { (e), (s) },
234 #define FT_ERROR_END_LIST { 0, NULL } };
243 #define FT_ERRMSG(e) ft_errors[e].err_msg
257 const Glyph *
a = key, *bb =
b;
258 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
259 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
278 !(glyph->glyph =
av_mallocz(
sizeof(*glyph->glyph)))) {
284 if (FT_Get_Glyph(s->
face->glyph, glyph->glyph)) {
289 glyph->bitmap = s->
face->glyph->bitmap;
290 glyph->bitmap_left = s->
face->glyph->bitmap_left;
291 glyph->bitmap_top = s->
face->glyph->bitmap_top;
292 glyph->advance = s->
face->glyph->advance.x >> 6;
295 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
322 err = FT_New_Face(s->
library, path, index, &s->
face);
330 #if CONFIG_FONTCONFIG
331 static int load_font_fontconfig(
AVFilterContext *ctx,
const char **error)
334 FcConfig *fontconfig;
335 FcPattern *pattern, *fpat;
336 FcResult result = FcResultMatch;
341 fontconfig = FcInitLoadConfigAndFonts();
343 *error =
"impossible to init fontconfig\n";
347 (
uint8_t *)(intptr_t)
"default");
349 *error =
"could not parse fontconfig pattern";
352 if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
353 *error =
"could not substitue fontconfig options";
356 FcDefaultSubstitute(pattern);
357 fpat = FcFontMatch(fontconfig, pattern, &result);
358 if (!fpat || result != FcResultMatch) {
359 *error =
"impossible to find a matching font";
362 if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch ||
363 FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch ||
364 FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) {
365 *error =
"impossible to find font information";
374 FcPatternDestroy(fpat);
375 FcPatternDestroy(pattern);
376 FcConfigDestroy(fontconfig);
385 const char *error =
"unknown error\n";
391 #if CONFIG_FONTCONFIG
392 err = load_font_fontconfig(ctx, &error);
410 "The text file '%s' could not be read or is empty\n",
417 memcpy(s->
text, textbuf, textbuf_size);
418 s->
text[textbuf_size] = 0;
430 #if FF_API_DRAWTEXT_OLD_TIMELINE
433 "you are encouraged to use the generic timeline support through the 'enable' option\n");
444 "Both text and text file provided. Please provide only one\n");
467 "Either text, a valid file or a timecode must be provided\n");
471 if ((err = FT_Init_FreeType(&(s->
library)))) {
473 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
482 if ((err = FT_Set_Pixel_Sizes(s->
face, 0, s->
fontsize))) {
494 if ((err =
load_glyph(ctx, &glyph,
' ')) < 0) {
501 (strchr(s->
text,
'%') || strchr(s->
text,
'\\')))
519 FT_Done_Glyph(*glyph->glyph);
531 #if FF_API_DRAWTEXT_OLD_TIMELINE
543 FT_Done_Face(s->
face);
551 return c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
579 #if FF_API_DRAWTEXT_OLD_TIMELINE
592 #if FF_API_DRAWTEXT_OLD_TIMELINE
606 if (!strcmp(cmd,
"reinit")) {
610 if ((ret =
init(ctx)) < 0)
619 char *fct,
unsigned argc,
char **argv,
int tag)
628 char *fct,
unsigned argc,
char **argv,
int tag)
637 char *fct,
unsigned argc,
char **argv,
int tag)
646 char *fct,
unsigned argc,
char **argv,
int tag)
656 #if !HAVE_LOCALTIME_R
664 char *fct,
unsigned argc,
char **argv,
int tag)
666 const char *
fmt = argc ? argv[0] :
"%Y-%m-%d %H:%M:%S";
680 char *fct,
unsigned argc,
char **argv,
int tag)
691 "Expression '%s' for the expr text expansion function is not valid\n",
717 unsigned argc,
char **argv)
745 const char *text = *rtext;
746 char *argv[16] = { NULL };
747 unsigned argc = 0, i;
772 if ((ret =
eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
775 *rtext = (
char *)text + 1;
778 for (i = 0; i < argc; i++)
786 char *text = s->
text;
792 if (*text ==
'\\' && text[1]) {
795 }
else if (*text ==
'%') {
818 for (i = 0, p = text; *p; i++) {
823 if (code ==
'\n' || code ==
'\r' || code ==
'\t')
829 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
830 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
838 glyph->bitmap.buffer, glyph->bitmap.pitch,
839 glyph->bitmap.width, glyph->bitmap.rows,
840 glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
853 uint32_t code = 0, prev_code = 0;
854 int x = 0,
y = 0, i = 0,
ret;
855 int max_text_line_w = 0,
len;
859 int y_min = 32000, y_max = -32000;
860 int x_min = 32000, x_max = -32000;
862 Glyph *glyph = NULL, *prev_glyph = NULL;
865 time_t now = time(0);
909 for (i = 0, p = text; *p; i++) {
919 y_min =
FFMIN(glyph->bbox.yMin, y_min);
920 y_max =
FFMAX(glyph->bbox.yMax, y_max);
921 x_min =
FFMIN(glyph->bbox.xMin, x_min);
922 x_max =
FFMAX(glyph->bbox.xMax, x_max);
929 for (i = 0, p = text; *p; i++) {
933 if (prev_code ==
'\r' && code ==
'\n')
939 max_text_line_w =
FFMAX(max_text_line_w, x);
952 FT_Get_Kerning(s->
face, prev_glyph->code, glyph->code,
953 ft_kerning_default, &delta);
958 s->
positions[i].x = x + glyph->bitmap_left;
959 s->
positions[i].y =
y - glyph->bitmap_top + y_max;
961 else x += glyph->advance;
964 max_text_line_w =
FFMAX(x, max_text_line_w);
979 #if FF_API_DRAWTEXT_OLD_TIMELINE
990 box_w =
FFMIN(width - 1 , max_text_line_w);
997 s->
x, s->
y, box_w, box_h);
1046 .needs_writable = 1,
1061 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
1063 .priv_class = &drawtext_class,
1067 .
inputs = avfilter_vf_drawtext_inputs,
1068 .
outputs = avfilter_vf_drawtext_outputs,
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
void ff_blend_mask(FFDrawContext *draw, FFDrawColor *color, uint8_t *dst[], int dst_linesize[], int dst_w, int dst_h, uint8_t *mask, int mask_linesize, int mask_w, int mask_h, int l2depth, unsigned endianness, int x0, int y0)
Blend an alpha mask with an uniform color.
AVFilterFormats * ff_draw_supported_pixel_formats(unsigned flags)
Return the list of pixel formats supported by the draw functions.
int draw
set to zero to prevent drawing
static int func_frame_num(AVFilterContext *ctx, AVBPrint *bp, char *fct, unsigned argc, char **argv, int tag)
char * y_expr
expression for y position
int tc24hmax
1 if timecode is wrapped to 24 hours, 0 otherwise
This structure describes decoded (raw) audio or video data.
int x
x position to start drawing text
double av_expr_eval(AVExpr *e, const double *const_values, void *opaque)
Evaluate a previously parsed expression.
static double drand(void *opaque, double min, double max)
static int func_metadata(AVFilterContext *ctx, AVBPrint *bp, char *fct, unsigned argc, char **argv, int tag)
const char * name
Filter name.
static const AVOption drawtext_options[]
unsigned int fontsize
font size to use
FFDrawColor boxcolor
background color
void * priv
private data for use by the filter
#define AV_LOG_WARNING
Something somehow does not look correct.
static const AVFilterPad outputs[]
char * x_expr
expression for x position
char * av_strdup(const char *s) av_malloc_attrib
Duplicate the string s.
uint8_t * fontfile
font to be used
int h
agreed upon image height
AVDictionaryEntry * av_dict_get(AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
static struct drawtext_function functions[]
void av_log(void *avcl, int level, const char *fmt,...) av_printf_format(3
Send the specified message to the log if the level is less than or equal to the current av_log_level...
static int draw_text(AVFilterContext *ctx, AVFrame *frame, int width, int height)
int av_expr_parse(AVExpr **expr, const char *s, const char *const *const_names, const char *const *func1_names, double(*const *funcs1)(void *, double), const char *const *func2_names, double(*const *funcs2)(void *, double, double), int log_offset, void *log_ctx)
Parse an expression.
uint8_t * text
text to be drawn
char * tc_opt_string
specified timecode option string
void av_expr_free(AVExpr *e)
Free a parsed expression previously created with av_expr_parse().
int is_disabled
the enabled state from the last expression evaluation
int(* func)(AVFilterContext *, AVBPrint *, char *, unsigned, char **, int)
void av_freep(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc() and set the pointer ...
#define AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
Some filters support a generic "enable" expression option that can be used to enable or disable a fil...
struct AVTreeNode * av_tree_node_alloc(void)
Allocate an AVTreeNode.
const char * name
Pad name.
AVFilter avfilter_vf_drawtext
char * av_get_token(const char **buf, const char *term)
Unescape the given string until a non escaped terminating char, and return the token corresponding to...
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
static int glyph_enu_free(void *opaque, void *elem)
void * av_tree_find(const AVTreeNode *t, void *key, int(*cmp)(void *key, const void *b), void *next[2])
static int func_pict_type(AVFilterContext *ctx, AVBPrint *bp, char *fct, unsigned argc, char **argv, int tag)
static void drawtext(AVFrame *pic, int x, int y, int ftid, const uint8_t *color, const char *fmt,...)
static const uint32_t color[16+AV_CLASS_CATEGORY_NB]
static unsigned int av_lfg_get(AVLFG *c)
Get the next random unsigned 32-bit number using an ALFG.
static av_cold int end(AVCodecContext *avctx)
FT_Face face
freetype font face handle
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
static av_cold int init(AVFilterContext *ctx)
#define CONFIG_FONTCONFIG
int start_number
starting frame number for n/frame_num var
static double av_q2d(AVRational a)
Convert rational to double.
static const AVFilterPad avfilter_vf_drawtext_inputs[]
char av_get_picture_type_char(enum AVPictureType pict_type)
Return a single letter to describe the given picture type pict_type.
static av_cold void uninit(AVFilterContext *ctx)
timecode wraps after 24 hours
FT_Vector * positions
positions for each element in the text
void av_tree_destroy(AVTreeNode *t)
static void localtime_r(const time_t *t, struct tm *tm)
A filter pad used for either input or output.
char * draw_expr
expression for draw
A link between two filters.
void * av_tree_insert(AVTreeNode **tp, void *key, int(*cmp)(void *key, const void *b), AVTreeNode **next)
Insert or remove an element.
double var_values[VAR_VARS_NB]
int width
width and height of the video frame
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
void av_free(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc(). ...
AVBPrint expanded_text
used to contain the expanded text
#define AV_BPRINT_SIZE_UNLIMITED
Convenience macros for special values for av_bprint_init() size_max parameter.
AVExpr * y_pexpr
parsed expressions for x and y
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
int y
y position to start drawing text
void ff_draw_color(FFDrawContext *draw, FFDrawColor *color, const uint8_t rgba[4])
Prepare a color.
static int expand_function(AVFilterContext *ctx, AVBPrint *bp, char **rtext)
AVRational time_base
Define the time base used by the PTS of the frames/samples which will pass through this link...
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
FFDrawColor fontcolor
foreground color
static const char *const fun2_names[]
int reload
reload text file for each frame
int w
agreed upon image width
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Init a print buffer.
#define FF_ARRAY_ELEMS(a)
Buffer to print data progressively.
enum AVPictureType pict_type
Picture type of the frame.
static int av_bprint_is_complete(AVBPrint *buf)
Test if the print buffer is complete (not truncated).
void * av_realloc(void *ptr, size_t size) 1(2)
Allocate or reallocate a block of memory.
int64_t basetime
base pts time in the real world for display
AVDictionary * av_frame_get_metadata(const AVFrame *frame)
char * av_timecode_make_string(const AVTimecode *tc, char *buf, int framenum)
Load timecode string in buf.
int max_glyph_h
max glyph height
AVRational tc_rate
frame rate for timecode
AVExpr * draw_pexpr
parsed expression for draw
int av_expr_parse_and_eval(double *res, const char *s, const char *const *const_names, const double *const_values, const char *const *func1_names, double(*const *funcs1)(void *, double), const char *const *func2_names, double(*const *funcs2)(void *, double, double), void *opaque, int log_offset, void *log_ctx)
Parse and evaluate an expression.
static int process_command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
static int func_eval_expr(AVFilterContext *ctx, AVBPrint *bp, char *fct, unsigned argc, char **argv, int tag)
double(* eval_func2)(void *, double a, double b)
static int expand_text(AVFilterContext *ctx)
int format
agreed upon media format
AVTimecode tc
timecode context
Main libavfilter public API header.
AVFilterLink ** outputs
array of pointers to output links
#define AV_LOG_INFO
Standard information.
void av_lfg_init(AVLFG *c, unsigned int seed)
static int draw_glyphs(DrawTextContext *s, AVFrame *frame, int width, int height, const uint8_t rgbcolor[4], FFDrawColor *color, int x, int y)
static int load_font(AVFilterContext *ctx)
#define AV_TIMECODE_STR_SIZE
static int command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
BYTE int const BYTE int int int height
Describe the class of an AVClass context structure.
static const AVFilterPad inputs[]
AVFilterLink ** inputs
array of pointers to input links
static int func_strftime(AVFilterContext *ctx, AVBPrint *bp, char *fct, unsigned argc, char **argv, int tag)
rational number numerator/denominator
void ff_blend_rectangle(FFDrawContext *draw, FFDrawColor *color, uint8_t *dst[], int dst_linesize[], int dst_w, int dst_h, int x0, int y0, int w, int h)
Blend a rectangle with an uniform color.
struct AVTreeNode * glyphs
rendered glyphs, stored using the UTF-32 char code
static int is_newline(uint32_t c)
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
int ff_draw_init(FFDrawContext *draw, enum AVPixelFormat format, unsigned flags)
Init a draw context.
AVRational sample_aspect_ratio
agreed upon sample aspect ratio
AVFilterContext * dst
dest filter
void av_bprint_clear(AVBPrint *buf)
Reset the string to "" but keep internal allocated data.
#define AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
Same as AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, except that the filter will have its filter_frame() c...
int tag
opaque argument to func
struct ft_error ft_errors[]
short int draw_box
draw box around text - true or false
static int config_input(AVFilterLink *inlink)
int av_timecode_init_from_string(AVTimecode *tc, AVRational rate, const char *str, void *log_ctx)
Parse timecode representation (hh:mm:ss[:;.
static int load_font_file(AVFilterContext *ctx, const char *path, int index, const char **error)
static const char *const var_names[]
int use_kerning
font kerning is used - true/false
#define FF_API_DRAWTEXT_OLD_TIMELINE
static int glyph_cmp(void *key, const void *b)
static int query_formats(AVFilterContext *ctx)
FT_Library library
freetype font library handle
void av_file_unmap(uint8_t *bufptr, size_t size)
Unmap or free the buffer bufptr created by av_file_map().
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
static const AVFilterPad avfilter_vf_drawtext_outputs[]
void av_bprintf(AVBPrint *buf, const char *fmt,...) av_printf_format(2
Append a formatted string to a print buffer.
#define AVFILTER_DEFINE_CLASS(fname)
int reinit
tells if the filter is being reinited
FFDrawColor shadowcolor
shadow color
int max_glyph_w
max glyph width
static const eval_func2 fun2[]
int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, int log_offset, void *log_ctx)
Read the file with name filename, and put its content in a newly allocated buffer or map it with mmap...
static int func_pts(AVFilterContext *ctx, AVBPrint *bp, char *fct, unsigned argc, char **argv, int tag)
enum expansion_mode exp_mode
expansion mode to use for the text
int fix_bounds
do we let it go out of frame bounds - t/f
uint32_t av_get_random_seed(void)
Get a seed to use in conjunction with random functions.
char * textfile
file with text to be drawn
static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
Load glyphs corresponding to the UTF-32 codepoint code.
static int load_textfile(AVFilterContext *ctx)
static int eval_function(AVFilterContext *ctx, AVBPrint *bp, char *fct, unsigned argc, char **argv)
int ft_load_flags
flags used for loading fonts, see FT_LOAD_*
uint32_t flags
flags such as drop frame, +24 hours support, ...
void av_tree_enumerate(AVTreeNode *t, void *opaque, int(*cmp)(void *opaque, void *elem), int(*enu)(void *opaque, void *elem))
Apply enu(opaque, &elem) to all the elements in the tree in a given range.
void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm)
Append a formatted date and time to a print buffer.
#define GET_UTF8(val, GET_BYTE, ERROR)
size_t nb_positions
number of elements of positions array
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
int64_t frame_count
Number of past frames sent through the link.
#define AV_NOPTS_VALUE
Undefined timestamp value.
void * av_mallocz(size_t size) av_malloc_attrib 1(1)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...