Use full-bleed photos with a single-line bottom watermark
This commit is contained in:
@@ -25,7 +25,7 @@ def test_canvas_respects_rotation_and_explicit_direction(rotation, mode):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("profile", ["color", "grayscale"])
|
||||
def test_landscape_caption_keeps_three_rows(service, profile):
|
||||
def test_landscape_caption_uses_compact_watermark(service, profile):
|
||||
out = BytesIO()
|
||||
Image.new("RGB", (1600, 900), "blue").save(out, "JPEG")
|
||||
data = render_captioned_photo(
|
||||
@@ -37,8 +37,9 @@ def test_landscape_caption_keeps_three_rows(service, profile):
|
||||
)
|
||||
with Image.open(BytesIO(data)) as image:
|
||||
assert image.size == (1600, 1200)
|
||||
for y in [984, 1048, 1100]:
|
||||
assert image.crop((60, y, 1540, y + 50)).convert("L").getextrema()[0] < 50
|
||||
assert image.crop((30, 1128, 1570, 1200)).convert("L").getextrema()[0] < 50
|
||||
assert image.getpixel((20, 1100))[0] < 230
|
||||
assert image.getpixel((20, 1190))[0] > 240
|
||||
pixel = image.getpixel((800, 480))
|
||||
assert (
|
||||
len(set(pixel)) <= 2
|
||||
|
||||
@@ -46,7 +46,7 @@ def test_child_folder_inherits_date_and_location():
|
||||
|
||||
|
||||
@pytest.mark.parametrize("profile", ["color", "grayscale"])
|
||||
def test_native_caption_layout_has_three_separate_lines(service, profile):
|
||||
def test_native_caption_layout_has_single_watermark_line(service, profile):
|
||||
out = BytesIO()
|
||||
Image.new("RGB", (1600, 900), "blue").save(out, "JPEG")
|
||||
result = render_captioned_photo(
|
||||
@@ -57,9 +57,9 @@ def test_native_caption_layout_has_three_separate_lines(service, profile):
|
||||
)
|
||||
with Image.open(BytesIO(result)) as image:
|
||||
assert image.size == (1200, 1600)
|
||||
for y in [1320, 1410, 1480]:
|
||||
darkest = image.crop((50, y, 1150, y + 65)).convert("L").getextrema()[0]
|
||||
assert darkest < 50, "Caption row is missing"
|
||||
assert image.crop((30, 1528, 1170, 1600)).convert("L").getextrema()[0] < 50
|
||||
assert image.getpixel((20, 1500))[0] < 230
|
||||
assert image.getpixel((20, 1590))[0] > 240
|
||||
pixel = image.getpixel((600, 640))
|
||||
assert (
|
||||
len(set(pixel)) <= 2
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from pigway_frame_flow.renderers.photo_caption import render_captioned_photo
|
||||
from pigway_frame_flow.renderers.photo_layout import PhotoLayout, WatermarkLayout
|
||||
|
||||
|
||||
@pytest.mark.parametrize("size", [(1600, 1200), (1200, 1600)])
|
||||
@pytest.mark.parametrize("fit", ["contain", "cover", "smart"])
|
||||
def test_watermark_has_no_letterboxing(service, size, fit):
|
||||
stream = BytesIO()
|
||||
Image.new("RGB", (400, 100), "blue").save(stream, "JPEG")
|
||||
data = render_captioned_photo(
|
||||
stream.getvalue(),
|
||||
{"title": "Family", "date": "2026-10-06", "place": "Shanghai"},
|
||||
service.renderer.font,
|
||||
fit=fit,
|
||||
size=size,
|
||||
)
|
||||
with Image.open(BytesIO(data)) as image:
|
||||
assert image.size == size
|
||||
layout = WatermarkLayout().layout(size, {})
|
||||
for point in [
|
||||
(10, 10),
|
||||
(size[0] - 10, 10),
|
||||
(10, size[1] - layout.footer_height - 10),
|
||||
]:
|
||||
assert image.getpixel(point)[2] > image.getpixel(point)[0] + 100
|
||||
assert image.getpixel((size[0] - 10, size[1] - 10))[0] > 240
|
||||
assert layout.footer_height / min(size) <= 0.06
|
||||
|
||||
|
||||
def test_layout_provider_is_injectable(service):
|
||||
class Custom:
|
||||
def layout(self, size, caption):
|
||||
return PhotoLayout(100, 30, 40)
|
||||
|
||||
stream = BytesIO()
|
||||
Image.new("RGB", (160, 120), "blue").save(stream, "JPEG")
|
||||
data = render_captioned_photo(
|
||||
stream.getvalue(),
|
||||
{"title": "Custom", "date": "2026-10-06"},
|
||||
service.renderer.font,
|
||||
size=(1600, 1200),
|
||||
layout_provider=Custom(),
|
||||
)
|
||||
with Image.open(BytesIO(data)) as image:
|
||||
assert image.getpixel((1500, 1120))[0] > 240
|
||||
|
||||
|
||||
def test_long_multiline_caption_does_not_wrap_or_hang(service):
|
||||
stream = BytesIO()
|
||||
Image.new("RGB", (160, 120), "blue").save(stream, "JPEG")
|
||||
data = render_captioned_photo(
|
||||
stream.getvalue(),
|
||||
{"title": "Long title\n" * 100, "date": "2026-10-06", "place": "Place " * 100},
|
||||
service.renderer.font,
|
||||
)
|
||||
assert Image.open(BytesIO(data)).size == (1200, 1600)
|
||||
Reference in New Issue
Block a user