前端实现 HTML 转 PDF 并导出

·  阅读 6054

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

主要介绍了前端实现 HTML 转 PDF 并导出的一些方法,包括浏览器自带的、vue-print-nb、html2canvas 和 jspdf,wkhtmltopdf 以及 iText

系统中使用了图形化报表,那么导出报表也是必不可少了,一般的导出形式就是pdf文件,对此,进行了一番研究,以下就是踩坑经验。

以如下报表页为例: ic_print.png

Window.print

官网地址:developer.mozilla.org/zh-CN/docs/…

详细使用可参考文档:window.print() 前端实现网页打印详解

这个是借助浏览器自带的打印来实现的,默认打印页面中 body里的所有内容,即全局打印,如果要局部打印,需要替换 body 的内容,会改变原网页内容,体验不是很好。

 <a-button type="primary" @click="window.print()">window.print</a-button>
复制代码

ic_print_1.png

vue-print-nb

官网安装及使用文档:www.npmjs.com/package/vue…

该插件本质上也是使用浏览器自带的打印 window.print()来实现的,默认会创建一个 iframe,将要打印的内容嵌入进去。相较第一个方法而言,更加灵活一点,但是样式的支持还是比较差的,ant design vue 组件的样式展示不全,需要自己去重写样式,这样就麻烦了呀。

// 1. 安装
npm install vue-print-nb --save

// 2. 全局注册(main.js)
import Print from "vue-print-nb";
Vue.use(Print);

// 3. 使用
<a-button type="primary" v-print="printObj">
  vue-print-nb
</a-button>;

var printObj = {
  id: "pdfDom",
  popTitle: "good print",
  extraCss: "https://www.google.com,https://www.google.com",
  extraHead: '<meta http-equiv="Content-Language"content="zh-cn"/>',
};
复制代码

ic_print_2.png

html2canvas + jspdf

html2canvas:github.com/niklasvh/ht…

jspdf:github.com/parallax/js…

通过 html2canvasHTML 页面转换成canvas图片,再通过 jspdf 将图片转成 pdf格式文件。

缺点:

  1. 清晰度不够
  2. 分页时,内容会被截断,不能智能展示完整
  3. 页面内容太长时(宽高限制为 14400),无法打印出内容,一直是空白页。这个真的就很不友好了呀,报表的内容肯定多呀...

ic_print_4.png

注意点:

  • 引入外链图片时,需要配置图片跨域,给 img 标签设置 crossOrigin='anonymous'
  • 提高生成图片质量,可以适当放大 canvas 画布,通过设置 scale 缩放画布大小,或者设置 dpi 提高清晰度。
  1. 安装
npm install html2canvas jspdf --save
复制代码
  1. 定义工具函数
// utils/htmlToPdf.js:导出页面为PDF格式
import html2Canvas from "html2canvas";
import JsPDF from "jspdf";

export default {
  install(Vue, options) {
    // id-导出pdf的div容器;title-导出文件标题
    Vue.prototype.htmlToPdf = (id, title) => {
      const element = document.getElementById(`${id}`);
      const opts = {
        scale: 12, // 缩放比例,提高生成图片清晰度
        useCORS: true, // 允许加载跨域的图片
        allowTaint: false, // 允许图片跨域,和 useCORS 二者不可共同使用
        tainttest: true, // 检测每张图片已经加载完成
        logging: true, // 日志开关,发布的时候记得改成 false
      };

      html2Canvas(element, opts)
        .then((canvas) => {
          let contentWidth = canvas.width;
          let contentHeight = canvas.height;
          // 一页pdf显示html页面生成的canvas高度;
          let pageHeight = (contentWidth / 592.28) * 841.89;
          // 未生成pdf的html页面高度
          let leftHeight = contentHeight;
          // 页面偏移
          let position = 0;
          // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
          let imgWidth = 595.28;
          let imgHeight = (592.28 / contentWidth) * contentHeight;
          let pageData = canvas.toDataURL("image/jpeg", 1.0);

          // a4纸纵向,一般默认使用;new JsPDF('landscape'); 横向页面
          let PDF = new JsPDF("", "pt", "a4");

          // 当内容未超过pdf一页显示的范围,无需分页
          if (leftHeight < pageHeight) {
            // addImage(pageData, 'JPEG', 左,上,宽度,高度)设置
            PDF.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
          } else {
            // 超过一页时,分页打印(每页高度841.89)
            while (leftHeight > 0) {
              PDF.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
              leftHeight -= pageHeight;
              position -= 841.89;
              if (leftHeight > 0) {
                PDF.addPage();
              }
            }
          }
          PDF.save(title + ".pdf");
        })
        .catch((error) => {
          console.log("打印失败", error);
        });
    };
  },
};
复制代码
  1. 全局注册
// main.js文件
import htmlToPdf from "./utils/htmlToPdf";
Vue.use(htmlToPdf);
复制代码
  1. 使用
export default {
  methods: {
    handleExportPdf() {
      // 滚动到顶部,确保打印内容完整
      document.body.scrollTop = 0; // IE的
      document.documentElement.scrollTop = 0; // 其他
      this.htmlToPdf("pdfDom", "统计报告");
    },
  },
};
复制代码

ic_print_3.png

下一篇介绍wkhtmltopdf和iText踩坑

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改