ADR 0007: Pillow for imaging¶
Status¶
Accepted — 2026-09-19
Context¶
After a provider returns art, thumbforge must fit it to the target 16:9 size, composite the title and "Part N" badge (ADR 0008), check YouTube compliance and write a JPEG/PNG under the size limit. Verified YouTube requirements (support.google.com/youtube/answer/72431): recommended 3840×2160, minimum width 640, 16:9, JPG/PNG, 2 MB limit on mobile and 50 MB on desktop.
Decision¶
- Pillow is the sole imaging library, used in
src/thumbforge/imaging/:fit.py: resize and centre-crop any input to the configured[output] width × height(default 1920×1080, decision D2) usingImage.Resampling.LANCZOS; upscaling from the provider's native size (Antigravity: 1376×768 pending spike S3) is permitted.overlay.py: text rendering withImageDraw.textandImageFont.truetype, stroke and shadow, auto-shrink to fit the title box.fonts.py: resolves fonts from the layout spec, falling back to a bundled OFL font (Inter, decision D6) inimaging/fonts/; golden tests use only bundled fonts.compliance.py: the project check is stricter than YouTube's floor — aspect exactly 16:9 (±1 px), width ≥ 1280, format ∈ {JPEG, PNG}, file size ≤[output] max_bytes(default 2 097 152;--max-bytesmay raise it up to 50 MB), sRGB (ICC profile stripped or sRGB). Failure raisesComplianceError(exit5) and is stored inasset.compliance_report_json.
- Output encoding:
format="jpeg", quality=90by default; if the result exceedsmax_bytes, quality is stepped down by 5 to a floor of 70 before failing. - Provider output is always opened through Pillow as the definition of "valid image" (
PLAN.md§4.3 step 3). - Golden tests (
-m golden) compare overlay output against checked-in PNGs with a small per-pixel tolerance.
Consequences¶
- One dependency covers decode, resize, text and encode; wheels exist for 3.14 on Windows and Linux.
- Text quality depends on Pillow's FreeType build, which is bundled in official wheels.
- Compliance numbers live in one module and are cited by
docs/specs/phase-5-imaging.md.
Alternatives considered¶
- OpenCV — rejected: large binary wheel, poor text rendering, no advantage for still-image compositing.
- Wand / ImageMagick — rejected: external binary dependency on Windows.
- Asking the provider to render text — rejected in ADR 0008.
pyvips— rejected: needs libvips installed separately on Windows; Pillow is self-contained.