🗃️ 💄 insert text in custom font, typeSignature

This commit is contained in:
Timur Ercan
2023-02-20 18:14:52 +01:00
parent 0cda32d197
commit 9e4874bd27
6 changed files with 82 additions and 802 deletions

View File

@@ -15,6 +15,7 @@
"@documenso/ui": "*", "@documenso/ui": "*",
"@headlessui/react": "^1.7.4", "@headlessui/react": "^1.7.4",
"@heroicons/react": "^2.0.13", "@heroicons/react": "^2.0.13",
"@pdf-lib/fontkit": "^1.1.1",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@types/bcryptjs": "^2.4.2", "@types/bcryptjs": "^2.4.2",
"@types/filesystem": "^0.0.32", "@types/filesystem": "^0.0.32",
@@ -45,7 +46,6 @@
"sass": "^1.57.1", "sass": "^1.57.1",
"short-uuid": "^4.2.2", "short-uuid": "^4.2.2",
"string-to-color": "^2.2.2", "string-to-color": "^2.2.2",
"text2png": "^2.3.0",
"typescript": "4.8.4" "typescript": "4.8.4"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -36,14 +36,19 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
if (!recipients.length) return res.status(200).end(""); if (!recipients.length) return res.status(200).end("");
let sentRequests = 0;
recipients.forEach(async (recipient) => { recipients.forEach(async (recipient) => {
await sendSigningRequest(recipient, document, user).catch((err) => { await sendSigningRequest(recipient, document, user).catch((err) => {
console.log(err); console.log(err);
return res.status(502).end("Coud not send request for signing."); return res.status(502).end("Coud not send request for signing.");
}); });
sentRequests++;
if (sentRequests === recipients.length) {
return res.status(200).end();
}
}); });
return res.status(202).end();
// todo check if recipient has an account and show them in their inbox or something // todo check if recipient has an account and show them in their inbox or something
} }

View File

