在vue3中使用pdf.js-3.5渲染pdf文件
关于pdf.js,这是mozilla开发和维护的开源软件,和所有mozilla的软件一样,他们的功能十分强大,迭代速度也飞快,但文档一般跟不上开发速度,所以当你尝试使用它时,你会发现你找不到文档,或者能找到的文章用的是老旧的版本。我们在page.render会返回一个promise,我们可以根据这个promise判断canvas是否渲染完毕,渲染完毕后,动态更新height property. 然
关于pdf.js,这是mozilla开发和维护的开源软件,和所有mozilla的软件一样,他们的功能十分强大,迭代速度也飞快,但文档一般跟不上开发速度,所以当你尝试使用它时,你会发现你找不到文档,或者能找到的文章用的是老旧的版本。
mozilla团队坦言,他们不会为第三方框架提供支持,也就是说,没有官方对vue, react, angular等框架的绑定,也没有使用教程。我在网上找到的通常都是2.5以下版本的教程。不是不能用,但是我更倾向于使用最新的稳定版本。
pdf.js是轻量级渲染引擎,不是排版引擎。这一点非常重要。pdf.js的工作原理是,在canvas上以图片形式绘制pdf内容。另外用dom渲染文字,以供选择和批注。
我这篇博客只写如何渲染,更多内容未来会更新。
使用过程分如下步骤:
- 加载库,设置worker(worker相当于另一个进程,用于执行较大运算量的后台工作)
- 加载文件
- 按页渲染pdf
- 渲染text layer
加载库,设置worker
// 加载库,设置worker
import * as pdfjsLib from "pdfjs-dist/build/pdf";
import "pdfjs-dist/web/pdf_viewer.css";
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://cdn.jsdelivr.net/npm/pdfjs-dist@3.5.141/build/pdf.worker.min.js";
template:
<div id="container">
<canvas id="theCanvas"></canvas>
</div>
加载pdf文件:
注意,这里会返回一个task,可以保存在变量中复用
this.loadingTask = pdfjsLib.getDocument(pdfUrl);
渲染canvas
const canvas = document.getElementById("theCanvas");
const context = canvas.getContext("2d");
const scale = 1.5;
this.loadingTask.promise.then((pdf) => {
pdf.getpage(1)
.then((page) => {
const viewport = page.getViewport({ scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport,
};
const renderTask = page.render(renderContext);
})
})
渲染文字dom:
const container = document.getElementById("container");
this.loadingTask.promise.then((pdf) => {
pdf.getpage(1)
.then((page) => {
const textlayer = document.createElement("div");
textlayer.className = "textLayer";
container.appendChild(textlayer);
page.getTextContent().then((textContent) => {
pdfjsLib.renderTextLayer({
textContentSource: textContent,
container: textlayer,
viewport: viewport,
textDivs: [],
});
});
})
})
分页:
<div class="page-nav">
<button @click="prev">prev</button>
<span>{{ currentPage }}/ {{ numPages }} </span>
<button @click="next">next</button>
<input
type="number"
v-model="selectedPage"
@input="gotoSelectedPage"
min="1"
max="numPages"
/>
</div>
export default {
data() {
return {
currentPage: 1,
numPages: 0,
selectedPage: 1
};
},
methods: {
renderPage(num) {
const container = document.getElementById("container");
const canvas = document.getElementById("theCanvas");
const context = canvas.getContext("2d");
const scale = 1.5;
this.loadingTask.promise.then((pdf) => {
pdf
.getPage(num)
.then((page) => {
const viewport = page.getViewport({ scale });
const textlayer = document.createElement("div");
textlayer.className = "textLayer";
container.appendChild(textlayer);
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport,
};
const renderTask = page.render(renderContext);
page.getTextContent().then((textContent) => {
pdfjsLib.renderTextLayer({
textContentSource: textContent,
container: textlayer,
viewport: viewport,
textDivs: [],
});
});
})
});
},
next() {
if (this.currentPage < this.numPages) {
this.currentPage++;
this.renderPage(this.currentPage);
}
},
prev() {
if (this.currentPage > 1) {
this.currentPage--;
this.renderPage(this.currentPage);
}
},
gotoSelectedPage() {
if (this.selectedPage >= 1 && this.selectedPage <= this.numPages) {
this.currentPage = this.selectedPage;
this.renderPage(this.currentPage);
}
},
}
},
特别注意,由于pdf.js要求container div必须是absolute position,我们的分页nav是放在下面的,所以要动态地设置top.
this.loadingTask.promise.then((pdf) => {
\\...
pdf
.getPage(num)
.then((page) => {
\\...
const renderTask = page.render(renderContext);
\\...
return renderTask.promise;
}).then(() => {
const canvas = document.getElementById("theCanvas");
this.canvasHeight = canvas.offsetHeight;
});
})
我们在page.render会返回一个promise,我们可以根据这个promise判断canvas是否渲染完毕,渲染完毕后,动态更新height property. 然后再绑定即可。
export default {
data() {
return {
\\ ..
canvasHeight:0
}
},
computed: {
pageNavTop(){
var v = this.canvasHeight + 40;
return `${v}px`;
},
}
\\...
}
技术细节也就差不多这些,具体请见codesandbox
或github
更多推荐
所有评论(0)