fix(po): size XLSX export images by pixels (aspect preserved) #60
1 changed files with 40 additions and 18 deletions
|
|
@ -459,14 +459,49 @@ export async function GET(request: NextRequest, { params }: Props) {
|
|||
ws.getRow(SIG_ROW + 1).height = 14;
|
||||
ws.getRow(SIG_ROW + 2).height = 14;
|
||||
|
||||
// Left sig block (approver — the manager who authorized the PO)
|
||||
if (signatureBase64) {
|
||||
// Left signatory block (cols A-D). Position images by absolute pixels via native
|
||||
// EMU offsets — ExcelJS's fractional-column anchors don't map cleanly to pixels.
|
||||
const EMU = 9525; // EMU per pixel
|
||||
const COL_PX = [22, 4, 28, 15, 8, 15, 15, 8, 16].map((w) => Math.round(w * 7 + 5));
|
||||
const SIG_BLOCK_PX = COL_PX[0] + COL_PX[1] + COL_PX[2] + COL_PX[3]; // A-D
|
||||
const anchorAt = (leftPx: number, row: number) => {
|
||||
let x = 0;
|
||||
for (let c = 0; c < COL_PX.length - 1; c++) {
|
||||
if (leftPx < x + COL_PX[c]) {
|
||||
return { nativeCol: c, nativeColOff: Math.round((leftPx - x) * EMU), nativeRow: row, nativeRowOff: 0 } as unknown as ExcelJS.Anchor;
|
||||
}
|
||||
x += COL_PX[c];
|
||||
}
|
||||
return { nativeCol: COL_PX.length - 1, nativeColOff: Math.round((leftPx - x) * EMU), nativeRow: row, nativeRowOff: 0 } as unknown as ExcelJS.Anchor;
|
||||
};
|
||||
|
||||
const sigExt = signatureBase64 ? scaleToBox(signatureSize ?? { width: 360, height: 96 }, 165, 44) : null;
|
||||
const sigLeft = sigExt ? Math.round((SIG_BLOCK_PX - sigExt.width) / 2) : 0; // centred over the name
|
||||
|
||||
// Stamp / seal — drawn FIRST so it sits BEHIND the signature, tucked to its right.
|
||||
if (stampImg) {
|
||||
const stampExt = scaleToBox(stampImg, 80, 66);
|
||||
const stampLeft = sigExt
|
||||
? Math.min(SIG_BLOCK_PX - stampExt.width, sigLeft + sigExt.width - Math.round(stampExt.width * 0.35))
|
||||
: SIG_BLOCK_PX - stampExt.width - 6;
|
||||
const stampId = wb.addImage({
|
||||
base64: stampImg.base64,
|
||||
extension: stampImg.mime === "image/jpeg" ? "jpeg" : "png",
|
||||
});
|
||||
ws.addImage(stampId, {
|
||||
tl: anchorAt(Math.max(0, stampLeft), SIG_ROW - 1),
|
||||
ext: stampExt,
|
||||
editAs: "oneCell",
|
||||
});
|
||||
}
|
||||
|
||||
// Approver signature — drawn AFTER the stamp (on top), centred over the name.
|
||||
if (signatureBase64 && sigExt) {
|
||||
const imgType = signatureMime === "image/jpeg" ? "jpeg" : "png";
|
||||
const imgId = wb.addImage({ base64: signatureBase64, extension: imgType });
|
||||
// Sized by pixels (aspect preserved) and placed inside the signatory box
|
||||
ws.addImage(imgId, {
|
||||
tl: { col: 0.4, row: SIG_ROW - 1 } as unknown as ExcelJS.Anchor,
|
||||
ext: scaleToBox(signatureSize ?? { width: 360, height: 96 }, 165, 44),
|
||||
tl: anchorAt(Math.max(0, sigLeft), SIG_ROW - 1),
|
||||
ext: sigExt,
|
||||
editAs: "oneCell",
|
||||
});
|
||||
sc(SIG_ROW, 1, "", { border: { top: thin(), left: thin(), right: thin() } });
|
||||
|
|
@ -485,19 +520,6 @@ export async function GET(request: NextRequest, { params }: Props) {
|
|||
ws.getRow(SIG_ROW + 2).height = 14;
|
||||
ws.getRow(SIG_ROW + 3).height = 14;
|
||||
|
||||
// Company stamp / seal — overlays the right of the approver's signatory block (aspect preserved)
|
||||
if (stampImg) {
|
||||
const stampId = wb.addImage({
|
||||
base64: stampImg.base64,
|
||||
extension: stampImg.mime === "image/jpeg" ? "jpeg" : "png",
|
||||
});
|
||||
ws.addImage(stampId, {
|
||||
tl: { col: 2.35, row: SIG_ROW - 1 } as unknown as ExcelJS.Anchor,
|
||||
ext: scaleToBox(stampImg, 80, 66),
|
||||
editAs: "oneCell",
|
||||
});
|
||||
}
|
||||
|
||||
// Right sig block (vendor)
|
||||
const vName = po.vendor?.name ?? "";
|
||||
sc(SIG_ROW, 6, vName, { font: fBold, border: { top: thin(), left: thin(), right: thin() }, align: alignC });
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue