2023 最新 PDF.js 在 Vue3 中的使用

因为自己写业务要定制各种 pdf 预览情况(可能),所以采用了 pdf.js 而不是各种第三方封装库,主要还是为了更好的自由度。

一、PDF.js 介绍

官方地址
中文文档

PDF.js 是一个使用 HTML5 构建的便携式文档格式查看器。
pdf.js 是社区驱动的,并由 Mozilla 支持。我们的目标是为解析和呈现 PDF 创建一个通用的、基于 Web 标准的平台。

二、 安装方法

1、下载 pdf.js

下载地址
在这里插入图片描述
我下载的版本是 pdfjs-4.0.189-dist
在这里插入图片描述

2、解压包并放到项目中

解压后将完整文件夹放到 vue3 的 public 文件夹内
在这里插入图片描述

3、屏蔽跨域错误,允许跨域

web/viewer.mjs 内找到搜索 throw new Error("file origin does not match viewer's") 并注释掉,如果不注释,可能会出现跨域错误,无法正常预览文件
在这里插入图片描述
这样就算安装完成了,后面我们开始在项目中使用。

三、基础使用

1、创建 PDF 组件

我们可以创建一个 PDF 组件,代码如下:

<script setup lang="ts">
import { onMounted, ref } from 'vue';
interface Props {
  url: string; // pdf文件地址
}
const props = defineProps<Props>();
const pdfUrl = ref(''); // pdf文件地址
const fileUrl = '/pdfjs-4.0.189-dist/web/viewer.html?file='; // pdfjs文件地址

onMounted(() => {
  // encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。
  // 核心就是将 iframe 的 src 属性设置为 pdfjs 的地址,然后将 pdf 文件的地址作为参数传递给 pdfjs
  // 例如:http://localhost:8080/pdfjs-4.0.189-dist/web/viewer.html?file=http%3A%2F%2Flocalhost%3A8080%2Fpdf%2Ftest.pdf
  pdfUrl.value = fileUrl + encodeURIComponent(props.url);
});
</script>

<template>
  <div class="container">
    <iframe :src="pdfUrl" width="100%" height="100%"></iframe>
  </div>
</template>

<style scoped lang="scss">
.container {
  width: 100%;
  height: 100%;
}
</style>

2、使用组件

比如我们需要预览 public 下的一个 test.pdf 文件

<div class="pdf-box">
  <PDF url="/public/test.pdf" />
</div>

下面是界面默认预览效果
在这里插入图片描述

四、进阶使用

1、页面跳转(传参)

比如我们要跳到第 10 页,我们可以在地址里面添加参数 &page=${10}

pdfUrl.value = fileUrl + encodeURIComponent(props.url) + `&page=${10}`;

viewer.mjs 找到 setInitialView 函数,注意是下面这个:
在这里插入图片描述
重点:在函数末尾最下面添加下面的跳转代码(写在上面会报错,因为还没有获取到实例)

    console.log(this.pdfViewer);
    // 获取url参数
    function getQueryVariable(variable) {
      var query = window.location.search.substring(1);
      var vars = query.split('&');
      for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        if (pair[0] == variable) {
          return pair[1];
        }
      }
      return false;
    }
    // 跳转到指定页
    const page = getQueryVariable('page');
    console.log(page);
    if (page) {
      this.pdfViewer.currentPageNumber = Number(page);
    }

2、文本标注(传参)

某些时候我们需要跳转到指定页面,然后自动标注文本,这个时候就需要自动标注了
在这里插入图片描述
代码跟跳转一样,写在后面就可以了

    // 自动高亮文本(要解码)decodeURIComponent: 解码
    const markText = decodeURIComponent(getQueryVariable('markText'));
    console.log('markText===>', markText);
    if (markText) {
      // 对查询输入框进行赋值
      document.getElementById('findInput').value = markText;
      // 点击高亮按钮实现高亮显示关键词
      document.getElementById('findHighlightAll').click();
    }

目前我还没有找到批量标注的办法,批量标注建议还是使用下面页面+坐标,遮罩的方法
在这里插入图片描述

3、添加遮罩高亮(页码+坐标)

主要是为了解决批量标注的问题,因为 pdfjs 原生只支持单文本,不支持批量,要修改大量源码(我能力不行,太难了😥)

所以还是换了种方案,就是后端返回页码+坐标,添加遮罩层渲染的方式。

