前言
在上次的项目中有渲染pdf文档的需求,在Chrome浏览器中是可以直接渲染pdf文档的,而项目跑在webview中,经过测试不支持直接渲染pdf,那么就需要用第三方库了,然后我用的是pdf.js这个库
使用方式
pdf.js有多种使用模式
- 直接使用官方的示例iframe方式
- 直接引入pdf.js使用方式
- 通过npm引入的方式
而通过npm引入方式,它又分两种渲染模式,一种是canvas渲染,另外一种是HTML渲染(这种方式渲染出来的文本内容是可以复制的)
我这里将分别讲解iframe模式和npm引入模式,和canvas渲染 和 HTML渲染
文末将提供源码示例
官方完整iframe使用
首先下载官方稳定包,官方下载地址:Getting Started (mozilla.github.io)
使用
下载解压后,通过vscode打开,启动服务模式,访问网址http://127.0.0.1:5500/web/viewer.html
我们可以在链接后面添加 file=pdf.js文件地址 参数来实现渲染我们想要的pdf
http://127.0.0.1:5500/web/viewer.html?file=http://www.xxx.cn/test.pdf
问题:
如果你的文件链接不同域名,会产生跨域问题,我们需要通过修改viewer.js文件来解决
打开该js文件,搜索 file origin does not match viewer 注释这行代码即可,如图
在vue项目中使用
首先将解压后的文件放到 public文件夹内,这里为了减少体积,我将一些无用的map文件删除了
封装一个组件,接收一个url参数,之后哪里需要哪里调用该组件渲染即可
<template>
<iframe :src="src" frameborder="0" width="100%" height="100%" class="pdf-iframe"></iframe>
</template>
<script setup>
import { computed } from "vue"
const props = defineProps({
url: {
type: String,
required: true
}
})
const emit = defineEmits(["pdfload"])
const src = computed(() => {
// 拼接url路径,如果你的url有参数,那么需要URL编码一下
return `/web-pdfjs2.6.347/web/viewer.html?file=${props.url}`
})
</script>
调用
<template>
<div class="iframebox">
<PdfIframe :url="url"/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import PdfIframe from './pdf-iframe/index.vue'
const url = ref('http://www.xxx.com/test2.pdf') // pdf文件的路径或者url
</script>
<style>
.iframebox {
height: 100vh;
}
</style>
效果
npm方式使用
pdfjs的npm包名称为:pdfjs-dist,我这里使用的是指定的版本,不同版本用法有差异 这种方式相当于自定义渲染,也就是像翻页,打印,等都需要自己实现
canvas方式渲染
安装指定版本
npm install pdfjs-dist@2.16.105
封装组件
<template>
<div ref="pdfRef" class="pdf-view"></div>
</template>
<script setup>
import { ref, watch } from "vue"
import PdfjsWorker from "pdfjs-dist/build/pdf.worker.js?worker"
const props = defineProps({
url: { type: String, required: true}
})
const pdfRef = ref(null)
// 侦听props的url改变,则立即切换渲染pdf
watch(
() => props.url,
() => {
setTimeout(() => init(), 1)
},
{ immediate: true }
)
// 渲染pdf
async function init() {
// 异步加载pdf.js
const PDFJS = await import("pdfjs-dist/build/pdf.js")
if (typeof window !== "undefined" && "Worker" in window) {
PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker()
}
// 加载文档
let loadingTask = PDFJS.getDocument({ url: props.url })
loadingTask.__PDFDocumentLoadingTask = true
const pdf = await loadingTask.promise // 使用await等待加载完毕
// 循环渲染每一页
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i)
let pixelRatio = 3
let viewport = page.getViewport({ scale: 1 })
let divPage = window.document.createElement("div") // canvas的外层div
// 使用canvas渲染
let canvas = divPage.appendChild(window.document.createElement("canvas"))
divPage.className = "page"
pdfRef.value.appendChild(divPage)
canvas.width = viewport.width * pixelRatio // 计算宽度
canvas.height = viewport.height * pixelRatio
let renderContext = {
canvasContext: canvas.getContext("2d"),
viewport: viewport,
transform: [pixelRatio, 0, 0, pixelRatio, 0, 0]
}
await page.render(renderContext).promise // 一页一页的渲染
divPage.className = "page complete"
}
console.log('pdf页面全部渲染完毕', pdf.numPages, pdf)
}
</script>
<style scoped lang="scss">
.pdf-view {
position: relative;
display: block;
width: 100%;
height: 100%;
}
// 组件样式
:deep() {
.page {
position: relative;
canvas {
width: 100%;
}
}
}
</style>
调用
<template>
<PdfCanvas :url="url"/>
</template>
<script setup>
import { ref } from 'vue';
import PdfCanvas from './pdf-canvas/index.vue'
const url = ref('/test2.pdf') // pdf的链接
</script>
渲染效果
dom方式渲染
安装指定版本
npm install pdfjs-dist@2.0.943
话不多说,上代码,封装成组件
<template>
<div id="pageContainer">
<div id="viewer" class="pdfViewer"></div>
</div>
</template>
<script setup>
import { defineProps, defineEmits, onMounted } from 'vue'
import pdfjsLib from 'pdfjs-dist/build/pdf.js'
import PdfjsWorker from "pdfjs-dist/build/pdf.worker.min.js?worker"
import { PDFViewer } from 'pdfjs-dist/web/pdf_viewer'
import 'pdfjs-dist/web/pdf_viewer.css'
pdfjsLib.GlobalWorkerOptions.workerPort = new PdfjsWorker()
const props = defineProps({
url: {
type: String,
default: '',
},
})
const emit = defineEmits(['pdfload'])
onMounted(() => getPdf()) // 等组件初始化完成后在渲染pdf,因为要挂载到div上
async function getPdf() {
const container = document.getElementById('pageContainer') // 获取挂载点
// 实例化pdf视图
const pdfViewer = new PDFViewer({
container: container,
})
// 加载pdf文件
const loadingTask = pdfjsLib.getDocument(props.url)
// 使用await等待加载完毕
const pdf = await loadingTask.promise
// 开始绘制到dom
pdfViewer.setDocument(pdf)
// 监听pagerendered来实现 判断 是否渲染完成,如果要打印一定要渲染完成后再打印,不然会有空白
document.addEventListener('pagerendered', function (e) {
if (e.detail.pageNumber === pdf.numPages) {
emit('pdfload', pdf) // 渲染完成,通知父组件
}
})
}
</script>
调用
<template>
<div>
<PdfViewer :url="url"/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import PdfViewer from './pdf-viewer/index.vue'
const url = ref('/test2.pdf') // pdf链接
</script>
渲染效果
会比canvas方式要 清晰一点,因为是dom渲染
最后
本文中所有调用方式的示例项目代码:
gitee:gitee.com/xlybyte/vue…
github:github.com/xlybyte/vue…
pdfjs的一些常用属性和方法,记录一下
| 属性/方法 | 描述 |
|---|---|
| PDFJS | PDFJS 全局对象 |
| PDFJS.version | pdf.js 版本 |
| PDFJS.workerSrc | worker 路径 |
| PDFJS.getDocument(source) | 获取 PDF 文档 |
| PDFJS.getDocument(params) | 获取 PDF 文档,params 中包含 URL 和需要的其他信息 |
| PDFJS.disableWorker | 禁用 worker |
| PDFJS.disableStream | 禁用流模式 |
| PDFJS.disableAutoFetch | 禁用自动获取 |
| PDFJS.getDocumentLoadingTask(source) | 获取异步加载 PDF 文档的任务 |
| PDFJS.getDocumentLoadingTask(params) | 获取异步加载 PDF 文档的任务,params 中包含 URL 和需要的其他信息 |
| PDFJS.getDocumentProxy(params) | 获取代理对象 |
| PDFJS.TextRenderingMode | 文本渲染模式 |
| PDFJS.createPromiseCapability() | 创建 promise 能力对象 |