如何用pdf-lib的drawText()动态定位并绘制可编辑文本到PDF
PDF文本位置不匹配问题:pdf.js渲染与pdf-lib编辑的坐标对齐方案
你的问题核心是pdf.js渲染的画布坐标与pdf-lib使用的PDF原生坐标系统没有正确映射,当前代码存在几个关键错误:
- 点击坐标获取未考虑容器偏移与画布缩放
- 像素转PDF点的硬编码系数(
72/1.4)完全错误 - 未结合pdf.js的viewport进行精确坐标转换
修复步骤与关键代码修改
1. 正确获取点击点相对于画布的本地坐标
在addEditableContent中,放弃全局坐标计算,转而获取点击点相对于canvas的精准位置,同时考虑容器滚动偏移:
function addEditableContent(event) { if (event.target.tagName.toLowerCase() === 'canvas') { const canvas = event.target; // 获取canvas在页面中的位置信息 const rect = canvas.getBoundingClientRect(); // 计算点击点相对于canvas左上角的坐标(抵消滚动和容器偏移) const canvasX = event.clientX - rect.left + canvas.scrollLeft; const canvasY = event.clientY - rect.top + canvas.scrollTop; let customFontSize = 6; const editableContent = document.createElement('div'); editableContent.contentEditable = true; editableContent.style.position = 'absolute'; // 基于canvas的页面位置定位编辑框 editableContent.style.left = `${rect.left + canvasX}px`; editableContent.style.top = `${rect.top + canvasY}px`; editableContent.style.fontSize = `${customFontSize}px`; editableContent.innerText = 'Editable Text'; const canvasContainer = document.getElementById('pdf-container'); canvasContainer.appendChild(editableContent); // 保存画布本地坐标,而非全局坐标 editableContents.push({ element: editableContent, canvasX, canvasY }); editableContent.focus(); } }
2. 基于pdf.js的viewport实现精确坐标转换
删除硬编码的pixelsToPoints函数,改用pdf.js内置的viewport.convertToPdfPoint方法,自动处理缩放、旋转和页面边界:
// 全局保存当前页面的viewport,供后续坐标转换使用 let currentViewport; async function loadAndDisplayPdf() { const pdfUrl = 'software-development-proposal-template-1702352345704.pdf'; pdfBytes = await fetch(pdfUrl).then(response => response.arrayBuffer()); const loadingTask = pdfjsLib.getDocument({ data: pdfBytes }); const pdf = await loadingTask.promise; const pageNumber = 1; const page = await pdf.getPage(pageNumber); const scale = 1.5; const viewport = page.getViewport({ scale }); currentViewport = viewport; // 保存viewport const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.width = viewport.width; canvas.height = viewport.height; await page.render({ canvasContext: context, viewport }).promise; const pdfContainer = document.getElementById('pdf-container'); pdfContainer.innerHTML = ''; pdfContainer.appendChild(canvas); canvas.addEventListener('dblclick', addEditableContent); }
3. 修改pdf-lib的文本绘制坐标计算
在modifyAndDownloadPdf中,结合保存的viewport和PDF页面的cropBox,完成坐标映射并处理Y轴反转:
async function modifyAndDownloadPdf() { if (!pdfBytes || !currentViewport) { console.error('PDF或viewport未加载'); return; } const pdfDoc = await PDFDocument.load(pdfBytes); const firstPage = pdfDoc.getPages()[0]; // 获取PDF页面的实际显示边界(优先用cropBox,无则用mediabox) const cropBox = firstPage.getCropBox(); const pageWidth = cropBox.width; const pageHeight = cropBox.height; editableContents.forEach(({ element, canvasX, canvasY }) => { const editText = element.innerText; // 将画布坐标直接转换为PDF原生坐标 const [pdfX, pdfY] = currentViewport.convertToPdfPoint(canvasX, canvasY); // 对齐cropBox的坐标系(处理页面偏移) const finalX = pdfX + cropBox.x; // PDF原点在左下角,画布原点在左上角,反转Y轴 const finalY = pageHeight - (pdfY - cropBox.y); firstPage.drawText(editText, { x: finalX, y: finalY, size: 6, color: rgb(0.95, 0.1, 0.1), }); }); const modifiedPdfBytes = await pdfDoc.save(); const downloadLink = document.createElement('a'); downloadLink.href = URL.createObjectURL(new Blob([modifiedPdfBytes], { type: 'application/pdf' })); downloadLink.download = 'modified-pdf.pdf'; downloadLink.click(); }
核心原理说明
- 坐标系统对齐:pdf.js的
viewport.convertToPdfPoint自动完成画布像素到PDF原生点(1点=1/72英寸)的转换,无需手动计算DPI或缩放比例。 - 页面边界处理:
getCropBox获取PDF页面的实际显示区域,避免因页面边距、裁剪导致的位置偏移。 - Y轴反转:PDF坐标原点在页面左下角,画布原点在左上角,通过页面高度减去转换后的Y值实现位置对齐。
内容的提问来源于stack exchange,提问作者Gabriel J
相关产品推荐
相关产品推荐

