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

如何用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();
}

核心原理说明

  1. 坐标系统对齐:pdf.js的viewport.convertToPdfPoint自动完成画布像素到PDF原生点(1点=1/72英寸)的转换,无需手动计算DPI或缩放比例。
  2. 页面边界处理:getCropBox获取PDF页面的实际显示区域,避免因页面边距、裁剪导致的位置偏移。
  3. Y轴反转:PDF坐标原点在页面左下角,画布原点在左上角,通过页面高度减去转换后的Y值实现位置对齐。

内容的提问来源于stack exchange,提问作者Gabriel J

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:53:23