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>

4、可能出现的问题

(1) 部分字体出现乱码或浏览器控制台出现警告

浏览器警告:

浏览器警告

解决方案:

getDocument 方法中追加 cMapUrlcMapPacked 参数:

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) => {...})})}

参考资料:

三、效果展示

在这里插入图片描述


四、参考资料

功能实现参考资料

pdf.js 相关参考资料

问题解决参考资料

其他

  • 30
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值