PDF 解析

双栏 PDF 抽出来为什么串行?先保留坐标,再谈阅读顺序

解释双栏PDF文本串行源于格式缺乏语义结构,澄清sort参数仅为几何排序。提供基于坐标分区的合成Python示例演示顺序重组逻辑,明确适用边界与人工审查红线,并给出衔接OmniDoc翻译工作流的务实验收路径。

阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。 左栏读到一半,下一句突然接上右栏的公式说明。这类问题不一定出在识别:字可能全都提对了,只是顺序错了。对普通双栏论文,一般应读完一个区段的左栏,再读右栏;把左右两栏按同一高度逐行交替拼接,反而会破坏段落。 解决顺序问题之前,先保留文本的坐标和来源页。不要急着把整个页面压成一个长字符串:一旦丢掉区域信息,后续翻译拿到的就是已经串错的上下文,换一个翻译模型也未必能补救。 sort=True 值得试,但不是版面理解器 PyMuPDF 提供文字、词、块等不同提取形式。 sort 的具体处理随输出类型与版本有所不同,不能把它一概描述成某一种固定的排版算法,更不能据此承诺恢复所有文档的阅读顺序。 对于规则页面,可以先比较开启与关闭排序的结果。对有跨栏标题、边注、脚注的页面,必须再看区域关系。PDF 也可能含有结构标记,但这不意味着当前采用的提取接口会自动给出你需要的语义段落树。本文不实现通用布局识别,只演示“已有区域规则之后如何排序”,把能力边界留清楚。 用五个合成文本块说明问题 设页面宽 600 点,栏间分界为 300;顶部 80 点以内是跨栏标题区,其下是正文区。输入故意打乱:右栏第二段在前,左栏第一段在后。每个元组包含矩形的四个坐标和文本。这里是我们定义的简化结构,不冒充 get_text("blocks") 完整返回值。 预期结果应为:标题、左栏第一段、左栏第二段、右栏第一段、右栏第二段。一个跨越栏间分界的正文块,则必须进入异常列表,而不是被悄悄丢弃或硬塞到左栏。 一个可以完整运行的小例子 下面只用 Python 标准库。实际接 PyMuPDF 时,可从块提取结果保留前五个字段,并先排除非文字块。这里不打开外部文件,不要求 OCR,也不自动推断栏界。 def order_known_layout(blocks, split_x=300, title_bottom=80): groups = {"title": [], "left": [], "right": []} rejected = [] for block in blocks: x0, y0, x1, y1, text = block if x1 <= x0 or y1 <= y0: rejected.append(block) continue if y1 <= title_bottom: region = "title" elif y0 < title_bottom: rejected.append(block) continue elif x1 <= split_x: region = "left" elif x0 >= split_x: region = "right" else: rejected.append(block) continue groups[region].append(block) ordered = [] for region in ("title", "left", "right"): ordered.extend(sorted(groups[region], key=lambda b: (b[1], b[0]))) return [b[4] for b in ordered], rejected sample = [(330, 160, 550, 190, "R2"), (40, 100, 270, 130, "L1"), (40, 20, 550, 50, "TITLE"), (330, 100, 550, 130, "R1"), (40, 160, 270, 190, "L2")] ordered, rejected = order_known_layout(sample) assert ordered == ["TITLE", "L1", "L2", "R1", "R2"] assert rejected == [] wide = (40, 220, 550, 250, "FULL WIDTH BODY") assert order_known_layout(sample + [wide])[1] == [wide] assert order_known_layout([]) == ([], []) print("known-layout examples: 3 assertions passed") 80 和 300 都来自这张合成页面的设定,不是应当复制到所有 PDF 的经验阈值。遇到下一页左右栏宽度变化时,沿用上一页参数可能产生“运行正常、内容错误”的结果。区域规则应跟页面走,至少记录规则版本与原页尺寸。 验收不要只看有没有报错 检查项 在这组例子中应看到什么 不符合时怎么做 区域顺序 标题后先完整左栏,再完整右栏 检查分区规则,不靠调换字符串补丁修复 内容守恒 接受块数加拒绝块数等于输入块数 阻断交付,查有没有静默丢块 跨栏正文 出现在拒绝列表 新增跨栏区段,或交人工判定 旋转与裁切 输入块与分区使用同一坐标系 先统一坐标,再做包含判断 页眉、脚注 按业务规则单独归类 不把所有底部文字一律删除 真实论文还可能在中间插入一张跨栏图,图下面重新开始双栏。这时需要先把页面分成上下区段,再分别排序,本文的一次性三组分区并不够。中文竖排、从右向左的文本、复杂边注,也不应套用这个阅读方向。 怎么接到 OmniDoc 的翻译流程 自建脚本的价值是把你的验收规则写清楚;现成翻译工具的价值是减少从文件到译稿的操作环节。两者并不冲突。 OmniDoc(产品入口) 提供文档翻译入口,官网也介绍了 OCR、术语表和双语对照功能。可以拿一份有权上传的脱敏论文,用同一套顺序清单检查译稿。 重点看跨栏标题后第一句是否接对、图注有没有并入正文、脚注引用是否仍能对应。这是在评估交付结果,不是在推断 OmniDoc 的内部算法。若结果不符合要求,保留原页和错误区段,先查明是识别、排序还是翻译出了问题,再决定如何修正。 从一页开始,而不是一次改完整本 先挑一张有代表性的双栏页,人工标出应该怎么读,再运行规则。把成功样本与失败样本都留下,调整规则后一起回归。一个规则只有在保留已有正确结果的同时修复新问题,才值得放到批量任务里。 接口与提取形式参考: PyMuPDF 官方文字提取指南 。示例检验的是已知区域排序,不代表已经验证任意 PDF 的布局分析效果。 - 1 -
更新于 2026-10-08