背景
我司有一个需求是要预览一个 PDF 文件合同,且要在滚动在底部后方可点击按钮进行下一步的操作。
最开始我的设想是潜逃一个<iframe>
标签,给<iframe>
指定 src 属性为我们的 PDF 文件路径。嗯。。。一切都是美好的开始,这个功能虽然能实现 PDF 文件的预览,可是我死活无法监听<iframe>
的滚动事件!!!
当然,这不存在跨域的问题,我也确确实实的拿到了<iframe>
的contentDocument
属性,我猜测是因为浏览器有一套自己的 PDF 文件预览机制?
最终,无奈转换思路,决定使用 vue-pdf 插件来实现 PDF 的预览,然后监听滚动条。
vue-pdf 的基本使用
vue-pdf 地址:
GitHub - FranckFreiburger/vue-pdf: vue.js pdf viewer
我安装的版本为:
"vue-pdf": "^4.3.0"
然后在组件内进行使用:
<template>
<div ref="scrollRef" class="pdf-view-scrool">
<pdf v-for="item in numPages" :key="item" :src="docUrl" :page="item" />
</div>
<template>
import pdf from 'vue-pdf';
export default {
components: {
},
data(){
return {
docUrl: "",
numPages: 0
}
},
methods: {
async getPDFUrl(){
let url = await axios.get('xxxx');
// 解析 PDF
const taskData = pdf.createLoadingTask(url);
// 把解析后的对象进行赋值
this.docUrl = taskData
// 拿到 PDF 的页数
taskData.promise.then((res) => {
this.numPages = res.numPages
})
}
}
}
无法显示中文内容
然后,就看到这么一段报错:
Warning: Error during font loading: The CMap “baseUrl” parameter must be specified, ensure that the “cMapUrl” and “cMapPacked” API parameters are provided.
出现这个问题,通常是因为无法解析中文字导致的,Google 了一圈明白了大致的意思是没有找到对应的字体文件。
网上提供的解决方案基本都是指定一个 CDN 的字体文件地址:
methods: {
async getPDFUrl(){
let url = await axios.get('xxxx');
// 解析 PDF
const taskData = pdf.createLoadingTask({
url: url,
cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.5.207/cmaps/",
cMapPacked: true
});
// 把解析后的地址进行赋值
this.docUrl = taskData
// 拿到 PDF 的页数
taskData.promise.then((res) => {
this.numPages = res.numPages
})
}
}
到这里,问题解决!
无法显示电子签章
很快,我在控制台有看到一个新的报错:
Warning: Unimplemented widget field type “Sig”, falling back to base field type.
意思是不能显示签名,然后我去查看合同确实发现没有红章:
带着这个问题,去搜索就会发现 vue-pdf 是依赖于 pdfjs-dist 库的,然后你就会在 node_modules/pdfjs-dist/es5/build/pdf.worker.js 发现有这么一段代码:
if (data.fieldType === 'Sig') {
data.fieldValue = null;
_this3.setFlags(_util.AnnotationFlag.HIDDEN);
}
网上基本上都是说,只要把这行注释掉就可以显示红章了,事实上我确实也这么做了,结果也确实如此:
if (data.fieldType === 'Sig') {
data.fieldValue = null;
// _this3.setFlags(_util.AnnotationFlag.HIDDEN);
}
然后去看 mozillia/pdfjs的 issue,也说了出于一些原因将签章功能屏蔽了,所以我的方法最终还是注释代码。
可是,就算我本地把 node_modules 立马的源码注释掉了,当其他人设备或者服务器设备问题依然存在,这个时候就要使用 patch-package 工具来对第三方的库进行打补丁啦~
patch-package 打补丁
首先,你要简单的知道 patch-package 是 npm 的一个钩子,在依赖包被 install 之后执行!
具体详见:
GitHub - ds300/patch-package: Fix broken node modules instantly 🏃🏽♀️💨
1、安装打补丁工具:
$ yarn add patch-package -S
我安装的版本是:
"patch-package": "^7.0.0"
2、生成补丁文件:
$ yarn patch-package pdfjs-dist
如果你是第一次执行这个命令,那么你的工程目录下会创建一个 patches 的文件夹:
3、配置补丁命令 package.json 的 scripts:
"scripts": {
// 其他的命令
"postinstall": "patch-package"
}
又一次完美解决,最后 build 编译上测试!
等等,这个更改后的文件怎么跑到了外部?
这怎么忍的了?
更改 worker-loader 的打包位置
原来 worker-loader 的默认打包路径是在 dist 根目录下,为了统一我们想把它打包到 dist/static/js 目录下,我们依旧通过更改源码的方式是设置路径。
找到 node_modules/worker-loader/dist/index.js 文件,然后把:
const filename = _loaderUtils2.default.interpolateName(this, options.name || '[hash].worker.js', {
context: options.context || this.rootContext || this.options.context,
regExp: options.regExp
});
更改为我们想要的路径:
const filename = _loaderUtils2.default.interpolateName(this, options.name || 'static/js/[hash].worker.js', {
context: options.context || this.rootContext || this.options.context,
regExp: options.regExp
});
最后,再运行 patch-package 生成一个补丁文件:
$ yarn patch-package worker-loader
接着再进行打包编译就会发现文件被移动到 dist/static/js 目录内了:
完美依旧!!!
感谢
移动端pdf预览-水印&电子签章问题 - 掘金
Warning: Unimplemented widget field type “Sig“, falling back to base field type._木木夕zzZ的博客-CSDN博客
Vue中 引入使用 patch-package 为依赖打补丁 (以修改 vue-pdf 打包后 [hash].worker.js 路径问题为例)vue 补丁明天也要努力的博客-CSDN博客