概述
因为生成的PDF内还有富文本,后端不好生成PDF,故由前端完成。
原理
html2canva生成截图,jsPDF利用图片生成PDF。
过程中遇到的问题
1、生成大于30多页时,容易造成生成页面黑屏问题。
- 原因:canvas生成图片时,对内容高度有一定的限制,超过这个高度,canvas生成不了图片。
- 解决方案:将html文档中需要生成PDF的部分,不再整个内容生成一张截图,而是根据内容切割成多个具有相同类名的div,生成多张截图。然后循环截图列表,生成PDF。
2、 因为html内容时table包裹的,没有连续一行都是空白的,所以如何判断canvas可以截断
- 解决方案:遍历循环,如果不是白色的像素点不超过20,可以截断
3、解决了黑屏问题,但是随之,想在一页上添加水印造成了困难。
- 解决方案:最终生成PDF文件时,遍历每一页PDF,然后将canvas生成的水印于PNG格式,覆盖在每一页PDF上。看了源码才看到jspdf的addImage接口可以传canvas对象,这时候会对图片格式进行确认,然后传PNG格式就可以不覆盖原本的内容了。
生成的PDF截图
页眉、页脚、封面、水印
代码示例
- 注意:在本次示例中,因为内容从data中获取,所以在mounted生命周期中调用生成PDF方法。在实际开发中可能是从后台获取的,需要在updated生命周期中调用生成PDF方法。
<template>
<div class="app-container">
<div class="mask">
<div v-if="!isAddPage" class="tips">
<i class="el-icon-loading"/>
附件正在生成中......
</div>
<div v-else class="tips">
<i class="el-icon-loading"/>
附件正在生成第<span class="pdfProgress">{
{ pdfPage }}</span>页,共<span class="pdfTotal">{
{ pdfProgress }}</span>页......
</div>
</div>
<!-- 页眉和页码 -->
<div class="hideDiv">
<div id="title">
<p> 我是页眉 </p>
</div>
<div id="footer">
第<span class="pdfProgress">{
{ pdfPage }}</span>页
</div>
</div>
<div id="pdfDom">
<div id="cover">
<div class="coverContent">
<h1 style="font-size: 22px;">XXX股份有限公司</h1>
<h2 style="font-size: 22px;">《张三报告》</h2>
</div>
</div>
<div class="content">
<div class="casepoint">
{
{ pdfContent.content }}
</div>
<div class="casepoint">
{
{ pdfContent.content }}
</div>
<div class="casepoint">
{
{ pdfContent.content }}
</div>
</div>
</div>
</div>
</template>
<script>
import html2Canvas from 'html2canvas'
import jsPDF from 'jspdf'
export default{
name: 'SeePdf',
data() {
return {
pdfProgress: 1,
pdfPage: 1,
pdfTotal: null,
type: '01', // 01预览 02下载
taskno: '',
pdfContent: {
content: '我是内容' },
isDownloaded: false, // 是否生成PDF
isOver: false, // 是否生成完毕
a4Width: 595.28,
a4Height: 841.89,
pdf: null,
canvas: [],
a4HeightRef: 0,
position: 0,
leftHeight: 0,
pageFooter: null,
canvasFooter: null,
pageTitle: null,
canvasTitle: null,
canvasTitleH: null,
canvasbreakHeight: 0,
canvasIndex: 0,
pageData: null,
isAddPage: false
}
},
watch: {
isOver: function(newVal) {
if (newVal) {
setTimeout(function() {
window.close()
}, 300)
}
}
},
mounted() {
if (!this.isDownloaded) {
this.getPdfFun()
}
},
// 内容从后台接口获取时,在updated生命周期中调用生成PDF方法
// updated() {
// if (!this.isDownloaded) {
// this.getPdfFun()
// }
// },
methods: {
getPdfFun() {
// 初始化PDF
this.pdf = new jsPDF('x', 'pt', 'a4')
this.pdf.page = 1
this.pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen')
this.isDownloaded = true
if (this.type === '01') {
this.getPdf('pdf名字', true)
} else if (this.type === '02') {
this.getPdf('pdf名字', false)
}
},
async getPdf(title, show) {
// 生成页眉图片
this.canvasTitle = await html2Canvas(document.getElementById('title'))
this.pageTitle = this.canvasTitle.toDataURL('image/jpeg', 1.0)
// 生成首页图片
const canvasCover = await html2Canvas(document.querySelector('#cover'), {
allowTaint: true,
scale: 2
})
// 生成首页PDF
this.pdf.addImage(canvasCover.toDataURL('image/jpeg', 1.0), 'JPEG', 0, 0, this.a4Width, this.a4Width / canvasCover.width * canvasCover.height)
// 生成内容 6个案件点的图片
const doms = document.getElementsByClassName('casepoint')
for (let i = 0