一個PDF在線預覽小demo(文本可複製)

某天測試妹子告訴我,爲啥這個pdf在ie裏打不開?而後我去瞟了一眼:css

This browser does not support inline PDFs. Please download the PDF to view it: Download PDF
複製代碼

以前沒怎麼接觸過PDF在線閱讀,這項目也是老項目,而後就去看這些傢伙到底用的什麼不兼容ie的PDF在線閱讀插件的,發現是PDFObject,然而PDFObject並不支持在沒有pdf渲染器的瀏覽器中呈現pdf
既然這樣,那麼咱們就下載一個Adobe Reader吧!等等...難道每一個客戶我都要提醒下載這個reader嗎?客戶心理不會mmp嗎?html

在這裏PDFObject的做者建議可使用 pdf.js 強制渲染PDF

You can also use PDF.js to force PDF rendering in-browser without a plugin, but that's outside the scope of PDFObject.vue

學習一個新東西,從demo作起git

1. 安裝PDF.js

這次demo直接使用vue-cli建立github

  • 源碼安裝,從官網直接下載源碼
  • 能夠經過如下方式使用CDN

www.jsdelivr.com/package/npm…
cdnjs.com/libraries/p…
unpkg.com/pdfjs-dist/web

  • 使用預構建版本,從npm下載
npm i pdfjs-dist
複製代碼

orvue-cli

yarn add pdfjs-dist
複製代碼

2. 在vue中使用

import PDFJS from 'pdfjs-dist';

PDFJS.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/build/pdf.worker.js';
複製代碼

爲了提高解析和渲染PDF的性能,pdf.js引入了Web Workers,不瞭解web worker的童鞋也能夠戳阮一峯老師的這篇文章npm

PDFJs經過canvas將pdf內容渲染到瀏覽器,因此咱們須要在HTML中添加一個canvascanvas

<canvas id="the-canvas" style="border:1px solid black"></canvas>
複製代碼

然而此方式渲染出來的PDF內容沒法被選中,怎麼辦呢?api

使用getTextContent獲取pdf內容,再經過TextLayerBuilder將內容渲染到canvas圖層之上就能夠選中了

首先調用 PDFJS.getDocument 方法獲取一個PDFDocumentLoadingTask

getDocument (src)→{ PDFDocumentLoadingTask }

src類型能夠爲 string | TypedArray | DocumentInitParameters | PDFDataRangeTransport,在這裏咱們直接使用string類型,即pdf的url字符串

該方法雖然返回一個PDFDocumentLoadingTask,可是仍然能夠像使用promise同樣使用它,源碼以下,在調用pdf.then的時候會返回一個Promise對象。

class PDFDocumentLoadingTask {
    /* 省略若干代碼 */
    /** * @return {Promise} */
    get promise() {
      return this._capability.promise;
    }

    then(onFulfilled, onRejected) {
      deprecated('PDFDocumentLoadingTask.then method, ' +
                 'use the `promise` getter instead.');
      return this.promise.then.apply(this.promise, arguments);
    }
  }
複製代碼

拿到PDFDocumentLoadingTask以後,根據pdf頁碼獲取當前頁的pdf

let pdf = await PDFJS.getDocument(url) // URL爲pdf的連接
let page = await pdf.getPage(num) // num 爲頁碼,如 1
複製代碼

設置PDf文檔的頁面尺寸(展現比例)

let scale = 1.5;
let viewport = page.getViewport(scale);
複製代碼

渲染pdf

let renderContext = {
    canvasContext: context, // 此爲canvas的context
    viewport: viewport
};
        
await page.render(renderContext); // 這裏await是爲了後面渲染pdf文本
複製代碼

而後拿到pdf的內容渲染成文本

let textContent = await page.getTextContent()
/* ... */
// 建立新的TextLayerBuilder實例
var textLayer = new TextLayerBuilder({
    textLayerDiv: textLayerDiv, // 放置文本的dom
    pageIndex: page.pageIndex, // pdf頁碼
    viewport: viewport
});

textLayer.setTextContent(textContent);

textLayer.render();
複製代碼

完整代碼以下,接下來就是展現CV大法的時候了,能夠參考example

巴拉巴拉...

import PDFJS from "pdfjs-dist";
import { TextLayerBuilder } from "pdfjs-dist/web/pdf_viewer";
import "pdfjs-dist/web/pdf_viewer.css";
PDFJS.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.js";

var container;
export default {
  name: "HelloWorld",
  props: {
    msg: String
  },
  mounted() {
    this.$nextTick(() => {
        let url =
        "http://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf";
        this.getPDF(url);
    });
  },
  methods: {
    async getPDF(url) {
        let pdf = await PDFJS.getDocument(url)
        container = container || document.querySelector('#container')
        for(let i = 0; i < pdf.numPages; i++) {
            try{
                await this.rendPDF(pdf, i)
            } catch(e) {
                // console.error(e)
            }
        }
    },
    async renderPDF(pdf, num) {
        let page = await pdf.getPage(num)
        // 設置展現比例
        let scale = 1.5;
        let viewport = page.getViewport(scale);

        let pageDiv = document.createElement('div');
        pageDiv.setAttribute('id', 'page-' + (page.pageIndex + 1));
        pageDiv.setAttribute('style', 'position: relative');
        container.appendChild(pageDiv);

        let canvas = document.createElement('canvas');
        pageDiv.appendChild(canvas);
        let context = canvas.getContext('2d');
        canvas.height = viewport.height;
        canvas.width = viewport.width;
        
        let renderContext = {
            canvasContext: context,
            viewport: viewport
        };
        
        await page.render(renderContext);
        let textContent = await page.getTextContent()
        // 建立文本圖層div
        const textLayerDiv = document.createElement('div');
        textLayerDiv.setAttribute('class', 'textLayer');
        textLayerDiv.setAttribute('style', `width: ${viewport.width}px; margin: 0 auto;`)
        // 將文本圖層div添加至每頁pdf的div中
        pageDiv.appendChild(textLayerDiv);
        
        // 建立新的TextLayerBuilder實例
        var textLayer = new TextLayerBuilder({
            textLayerDiv: textLayerDiv,
            pageIndex: page.pageIndex,
            viewport: viewport
        });
        
        textLayer.setTextContent(textContent);
        
        textLayer.render();

    }
  }
};
複製代碼

3.效果

能夠看到內容文本已經被已純文本的形式渲染出來了。

文本選擇也木有問題。
最後附上 demo地址 溜~
相關文章
相關標籤/搜索