vue实现一个pdf在线预览,pdf选择文本并提取复制文字触发弹窗效果

devtools/2025/3/11 4:30:40/

[TOC]在这里插入图片描述

一、文件预览

1、安装依赖包

这里安装了disjs-dist@2.16版本,安装过程中报错缺少worker-loader

npm i pdfjs-dist@2.16.105 worker-loader@3.0.8

2、模板部分

<template><div id="pdf-view"><canvas v-for="page in pdfPages" :key="page" :id="pdfCanvas" /><div id="text-view"></div></div>
</template>

3、js部分(核心)

核心代码如下:

  1. 利用 PDF.getDocument获取pdf基础数据
  2. 通过canvas将pdf渲染到canvas画布上
<script>import * as pdfjsViewer from "pdfjs-dist/web/pdf_viewer.js";import "pdfjs-dist/web/pdf_viewer.css";import * as PDF from "pdfjs-dist/webpack";export default {name: "",components: {},data() {return {pdfPages: 1,pdfPath: "http://localhost:8080/qfnext.pdf",// 总页数pdfPages: 1,// 页面缩放pdfScale: 1,pdfDoc: null,};},mounted() {this.loadFile(this.pdfPath);},methods: {loadFile(url) {PDF.getDocument({url,cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/",cMapPacked: true,}).promise.then((pdf) => {this.pdfDoc = pdf;// 获取pdf文件总页数this.pdfPages = pdf.numPages;this.$nextTick(() => {this.renderPage(1); // 从第一页开始渲染});});},renderPage(num) {this.pdfDoc.getPage(num).then((page) => {const canvas = document.getElementById(`pdfCanvas`);const ctx = canvas.getContext("2d");const viewport = page.getViewport({ scale: this.pdfScale });canvas.width = viewport.width;canvas.height = viewport.height;const renderContext = {canvasContext: ctx,viewport,};page.render(renderContext);});},},};
</script>
可能出现的问题:

(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%;
}
pdf_87">pdf多页面处理
  1. 模板处理id作为唯一标识
 <canvas v-for="page in pdfPages" :key="page" :id="`page-${page}`" />
  1. 修改canvas渲染逻辑,主要通过递归的方式逐一渲染
 renderPage(num) {this.pdfDoc.getPage(num).then((page) => {const canvas = document.getElementById(`page-${num}`);const ctx = canvas.getContext("2d");const viewport = page.getViewport({ scale: this.pdfScale });canvas.width = viewport.width;canvas.height = viewport.height;const renderContext = {canvasContext: ctx,viewport,};page.render(renderContext);if (num < this.pdfPages) {this.renderPage(num + 1);}});},

二、文本选中与弹窗(核心代码)

   Promise.all([getTextContentPromise, renderPagePromise]).then(([textContent]) => {const textLayerDiv = document.createElement("div");// 注意:此处不要修改该元素的class名称,该元素的样式通过外部导入,名称是固定的textLayerDiv.setAttribute("class", "textLayer");// 设置容器样式textLayerDiv.setAttribute("style",`z-index: 1;opacity: .2;// 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();// 当选择文本后鼠标取消点击时触发textLayerDiv.addEventListener("mouseup", () => {// // 隐藏文本层// textLayerDiv.style.display = 'none';// 是否选择了文本const isTextSelected =window.getSelection().toString().trim() !== "";if (isTextSelected) {//选择的文本内容const selectedText = window.getSelection().toString();console.log("Selected text:", selectedText);if (selectedText) {alert(selectedText);}}});}).catch((error) => {console.error("Error rendering page:", error);});

三、完整代码如下

<template><div id="pdf-view"><canvas v-for="page in pdfPages" :key="page" :id="`page-${page}`" /><div id="text-view"></div></div>
</template><script>import * as pdfjsViewer from "pdfjs-dist/web/pdf_viewer.js";import "pdfjs-dist/web/pdf_viewer.css";import * as PDF from "pdfjs-dist/webpack";// import { getDocument } from 'pdfjs-dist/webpack';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();export default {name: "",components: {},data() {return {pdfPages: 1,pdfPath: "http://localhost:8080/qfnext.pdf",// 总页数pdfPages: 1,// 页面缩放pdfScale: 1,pdfDoc: null,};},mounted() {this.loadFile(this.pdfPath);},methods: {loadFile(url) {PDF.getDocument({url,cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/",cMapPacked: true,}).promise.then((pdf) => {this.pdfDoc = pdf;// 获取pdf文件总页数this.pdfPages = pdf.numPages;this.$nextTick(() => {this.renderPage(1); // 从第一页开始渲染});});},renderPage(num) {this.pdfDoc.getPage(num).then((page) => {const canvas = document.getElementById(`page-${num}`);const ctx = canvas.getContext("2d");const viewport = page.getViewport({ scale: this.pdfScale });canvas.width = viewport.width;canvas.height = viewport.height;const renderContext = {canvasContext: ctx,viewport,};// 获取文本内容和渲染页面的 Promiseconst getTextContentPromise = page.getTextContent();const renderPagePromise = page.render(renderContext);if (num < this.pdfPages) {this.renderPage(num + 1);}Promise.all([getTextContentPromise, renderPagePromise]).then(([textContent]) => {const textLayerDiv = document.createElement("div");// 注意:此处不要修改该元素的class名称,该元素的样式通过外部导入,名称是固定的textLayerDiv.setAttribute("class", "textLayer");// 设置容器样式textLayerDiv.setAttribute("style",`z-index: 1;opacity: .2;// 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();// 当选择文本后鼠标取消点击时触发textLayerDiv.addEventListener("mouseup", () => {// // 隐藏文本层// textLayerDiv.style.display = 'none';// 是否选择了文本const isTextSelected =window.getSelection().toString().trim() !== "";if (isTextSelected) {//选择的文本内容const selectedText = window.getSelection().toString();console.log("Selected text:", selectedText);if (selectedText) {alert(selectedText);}}});}).catch((error) => {console.error("Error rendering page:", error);});});},},};
</script>
<style lang="scss" scoped>.pdf-con {border: 2px solid #ccc;width: 80%;margin: auto;height: 800px;overflow: auto;// display: none;}
</style>

http://www.ppmy.cn/devtools/166185.html

相关文章

【GoTeams】-5:引入Docker

本文目录 1. Dokcer-compose回顾下Docker知识编写docker-compose.yaml运行docker 2. 部署go服务编写dockerfile 1. Dokcer-compose 这里简单先用一下win版本的Docker&#xff0c;后期开发好了部署的时候再移植到服务器下进行docker部署。 输入命令docker-compose version 就可…

系统架构设计师—系统架构设计篇—特定领域软件体系结构

文章目录 概述领域分类垂直域水平域 系统模型基本活动参与角色 概述 特定领域软件架构&#xff08;Domain Specific Software Architecture&#xff0c;DSSA&#xff09;是在一个特定应用领域中&#xff0c;为一组应用提供组织结构参考的标准团建体系结构。 领域分类 垂直域…

FreeRTOS第17篇:FreeRTOS链表实现细节05_MiniListItem_t:FreeRTOS内存优化

文/指尖动听知识库-星愿 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:深入FreeRTOS内核:从原理到实战的嵌入式开发指南 1 为什么需要迷你列表项? 在嵌入式系统中,内存资源极其宝贵。FreeRTOS为满足不同场景需求,设计了标准列表项(ListItem_…

用Deepseek写一个 HTML 和 JavaScript 实现一个简单的飞机游戏

大家好&#xff01;今天我将分享如何使用 HTML 和 JavaScript 编写一个简单的飞机游戏。这个游戏的核心功能包括&#xff1a;控制飞机移动、发射子弹、敌机生成、碰撞检测和得分统计。代码简洁易懂&#xff0c;适合初学者学习和实践。 游戏功能概述 玩家控制&#xff1a;使用键…

vulnhub靶场之【digitalworld.local系列】的torment靶机

前言 靶机&#xff1a;digitalworld.local-torment&#xff0c;IP地址为192.168.10.12 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.6 kali采用VMware虚拟机&#xff0c;靶机选择使用VMware打开文件&#xff0c;都选择桥接网络 这里官方给的有两种方式&#xff0c…

Javaweb后端spring事务管理 事务四大特性ACID

2步操作&#xff0c;只能同时成功&#xff0c;同时失败&#xff0c;要放在一个事务中&#xff0c;最后提交事务或者回滚事务 事务控制 事务管理进阶 事务的注解 这是所有异常都会回滚 事务注解 事务的传播行为 四大特性

c++:set与map

1.序列式容器与关联式容器 序列式容器 定义&#xff1a;以线性的序列存储数据&#xff0c;数据按照顺序插入&#xff0c;两个数据位置没有紧密关系&#xff0c;即使交换了也不会影响 eg&#xff1a;数组&#xff0c;链表&#xff0c;栈&#xff0c;队列 通常用于按照顺序访问和…

大白话如何利用 CSS 实现一个三角形?原理是什么?

大白话如何利用 CSS 实现一个三角形&#xff1f;原理是什么&#xff1f; 答题思路 先说明实现三角形的方法基础&#xff1a;即利用 CSS 中元素的边框特性来构建三角形&#xff0c;让读者对整体思路有个初步概念。详细阐述具体的实现步骤&#xff1a;包括设置元素的基本样式&a…