当前位置: 首页 > news >正文

文海问津创新实训项目记录(八)

项目已经做的差不多了,最后收尾阶段大家都在对用户界面进行优化以及最后的部署和测试,我在测试的时候发现了一些pdf阅读页的问题,我们的项目毕竟是以论文和agent为核心,这一块的前端必须得优化好,于是有了这篇博客的工作:

很多人第一次做 PDF 阅读页时,都会先觉得这件事“应该不复杂”:

  • 先把 PDF 用 canvas 画出来;
  • 再盖一层透明文字层;
  • 浏览器原生 selection 自然就能工作。

这条路一开始确实能跑。

但只要你开始真的拿它做阅读、划词、引用和翻译,问题就会慢慢冒出来。

这次在 PaperFlow 的论文阅读页里,实际的问题不是“选不了”,而是更烦的一类问题:

  • 同一句话,在小窗和大窗下,高亮宽度不一致;
  • 右边缘会出现一截一截、不贴文本的感觉;
  • 左右边缘的竖排日期、水印也会被选进来;
  • 单纯调整颜色、透明度和圆角后,观感依然不理想。

这说明问题已经不只是“高亮颜色好不好看”,而是文字层本身的几何计算出现了偏差。

1. 这次问题,最后并没有落在 CSS 上

如果只看表象,最容易想到的做法是继续调整:

  • ::selection 的颜色和透明度;
  • 文字层的透明策略;
  • 圆角、阴影等视觉样式。

这些确实能影响观感,但解决不了根本问题:

  • canvas 和文字层是否处于同一套坐标系;
  • 浏览器排版后的文字宽度是否等于 PDF 原始文字宽度;
  • 边缘水印是否被错误加入了可选文本范围。

最后排查下来,问题主要来自三个方面:

  1. 主阅读页在窗口变化时,canvas 和文字层的显示尺寸同步不够稳定;
  2. 手写文字层中的 span 使用浏览器自然排版宽度,与 PDF 原始宽度存在偏差;
  3. 左右边缘的竖排日期、水印也进入了文字层,导致被浏览器当成正常文本选中。

因此这次修复本质上并不是样式优化,而是一次关于坐标、宽度和选区范围的整体修正。

2. 为什么没有直接换回官方 TextLayer

中间其实尝试过几种方案。

第一种是继续调 CSS。

这种方式虽然成本低,但最多只能让问题“看起来不那么明显”,无法真正解决错位。

第二种是重新接回 pdf.js 官方 TextLayer。

官方实现的几何计算更加完整,也更符合标准。

但当前阅读页已经集成了:

  • 选区弹窗(selectionPopover);
  • 引用追加;
  • 翻译功能;
  • AI 对话联动。

如果直接切回完整 Viewer,不仅仅是替换文字层,而是会影响现有整套交互逻辑,风险较大。

最终选择了一条更加可控的路线:

  • 固定逻辑页宽;
  • 将页面拆分为 shell、zoom、content 三层结构;
  • 保留现有手写文字层;
  • 利用 PDF 原始宽度校正文字显示宽度;
  • 过滤左右边缘竖排水印。

虽然不是最标准的方案,但更适合当前项目阶段。

3. 先解决坐标系问题

真正开始修复时,我们并没有直接修改高亮,而是先统一页面内部坐标系。

阅读页引入了固定逻辑页宽:

const PDF_LOGICAL_PAGE_WIDTH = 920;

同时根据逻辑宽度重新计算 viewport 和显示缩放比例。

核心逻辑是:

const PDF_LOGICAL_PAGE_WIDTH = 920;

const logicalScale = PDF_LOGICAL_PAGE_WIDTH / baseViewport.width;

const logicalViewport = page.getViewport({ scale: logicalScale });

const displayZoom = Math.min(1, Math.max(0.4, availableWidth / logicalViewport.width));

这样做的目的并不是固定页面显示大小,而是让:

  • canvas 与文字层共享同一套内部坐标;
  • 窗口变化时只改变显示缩放;
  • 页面内部几何计算保持稳定。

这一步虽然不能彻底解决问题,但为后续所有修复提供了统一基础。

4. 将阅读页拆成三层结构

随后我们重新整理了页面结构。

主阅读区域被拆成:

  • shell
  • zoom
  • content

看起来只是多包了几层 div,但实际意义很大。

