user20162: A script takes a single word 3 to 11 characters long, draws it with a custom font and overlays on top of a video. The word should fit a certain width.
The animation involves pan and zoom that the word follows. Maybe this compositing job suits smth like After Effects, but the task is ffmpeg implementation. I made it work with the shortest 3-character long sample word and the longest 11-character one.
drawtext filter generates a transparent png with big margins around the text. zoompan filter does the zooming afterwards.
I determined the font size for 3- and 11-char sample words and tried linear interpolation to get font size, depending on the word length. This approach fails with some words, as the font has variable width letters, and "MMM" vs. "III" differs n times in width.
Is there a way in ffmpeg to fit a variable-width text to fixed dimensions?
Maybe some filter to detect a least-sized rectangle with non-transparent pixels? 
Plan-B works fine: bash function that calculates word length depending on each letter width in the particular font. Had to measure each letter width in Photoshop in pixels at 100pt font size.
Still wondering if there is a way ffmpeg could scale font size text to fit some fixed width.

user25001: I call FFMPEG from python and this worked with me:
from PIL import ImageFont

translation_font = ImageFont.truetype(
    "SourceCodePro-Regular.otf", size=55, encoding="unic")
w, _ = translation_font.getsize(" ".join([line, word]))

Which will put the width of the text (rendered with the given settings) in w. 
Based on that you can find the first word that makes the text get outside of the screen and insert a new line before it
def fit_text(string: str, frame_width):
    split_line = [x.strip() for x in string.split()]
    lines = ""
    w = 0
    line_num = 0
    line = ""
    for word in split_line:
        # Make a test
        w, _ = translation_font.getsize(" ".join([line, word]))
        # If it exceeds the frame width, add a new line
        if w > (frame_width - (2 * 6)):  # Leave 6px margin on each side
            lines += line.strip() + "\n"
            line = ""

        line += word + " "

    lines += line.strip()  # Append leftover words
    return lines


user24237: I know this question is about achieving the result with the drawtext filter.
However this might be an alternative approach using the ass filter.
in="Slow_when_flashing_school_zone_traffic_sign_in_Chinese.jpg"
txt="school_sign"

ffmpeg -i "$in" \
-vf "zoompan=z='min(zoom+0.005,1.5)':y='if(gte(zoom,1.5),y,y+1)':x='x':d=125,\
scale=w=600:h=800,\
fps=fps=25,\
ass=${txt}.ass" -f nut pipe: | ffplay pipe:

Preview: (the color changing text is the animated subtitle)

