前端实现 HTML 转 PDF 并导出 - zptime/blog GitHub Wiki
主要介绍了前端实现 HTML 转 PDF 并导出的一些方法,包括浏览器自带的、vue-print-nb、html2canvas和jspdf,wkhtmltopdf以及iText
系统中使用了图形化报表,那么导出报表也是必不可少了,一般的导出形式就是pdf
文件,对此,进行了一番研究,以下就是踩坑经验。
以如下报表页为例:
官网地址:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/print
详细使用可参考文档:window.print() 前端实现网页打印详解
这个是借助浏览器自带的打印来实现的,默认打印页面中 body
里的所有内容,即全局打印,如果要局部打印,需要替换 body
的内容,会改变原网页内容,体验不是很好。
<a-button type="primary" @click="window.print()">window.print</a-button>
官网安装及使用文档:https://www.npmjs.com/package/vue-print-nb
该插件本质上也是使用浏览器自带的打印 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"/>',
};
html2canvas:https://github.com/niklasvh/html2canvas
jspdf:https://github.com/parallax/jsPDF
通过 html2canvas
将 HTML
页面转换成canvas
图片,再通过 jspdf
将图片转成 pdf
格式文件。
缺点:
- 清晰度不够
- 分页时,内容会被截断,不能智能展示完整
- 页面内容太长时(宽高限制为 14400),无法打印出内容,一直是空白页。这个真的就很不友好了呀,报表的内容肯定多呀...
注意点:
- 引入外链图片时,需要配置图片跨域,给
img
标签设置crossOrigin='anonymous'
。 - 提高生成图片质量,可以适当放大
canvas
画布,通过设置scale
缩放画布大小,或者设置dpi
提高清晰度。
- 安装
npm install html2canvas jspdf --save
- 定义工具函数
// 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);
});
};
},
};
- 全局注册
// main.js文件
import htmlToPdf from "./utils/htmlToPdf";
Vue.use(htmlToPdf);
- 使用
export default {
methods: {
handleExportPdf() {
// 滚动到顶部,确保打印内容完整
document.body.scrollTop = 0; // IE的
document.documentElement.scrollTop = 0; // 其他
this.htmlToPdf("pdfDom", "统计报告");
},
},
};
wkhtmltopdf
使用 webkit
渲染引擎开发的,直接把 html
页面转成 pdf
,但是需要安装在服务器上。
官方 Issues:https://github.com/wkhtmltopdf/wkhtmltopdf/issues
配置参数:https://wkhtmltopdf.org/usage/wkhtmltopdf.txt
配置参数中文参考:https://segmentfault.com/a/1190000018988358
// 1. 安装:在官网下载安装包
// 2. 查看版本号:wkhtmltopdf 0.12.6 (with patched qt)
$ wkhtmltopdf -V
// 3. 测试:在cmd里输入测试指令,查看处理进度
$ wkhtmltopdf https://www.baidu.com/ ~/Desktop/baidu.pdf // 网络页面
$ wkhtmltopdf --javascript-delay 3000 report.html test.pdf // 本地HTML页面
百度页测试指令,可查看处理进度:
百度页导出效果展示:
前端需要写静态html
模版页面,交给服务端渲染并且导出。
模版页实践踩坑:
- 采用
webkit
渲染引擎开发,对CSS
样式整体支持友好,但对CSS3
的新特性支持不太好,部分页面样式会失效。 wkhtmltopdf uses Qt to render HTML. The current version of wkhtmltopdf uses Qt 4.8.5, which uses version 2.2.4 of WebKit, As you can see I tried using the -webkit- prefixes(wkhtmltopdf 使用 Qt 来呈现 HTML。 wkhtmltopdf 的当前版本使用 Qt 4.8.5,它使用 WebKit 的 2.2.4 版,建议使用 -webkit- 前缀) - 采用 flex 布局,需要使用兼容性样式,具体可查看issue
- 采用 flex 布局,导出的 pdf 会被截断,因此改为 table 布局
- 默认字体时宋体,如果机器上没有,中文无法显示;要想使用别的字体,必须安装对应的字体,具体可查看issue
- vertical-align 属性不起作用
- 静态模板写 CSS 样式比较麻烦,可以直接写 SCSS,然后同步转化成 CSS
// 安装sass
npm install -g sass
// 实时编译命令
sass --watch test.scss:test.css
IText:用于 Java 后台,将 HTML 文件转换成 PDF 文件。基于 java 将 html 的 css 样式做解析处理,仅对较简单的页面和样式支持。
使用流程:前端负责完成静态模版页面,后端基于模版页进行数据填充,然后导出 pdf 文件,前端进行下载。
模版页实践踩坑:
- 对 CSS 样式支持极差,CSS3 属性都不支持
- 不能用 flex 布局,基本只能用 table 实现
- 要换页的地方,添加
page-break-after: always;
,表示下一个元素将会换页,转 pdf 时 itext 会自动识别 - 定位中,不支持以百分比为单位的 css 属性 top(css property top in percents is not supported)
- 绝对定位可能报错(Occupied area has not been initialized. Absolute positioning might be applied incorrectly)