在electron下實現PDF線上預覽功能

chenyuhu發表於2019-04-16

由於公司業務的開發需要,需要實現在electron上實現PDF的線上預覽功能。electron 3.x版本後就不在支援PDF的預覽功能了,官方給的解釋是由於人手不夠將不再支援PDF預覽功能(也是醉了),在經過一番調研結合大佬們的經驗實現了PDF的在線上預覽功能

實現方式一、

  1. 下載pdfjs-dist

  2. 將下好的檔案放入static檔案目錄下

    在electron下實現PDF線上預覽功能

  3. 在electron的主執行緒中通過渲染執行緒與主執行緒之間的通訊獲取viewer.html檔案位置

    // 監聽獲取viewer.html檔案位置、
    function getUrl(win) {
      const filePath = process.env.NODE_ENV === 'development' ? `${__static}\\pdfjs\\web\\viewer.html` : path.resolve(__dirname, '../../../static/pdfjs/web/viewer.html')
      win.webContents.send('recieve', filePath)
    }
    // pdf預覽獲取viewer.html檔案位置
        ipcMain.on('getUrl', () => getUrl(win))
    複製程式碼
  4. 在pdf.vue中寫

    <template>
       <iframe
            :src="base + '?file=' + url"
            :width="width"
            :height="height"
            class="iframe-placeholder"></iframe>
    </template>
    <script>
    import { ipcRenderer } from 'electron' // eslint-disable-line
    
    export default {
      props: {
        url: {
          type: String,
          default: '',
        },
        width: {
          type: Number,
          default: 750
        },
        height: {
          type: Number,
          default: 600
        },
      },
      data() {
        return {
          preview: false,
          base: '',
        }
      },
      computed: {
        // 講義檔名
        name() {
          const uri = decodeURI(this.url)
          const arr = uri.split('/')
          const len = arr.length
          return arr[len - 1]
        },
      },
      created() {
        // 獲取viewer.html的位置
        ipcRenderer.send('getUrl')
      },
      mounted() {
        ipcRenderer.on('recieve', (e, arg) => {
          console.log('檔案路徑', arg)
          this.base = arg
        })
      }
    }
    </script>
    
    <style lang="scss" scoped>
    .wrapper {
      .info {
        width: 200px;
      }
      .iframe-placeholder {
        background: url('../../../assets/loading.svg') no-repeat 50% 50%;
      }
    }
    </style>
    
    複製程式碼
  5. 在需要引入的地方引用即可

問題

樣式比較醜,但是可以實現PDF的分頁和列印的功能,且在打完包後檔案比較大

實現方式二、

  1. 下載npm pdfjs-dist 包

    // npm的下載方式
    npm i pdfjs-dist
    // yarn 的下載方式
    yarn add pdfjs-dist
    複製程式碼
  2. 在webpack的配置中寫入

     entry: {
        renderer: path.join(__dirname, '../src/renderer/main.js'),
        'pdf.worker': 'pdfjs-dist/build/pdf.worker.entry',
      },
    複製程式碼
  3. 在pdf.vue中寫入

    <template>
      <el-dialog
        :visible.sync="pdfjsView"
        title=""
        width="55%"
        class="cpdf"
        append-to-body
        @close="pdfurl = null">
        <div v-loading="loading" v-if="pdfurl" class="center" style="height:600px">
          <canvas v-for="data in canvasData" :key="data" :id="'the-canvas-'+data" class="canvasstyle"></canvas>
        </div>
        <div v-else style="font-size:18px;text-align:center;font-weight:900">
          沒有PDF檔案可以預覽
        </div>
        <span slot="footer">
          <el-button @click="pdfjsView = false">取 消</el-button>
          <el-button type="primary" @click="pdfjsView = false">確 定</el-button>
        </span>
      </el-dialog>
    </template>
    
    <script type="text/ecmascript-6">
    import PDFJS from 'pdfjs-dist'
    
    export default {
      name: 'CPdf',
      components: {},
      data() {
        return {
          pdfDoc: null, // pdfjs 生成的物件
          pageNum: 1, //
          pageRendering: false,
          pageNumPending: null,
          scale: 1.2, // 放大倍數
          page_num: 0, // 當前頁數
          page_count: 0, // 總頁數
          maxscale: 2, // 最大放大倍數
          minscale: 0.8, // 最小放大倍數
          canvasData: [],
          pdfjsView: false,
          pdfurl: null,
          loading: false
        }
      },
      methods: {
        renderPage(num) {
          // 渲染pdf
          const vm = this
          this.pageRendering = true
          const canvas = document.getElementById(`the-canvas-${num}`)
          // Using promise to fetch the page
          this.pdfDoc.getPage(num).then((page) => {
            const viewport = page.getViewport(vm.scale)
            // alert(vm.canvas.height)
            canvas.height = viewport.height
            canvas.width = viewport.width
    
            // Render PDF page into canvas context
            const renderContext = {
              //   canvasContext: vm.ctx,
              canvasContext: canvas.getContext('2d'),
              viewport
            }
            const renderTask = page.render(renderContext)
    
            // Wait for rendering to finish
            renderTask.promise.then(() => {
              vm.pageRendering = false
              if (vm.pageNumPending !== null) {
                // New page rendering is pending
                vm.renderPage(vm.pageNumPending)
                vm.pageNumPending = null
              }
            })
          })
          vm.page_num = vm.pageNum
        },
        getUrl(url) {
          this.pdfurl = url
          this.pdfjsView = true
          this.showPDf()
        },
        showPDf() {
          const vm = this
          this.loading = true
          vm.canvasData = []
          // PDFJS.workerSrc = '../../../static/PDF/pdf.worker.min.js'
          PDFJS.getDocument(vm.pdfurl)
            .then((pdfDoc_) => {
            // 初始化pdf
              vm.pdfDoc = pdfDoc_
              vm.page_count = vm.pdfDoc.numPages
              for (let i = 0; i < vm.page_count; i += 1) {
                vm.canvasData.push(i + 1)
              }
              return pdfDoc_
            })
            .then((pdfDoc_) => {
            // 初始化pdf
              vm.pdfDoc = pdfDoc_
              vm.page_count = vm.pdfDoc.numPages
              for (let i = 0; i < vm.page_count; i += 1) {
                vm.renderPage(i + 1)
              }
              vm.loading = false
            })
        }
      },
      computed: {},
      mounted() {}
    }
    </script>
    
    <style lang="scss" scoped type="text/css">
    .cpdf {
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      z-index: 99999;
      display: flex;
      justify-content: center;
      align-items: center;
      .center {
        text-align: center;
        height: 100%;
        overflow: auto;
        padding-top: 20px;
        .contor {
          margin-bottom: 10px;
        }
      }
      .page-foot {
        position: fixed;
        left: 0px;
        bottom: 0px;
        width: 100%;
        height: 56px;
        line-height: 56px;
        background-color: #fff;
        text-align: center;
        z-index: 10;
        .foot-button {
            display: inline-block;
            height: 56px;
            position: relative;
            top: -22px;
            left: 20px;
        }
      }
    }
    </style>
    複製程式碼
  4. 在需要引入的地方引入即可

問題

實現方式三、

載入 npm i electron-pdf ,由於這種方法會導致新建視窗,故沒有做實驗

相關文章