Implementing PDF Contract Electronic Signature with Vue, pdfjs-dist, and pdf-lib

This article describes a complete solution for creating a PDF contract electronic signature system using Vue for the front‑end rendering, pdfjs‑dist to extract predefined form fields, and pdf‑lib on the back‑end to generate a signed PDF, covering field extraction, coordinate conversion, canvas overlay, and server‑side PDF manipulation.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing PDF Contract Electronic Signature with Vue, pdfjs-dist, and pdf-lib

The author needed a way to generate PDF contracts with many fields and decided to build a Vue‑based front‑end that can both render PDFs and extract predefined form field positions using pdfjs-dist. The extracted field rectangles are converted from PDF coordinates (origin at bottom‑left) to canvas coordinates, then displayed as interactive overlays.

Rendering is performed by loading the PDF with getDocument, creating a canvas, and drawing the page using the PDF page's viewport. A second overlay canvas captures mouse events for drawing rectangles that represent form fields.

Field extraction code (simplified):

const annotations = await page.getAnnotations();
annotations.forEach(annot => {
  if (annot.subtype === 'Widget' && annot.fieldName && annot.fieldType === 'Tx') {
    const {x, y} = convertToCanvasCoordinates(annot.rect[0], annot.rect[1], viewport.height, scale);
    fields.push({name: annot.fieldName, type: annot.fieldType, x, y, width: (annot.rect[2]-annot.rect[0])*scale, height: (annot.rect[3]-annot.rect[1])*scale});
    generateForm(annot);
  }
});

The generated form elements are inserted into a floating container, allowing users to click a field and scroll it into view. The front‑end then sends the scaled rectangle data to a back‑end API.

On the server side, pdf-lib loads a template PDF, creates text fields at the received coordinates (adjusting for PDF coordinate system), and saves the modified PDF:

const pdfDoc = await PDFDocument.load(pdfBuffer);
const page = pdfDoc.getPages()[0];
const {height: pdfHeight} = page.getSize();
rectangles.forEach(rect => {
  const adjustedY = pdfHeight - rect.y - rect.height;
  const field = form.createTextField(rect.fieldName);
  field.addToPage(page, {x: rect.x, y: adjustedY, width: rect.width, height: rect.height, backgroundColor: rgb(0.95,0.95,0.95), borderColor: rgb(1,1,1), borderWidth: 1});
});
const modifiedPdfBytes = await pdfDoc.save();
fs.writeFileSync('public/modified.pdf', modifiedPdfBytes);

The complete workflow enables users to fill form data on the front‑end, see visual field placement, and generate a final signed PDF on the back‑end, solving the original problem of handling numerous contract fields without a UI.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendVuepdf-libPDF signaturepdfjs-dist
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.