目的:前端页面自定义元素生成pdf并提供下载
方法1:
html2canvas.min.js+jsPdf.debug.js
原理:将前端界面通过html2canvas截屏生成图片,再使用jsPdf将生成的图片存入pdf中;
//id为pdf元素容器id;
function toPdfDown(id) {
var d = new Date();
$("#"+id).css("overflow-x","visible")关闭页面滑动,防止生成的pdf不全
var element = $("#"+id); // 这个dom元素是要导出pdf的div容器
var w = element.width(); // 获得该容器的宽
var h = element.height(); // 获得该容器的高
var offsetTop = element.offset().top; // 获得该容器到文档顶部的距离
var offsetLeft = element.offset().left; // 获得该容器到文档最左的距离
var canvas = document.createElement("canvas");
var abs = 0;
var win_i = $(window).width(); // 获得当前可视窗口的宽度(不包含滚动条)
var win_o = window.innerWidth; // 获得当前窗口的宽度(包含滚动条)
if(win_o>win_i){
abs = (win_o - win_i)/2; // 获得滚动条长度的一半
}
canvas.width = w * 2; // 将画布宽&&高放大两倍
canvas.height = h * 2;
var context = canvas.getContext("2d");
context.scale(2, 2);
context.translate(-offsetLeft-abs,-offsetTop);
// 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
// translate的时候,要把这个差值去掉
html2canvas(element).then(function(canvas) {
var contentWidth = canvas.width;
var contentHeight = canvas.height;
//一页pdf显示html页面生成的canvas高度;
var pageHeight = contentWidth / 592.28 * 841.89;
//未生成pdf的html页面高度
var leftHeight = contentHeight;
//页面偏移
var position = 0;
//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
var imgWidth = 595.28;
var imgHeight = 592.28/contentWidth * contentHeight;
var pageData = canvas.toDataURL('image/jpeg', 1.0);
var pdf = new jsPDF('', 'pt', 'a4');
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
//当内容未超过pdf一页显示的范围,无需分页
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 -= 841.89;
//避免添加空白页
if(leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save('test'+d.getHours()+d.getMinutes()+d.getSeconds()+'.pdf');
$("#"+id).css("overflow-x","hidden")//开启页面滑动
})
}
缺陷:生成pdf模糊,另外最致命的是图片会被切割,这个过程可能导致我们的元素正好被切割照成pdf的阅读问题
尝试网上改良方法:
function toPdfDown(id){
if (confirm("您确认下载该PDF文件吗?")) {
$("#"+id).css("overflow-x","visible")关闭页面滑动
html2canvas($("#"+id), {
background: "#FFFFFF",//如果指定的div没有设置背景色会默认成黑色,这里是个坑
onrendered:function(canvas) {
//未生成pdf的html页面高度
var leftHeight = canvas.height;
var a4Width = 595.28
var a4Height = 841.89
//一页pdf显示html页面生成的canvas高度;
var a4HeightRef = Math.floor(canvas.width / a4Width * a4Height);
//pdf页面偏移
var position = 0;
var pageData = canvas.toDataURL('image/jpeg', 1.0);
var pdf = new jsPDF('x', 'pt', 'a4');
var index = 1,
canvas1 = document.createElement('canvas'),
height;
pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen');
var pdfName='test';
/*$('.head-content').append($(
'<div class="pdfTip">' +
' <div>正在生成第<span class="pdfProgress">1</span>页,共<span class="pdfTotal"></span>页...' +
'</div>'));*/
function createImpl(canvas) {
if (leftHeight > 0) {
index++;
var checkCount = 0;
if (leftHeight > a4HeightRef) {
var i = position + a4HeightRef;
for (i = position + a4HeightRef; i >= position; i--) {
var isWrite = true
for (var j = 0; j < canvas.width; j++) {
var c = canvas.getContext('2d').getImageData(j, i, 1, 1).data
console.log(c)
console.log("--------------------------")
if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) {
isWrite = false
break
}
}
if (isWrite) {
checkCount++
if (checkCount >= 10) {
break
}
} else {
checkCount = 0
}
}
height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef);
if(height<=0){
height = a4HeightRef;
}
} else {
height = leftHeight;
}
canvas1.width = canvas.width;
canvas1.height = height;
// console.log(index, 'height:', height, 'pos', position);
var ctx = canvas1.getContext('2d');
ctx.drawImage(canvas, 0, position, canvas.width, height, 0, 0, canvas.width, height);
var pageHeight = Math.round(a4Width / canvas.width * height);
// pdf.setPageSize(null, pageHeight)
if(position != 0){
pdf.addPage();
}
pdf.addImage(canvas1.toDataURL('image/jpeg', 1.0), 'JPEG', 0, 0, a4Width, a4Width / canvas1.width * height);
leftHeight -= height;
position += height;
$('.pdfProgress').text(index + 1);
$('.pdfTotal').text(index + Math.ceil(leftHeight / a4HeightRef));
if (leftHeight > 0) {
setTimeout(createImpl, 500, canvas);
} else {
pdf.save(pdfName + '.pdf');
$('.pdfTip').remove();
}
}
}
//当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < a4HeightRef) {
pdf.addImage(pageData, 'JPEG', 0, 0, a4Width, a4Width / canvas.width * leftHeight);
pdf.save(pdfName + '.pdf')
} else {
try {
pdf.deletePage(0);
$('.pdfTip').show();
$('.pdfTotal').text(index + Math.ceil(leftHeight / a4HeightRef));
setTimeout(createImpl, 500, canvas);
} catch (err) {
console.log(err);
}
}
}
})
}
$("#"+id).css("overflow-x","hidden")//开启页面滑动
};
该方法切割问题明显改善,但原理是计算div块高度,可以有效避免小高度块元素的切割问题,但对于太长的表格(如超一页)还是会存在切割问题,当然这个原理都清除了,哪位大佬有空完全可以去再计算一下tr的高估,但个方法资源耗费很大
直接换思路,利用浏览器自带打印功能实现pdf文件的下载
原理:通过window.print(),调用浏览器方法,具体可以去网上查询,这里列出方法打印我们想要的div
function preview (id) {
//$("html").css("height", "auto");
$("#"+id).after("<!--endprint-->")自己写到html里,另外注释掉这一句 打印结束标记
$("#"+id).before("<!--startprint-->")//自己写到html里,另外注释掉这一句 打印起始标记
let bdhtml=window.document.body.innerHTML;
let sprnstr="<!--startprint-->"; //开始打印标识字符串有17个字符
let eprnstr="<!--endprint-->"; //结束打印标识字符串
let prnhtml=bdhtml.substr(bdhtml.indexOf(sprnstr)+17); //从开始打印标识之后的内容
prnhtml=prnhtml.substring(0,prnhtml.indexOf(eprnstr)); //截取开始标识和结束标识之间的内容
window.document.body.innerHTML=prnhtml; //把需要打印的指定内容赋给body.innerHTML
window.print(); //调用浏览器的打印功能打印指定区域
window.document.body.innerHTML=bdhtml;//重新给页面内容赋值;
//$("html").css("height", "100%");
return false;
}
该方法实现本页打印;但依旧存在问题
1:对于只打印一页,仔细查查html是否设置了height:100%;如果设置了在方法里修改html的css
2:对于页面存在js动态绑定的方法,需要刷新页面否则按键会失效
最终版: 为应对第二种问题我选择在新窗口进行打印
function doPrint() {
let winname,head,bdhtml,sprnstr,eprnstr,prnhtml;
//$("html").css("height", "auto");
winname = window.open('', "_blank",'');
head ='';//注意这里!!
bdhtml=window.document.body.innerHTML;
sprnstr="<!--startprintBG-->";
eprnstr="<!--endprintBG-->";
prnhtml=bdhtml.substr(bdhtml.indexOf(sprnstr)+19);
prnhtml=prnhtml.substring(0,prnhtml.indexOf(eprnstr));
winname.document.head.innerHTML=head;
winname.document.body.innerHTML=prnhtml;
winname.print();
//$("html").css("height", "100%");
}
最终问题及解决思路:
该方法会照成打开界面时css样式的丢失,如果全写的内联那不存在这个问题,仔细检索发现是样式未传递过来(如果你的样式放在服务器上,大可在head里直接window.document.head.innerHTML);
解决:在head中写入<style> 样式.............. </style>,这些样式会被带到新界面并被调用
至于打印的一些默认样式,这个自己百度网上有
——————————————————————————————————————以上结束—————————————————————————————————————————————————