mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 00:32:43 +10:00
fix: insert fields correctly for rotated pdfs
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
|
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
|
||||||
import fontkit from '@pdf-lib/fontkit';
|
import fontkit from '@pdf-lib/fontkit';
|
||||||
import { PDFDocument } from 'pdf-lib';
|
import { PDFDocument, RotationTypes, degrees, radiansToDegrees } from 'pdf-lib';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_HANDWRITING_FONT_SIZE,
|
DEFAULT_HANDWRITING_FONT_SIZE,
|
||||||
@ -37,7 +38,25 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
|||||||
throw new Error(`Page ${field.page} does not exist`);
|
throw new Error(`Page ${field.page} does not exist`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { width: pageWidth, height: pageHeight } = page.getSize();
|
const pageRotation = page.getRotation();
|
||||||
|
|
||||||
|
let pageRotationInDegrees = match(pageRotation.type)
|
||||||
|
.with(RotationTypes.Degrees, () => pageRotation.angle)
|
||||||
|
.with(RotationTypes.Radians, () => radiansToDegrees(pageRotation.angle))
|
||||||
|
.exhaustive();
|
||||||
|
|
||||||
|
// Round to the closest multiple of 90 degrees.
|
||||||
|
pageRotationInDegrees = Math.round(pageRotationInDegrees / 90) * 90;
|
||||||
|
|
||||||
|
const isPageRotatedToLandscape = pageRotationInDegrees === 90 || pageRotationInDegrees === 270;
|
||||||
|
|
||||||
|
let { width: pageWidth, height: pageHeight } = page.getSize();
|
||||||
|
|
||||||
|
// For landscape pages, swap the width and height so we can calculate item position the same way
|
||||||
|
// the user placed it in the editor.
|
||||||
|
if (isPageRotatedToLandscape) {
|
||||||
|
[pageWidth, pageHeight] = [pageHeight, pageWidth];
|
||||||
|
}
|
||||||
|
|
||||||
const fieldWidth = pageWidth * (Number(field.width) / 100);
|
const fieldWidth = pageWidth * (Number(field.width) / 100);
|
||||||
const fieldHeight = pageHeight * (Number(field.height) / 100);
|
const fieldHeight = pageHeight * (Number(field.height) / 100);
|
||||||
@ -65,17 +84,31 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
|||||||
imageWidth = imageWidth * scalingFactor;
|
imageWidth = imageWidth * scalingFactor;
|
||||||
imageHeight = imageHeight * scalingFactor;
|
imageHeight = imageHeight * scalingFactor;
|
||||||
|
|
||||||
const imageX = fieldX + (fieldWidth - imageWidth) / 2;
|
let imageX = fieldX + (fieldWidth - imageWidth) / 2;
|
||||||
let imageY = fieldY + (fieldHeight - imageHeight) / 2;
|
let imageY = fieldY + (fieldHeight - imageHeight) / 2;
|
||||||
|
|
||||||
// Invert the Y axis since PDFs use a bottom-left coordinate system
|
// Invert the Y axis since PDFs use a bottom-left coordinate system
|
||||||
imageY = pageHeight - imageY - imageHeight;
|
imageY = pageHeight - imageY - imageHeight;
|
||||||
|
|
||||||
|
if (pageRotationInDegrees !== 0) {
|
||||||
|
const adjustedPosition = adjustPositionForRotation(
|
||||||
|
pageWidth,
|
||||||
|
pageHeight,
|
||||||
|
imageX,
|
||||||
|
imageY,
|
||||||
|
pageRotationInDegrees,
|
||||||
|
);
|
||||||
|
|
||||||
|
imageX = adjustedPosition.xPos;
|
||||||
|
imageY = adjustedPosition.yPos;
|
||||||
|
}
|
||||||
|
|
||||||
page.drawImage(image, {
|
page.drawImage(image, {
|
||||||
x: imageX,
|
x: imageX,
|
||||||
y: imageY,
|
y: imageY,
|
||||||
width: imageWidth,
|
width: imageWidth,
|
||||||
height: imageHeight,
|
height: imageHeight,
|
||||||
|
rotate: degrees(pageRotationInDegrees),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const longestLineInTextForWidth = field.customText
|
const longestLineInTextForWidth = field.customText
|
||||||
@ -90,17 +123,31 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
|||||||
fontSize = Math.max(Math.min(fontSize * scalingFactor, maxFontSize), minFontSize);
|
fontSize = Math.max(Math.min(fontSize * scalingFactor, maxFontSize), minFontSize);
|
||||||
textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
||||||
|
|
||||||
const textX = fieldX + (fieldWidth - textWidth) / 2;
|
let textX = fieldX + (fieldWidth - textWidth) / 2;
|
||||||
let textY = fieldY + (fieldHeight - textHeight) / 2;
|
let textY = fieldY + (fieldHeight - textHeight) / 2;
|
||||||
|
|
||||||
// Invert the Y axis since PDFs use a bottom-left coordinate system
|
// Invert the Y axis since PDFs use a bottom-left coordinate system
|
||||||
textY = pageHeight - textY - textHeight;
|
textY = pageHeight - textY - textHeight;
|
||||||
|
|
||||||
|
if (pageRotationInDegrees !== 0) {
|
||||||
|
const adjustedPosition = adjustPositionForRotation(
|
||||||
|
pageWidth,
|
||||||
|
pageHeight,
|
||||||
|
textX,
|
||||||
|
textY,
|
||||||
|
pageRotationInDegrees,
|
||||||
|
);
|
||||||
|
|
||||||
|
textX = adjustedPosition.xPos;
|
||||||
|
textY = adjustedPosition.yPos;
|
||||||
|
}
|
||||||
|
|
||||||
page.drawText(field.customText, {
|
page.drawText(field.customText, {
|
||||||
x: textX,
|
x: textX,
|
||||||
y: textY,
|
y: textY,
|
||||||
size: fontSize,
|
size: fontSize,
|
||||||
font,
|
font,
|
||||||
|
rotate: degrees(pageRotationInDegrees),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,3 +164,33 @@ export const insertFieldInPDFBytes = async (
|
|||||||
|
|
||||||
return await pdfDoc.save();
|
return await pdfDoc.save();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const adjustPositionForRotation = (
|
||||||
|
pageWidth: number,
|
||||||
|
pageHeight: number,
|
||||||
|
xPos: number,
|
||||||
|
yPos: number,
|
||||||
|
pageRotationInDegrees: number,
|
||||||
|
) => {
|
||||||
|
if (pageRotationInDegrees === 270) {
|
||||||
|
xPos = pageWidth - xPos;
|
||||||
|
|
||||||
|
[xPos, yPos] = [yPos, xPos];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageRotationInDegrees === 90) {
|
||||||
|
yPos = pageHeight - yPos;
|
||||||
|
|
||||||
|
[xPos, yPos] = [yPos, xPos];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageRotationInDegrees === 180) {
|
||||||
|
xPos = pageWidth - xPos;
|
||||||
|
yPos = pageHeight - yPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
xPos,
|
||||||
|
yPos,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user