PDF 文本提取与版面重构(上):多栏排版与阅读顺序 探讨了如何通过字符频次统计、边距降噪以及左侧重叠拓扑排序,将 PDF 混乱的二维画布还原为符合人类阅读顺序的一维文本流。
然而,得到按顺序排列的文本块仅仅完成了第一步。在每个栏目内部,文本依然是由零散的字符或单行文字碎片组成的。
要让程序真正理解这些文字,还必须解决三个微观排版难题:
- 如何把散落的行拼成段落? PDF 里没有段落符号,两行之间的物理距离是由排版软件随意决定的。
- 如何把学术论文中的段落切分成句子? 论文中充斥着缩写、编号和图表说明,常规分句器会将其切得支离破碎。
- 如何把提取出来的句子反向投影回 PDF 页面? 当程序引用某一句话时,阅读器需要知道这句话在 PDF 页面上具体由哪几个坐标矩形构成,以便绘制贴合文字的高亮背景。
本文基于 BibGenie 的工程实践,继续探讨从单行聚合、统计学段落切分,到字符级物理四边形(Quad)坐标映射的实现机制。
一、 浮动字形聚合:自适应行检测
在 PDF 中,同一行文字经常会被打碎。例如带上标的化学式 或带文献引用编号的句子(如 text[1]),上标文字在 Y 轴上会有轻微的向上漂移;此外,段落首字可能存在放大下沉(Drop Cap)。
如果采用固定的 Y 轴坐标阈值(比如要求文字在同一水平线上,误差不能超过 1pt),上标和首字下沉就会被误认为是一行全新的独立文字。
行聚合算法采用了基于字号中位数自适应容差的设计:
首先统计局部文本块中的字号中位数,以此确定垂直公差范围:
1. 垂直聚类与行内碎片吸收
- 自适应公差:对于 24pt 的大标题,允许 4pt 左右的垂直对齐波动;对于 8pt 的注脚,公差自动收缩到 1pt 以内,避免将上下两行注脚粘在一起。
- 重叠行合并:检测上下两行在 Y 轴上的包围盒重叠度。如果一行文字(如上标
[1])的高度远小于主行高,且水平位置紧贴主行,算法将其垂直吸收并入同一视觉行。
2. 水平大间隙断裂
在同一水平行内,字符并不总是连续的。有时候,表格中同一行的两个单元格,或者排版中未被分开的左右两端文字,会共用同一个水平 Y 坐标。
算法通过计算栏内字符宽度的中位数(),设定水平空隙阈值:
当检测到一行内两个相邻文本片段之间的水平空白超过此阈值时,算法在此处产生物理断裂,将其拆分为两行独立的文本单元。
二、 统计学段落切分:中位数绝对偏差(MAD)决策器
拿到按行排列的文本后,算法需要判断:哪些行应该合并为一个自然段,哪一行是新段落的开始?
传统解析器通常设定一个硬编码间距(例如两行间隙超过 5pt 就判定为换行)。但在不同排版下,正文的行间距差异巨大:紧凑排版行距只有 2pt,宽松排版行距可达 8pt。固定数值极其容易误判。
更稳健的方式是利用排版几何统计学中的**中位数绝对偏差(MAD, Median Absolute Deviation)**建立自适应判定模型。
1. 为什么使用 MAD 而非标准差?
在统计一组行的左右边缘位置时,段落首行(缩进)、段落末行(通常只占半行)以及居中标题属于典型的离群点(Outliers)。
普通的平均值(Mean)和标准差(Standard Deviation)对离群点极度敏感。如果一个栏内有几行极短的公式或小标题,平均值会被大幅拉偏。而 MAD 则是寻找与中位数的绝对偏差的中位数,能够抗击高达 的离群数据污染,准确找到该栏正文的“真正边缘”。
算法为每个栏目独立计算以下基准数据:
leftEdgeMode:栏内绝大多数普通行的左对齐基准线。rightEdgeMode:栏内绝大多数普通行右侧顶格的边缘线。medianGap:正常连续行之间的垂直行距中位数。
2. 识别新段落的四个排版特征
对于栏内的任意相邻两行(当前行 与上一行 ),算法检查以下四个特征:
[上一行 L_i-1] ────────────────────────────┐ (右边缘提前结束?)
│
[垂直间距 Gap] ── (超过 medianGap?) │
▼
[当前行 L_i] ───► [首行缩进?] ───────────────
- 行距突变(Gap Excess): 两行之间的物理留白明显大于普通行间距,表明排版上留出了段间距。
- 首行缩进(First-Line Indent): 当前行的起始位置明显向右缩进,符合段落首行缩进特征。
- 上一行提前结束(Early Line End): 上一行的右边缘远远没有达到该栏右侧边界(即上一行末尾留有大面积空白)。由于书籍和论文在段落中间排版时都会尽量将每一行文字排满到右边缘,上一行提前截断意味着作者主动按下了回车换行。
- 字体或字号变化(Style Shift): 当前行与上一行的字号、粗细或字体发生改变。这种情况通常意味着排版从小标题切入正文,或者从正文进入加粗小节,需要作为新段落切开。
只要满足上述特征之一,BibGenie 即判定 开启了一个全新的段落。
3. 多栏独立统计隔离
这个统计模型必须按栏独立计算。
学术论文的正文部分排版通常较为舒展(如行距 12pt),但文章末尾的参考文献(References)通常使用极小的字号和极其致密的行间距(如行距 3pt)。如果采用整页或全文档的统一中位数,密集的参考文献会瞬间拉低全局阈值,导致正文每一行都被误当成间隙超标的独立段落。
三、 跨行断词与连字符还原
在单栏宽度较窄的双栏论文中,为了让右侧边缘对齐,排版软件会频繁使用连字符(Hyphen)将一个较长的英文单词拆成两半跨行显示:
The system performs semantic seg-
mentation on raw visual inputs.
如果直接按行读取,提取出的文本会变成两个破碎片段,破坏词汇的完整性。
合并文本时需要执行连字符展开逻辑:
- 检查上一行末尾字符是否为连字号(
-或 Unicode 软连字符\u00AD)。 - 区分真实复合词与断行连字符:
- 如果上一行末尾为小写字母加
-,且下一行首字母也是小写字母(如seg-与mentation),判定为排版折行。BibGenie 会剔除连字符,将两段无缝拼合为完整单词。 - 如果是本身自带连字符的词汇(如
state-of-the-art恰好在连字号处换行),则保留该符号并紧密连接,避免引入多余空格。
- 如果上一行末尾为小写字母加
四、 学术论文分句:句子范围修正管道
将行拼合成段落后,许多应用场景需要进一步把段落拆解为独立的句子。
如果直接对全文调用标准的分句库(如根据句号 . 切分),学术论文中密集的特殊缩写会导致分句出现大量错误截断。
为了保证分句的准确性,分句必须严格限定在单个段落内部进行。不能跨段落或跨栏通篇切分,防止将小标题与段落第一句强行缝合成一句话。
在段落内部,切出的初步句子区间会进入一条线性执行的后处理清洗管道:
1. 图表标签合并
论文中的图表注记常常以 Figure 1.、Table 2. 开头。标准分句器会将句点识别为句子终止,切分出只有两三个字符的独立碎片。
后处理管道维护一份多语言图表与章节前缀词表(包含英语的 Figure、Table、Section,德语的 Abbildung,以及中文的“图”、“表”等)。当检测到前一个切片的内容满足“前缀词 + 编号 + 句点”,且长度较短时,强制将其与后一个切片合并为一句。
2. 伪装成句号的特殊字体符号清除
许多 PDF 制作工具会将列表项前面的圆点符号(Bullet)用特殊字体的 ASCII . 来渲染。分句器在解析文本时,会把列表项开头的这个点当成前一句话的句号,或者拆成一个只有单个点 "." 的句子。后处理管道会识别并剔除这种孤立的前导符号。
3. 参考文献作者缩写保护
在文末参考文献列表中,作者姓名通常缩写为姓加名字首字母:Vaswani, A., Shazeer, N., Parmar, N. (2017).。
这段文字中包含了大量的句点,但整条参考文献在语义上应当是一个不可分割的引用单元。管道通过检测高密度的“大写字母 + 句点 + 逗号”模式,阻止切片切断作者姓名链。
4. 孤立闭合符号吸附
如果一句话以引号或括号结尾,分句器有时在遇到句点时立即切断,将闭合的双引号 "、单引号 ' 或右括号 ) 留给下一句作为开头。管道检查后一个切片的起始字符,如果发现孤立闭合符号,将其逆向吸附回上一句的尾部。
五、 字符级物理坐标映射:生成高亮矩形
处理完文字后,进入最后一道工序:把切分出的每一个句子,精确对应到 PDF 页面上的几何矩形。
1. 文本字符与物理坐标的一一对应
为了精确知道句子中每个字符落在页面的哪个物理位置,清洗后的文本字符串与底层的物理字符数组必须保持严格的一对一对应:字符串中的第 个字符,直接对应底层数组中的第 个字符对象。
每一个底层字符不仅保存了 Unicode 码点本身,还包含了 MuPDF 结构化文本接口原生输出的 8 浮点数四边形坐标(QuadPoint):
分别代表该字符在页面上的左上、右上、左下、右下四个顶点的精确绝对物理坐标。
在从段落拼合文本到分句清洗的过程中,BibGenie 全程维护一份并行的位置映射索引:文本字符串中的每一个下标,都能直接反查到其来源于哪一行的哪一个具体字符。
2. 跨行句子的多矩形切片生成
真实论文中的句子很少能完全容纳在单行内,绝大多数句子都跨越了 2~3 行。
如果直接对跨行句子的所有字符求一个整体的外接矩形,会得到一个巨大的包围盒,将相邻无关内容也框选进去。正确做法是按行切片投影:
[Line 1] ... [ Start of sentence ─────── ] ===> BoundingBox 1
[Line 2] [ ────── Middle of sentence ──── ] ===> BoundingBox 2
[Line 3] [ End of sentence. ] ... ===> BoundingBox 3
- 通过位置映射索引,查出该句子覆盖的所有行。
- 将句子中的字符按行分组。
- 提取每行内所属字符的顶点坐标,融合成贴合该行文字外观的独立矩形
BoundingBox。 - 最终每个句子对象输出一个由多个 BBox 组成的数组。当阅读器绘制高亮时,每一行文字都能呈现出贴合文本本身的平整矩形框。
3. 坐标系统转换
最后,MuPDF 的输出坐标必须转换为阅读器标准坐标。
MuPDF 采用计算机图形学标准:以左上角为原点 ,Y 轴向下增长。 PDF 阅读器规范(例如 Zotero 等文献阅读器的标注标准)通常采用笛卡尔坐标系:以页面左下角为原点 ,Y 轴向上增长,且伴随页面可能的裁剪偏移与旋转角。
坐标映射公式完成这层翻转变换:
通过这层变换,提取出的句子矩形框便能与阅读器界面无缝对齐。
六、 总结
回顾上下两篇的完整处理流程,从底层图元到最终结构化文本的步骤如下:
[物理图元] MuPDF 字符 Quad 与 矢量线条/底色
│
▼ (上篇:宏观版面重构)
[全局基准] 字符频次统计 ── 识别主正文字号
[物理降噪] 跨页频次与单调递增验证 ── 剥离页眉页码
[阅读流向] 矢量底色阻断与左侧重叠拓扑排序 ── 多栏排序
│
▼ (下篇:微观语义与坐标对齐)
[行级聚合] 字号自适应容差与水平空隙切分
[段落重构] MAD 中位数边缘统计与四大分段特征
[句子清洗] 分句后处理管道修复学术缩写
[坐标映射] 字符一一对应与多行 BBox 投影
PDF 从来不是一种易于程序读取的文本格式,但通过将排版几何学、字体统计学与严密的字符级映射机制相结合,BibGenie 完全可以在不依赖重量级外部模型的前提下,在毫秒级时间内将这张无序的矢量画布,还原为排版规整、能够准确定位原文物理位置的结构化文本。