最近接到一个需求,要实现将PDF合并并且实现静默打印功能。网上各种各样的方案,有后端合并、有用lodop的,不是要增加后端工作量,就是要客户端装插件,还有用pdf.js、jspdf、html2canvas的,不是不满足自己的功能需求,就是写的不全,能不能来点有用的或者简单点完整的,我就只是想要做pdf合并,然后可打印可下载,网上资料很多,好处是东西多,坏处是可能被带坑里去,所以想将我的方案,或者说我解决问题的方法记录下来,方法只是现有技术的灵活运用。
想到pdf.js可以将pdf分解成一张一张图片,那将多个pdf都分解成图片,再将图片拼接起来不就可以了吗?答案是肯定的,我们可以用pdf.js将图片分解。然后要怎么合并呢?jspdf可以将图片生成pdf,那咱们就只需要用到pdf.js和jspdf就能够合并pdf不是吗?不明白为什么pdf.js既然能够将pdf分解成图片,那为什么不能够直接将pdf合并,省去我们来合并的这一步呢?这个功能不是很正常的需求吗?有知道的小伙伴可以留言告知。
本次分享是一次曲线救国,下载完合并后的PDF文档自行打开,设置打印参数(还是有必要弹设置窗口,如果一定要静默打印,还是需要使用到lodop,静默需要收费,否则页脚有试用版水印,本次分享并不涉及)。
那现在我们来对这个功能进行分解吧,稍微懂点js的基本上都懂,这不就是分享这个功能的意义所在了吗?下面我们正式开始吧。
1、引用所需JS
<script src="~/js/pdfjs/pdf.js"></script>
<script src="~/js/pdfjs/jspdf.debug.js"></script>
2、根据项目实际功能,去加载pdf,选中加载、全选加载等等,就细说了,这些不是本次分享重点
//注意:Canvas渲染出来之前,合并pdf为空白,因此需要监听canvas是否渲染完,完成之前不允许合并
///pdfUrl:pdf地址;
///id:只是为了将canvas做个标记(一个pdf分解成多张canvas图片,canvasid="id-页码"),区分不同pdf,后面可以根据id找到对应的pdf,
function loadPDF(pdfUrl, id) {
pdfUrl && PDFJS.getDocument(pdfUrl).then(function (pdf) {
//用 promise 获取页面
var canvasid = '';
var idTemplate = 'pdf_' + id + '-';
var pageNum = pdf.numPages;
pageNum_all += pageNum;
//根据页码创建画布
createSeriesCanvas(pageNum, idTemplate);
//将pdf渲染到画布上去
for (var i = 1; i <= pageNum; i++) {
canvasid = idTemplate + i;
renderPDF(pdf, i, canvasid);
}
});
}
//创建和pdf页数等同的canvas数
function createSeriesCanvas(num, template) {
var id = '';
for (var j = 1; j <= num; j++) {
id = template + j;
createPdfContainer(id, 'pdfClass');
}
// $("#container").append("<a href='javaScript:closePDF()' class='closeP'>点击收起</a>");
}
//创建
function createPdfContainer(id, className) {
var pdfContainer = document.getElementById('pop');
var canvasNew = document.createElement('canvas');
canvasNew.id = id;
canvasNew.className = className;
pdfContainer.appendChild(canvasNew);
};
//渲染pdf
//建议给定pdf宽度
function renderPDF(pdf, i, id) {
pdf.getPage(i).then(function (page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
//
// 准备用于渲染的 canvas 元素
//
var wrapper = document.getElementById("pop");
var canvas = document.getElementById(id);
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
canvas.style.width = "100%";
canvas.style.height = "100%";
wrapper.style.width = Math.floor(viewport.width / scale) + 'pt';
wrapper.style.height = Math.floor(viewport.height / scale) + 'pt';
//
// 将 PDF 页面渲染到 canvas 上下文中
//
var renderContext = {
canvasContext: context,
viewport: viewport
};
//page.render(renderContext);
// Step 1:store a refer to the renderer
var pageRendering = page.render(renderContext);
//步骤:钩入pdf渲染完成事件
var completeCallback = pageRendering._internalRenderTask.callback;
pageRendering._internalRenderTask.callback = function (error) {
//第二步:调用完成方法之前要做的事情
completeCallback.call(this, error);
//第3步:做一些更多的东西
pageNum_compelted++;
//注意:渲染完成之后需要做的事情,渲染出来之前,合并pdf为空白
if (pageNum_all == pageNum_compelted && pageNum_all != 0) {
$("#mulitprint").find("svg").hide();
$("#mulitprint").find("i").show();
} else {
$("#mulitprint").find("svg").show();
$("#mulitprint").find("i").hide();
}
};
});
};
3、将pdf分解成canvas图片之后,就要将图片合并成一个pdf了
注意:
1>、canvas.toDataURL("image/JPEG");JPEG格式生成的pdf最小,png格式大非常非常多,此处划重点
2>、想要直接将pdf输出到iframe中,未能实现,data:application/pdf;base格式按理可以打开,在浏览器中也无法打开,知道的小伙伴留言告知,感谢!
3>、曲线救国,下载完自行打开,设置打印参数(还是有必要弹设置窗口,如果一定要静默打印,那本次分享并不满足)
function createPDF() {
if ($("#pop>canvas").length <= 0) {
Mozlite.alert("请选择打印文档!");
return;
}
if (pageNum_all != pageNum_compelted || pageNum_all == 0) {
Mozlite.alert("请等待文档合并完成!");
return;
}
var doc = new jsPDF('', 'pt', 'a4');
var pop = document.getElementById("pop");
$("#pop>canvas").each((index, canvas) => {
if (index > 0)
doc.addPage();//添加页
//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
var contentWidth = canvas.width;
var contentHeight = canvas.height;
var imgWidth = 595.28;
var imgHeight = 592.28 / contentWidth * contentHeight;
var canvas_1Url = canvas.toDataURL("image/JPEG");
doc.addImage(canvas_1Url, 'JPEG', 0, 0, imgWidth, imgHeight);
})
var outputstr = doc.output('datauristring');
console.log(outputstr);
doc.save('待打印文件.pdf');
//doc.output('datauri');
//var outputstr = doc.output('datauristring');
//console.log(outputstr);
// document.getElementById("preview").src = outputstr;//在iframe中显示
//var blob = doc.output('blob')//输出为blob
console.log(blob);
blob转url
//var urll = window.URL.createObjectURL(new File([blob], 'a_name.pdf', { type: 'application/pdf' }))
//console.log(urll);
用PDFjs打开这个url能看到pdf 可以打印 /下载等
viewer.html 时本地路径 具体看下面
//window.open('/pdf/viewer.html?file=' + encodeURIComponent(urll))
//document.getElementById("iframePrint").src = doc.output('datauristring');//在iframe中显示
}