这种方法主要是找到渲染的 dom元素,因为渲染的pdf有一个叫做 data-page-number="1" 的属性,因此我们可以通过 js 的 querySelectorAll 选择器找到对应属性的 dom 元素,然后再操作添加遮罩就可以了,代码放在下面。
在这里插入图片描述

    // 测试的坐标
    const content_pos_1 = {
      x: 0.5135954145019941,
      y: 0.4662730487881233,
    };
    const content_pos_2 = {
      x: 0.7135954145019941,
      y: 0.8662730487881233,
    };
    // 查找属性 data-page-number='页码' 的 dom 元素
    const pageList = document.querySelectorAll(`[data-page-number='${page}']`);
    console.log('查询到的dom列表===>\n', pageList[1]);
    // 查询到的第一个是左侧小菜单页码div,第二个是才是展示的div
    const pageView = pageList[1];
    console.log('右侧展示的dom===>\n', pageView);
    // 在元素上画一个div
    const div = document.createElement('div');
    div.style.width = (content_pos_2.x - content_pos_1.x) * 100 + '%';
    div.style.height = (content_pos_2.y - content_pos_1.y) * 100 + '%';
    div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';
    div.style.position = 'absolute';
    div.style.top = content_pos_1.y * 100 + '%';
    div.style.left = content_pos_1.x * 100 + '%';
    div.style.zIndex = '1'; // pdfjs 文本的层级是2 所以这里要设置为1 放着不能复制
    pageView.appendChild(div);

渲染到pdf上就是下面的样子:
在这里插入图片描述

4、添加遮罩高亮(缩放动态更新)

因为 pdf 会缩放的缘故,缩放的话会重新更新 pdf ,我们添加的 div 就会消失,所以我们要在重新更新的时候重新添加,源码内部重新添加的函数在这个位置: #updateUIState
在这里插入图片描述
我们只需要将修改后重新添加的代码放在尾部就行
首先我们要修改第三部分的代码

   // 测试的坐标
    const content_pos_1 = {
      x: 0.5135954145019941,
      y: 0.4662730487881233,
    };
    const content_pos_2 = {
      x: 0.7135954145019941,
      y: 0.8662730487881233,
    };
    // pdf 缩放会重新设置,所以放在window保存,其他地方要用
    window.page = page;
    window.shade = {
      width: (content_pos_2.x - content_pos_1.x) * 100 + '%',
      height: (content_pos_2.y - content_pos_1.y) * 100 + '%',
      top: content_pos_1.y * 100 + '%',
      left: content_pos_1.x * 100 + '%',
    };
    console.log(window.shade);
    // 查找属性 data-page-number='页码' 的 dom 元素
    const pageList = document.querySelectorAll(`[data-page-number='${page}']`);
    console.log('查询到的dom列表===>\n', pageList[1]);
    // 查询到的第一个是左侧小菜单页码div,第二个是才是展示的div
    const pageView = pageList[1];
    console.log('右侧展示的dom===>\n', pageView);
    // 在元素上画一个div
    const div = document.createElement('div');
    div.id = 'shade';
    div.style.width = window.shade.width;
    div.style.height = window.shade.height;
    div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';
    div.style.position = 'absolute';
    div.style.top = window.shade.top;
    div.style.left = window.shade.left;
    div.style.zIndex = '1';
    pageView.appendChild(div);

然后在 #updateUIState 函数的末尾添加下面的新增代码

    setTimeout(() => {
      if (!window.page) return;
      const pageList = document.querySelectorAll(`[data-page-number='${window.page}']`);
      const pageView = pageList[1];
      // 删除 id 为 shade 的元素(旧遮罩)
      const shade = document.getElementById('shade');
      if (shade) {
        shade.remove();
      }
      const div = document.createElement('div');
      div.id = 'shade';
      div.style.width = window.shade.width;
      div.style.height = window.shade.height;
      div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';
      div.style.position = 'absolute';
 	  div.style.top = window.shade.top;
      div.style.left = window.shade.left;
      div.style.zIndex = '1';
      pageView.appendChild(div);
    }, 500);

最终效果如下:
在这里插入图片描述
ps:如果要做大量的页面+坐标渲染(后端返回的是个数组),修改下上面的代码逻辑就行,传参自己写,不难的

当然,也可以看下面的代码哈哈哈,我还是写出来吧

5、添加遮罩高亮(数组批量跨页渲染)

假设后端返回的数据格式是这样的,是一个包含 页码、坐标的标注数组,我们需要在每个页码内渲染遮罩
在这里插入图片描述