以前页面尺寸变化时:

  • 页面布局变化;
  • canvas 尺寸变化;
  • 文字层尺寸变化;

全部同时发生。

现在则变成:

  • content 保存逻辑尺寸;
  • zoom 负责显示缩放;
  • shell 负责页面布局和滚动。

这样内部坐标能够保持稳定,窗口变化带来的影响也被控制在更小范围内。

5. 真正解决问题的是宽度校正

经过前面的调整后,高亮已经比之前稳定许多,但右边缘仍然存在偏差。

进一步排查发现:

问题来自浏览器自然排版宽度。

PDF 中每段文字都有自己的原始宽度信息,但浏览器渲染时:

  • 字体替换;
  • 缩放比例;
  • 排版细节;

都会导致最终显示宽度与 PDF 原始宽度不完全一致。

因此最后采用了宽度校正方案。

简单来说:

  • 读取 PDF 原始文字宽度;
  • 获取当前 DOM 实际宽度;
  • 计算比例;
  • 使用 scaleX 对文字进行微调。

核心逻辑如下:

const expectedWidth = it.width * logicalScale;

const measuredWidth = span.getBoundingClientRect().width;

const scaleX = expectedWidth / measuredWidth;

只有当误差达到一定程度时才会执行校正,并且限制缩放范围,避免出现异常显示。

同时将文字层 span 调整为 block 布局,使宽度计算更加稳定。

这一部分才是真正让高亮重新贴合正文的关键。

6. 过滤左右边缘水印

正文选区修复后,又出现了新的需求:

用户希望 PDF 两侧的日期和水印不要再被选中。

最简单的办法当然是直接过滤所有边缘文本。

但这样会误伤:

  • 页码;
  • 边栏说明;
  • 靠近边缘的正文内容。

因此最终采用了更精细的判断规则。

只有同时满足以下条件的文本才会被过滤:

  • 文本长度达到一定程度;
  • 文本方向接近竖排;
  • 位于页面左右边缘区域。

如下:

const rotation = Math.atan2(b, a);

const isVertical = Math.abs(Math.cos(rotation)) < 0.35;

const edgeThreshold = Math.max(36, input.pageWidth * 0.08);

const isOnLeftEdge = x <= edgeThreshold;

const isOnRightEdge = x >= input.pageWidth - edgeThreshold;

return isOnLeftEdge || isOnRightEdge;

这样可以做到:

  • 水印不再进入选区;
  • 页码仍然保留;
  • 正常正文内容不受影响。

7. 保留现有交互体系

这次还有一个比较重要的取舍。

我们只修文字层,不重写交互层。

因此:

  • selectionPopover 继续保留;
  • 引用功能继续保留;
  • 翻译功能继续保留;
  • AI 对话联动继续保留。

这样可以把问题控制在文字层范围内,避免一次修改牵动整个阅读页架构。

对于仍在快速迭代的项目来说,这种渐进式修复往往更加稳妥。

8. 修复效果验证

完成开发后,我们分别从三个方面进行了验证:

文字层逻辑测试

重点验证:

  • 宽度计算是否正确;
  • 旋转文本判断是否正确;
  • 水印过滤规则是否生效。

页面结构测试

重点验证:

  • 固定逻辑尺寸是否保留;
  • shell、zoom、content 三层结构是否正常工作;
  • 页面缩放后文字层是否仍能保持同步。

选区样式测试

重点验证:

  • 高亮样式是否正确;
  • 透明文字层是否正常;
  • 新增页面结构是否影响选区渲染。

最终验收主要关注两个实际效果:

  • 同一段文字在不同窗口尺寸下,高亮是否仍然贴合正文;
  • 左右边缘竖排水印是否已经无法被选中。

经过多轮测试后,这两个问题都得到了明显改善。

9. 这次修复中踩过的坑

回头总结,这次最容易踩的几个坑分别是:

第一,把问题误认为纯 CSS 问题。

实际上颜色、透明度和圆角只能改善视觉效果,解决不了文字层几何偏差。

第二,以为固定页面尺寸就能解决所有问题。

如果浏览器排版宽度与 PDF 原始宽度不同,误差仍然会存在。

第三,对边缘文本进行粗暴过滤。

这样虽然能去掉水印,但也容易误伤正常内容。

第四,过早重构整个阅读器体系。

为了修复一个高亮问题而重做整套阅读页架构,风险远远大于收益。

10. 回头看,这次方案最核心的价值不是完美,而是收得住

