You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Node.js中实现PDF鼠标手绘签名功能求助

实现鼠标绘制手写签名嵌入PDF(Node.js + PDF-Lib)

要实现终端用户手写签名嵌入PDF,核心是把用户绘制的签名转为图像,再将图像插入到PDF的签名字段位置。以下是具体实现步骤:

1. 前端实现签名绘制与导出

首先需要在前端用Canvas让用户绘制签名,然后导出为PNG图片(Base64格式)传给后端:

<!-- 前端签名画布 -->
<canvas id="signatureCanvas" width="300" height="100" style="border:1px solid #ccc;"></canvas>
<button onclick="exportSignature()">确认签名并提交</button>

<script>
const canvas = document.getElementById('signatureCanvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;

// 手写绘制逻辑
canvas.addEventListener('mousedown', (e) => {
  isDrawing = true;
  ctx.beginPath();
  ctx.moveTo(e.offsetX, e.offsetY);
  ctx.lineWidth = 2;
  ctx.strokeStyle = '#000';
});

canvas.addEventListener('mousemove', (e) => {
  if (isDrawing) {
    ctx.lineTo(e.offsetX, e.offsetY);
    ctx.stroke();
  }
});

canvas.addEventListener('mouseup', () => isDrawing = false);
canvas.addEventListener('mouseleave', () => isDrawing = false);

// 导出签名为Base64并发送后端
function exportSignature() {
  const signatureBase64 = canvas.toDataURL('image/png');
  fetch('/embed-signature', {
    method: 'POST',
    body: JSON.stringify({ 
      signature: signatureBase64,
      targetPdf: 'pdf24_merged.pdf'
    }),
    headers: { 'Content-Type': 'application/json' }
  });
}
</script>

2. 后端用PDF-Lib嵌入签名图像

后端接收前端传来的签名图片,将其嵌入到PDF的签名字段对应位置。原代码使用的是TextField,我们需要先获取该字段的坐标和尺寸,再在对应位置插入图像:

const { PDFDocument } = require('pdf-lib');
const fs = require('fs').promises;
const path = require('path');
const express = require('express');
const app = express();

app.use(express.json());

async function embedSignatureToPdf(pdfPath, signatureBase64) {
  // 加载目标PDF
  const pdfBuffer = await fs.readFile(path.join(__dirname, pdfPath));
  const pdfDoc = await PDFDocument.load(pdfBuffer);

  // 获取签名字段
  const form = pdfDoc.getForm();
  const signatureField = form.getTextField('Datum und Unterschrift');
  
  // 获取字段的位置与尺寸(PDF坐标系:原点在页面左下角,单位为点)
  const [fieldX, fieldY, fieldWidth, fieldHeight] = signatureField.getRectangle();

  // 解码Base64签名图片
  const signatureBytes = Buffer.from(signatureBase64.split(',')[1], 'base64');
  const signatureImage = await pdfDoc.embedPng(signatureBytes);

  // 获取签名所在页面(默认第一页,可根据实际调整)
  const targetPage = pdfDoc.getPage(0);

  // 转换Canvas坐标系到PDF坐标系,绘制图像适配字段尺寸
  targetPage.drawImage(signatureImage, {
    x: fieldX,
    y: targetPage.getHeight() - fieldY - fieldHeight,
    width: fieldWidth,
    height: fieldHeight,
  });

  // 隐藏原文本字段(避免重叠)
  signatureField.setVisibility(false);

  // 保存签名后的PDF
  const signedPdfBuffer = await pdfDoc.save();
  await fs.writeFile(path.join(__dirname, 'signed_document.pdf'), signedPdfBuffer);
}

// 处理前端提交的签名请求
app.post('/embed-signature', async (req, res) => {
  const { signature, targetPdf } = req.body;
  try {
    await embedSignatureToPdf(targetPdf, signature);
    res.send('签名已成功嵌入PDF');
  } catch (err) {
    res.status(500).send(`处理失败:${err.message}`);
  }
});

app.listen(3000, () => console.log('服务运行在3000端口'));

关键注意点

  • 坐标系转换:PDF原点在页面左下角,而Canvas原点在左上角,因此绘制图像时需要用页面高度 - 字段Y坐标 - 字段高度来修正Y轴位置。
  • 签名适配:如果用户绘制的签名比例与字段不符,可以在drawImage中添加scale参数或调整宽高,避免签名拉伸变形。
  • 字段定位:如果签名字段不在第一页,需要修改getPage(0)为对应页码。

内容的提问来源于stack exchange,提问作者Muhammad Muneeb Waqas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 00:10:59