使用PDFJS-dist预览PDF

11,608 阅读2分钟

预览PDF

述语

在h5中预览PDF,最开始使用的是vue-PDF 发现在渲染PDF时 是 乱序渲染,不是从头渲染,而且 每次渲染都要读取一遍pdf流,这样如果在查看PDF 时, 假如在 PDF未渲染完某页 就返回时 会报错,导致下一次打开PDF 不会渲染出来。导致效果不好

所以 更改 PDF插件,使用PDFjs-dist,但是注意点还是要注意以上。

但是我还是觉得 将后台将pdf转换成图片的base64流,传给前台,前台直接渲染图片会更简单

基本代码

先下载pdfjs-dist

以下是预览PDF的组件,其中url可以是路径也可以是base流,

canvas 绘制

缺点:放大后会模糊

svg 绘制

canvas 按像素绘制 svg按弧度 转角...绘制

包含 canvas 绘制 和svg绘制 PDF

<template>
 <div class="view-wrapper">
    <van-nav-bar title="查看PDF" left-arrow @click-left="goHome()" fixed />
    <div class='box' style="background: #fff">
        //-------------canvas 绘制
      <div>
        <canvas v-for="page in pages" :id="'the-canvas'+page" :key="page"></canvas>
      </div>
       //----END
      //----------svg 绘制
      <div class="view" id="canvas-wrap"></div>
       //----END
    </div>
    <van-loading v-show="loadding" type="spinner" />
 </div>
</template>
<script>

import PDFJS from 'pdfjs-dist';
import workerSrc from 'pdfjs-dist/build/pdf.worker.entry';


PDFJS.workerSrc = workerSrc;

// PDFJS.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.943/build/pdf.worker.min.js';
//请注意,这里的cdn worker地址是贴的网上的,并且签章那部分代码是没有注释的,直接拿去用是达不到效果的,我也没有现成的资源可提供,只是贴一下方法

