fix(po): size XLSX export images by pixels (aspect preserved) #60

Merged
shad0w merged 2 commits from fix/xlsx-export-asset-sizing into master 2026-06-21 08:21:30 +00:00
Owner

Fixes the XLSX export where the logo, signature, stamp, and CANCELLED watermark were sized/stretched incorrectly (the PDF was already fine).

Cause

Those images were placed with ExcelJS two-cell (tl/br) anchors, which stretch an image to fill a cell range — so aspect ratios were distorted, and the watermark (which has transparent margins) only filled the centre of its box, looking small and squished. The PDF looks right because CSS sizes by aspect (object-fit: contain / fixed text size).

Fix

  • New lib/image-size.ts: getImageSize (PNG/JPEG/WebP header parse, no deps) + scaleToBox (fit within a px box, aspect preserved).
  • Export route now places each image with a one-cell tl + pixel ext, sized to match the PDF: logo ≤96×52, signature ≤165×44, stamp ≤80×66, watermark ≤880×720.
  • Watermark regenerated as a landscape canvas with the text filling it, so it spans the page diagonally like the PDF instead of sitting small in the middle.

Verification

  • Generated an XLSX with the sample assets and confirmed via the drawing XML that all four images are now oneCellAnchors with fixed pixel sizes — 49×52 / 45×44 / 67×66 / 880×629 — aspect preserved, no cell-range stretching.
  • Unit test for getImageSize + scaleToBox. pnpm test green (199), type-check clean.

I can't open Excel in this environment to render the final sheet, so the structural proof (anchor type + pixel sizes) is the verification here — please eyeball the exported .xlsx to confirm it now matches the sample.

🤖 Generated with Claude Code

Fixes the XLSX export where the **logo, signature, stamp, and CANCELLED watermark** were sized/stretched incorrectly (the PDF was already fine). ## Cause Those images were placed with ExcelJS **two-cell (`tl`/`br`) anchors**, which stretch an image to fill a *cell range* — so aspect ratios were distorted, and the watermark (which has transparent margins) only filled the centre of its box, looking small and squished. The PDF looks right because CSS sizes by aspect (`object-fit: contain` / fixed text size). ## Fix - New `lib/image-size.ts`: `getImageSize` (PNG/JPEG/WebP header parse, no deps) + `scaleToBox` (fit within a px box, aspect preserved). - Export route now places each image with a **one-cell `tl` + pixel `ext`**, sized to match the PDF: logo ≤96×52, signature ≤165×44, stamp ≤80×66, watermark ≤880×720. - Watermark regenerated as a landscape canvas with the text filling it, so it **spans the page diagonally like the PDF** instead of sitting small in the middle. ## Verification - Generated an XLSX with the sample assets and confirmed via the drawing XML that all four images are now `oneCellAnchor`s with fixed pixel sizes — **49×52 / 45×44 / 67×66 / 880×629** — aspect preserved, no cell-range stretching. - Unit test for `getImageSize` + `scaleToBox`. `pnpm test` green (199), type-check clean. > I can't open Excel in this environment to render the final sheet, so the structural proof (anchor type + pixel sizes) is the verification here — please eyeball the exported `.xlsx` to confirm it now matches the sample. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
shad0w added 1 commit 2026-06-21 07:57:34 +00:00
fix(po): size XLSX export images by pixels (aspect preserved)
All checks were successful
PR checks / checks (pull_request) Successful in 34s
PR checks / integration (pull_request) Successful in 26s
6677ef4fcf
The logo, signature, stamp and cancelled watermark were placed with ExcelJS
two-cell (tl/br) anchors, which stretch each image to fill a cell range —
distorting them and making the watermark text small/squished. The PDF looked
fine because CSS sizes by aspect.

- New lib/image-size.ts: getImageSize (PNG/JPEG/WebP header parse) + scaleToBox.
- Export route now places each image with a oneCell `tl` + pixel `ext`,
  aspect preserved and matched to the PDF sizes (logo ≤96×52, signature ≤165×44,
  stamp ≤80×66, watermark ≤880×720).
- Watermark regenerated as a landscape canvas with the text filling it, so it
  spans the page like the PDF instead of sitting small in the centre.
- Unit test for getImageSize + scaleToBox.

Verified structurally: generated XLSX uses oneCellAnchors with fixed pixel
ext sizes (49×52 / 45×44 / 67×66 / 880×629), not stretched cell ranges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
shad0w added 1 commit 2026-06-21 08:15:59 +00:00
fix(po): centre signature over name; stamp to its right and behind it
All checks were successful
PR checks / checks (pull_request) Successful in 34s
PR checks / integration (pull_request) Successful in 25s
610c9aa56d
In the XLSX signatory block, place the approver signature centred over the
name and tuck the stamp to its right with a slight overlap. The stamp is now
drawn before the signature so it layers behind it (Excel z-order = add order).

Images are positioned by absolute pixels via native EMU offsets — ExcelJS's
fractional-column anchors don't map cleanly to pixels (the stamp was landing
on top of the signature centre instead of to its right). Verified in a real
export: signature centre 252px in the 503px A-D block (centred), stamp to the
right (305-372px), stamp drawn behind the signature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
shad0w merged commit cb661949d9 into master 2026-06-21 08:21:30 +00:00
Author
Owner

Reference issue #53

Reference issue #53
Sign in to join this conversation.
No description provided.