pc项目中有下载pdf的需求,内容是动态变化的,要求分页,加水印效果,各方面综合考虑以后要求前端实现
技术要点:jspdf+html2canvas
**原理:**利用html2canvas将html转成canvas,再利用canvas.toDataURL生成网路图片,然后用jspdf.addImage将图片画到pdf中
**难点:**图片的高度>一页pdf高度时,利用图片的偏移量来实现分页效果,详见代码
下面分别描述一下水印、分页等是如何实现的
水印效果
- 本地png格式的图片在线转成base64格式
- doc.addImage(‘图片地址’,‘图片格式’,x,y,‘图片宽度’,‘图片高度’)
分页效果
- html2canvas将html转成canvas(放大两倍,保证以后追加图片的清晰度)
- canvas.toDataURL(‘image/png’, 1.0)
- doc.addImage(‘图片地址’,‘图片格式’,x,y,‘图片宽度’,‘图片高度’)
- 每个页面添加图片的时候,其实用的是一张图,不同的是图片距离pdf顶部的距离,也就是图片的偏移来实现
pdf左右间距和上下间距的实现(图片高度>pdf每页高度)
- 左右间距直接在html中设置margin:0 100px;
- 上下间距,通过在每页pdf中添加白底的矩形遮盖多余内容的显示
文字链接
- 算好最后一页中剩余高度可容纳文字链接的个数、一整页pdf可容纳文字链接的个数,每一个文字链接距离顶部的位置
- 循环追加文字链接doc.textWithLink(‘超链接文本’,x,y,{url:‘超链接地址’})
问题
- 添加jpeg格式的图片是黑底的;解决办法:所有图片格式转成png
- 利用图片偏移实现分页,有时候会出现文字被裁切的问题;目前没有好的解决办法,是通过手动微调文字间距和偏移量实现的
- 文字链接乱码;解决办法:doc.setFont(‘simhei’),jspdf只支持英文字体,需要手动设置中文字体
- canvas默认白底,会覆盖水印;解决办法:html2canvas(‘html’,{scale:2,backgroundColor:‘rgba(255,255,255,0)’})
完整代码
function downPdf(html){
//初始化jspdf实例
const doc=new jsPDF()//a4 [552.28,841.89]
doc.setFont('simhei')//设置中文字体,避免乱码
//每页pdf宽高
let pageWidth = doc.internal.pageSize.width
let pageHeight = doc.internal.pageSize.height
//html容器的宽高
let contentWidth = html.clientWidth
let contentHeight = html.clientHeight
//图片的宽高
let imgWidth=pageWidth
let imgHeight=imgWidth*contentHeight/contentWidth
//html-canvas-img
let canvas=await html2canvas(html,{scale:2,backgroundColor:null})//设置null,背景是透明的,会跟水印有叠加的效果
let pageData=canvas.toDataURL('image/png', 1.0)//1.0保证图片的清晰度
let position=10
if(imgHeight<pageHeight){
doc.addImage(pageData, 'PNG', 0, 10, imgWidth, imgHeight)
}else{
let height=imgHeight//剩余图片高度
while(height>0){
//设置每页的上下间距
doc.setFillColor(255,255,255);
doc.rect(0,0,pageWidth,10,'F')//头部白条
doc.rect(0,pageHeight-10,pageWidth,10,'F')//尾部白条
//画图
doc.addImage(pageData, 'PNG', 0, position, imgWidth, imgHeight)
height=height-(pageHeight-20)
position=position-(pageHeight-20)
if(height>0){//剩余图片高度大于每页pdf高度时,添加新页面(并添加水印)
doc.addPage()
this.addWaterMark(doc)
}else{
//每页26条链接
doc.setTextColor('#E02020')
let pos=10+(height+(pageHeight-20))+1//第一个文字链接距离顶部的距离(10头部白条高度,1距离图片下边缘的距离)
//计算剩余高度可容纳文字链接的个数
let sheng=pageHeight-pos
let num=Math.floor(sheng/14)
//开始画超链接
let newPos=15//新页面中第一个文字链接的位置
let currentPge=1
//循环文字类链接列表
txts.forEach((item,index) => {
if(index<num){
doc.textWithLink((index+1)+'、'+item.name,15,pos,{url:item.url})
pos+=10
}else if(index%26===num){
currentPge++
doc.addPage()
this.addWaterMark(doc)
doc.textWithLink('txt',15,newPos,{url:''})
newPos=25
}
else{
doc.textWithLink('txt',15,newPos,{url:''})
newPos+=10
}
})
//保存下载
doc.save('test.pdf')
}
}
}
}