当前位置: 首页 > news >正文

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

[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多页面处理
  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>

相关文章:

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

[TOC] 一、文件预览 1、安装依赖包 这里安装了disjs-dist2.16版本&#xff0c;安装过程中报错缺少worker-loader npm i pdfjs-dist2.16.105 worker-loader3.0.8 2、模板部分 <template><div id"pdf-view"><canvas v-for"page in pdfPages&qu…...

【CSS 】Class Variance Authority CSS 类名管理工具库

1.背景、什么是 CVA&#xff1f; Class Variance Authority (CVA) 是一个用于管理 CSS 类名 的工具库&#xff0c;特别适合在 React 或 Vue 等前端框架中使用。它可以帮助你更轻松地处理组件的 样式变体&#xff08;Variants&#xff09;&#xff0c;比如按钮的不同状态&#…...

自然语言处理:文本分类

介绍 大家好&#xff0c;我这个热衷于分享知识的博主又来啦&#xff01;之前我们一起深入探讨了自然语言处理领域中非常重要的两个方法&#xff1a;朴素贝叶斯和逻辑斯谛回归。在探索的过程中&#xff0c;我们剖析了朴素贝叶斯如何基于概率原理和特征条件独立假设&#xff0c;…...

刷题记录 HOT100 贪心-2:45. 跳跃游戏 II

题目&#xff1a;45. 跳跃游戏 II 难度&#xff1a;中等 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 &l…...

7.2 奇异值分解的基与矩阵

一、奇异值分解 奇异值分解&#xff08;SVD&#xff09;是线性代数的高光时刻。 A A A 是一个 m n m\times n mn 的矩阵&#xff0c;可以是方阵或者长方形矩阵&#xff0c;秩为 r r r。我们要对角化 A A A&#xff0c;但并不是把它化成 X − 1 A X X^{-1}A X X−1AX 的形…...

PDFMathTranslate安装使用

PDF全文翻译&#xff01;&#xff01;&#xff01;&#xff01; PDFMathTranslate安装使用 它是个啥 PDFMathTranslate 可能是一个用于 PDF 文件的数学公式翻译 工具。它可能包含以下功能&#xff1a; 提取 PDF 内的数学公式 将数学公式转换成 LaTeX 代码 翻译数学公式的内…...

STL之list的使用(超详解)

目录 一、list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 iterator的使用 1.2.3capacity&#xff08;容量相关&#xff09; 1.2.4 element access&#xff08;元素访问&#xff09; 1.2.5 modifiers&#xff08;链表修改&#xff09;…...

动态 SQL 的使用

目录 1、< if> 标签2、< trim> 标签3、< where> 标签4、< set> 标签5、< foreach> 标签 1、< if> 标签 < if test“条件语句”> xxxx < /if> 只有当条件语句满足条件&#xff0c;才会拼接 < if> 标签内容&#xff0c;因…...

【如何删除在 Linux 系统中的删除乱码文件】

如何删除在 Linux 系统中的删除乱码文件 1. 列出文件并找到乱码文件&#xff1a;2. 使用通配符&#xff08;谨慎使用&#xff09;&#xff1a;3. 转义特殊字符&#xff1a;4. 使用 find 命令&#xff1a;5. 使用 inode 号删除文件&#xff1a;6. 图形界面文件管理器&#xff1a…...

防火墙IPSec (无固定IP地址---一对多)

目录 前言 一、场景&#xff1a; 二、实现 1.拓扑图 2.配置思路 ①基础通信配置 ②PPPoE配置 ③总部的模版IPSec配置 ④分部的IPSec配置 ⑤NAT配置 3.具体配置 ①基础配置 ②详细配置和顺序 效果测试&#xff1a; ③PPPOE ①配置PPPoE ②策略放行 ③IPSec与NA…...

基于SpringBoot的智能问诊系统设计与隐私保护策略

通过SpringBoot框架&#xff0c;我们可以快速搭建一个智能问诊系统&#xff0c;为用户提供便捷的线上医疗服务。然而&#xff0c;在系统设计和实现过程中&#xff0c;如何保障用户的隐私和数据安全&#xff0c;始终是一个亟需关注的问题。本文将探讨基于SpringBoot的智能问诊系…...

DeepSeek进阶应用(一):结合Mermaid绘图(流程图、时序图、类图、状态图、甘特图、饼图)

&#x1f31f;前言: 在软件开发、项目管理和系统设计等领域&#xff0c;图表是表达复杂信息的有效工具。随着AI助手如DeepSeek的普及&#xff0c;我们现在可以更轻松地创建各种专业图表。 名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_cycle 函数

nei声明在 src/core/ngx_cycle.h ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle);实现在 src/core/ngx_cycle.c ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) {void *rv;char **senv;ngx_uint_t i, n;ngx_log_t …...

【redis】数据类型之geo

Redis的GEO数据类型用于存储地理位置信息&#xff08;如经纬度&#xff09;&#xff0c;并提供高效的地理位置查询功能&#xff08;如计算两地距离、搜索附近地点等&#xff09;。其底层基于Sorted Set&#xff08;有序集合&#xff09;实现&#xff0c;通过Geohash编码将经纬度…...

vue3 vite或者vue2 百度地图(卫星图)离线使用详细讲解

1、在Windows上下载瓦片&#xff0c;使用的工具为: 全能电子地图下载器3.0最新版&#xff08;推荐&#xff09; 下载后解压&#xff0c;然后进入目录"全能电子地图下载器3.0最新版&#xff08;推荐&#xff09;\全能电子地图下载器3.0\MapTileDownloader" 在这个目录…...

《Python实战进阶》No17: 数据库连接与 ORM(SQLAlchemy 实战)

No17: 数据库连接与 ORM&#xff08;SQLAlchemy 实战&#xff09; 摘要 本文深入探讨SQLAlchemy在复杂场景下的高级应用&#xff0c;涵盖四大核心主题&#xff1a; 会话生命周期管理&#xff1a;通过事件钩子实现事务监控与审计追踪混合继承映射&#xff1a;结合单表/连接表继…...

工程化与框架系列(27)--前端音视频处理

前端音视频处理 &#x1f3a5; 引言 前端音视频处理是现代Web应用中的重要组成部分&#xff0c;涉及音频播放、视频处理、流媒体传输等多个方面。本文将深入探讨前端音视频处理的关键技术和最佳实践&#xff0c;帮助开发者构建高质量的多媒体应用。 音视频技术概述 前端音视…...

芋道打包时报错:缺失@unocss插件

在遇到打包时&#xff0c;报这个错误&#xff0c;提示构建失败是因为 ESLint 在加载 unocss 插件时&#xff0c;找不到 unocss/eslint-plugin 模块 解决办法&#xff1a;安装缺失的依赖&#xff1a;保证unocss/eslint-plugin已经被正确安装&#xff0c; 使用以下命令安装&…...

PY32MD320单片机 QFN32封装,内置多功能三相 NN 型预驱。

PY32MD320单片机是普冉半导体的一款电机专用MCU&#xff0c;芯片采用了高性能的 32 位 ARM Cortex-M0 内核&#xff0c;主要用于电机控制。PY32MD320嵌入高达 64 KB Flash 和 8 KB SRAM 存储器&#xff0c;最高工作频率 48 MHz。PY32MD320单片机的工作温度范围为 -40 ~ 105 ℃&…...

深入解析 configService.addListener 使用中的注意事项

在使用 Nacos 的 configService.addListener 方法进行配置监听时&#xff0c;为了确保程序的稳定性、可靠性以及高效性&#xff0c;有诸多注意事项需要我们关注。下面将对这些关键要点进行详细阐述。 一、连接稳定性 1.1 网络连接问题 Nacos 客户端与服务端通过网络进行通信&…...

四旋翼变形控制:RL与MPC在混合动力学中的对比

1. 四旋翼变形控制的技术挑战与解决方案四旋翼变形控制&#xff08;Quadrotor Morpho-Transition&#xff09;是当前机器人领域最具挑战性的前沿技术之一。这项技术使机器人能够在空中完成形态变换&#xff0c;实现从飞行模式到地面模式的平滑切换。想象一下&#xff0c;一架四…...

告别命令行!用Python脚本批量管理Docker容器,效率提升不止一点点

告别命令行&#xff01;用Python脚本批量管理Docker容器&#xff0c;效率提升不止一点点每次在终端敲入docker ps、docker stop、docker rm时&#xff0c;你是否想过——当容器数量超过两位数&#xff0c;这种重复劳动是否在消耗你的生命&#xff1f;去年我们团队在迁移微服务架…...

我靠这个测试设计方法,把漏测率降低了80%

当“直觉测试”撞上南墙很长一段时间里&#xff0c;我和许多测试同行一样&#xff0c;测试用例的设计主要依靠两样东西&#xff1a;需求文档和“测试直觉”。这种模式在业务逻辑相对简单、迭代速度平缓时还能勉强应付。一旦面对复杂的企业级应用、高频的敏捷迭代&#xff0c;或…...

【MySQL数据库 | 第一篇】 概述

数据库相关概念&#xff1a; 数据库(Database)&#xff1a;数据库是指一组有组织的数据的集合&#xff0c;通过计算机程序进行管理和访问。数据库管理系统&#xff1a;操纵和管理数据库的大型软件SQL&#xff1a;操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数…...

告别数据饥荒:用PyTorch手把手实现原型网络(Prototypical Networks)做电影评论情感分类

告别数据饥荒&#xff1a;用PyTorch手把手实现原型网络做电影评论情感分类 在自然语言处理领域&#xff0c;情感分析一直是热门研究方向&#xff0c;但现实中的开发者常面临一个尴尬困境&#xff1a;标注数据太少。传统深度学习方法动辄需要成千上万的标注样本&#xff0c;而实…...

终极Node.js Mock工具:Mockery入门到精通实战教程

终极Node.js Mock工具&#xff1a;Mockery入门到精通实战教程 【免费下载链接】mockery Simplifying the use of mocks with Node.js 项目地址: https://gitcode.com/gh_mirrors/mock/mockery Mockery是Node.js生态中简化Mock使用的终极工具&#xff0c;它为开发者提供了…...

通过Taotoken实现Hermes Agent自定义模型供应商接入

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 通过Taotoken实现Hermes Agent自定义模型供应商接入 Hermes Agent是一个流行的AI智能体开发框架&#xff0c;它支持通过配置自定义…...

Autodesk Fusion 360在Linux上的技术实现与性能优化深度解析

Autodesk Fusion 360在Linux上的技术实现与性能优化深度解析 【免费下载链接】Autodesk-Fusion-360-for-Linux This is a project, where I give you a way to use Autodesk Fusion 360 on Linux! 项目地址: https://gitcode.com/gh_mirrors/au/Autodesk-Fusion-360-for-Linu…...

大厂校招变了:AI 能力正在进入笔试和面试

最近不少同学投递校招时&#xff0c;应该已经发现一个变化&#xff1a; 以前 JD 里写的是“熟悉 Python / Java / SQL / Office 优先”。 现在越来越多岗位开始出现新的描述&#xff1a; “熟练使用 AI 工具者优先” “了解大模型应用者优先” “具备 AI 辅助编程经验优先” “…...

【C++】零基础入门 · 第 6 节:数组

上一节我们学习了函数,知道了如何把代码封装起来方便复用。但在实际编程中,你很快就会遇到一个问题:如果要存储 100 个学生的成绩,难道要定义 100 个变量吗?这显然不现实。数组就是 C++ 给出的答案——它让我们能用一个变量名管理一组相同类型的数据。 1. 为什么需要数组…...