最近做移动端项目,封装PDF预览组件,同事开始用的是vue-pdf来做的预览,连连踩坑,基本遇到3个问题:
1、PDF中文不显示
2、PDF签章没显示出来,控制台提示:Warning: Unimplemented widget field type "Sig", falling back to base field type
3、第二次打开PDF的时候会遇到PDF空白的问题,控制台提示:Error during font loading: Failed to execute 'postMessage' on 'Worker': ArrayBuffer at index 0 is already detached
对于问题1,大家的做法都一样,通过引入CMapReaderFactory.js解决问题:
import CMapReaderFactory from 'vue-pdf/src/CMapReaderFactory.js'
this.src = pdf.createLoadingTask({
url: this.pdfUrl,
CMapReaderFactory
});
引入CMapReaderFactory.js确实解决了问题1,但是这也会引起问题3的出现,这个我们聊完问题二再说。
对于问题二,网上也有很多解决办法,vue-pdf实际上也是集成了pdfJS的实现,一般通过注释pdfJS中的代码,开启签章即可解决问题,参考http://www.zyiz.net/tech/detail-129606.html:
//pdf.worker.js
if (data.fieldType === 'Sig') {
data.fieldValue = null;
//注释此行代码即可
//_this3.setFlags(_util.AnnotationFlag.HIDDEN);
}
对于问题三,前面提到是由于 引入了CMapReaderFactory导致的,你代码断点进去就会发现他的代码是这样写的:
import { CMapCompressionType } from 'pdfjs-dist/build/pdf.js'
// see https://github.com/mozilla/pdf.js/blob/628e70fbb5dea3b9066aa5c34cca70aaafef8db2/src/display/dom_utils.js#L64
export default function() { this.fetch = async function(query) {
/* webpackChunkName: "noprefetch-[request]" */
return import('./buffer-loader!pdfjs-dist/cmaps/'+query.name+'.bcmap')
.then(function(bcmap) {
return {
cMapData: bcmap.default,
compressionType: CMapCompressionType.BINARY,
};
});
}
};
这段代码通过动态import PDF的语言文件实现解决中文显示的问题,但是第二次加载PDF的时候你会发现加载的页面是空白页,我调试了一天,才发现这坑爹玩意在你第一次加载PDF的时候,bcmap的返回值是Uint8Array[43366]的数组对象,而第二次预览PDF加载时发现bcmap的返回值是Uint8Array []的空数组,断点进去看了一下也没看的明了,但是大概能猜到是因为缓存问题,第二次加载时取的是初次加载PDF文件时的语言文件的loadModules的缓存,但是取的过程中导致了失败,返回了空值,那解决问题其实就是一行代码的事:
import { CMapCompressionType } from 'pdfjs-dist-sign/build/pdf.js'
// see https://github.com/mozilla/pdf.js/blob/628e70fbb5dea3b9066aa5c34cca70aaafef8db2/src/display/dom_utils.js#L64
export default function() { this.fetch = async function(query) {
/* webpackChunkName: "noprefetch-[request]" */
return import('./buffer-loader!pdfjs-dist-sign/cmaps/'+query.name+'.bcmap').then(function(bcmap) {
//加载完语言文件后清除缓存
delete require.cache[require.resolve('./buffer-loader!pdfjs-dist-sign/cmaps/'+query.name+'.bcmap')];
return {
cMapData: bcmap.default,
compressionType: CMapCompressionType.BINARY,
};
});
}
};
通过删除require的cache即可多次加载预览PDF了,但是这是别人插件的源码,你本地改了,其他小伙伴们不知道呀,那要怎么办呢?我这边的处理方案是在我封装的PDF组件增加清除缓存的代码:
that.src = pdf.createLoadingTask({
url: this.pdfUrl,
CMapReaderFactory
});
//加载完PDF后对缓存进行清除
for(var key in require.cache) {
if(key.indexOf('bcmap') >= 0) {
delete require.cache[key];
}
}
上面这种方式在本地试了确实是可行的,但是放到服务器部署上去发现行不通,调试了一下发现是因为webpack打包的时候把文件名压缩成别名,导致根据文件名删除require的缓存行不通,没办法只能老老实实去vue-pdf的github那里fork了一个分支过来,再clone下来本地调整,再发布到npm去,你如果懒的话可以直接使用vue-pdf-signature这个包替换掉vue-pdf,这个是我改过的vue-pdf的版本,如果想自己动手的话,把里面对pdf-dist的引用改成对pdf-dist-sign的引用,再把清理缓存的代码加进去再发个npm包即可。
//替换vue-pdf
npm install --save vue-pdf-signature