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

使用html2canvas与jsPDF遇分页数据重复及iOS/Safari下载失败问题

问题解决:PDF生成分页重复、Safari下载失败及html2pdf适配问题

一、html2canvas+jsPDF分页内容重复问题

原代码的分页逻辑是直接将完整canvas偏移位置添加到新页面,导致内容重复。正确做法是截取canvas对应页面的区域,而非整图偏移。

修正代码:

const accordionTable = document.createElement('div');
ReactDOM.render(<ReportMainPDF repdata={response.data} />, accordionTable);
document.body.appendChild(accordionTable);

// 等待组件渲染完成,确保DOM完全加载
setTimeout(() => {
  html2canvas(accordionTable, { logging: true, scale: 2, allowTaint: true, useCORS: true }).then((canvas) => {
    const imgWidth = 208;
    const pageHeight = 295;
    const imgHeight = (canvas.height * imgWidth) / canvas.width;
    const pageCount = Math.ceil(imgHeight / pageHeight);
    const doc = new jsPDF('p', 'mm', [210, 297]); // 用标准A4尺寸减少适配问题

    for (let i = 0; i < pageCount; i++) {
      // 创建临时canvas截取当前页内容
      const tempCanvas = document.createElement('canvas');
      tempCanvas.width = canvas.width;
      tempCanvas.height = pageHeight * (canvas.width / imgWidth); // 按比例计算每页高度
      const ctx = tempCanvas.getContext('2d');
      // 截取对应区域:y轴偏移为当前页的起始位置
      const yOffset = i * tempCanvas.height;
      ctx.drawImage(canvas, 0, yOffset, canvas.width, tempCanvas.height, 0, 0, tempCanvas.width, tempCanvas.height);

      if (i > 0) doc.addPage();
      doc.addImage(tempCanvas.toDataURL('image/png'), 'PNG', 0, 0, imgWidth, pageHeight);
    }

    // 兼容Safari的下载逻辑
    const blob = doc.output('blob');
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'test.pdf';
    document.body.appendChild(a);
    
    // Safari特殊处理:无法直接触发a标签下载,改用打开新窗口让用户手动保存
    if (navigator.userAgent.includes('Safari') && !navigator.userAgent.includes('Chrome')) {
      window.open(url);
    } else {
      a.click();
    }
    setTimeout(() => {
      URL.revokeObjectURL(url);
      a.remove();
    }, 100);

    ReactDOM.unmountComponentAtNode(accordionTable);
    accordionTable.remove();
    setIsLoading(false);
  });
}, 500); // 等待时间可根据组件渲染复杂度调整

二、iPhone Safari无法下载PDF问题

Safari对doc.save()的支持存在兼容性问题,改用Blob生成下载链接做兼容:

  • 先将PDF转为Blob对象,创建临时URL
  • 非Safari环境用a标签触发下载,Safari环境打开新窗口让用户手动保存PDF

三、html2pdf内容放大适配+空白页问题

改用html2pdf时,需调整配置确保渲染正确:

修正配置示例:

const accordionTable = document.createElement('div');
ReactDOM.render(<ReportMainPDF repdata={response.data} />, accordionTable);
document.body.appendChild(accordionTable);

setTimeout(() => {
  const opt = {
    margin: 10,
    filename: 'test.pdf',
    image: { type: 'jpeg', quality: 0.98 },
    html2canvas: { scale: 1, logging: true, allowTaint: true, useCORS: true },
    jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
    pagebreak: { mode: ['css', 'legacy'] } // 启用CSS分页规则
  };

  // 确保生成PDF的元素在DOM中可见,避免移动端渲染空白
  accordionTable.style.display = 'block';
  accordionTable.style.position = 'relative';
  accordionTable.style.zIndex = '9999';

  html2pdf().set(opt).from(accordionTable).save().then(() => {
    ReactDOM.unmountComponentAtNode(accordionTable);
    accordionTable.remove();
    setIsLoading(false);
  });
}, 800);

关键注意点:

  • 等待React组件完全渲染后再调用html2pdf,避免未加载完成导致空白
  • 设置html2canvas.scale:1避免内容放大,配合jsPDF.format:'a4'适配页面
  • 启用pagebreak规则,或在React组件中用CSS page-break-after: always;手动设置分页
  • 确保生成PDF的元素在DOM中可见,避免移动端因隐藏元素导致空白

内容的提问来源于stack exchange,提问作者Abdur Rehman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:53:22