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

news2024/11/23 13:48:05

因为自己写业务要定制各种 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根据路径里传参数高亮显示关键字(跳转到对应页面)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python进阶笔记】md文档笔记第6篇:Python进程和多线程使用(图文和代码)

本文从14大模块展示了python高级用的应用。分别有Linux命令&#xff0c;多任务编程、网络编程、Http协议和静态Web编程、htmlcss、JavaScript、jQuery、MySql数据库的各种用法、python的闭包和装饰器、mini-web框架、正则表达式等相关文章的详细讲述。 全套md格式笔记和代码自…

2015年全国硕士研究生入学统一考试管理类专业学位联考数学试题——解析版

文章目录 2015 级考研管理类联考数学真题一、问题求解&#xff08;本大题共 15 小题&#xff0c;每小题 3 分&#xff0c;共 45 分&#xff09;下列每题给出 5 个选项中&#xff0c;只有一个是符合要求的&#xff0c;请在答题卡上将所选择的字母涂黑。真题&#xff08;2015-01&…

白嫖CTG4.0

大家好&#xff0c;到点了我来给各位大佬献策CTG&#xff0c;不是花钱买不起&#xff0c;而是免费更有性价比&#xff0c;哈哈哈不调侃了我们自此开始正文&#xff0c;咱们主打的就是一个分享是一种态度 当然我更希望大家支持国产对国产有自己的信心&#xff08;文心一言&…

Ubuntu18.04安装A-Loam保姆级教程

系统环境&#xff1a;Ubuntu18.04.6 LTS 1.A-Loam的安装前要求&#xff1a; 1.1 ROS安装&#xff1a;参考我的另一篇博客 Ubuntu18.04安装ROS-melodic保姆级教程_灬杨三岁灬的博客-CSDN博客还是那句话&#xff0c;有时候加了这行也不好使&#xff0c;我是疯狂试了20次&#…

用XMind2TestCase,测试更轻松

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

(Java版)新一代最强开源UI自动化测试神器Playwright(模拟移动设备和浏览器行为)

使用 Playwright&#xff0c;你可以在任何浏览器上测试你的应用程序&#xff0c;也可以模拟真实设备&#xff0c;例如手机或平板电脑。只需配置你想要模拟的设备&#xff0c;Playwright 就会模拟浏览器行为&#xff0c;例如“userAgent”、“screenSize”、“viewport”以及是否…

PCB板材有哪些分类

1、按板材的刚柔程度分为刚性覆铜箔板和挠性覆铜箔板两大类。 2、按增强材料不同&#xff0c;分为&#xff1a;纸基、玻璃布基、复合基&#xff08;CEM系列等&#xff09;和特殊材料基&#xff08;陶瓷、金属基等&#xff09;四大类。 (一)纸基板 酚醛纸基板&#xff08;俗称…

一些在使用Python中常用网页字符串处理方法

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 首先一些Python字符串处理的简易常用的用法。 1.去掉重复空格 s "hello hello hello" s .join(s.split())2.去掉所有回车&#xff08;或其他字…

浅析linux中的信号

人们往往将信号称为“软件中断”&#xff0c;它提供了异步事件的处理机制&#xff0c;这些事件可以来自系统外部&#xff08;如用户按下ctrlc产生中断符&#xff09;&#xff0c;也可能来自程序或者内核内部的执行动作&#xff08;如进程除零操作&#xff09;。进程收到信号&am…

老生常谈 - 从输入URL到页面加载的过程(详细版)

从输入URL到页面加载的过程 之前一直都是直接看一下总结的八股文章&#xff0c;对于实际的整个链路并不是特别熟悉&#xff0c;这次花了一天多的时间看了很多资料&#xff0c;对于整个页面加载的流程有了自己的理解&#xff0c;从前端开始访问的浏览器多线程、缓存等问题&#…

在华为昇腾开发板安装gdal-python

