mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
|
|
import fontkit from '@pdf-lib/fontkit';
|
|
import { PDFDocument } from 'pdf-lib';
|
|
|
|
import {
|
|
DEFAULT_HANDWRITING_FONT_SIZE,
|
|
DEFAULT_STANDARD_FONT_SIZE,
|
|
MIN_HANDWRITING_FONT_SIZE,
|
|
MIN_STANDARD_FONT_SIZE,
|
|
} from '@documenso/lib/constants/pdf';
|
|
import { FieldType } from '@documenso/prisma/client';
|
|
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
|
|
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
|
|
|
export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignature) => {
|
|
const fontCaveat = await fetch(process.env.FONT_CAVEAT_URI).then(async (res) =>
|
|
res.arrayBuffer(),
|
|
);
|
|
|
|
const fontNoto = await fetch(process.env.FONT_NOTO_SANS_URI).then(async (res) =>
|
|
res.arrayBuffer(),
|
|
);
|
|
|
|
const isSignatureField = isSignatureFieldType(field.type);
|
|
|
|
pdf.registerFontkit(fontkit);
|
|
|
|
const pages = pdf.getPages();
|
|
|
|
const minFontSize = isSignatureField ? MIN_HANDWRITING_FONT_SIZE : MIN_STANDARD_FONT_SIZE;
|
|
const maxFontSize = isSignatureField ? DEFAULT_HANDWRITING_FONT_SIZE : DEFAULT_STANDARD_FONT_SIZE;
|
|
let fontSize = maxFontSize;
|
|
|
|
const page = pages.at(field.page - 1);
|
|
|
|
if (!page) {
|
|
throw new Error(`Page ${field.page} does not exist`);
|
|
}
|
|
|
|
const { width: pageWidth, height: pageHeight } = page.getSize();
|
|
|
|
const fieldWidth = pageWidth * (Number(field.width) / 100);
|
|
const fieldHeight = pageHeight * (Number(field.height) / 100);
|
|
|
|
const fieldX = pageWidth * (Number(field.positionX) / 100);
|
|
const fieldY = pageHeight * (Number(field.positionY) / 100);
|
|
|
|
const font = await pdf.embedFont(isSignatureField ? fontCaveat : fontNoto);
|
|
|
|
if (field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE) {
|
|
await pdf.embedFont(fontCaveat);
|
|
}
|
|
|
|
const isInsertingImage =
|
|
isSignatureField && typeof field.Signature?.signatureImageAsBase64 === 'string';
|
|
|
|
if (isSignatureField && isInsertingImage) {
|
|
const image = await pdf.embedPng(field.Signature?.signatureImageAsBase64 ?? '');
|
|
|
|
let imageWidth = image.width;
|
|
let imageHeight = image.height;
|
|
|
|
const scalingFactor = Math.min(fieldWidth / imageWidth, fieldHeight / imageHeight, 1);
|
|
|
|
imageWidth = imageWidth * scalingFactor;
|
|
imageHeight = imageHeight * scalingFactor;
|
|
|
|
const 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;
|
|
|
|
page.drawImage(image, {
|
|
x: imageX,
|
|
y: imageY,
|
|
width: imageWidth,
|
|
height: imageHeight,
|
|
});
|
|
} else {
|
|
const longestLineInTextForWidth = field.customText
|
|
.split('\n')
|
|
.sort((a, b) => b.length - a.length)[0];
|
|
|
|
let textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
|
const textHeight = font.heightAtSize(fontSize);
|
|
|
|
const scalingFactor = Math.min(fieldWidth / textWidth, fieldHeight / textHeight, 1);
|
|
|
|
fontSize = Math.max(Math.min(fontSize * scalingFactor, maxFontSize), minFontSize);
|
|
textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
|
|
|
const 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;
|
|
|
|
page.drawText(field.customText, {
|
|
x: textX,
|
|
y: textY,
|
|
size: fontSize,
|
|
font,
|
|
});
|
|
}
|
|
|
|
return pdf;
|
|
};
|
|
|
|
export const insertFieldInPDFBytes = async (
|
|
pdf: ArrayBuffer | Uint8Array | string,
|
|
field: FieldWithSignature,
|
|
) => {
|
|
const pdfDoc = await PDFDocument.load(pdf);
|
|
|
|
await insertFieldInPDF(pdfDoc, field);
|
|
|
|
return await pdfDoc.save();
|
|
};
|