import { PDFName, PDFPage, PDFString, PDFDocument } from "pdf-lib"; import { PdfData } from "pdfdataextract"; import { config } from "./config"; import { IGetMetaResponse } from "./types"; import { verifyPDF } from "./lib"; import { formatPdfTime } from "./lib/timeUtils"; import { AppError, GeneralError } from "./lib/errors"; import { isPDF } from "./lib/generalUtils"; type SealCoords = { [key: string]: { x: string; y: string }; }; class PDFparser { readonly document; readonly config; constructor(document: Buffer) { this.document = document; this.config = config; } getPDFMeta = async (): Promise<IGetMetaResponse> => { if (!(this.document instanceof Buffer)) { throw new AppError("Document is not Buffer"); } if (!isPDF(this.document)) { throw new AppError("Only pdf file type is supported"); } try { const signaturesMeta = await verifyPDF(this.document); const pdfMeta = await PdfData.extract(this.document, config); const result = { pages: pdfMeta.pages, title: pdfMeta.info.Title || "Unknown", author: pdfMeta.info.Author || "Unknown", creation_date: formatPdfTime(pdfMeta.info.CreationDate), mod_date: formatPdfTime(pdfMeta.info.ModDate), }; if (signaturesMeta) { result["signatures"] = signaturesMeta.signatures; result["expired"] = signaturesMeta.expired; } return result; } catch (error) { throw new GeneralError(error); } }; insertQrCode = async ( imgBytes: ArrayBuffer, url: string, coords: SealCoords, scaleFactor: number ): Promise<ArrayBuffer> => { const pdfDoc = await PDFDocument.load(this.document); const img = await pdfDoc.embedPng(imgBytes); const scaled = img.scale(scaleFactor); const pages = pdfDoc.getPages(); console.log(coords); for (let index = 0; index < pages.length; index++) { const page = pages[index]; console.log(coords[index + 1]); const x = typeof coords[index + 1] !== "undefined" ? parseFloat(coords[index + 1]["x"]) : null; const y = typeof typeof coords[index + 1] !== "undefined" ? parseFloat(coords[index + 1]["y"]) : null; if (x && y) { page.drawImage(img, { x, y, width: scaled.width, height: scaled.height, }); const link = this.createPageLinkAnnotation(page, url, { imgXPos: x, imgYPos: y, imgWidth: scaled.width, imagHeight: scaled.height, }); page.node.set(PDFName.of("Annots"), pdfDoc.context.obj([link])); } } const pdfBytes = await pdfDoc.save(); return pdfBytes; }; private createPageLinkAnnotation = ( page: PDFPage, uri: string, { imgXPos, imgYPos, imgWidth, imagHeight } ) => page.doc.context.register( page.doc.context.obj({ Type: "Annot", Subtype: "Link", Rect: [imgXPos, imgYPos, imgXPos + imgWidth, imgYPos + imagHeight], A: { Type: "Action", S: "URI", URI: PDFString.of(uri), }, }) ); } export default PDFparser;