export default {
    data() {
      return {
          pdfDoc: null,
          pages: 0,
          pdfUrl:'',
          src:'',
          loadding:true,
          file:true,
          isDestory:true
      }
    },
 metaInfo: {
    meta: [
      { name: 'viewport', content: 'width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=5,user-scalable=yes' }
    ]
  },
  watch: {
  },
    methods: {
    _renderPage (num) {
      // getPage 处理每个页面
      // 返回单页内容实例(页面引索) pdf.getPage(index)
      this.pdfDoc.getPage(num).then((page) => {
        // canvas 绘制 PDF
        let canvas = document.getElementById('the-canvas' + num)
        let ctx = canvas.getContext('2d');
        ctx.mozImageSmoothingEnabled = false;
        ctx.webkitImageSmoothingEnabled = false;
        ctx.msImageSmoothingEnabled = false;
        ctx.imageSmoothingEnabled = false;
        let dpr = window.devicePixelRatio || 1
        let bsr = ctx.webkitBackingStorePixelRatio ||
                  ctx.mozBackingStorePixelRatio ||
                  ctx.msBackingStorePixelRatio ||
                  ctx.oBackingStorePixelRatio ||
                  ctx.backingStorePixelRatio || 1
        let ratio = dpr / bsr
        // 返回页面内容(比例) page.getViewport({scale:2.0})语法改这么写
        let viewport = page.getViewport({scale:screen.availWidth / page.getViewport({scale:1.0}).width});//这是让pdf文件的大小等于视口的大小
        canvas.width = viewport.width * ratio
        canvas.height = viewport.height * ratio//这里会进行压缩,解决模糊问题
        canvas.style.width = viewport.width + 'px'
        canvas.style.height = viewport.height + 'px'
        let renderContext = {
          canvasContext: ctx,
          viewport: viewport,
          transform: [ratio, 0, 0, ratio, 0, 0]//这里会进行放大,解决模糊问题
        }
        const that = this
         page.render(renderContext).promise.then(function(){
          //  防止 在 PDF未渲染完某页 就返回时 会报错,导致下一次打开PDF 不会渲染出来
           if(that.isDestory){
             console.log('xuanran')
          // page.getTextContent()
            if (that.pages > num) {
            that._renderPage(num + 1)
          }
           }
        });
         //-----------END----------

        //svg绘制 PDF
         /* svg实现方式 */
        let viewport = page.getViewport({scale: vm.scale})
        let container = document.createElement('div')
        container.id = 'canvas_' + num
        container.className = 'pageContainer'
        container.style.width = viewport.width + 'px'
        container.style.height = viewport.height + 'px'
        document.getElementById('canvas-wrap').appendChild(container)

        return page.getOperatorList().then(function(opList) {
            let svgGfx = new PDFJS.SVGGraphics(page.commonObjs, page.objs)
            return svgGfx.getSVG(opList, viewport).then(function(svg) {
                container.appendChild(svg)
            })
        })

        //-----------END----------
      })
    },
    _loadFile (url) {
      // 获取整个 文档
      PDFJS.getDocument({
        url,
        // cMapUrl: 'https://unpkg.zhimg.com/pdfjs-dist@1.9.426/cmaps/',//这里同样要引入字体解决水印问题,需自己提供
        // 注意:如果PDF的水印是收费字体,则需要 通过cMapUrl引入对应的字体,如果是免费字体,则不需要引入字体 水印会直接渲染出来
        cMapPacked: true
      }).promise.then((pdf) => {
        this.pdfDoc = pdf
        this.pages = this.pdfDoc.numPages
        this.loadding = false
        const that = this
         if(that.isDestory){
            this.$nextTick(() => {
              this._renderPage(1)
            })
         }
      },(err) => {
        if(err.name == 'MissingPDFException'){
          this.$toast('无效的PDF链接')

        }
        // reject(err);
        })
    }
},
  mounted() {
  this._loadFile('http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf');
  },
  beforeDestroy(){
      console.log('destory')
      this.isDestory = false
      this._renderPage = null

    }
}

</script>
<style lang="scss" scoped>
.view-wrapper {
  padding-top:1.22667rem;
}
.view-wrapper /deep/ .van-nav-bar .van-icon {
  color: #333;
  font-size: 18px;
  margin-right: 3px;
}
.view-wrapper /deep/ .van-loading {
    position: absolute;
    top: 50%;
    left: 46%;
}
</style>

引入pdfjs-dist 报错 总是引入不进来

const PDFJS = require('pdfjs-dist')
PDFJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/build/pdf.worker.entry')

有些字体加载不出来

如果pdf中有商用字体,对于商用字体,默认是不支持的,想要渲染出商用字体,需要引入对应的字体:一般是全部引入

cMapUrl: 'https://unpkg.zhimg.com/pdfjs-dist@1.9.426/cmaps/',
//这里同样要引入字体解决水印问题,需自己提供 
// 注意:如果PDF的水印是收费字体,则需要 通过cMapUrl引入对应的字体,如果是免费字体,则不需要引入字体 水印会直接渲染出来
cMapPacked: true

查看 签章 电子签名

在node_moduels pdfjs-dist 的 pdf.worker.js 文件中,将以下代码注释:

if (data.fieldType === 'Sig') { 
  this.setFlags(AnnotationFlag.HIDDEN);
}

缩放pdf

用的是 <meta name="viewport" content="width=device-width,initial-scale=1.0"> 在查看pdf页 设置 成可以缩放,在其他页面设置成不可以缩放

在vue中更改 html的meta 可以 通过v-meta插件 设置

metaInfo: {
//和data是平级   
meta: [     
{ name: 'viewport', content: 'width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=5,user-scalable=yes' }    
]   
},

contains 属性冲突

PDFJS 里给Aray添加了contains属性,Array.prototype.contains,所以如果在项目中同时添加contains属性会报错