🚧 📑 pdf helpers, todos for signign

This commit is contained in:
Timur Ercan
2023-02-18 14:37:26 +01:00
parent 15cd0d2a7b
commit 017a76ef5d
9 changed files with 174 additions and 3 deletions

View File

@ -10,6 +10,7 @@ const withTM = require("next-transpile-modules")([
"@documenso/prisma", "@documenso/prisma",
"@documenso/lib", "@documenso/lib",
"@documenso/ui", "@documenso/ui",
"@documenso/pdf",
"@documenso/features", "@documenso/features",
]); ]);
const plugins = []; const plugins = [];

View File

@ -10,6 +10,7 @@
"db-studio": "prisma db studio" "db-studio": "prisma db studio"
}, },
"dependencies": { "dependencies": {
"@documenso/pdf": "*",
"@documenso/prisma": "*", "@documenso/prisma": "*",
"@documenso/ui": "*", "@documenso/ui": "*",
"@headlessui/react": "^1.7.4", "@headlessui/react": "^1.7.4",
@ -19,6 +20,7 @@
"@types/filesystem": "^0.0.32", "@types/filesystem": "^0.0.32",
"@types/react-dom": "18.0.9", "@types/react-dom": "18.0.9",
"avatar-from-initials": "^1.0.3", "avatar-from-initials": "^1.0.3",
"base64-arraybuffer": "^1.0.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"eslint": "8.27.0", "eslint": "8.27.0",

View File

@ -8,6 +8,7 @@ import { NextApiRequest, NextApiResponse } from "next";
import { SigningStatus, DocumentStatus } from "@prisma/client"; 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";
async function postHandler(req: NextApiRequest, res: NextApiResponse) { async function postHandler(req: NextApiRequest, res: NextApiResponse) {
const existingUser = await getUserFromToken(req, res); const existingUser = await getUserFromToken(req, res);
@ -33,7 +34,7 @@ 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.`);
// save signature to db for later use // todo save signature to db for later use
await prisma.recipient.update({ await prisma.recipient.update({
where: { where: {
@ -61,7 +62,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
}); });
if (unsignedRecipients.length === 0) { if (unsignedRecipients.length === 0) {
// if everybody signed insert images and create signature // todo if everybody signed insert images and create signature
await prisma.document.update({ await prisma.document.update({
where: { where: {
id: recipient.documentId, id: recipient.documentId,
@ -70,7 +71,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
status: DocumentStatus.COMPLETED, status: DocumentStatus.COMPLETED,
}, },
}); });
// send notifications // todo send notifications
} }
return res.status(200).end(); return res.status(200).end();

105
package-lock.json generated
View File

@ -29,6 +29,7 @@
"next-auth": "^4.18.3", "next-auth": "^4.18.3",
"next-transpile-modules": "^10.0.0", "next-transpile-modules": "^10.0.0",
"npm": "^9.1.3", "npm": "^9.1.3",
"pdf-lib": "^1.17.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-hook-form": "^7.41.5", "react-hook-form": "^7.41.5",
@ -45,6 +46,7 @@
"name": "@documenso/web", "name": "@documenso/web",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@documenso/pdf": "*",
"@documenso/prisma": "*", "@documenso/prisma": "*",
"@documenso/ui": "*", "@documenso/ui": "*",
"@headlessui/react": "^1.7.4", "@headlessui/react": "^1.7.4",
@ -54,6 +56,7 @@
"@types/filesystem": "^0.0.32", "@types/filesystem": "^0.0.32",
"@types/react-dom": "18.0.9", "@types/react-dom": "18.0.9",
"avatar-from-initials": "^1.0.3", "avatar-from-initials": "^1.0.3",
"base64-arraybuffer": "^1.0.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"eslint": "8.27.0", "eslint": "8.27.0",
@ -153,6 +156,10 @@
"resolved": "packages/lib", "resolved": "packages/lib",
"link": true "link": true
}, },
"node_modules/@documenso/pdf": {
"resolved": "packages/pdf",
"link": true
},
"node_modules/@documenso/prisma": { "node_modules/@documenso/prisma": {
"resolved": "packages/prisma", "resolved": "packages/prisma",
"link": true "link": true
@ -560,6 +567,22 @@
"url": "https://github.com/sponsors/panva" "url": "https://github.com/sponsors/panva"
} }
}, },
"node_modules/@pdf-lib/standard-fonts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
"dependencies": {
"pako": "^1.0.6"
}
},
"node_modules/@pdf-lib/upng": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
"dependencies": {
"pako": "^1.0.10"
}
},
"node_modules/@prisma/client": { "node_modules/@prisma/client": {
"version": "4.9.0", "version": "4.9.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz",
@ -1491,6 +1514,14 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
}, },
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/bcrypt-pbkdf": { "node_modules/bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -6801,6 +6832,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"node_modules/parent-module": { "node_modules/parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -6849,6 +6885,22 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/pdf-lib": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
"dependencies": {
"@pdf-lib/standard-fonts": "^1.0.0",
"@pdf-lib/upng": "^1.0.1",
"pako": "^1.0.11",
"tslib": "^1.11.1"
}
},
"node_modules/pdf-lib/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/pdfjs-dist": { "node_modules/pdfjs-dist": {
"version": "2.16.105", "version": "2.16.105",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz",
@ -8479,6 +8531,10 @@
"bcryptjs": "^2.4.3" "bcryptjs": "^2.4.3"
} }
}, },
"packages/pdf": {
"name": "@documenso/pdf",
"version": "0.0.0"
},
"packages/prisma": { "packages/prisma": {
"name": "@documenso/prisma", "name": "@documenso/prisma",
"version": "0.0.0", "version": "0.0.0",
@ -8535,6 +8591,9 @@
"bcryptjs": "^2.4.3" "bcryptjs": "^2.4.3"
} }
}, },
"@documenso/pdf": {
"version": "file:packages/pdf"
},
"@documenso/prisma": { "@documenso/prisma": {
"version": "file:packages/prisma", "version": "file:packages/prisma",
"requires": { "requires": {
@ -8562,6 +8621,7 @@
"@documenso/web": { "@documenso/web": {
"version": "file:apps/web", "version": "file:apps/web",
"requires": { "requires": {
"@documenso/pdf": "*",
"@documenso/prisma": "*", "@documenso/prisma": "*",
"@documenso/ui": "*", "@documenso/ui": "*",
"@headlessui/react": "^1.7.4", "@headlessui/react": "^1.7.4",
@ -8578,6 +8638,7 @@
"@types/react-resizable": "^3.0.3", "@types/react-resizable": "^3.0.3",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"avatar-from-initials": "^1.0.3", "avatar-from-initials": "^1.0.3",
"base64-arraybuffer": "^1.0.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"eslint": "8.27.0", "eslint": "8.27.0",
@ -8854,6 +8915,22 @@
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz",
"integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==" "integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA=="
}, },
"@pdf-lib/standard-fonts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
"requires": {
"pako": "^1.0.6"
}
},
"@pdf-lib/upng": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
"requires": {
"pako": "^1.0.10"
}
},
"@prisma/client": { "@prisma/client": {
"version": "4.9.0", "version": "4.9.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz",
@ -9609,6 +9686,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
}, },
"base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
},
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -13271,6 +13353,11 @@
"p-limit": "^3.0.2" "p-limit": "^3.0.2"
} }
}, },
"pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"parent-module": { "parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -13304,6 +13391,24 @@
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
}, },
"pdf-lib": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
"requires": {
"@pdf-lib/standard-fonts": "^1.0.0",
"@pdf-lib/upng": "^1.0.1",
"pako": "^1.0.11",
"tslib": "^1.11.1"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"pdfjs-dist": { "pdfjs-dist": {
"version": "2.16.105", "version": "2.16.105",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz",

View File

@ -34,6 +34,7 @@
"next-auth": "^4.18.3", "next-auth": "^4.18.3",
"next-transpile-modules": "^10.0.0", "next-transpile-modules": "^10.0.0",
"npm": "^9.1.3", "npm": "^9.1.3",
"pdf-lib": "^1.17.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-hook-form": "^7.41.5", "react-hook-form": "^7.41.5",

2
packages/pdf/index.ts Normal file
View File

@ -0,0 +1,2 @@
export { insertTextInPDF } from "./insertTextInPDF";
export { insertImageInPDF } from "./insertImageInPDF";

View File

@ -0,0 +1,25 @@
import { degrees, PDFDocument, PDFImage, rgb, StandardFonts } from "pdf-lib";
export async function insertImageInPDF(
pdfAsBase64: string,
image: string | Uint8Array | ArrayBuffer,
positionX: number,
positionY: number,
page: number = 0
): Promise<string> {
const existingPdfBytes = pdfAsBase64;
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const pages = pdfDoc.getPages();
const pdfPage = pages[page];
const pngImage = await pdfDoc.embedPng(image);
pdfPage.drawImage(pngImage, {
x: positionX,
y: positionY,
width: pngImage.width,
height: pngImage.height,
});
const pdfAsUint8Array = await pdfDoc.save();
return Buffer.from(pdfAsUint8Array).toString("base64");
}

View File

@ -0,0 +1,27 @@
import { degrees, PDFDocument, rgb, StandardFonts } from "pdf-lib";
export async function insertTextInPDF(
pdfAsBase64: string,
text: string,
positionX: number,
positionY: number,
page: number = 0
): Promise<string> {
const existingPdfBytes = pdfAsBase64;
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);
const pages = pdfDoc.getPages();
const firstPage = pages[page];
firstPage.drawText(text, {
x: positionX,
y: positionY,
size: 25,
font: helveticaFont,
color: rgb(0, 0, 0),
});
const pdfAsUint8Array = await pdfDoc.save();
return Buffer.from(pdfAsUint8Array).toString("base64");
}

View File

@ -0,0 +1,7 @@
{
"name": "@documenso/pdf",
"version": "0.0.0",
"private": true,
"main": "index.ts",
"dependencies": {}
}