我们就需要这样传参
在这里插入图片描述
setInitialView(storedHash, { rotation, sidebarView, scrollMode, spreadMode } = {}) 初始化函数中:

    window.content_pos = JSON.parse(decodeURIComponent(getQueryVariable('content_pos')));
    console.log(window.content_pos[0]);
    window.content_pos.forEach((item, index) => {
      const page = item.page_no;
      const shade = {
        width: (item.right_bottom.x - item.left_top.x) * 100 + '%',
        height: (item.right_bottom.y - item.left_top.y) * 100 + '%',
        top: item.left_top.y * 100 + '%',
        left: item.left_top.x * 100 + '%',
      };
      console.log(shade);
      const pageList = document.querySelectorAll(`[data-page-number='${page}']`);
      const pageView = pageList[1];
      const div = document.createElement('div');
      div.id = 'shade' + index;
      div.style.width = shade.width;
      div.style.height = shade.height;
      div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';
      div.style.position = 'absolute';
      div.style.top = shade.top;
      div.style.left = shade.left;
      div.style.zIndex = '1';
      pageView.appendChild(div);
    });

#updateUIState(resetNumPages = false) 更新函数中:

    setTimeout(() => {
      if (window.content_pos) {
        window.content_pos.forEach((item, index) => {
          const shadeEl = document.getElementById('shade' + index);
          if (shadeEl) {
            shadeEl.remove();
          }
          const page = item.page_no;
          const shade = {
            width: (item.right_bottom.x - item.left_top.x) * 100 + '%',
            height: (item.right_bottom.y - item.left_top.y) * 100 + '%',
            top: item.left_top.y * 100 + '%',
            left: item.left_top.x * 100 + '%',
          };
          const pageList = document.querySelectorAll(`[data-page-number='${page}']`);
          const pageView = pageList[1];
          const div = document.createElement('div');
          div.id = 'shade' + index;
          div.style.width = shade.width;
          div.style.height = shade.height;
          div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';
          div.style.position = 'absolute';
          div.style.top = shade.top;
          div.style.left = shade.left;
          div.style.zIndex = '1';
          pageView.appendChild(div);
        });
      }
    }, 500);

效果展示,可以实现跨页,多页渲染
在这里插入图片描述

后续根据开发业务持续更新😁

感谢大佬们的无私分享

详细|vue中使用PDF.js预览文件实践
vue3项目使用pdf.js插件实现:搜索高亮、修改pdf.js显示的页码、向pdf.js传值、控制搜索、处理接口文件流
pdf.js根据路径里传参数高亮显示关键字(跳转到对应页面)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/188584.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文献速递:人工智能在新生儿重症监护室:现在是时候了

人工智能在新生儿重症监护室&#xff1a;现在是时候了 01 文献速递介绍 文章介绍了AI的多学科特性&#xff0c;包括计算机科学、数学、神经科学和哲学。AI的目标是通过各种计算和算法技术创建智能机器。尽管早期对人类水平AI的预测未能实现&#xff0c;但对AI的期待仍然强烈…

PC端从零搭建微信自动回复机器人(一)基础框架搭建及源码

由于工作需要&#xff0c;最近一年一直在研究和使用C#&#xff0c;加上最近工作上有做微信机器人的需要&#xff0c;在已经对接、调试稳定之后&#xff0c;将项目的源码分享给大家&#xff0c;传递开源精神。 一、环境依赖 1、开发工具&#xff1a;Vistual Studio 2022 2、Ne…

java学习part11继承

1.类的继承 继承为了让类之间建立联系&#xff0c;同时复用代码。 子类和父类的同名函数构成重写&#xff0c;能覆盖&#xff0c;除非用super.xx()调。 同名属性不会覆盖&#xff0c;而是并存&#xff0c;用super.xx调。 2.子类初始化 子类会自动调用父类无参构造super() 3.重…

RuntimeError: CUDA error: device-side assert triggered

背景&#xff1a; 使用SAGEConv卷积层的图神经网络&#xff0c;网络架构如下 原因&#xff1a; 我在卷积层之前改变了特征矩阵的维度&#xff0c;原本为[172,1,32] 现在改为了 [172,2,32]。导致了特征矩阵x在进行 “x x.squeeze(1)” 操作时并没有将第二向量值去除&#xff08…

国标交流充电桩接口和直流充电桩接口介绍

1、背景 与传统油车相比&#xff0c;纯电车有太多的优势&#xff0c;但是纯电需要考虑充电时间的长短以及电池的使用寿命。然而相比较而言&#xff0c;混动有好多的备选方案比如插电式、增程式等&#xff0c;除了满足比电车较远的续航外&#xff0c;充电等待时间大大缩短。 在…

ElasticSearch01

ElasticSearch 版本&#xff1a;7.8 学习视频&#xff1a;尚硅谷 笔记&#xff1a;https://zgtsky.top/ ElasticSearch介绍 Elaticsearch&#xff0c;简称为es&#xff0c; es是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据&#xff1b…

C++实现十大排序算法

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

基于Flutter的图片浏览器的实现

