Merge branch 'main' into feat/public-profiles

This commit is contained in:
Lucas Smith
2024-06-13 15:20:26 +10:00
committed by GitHub
26 changed files with 608 additions and 584 deletions

View File

@ -1,6 +1,7 @@
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
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 {
DEFAULT_HANDWRITING_FONT_SIZE,
@ -37,7 +38,32 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
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();
// PDFs can have pages that are rotated, which are correctly rendered in the frontend.
// However when we load the PDF in the backend, the rotation is applied.
//
// To account for this, we swap the width and height for pages that are rotated by 90/270
// degrees. This is so we can calculate the virtual position the field was placed if it
// was correctly oriented in the frontend.
//
// Then when we insert the fields, we apply a transformation to the position of the field
// so it is rotated correctly.
if (isPageRotatedToLandscape) {
[pageWidth, pageHeight] = [pageHeight, pageWidth];
}
const fieldWidth = pageWidth * (Number(field.width) / 100);
const fieldHeight = pageHeight * (Number(field.height) / 100);
@ -65,17 +91,31 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
imageWidth = imageWidth * scalingFactor;
imageHeight = imageHeight * scalingFactor;
const imageX = fieldX + (fieldWidth - imageWidth) / 2;
let imageX = fieldX + (fieldWidth - imageWidth) / 2;
let imageY = fieldY + (fieldHeight - imageHeight) / 2;
// Invert the Y axis since PDFs use a bottom-left coordinate system
imageY = pageHeight - imageY - imageHeight;
if (pageRotationInDegrees !== 0) {
const adjustedPosition = adjustPositionForRotation(
pageWidth,
pageHeight,
imageX,
imageY,
pageRotationInDegrees,
);
imageX = adjustedPosition.xPos;
imageY = adjustedPosition.yPos;
}
page.drawImage(image, {
x: imageX,
y: imageY,
width: imageWidth,
height: imageHeight,
rotate: degrees(pageRotationInDegrees),
});
} else {
const longestLineInTextForWidth = field.customText
@ -90,17 +130,31 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
fontSize = Math.max(Math.min(fontSize * scalingFactor, maxFontSize), minFontSize);
textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
const textX = fieldX + (fieldWidth - textWidth) / 2;
let textX = fieldX + (fieldWidth - textWidth) / 2;
let textY = fieldY + (fieldHeight - textHeight) / 2;
// Invert the Y axis since PDFs use a bottom-left coordinate system
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, {
x: textX,
y: textY,
size: fontSize,
font,
rotate: degrees(pageRotationInDegrees),
});
}
@ -117,3 +171,32 @@ export const insertFieldInPDFBytes = async (
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];
}
// Invert all the positions since it's rotated by 180 degrees.
if (pageRotationInDegrees === 180) {
xPos = pageWidth - xPos;
yPos = pageHeight - yPos;
}
return {
xPos,
yPos,
};
};

View File

@ -480,6 +480,7 @@ export const createDocumentFromDirectTemplate = async ({
// Send email to template owner.
const emailTemplate = createElement(DocumentCreatedFromDirectTemplateEmailTemplate, {
recipientName: directRecipientEmail,
recipientRole: directTemplateRecipient.role,
documentLink: `${formatDocumentsPath(document.team?.url)}/${document.id}`,
documentName: document.title,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000',