关于pdf.js中文本坐标尺寸的使用

news/2024/5/20 9:10:45

        一个电子教材项目中有这样一个需求:

       用户向网站上传一个PDF书籍后,网站可以对PDF书籍进行解析,并支持用户对PDF书籍的每一页做一些操作,比如:为英语课本的单词和句子添加音频热区。因为热区数量很多,所以,希望网站 “在初始化课本页面的时候,自动初始化热区,然后用户再在此基础上调整”,这样可以大大减少工作量。

        我使用pdf.js来实现该功能,该库可以获取到pdf中的文本及位置、宽高,但这些位置尺寸使用起来有几处值得注意的细节(稍不注意,可能会被卡很久)。

一、pdf.js提供的文本信息

        如图所示,这是一个PDF页面中获取到的文本信息。这里要用到的字段有:height(高)、width(宽)、transform(这是组matrix矩阵数据,其中最末两位分别是水平方向和垂直方向的位置信息)。

二、transform数据对应的坐标系

        1)初始坐标数据

        通常,我们定位一个元素时,会设置它的left和top,left的数值从左向右递增,top的数值自上而下递增;而transform中的垂直方向数值是从下往上递增的。(下图是坐标系不同导致的错误结果,这个结果是多种原因造成的,后续我们逐一修正)

        2)矫正坐标数据(垂直方向翻转)

        基于上一步,首先,先来矫正坐标系。我们将垂直方向的数值进行矫正:

新值 = pdf页面高度 - 旧值。

        再次渲染后,会发现垂直方向的坐标系已经对了。但仍有两个问题:一个是横纵方向的定位都存在偏差,另一个是热区的宽高比实际文本所占空间大。这主要是因为“绘制pdf的canvas画布的width、height”和“canvas画布在页面布局中被定义的样式style中的width、height”不一致,二者存在比例换算。

三、数据换算

        1)矫正比例换算

        基于上一步,结合canvas的内外尺寸来矫正热区的宽高和定位:

left = textinfo.transform[4] / canvas.width * canvas.style.width;

top = textinfo.transform[5] / canvas.height * canvas.style.height;

width = textinfo.width / canvas.width * canvas.style.width;

height = textinfo.height / canvas.height * canvas.style.height;

        再次渲染后,会发现水平方向的尺寸、定位已经对了。但垂直方向上的定位仍然存在少许偏差。这个问题很细节,我困扰了好几个小时才发觉:我们已经知道初始时的垂直坐标是自下而上的,那么在垂直翻转时,应该把文本所占的高度也减掉才对。

        2)再次矫正垂直方向数值

新值 = (pdf页面高度 - 旧值 - 文本自身高度) / canvas.height * canvas.style.height。

        修改后再次渲染,可以发现效果已经符合预期了。

四、相关代码片段展示

