Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions pycaption/subtitler_image_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,22 @@ def write_images(
def printLine(self, draw: ImageDraw, caption_list: Caption, fnt: ImageFont, position: str = 'bottom',
align: str = 'left'):
ascender, descender = fnt.getmetrics()
line_spacing = (ascender + abs(descender)) * 0.75 # Basic line height without extra padding
line_spacing = (ascender + abs(descender)) * 0.80 # Basic line height without extra padding

# Split captions containing \n into separate single-line captions
flat_captions = []
for caption in caption_list:
text = caption.get_text()
if '\n' in text:
for line in text.split('\n'):
flat_captions.append(Caption(caption.start, caption.end,
[CaptionNode.create_text(line)],
layout_info=caption.layout_info))
else:
flat_captions.append(caption)

lines_written = 0
for caption in caption_list[::-1]:
for caption in flat_captions[::-1]:
text = caption.get_text()
l, t, r, b = draw.textbbox((0, 0), text, font=fnt, align=align)

Expand Down Expand Up @@ -266,7 +279,8 @@ def printLine(self, draw: ImageDraw, caption_list: Caption, fnt: ImageFont, posi
if position != 'source':
x = self.video_width / 2 - r / 2
if position == 'bottom':
# Place baseline at 5% from the bottom; descender runs below
# Place baseline at 8% from the bottom; descender runs below
# Additional lines grow upward from the same anchor
y = self.video_height * 0.92 - ascender - lines_written * line_spacing
elif position == 'top':
y = 10 + lines_written * line_spacing
Expand Down
15 changes: 14 additions & 1 deletion tests/test_subtitler_image_based.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@

import os
from unittest import skip

import pytest
from PIL import Image, ImageDraw, ImageFont

from pycaption import SRTReader
from pycaption.base import Caption, CaptionNode
from pycaption.exceptions import CaptionRendererError
from pycaption.filtergraph import FiltergraphWriter
from pycaption.geometry import Layout, Point, Size, UnitEnum
from pycaption.subtitler_image_based import SubtitleImageBasedWriter

Expand Down Expand Up @@ -112,12 +115,20 @@ class TestBaselineAlignment:

NO_DESCENDER = "AHLEN" # no descenders
WITH_DESCENDER = "gypsy" # descenders: g, y, p
WITH_DESCENDER_TOP = "gypsy\nAHLEN" # descenders: g, y, p
WITH_DESCENDER_BOTTOM = "AHLEN\ngypsy" # descenders: g, y, p


COMBOS = [
("no_desc_x2", [NO_DESCENDER, NO_DESCENDER]),
("desc_x2", [WITH_DESCENDER, WITH_DESCENDER]),
("top_no_bottom_yes", [NO_DESCENDER, WITH_DESCENDER]),
("top_yes_bottom_no", [WITH_DESCENDER, NO_DESCENDER]),
("one_line-no", [NO_DESCENDER]),
("one_line-yes", [WITH_DESCENDER]),
("one_line-yes", [WITH_DESCENDER]),
("two-in-one-a", [WITH_DESCENDER_TOP]),
("tow-in-one-b", [WITH_DESCENDER_BOTTOM]),
]

@pytest.fixture(params=COMBOS, ids=[c[0] for c in COMBOS])
Expand All @@ -140,5 +151,7 @@ def test_baseline_visual(self, combo, tmp_path):
guide.line([(0, baseline_y), (width, baseline_y)], fill=(255, 0, 0, 200), width=1)

out = tmp_path / f"baseline_{name}.png"
out = f"tests/baseline_samples/baseline_{name}.png"
img.save(str(out))
print(f"\nSaved: {out}")
print(f"\nSaved: {out}")

Loading