《PDF解析工程实录》第 20 章|跨页表格合并:克制增强,而不是拍脑袋联表

低功耗蓝牙项目,需要一块懂省电的板

思澈 SF32LB52 芯片,BLE 协议栈深度优化,上手即开发

封面


点此进入系列专栏


如果你在真实业务里解析过 PDF 表格,一定见过这种情况:

  • 第一页的表格在页尾被截断
  • 下一页紧接着是“明显像续表”的内容
  • 表头不再重复
  • 列宽、边框、对齐方式几乎一模一样

特别是在比如财报等场景下,这十分常见:

跨页表格示例
图:跨页表格示例

人一眼就懂:

这是同一个表格。

但对系统来说,这是一个高收益、也高风险的时刻。


跨页表格不是“基础能力”,而是工程上的增强项

先说一个容易被误解的点。

我并不认为:

只有 Lattice 才“配”做跨页表格合并
也不认为其他方案“绝对不行”

真实原因其实更朴素:

我目前只在 Lattice 表格上,找到了足够稳定、可解释、误伤成本可控的合并信号。

这是一种基于可靠性的工程选择,而不是能力上的断言。所以跨页合并在我的系统里,被明确定位为:

对“已经高度可信的表格结果”的一种克制增强,而不是兜底修复。


为什么 Lattice 的结果更“适合”做跨页合并

不是因为它更聪明,而是因为它给了你这些东西:

  • 明确的表格外框
  • 稳定的左右边界
  • 可枚举的列分割线
  • 可验证的行列网格

跨页合并真正依赖的不是“文本连续性”,而是:

几何一致性在分页处是否还能延续。

而这一点,恰恰是有物理线框的表格,最擅长提供的信号。


先把一个前提说清楚:页眉页脚必须先裁掉

在尝试任何跨页判断之前,我都会先做一件事:

  • 去掉页脚(当前页)
  • 去掉页眉(下一页)

原因很简单,页眉页脚是分页装饰,不是内容,它们会:

  • 干扰“是否在页尾/页头”的判断
  • 干扰边框连续性的判断

只有当页面被裁到“只剩正文区域”之后,跨页逻辑才有意义。


什么叫“页尾表格 / 页头表格”

这一步如果定义不清,后面全是灾难。

页尾表格的判定条件

一个 Lattice 表格,被认为是页尾表格,需要同时满足:

  1. 它是页面最下面的表格对象
  2. 其 bbox 触达页面底部的一定比例区域
  • 例如:bbox.bottom ≤ 页面高度的最后 20%

也就是说:

它不只是“在下面”,而是“已经贴近分页边界”。

当前页 p

页面内容区域
(已去页脚)

表格对象 bbox(t)

t 是页面最下面的对象?
(在表格/候选对象中)

不是页尾候选

bbox.bottom 落在底部 20% 区域?
(bottom <= y_threshold)

判定:页尾表格候选


页头表格的判定条件

类似地,一个表格被认为是页头表格

  • 是页面最上面的表格对象
  • bbox.top 触达页面顶部一定比例区域

下一页 p+1

页面内容区域
(已去页眉)

