vbuch / node-signpdf

Simple signing of PDFs in node.
MIT License
676 stars 174 forks source link

pdf.ref is not a function #259

Open riicardojs opened 2 days ago

riicardojs commented 2 days ago

I'm having trouble generating the signature in the document. I’ve tried with both a PDF created from scratch using pdf-lib and an existing PDF in versions 1.7 and 1.4, but all attempts have failed. I keep getting the error "pdf.ref is not a function." Has anyone encountered this problem? I was successful with placeholder-plain, but I'm unable to get it to work with placeholder-pdfkit010, which is supposed to allow for a visible signature.

import {
  pdfkitAddPlaceholder,
  ReturnType,
} from "@signpdf/placeholder-pdfkit010";
import { P12Signer } from "@signpdf/signer-p12";
import signpdf from "@signpdf/signpdf";
import { FastifyInstance } from "fastify";
import fs from "fs";
import { PDFDocument, rgb } from "pdf-lib";
import z from "zod";

export async function getSign(app: FastifyInstance) {
  app.get("/sign/:certificateId", async (request, reply) => {
    const getSignBody = z.object({
      certificateId: z.string().uuid(),
    });

    const { certificateId } = getSignBody.parse(request.params);

    const pdfBuffer = fs.readFileSync(
      `${__dirname}/../../../resources/${certificateId}.pdf`
    );
    const certificateBuffer = fs.readFileSync(
      `${__dirname}/../../../resources/certificate.p12`
    );

    const targetPath = `${__dirname}/../../../output/${certificateId}.pdf`;

    const pdfWithVisual = await addVisual(pdfBuffer);

    const signer = new P12Signer(certificateBuffer, {
      passphrase: process.env.CERT_PASSWORD,
    });

    const signedPdf = await signpdf.sign(pdfWithVisual, signer);

    fs.writeFileSync(targetPath, signedPdf);

    return reply.status(200).send({ certificateId });
  });
}

async function addVisual(pdfBuffer: Buffer): Promise<Uint8Array> {
  const pdfDoc = await PDFDocument.load(pdfBuffer);
  const pages = pdfDoc.getPages();
  const firstPage = pages[0];

  const { width, height } = firstPage.getSize();

  const margin = 30;
  const padding = 10;
  const labelDoc = "Doc signature";
  const labelName = "Company name";
  const labelDate = "Data: 03/07/2024 19:22:23-0300";
  const imageBuffer = fs.readFileSync(
    `${__dirname}/../../../assets/govbr-logo.png`
  );
  const textWidth = 200;
  const textHeight = 20;

  const x = width - textWidth - margin;
  const y = height - textHeight - margin;

  firstPage.drawText(labelDoc, {
    x,
    y,
    size: 10,
    color: rgb(0, 0, 0),
  });

  firstPage.drawText(labelName, {
    x,
    y: y - 20,
    size: 10,
    color: rgb(0, 0, 0),
  });

  firstPage.drawText(labelDate, {
    x,
    y: y - 35,
    size: 10,
    color: rgb(0, 0, 0),
  });

  const pngImage = await pdfDoc.embedPng(imageBuffer);

  // Desenhar a imagem na primeira página
  firstPage.drawImage(pngImage, {
    x: x - 65,
    y: y - 40,
    width: 70,
    height: 30,
  });

  const visualRect = [
    x - padding,
    y - padding,
    x + textWidth + padding,
    y + textHeight + padding,
  ];
  const widgetRect = topLeftToBottomLeft(visualRect, { width, height });

  const refs = pdfkitAddPlaceholder({
    pdf: pdfDoc,
    pdfBuffer,
    reason: "Showing off.",
    contactInfo: "signpdf@example.com",
    name: "Sign PDF",
    location: "The digital world.",
    signatureLength: 1612,
    widgetRect,
    signingTime: new Date(),
  });

  // Manually end the refs
  (Object.keys(refs) as (keyof ReturnType)[]).forEach((key) => {
    refs[key].end();
  });

  const modifiedPdfBuffer = await pdfDoc.save();

  return modifiedPdfBuffer;
}

function topLeftToBottomLeft(
  coords: number[],
  page: { width: number; height: number }
): number[] {
  return [
    coords[0], // x1
    page.height - coords[1], // y1
    coords[2], // x2
    page.height - coords[3], // y2
  ];
}