Hello all. I am in the process of implementing ‘text appears a word at a time’ functionality for VSE. A crude example:
To achieve this my plan is to take a regular text sequence that contains a sentence of words to split, and then split that into separate sequences for individual words. Those sequences can then be offset in time with relative ease using frame_start.
However, for relative positioning it would be helpful to know the actual dimensions of a word. Judging from how it appears in the VSE, a text effect sequence by default is the size of the output. I have had a look at the API and there doesn’t appear to be anything obvious- is there an internal method for doing so? I did try searching, but most results relating to text width are for the 3d context (example).
Q: is there any way to determine the actual size (width/height) of text in VSE?
This is technically possible, but the process is a bit convoluted- you can use the blf module (which is normally used for rendering text) to calculate the dimensions of a string. Here’s a minimal example that should give you some ideas- you will obviously need to take the sequence’s transform into consideration, render resolution, things like that.
# the first three font indices are built-in blender fonts, so if you have a single custom font
# loaded the font_id to use here is 3. you basically just want to loop through bpy.data.fonts until
# you find seq.font, then add 3 to whatever index you found it at.
font_id = 3
blf.size(font_id, seq.font_size, 72)
width, height = blf.dimensions(font_id, text)
print(f"Text strip dimensions: {width} x {height}")
I’m not sure how accurate it is, so you might need to do a bit of experimenting, but I did a quick test where I loaded a super condensed font, and then a bold/book font and saw a notable difference in the width output. I didn’t actually take any screenshots and measure the pixels to compare- so ymmv. Anyway VSE is not my area of expertise, but I wanted to give you an idea of some things to try, since there weren’t any replies to your question yet.
I was getting odd results using ‘bpy.data.fonts + offset of 3’, so I wrote an operator to walk sequences and fontids, since there doesn’t seem to be a way of enumerating the latter:
class SEQUENCER_OT_debug_font_sizing(bpy.types.Operator):
"""Print font sizing info"""
bl_label = "Show font sizing info"
bl_idname = "sequencer.debug_font_sizing"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
"""Ensure we're in the VSE with at least one sequence selected"""
return (context.scene and context.scene.sequence_editor
and context.selected_editable_sequences is not None)
def execute(self, context):
"""Loop through sequences, printing info about font and text"""
import blf
OUTFILE = "/tmp/blender_font_infos.txt"
render_rez_x = bpy.data.scenes["Scene"].render.resolution_x
with open(OUTFILE, "w", encoding="UTF-8") as fh:
# bpy.data.fonts
fh.write(f"There are {len(bpy.data.fonts)} fonts in bpy.data.fonts:\n")
for bpyfont in bpy.data.fonts:
fh.write(f"\t{bpyfont}\n")
fh.write("*"*72 + "\n\n\n")
# sequences
fh.write(f"There are {len(context.selected_editable_sequences)} sequences\n\n")
for seq in context.selected_editable_sequences:
closest_diff = render_rez_x
closest_width = 0
closest_id = 0
fh.write(f"With sequence '{seq.name}':\n")
fh.write(f"\tSequence text: '{seq.text}\'\n")
fh.write(f"\tSequence font: '{seq.font}'\n")
fh.write(f"\tSequence size: '{seq.font_size}'\n")
for fid in range(0, 50):
blf.size(fid, seq.font_size)
w, h = blf.dimensions(fid, seq.text)
if w == 0 and h == 0:
continue
fh.write(f"\t\tfid: {fid}\tw: {w}\th: {h}\n")
difference = abs(render_rez_x - w)
if difference < closest_diff:
closest_diff = difference
closest_width = w
closest_id = fid
fh.write(f"\nClosest candidate:fid={closest_id} at {closest_width} px.\n")
fh.write(f"Font id - 25 ({closest_id - 25}) to bpy.data.fonts: {bpy.data.fonts[closest_id-25].name}\n")
fh.write("-"*72 + "\n\n")
return {'FINISHED'}
I set up three sequences with different fonts & texts, and then sized them all to be as close to the full width of output as I could:
There seem to be 28 fonts loaded, and the ones for the test strips start in the mid 20s. Edit: a new VSE file with one text strip added and a custom font selected has 26 fontids (0…25) that give a size, with fontid = 25 seemingly being the custom font.
Q: is there a way to get the font (name) from a fontid ? I looked at the documentation for blf, but there doesn’t seem to be much that gives. Alternatively: is there a way to get a fontid from a VectorFont ?
With some further testing I have found that the offset between fontid (as used by blf) is not necessarily consistent even within the same blend file. My educated guess is that it depends on what order fonts are used, if other fonts have been loaded in the interim.
Since this is getting further into the API, I have asked about matching up the fontids with fonts over at devtalk, for anyone wanting to follow the discussion there:
Just to round this off for anyone else who like me is looking for a way to get the size of a text sequence, the workaround I posted on devtalk:
This feels like an ugly workaround, as it relies on Blender caching / reusing fontids for the same font file; but it works.
The basic approach is:
get filepath property from TextSequence.font (ref)
resolve that to the absolute path via Python’s path.normpath() - just using Blender’s path.abspath() gives an ‘absolute path relative (??!) to the blend’; if you don’t and use the ‘absolute relative’ path you get a new fontid, which should still work for getting the size, but it feels better to have obvious side effects