news 2026/6/17 2:21:10

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
文海问津创新实训项目记录(八)

项目已经做的差不多了,最后收尾阶段大家都在对用户界面进行优化以及最后的部署和测试,我在测试的时候发现了一些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。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 2:20:38

多标签分类实战指南:从原理、评估到工程落地

1. 多标签分类不是“多选题”&#xff0c;而是现实世界的自然表达你有没有遇到过这样的场景&#xff1a;给一张照片打标签&#xff0c;它既是“海滩”&#xff0c;又是“日落”&#xff0c;还是“情侣”&#xff1b;读一篇技术文章&#xff0c;它同时属于“Python”、“机器学习…

作者头像 李华
网站建设 2026/6/17 2:15:00

嵌入式Linux安全自动化:Vigiles工具集成与CVE漏洞管理实践

1. 项目概述&#xff1a;嵌入式安全的“隐形守护者”在嵌入式Linux开发这个行当里摸爬滚打了十几年&#xff0c;我见过太多项目在临近交付时&#xff0c;因为一个突如其来的安全漏洞警报而手忙脚乱。开发团队往往把精力都放在了功能实现和性能优化上&#xff0c;却对软件供应链…

作者头像 李华
网站建设 2026/6/17 1:53:48

如何让重要网页永不消失?网页时光机浏览器扩展揭秘

如何让重要网页永不消失&#xff1f;网页时光机浏览器扩展揭秘 【免费下载链接】wayback-machine-webextension A web browser extension for Chrome, Firefox, Edge, and Safari 14. 项目地址: https://gitcode.com/gh_mirrors/wa/wayback-machine-webextension 你是否…

作者头像 李华
网站建设 2026/6/17 1:53:10

2026年GEO公司测评:五大服务商能力对比,为什么首推虎博科技?

生成式AI正在重构企业获客入口。用户不再只通过搜索引擎查找品牌&#xff0c;而是越来越多地通过DeepSeek、豆包、文心一言、Kimi、ChatGPT、Gemini、Perplexity等AI工具直接提问。企业能否被AI准确识别、稳定引用、优先推荐&#xff0c;正在成为新的品牌竞争力。因此&#xff…

作者头像 李华
网站建设 2026/6/17 1:50:02

终极指南:让老款Mac重获新生,免费升级到最新macOS系统

终极指南&#xff1a;让老款Mac重获新生&#xff0c;免费升级到最新macOS系统 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 你是否还在为苹果官方放弃支持的…

作者头像 李华
网站建设 2026/6/17 1:47:01

HYMEM:图结构混合自进化GUI代理记忆系统解析

1. HYMEM&#xff1a;图结构混合自进化GUI代理记忆系统解析 在人工智能代理领域&#xff0c;GUI代理面临着长期任务执行中的记忆管理挑战。传统方法要么将交互轨迹压缩为离散的文本摘要丢失视觉细节&#xff0c;要么存储为连续的嵌入向量缺乏结构化组织。这两种方式都难以模拟人…

作者头像 李华