表格对象 bbox(t')

t' 是页面最上面的对象?

不是页头候选

bbox.top 落在顶部 20% 区域?
(top >= y_threshold)

判定:页头表格候选


并排表格的兼容性

现实中,经常会出现:

  • 页尾有两个并排的小表格
  • 下一页页头也有两个并排的续表

所以“是不是页尾/页头表格”,不能只看纵向位置

我额外引入了一个非常实用的几何信号:

如果表格的上边框(页头)或下边框(页尾)在几何上几乎是水平的、连续的,那么它就具备跨页候选资格。

这样做的结果是:

  • 可以在同一页尾部,同时识别多个可跨页表格
  • 下一页也可以对应位置逐一尝试合并
  • 而不是强行“一页只能合一个”

页尾候选表格列表
T_p = {t1, t2, ...}
(可能并排)

对每个 t_i 计算横向投影区间
[x0, x1]

下一页页头表格列表
T_{p+1} = {t1', t2', ...}

对每个 t_j' 计算横向投影区间
[x0, x1]

按横向区间做匹配
(重叠度/对齐度)

t_i 找到唯一/最佳匹配?

不合并该 t_i
保持独立表格

进入几何硬条件校验
宽度/边框/列边界

通过?

合并 t_i 与 t_j'


连续多页跨页合并:不是一次性,而是循环推进

在工程实现里,我的逻辑是:

只要还能合,就继续合。

具体流程是:

  1. 在当前页,找到所有满足条件的“页尾 Lattice 表格”
  2. 对每一个表格:去下一页,寻找在页头部、位置匹配的表格
  3. 如果满足合并条件,则合并,得到一个“更长的表格”
  4. 如果这个合并后的表格,仍然位于页尾,继续看下一页
  5. 直到某一页无法再匹配为止

这是一个逐页推进、可中断的过程,而不是一次性把多页拍成一个整体。

当前页 p
(已裁掉页脚)

检测 Lattice 表格集合 T_p

筛选页尾表格候选
- 是页面最下方对象
- bbox 触达底部20%
- 下边框近水平(可选)

还有候选表格?

取一个页尾表格 t

下一页 p+1
(已裁掉页眉)

在页头区域找匹配表格集合 T_{p+1}
(兼容并排)

存在可合并的表格 t'?

停止合并该 t
输出当前累计结果

检查几何硬条件
- 宽度几乎一致
- 左右边框对齐
- 列边界可对齐(可选)

条件满足?

合并 t 与 t'
得到 t_new

t_new 仍是页尾表格?

p = p+1
继续尝试下一页

停止合并
输出 t_new

结束:输出本页所有表格结果


跨页合并的几何硬条件

在原有条件基础上,我这里明确补充一条:

① 上下页表格的宽度(PDF 单位)几乎一致

这条很重要,而且必须在 PDF 坐标系 下判断,而不是像素空间。

abs(w1 - w2) / max(w1, w2) < ε

原因是:

  • 渲染 DPI 会变
  • 像素尺度会变
  • 但 PDF 用户空间下的宽度,才是真正稳定的版式信号

② 左右边框对齐

  • x0 接近
  • x1 接近

这是“是不是同一张表”的最强信号之一。


③(强信号)列数一致,列边界一一对应

  • Lattice 已经给了你列网格

  • 你完全可以:

    • 比对列数
    • 比对每一列的 x 边界

如果这条成立:

跨页合并的可信度会非常高。


跨页合并时,要不要“合并单元格”?

这是一个非常容易被忽略,但在工程里非常真实的问题

现实场景是:

  • 上一页最后一行,有横向合并的单元格
  • 下一页第一行,对应位置是空的
  • 但线框结构是连续的

人类会自然理解为:

这是一个被分页切开的单元格。


我的工程判断

由于通过代码规则匹配语义的方式判断是否跨页合并单元格不靠谱,我目前采用的是一个纯几何 + 空值规则,如果同时满足:

  1. 后一页表格第一行第一个单元格是空的
  2. 前后页表格的网格线能一一对齐
    • 包括:
      • 普通单元格
      • 横向合并单元格(span)上下也能对齐
  3. 没有出现列数错位

那么:

直接合并前一页最后一行单元格 和 后一页第一行单元格。

这里有一个很重要的工程立场:

  • 我不尝试理解语义
  • 不判断“这是不是一句话”
  • 只基于:
    • 线框
    • 空值
    • 结构连续性

在实践中,这个规则的正确率远高于直觉预期,而且失败成本可控。

已确定 t(页p) 与 t'(页p+1) 要合并

检查:网格线是否一一对齐
- 列边界对齐
- 行线延续一致
- span 的上下边界也对齐

网格对齐?

不做跨页单元格合并
只做表格拼接

检查:t' 第一行左上角格子是否为空?

为空?

合并单元格:
把 t 最后一行对应单元格
与 t' 第一行对应单元格拼接

得到跨页连续单元格


合并失败的原则:什么都不发生

这一点和你前面章节里的思想是一致的:

失败不产生副作用。

具体表现为:

  • 任一条件不满足 → 不合并
  • 合并过程中出现异常 → 回退到原始表格
  • 不做“部分合并”
  • 不做“猜一半”

跨页合并是增强,不是必需。


小结:跨页表格,是“可持续工程”的体现

这一章真正想表达的不是“表格怎么合”,而是:

  • 跨页合并不是靠胆子大
  • 而是靠:
    • 几何信号足够强
    • 判断足够克制
    • 失败路径足够干净

你不是在“把表拼长”,你是在判断:

分页有没有真的把一个结构切断。

而一旦你能把这件事做得稳,你会发现:

  • 两页合并并不难
  • 连续多页也只是循环
  • 真正难的是:知道什么时候该停

——这,才是工程。

低功耗蓝牙项目,需要一块懂省电的板

思澈 SF32LB52 芯片,BLE 协议栈深度优化,上手即开发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值