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部分(核心)
核心代码如下:
- 利用 PDF.getDocument获取pdf基础数据
- 通过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多页面处理
- 模板处理id作为唯一标识
<canvas v-for="page in pdfPages" :key="page" :id="`page-${page}`" />
- 修改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版本,安装过程中报错缺少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? Class Variance Authority (CVA) 是一个用于管理 CSS 类名 的工具库,特别适合在 React 或 Vue 等前端框架中使用。它可以帮助你更轻松地处理组件的 样式变体(Variants),比如按钮的不同状态&#…...
自然语言处理:文本分类
介绍 大家好,我这个热衷于分享知识的博主又来啦!之前我们一起深入探讨了自然语言处理领域中非常重要的两个方法:朴素贝叶斯和逻辑斯谛回归。在探索的过程中,我们剖析了朴素贝叶斯如何基于概率原理和特征条件独立假设,…...
刷题记录 HOT100 贪心-2:45. 跳跃游戏 II
题目:45. 跳跃游戏 II 难度:中等 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i j] 处: 0 &l…...
7.2 奇异值分解的基与矩阵
一、奇异值分解 奇异值分解(SVD)是线性代数的高光时刻。 A A A 是一个 m n m\times n mn 的矩阵,可以是方阵或者长方形矩阵,秩为 r r r。我们要对角化 A A A,但并不是把它化成 X − 1 A X X^{-1}A X X−1AX 的形…...
PDFMathTranslate安装使用
PDF全文翻译!!!! PDFMathTranslate安装使用 它是个啥 PDFMathTranslate 可能是一个用于 PDF 文件的数学公式翻译 工具。它可能包含以下功能: 提取 PDF 内的数学公式 将数学公式转换成 LaTeX 代码 翻译数学公式的内…...
STL之list的使用(超详解)
目录 一、list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 iterator的使用 1.2.3capacity(容量相关) 1.2.4 element access(元素访问) 1.2.5 modifiers(链表修改)…...
动态 SQL 的使用
目录 1、< if> 标签2、< trim> 标签3、< where> 标签4、< set> 标签5、< foreach> 标签 1、< if> 标签 < if test“条件语句”> xxxx < /if> 只有当条件语句满足条件,才会拼接 < if> 标签内容,因…...
【如何删除在 Linux 系统中的删除乱码文件】
如何删除在 Linux 系统中的删除乱码文件 1. 列出文件并找到乱码文件:2. 使用通配符(谨慎使用):3. 转义特殊字符:4. 使用 find 命令:5. 使用 inode 号删除文件:6. 图形界面文件管理器:…...
防火墙IPSec (无固定IP地址---一对多)
目录 前言 一、场景: 二、实现 1.拓扑图 2.配置思路 ①基础通信配置 ②PPPoE配置 ③总部的模版IPSec配置 ④分部的IPSec配置 ⑤NAT配置 3.具体配置 ①基础配置 ②详细配置和顺序 效果测试: ③PPPOE ①配置PPPoE ②策略放行 ③IPSec与NA…...
基于SpringBoot的智能问诊系统设计与隐私保护策略
通过SpringBoot框架,我们可以快速搭建一个智能问诊系统,为用户提供便捷的线上医疗服务。然而,在系统设计和实现过程中,如何保障用户的隐私和数据安全,始终是一个亟需关注的问题。本文将探讨基于SpringBoot的智能问诊系…...
DeepSeek进阶应用(一):结合Mermaid绘图(流程图、时序图、类图、状态图、甘特图、饼图)
🌟前言: 在软件开发、项目管理和系统设计等领域,图表是表达复杂信息的有效工具。随着AI助手如DeepSeek的普及,我们现在可以更轻松地创建各种专业图表。 名人说:博观而约取,厚积而薄发。——苏轼《稼说送张琥》 创作者&…...
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数据类型用于存储地理位置信息(如经纬度),并提供高效的地理位置查询功能(如计算两地距离、搜索附近地点等)。其底层基于Sorted Set(有序集合)实现,通过Geohash编码将经纬度…...
vue3 vite或者vue2 百度地图(卫星图)离线使用详细讲解
1、在Windows上下载瓦片,使用的工具为: 全能电子地图下载器3.0最新版(推荐) 下载后解压,然后进入目录"全能电子地图下载器3.0最新版(推荐)\全能电子地图下载器3.0\MapTileDownloader" 在这个目录…...
《Python实战进阶》No17: 数据库连接与 ORM(SQLAlchemy 实战)
No17: 数据库连接与 ORM(SQLAlchemy 实战) 摘要 本文深入探讨SQLAlchemy在复杂场景下的高级应用,涵盖四大核心主题: 会话生命周期管理:通过事件钩子实现事务监控与审计追踪混合继承映射:结合单表/连接表继…...
工程化与框架系列(27)--前端音视频处理
前端音视频处理 🎥 引言 前端音视频处理是现代Web应用中的重要组成部分,涉及音频播放、视频处理、流媒体传输等多个方面。本文将深入探讨前端音视频处理的关键技术和最佳实践,帮助开发者构建高质量的多媒体应用。 音视频技术概述 前端音视…...
芋道打包时报错:缺失@unocss插件
在遇到打包时,报这个错误,提示构建失败是因为 ESLint 在加载 unocss 插件时,找不到 unocss/eslint-plugin 模块 解决办法:安装缺失的依赖:保证unocss/eslint-plugin已经被正确安装, 使用以下命令安装&…...
PY32MD320单片机 QFN32封装,内置多功能三相 NN 型预驱。
PY32MD320单片机是普冉半导体的一款电机专用MCU,芯片采用了高性能的 32 位 ARM Cortex-M0 内核,主要用于电机控制。PY32MD320嵌入高达 64 KB Flash 和 8 KB SRAM 存储器,最高工作频率 48 MHz。PY32MD320单片机的工作温度范围为 -40 ~ 105 ℃&…...
深入解析 configService.addListener 使用中的注意事项
在使用 Nacos 的 configService.addListener 方法进行配置监听时,为了确保程序的稳定性、可靠性以及高效性,有诸多注意事项需要我们关注。下面将对这些关键要点进行详细阐述。 一、连接稳定性 1.1 网络连接问题 Nacos 客户端与服务端通过网络进行通信&…...
四旋翼变形控制:RL与MPC在混合动力学中的对比
1. 四旋翼变形控制的技术挑战与解决方案四旋翼变形控制(Quadrotor Morpho-Transition)是当前机器人领域最具挑战性的前沿技术之一。这项技术使机器人能够在空中完成形态变换,实现从飞行模式到地面模式的平滑切换。想象一下,一架四…...
告别命令行!用Python脚本批量管理Docker容器,效率提升不止一点点
告别命令行!用Python脚本批量管理Docker容器,效率提升不止一点点每次在终端敲入docker ps、docker stop、docker rm时,你是否想过——当容器数量超过两位数,这种重复劳动是否在消耗你的生命?去年我们团队在迁移微服务架…...
我靠这个测试设计方法,把漏测率降低了80%
当“直觉测试”撞上南墙很长一段时间里,我和许多测试同行一样,测试用例的设计主要依靠两样东西:需求文档和“测试直觉”。这种模式在业务逻辑相对简单、迭代速度平缓时还能勉强应付。一旦面对复杂的企业级应用、高频的敏捷迭代,或…...
【MySQL数据库 | 第一篇】 概述
数据库相关概念: 数据库(Database):数据库是指一组有组织的数据的集合,通过计算机程序进行管理和访问。数据库管理系统:操纵和管理数据库的大型软件SQL:操作关系型数据库的编程语言,定义了一套操作关系型数…...
告别数据饥荒:用PyTorch手把手实现原型网络(Prototypical Networks)做电影评论情感分类
告别数据饥荒:用PyTorch手把手实现原型网络做电影评论情感分类 在自然语言处理领域,情感分析一直是热门研究方向,但现实中的开发者常面临一个尴尬困境:标注数据太少。传统深度学习方法动辄需要成千上万的标注样本,而实…...
终极Node.js Mock工具:Mockery入门到精通实战教程
终极Node.js Mock工具:Mockery入门到精通实战教程 【免费下载链接】mockery Simplifying the use of mocks with Node.js 项目地址: https://gitcode.com/gh_mirrors/mock/mockery Mockery是Node.js生态中简化Mock使用的终极工具,它为开发者提供了…...
通过Taotoken实现Hermes Agent自定义模型供应商接入
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 通过Taotoken实现Hermes Agent自定义模型供应商接入 Hermes Agent是一个流行的AI智能体开发框架,它支持通过配置自定义…...
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 能力正在进入笔试和面试
最近不少同学投递校招时,应该已经发现一个变化: 以前 JD 里写的是“熟悉 Python / Java / SQL / Office 优先”。 现在越来越多岗位开始出现新的描述: “熟练使用 AI 工具者优先” “了解大模型应用者优先” “具备 AI 辅助编程经验优先” “…...
【C++】零基础入门 · 第 6 节:数组
上一节我们学习了函数,知道了如何把代码封装起来方便复用。但在实际编程中,你很快就会遇到一个问题:如果要存储 100 个学生的成绩,难道要定义 100 个变量吗?这显然不现实。数组就是 C++ 给出的答案——它让我们能用一个变量名管理一组相同类型的数据。 1. 为什么需要数组…...
