前端导出PDF

3,296 阅读2分钟

项目开发过程中有导出当前的页面为pdf的功能,为了减少服务器压力,所以由前端结合html2canvasjspdf2个库来实现前端导出pdf。

基本思路

  1. 使用html2canvas将对应的DOM节点生成canvas
  2. 通过jspdf库将canvas生成pdf
  3. 最后下载pdf

通过DOM节点生成canvas

/**
 *  @param elId 打印的节点ID
 */
async function getCanvasByHtmlId(elId) {
  let canvas = await html2canvas(document.getElementById(elId), {
    scale: 2,
    useCORS: true,
    allowTaint: true,
    taintTest: false,
    imageTimeout: 0
  }).then(canvas => {
    return canvas;
  });

  return canvas;
}

通过canvas创建pdf

/**
 *  @param htmlCanvas canvas对象
 */
function canvasToPdf(htmlCanvas) {
  let canvasWidth = htmlCanvas.width;
  let canvasHeight = htmlCanvas.height;
  let imgBase64 = htmlCanvas.toDataURL("image/jpeg", 1.0);

  //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
  let imgWidth = 595.28;
  //图片高度需要等比缩放
  let imgHeight = 595.28 / canvasWidth * canvasHeight;

  let pageHeight = imgHeight; //pdf转化后页面总高度
  let position = 0;

  let pdfInstance = new jspdf("", "pt", "a4");
  pdfInstance.setFontSize(12);

  if (imgHeight < 841.89) {
    pdfInstance.addImage(imgBase64, "JPEG", 0, 0, imgWidth, imgHeight);
  } else {
    while (pageHeight > 0) {
      pdfInstance.addImage(imgBase64, "JPEG", 0, position, imgWidth, imgHeight);
      pageHeight -= 841.89;
      position -= 841.89;
      if (pageHeight > 0) {
        pdfInstance.addPage();
      }
    }
  }

  return pdfInstance;
}

下载pdf

function downPdf(pdfInstance, title) {
  // 文件名过长导致下载失败
  if (title.length > 50) {
    title = title.substring(title.length - 50);
  }

  pdfInstance.save(title + ".pdf", { returnPromise: true }).then(function() {
    //搜狗浏览器下载机制问题暂时不关闭
    if (!(navigator.userAgent.toLowerCase().indexOf("se 2.x") > -1)) {
      setTimeout(window.close, 300);
    }
  });
}

整体代码如下

import html2canvas from "html2canvas";
import jspdf from "jspdf";

/**
 *  @param title pdf文件名
 *  @param elId 打印的节点ID
 */
async function htmlToPdf(title = "文件", elId) {
  if (!elId) {
    console.error("导出节点不存在!");
    return;
  }

  //将html dom节点生成canvas
  let htmlCanvas = await getCanvasByHtmlId(elId);
  // 将canvas对象转为pdf
  let pdf = canvasToPdf(htmlCanvas);
  // 通过浏览器下载pdf
  downPdf(pdf, title);
}

/**
 *  @param elId 打印的节点ID
 */
async function getCanvasByHtmlId(elId) {
  let canvas = await html2canvas(document.getElementById(elId), {
    scale: 2,
    useCORS: true,
    allowTaint: true,
    taintTest: false,
    imageTimeout: 0
  }).then(canvas => {
    return canvas;
  });

  return canvas;
}

/**
 *  @param htmlCanvas canvas对象
 */
function canvasToPdf(htmlCanvas) {
  let canvasWidth = htmlCanvas.width;
  let canvasHeight = htmlCanvas.height;
  let imgBase64 = htmlCanvas.toDataURL("image/jpeg", 1.0);

  //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
  let imgWidth = 595.28;
  //图片高度需要等比缩放
  let imgHeight = 595.28 / canvasWidth * canvasHeight;

  let pageHeight = imgHeight; //pdf转化后页面总高度
  let position = 0;

  let pdfInstance = new jspdf("", "pt", "a4");
  pdfInstance.setFontSize(12);

  if (imgHeight < 841.89) {
    pdfInstance.addImage(imgBase64, "JPEG", 0, 0, imgWidth, imgHeight);
  } else {
    while (pageHeight > 0) {
      pdfInstance.addImage(imgBase64, "JPEG", 0, position, imgWidth, imgHeight);
      pageHeight -= 841.89;
      position -= 841.89;
      if (pageHeight > 0) {
        pdfInstance.addPage();
      }
    }
  }

  return pdfInstance;
}

function downPdf(pdfInstance, title) {
  // 文件名过长导致下载失败
  if (title.length > 50) {
    title = title.substring(title.length - 50);
  }

  pdfInstance.save(title + ".pdf", { returnPromise: true }).then(function() {
    //搜狗浏览器下载机制问题暂时不关闭
    if (!(navigator.userAgent.toLowerCase().indexOf("se 2.x") > -1)) {
      setTimeout(window.close, 300);
    }
  });
}

export default htmlToPdf;