nuxt uses pdfjs dist plug-in to realize pdf preview

Keywords: Vue nuxt

First of all, if the version of pdfjs dist is not installed correctly, there will be all kinds of bug s!!!

At present, the versions of pdfjs dist and nuxt in my project:

"nuxt": "^2.15.8",
"pdfjs-dist": "2.3.200",

1, Project background

Because the contract signing function in the company's project needs to involve the user preview contract function, my pain comes o(╥﹏╥) O

After the initial discussion of this function, the back-end returns the pdf link. The front-end students realize the effect of pdf Online preview. The effect diagram is as follows:

 

  2, Function realization

At first, after searching on the Internet, I decided to use Vue pdf plug-in to realize the function of pdf preview. I realized the function in local development and released it to the test environment.

The most worrying thing happened. After being released to the formal environment, the whole project reported errors. I really vomited blood....

Uncaught DOMException: Failed to construct 'Worker': 
Script at 'http://a.com/ef3086d432cfbec65966.worker.js' 
cannot be accessed from origin 'http:/b.com'.

After some searching, we finally found the specific reason. It is because Vue pdf uses worker in order to speed up the rendering of pdf, because the static files of our project are placed on qiniu in the formal environment. However, worker cannot cross domain, and cross domain problems will occur in the scene where pages are separated from static resources-- Solve the problem of cross domain error reporting after Vue PDF packaging

3, Solution

node_ In modules / Vue PDF / SRC / vuepdfnosss.vue, there is a judgment supported by workers. In general Vue projects, you can directly comment it out and repackage it.

// notes if Two sentences of code or comments in the whole if sentence
if ( typeof window !== 'undefined' && 'Worker' in window && navigator.appVersion.indexOf('MSIE 10') === -1 ) {
  // var PdfjsWorker = require('worker-loader!pdfjs-dist/build/pdf.worker.js');
  // PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker();
}

Comment out the above code, do not go to the web worker, and the page can run normally; In contrast, the page loading time may be longer and the performance may be reduced;

Note: because my project uses automated deployment, I will re npm i pull new packages every time I deploy, so I can't modify Vue PDF files locally

For the above reasons, I can only replace the plug-in and find the appropriate plug-in again. After some searching, it is replaced with pdfjs dist plug-in. The specific implementation is as follows:

<template>
  <div class="pdf-preview-container">
    <div
      v-for="page in docPages"
      :key="page"
      ref="container"
      class="page-container"
      :style="{
        height: `${pageHeight}px`,
      }"
    >
      <canvas v-if="renderList.includes(page)"> </canvas>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    url: {
      type: String,
      required: true,
    },
    renderPages: {
      type: Number,
      default: 5,
    },
    customScroll: {
      type: Boolean,
      default: false,
    },
    offsetHeight: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      doc: null,
      docPages: 0,
      currentPage: 0,
      pageHeight: 0,
      renderList: [],
    }
  },
  watch: {
    url: {
      immediate: true,
      handler() {
        this.getPDFFile()
      },
    },
  },
  mounted() {
    if (!this.customScroll) {
      document.addEventListener('scroll', this.scroll)
    }
  },
  beforeDestroy() {
    document.removeEventListener('scroll', this.scroll)
  },
  methods: {
    getPDFFile() {
      if (!this.url) return
      this.currentPage = 0
      const pdfJS = require('pdfjs-dist/build/pdf')
      const pdfjsWorker = require('pdfjs-dist/build/pdf.worker.entry')
      pdfJS.GlobalWorkerOptions.workerSrc = pdfjsWorker
      pdfJS.getDocument(this.url).then(pdf => {
        // console.log('pdf: ', pdf)
        this.doc = pdf
        this.docPages = pdf._pdfInfo.numPages
        this.$nextTick(() => {
          this.docPages && this.scrollToPage(1)
        })
      })
    },
    scrollToPage(pageNo) {
      if (this.currentPage === pageNo) return
      this.currentPage = pageNo
      let list = []
      for (
        let page = pageNo - this.renderPages;
        page <= pageNo + this.renderPages;
        page++
      ) {
        list.push(page)
      }
      list = list.filter(page => page <= this.docPages && page >= 1)
      this.$nextTick(() => {
        this.renderList = list
        this.renderList.forEach(page => {
          this.renderPage(page)
        })
      })
    },
    // Render page
    renderPage(pageNo) {
      this.doc.getPage(pageNo).then(page => {
        // console.log('page: ', page)
        const container = this.$refs.container[pageNo - 1]
        if (!container) return
        const canvas = container.querySelector('canvas')
        if (!canvas || canvas.__rendered) return
        const ctx = canvas.getContext('2d')
        const dpr = window.devicePixelRatio || 1
        const bsr =
          ctx.webkitBackingStorePixelRatio ||
          ctx.mozBackingStorePixelRatio ||
          ctx.msBackingStorePixelRatio ||
          ctx.oBackingStorePixelRatio ||
          ctx.backingStorePixelRatio ||
          1
        const ratio = dpr / bsr
        const rect = container.getBoundingClientRect()
        const viewport = page.getViewport(1)
        const width = rect.width
        const height = (width / viewport.width) * viewport.height
        canvas.style.width = `${width}px`
        canvas.style.height = `${height}px`
        this.pageHeight = height
        canvas.height = height * ratio
        canvas.width = width * ratio
        ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
        page.render({
          canvasContext: ctx,
          viewport: page.getViewport(width / viewport.width),
        })
        canvas.__rendered = true
      })
    },
    scroll() {
      this.checkRender(document.documentElement)
    },
    checkRender(el) {
      if (!this.pageHeight) return
      let scrollTop = el.scrollTop
      if (el === document.documentElement) {
        scrollTop =
          el.scrollTop || window.pageYOffset || document.body.scrollTop
      }
      let page = Math.floor((scrollTop - this.offsetHeight) / this.pageHeight)
      page = Math.max(page, 1)
      page = Math.min(page, this.docPages)
      this.scrollToPage(page)
    },
  },
}
</script>

Call on page:

<template>
  <div id="app">
    <pdf-preview :url="url"></pdf-preview>
  </div>
</template>

<script>
import pdfPreview from "./components/PdfPreview.vue";

export default {
  name: "app",
  components: {
    pdfPreview,
  },
  data() {
    return {
      url: "/static/pdf.pdf",
    };
  },
};
</script>

Reference link: https://github.com/Lushenggang/pdf-preview

After some tossing, the problem was finally solved. I really spit blood.

 

Posted by j0n on Tue, 02 Nov 2021 20:17:03 -0700