vue-pdf预览无法显示后端定义的字体问题解决(直接使用vue-pdf,传参即可,不需要复杂操作)
pdf预览只显示一页问题
这个网上有很多,vue-pdf
的README
也有示例,这个是整个问题的起因,简单讲讲
< pdf
ref= "pdf"
v- for = "item in numPages"
: key= "item"
: page= "item"
: src= "pdfSrc"
class = "page"
> < / pdf>
< script>
import pdf from "vue-pdf" ;
export default {
...
props : {
filePath : {
type : String,
default : "" ,
} ,
} ,
watch : {
filePath ( ) {
this . getPageNum ( ) ;
} ,
} ,
data ( ) {
return {
numPages : 1 ,
pdfSrc : null ,
} ;
} ,
methods : {
...
getPageNum ( ) {
try {
new URL ( this . filePath) ;
this . pdfSrc = pdf. createLoadingTask ( { url : this . filePath } ) ;
this . pdfSrc. promise. then ( ( pdf ) => {
this . numPages = pdf. numPages;
} ) ;
} catch ( err) {
this . $Message. error ( ` pdf加载失败: ${ err. message || err} ` ) ;
}
} ,
} ,
} ;
< / script>
. page {
border- bottom: 1px solid #dbdfe9;
}
后端动态定义的字体无法显示问题
在上面使用createLoadingTask
方法后,就会出现后端动态定义的字体无法显示的问题,这个vue-pdf
也有提供解决方法
import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js" ;
this . pdfSrc = pdf. createLoadingTask ( {
url : this . filePath,
CMapReaderFactory,
} ) ;
但是这个方法有问题,只有第一次打开的时候才能正常显示,第二次打开又是空白,且控制台报错Error during font loading: Failed to execute 'postMessage' on 'Worker': ArrayBuffer at index 0 is already detached
去github上看了有这个issue
第一次打开可以显示,第二次打开又是空白问题
方法一:使用线上的cmap
this . pdfSrc = pdf. createLoadingTask ( {
url : this . filePath,
cMapUrl : 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.5.207/cmaps/' ,
cMapPacked : true
} )
方法二:把cmap放本地
这个本地是打包后的地方,比如vue2的public目录,通过/cmaps/
访问,他需要什么字体就自己去请求,能访问的环境就可以 问题:又要新增一大堆字体文件(强迫症表示不舒服。。)
this . pdfSrc = pdf. createLoadingTask ( {
url : this . filePath,
cMapUrl : '/cmaps/' ,
cMapPacked : true
} )
方法三:清除字体文件的缓存
首先,出现这个问题的原因其实是CMapReaderFactory 对象动态import语言文件的时候缓存了文件,导致第二次取缓存加载时出了问题,把CMapReaderFactory.js的代码改成下面的就行了 但是 ,这个是依赖包,不可能每个人拉代码都要自己手动改吧,这是不可能的,于是有人提供了vue-pdf-signature
,于是我开心的删掉vue-pdf
去下载vue-pdf-signature
,运行之后,又会出现一个问题:缺少依赖包pdfjs-dist ,翻了下它的源码/vue-pdf-signature/src/pdfjsWrapper.js
里面使用了pdfjs-dist
,package.json
里面却没引入???? ,你这是没改全就发上来还是啥情况,难道还要有手动再装个pdfjs-dist
(⚪出)!!!
import { CMapCompressionType } from 'pdfjs-dist-sign/es5/build/pdf.js'
export default function ( ) {
this . fetch = function ( query ) {
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 ,
} ;
} ) ;
}
} ;
我的方法:重构CMapReaderFactory
import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js" ;
this . pdfSrc = pdf. createLoadingTask ( {
url : this . filePath,
CMapReaderFactory ( ) {
const CMapReader = new CMapReaderFactory ( ) ;
this . fetch = function ( query ) {
return CMapReader. fetch ( query) . then ( ( data ) => {
return {
cMapData : new Uint8Array ( data. cMapData) ,
compressionType : data. compressionType,
} ;
} ) ;
} ;
} ,
} ) ;
原理:web worker
的多线程发送信息postMessage
在发送二进制数据Uint8Array
时,采用的是把内存访问权限给子线程,导致第二次fetch
读取缓存时,返回的是空,所以只要重建一个Uint8Array
给子线程使用即可 有兴趣了解的可以看下其他大佬写的:web worker的通信消耗与方式
结论
< pdf
ref= "pdf"
v- for = "item in numPages"
: key= "item"
: page= "item"
: src= "pdfSrc"
class = "page"
> < / pdf>
< script>
import pdf from "vue-pdf" ;
export default {
...
props : {
filePath : {
type : String,
default : "" ,
} ,
} ,
watch : {
filePath ( ) {
this . getPageNum ( ) ;
} ,
} ,
data ( ) {
return {
numPages : 1 ,
pdfSrc : null ,
} ;
} ,
methods : {
...
getPageNum ( ) {
try {
new URL ( this . filePath) ;
this . pdfSrc = pdf. createLoadingTask ( {
url : this . filePath,
CMapReaderFactory ( ) {
const CMapReader = new CMapReaderFactory ( ) ;
this . fetch = function ( query ) {
return CMapReader. fetch ( query) . then ( ( data ) => {
return {
cMapData : new Uint8Array ( data. cMapData) ,
compressionType : data. compressionType,
} ;
} ) ;
} ;
} ,
} ) ;
this . pdfSrc. promise. then ( ( pdf ) => {
this . numPages = pdf. numPages;
} ) ;
} catch ( err) {
this . $Message. error ( ` pdf加载失败: ${ err. message || err} ` ) ;
}
} ,
} ,
} ;
< / script>
. page {
border- bottom: 1px solid #dbdfe9;
}