Recently a task is given to me for signing a pdf using pfx file using node.js . And I digitally signed the pdf and also the place the placeholder for sign. But visually the sign is not appearing as we see in official document or govt. Docs [like that]. In my pdf when I hover mouse cursor on that placeholder it shows signature validity but visually it is not appearing. This is the problem I am facing LIKE THAT. Here I am getting blank box but when I click on that ,a dialog box is opened it shows signature validity, but here nothing has visually appeared and there is not ✔ tick or cross ✖ or warning sign like I attached image below
I am expecting output like this on my pdf.
here is my code.
const express = require('express');
const { PDFDocument, rgb, StandardFonts, PDFName, PDFNumber } = require('pdf-lib');
const signpdf = require('@signpdf/signpdf').default;
const { P12Signer } = require('@signpdf/signer-p12');
const { pdflibAddPlaceholder } = require('@signpdf/placeholder-pdf-lib');
const pdfjsLib = require('pdfjs-dist/legacy/build/pdf.mjs');
const app = express();
app.use(express.json({ limit: '50mb' }));
async function findMarkerCoords(pdfBuffer, marker) {
const loadingTask = pdfjsLib.getDocument({ data: new Uint8Array(pdfBuffer) });
const pdf = await loadingTask.promise;
const numPages = pdf.numPages;
for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {
const page = await pdf.getPage(pageIndex + 1);
const textContent = await page.getTextContent();
for (const item of textContent.items) {
if (item.str && item.str.includes(marker)) {
return {
pageIndex,
x: item.transform[4],
y: item.transform[5]
};
}
}
}
throw new Error(`Marker "${marker}" not found in any page.`);
}
app.post('/sign', async (req, res) => {
try {
const { pdf, pfx, password } = req.body;
if (!pdf || !pfx || !password) {
return res.status(400).json({ status: 'error', message: 'Missing pdf, pfx, or password.' });
}
const pdfBuffer = Buffer.from(pdf, 'base64');
const pfxBuffer = Buffer.from(pfx, 'base64');
const marker = 'DigiSign';
// Get coordinates of the marker text
const { pageIndex, x, y } = await findMarkerCoords(pdfBuffer, marker);
// Load PDF with pdf-lib
const pdfDoc = await PDFDocument.load(pdfBuffer);
const pdfPage = pdfDoc.getPages()[pageIndex];
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
const fontSize = 10;
// Draw the signature appearance box
const sigX = x;
const sigY = y;
const sigWidth = 180;
const sigHeight = 60;
const pages = pdfDoc.getPages();
for (const page of pages) {
const annots = page.node.Annots();
if (annots) {
const annotsArray = annots.asArray();
for (const annotRef of annotsArray) {
const annot = pdfDoc.context.lookup(annotRef);
annot.set(PDFName.of('F'), PDFNumber.of(4));
annot.set(PDFName.of('Type'),PDFName.of('Annot'));
annot.set(PDFName.of('Subtype'),PDFName.of('Widget'));
annot.set(PDFName.of('FT'),PDFName.of('Sig'));
const rect = pdfDoc.context.obj([sigX,sigY,sigX + sigWidth , sigY + sigHeight]);
annot.set(PDFName.of('Rect'),rect);
}
}
}
// Add visible signature placeholder
pdflibAddPlaceholder({
pdfDoc : pdfDoc,
pdfPage : pdfPage,
reason: 'Signed for approval',
contactInfo: '[email protected]',
name: 'Aditya Garg',
location: 'India',
signingTime: new Date(),
signatureLength: 12000,
widgetRect: [sigX, sigY, sigX + sigWidth, sigY + sigHeight],
});
// Make signature annotation visible
const pdfWithPlaceholder = await pdfDoc.save({ useObjectStreams: false });
const signer = new P12Signer(pfxBuffer, { passphrase: password });
const signedPdf = await signpdf.sign(Buffer.from(pdfWithPlaceholder), signer);
return res.json({ status: 'success', signedPdf: signedPdf.toString('base64') });
} catch (err) {
console.error('Signing failed:', err);
return res.status(500).json({ status: 'error', message: err.message });
}
});
app.listen(3000, () => {
console.log('✅ Digital Signature Server running at http://localhost:3000');
});