test
This commit is contained in:
@ -17,13 +17,17 @@ that maximizes each printed photo's area on a fixed 8.5x11 letter sheet
|
||||
a strip has few photos and a wide page fills better
|
||||
(e.g. 1 photo per shoot, or one big photo).
|
||||
|
||||
The date is printed only inside the footer. Sheets are saved with 300-DPI
|
||||
metadata so their physical print size is well-defined (a full letter page)
|
||||
for CUPS or any other program they are printed from.
|
||||
The date is printed only inside the footer.
|
||||
|
||||
Each sheet is saved twice: a JPEG (300-DPI metadata, handy for viewing or
|
||||
reprinting from other apps) and a PDF twin whose page geometry is exactly a
|
||||
300-DPI letter page. `print_sheet` sends the PDF, so the print pipeline
|
||||
rasterizes exactly what was composed - no image-DPI guessing, no warping.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
@ -234,24 +238,34 @@ def build_sheet(photo_paths, out_path, footer_cfg=None,
|
||||
# Stamp 300 DPI into the file: without it the sheet has no defined
|
||||
# physical size and the print pipeline has to guess (and may stretch).
|
||||
sheet.save(out_path, quality=95, dpi=(300, 300))
|
||||
|
||||
# PDF twin: page size is exactly a 300-DPI letter page, so printing it
|
||||
# reproduces the composed geometry 1:1 (PIL maps pixels at `resolution`
|
||||
# DPI onto the PDF page).
|
||||
sheet.save(Path(out_path).with_suffix(".pdf"), "PDF", resolution=300.0)
|
||||
return out_path
|
||||
|
||||
|
||||
def print_sheet(path, printer=""):
|
||||
"""Send the sheet to the printer via lp. Returns (ok, message).
|
||||
|
||||
fit-to-page scales uniformly, so the sheet's aspect ratio is preserved.
|
||||
Passing the sheet's orientation explicitly keeps a landscape sheet from
|
||||
being rotated or squeezed by driver defaults.
|
||||
Prefers the sheet's PDF twin (exact page geometry) when it exists, and
|
||||
falls back to the image itself. fit-to-page scales uniformly, so the
|
||||
sheet's aspect ratio is preserved. For plain images we also pass the
|
||||
orientation explicitly so a landscape sheet is not rotated or squeezed
|
||||
by driver defaults; a PDF carries its orientation in its page size.
|
||||
"""
|
||||
with Image.open(path) as im:
|
||||
sheet_w, sheet_h = im.size
|
||||
orientation = "landscape" if sheet_w > sheet_h else "portrait"
|
||||
cmd = ["lp", "-o", "media=letter", "-o", "fit-to-page",
|
||||
"-o", orientation]
|
||||
path = Path(path)
|
||||
job = path.with_suffix(".pdf")
|
||||
job = job if job.exists() else path
|
||||
cmd = ["lp", "-o", "media=letter", "-o", "fit-to-page"]
|
||||
if job.suffix.lower() != ".pdf":
|
||||
with Image.open(job) as im:
|
||||
sheet_w, sheet_h = im.size
|
||||
cmd += ["-o", "landscape" if sheet_w > sheet_h else "portrait"]
|
||||
if printer:
|
||||
cmd += ["-d", printer]
|
||||
cmd.append(str(path))
|
||||
cmd.append(str(job))
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
||||
except (OSError, subprocess.TimeoutExpired) as e:
|
||||
|
||||
Reference in New Issue
Block a user