@@ -9,6 +9,7 @@ import { SigningStatus, DocumentStatus } from "@prisma/client";
import { getDocument } from "@documenso/lib/query"; import { getDocument } from "@documenso/lib/query";
import { Document as PrismaDocument } from "@prisma/client"; import { Document as PrismaDocument } from "@prisma/client";
import { insertImageInPDF, insertTextInPDF } from "@documenso/pdf"; import { insertImageInPDF, insertTextInPDF } from "@documenso/pdf";
import { create } from "domain";
const text2png = require("text2png"); const text2png = require("text2png");
async function postHandler(req: NextApiRequest, res: NextApiResponse) { async function postHandler(req: NextApiRequest, res: NextApiResponse) {
@@ -37,17 +38,23 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
if (!document) res.status(404).end(`No document found.`); if (!document) res.status(404).end(`No document found.`);
// todo insert if not exits // todo insert if not exits
signatures.forEach(async (signature) => { signatures.forEach(async (signature) => {
await prisma.signature.create({ if (!signature.signatureImage && !signature.typedSignature)
data: { throw new Error("Cant't save invalid signature.");
await prisma.signature.upsert({
where: {
fieldId: signature.fieldId,
},
update: {},
create: {
recipientId: recipient.id, recipientId: recipient.id,
fieldId: signature.fieldId, fieldId: signature.fieldId,
signatureImageAsBase64: signature.signatureImage signatureImageAsBase64: signature.signatureImage
? signature.signatureImage ? signature.signatureImage
: text2png(signature.typedSignature, { : null,
color: "black", typedSignature: signature.typedSignature
lineSpacing: 10, ? signature.typedSignature
padding: 20, : null,
}).toString("base64"),
}, },
}); });
}); });
@@ -68,6 +75,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
}, },
}); });
console.log("unsignedRecipients.length.." + unsignedRecipients.length);
if (unsignedRecipients.length === 0) { if (unsignedRecipients.length === 0) {
// todo if everybody signed insert images and create signature // todo if everybody signed insert images and create signature
const signedFields = await prisma.field.findMany({ const signedFields = await prisma.field.findMany({
@@ -77,18 +85,36 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
// todo rename .document to documentImageAsBase64 or sth. like that // todo rename .document to documentImageAsBase64 or sth. like that
let documentWithSignatureImages = document.document; let documentWithSignatureImages = document.document;
let signaturesInserted = 0; let signaturesInserted = 0;
signedFields.forEach(async (item) => { signedFields.forEach(async (signedField) => {
if (!item.Signature) { if (!signedField.Signature) {
documentWithSignatureImages = document.document; documentWithSignatureImages = document.document;
throw new Error("Invalid Signature in Field"); throw new Error("Invalid Signature in Field");
} }
if (signedField.Signature.signatureImageAsBase64) {
documentWithSignatureImages = await insertImageInPDF( documentWithSignatureImages = await insertImageInPDF(
documentWithSignatureImages, documentWithSignatureImages,
item.Signature ? item.Signature?.signatureImageAsBase64 : "", signedField.Signature
item.positionX, ? signedField.Signature?.signatureImageAsBase64
item.positionY, : "",
item.page signedField.positionX,
signedField.positionY,
signedField.page
); );
} else if (signedField.Signature.typedSignature) {
console.log("inserting text");
documentWithSignatureImages = await insertTextInPDF(
documentWithSignatureImages,
signedField.Signature.typedSignature,
signedField.positionX,
signedField.positionY,
signedField.page
);
} else {
documentWithSignatureImages = document.document;
throw new Error("Invalid signature could not be inserted.");
}
signaturesInserted++; signaturesInserted++;
if (signaturesInserted == signedFields.length) { if (signaturesInserted == signedFields.length) {
await prisma.document.update({ await prisma.document.update({

795
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,6 @@
import { PDFDocument, rgb, StandardFonts } from "pdf-lib"; import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
import fontkit from "@pdf-lib/fontkit";
import * as fs from "fs";
export async function insertTextInPDF( export async function insertTextInPDF(
pdfAsBase64: string, pdfAsBase64: string,
@@ -7,20 +9,25 @@ export async function insertTextInPDF(
positionY: number, positionY: number,
page: number = 0 page: number = 0
): Promise<string> { ): Promise<string> {
const fontBytes = fs.readFileSync("public/fonts/Qwigley-Regular.ttf");
const existingPdfBytes = pdfAsBase64; const existingPdfBytes = pdfAsBase64;
const pdfDoc = await PDFDocument.load(existingPdfBytes); const pdfDoc = await PDFDocument.load(existingPdfBytes);
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica); pdfDoc.registerFontkit(fontkit);
const customFont = await pdfDoc.embedFont(fontBytes);
const pages = pdfDoc.getPages(); const pages = pdfDoc.getPages();
const pdfPage = pages[page]; const pdfPage = pages[page];
const lineHeightEsimate = 25; const textSize = 50;
const textWidth = customFont.widthOfTextAtSize(text, textSize);
const textHeight = customFont.heightAtSize(textSize);
pdfPage.drawText(text, { pdfPage.drawText(text, {
x: pdfPage.getWidth() - positionX, x: pdfPage.getWidth() - positionX - textWidth / 2, // todo adjust for exact field size
y: pdfPage.getHeight() - positionY - lineHeightEsimate, y: pdfPage.getHeight() - positionY - textHeight / 2, // todo adjust for exact field size
size: 25, size: textSize,
font: helveticaFont, font: customFont,
color: rgb(0, 0, 0), color: rgb(0, 0, 0),
}); });

View File

@@ -121,7 +121,8 @@ model Signature {
created DateTime @default(now()) created DateTime @default(now())
recipientId Int recipientId Int
fieldId Int @unique fieldId Int @unique
signatureImageAsBase64 String signatureImageAsBase64 String?
typedSignature String?
Recipient Recipient @relation(fields: [recipientId], references: [id], onDelete: Restrict) Recipient Recipient @relation(fields: [recipientId], references: [id], onDelete: Restrict)
Field Field @relation(fields: [fieldId], references: [id], onDelete: Restrict) Field Field @relation(fields: [fieldId], references: [id], onDelete: Restrict)