如果只追求最标准的实现方式,直接接回官方 TextLayer 确实很有吸引力。

但真实项目开发中,很多选择并不是看谁最标准,而是谁在当前阶段最可控。

最终我们收敛出的方案是:

  • 固定逻辑页宽;
  • 分离 shell、zoom、content 三层结构;
  • 保留现有手写文字层;
  • 利用 PDF 原始宽度校正浏览器排版宽度;
  • 精确过滤左右边缘竖排水印。

它并不是一次大规模重构,但解决了用户最关心的问题:

  • 正文高亮更加贴合文本;
  • 不同窗口尺寸下显示更加稳定;
  • 水印不再干扰选区;
  • 原有引用、翻译和 AI 对话功能全部保留。

后续如果继续迭代,我们计划进一步拆分文字层相关逻辑,降低阅读页组件复杂度;等阅读页整体架构更加稳定后,再评估是否重新接入官方 TextLayer。

http://www.rkmt.cn/news/1538895.html

相关文章:

  • 根据 MT4 交易账单复刻策略:用 AI Agent 从对账单逆向出可回测的 MT5 EA
  • 有交易经验但不会代码,怎么把一个想法拆成信号?
  • 2026年专家访谈服务商如何选?资深从业者亲测推荐这几家 - 优质品牌商家
  • 波浪补偿控制系统(AHC)原理、设计与工程实践全解析
  • AI热点:超级App集体变身AI Agent,微信生态开放打响第一枪
  • 2026无菌冷灌生产线优选指南:高效稳定才是王道
  • 2026年浙江省CPPM考试最新全攻略:科目题型、通过率、备考重点及官方双认证报考机构推荐 - 众智商学院课程中心
  • 2026绵阳灭白蚁公司官方甄选指南:本地服务商综合评测与推荐 - 优质品牌商家
  • 宿迁房屋渗漏水检测维修、卫生间漏水免砸砖维修、漏水点精准检测、厨房漏水防水补漏、正规防水补漏公司、口碑榜TOP5靠谱推荐、本地人必选的防水维修公司 - 安佳防水
  • 2026年近期天津有实力的装饰装修公司选哪家?深度剖析麦田美墅(天津)设计有限公司 - 品牌鉴赏官2026
  • 东莞工业吸尘机生产厂家2025年度十大品牌排行榜 - 工业清洁测评社
  • 大模型对抗攻击与鲁棒性防御深度解析:从梯度对抗样本到认证鲁棒性的攻防实战
  • 湖州漏水检测维修权威推荐:卫生间-厨房-阳台-屋顶天花板漏水维修:靠谱防水补漏公司团队TOP5推荐(2026最新深度调研实测榜单) - 即刻修防水
  • 2026年湖南智能搬运设备怎么选?助力机械手、平衡吊供应商深度评测与推荐 - 优质品牌商家
  • 构建生产级大模型API客户端:认证、流式与限流全解析
  • Java+SpringBoot宠物社交系统完整源码解析:前后端分离架构搭建全过程
  • DPAA以太网驱动设备树配置:私有、共享与虚拟模式详解
  • Exchange索引损坏诊断与重建:DAG与独立服务器场景实操指南
  • RyuSAK:一站式Switch模拟器管理工具,轻松打造完美游戏体验
  • 2026年静力切割施工品牌官方甄选:西北地区专业加固公司实力对比 - 优质品牌商家
  • 2026年四川设备房噪音治理服务商甄选参考:技术实力与工程实践解析 - 优质品牌商家
  • 2026年 雨水收集系统/模块/厂家TOP榜单:PP模块、海绵城市与市政道路雨水的专业实力与口碑推荐 - 品牌发掘
  • 清远漏水检测维修权威推荐:卫生间-厨房-阳台-屋顶天花板漏水维修:靠谱防水补漏公司团队TOP5推荐(2026最新深度调研实测榜单) - 即刻修防水
  • K-means与Watershed图像分割实战:无监督、可解释、轻量级方案
  • 构建智能决策辅助系统:从Alpha因子挖掘到实战应用
  • 2026年GEO公司测评:五大服务商能力对比,为什么首推虎博科技?
  • C++ 内存模型详解
  • 云工场科技将携AIoT道路巡查与算力体系,亮相大湾区智慧交通大会
  • 锥形纸管堵头生产厂家
  • 炎症界的 “双面侠”——NLRP3