需求
有一个pdf模板文档,需要根据客户输入生成协议,并手写签字
项目使用框架
vue-cli, vue:^2.5.10
实现签名使用框架
"pdfjs-dist": "^2.5.2076" // 用于将pdf渲染成canvas
"pdf-lib": "^1.17.1" // 用于给pdf打水印,手写签名后将签名想水印一样印在pdf上
实现
pdf渲染成canvas
<div v-for="(item, i) in imgFiles" :key="i" class="pdf-page">
<div class="canvas-page">
<canvas :id="`pdf_canvas_${item}`" style="border: 1px solid #eeeeee;"></canvas>
<canvas class="sign_canva" :id="`sign_canva_${item}`" style="border: 1px solid #eeeeee;" @mousedown="e => mousedown(e, i)"
@mousemove="e => mousemove(e, i)"
@mouseup="mouseup"></canvas>
</div>
</div>
import * as pdfjs from 'pdfjs-dist'
import * as pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry'
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker
//...
data () {
return {
basePdf: 'https://xxxx.pdf' //pdf模版
}
},
methods: {
// 请求pdf并转换成base64
init() {
const self = this
const request = new XMLHttpRequest();
request.open('GET', this.basePdf, true);
request.responseType = 'arraybuffer';
request.onreadystatechange = async function () {
if (request.readyState === 4 && request.status === 200) {
// 转换成base64
self.base64 = self.translateArrayBufferToBase64(request.response);
// base64渲染
await self.render(self.base64)
}
};
request.send()
},
// arraybuffer转base64
translateArrayBufferToBase64(buffer){
let binaryStr = "";
const bytes = new Uint8Array(buffer);
for(let i=0,len = bytes.byteLength;i<len;i++){
binaryStr +=String.fromCharCode(bytes [i]);
}
// 注意
return 'data:application/pdf;base64,' + window.btoa(binaryStr );
},
// 渲染pdf
render(base64) {
let that = this;
const loadingTask = pdfjs.getDocument(base64)
loadingTask.promise.then((pdf) => {
var pageNum = pdf.numPages;
//准备图片
for (let i = 1; i <= pageNum; i++) {
let one = i;
that.imgFiles.push(one);
}
//处理
for (let i = 1; i <= pageNum; i++) {
pdf.getPage(i).then((page) => {
const viewport = page.getViewport({ scale: 4 })
// 初始化渲染pdf的canvas
const canvas = that.initCanvas('pdf_canvas_' + i, viewport);
// 初始化用于签名的canvas
that.signCanvass.push(that.initCanvas('sign_canva_' + i, viewport));
const ctx = canvas.getContext('2d')
page.render({
canvasContext: ctx,
viewport,
})
})
}
})
},
// 初始化canvas
initCanvas(idName, viewport) {
const canvas = document.getElementById(idName);
canvas.height = viewport.height;
canvas.width = viewport.width;
this.scale = viewport.width / 1000;
const destWidth = 1000;
canvas.style.width = destWidth + 'px';
canvas.style.height = destWidth * (viewport.height / viewport.width) + 'px';
return canvas
},
}
- 先请求pdf,转成base64
- 将base64渲染在canvas上,同时在pdf的canvas上覆盖一个一样大小canvas用于签名
将签名印到pdf上(pdf-lib)
import { PDFDocument } from 'pdf-lib';
async savePDF(){
const self = this
const pdfDoc = await PDFDocument.load(this.base64); // pdf的base64
const pages = pdfDoc.getPages();
for (let i = 0; i < pages.length; i++) {
//对应下标的 签名画布中的内容生成 png图片
const eleImgCover = await pdfDoc.embedPng(this.signCanvass[i].toDataURL('image/PNG'));
//页面中添加水印
pages[i].drawImage(eleImgCover, {
x: 0,
y: 0,
width: Math.round(1092.82 / 1.79),
height: Math.round(1414.24 / 1.79), //这里要进行缩放,因为之前的画布我们是放大过的
});
}
// 生成blob流
const pdfBytes = await pdfDoc.save();
// 这里将blob流转换成file文件,上传oss.也可以将blob转ArrayBuffer,再转buffer传oss
const file = this.BlobToFile(new Blob([pdfBytes]))
},
// blob流转file,
BlobToFile(blob) {
let filename = '协议.pdf'
var file = new File([blob], filename, {
type: 'application/pdf',
lastModified: Date.now()
});
return file;
},
- 签名好后,将签名的canvas转成png,然后覆在pdf上
- pdfDoc.save()将打好水印的pdf生成流
- 最后将流转成文件,可以上传oss或者其他地方,得到签好名的pdf
遇到的坑
-
在国外镜像下一切正常,部署到服务器之后编译出错,
大概意思是pdfjs-dist里用了高级语法,编译的时候没有转成es5
解决:用 babel-loader将高级语法转成es5
vue.config.js里面添加代码
chainWebpack: (config) => {
config.module
.rule('pdfjs-dist')
.test({
test: /.js$/,
include: path.join(__dirname, 'node_modules/pdfjs-dist')
})
.use('babel-loader')
.loader('babel-loader')
},
-
可以运行但是页面报错(..).split is not xxx
解决:降级pdfjs-dist版本为2.14.305