🗃️ 💄 insert text in custom font, typeSignature
This commit is contained in:
@@ -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": {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
795
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user