vue3中借助 pdfjs-dist 实现pdf文件展示、文本选中功能及使用过程中部分问题处理
·
其他方法实现:vue3中使用 vue-pdf-embed 实现pdf文件预览、翻页、下载等功能
文章目录
一、文件预览
1、安装 pdfjs-dist
,此处指定版本为 2.16.105
yarn add pdfjs-dist@2.16.105
注:3.x版本部分功能的实现方法与旧版本存在差异。
2、html
结构内容
<template>
<div id="pdf-view">
<canvas v-for="page in state.pdfPages" :key="page" id="pdfCanvas" />
<div id="text-view"></div>
</div>
</template>
3、js
功能实现:
<script setup>
import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer.js'
import 'pdfjs-dist/web/pdf_viewer.css'
import * as PDF from 'pdfjs-dist'
// 文件路径
import pdf from './2020试卷.pdf';
import { ref, reactive, onMounted, nextTick } from 'vue';
const state = reactive({
// 文件路径
pdfPath: pdf,
// 总页数
pdfPages: 1,
// 页面缩放
pdfScale: 2,
})
onMounted(() => {
loadFile(state.pdfPath)
});
let pdfDoc = null;
function loadFile(url) {
PDF.getDocument({
url,
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/',
cMapPacked: true,
}).promise.then((pdf) => {
pdfDoc = pdf
// 获取pdf文件总页数
state.pdfPages = pdf.numPages
nextTick(() => {
renderPage(1) // 从第一页开始渲染
})
})
}
function renderPage(num) {
pdfDoc.getPage(num).then((page) => {
const canvas = document.getElementById('pdfCanvas')
const ctx = canvas.getContext('2d')
const viewport = page.getViewport({ scale: state.pdfScale })
canvas.width = viewport.width
canvas.height = viewport.height
const renderContext = {
canvasContext: ctx,
viewport
}
page.render(renderContext)
})
}
</script>
如果要一次性展示全部页面的话,可以将代码修改成:
<template>
<div id="pdf-view">
<canvas v-for="page in state.pdfPages" :key="page" :id="`page-${page}`" />
<div id="text-view"></div>
</div>
</template>
修改页面渲染函数:
<script setup>
function renderPage(num) {
pdfDoc.getPage(num).then((page) => {
// 绑定 id 值对应的元素
const canvas = document.getElementById(`page-${num}`);
...
page.render(renderContext);
// state.pdfPages 为 pdf 文件总页数
if (num < state.pdfPages) {
renderPage(num + 1);
}
});
}
</script>
注意: 一次性渲染全部页面可能会出现加载缓慢或卡顿的问题,可以需要做相应的优化。
4、可能出现的问题
(1) 部分字体出现乱码或浏览器控制台出现警告
浏览器警告:
解决方案:
在 getDocument
方法中追加 cMapUrl
和 cMapPacked
参数:
PDF.getDocument({
url,
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/',
cMapPacked: true,
})
注:cMapUrl
参数可指定为本地文件路径,可在路径 node_modules/pdfjs-dist/cmaps
中获取。通过测试发现,该警告即便不处理依然不影响页面展示,但是在后续的文本选中功能上可能会受影响。
二、文本选中
1、功能实现
在文件预览的基础上添加以下代码:
import { TextLayerBuilder } from 'pdfjs-dist/web/pdf_viewer.js';
const pdfjsWorker = import('pdfjs-dist/build/pdf.worker.entry')
PDF.GlobalWorkerOptions.workerSrc = pdfjsWorker
const eventBus = new pdfjsViewer.EventBus();
function renderPage(num) {
pdfDoc.getPage(num).then((page) => {
...
const renderContext = {
...
}
// page.render(renderContext)
// 获取文本内容和渲染页面的 Promise
const getTextContentPromise = page.getTextContent();
const renderPagePromise = page.render(renderContext);
Promise.all([getTextContentPromise, renderPagePromise])
.then(([textContent]) => {
const textLayerDiv = document.createElement('div');
// 注意:此处不要修改该元素的class名称,该元素的样式通过外部导入,名称是固定的
textLayerDiv.setAttribute('class', 'textLayer');
// 设置容器样式
textLayerDiv.setAttribute('style', `
z-index: 1;
opacity: 1;
background-color:#fff;
transform: scale(1.1);
width: 100%,
height: 100%,
`);
// 设置容器的位置和宽高
textLayerDiv.style.left = canvas.offsetLeft + 'px';
textLayerDiv.style.top = canvas.offsetTop + 'px';
textLayerDiv.style.height = canvas.offsetHeight + 'px';
textLayerDiv.style.width = canvas.offsetWidth + 'px';
const textView = document.querySelector('#text-view');
textView.appendChild(textLayerDiv);
const textLayer = new TextLayerBuilder({
// container: ,
textLayerDiv: textLayerDiv,
pageIndex: page.pageIndex,
viewport: viewport,
eventBus,
// textDivs: []
});
textLayer.setTextContent(textContent);
textLayer.render();
})
.catch((error) => {
console.error('Error rendering page:', error);
})
})
}
2、可能出现的问题:
(1) 页面文字可选中,但文本不可见
通过测试发现,将 pdfjs-dist/web/pdf_viewer.css
路径下的 color
属性注释后可显示文本。
.textLayer span,
.textLayer br {
/* color: transparent; */
position: absolute;
white-space: pre;
cursor: text;
transform-origin: 0% 0%;
}
其他参考资料:https://github.com/mozilla/pdf.js/issues/11509
(2) 浏览器控制台报错 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'dispatch')
浏览器报错:
解决方案:
通过上网查找资料得知,需在 TextLayerBuilder
中追加参数 eventBus
:
const eventBus = new pdfjsViewer.EventBus();
function renderPage(num) {
pdfDoc.getPage(num).then((page) => {
...
Promise.all([getTextContentPromise, renderPagePromise])
.then(([textContent]) => {
...
const textLayer = new TextLayerBuilder({
...
eventBus,
});
...
}).catch ((error) => {...})})}
参考资料:
- https://github.com/mozilla/pdf.js/blob/master/examples/components/pageviewer.mjs
- https://github.com/mozilla/pdf.js/issues/12727
三、效果展示
四、参考资料
功能实现参考资料
- PDF.js实现个性化PDF渲染(文本复制)
- vue3项目使用pdf.js插件实现:搜索高亮、修改pdf.js显示的页码、向pdf.js传值、控制搜索、处理接口文件流
- Custom PDF Rendering in JavaScript with Mozilla’s PDF.Js
- 如何渲染文本图层
- Vue项目中通过pdfjs实现PDF预览
- 前端页面如何优雅的显示PDF(中):渲染文本
- 文档翻译 - 如何使用PDF.js提取PDF中的文字的位置/颜色/字体信息?
pdf.js 相关参考资料
问题解决参考资料
- pdf.js 常见问题解答
- pdf.js–issues
- TextLayer 包含呈现页面时不可见的文本
- pdf.js with text selection
- stackoverflow - 问题标签 pdfjs-dist
其他
更多推荐
已为社区贡献4条内容
所有评论(0)