Uploaded signatures/stamps aren't always transparent PNGs, so an opaque stamp overlapping the signature/name would cover them. Extract the signatory-block geometry into a tested helper (signatoryLayout): the signature is centred over the name and the stamp sits to its RIGHT with a 10px gap — never overlapping. - lib/po-export-layout.ts (signatoryLayout) + unit test - export route uses it instead of inline overlap math Verified in a real export: signature 175-328px (centred), stamp 338-405px (10px gap, no overlap), stamp drawn behind the signature. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
1.2 KiB
TypeScript
32 lines
1.2 KiB
TypeScript
// Geometry for the exported PO's left signatory block (cols A-D).
|
|
// The approver signature is centred over the name; the company stamp/seal sits to
|
|
// its RIGHT with a gap so it never overlays the signature or name — important
|
|
// because uploaded signatures/stamps aren't always transparent PNGs.
|
|
|
|
export interface Size { width: number; height: number }
|
|
|
|
export interface SignatoryLayout {
|
|
sigLeft: number | null; // px from the block's left edge, or null when no signature
|
|
stampLeft: number | null; // px from the block's left edge, or null when no stamp
|
|
}
|
|
|
|
export function signatoryLayout(opts: {
|
|
blockPx: number;
|
|
sig: Size | null;
|
|
stamp: Size | null;
|
|
gap?: number;
|
|
}): SignatoryLayout {
|
|
const gap = opts.gap ?? 10;
|
|
const sigLeft = opts.sig ? Math.round((opts.blockPx - opts.sig.width) / 2) : null; // centred
|
|
|
|
let stampLeft: number | null = null;
|
|
if (opts.stamp) {
|
|
stampLeft =
|
|
sigLeft != null && opts.sig
|
|
? Math.min(opts.blockPx - opts.stamp.width, sigLeft + opts.sig.width + gap) // clear of the signature
|
|
: opts.blockPx - opts.stamp.width - 6; // no signature → right-align in the block
|
|
stampLeft = Math.max(0, stampLeft);
|
|
}
|
|
|
|
return { sigLeft, stampLeft };
|
|
}
|