from io import BytesIO import pytest from PIL import Image, ImageDraw from pigway_frame_flow.renderers.photo_caption import render_captioned_photo from pigway_frame_flow.renderers.photo_layout import PhotoLayout, WatermarkLayout def test_watermark_includes_filename_and_recognized_people(service, monkeypatch): drawn = [] original = ImageDraw.ImageDraw.text def text(draw, xy, value, *args, **kwargs): drawn.append(value) return original(draw, xy, value, *args, **kwargs) monkeypatch.setattr(ImageDraw.ImageDraw, "text", text) stream = BytesIO() Image.new("RGB", (160, 120), "blue").save(stream, "JPEG") render_captioned_photo( stream.getvalue(), { "date": "2026-10-17", "title": "Family", "filename": "one.jpg", "people": "Alice & Bob", }, service.renderer.font, size=(1600, 1200), ) assert drawn == ["2026-10-17 · Alice & Bob · Family · one.jpg"] @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)