likes
comments
collection
share

使用 HTML2Canvas 和 jsPDF 导出和打印 PDF 的最佳实践在前端开发中,导出网页内容为 PDF 文件是

作者站长头像
站长
· 阅读数 32

在前端开发中,导出网页内容为 PDF 文件是一个常见需求,无论是生成报表、保存网页截图,还是打印功能。本文将介绍如何使用 html2CanvasjsPDF 库实现这一功能,并提供详细的代码示例和优化建议。

一、引入依赖

首先,我们需要安装 html2CanvasjsPDF 两个库:

npm install html2canvas jspdf

二、功能实现

1. 生成 PDF 的核心函数

drawPdf 函数

drawPdf 函数负责将指定的 DOM 元素转换为 PDF 文件:

  • 参数

    • option: 包含 PDF 尺寸的选项,默认尺寸为 A4 纸大小(210mm x 297mm)。
    • dom: 要转换为 PDF 的 DOM 元素。
    • pdf: 可选的 jsPDF 实例,用于在多页面导出时复用。
  • 步骤

    1. 使用 html2Canvas 将 DOM 元素转换为 Canvas 对象。
    2. 计算内容的宽高和 PDF 页面尺寸,确定分页逻辑。
    3. 使用 jsPDF 将 Canvas 转换为 PDF,并处理分页。
async function drawPdf(option = { width: 210, height: 297 }, dom, pdf) { 
  const element = dom || document.getElementById("pdfDom");
  window.scrollTo(0, 0);
  
  const canvas = await html2Canvas(element, {
    useCORS: true,
    allowTaint: false,
    taintTest: true,
    scale: 2,
  });
  
  const contentWidth = canvas.width;
  const contentHeight = canvas.height;
  const pdfWidth = option.width;
  const pdfHeight = option.height;
  const pageHeight = (contentWidth / pdfWidth) * pdfHeight;
  let leftHeight = contentHeight;
  let position = 0;

  const pageData = canvas.toDataURL("image/jpeg", 1.0);
  pdf = pdf || new jsPDF({
    orientation:"p", 
    unit: "mm", 
    format: [option.width, option.height]
  });

  if (leftHeight < pageHeight) {
    pdf.addImage(pageData, "JPEG", 0, 0, pdfWidth, pdfHeight);
  } else {
    while (leftHeight > 0) {
      pdf.addImage(pageData, "JPEG", 0, position, pdfWidth, (pdfWidth / contentWidth) * contentHeight);
      leftHeight -= pageHeight;
      position -= pdfHeight;
      if (leftHeight > 0) {
        pdf.addPage();
      }
    }
  }

  return pdf;
}

2. 打印 PDF

handlePrint 函数

handlePrint 函数通过打开一个新窗口加载 PDF 文件,并调用浏览器的打印功能:

  • 步骤

    1. 创建一个新的窗口并加载 PDF。
    2. 动态添加打印样式,确保打印效果。
    3. 打印完成后关闭窗口并释放资源。
function handlePrint(url) {
  const printWindow = window.open(url);
  printWindow.onload = function() {
    const style = `
      @media print {
        @page {
          size: auto;
          margin: 0;
        }
        body {
          transform: scale(1);
          transform-origin: top left;
          width: 100%;
          height: 100%;
          overflow: hidden;
        }
        img {
          width: 100%;
          height: auto;
        }
      }
    `;
    const styleSheet = printWindow.document.createElement("style");
    styleSheet.type = "text/css";
    styleSheet.innerText = style;
    printWindow.document.head.appendChild(styleSheet);
    printWindow.print();
    printWindow.onafterprint = function() {
      printWindow.close();
      URL.revokeObjectURL(url); // 清理资源
    };
  };
}

3. 导出和打印 PDF 的封装

htmlToPdf 对象

htmlToPdf 对象封装了导出和打印 PDF 的功能:

  • exportPdf 方法

    • 用于导出 PDF 文件,并支持处理多个 DOM 元素。
  • printPdf 方法

    • 用于打印 PDF 文件,同样支持处理多个 DOM 元素。
