当前位置: 首页 > 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.jsqmd.com/news/1027017/

相关文章:

  • OpCore Simplify:5步轻松配置黑苹果OpenCore EFI的终极指南
  • 2026年 佛山伸缩门厂家推荐排行榜:电动/手动/铝合金/不锈钢伸缩门,学校与工业园区高性价比品牌精选! - 品牌发掘
  • 2026年工业金属制品供应商甄选:可靠的304不锈钢异形拉伸壳厂家官方推荐 - 优质品牌商家
  • MCU设计到系统验证的高保真、高实时、高可靠系统
  • 根据 MT4 交易账单复刻策略:用 AI Agent 从对账单逆向出可回测的 MT5 EA
  • 《GNSS软件排查,这6个步骤帮你解决90%的定位问题》
  • Java毕设选题推荐:基于 SpringBoot 的计算思维训练与 AI 学习资源平台设计 面向学习者的人工智能知识科普网站设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • VLIW架构与VSPA引擎:从指令级并行的原理到向量处理器的编程实践
  • 波浪补偿控制系统(AHC)原理、设计与工程实践全解析
  • 2026年欧松板厂家综合实力观察:性价比与可靠性谁更胜一筹? - 优质品牌商家
  • 2026年专家访谈服务商如何选?资深从业者亲测推荐这几家 - 优质品牌商家
  • AI热点:超级App集体变身AI Agent,微信生态开放打响第一枪
  • 有交易经验但不会代码,怎么把一个想法拆成信号?
  • 2026年大型不锈钢雕塑生产商:实创不锈钢雕塑实力解析 - 品牌鉴赏官2026
  • 2026无菌冷灌生产线优选指南:高效稳定才是王道
  • 2026年浙江省CPPM考试最新全攻略:科目题型、通过率、备考重点及官方双认证报考机构推荐 - 众智商学院课程中心
  • 2026年近期天津有实力的装饰装修公司选哪家?深度剖析麦田美墅(天津)设计有限公司 - 品牌鉴赏官2026
  • 拒绝吃设定!我用 FastGPT 搭建了一个“网文质检员” Agent,网文作者直呼内行
  • 华硕笔记本终极优化指南:G-Helper轻量级控制工具完全教程
  • 宿迁房屋渗漏水检测维修、卫生间漏水免砸砖维修、漏水点精准检测、厨房漏水防水补漏、正规防水补漏公司、口碑榜TOP5靠谱推荐、本地人必选的防水维修公司 - 安佳防水
  • WSA-Script终极指南:在Windows 11上轻松安装完整Android子系统
  • 2026绵阳灭白蚁公司官方甄选指南:本地服务商综合评测与推荐 - 优质品牌商家
  • 2026年甄选评测:高评价变频串联谐振试验装置制造厂推荐指南 - 优质品牌商家
  • P4080DS USDPAA配置实战:DPAA硬件加速与Linux网络协同架构解析
  • 东莞工业吸尘机生产厂家2025年度十大品牌排行榜 - 工业清洁测评社
  • 2026年围挡租赁施工品牌甄选:专业、可靠与高性价比如何兼得? - 优质品牌商家
  • 大模型对抗攻击与鲁棒性防御深度解析:从梯度对抗样本到认证鲁棒性的攻防实战
  • 巨有科技|不止打卡,智慧服务如何重塑游客游览体验
  • 新桥街道专业的空调拆装服务商推荐排行 - 品牌排行榜
  • 默认参数的陷阱,每个Python新手都踩过