使用两个库html2Cancas和jspdf,分别用于将html元素转换为Canvas和将Canvas转为pdf文件。
html部分:
demo为要导出的部分
<div id="demo" ref="report">
//.........页面内容
</div>
js部分:
import JsPDF from "jspdf";
import html2Canvas from "html2canvas";
// 存储各个分页的留白高度
let zancunH = [];
// 判断节点是否被分割
function isSplit(nodes, index, pageHeight) {
if (nodes[index].offsetTop + nodes[index].scrollHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + nodes[index + 1].scrollHeight > pageHeight) {
return true; // 节点被分割
} else {
return false; // 节点未被分割
}
}
// 输出PDF方法
function outPutPdfFn() {
// 将页面滚动条置顶
document.body.scrollTop = 0;
document.body.scrollLeft = 0;
// A4纸张宽度和高度
const A4_WIDTH = 592.28;
const A4_HEIGHT = 841.89;
// 获取图片容器和页面高度
let imageWrapper = document.getElementById('demo');
let pageHeight = (imageWrapper.scrollWidth / A4_WIDTH) * A4_HEIGHT;
// 获取所有需要分割的节点
let lableListID = imageWrapper.querySelectorAll('.sign');
// 初始化分页留白数组
zancunH[0] = 0;
// 遍历节点,判断是否需要分割并添加留白节点
for (let i = 0; i < lableListID.length; i++) {
let multiple = Math.ceil((lableListID[i].offsetTop + lableListID[i].scrollHeight) / pageHeight);
if (isSplit(lableListID, i, multiple * pageHeight)) {
let divParent = lableListID[i].parentNode; // 获取节点的父节点
let newNode = document.createElement('div');
newNode.className = 'emptyDiv';
newNode.style.background = '#ffffff';
let _H = multiple * pageHeight - (lableListID[i].offsetTop + lableListID[i].scrollHeight);
zancunH[multiple] = _H;
newNode.style.height = _H + 'px'; // 设置留白节点高度
newNode.style.width = '100%';
let next = lableListID[i].nextSibling; // 获取节点的下一个兄弟节点
if (next) {
divParent.insertBefore(newNode, next); // 将留白节点插入到下一个兄弟节点之前
} else {
divParent.appendChild(newNode); // 将留白节点添加到最后
}
}
}
// 使用html2Canvas进行截图
html2Canvas(imageWrapper, { // 使用html2Canvas进行截图
allowTaint: true, // 允许跨域图片渲染
x: imageWrapper.getBoundingClientRect().left + 13, // 绘制的 dom 元素相对于视口的左边距加上偏移量
y: imageWrapper.getBoundingClientRect().top, // 绘制的 dom 元素相对于视口的上边距
width: imageWrapper.offsetWidth - 15, // 绘制的 dom 元素的宽度减去15像素
height: imageWrapper.offsetHeight, // 绘制的 dom 元素的高度
backgroundColor: '#FFF', // 绘制的背景颜色设置为白色
useCORS: true, // 使用跨域资源
scale: 3, // 图片放大倍数,提高清晰度
dpi: 350, // 设置图片的 dpi(每英寸像素数)
})
.then((canvas) => {
// 使用jsPDF生成PDF
let pdf = new JsPDF('p', 'mm', 'a4'); // 创建A4大小的PDF
let ctx = canvas.getContext('2d');
let a4w = 190;
let a4h = 270; // A4 大小,210mm x 297mm,四边各保留 10mm 的边距,显示区域 190x277,
let imgHeight = Math.floor((a4h * canvas.width) / a4w); // 按 A4显示比例换算一页图像的像素高度
let renderedHeight = 0;
// 按照A4大小进行分页渲染
while (renderedHeight < canvas.height) {
let page = document.createElement('canvas');
page.width = canvas.width;
page.height = Math.min(imgHeight, canvas.height - renderedHeight); // 可能内容不足一页
// 用 getImageData 剪裁指定区域,并画到前面创建的 canvas 对象中
page
.getContext('2d')
.putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0);
pdf.addImage(page.toDataURL('image/jpeg', 0.2), 'JPEG', 10, 10, a4w, Math.min(a4h, (a4w * page.height) / page.width)); // 添加图像到页面,保留 10mm 边距
renderedHeight += imgHeight;
if (renderedHeight < canvas.height) pdf.addPage(); // 如果后面还有内容,添加一个空页
}
pdf.save('paperDownName'); // 下载PDF文件
})
.then(() => {
// 下载成功提示
ElMessage({
message: '文件下载成功',
type: 'success',
});
})
.catch((error) => {
// 下载失败提示
console.error('PDF 文件保存失败:', error);
ElMessage.error('文件下载失败');
});
}
在进行截图时可能会出现被内容被截断的情况,通过上方的插入空白元素的方法还是会存在文字截断的问题。
通过对比pdf中每页的内容以及页面中空白元素插入的位置,发现pdf分页的位置并不是空白元素插入的位置,而是比空白元素的插入的位置再要往下一点,此时可减小在创建JsPDF时所设置的A4大小的高度,或加大初始时的pageHeight,减小两者之间的差距,从而使得pdf的截断位置和插入空白元素的位置相同。这也是在代码中a4的值(270)与注释中的值(277)不一样的原因。