Python: captionbob.effect_common
captionbob.effect_common.caption_measure
Caption measure: where libass draws each word of a caption.
The text of an ASS script does not say how wide a word is, and it does not say where libass breaks a caption that is too wide for one line. An effect that needs these facts asks this measure. The measure draws each caption on a black clip with ffmpeg, one frame for each word, with the whole caption in the script and only that word shown. It reads the line and the columns of the word from the frame.
The measure writes each word and each space as a run of its own. An effect that uses the places must write its captions in the same way, with WORD_RUN_TAG in front of each word and SPACE_RUN_TAG in front of each space. libass puts a word a part of a pixel off when the runs of its caption are cut at other places. The run tags, the alignment and the word separator of the effects are in effect_common.py, so the measure and the effects use the same ones.
The ffmpeg of the measure must be the ffmpeg that draws the video, so that the same libass and the same fonts give the places.
Source file: captionbob/effect_common/caption_measure.py
WordPlace
@dataclass(frozen=True)
class WordPlace
Where libass draws one word of a caption.
Args:
line: The line of the word in its caption, 0 for the top line.first_column: The first column of the video that holds a pixel of the word, with no outline.last_column: The last column of the video that holds a pixel of the word, with no outline.
CaptionMeasure
class CaptionMeasure
def __init__(self, ffmpeg_path: Path, work_folder: Path) -> None
Asks libass where it draws each word of a caption.
One measure at a time may use a work folder, because the script file has a fixed name.
Args:
ffmpeg_path: The ffmpeg that draws the video.work_folder: Folder in which the measure writes its script file. The file is removed after each run of ffmpeg.
Raises:
TypeError: When a path is not a Path.FileNotFoundError: When the ffmpeg is not a file.NotADirectoryError: When the work folder is not a folder.
CaptionMeasure.measure_captions
def measure_captions(
self, captions: Sequence[Sequence[str]], video: VideoInfo, *,
font_name: str, font_size: int, bold: bool, side_margin: int,
caption_bottom: int) -> list[list[WordPlace]]
Give the line and the columns of each word of each caption.
libass breaks a caption that is too wide for the room between the side margins into lines. The places are those of the caption with these breaks, centred between the margins.
Args:
captions: The captions, each as the texts of its words.video: The frame size of the video on which the captions are drawn.font_name: Name of the font of the captions.font_size: Height of the font in pixels.bold: True for bold text.side_margin: Room in pixels that stays free on the left and on the right of a caption.caption_bottom: The row of the video at which the bottom of a caption sits.
Returns:
For each caption, the place of each of its words.
Raises:
ValueError: When a caption has no word, when a word is not a text or is empty, when the caption bottom is not a row of the video, when a font value or the margin is outside its range, or when libass draws no pixel for a word.RuntimeError: When ffmpeg fails, or when it does not give one frame for each word.