一 效果展示&#xff1a; 1. 图片展示&#xff1a; 2.混色&#xff0c;平铺&#xff0c;拉伸&#xff0c;原图展示 二 实验准备&#xff1a; 1.在包结构中创建images包来存放我们用到的图片&#xff0c;在pubspec.yaml中声明路径&#xff1a; 2. 检查虚拟机是否正常运行&…

数字孪生智慧校园 Web 3D 可视化监测

当今&#xff0c;智慧校园发展阶段亟需推动信息可视化建设与发展&#xff0c;将大数据、云计算、可视化等高新技术相融合&#xff0c;为校园师生创造科学智能的学习环境&#xff0c;并实现教学资源最大化和信息服务智能化。帮助学校更好地应用校园可视化技术&#xff0c;提升校…

rfc4301- IP 安全架构

1. 引言 1.1. 文档内容摘要 本文档规定了符合IPsec标准的系统的基本架构。它描述了如何为IP层的流量提供一组安全服务&#xff0c;同时适用于IPv4 [Pos81a] 和 IPv6 [DH98] 环境。本文档描述了实现IPsec的系统的要求&#xff0c;这些系统的基本元素以及如何将这些元素结合起来…

Jensen不等式

如果是正数&#xff0c;并且它们的和等于1&#xff0c;f是凸函数&#xff0c;那么&#xff1a; 也可表述为&#xff1a; 即x期望的凸函数值小于等于x凸函数值的期望

【数据库】聊聊一颗B+树 可以存储多少数据

我们知道数据库使用的数据结构是B树&#xff0c;但是B树可以存储多少数据呢&#xff0c;在面试中也是经常会问的问题&#xff0c;所以我们从根上理解这个问题。 操作系统层面 数据都是存储在磁盘中的&#xff0c;而磁盘中的数据都是以最新单位扇区进行分割。一个扇区的大小是…

STM32 CAN协议讲解以及代码

STM32 CAN 文章目录 STM32 CAN前言一、CAN外设1.主控制寄存器CAN_MCR2.位时序寄存器CAN_BTR3.CAN的发送邮箱4.CAN的接收FIFO5.验收筛选器 二、代码配置1.初始化2.发送数据3.接收数据4.main.c 前言 前面学习了CAN的一些理论知识&#xff0c;他在我们的STM32里面是怎么用的呢 前…

vue3+elementPlus之侧边菜单栏功能

选择默认的颜色&#xff0c;将代码拷贝至<el-aside>模块中 稍微把不需要的修改一下。 <template><div class"common-layout"><el-container><el-header class"homeHeader"><div class"headerTitle">Devops…

2023年【安全员-C证】考试试卷及安全员-C证试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全员-C证考试试卷是安全生产模拟考试一点通生成的&#xff0c;安全员-C证证模拟考试题库是根据安全员-C证最新版教材汇编出安全员-C证仿真模拟考试。2023年【安全员-C证】考试试卷及安全员-C证试题及解析 1、【多选…

基于uQRCode封装的Vue3二维码生成插件

标题&#xff1a;基于uQRCode封装的Vue3二维码生成插件 摘要&#xff1a;本文介绍了一种基于uQRCode封装的Vue3二维码生成插件&#xff0c;可以在Javascript运行环境下生成二维码并返回图片地址。该插件适用于所有Javascript运行环境&#xff0c;并且支持微信小程序。本文将详…

springboot+vue项目如何集成onlyoffice开源文档组件

一、onlyoffice是什么 ONLYOFFICE 是一个开源的办公套件&#xff0c;适合多人在线协作。由总部位于总部在拉脱维亚的 IT 公司Acensio System SIA 开发。它提供在线协作文档编辑器&#xff08;包括文档、电子表格、演示文稿和表单&#xff09;&#xff0c;适用于 Windows、Linu…

Jenkins用126邮箱发邮件为什么发不出去

1、检查 Jenkins Location中的邮件地址配置与发邮件的地址配置是否一致 Manage Jenkins -》 system 2、检查地址和端口号 3、检查邮箱的登录配置是否正确&#xff08;这个地方的配置方式网上一抓一大把&#xff0c;自己搜一下就好&#xff09; 4、126邮箱发邮件不需要勾选ssl协…

QXDM Filter使用指南

QXDM Filter使用指南 1. QXDM简介2 如何制作和导入Filter2.1 制作Filter2.1.1 制作Windows环境下Filter2.1.2 制作Linux环境下Filter 2.2 Windows环境下导入Filter 3 Filter配置3.1 注册拨号问题3.1.1 LOG Packets(OTA)3.1.2 LOG Packets3.1.3 Event Reports3.1.4 Message Pack…

033.Python面向对象_类补充_生命周期

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…