initHotspots() {let pdfDoc = this.loadingTaskDict[this.pageActiveIndex] || this.loadingTask;if (!pdfDoc) return;pdfDoc.promise.then((pdf) => {let pageIndex = this.loadingTaskDict[this.pageActiveIndex] ? 1 : this.pageActiveIndex;pdf.getPage(pageIndex).then((page) => {let view = page.view || [];let pdfPageWidth = view[2] - view[0]; // pdf页面宽度(canvas.width)let pdfPageHeight = view[3] - view[1]; // pdf页面高度(canvas.height)page.getTextContent().then((textInfo) => {textInfo = textInfo || {};let textItems = textInfo.items || [];// bookPageDom是网页中pdf页面的包裹元素(bookPageInfo.width相当于canvas.style.width)let bookPageDom = document.querySelector('.book-page');let bookPageInfo = bookPageDom ? bookPageDom.getBoundingClientRect() : null;textItems.forEach((v) => {if (/[a-zA-Z]+/i.test(v.str) && v.str.length > 7 && bookPageInfo) {let x = (v.transform[4] / pdfPageWidth) * 100 + '%';let y = ((pdfPageHeight - (v.transform[5] + v.height)) / pdfPageHeight) * 100 + '%';this.addHotpot({top: y,'y%': y,left: x,'x%': x,width: (v.width / pdfPageWidth) * bookPageInfo.width,height: (v.height / pdfPageHeight) * bookPageInfo.height,original: v.str,styles: { left: x, top: y }});}});});});});
},

五、最后

        上方的截图,因为受制于页面布局,课本页面的尺寸比较小,看不清楚。所以,下面是一张demo效果图:


http://www.mrgr.cn/p/48364402

相关文章

【数据库】MongoDB

文章目录 [toc]数据库操作查询数据库切换数据库查询当前数据库删除数据库查询数据库版本 数据集合操作创建数据集合查询数据集合删除数据集合 数据插入插入id重复的数据 数据更新数据更新一条丢失其他字段保留其他字段 数据批量更新 数据删除数据删除一条数据批量删除 数据查询…

Java面向对象03——三大特性之继承

一、继承(extends) 1.1、继承是什么 继承就是 Java 允许我们用 extends 关键字,让一个类与另一个类建立起一种父子关系; 被继承的类称为父类(基类、超类),继承父类的类都称为子类(派生类) ,当子类继承父类后,就可以直接使用父类公共的属性和方法了 当子类继承父类后…

使用 Docker 部署 Nuxt.js 应用程序

来源:https://medium.com/@jkpeyi/deploying-a-nuxt-js-application-with-docker-69bf822c066dWhen developing a Nuxt.js application, it’s essential to be able to deploy it easily and reproducibly. In this article, we will explore how to use Docker to deploy a …

Rust中的函数指针

什么是函数指针 通过函数指针允许我们使用函数作为另一个函数的参数。函数的类型是 fn (使用小写的 ”f” )以免与 Fn 闭包 trait 相混淆。fn 被称为 函数指针(function pointer)。指定参数为函数指针的语法类似于闭包。 函数指…

Rabbitmq系列02---Exchange

个人理解: 交换机的类型划分个人理解是能过routing key来划分的,一是否按routing key找队列;fanout就是不按routingkey找队列,Direct和Topicr按routingkey找队列,只是一个模糊找,一个精准找,而headers不按routingkey 是按消头中的内容找队列。 一、Fanout(订阅模式|广播…

python之List列表

1. 高级数据类型 Python中的数据类型可以分为:数字型(基本数据类型)和非数字型(高级数据类型) 数字型包含:整型int、浮点型float、布尔型bool、复数型complex 非数字型包含:字符串str、列表l…

陈畅亮搞的专利在Windows上利用加解密DLL模块对数据库连接字符串进行加解密

陈畅亮搞的专利在Windows上利用加解密DLL模块对数据库连接字符串进行加解密这种专利权人是公司,个人是发明人,专利年费是申请人先垫付,然后公司报销了,这个专利本身就不属于员工的这个是公司是专利权人, 使用权是公司 , 如果想要维持权利的话 ,需要缴纳年费 ,专利发明…

[最新]CentOS7设置开机自启动Hadoop集群

安装好Hadoop后我们可以使用开机自启动的方式,节约敲命令的时间。注意是centOS7版本!!!和centOS6版本区别非常大!!! 1、切换到系统目录 [rootmaster ~]# cd /etc/systemd [rootmaster systemd]# ll total 32 -rw-r--r-- 1 root root 720 Jun 30 23:11 bootcha…

19-项目干系人管理(10/10 十大管理)

17.1 管理基础 17.1.1 管理的重要性 每个项目干系人,他们会受到项目积极或消极的影响,或者能对项目施加积极或消极的影响。项目经理和团队管理干系人的能力决定着项目的成败。为提高项目成功的概率,尽早开始识别干系人并引导干系人参与。 干系人满意度应作为项目目标加以识别…

基于Springboot的考研资讯平台

基于SpringbootVue的考研资讯平台的设计与实现 开发语言:Java数据库:MySQL技术:SpringbootMybatis工具:IDEA、Maven、Navicat 系统展示 用户登录 首页 考研资讯 报考指南 资料信息 论坛信息 后台登录 考研资讯管理 学生管理 资…

滴水逆向 FileBuffer-ImageBuffer 课后作业

1)- 实现如下功能 #include<stdio.h> #include<stdlib.h> #include<windows.h> BYTE* bufferApply nullptr;//将磁盘文件复制到内存中后, 使用bufferApply指向该空间 DWORD fileSize 0;//将磁盘文件复制到内存时使用需要申请空间, 使用fileSize设置申请空…

17-项目风险管理(8/10 十大管理)

15.1 管理基础 15.1.1 项目风险概述 项目风险是一种不确定的事件或条件,一旦发生,会对项目目标产生某种正面或负面的影响。项目风险既包括对项目目标的威胁,也包括促进项目目标的机会。 风险源于所有项目之中的不确定因素。项目在不同阶段会有不同的风险。风险会随着项目的进…

18-项目采购管理(9/10 十大管理)

16.1 管理基础 16.1.1 协议/采购合同 项目采购管理包括从项目团队外部采购或获取所需产品、服务或成果的各个过程。例如合同、订购单、协议备忘录(MOA)和服务水平协议(SLA)。被授权采购项目所需货物、服务的人员可以是项目团队、管理层或组织采购部的成员。 因应用领域不同…

URL路由基础与Django处理请求的过程分析

1. URL路由基础 对于高质量的Web应用来讲&#xff0c;使用简洁、优雅的URL设计模式非常有必要。Django框架允许设计人员自由地设计URL模式&#xff0c;而不用受到框架本身的约束。对于URL路由来讲&#xff0c;其主要实现了Web服务的入口。用户通过浏览器发送过来的任何请求&am…

15-项目沟通管理(7/10 十大管理)

14.1 管理基础 14.1.1 沟通 沟通是指用各种可能的方式来发送或接收信息。具体形式包括:书面行驶、口头形式、正式或非正式、手势动作、媒体行驶、遣词造句。 14.1.2 沟通模型 关键要素包括:编码:把思想或想法转化为他人能理解的语言 信息和反馈信息 媒介 噪声 解码:把信息还…

与 Apollo 共创生态:Apollo 七周年大会给带来的震撼

文章目录 一、七年蛰伏&#xff0c;Apollo 迎来“智变”时刻二、Apollo 企业生态计划与开放平台2.1 Apollo X 企业自动驾驶解决方案2.2 Apollo 开放平台携手伙伴共创生态 三、个人感悟 一、七年蛰伏&#xff0c;Apollo 迎来“智变”时刻 让我们把时间倒回到 2013 年&#xff0…

13-项目质量管理(5/10 十大管理)

12.1 管理基础 12.1.1 质量与项目质量 1.质量 国际标准定义:反映实体满足主体明确和隐含需求的能力的特性总和。 国家标准定义:一组固有特性满足要求的程度。 质量通常是指产品的质量,广义上的质量还包括工作质量。质量与等级是两个不同的概念。 质量作为实现的性能或成果;…

11-项目进度管理(3/10 十大管理)

10.1 管理基础 10.1.1 项目进度计划的定义和总要求 项目进度计划是一种用于沟通和管理干系人期望的工具,为绩效报告提供依据。 编制进度计划一般步骤:首先选择进度计划方法,例如关键路径法;然后将项目特定数据,如活动、计划日期、持续时间、资源、依赖关系和制约因素等输入…

12-项目成本管理(4/10 十大管理)

11.1 管理基础 11.1.1 重要性和意义 项目成本管理重点关注完成项目活动所需资源的成本,但同时也考虑项目决策对项目产品、服务或成果的使用成本、维护成本和支持成本的影响。项目成本管理应考虑干系人对成本的要求,不同的干系人会在不同的时间,用不同的方法测算项目成本。 对…

ThingsBoard服务端使用RPC通过网关给设备发送消息

一、概述 1、发送服务器端网关RPC 二、案例&#xff1a; 1、建立设备与网关之间的通讯 2、查看设备和网关是否在线状态啊 3、通过 仪表盘&#xff0c;创建设备A的模拟RPC调用的窗口链接 4、在客户端的网关设备上订阅RPC网关的主题信息 5、通过服务端的窗口&#xff0c;发…