某天测试妹子告诉我,为啥这个pdf在ie里打不开?然后我去瞟了一眼:
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吗?
You can also use PDF.js to force PDF rendering in-browser without a plugin, but that's outside the scope of PDFObject.
学习一个新东西,从demo做起
1. 安装PDF.js
此次demo直接使用
vue-cli
创建
- 源码安装,从官网直接下载源码
- 可以通过以下方式使用CDN
www.jsdelivr.com/package/npm…
cdnjs.com/libraries/p…
unpkg.com/pdfjs-dist/
- 使用预构建版本,从npm下载
npm i pdfjs-dist
or
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的童鞋也可以戳阮一峰老师的这篇文章
PDFJs通过canvas
将pdf内容渲染到浏览器,所以我们需要在HTML中添加一个canvas
<canvas id="the-canvas" style="border:1px solid black"></canvas>
然而此方式渲染出来的PDF内容无法被选中,怎么办呢?
使用
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地址
溜~