"""Full-bleed photo with a compact, single-line camera-style watermark.""" from io import BytesIO from PIL import Image, ImageDraw from pigway_frame_flow.renderers.images import convert from pigway_frame_flow.renderers.photo_layout import WatermarkLayout def render_captioned_photo( data, caption, font_loader, fit="cover", profile="color", *, size=(1200, 1600), layout_provider=None, centering=(0.5, 0.5), quality=93, respect_fit=False, ): width, height = size layout = (layout_provider or WatermarkLayout()).layout(size, caption) if not ( 0 < layout.footer_height < height and 0 < layout.margin < width / 2 and layout.font_size > 0 ): raise ValueError("Invalid photo layout") photo_height = height - layout.footer_height # Legacy tasks used contain: watermark photos now fill the photo area, # keeping aspect ratio and cropping rather than stretching or letterboxing. photo_data = convert( data, fit if respect_fit else ("smart" if fit == "smart" else "cover"), profile, size=(width, photo_height), centering=centering, quality=quality, ) canvas = Image.new("RGB", size, "white") with Image.open(BytesIO(photo_data), formats=("JPEG", "PNG", "WEBP")) as photo: canvas.paste(photo, (0, 0)) draw = ImageDraw.Draw(canvas) values = [ " ".join(str(caption.get(key, "")).split()) for key in ("date", "people", "title", "place") ] text = " · ".join(value for value in values if value) font_size = layout.font_size font = font_loader(font_size) available = width - 2 * layout.margin while font_size > 24 and draw.textlength(text, font=font) > available: font_size -= 2 font = font_loader(font_size) # Shorten the title before touching the date/location. Never wrap the band. while draw.textlength(text, font=font) > available and len(values[2]) > 2: values[2] = values[2].rstrip("…")[:-1] + "…" text = " · ".join(value for value in values if value) while draw.textlength(text, font=font) > available and len(text) > 2: text = text.rstrip("…")[:-1] + "…" draw.text( (width - layout.margin, photo_height + layout.footer_height / 2), text, font=font, fill="black", anchor="rm", ) out = BytesIO() canvas.save(out, "JPEG", quality=quality, optimize=True) return out.getvalue()