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

文章讲述了如何使用PDF.js库解析PDF文件,解决PDF页面中文字和热区的坐标转换问题,确保在初始化时自动初始化热区,优化用户体验。
摘要由CSDN通过智能技术生成

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

       用户向网站上传一个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效果图:

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半吊子伯爵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值