vue-pdf预览无法显示后端定义的字体问题解决

pdf预览只显示一页问题

  • 这个网上有很多,vue-pdfREADME也有示例,这个是整个问题的起因,简单讲讲
<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, // pdf文件总页数
      pdfSrc: null,
    };
  },
  methods: {
  	...
    getPageNum() {
      try {
        new URL(this.filePath); // 校验是否是真的URL
        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-distpackage.json里面却没引入
  • ????,你这是没改全就发上来还是啥情况,难道还要有手动再装个pdfjs-dist(⚪出)!!!
import { CMapCompressionType } from 'pdfjs-dist-sign/es5/build/pdf.js'

export default function() {

	this.fetch = 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,
			};
		});
	}
};

我的方法:重构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 {
          // 重建一个Uint8Array,修复第二次打开不能显示后端动态添加文字的问题
          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, // pdf文件总页数
      pdfSrc: null,
    };
  },
  methods: {
  	...
    getPageNum() {
      try {
        new URL(this.filePath); // 校验是否是真的URL
        this.pdfSrc = pdf.createLoadingTask({
          url: this.filePath,
		  CMapReaderFactory() {
		    const CMapReader = new CMapReaderFactory();
		    this.fetch = function (query) {
		      return CMapReader.fetch(query).then((data) => {
		        return {
		          // 重建一个Uint8Array,修复第二次打开不能显示后端动态添加文字的问题
		          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;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值