作者:朱金灿 来源:clever101的专栏 为什么大多数人学不会人工智能编程?>>> 在华为昇腾开发板安装gdal-python分为两步:编译gdal库和下载gdal对应的python包。 1.编译gdal库 首先下载gdal库,。在linux(arm架构)上编译的gdal库及其第三方库源码,内含一个编译…

100%纯血鸿蒙来了,Android程序员影响甚大

“纯血鸿蒙”来了&#xff01; 近日&#xff0c;美团、网易、今日头条、钉钉等多家互联网头部企业密集发布对鸿蒙系统相关人才的招聘信息&#xff0c;再度引爆市场对“纯血鸿蒙”的热议。 操作系统之“国货之光” 自2019年8月发布至今&#xff0c;鸿蒙系统已经更新到4.0版本…

为医药数据建起坚不可摧的堡垒——上海迅软DSE终端防泄密的力量!

某医药公司是一家综合性的医药产业公司&#xff0c;具有一定的规模和实力。其经营范围大范围&#xff0c;涉及到多个领域&#xff0c;包括药品、医药器械等的批发零售以及相关的互联网信息服务等。该公司在人员配置和知识产权方面也具备一定的优势&#xff0c;为其在医药产业的…

实验题【网关设置+VRRP+静态路由+OSPF】(H3C模拟器)

嘿&#xff0c;这里是目录&#xff01; ⭐ H3C模拟器资源链接1. 实验示意图2. 要求和考核目标3. 当前配置3.1 PC1、PC2、PC3、PC4和PC5配置3.2 SW配置3.2.1 SW2配置3.2.2 SW3配置3.2.3 SW4配置3.2.4 SW1配置 3.2. R配置3.2.1 R1配置3.2.2 R2配置 ⭐ H3C模拟器资源链接 H3C网络…

SQL常见函数整理 _ LAG() 向上偏移

1. 用法 窗口函数&#xff0c;用于访问窗口中当前行之前的行的数据。该函数可以根据需要计算当前行之前的值&#xff0c;使我们能够轻松地比较不同行之间的差异和变化。 2. 基本语法 LAG(column, offset, default_value) OVER (ORDER BY column)column&#xff1a;代表在返回…

【Django笔记】10大模块md文档第6篇:Django视图、Cookie和session状态、模板和过滤器

Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用&#xff0c;多个组件可以很方便的以"插件"形式服务于整个框架&#xff0c;Django有许多功能强大的第三方插件&#xff0c;你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展…

H5ke12--2--学生选课表格的编辑

方法1不可以修改的用label,如何按了哪一行 就会在下面有个文本显示可编辑的一行 方法2每一行后面都有一个编辑, 3对每一个修改,每一个td失去焦点都会有,直接到达我们服务器 注意 如果用span的每一个html元素都可以自己定义属性 Data-属性名,data-Address links也要给为span 1…

当班主任工资会高一些吗?

很多人在选择职业的时候&#xff0c;都会考虑到工资这一方面。而班主任这个职业&#xff0c;往往被认为是一种高收入的工作。那么&#xff0c;班主任的工资真的会比其他科任老师高吗&#xff1f; 班主任除了教学任务之外&#xff0c;还要负责管理班级的日常事务&#xff0c;包括…

Python入门03变量

目录 1 什么是变量2 变量声明3 变量命名规则4 变量类型5 类型转换总结 1 什么是变量 编程语言中变量就像容器一样&#xff0c;可以用来存放东西 我的变量就像杯子一样&#xff0c;可以用来盛放各种饮料。在Python中变量用来存放各种各样的数据&#xff0c;比如整数、浮点数、…

transformer训练与推理

以下transformer基于中译英任务 数据集原本的目标语言标签dec添加完padding后&#xff0c; 如果在dec前面加一个S构成了dec_input&#xff0c;而如果在dec后面加一个E构成了dec_output(输出的标签) attention 有两种&#xff0c;self attention(encoder里面的是源语言输入句子…