const htmlToPdf = {
  async exportPdf(option, dom, name) {
    try {
      let pdf = null;
      if (dom instanceof Array) {
        for (let index = 0; index < dom.length; index++) {
          pdf = await drawPdf(option, dom[index], pdf);
          if (index < dom.length-1) {
            pdf.addPage();
          }
        }
      } else {
        pdf = await drawPdf(option, dom)
      }
      pdf.save(`${name}.pdf`);
    } catch (error) {
      console.error("导出PDF失败", error);
    }
  },

  async printPdf(option, dom) {
    try {
      let pdf = null;
      if (dom instanceof Array) {
        for (let index = 0; index < dom.length; index++) {
          pdf = await drawPdf(option, dom[index], pdf);
          if (index < dom.length-1) {
            pdf.addPage();
          }
        }
      } else {
        pdf = await drawPdf(option, dom)
      }
      const pdfBlob = pdf.output('blob');
      const url = URL.createObjectURL(pdfBlob);
      handlePrint(url);
    } catch (error) {
      console.error("打印PDF失败", error);
    }
  },
};

export default htmlToPdf;

三、代码实现

将上述所有代码整合到一起,最终实现如下:

import html2Canvas from "html2canvas";
import jsPDF from "jspdf";

async function drawPdf(option = { width: 210, height: 297 }, dom, pdf) { 
  const element = dom || document.getElementById("pdfDom");
  window.scrollTo(0, 0);
  
  const canvas = await html2Canvas(element, {
    useCORS: true,
    allowTaint: false,
    taintTest: true,
    scale: 2,
  });
  
  const contentWidth = canvas.width;
  const contentHeight = canvas.height;
  const pdfWidth = option.width;
  const pdfHeight = option.height;
  const pageHeight = (contentWidth / pdfWidth) * pdfHeight;
  let leftHeight = contentHeight;
  let position = 0;

  const pageData = canvas.toDataURL("image/jpeg", 1.0);
  pdf = pdf || new jsPDF({
    orientation:"p", 
    unit: "mm", 
    format: [option.width, option.height]
  });

  if (leftHeight < pageHeight) {
    pdf.addImage(pageData, "JPEG", 0, 0, pdfWidth, pdfHeight);
  } else {
    while (leftHeight > 0) {
      pdf.addImage(pageData, "JPEG", 0, position, pdfWidth, (pdfWidth / contentWidth) * contentHeight);
      leftHeight -= pageHeight;
      position -= pdfHeight;
      if (leftHeight > 0) {
        pdf.addPage();
      }
    }
  }

  return pdf;
}

function handlePrint(url) {
  const printWindow = window.open(url);
  printWindow.onload = function() {
    const style = `
      @media print {
        @page {
          size: auto;
          margin: 0;
        }
        body {
          transform: scale(1);
          transform-origin: top left;
          width: 100%;
          height: 100%;
          overflow: hidden;
        }
        img {
          width: 100%;
          height: auto;
        }
      }
    `;
    const styleSheet = printWindow.document.createElement("style");
    styleSheet.type = "text/css";
    styleSheet.innerText = style;
    printWindow.document.head.appendChild(styleSheet);
    printWindow.print();
    printWindow.onafterprint = function() {
      printWindow.close();
      URL.revokeObjectURL(url); // 清理资源
    };
  };
}

const htmlToPdf = {
  async exportPdf(option, dom, name) {
    try {
      let pdf = null;
      if (dom instanceof Array) {
        for (let index = 0; index < dom.length; index++) {
          pdf = await drawPdf(option, dom[index], pdf);
          if (index < dom.length-1) {
            pdf.addPage();
          }
        }
      } else {
        pdf = await drawPdf(option, dom)
      }
      pdf.save(`${name}.pdf`);
    } catch (error) {
      console.error("导出PDF失败", error);
    }
  },

  async printPdf(option, dom) {
    try {
      let pdf = null;
      if (dom instanceof Array) {
        for (let index = 0; index < dom.length; index++) {
          pdf = await drawPdf(option, dom[index], pdf);
          if (index < dom.length-1) {
            pdf.addPage();
          }
        }
      } else {
        pdf = await drawPdf(option, dom)
      }
      const pdfBlob = pdf.output('blob');
      const url = URL.createObjectURL(pdfBlob);
      handlePrint(url);
    } catch (error) {
      console.error("打印PDF失败", error);
    }
  },
};

export default htmlToPdf;

四、总结

通过 html2CanvasjsPDF 库,我们可以轻松实现将网页内容导出为 PDF 并支持打印的功能。本文提供了详细的代码示例和优化建议,希望能对你有所帮助。

如果你有任何疑问或建议,欢迎在评论区留言讨论。一起交流,一起进步!


希望这篇文章对你在前端开发中处理 PDF 导出和打印功能有所帮助。如果你喜欢这篇文章,别忘了点个赞或者关注我,后续会有更多前端开发的精彩内容分享给大家!

转载自:https://juejin.cn/post/7390339205983862838
评论
请登录