这个PDF折腾我一天了,太难了。。。
问题1:导出的PDF有可能会因为纸张长度问题导致导出的PDF 会被截断
如图:
解决方案:
首先,根据A4纸的宽高,计算出DOM页面一页的高度,然后给pdf里面的所有元素添加一个类作为标识,在打印pdf前先判断这个元素是否在两页之间,如果在的话则创建一个空div,计算插入空白快的高度,然后插入到这个元素前面,将要被截断的元素往下移,这样即可避免会被截断的情况。
代码如下:
const A4_WIDTH = 841.89 //因为我是横向截图,所以A4纸的宽高会进行稍微调整
const A4_HEIGHT = 592.28
// const A4_WIDTH = 592.28
// const A4_HEIGHT = 841.89
// 根据A4的宽高计算DOM页面一页应该对应的高度
let pageHeight = dom.offsetWidth / A4_WIDTH * A4_HEIGHT
// 将所有不允许被截断的元素进行处理
let wholeNodes = document.querySelectorAll('.whole-node')
for (let i = 0; i < wholeNodes.length; i++) {
//1、 判断当前的不可分页元素是否在两页显示
const topPageNum = Math.ceil((wholeNodes[i].offsetTop) / pageHeight)
const bottomPageNum = Math.ceil((wholeNodes[i].offsetTop +
wholeNodes[i].offsetHeight) / pageHeight)
if (topPageNum !== bottomPageNum) {
//说明该dom会被截断
// 2、插入空白块使被截断元素下移
let divParent = wholeNodes[i].parentNode
let newBlock = document.createElement('div')
newBlock.className = 'emptyDiv'
newBlock.style.background = '#fff'
// 3、计算插入空白块的高度 可以适当流出空间使得内容太靠边,根据自己需求而定
let _H = topPageNum * pageHeight - wholeNodes[i].offsetTop
newBlock.style.height = _H + 30 + 'px'
divParent.insertBefore(newBlock, wholeNodes[i])
}
}
弄完即可正常转pdf即可
转pdf代码如下:
if (dom) {
html2canvas(dom, {
useCORS: true, //支持图片跨域
scale: 4, //设置放大的倍数
height: dom.scrollHeight,
windowHeight: dom.scrollHeight,
// dpi: 300, //解决生产图片模糊
dpi:window.devicePixelRatio*4,
allowTaint: true,
}).then((canvas) => {
let contentWidth = canvas.width
let contentHeight = canvas.height
let pageHeight = contentWidth / 841.89 * 592.28 // 一页pdf显示html页面生成的canvas高度;
let leftHeight = contentHeight //未生成pdf的html页面高度
let position = 0 //pdf页面偏移
let imgWidth = 841.89 //宽度
let imgHeight = 841.89 / contentWidth * contentHeight
let pageData = canvas.toDataURL('image/jpeg', 1.0)
let PDF = new JsPDF('l', 'pt', 'a4')
if (leftHeight < pageHeight) {
PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight );
}else {
while(leftHeight > 0) {
PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight;
position -= 592.28;
//避免添加空白页
if(leftHeight > 0) {
PDF.addPage();
}
}
}
// 这段可忽略,因为我要将导出的pdf返回给后台,所以需要这步骤
const name = `${fileName}.pdf`
const blob = PDF.output('blob',{name}) //代码获取pdf文件
const file = new File([blob],name,{type:blob.type})
const formData = new FormData()
formData.append('file',file)
formData.append('toUser',toUser)
formData.append('title',title)
formData.append('content',content)
formData.append('fileName',fileName)
// 正常导出
PDF.save()
完整代码
setTimeout(() => {
let dom = document.getElementById("app");
// const A4_WIDTH = 592.28
// const A4_HEIGHT = 841.89
const A4_WIDTH = 841.89
const A4_HEIGHT = 592.28
// 根据A4的宽高计算DOM页面一页应该对应的高度
let pageHeight = dom.offsetWidth / A4_WIDTH * A4_HEIGHT
// 将所有不允许被截断的元素进行处理
let wholeNodes = document.querySelectorAll('.whole-node')
for (let i = 0; i < wholeNodes.length; i++) {
//1、 判断当前的不可分页元素是否在两页显示
const topPageNum = Math.ceil((wholeNodes[i].offsetTop) / pageHeight)
const bottomPageNum = Math.ceil((wholeNodes[i].offsetTop +
wholeNodes[i].offsetHeight) / pageHeight)
if (topPageNum !== bottomPageNum) {
//说明该dom会被截断
// 2、插入空白块使被截断元素下移
let divParent = wholeNodes[i].parentNode
let newBlock = document.createElement('div')
newBlock.className = 'emptyDiv'
newBlock.style.background = '#fff'
// 3、计算插入空白块的高度 可以适当流出空间使得内容太靠边,根据自己需求而定
let _H = topPageNum * pageHeight - wholeNodes[i].offsetTop
newBlock.style.height = _H + 30 + 'px'
divParent.insertBefore(newBlock, wholeNodes[i])
}
}
const { fileName,content,title,toUser } = to.query
if (dom) {
html2canvas(dom, {
useCORS: true, //支持图片跨域
scale: 4, //设置放大的倍数
height: dom.scrollHeight,
windowHeight: dom.scrollHeight,
// dpi: 300, //解决生产图片模糊
dpi:window.devicePixelRatio*4,
allowTaint: true,
}).then((canvas) => {
let contentWidth = canvas.width
let contentHeight = canvas.height
let pageHeight = contentWidth / 841.89 * 592.28 // 一页pdf显示html页面生成的canvas高度;
let leftHeight = contentHeight //未生成pdf的html页面高度
let position = 0 //pdf页面偏移
let imgWidth = 841.89 //宽度
let imgHeight = 841.89 / contentWidth * contentHeight
let pageData = canvas.toDataURL('image/jpeg', 1.0)
let PDF = new JsPDF('l', 'pt', 'a4')
if (leftHeight < pageHeight) {
PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight );
}else {
while(leftHeight > 0) {
PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight;
position -= 592.28;
//避免添加空白页
if(leftHeight > 0) {
PDF.addPage();
}
}
}
const name = `${fileName}.pdf`
const blob = PDF.output('blob',{name})
const file = new File([blob],name,{type:blob.type})
const formData = new FormData()
formData.append('file',file)
formData.append('toUser',toUser)
formData.append('title',title)
formData.append('content',content)
formData.append('fileName',fileName)
PDF.save()
request({
// url: "路径",
method: "post",
headers: {
'Content-Type':'multipart/form-data'
},
data: formData,
}
)
});
}
}, 10000); //等待页面渲染完成
问题2:导出echarts图表模糊
这个问题解决比较简单,只需在图表初始化的时候,添加一个参数即可 { devicePixelRatio: 2 }
this.myChart = echarts.init(document.getElementById(this.id),null,{ devicePixelRatio: 2 });
问题3:转成pdf后,部分字体堆叠问题
这个只需给堆叠的元素加一个样式即可 style="letterSpacing:1px" 或者给堆叠的元素加一个空格之类的,都可以
感谢各大佬的帮助!
参考:
vue将页面导出为pdf(html2Canvas) - 文字堆叠问题_ad汉化后导出pdf字母堆叠-CSDN博客
使用html2Canvas、jspdf导出pdf,含echarts图表模糊 - 知乎 (zhihu.com)
Vue导出页面为PDF格式,解决导出pdf模糊问题_vue 页面转pdf上传后端不压缩-CSDN博客