Since ASS is very flexible and can be formatted extensively this might be worthwhile to explore.
You might want to have a look at Visual_Typesetting and ASS Override Tags (search for Animated transform and \t tag).
I suggest to generate the ASS file with Aegisub as a starting point. If you need to, you could further improve it with a text editor.
File and Image:
File: school_sign.ass (proof of concept)
[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: PC.601
PlayResX: 600
PlayResY: 800

[Aegisub Project Garbage]
Video AR Mode: 4
Video AR Value: 0.750000
Video Zoom Percent: 2.000000

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:04.94,Default,,0,0,0,,{\an5\fscx100\fscy100\t(0,3960,\fscx150\fscy150)\frz5.811\4c&H000000&\4a&HFF&\c&H2A2A2A&\3c&H000000&\3a&HFF&\move(307,527,460,755,0,3920)}Slow when flashing! School zone

Image: Slow_when_flashing_school_zone_traffic_sign_in_Chinese.jpg
Daniel Case / CC BY-SA (https://creativecommons.org/licenses/by-sa/3.0)


user1871: There's no way to force the drawtext filter to fit the text in a certain width.
What you can do, is get the rendered text dimensions in a first pass and then run drawtext accordingly in 2nd pass, or crop and scale the output using the values from first pass.
For first pass,
ffmpeg -i video -vf drawtext=fontfile=path/to/font:fontsize=FinalSize:text='Lorem Ipsor' \
                   :x=0+0*print(tw):y=0+0*print(th) -vframes 1 -f null -

This will print the text at the top-left. The print statements will output
84.000000
14.000000

The first value is tw - the width, and the 2nd th - the height, in pixels.

20162: <p>A script takes a single word 3 to 11 characters long, draws it with a custom font and overlays on top of a video. The word should fit a certain width.</p>

<p>The animation involves pan and zoom that the word follows. Maybe this compositing job suits smth like After Effects, but the task is ffmpeg implementation. I made it work with the shortest 3-character long sample word and the longest 11-character one.</p>

<p><code>drawtext</code> filter generates a transparent png with big margins around the text. <code>zoompan</code> filter does the zooming afterwards.</p>

<p>I determined the font size for 3- and 11-char sample words and tried linear interpolation to get font size, depending on the word length. This approach fails with some words, as the font has variable width letters, and "MMM" vs. "III" differs n times in width.</p>

<p>Is there a way in ffmpeg to fit a variable-width text to fixed dimensions?</p>

<p>Maybe some filter to detect a least-sized rectangle with non-transparent pixels? </p>

<p><strong>Plan-B</strong> works fine: bash function that calculates word length depending on each letter width in the particular font. Had to measure each letter width in Photoshop in pixels at 100pt font size.</p>

<p>Still wondering if there is a way <em>ffmpeg</em> could scale font size text to fit some fixed width.</p>
 25001: <p>I call FFMPEG from python and this worked with me:</p>

<pre><code>from PIL import ImageFont

translation_font = ImageFont.truetype(
    "SourceCodePro-Regular.otf", size=55, encoding="unic")
w, _ = translation_font.getsize(" ".join([line, word]))
</code></pre>

<p>Which will put the width of the text (rendered with the given settings) in <code>w</code>. </p>

<p>Based on that you can find the first word that makes the text get outside of the screen and insert a new line before it</p>

<pre><code>def fit_text(string: str, frame_width):
    split_line = [x.strip() for x in string.split()]
    lines = ""
    w = 0
    line_num = 0
    line = ""
    for word in split_line:
        # Make a test
        w, _ = translation_font.getsize(" ".join([line, word]))
        # If it exceeds the frame width, add a new line
        if w &gt; (frame_width - (2 * 6)):  # Leave 6px margin on each side
            lines += line.strip() + "\n"
            line = ""

        line += word + " "

    lines += line.strip()  # Append leftover words
    return lines
</code></pre>
 24237: <p>I know this question is about achieving the result with the <code>drawtext</code> filter.</p>

<p>However this might be an alternative approach using the <code>ass</code> filter.</p>

<pre><code>in="Slow_when_flashing_school_zone_traffic_sign_in_Chinese.jpg"
txt="school_sign"

ffmpeg -i "$in" \
-vf "zoompan=z='min(zoom+0.005,1.5)':y='if(gte(zoom,1.5),y,y+1)':x='x':d=125,\
scale=w=600:h=800,\
fps=fps=25,\
ass=${txt}.ass" -f nut pipe: | ffplay pipe:
</code></pre>

<p>Preview: (the color changing text is the animated subtitle)
<a href="https://i.stack.imgur.com/3UIMF.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/3UIMF.gif" alt="Preview of animated ASS subtitle"></a></p>

<p>Since ASS is very flexible and can be formatted extensively this might be worthwhile to explore.</p>

<p>You might want to have a look at <a href="http://docs.aegisub.org/3.2/Visual_Typesetting/" rel="nofollow noreferrer">Visual_Typesetting</a> and <a href="http://docs.aegisub.org/3.2/ASS_Tags/" rel="nofollow noreferrer">ASS Override Tags</a> (search for <code>Animated transform</code> and <code>\t tag</code>).</p>

<p>I suggest to generate the ASS file with <a href="http://www.aegisub.org/downloads/" rel="nofollow noreferrer">Aegisub</a> as a starting point. If you need to, you could further improve it with a text editor.</p>

<p>File and Image:</p>

<p>File: <code>school_sign.ass</code> (proof of concept)</p>

<pre><code>[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: PC.601
PlayResX: 600
PlayResY: 800

[Aegisub Project Garbage]
Video AR Mode: 4
Video AR Value: 0.750000
Video Zoom Percent: 2.000000

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&amp;H00FFFFFF,&amp;H000000FF,&amp;H00000000,&amp;H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:04.94,Default,,0,0,0,,{\an5\fscx100\fscy100\t(0,3960,\fscx150\fscy150)\frz5.811\4c&amp;H000000&amp;\4a&amp;HFF&amp;\c&amp;H2A2A2A&amp;\3c&amp;H000000&amp;\3a&amp;HFF&amp;\move(307,527,460,755,0,3920)}Slow when flashing! School zone
</code></pre>

<p>Image: <code>Slow_when_flashing_school_zone_traffic_sign_in_Chinese.jpg</code></p>

<p>Daniel Case / CC BY-SA (<a href="https://creativecommons.org/licenses/by-sa/3.0" rel="nofollow noreferrer">https://creativecommons.org/licenses/by-sa/3.0</a>)
<a href="https://i.stack.imgur.com/K0SQN.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/K0SQN.jpg" alt="Slow_when_flashing_school_zone_traffic_sign_in_Chinese.jpg"></a></p>
 1871: <p>There's no way to force the drawtext filter to fit the text in a certain width.</p>

<p>What you can do, is get the rendered text dimensions in a first pass and then run drawtext accordingly in 2nd pass, or crop and scale the output using the values from first pass.</p>

<p>For first pass,</p>

<pre><code>ffmpeg -i video -vf drawtext=fontfile=path/to/font:fontsize=FinalSize:text='Lorem Ipsor' \
                   :x=0+0*print(tw):y=0+0*print(th) -vframes 1 -f null -
</code></pre>

<p>This will print the text at the top-left. The print statements will output</p>

<pre><code>84.000000
14.000000
</code></pre>

<p>The first value is <code>tw</code> - the width, and the 2nd <code>th</code> - the height, in pixels.</p>
