如何基于PDF-LIB开发类似iLovePDF的Laravel签名PDF Web应用?
实现Laravel网站的手写签名PDF编辑器方案
一、前端核心模块(签名交互+PDF预览)
前端需完成PDF渲染、签名绘制、签名拖拽定位三个核心功能,以下是具体实现:
1. PDF预览渲染
使用pdfjs-dist(Mozilla官方PDF渲染库)实现前端PDF预览:
- 安装依赖:
npm install pdfjs-dist
- 初始化PDF加载逻辑:
import * as pdfjsLib from 'pdfjs-dist/webpack'; import 'pdfjs-dist/webpack/build/pdf.worker.entry'; // 全局存储PDF实例和视口信息,用于后续坐标转换 let currentPDF = null; let currentViewport = null; async function loadPDF(pdfUrl) { const pdf = await pdfjsLib.getDocument(pdfUrl).promise; const page = await pdf.getPage(1); currentViewport = page.getViewport({ scale: 1 }); const canvas = document.getElementById('pdf-preview'); const ctx = canvas.getContext('2d'); canvas.height = currentViewport.height; canvas.width = currentViewport.width; await page.render({ canvasContext: ctx, viewport: currentViewport }).promise; currentPDF = pdf; }
2. 手写签名画布
用轻量库signature_pad实现签名绘制,比原生Canvas更省心:
- 安装依赖:
npm install signature_pad
- 初始化签名画布:
import SignaturePad from 'signature_pad'; const sigCanvas = document.getElementById('signature-canvas'); const sigPad = new SignaturePad(sigCanvas, { backgroundColor: 'rgba(255,255,255,0)', penColor: '#000000', minWidth: 1, maxWidth: 2 }); // 绑定清空签名按钮 document.getElementById('clear-sig').addEventListener('click', () => sigPad.clear());
3. 签名拖拽定位
将签名转为图片,添加到PDF预览区域并支持拖拽调整位置:
function addSignatureToPreview() { if (sigPad.isEmpty()) return; const sigImg = document.createElement('img'); sigImg.src = sigPad.toDataURL('image/png'); sigImg.style.cssText = ` position: absolute; width: 160px; cursor: move; z-index: 10; `; // 原生拖拽逻辑 let isDragging = false; let startX, startY; sigImg.addEventListener('mousedown', e => { isDragging = true; startX = e.clientX - sigImg.offsetLeft; startY = e.clientY - sigImg.offsetTop; }); document.addEventListener('mousemove', e => { if (isDragging) { sigImg.style.left = `${e.clientX - startX}px`; sigImg.style.top = `${e.clientY - startY}px`; } }); document.addEventListener('mouseup', () => isDragging = false); document.getElementById('pdf-container').appendChild(sigImg); }
二、后端Laravel结合PDF-LIB完成签名嵌入
前端将签名数据、位置信息传给后端,用PDF-LIB(前端完成嵌入后传后端保存更高效)处理最终PDF:
1. 前端用PDF-LIB嵌入签名
import { PDFDocument } from 'pdf-lib'; async function generateSignedPDF(originalPdfUrl) { const sigElement = document.querySelector('#pdf-container img'); if (!sigElement) return; // 转换网页坐标到PDF坐标系(PDF以左下角为原点) const scale = currentViewport.scale; const pdfPos = { x: parseFloat(sigElement.style.left) / scale, y: currentViewport.height - (parseFloat(sigElement.style.top) / scale) - (parseFloat(sigElement.style.height) / scale), width: parseFloat(sigElement.style.width) / scale, height: parseFloat(sigElement.style.height) / scale }; // 加载原始PDF并嵌入签名 const pdfBytes = await fetch(originalPdfUrl).then(res => res.arrayBuffer()); const pdfDoc = await PDFDocument.load(pdfBytes); const sigImage = await pdfDoc.embedPng(sigPad.toDataURL('image/png')); const page = pdfDoc.getPage(0); page.drawImage(sigImage, pdfPos); // 生成Blob格式的签名PDF return new Blob([await pdfDoc.save()], { type: 'application/pdf' }); }
2. Laravel后端接收并保存PDF
- 添加路由:
// routes/web.php Route::post('/save-signed-pdf', [PdfController::class, 'store'])->name('pdf.signed.store');
- 控制器逻辑:
// app/Http/Controllers/PdfController.php use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; public function store(Request $request) { $request->validate([ 'signed_pdf' => 'required|file|mimes:pdf|max:10240' ]); $file = $request->file('signed_pdf'); $fileName = 'signed_' . uniqid() . '.pdf'; $path = $file->storeAs('public/signed_pdfs', $fileName); return response()->json([ 'success' => true, 'download_url' => Storage::url($path) ]); }
3. 前端上传签名PDF
async function saveSignedPDF() { const signedBlob = await generateSignedPDF('/storage/original.pdf'); const formData = new FormData(); formData.append('signed_pdf', signedBlob, 'signed_document.pdf'); const response = await fetch('/save-signed-pdf', { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content }, body: formData }); const result = await response.json(); if (result.success) { // 提示用户并提供下载链接 alert('签名PDF已保存'); document.getElementById('download-link').href = result.download_url; } }
三、功能优化建议
- 多页PDF支持:扩展PDF预览逻辑,添加页码切换按钮,记录签名对应的页码
- 签名样式自定义:添加颜色选择器、笔画粗细滑块,修改
signature_pad配置 - 响应式适配:根据屏幕尺寸调整PDF缩放比例,确保移动端正常交互
- 撤销/重做:利用
signature_pad的undo()和redo()方法实现签名回退功能
内容的提问来源于stack exchange,提问作者Muhammad Nafis
相关产品推荐
相关产品推荐

