Commit Graph

4 Commits

Author SHA1 Message Date
till e87c61435c fix: code-review findings in the print module
Security and robustness:
- EXIF orientation is now applied before any geometry. Phone photos carry the
  rotation only as metadata; sharp was cropping the unrotated raster, so a
  portrait shot came out of the printer sideways and wrongly framed.
- renderCell no longer materialises the padded image at source resolution.
  It is one extract-resize-extend chain now, which is also sharp's internal
  order. A panorama into a narrow contain target used to build a ~960 MB
  intermediate and then fail; it is 90 ms and a few MB now.
- Target size is capped (300 Mpx) and bleedMm is clamped in /api/print/single,
  which had no bound at all.
- The delivery gallery is validated before use - posixpath.join let a crafted
  name escape the target's base folder and create directories there.
- Sheet requests are capped at 500 pieces and the packer has a step budget, so
  a degenerate request cannot block the single-threaded server.
- Print presets: delete only your own (admins all), config size limit, count
  limit, and by_name honours anonymous_generations.
- Telegram callbacks require an active pairing, like every other path.
- Error responses no longer leak storage paths or delivery hostnames.

Correctness:
- allowRotate:undefined now means allowed, consistently with the packer.
- The many-formats shortcut no longer drops a format that only fits rotated.
- unplaced names the format that is actually missing, not the first one.
- Crop marks never sit inside the printed bleed - the offset is raised.
- capacity() computes the grid instead of probing with 200 copies.
- Image keys in the sheet cannot collide with a cell literally named x::rot.
- labelMm keeps real decimals; parseSizeMm reads a:b as width:height, so
  3:4/15 is portrait and 4:3/15 is landscape.
- The footer is skipped when there is no free space at the bottom.
- The UI warns when corner marks do not fit the margin, and when continuous
  guides are used with mixed sizes.

Tests: 21 -> 31, each finding has a regression test.
2026-08-18 07:50:11 +00:00
till 7d42782c41 feat: all AI formats printable, crop-vs-border rule, mobile layout, Passbilder rename
Print now offers every format the AI pipeline knows (9x13 to 60x90, DIN A6-A2,
squares, poster/frame sizes to 70x100, and the screen ratios as physical sizes).
When a picture does not match the target ratio the user picks per image between
cropping to fill and keeping the whole picture on a border colour - never a
stretch. The module is called Passbilder now; /druck redirects.

Fixes two real defects: sharp runs extend after resize, so padded cells came out
oversized (a 35x45 mm cell became 35x171 mm with a border), and the grid packer
rotated a single portrait photo just because more would fit sideways.

Mobile: cards become rows, touch targets ~40px, crop editor as a bottom sheet.
Verified at 390/820/1440px - all three produce the same PDF.
2026-08-18 07:04:08 +00:00
till f6b81a0f5e fix: drop tsx dev dependency, run tests with node's native type stripping
The tsx postinstall pulls its own esbuild binary, which fails inside the
Docker build ("Expected 0.28.2 but got 0.27.7") and broke the deploy.
Tests now run on node --experimental-strip-types, so the image installs
nothing extra.
2026-08-18 06:45:28 +00:00
till a651025422 feat: mm-based print layout math and paper/photo size tables
Pure geometry, no I/O: DIN A6-A2 incl. A3+ (329x483), photo-paper cuts,
US Letter/Legal, biometric and small photo formats, plus a forgiving free
size parser (12x15 cm, 35x45mm, 5, 4:3/15). Packing: exact grid for uniform
sizes, MaxRects with one fixed orientation per format for mixed sets, so
small prints fill the space next to a large one. Crop marks as line segments
(corner marks outside the trim box; continuous guides that never cross a
motif). Covered by tests/printlayout.test.ts.
2026-08-18 06:20:45 +00:00