如何基于已知变换矩阵对齐PDF文本且保留原有文本结构?
问题描述
存在多个文本块错位问题的PDF文件,需依据已知变换矩阵生成文本对齐后的新PDF。
使用PyMuPDF(fitz)提取源PDF文本信息插入目标PDF的实现方式,会丢失全部文本结构信息(blocks块、lines行、spans跨度等),复现代码如下:
import fitz src_doc = fitz.open('my.pdf') tgt_doc = fitz.open() src_page = doc[0] tgt_page = tgt_doc[0] text_dict = src_page.get_text('dict') transform = fitz.Matrix(1, 1) # would be non-identity in practice tw = fitz.TextWriter(tgt_page.rect) for block in text['blocks']: if block['type'] != 1: # ignore images blocks.append(block) for line in block['lines']: for span in line['spans']: tw.append(span['origin'], span['text']) tw.write_text(tgt_page, morph=[fitz.Point([0.0, 0.0]), transform]) tgt_doc.save('aligned.pdf') src_doc.close() tgt_doc.close()
上述代码可实现基础文本对齐效果,但会完全丢失原有文本结构,最终生成的目标页面包含的文本块数量远多于源页面。需求为实现同等文本对齐效果的同时,不破坏原页面的文本结构。此前参考ocrmypdf项目实现,使用pikepdf库完成相关操作时,发现pikepdf仅支持ASCII字符,处理非拉丁文本存在障碍,可接受其他满足需求的第三方库推荐。
解决方案
方案1:使用PyMuPDF原生块级变换(推荐,无结构损失)
原有代码丢失结构的核心原因是TextWriter会将所有提取到的文本打散重排为新的连续文本流,不会保留原始PDF内容流中的块、行分段标记。不需要重写文本,直接在复制的源页面上对原始文本块应用变换矩阵即可100%保留原有结构,参考实现如下:
import fitz src_doc = fitz.open("my.pdf") tgt_doc = fitz.open() # 直接将源页面完整拷贝到目标文档,保留所有原始内容、结构、字体信息 tgt_doc.insert_pdf(src_doc) tgt_page = tgt_doc[0] transform = fitz.Matrix(1, 1) # 替换为实际使用的非单位变换矩阵 pivot = fitz.Point(0, 0) # 变换锚点,可根据实际变换逻辑调整 # 获取源页面所有文本块的边界范围 text_dict = tgt_page.get_text("dict") for block in text_dict["blocks"]: if block["type"] != 0: # 跳过图片块 continue block_bbox = fitz.Rect(block["bbox"]) # 对单个原始文本块应用变换,不拆分块内的行、span结构 tgt_page.add_redact_annot( block_bbox, morph=(pivot, transform) ) # 应用变换时跳过图片、矢量图形,仅处理文本块 tgt_page.apply_redactions( images=fitz.PDF_REDACT_IMAGE_NONE, graphics=fitz.PDF_REDACT_LINE_ART_NONE ) tgt_doc.save("aligned.pdf") src_doc.close() tgt_doc.close()
如果需要更细粒度的控制,可以直接解析页面原始内容流,定位每个BT...ET标记包裹的独立文本对象,直接修改其内置的变换矩阵参数,该方法不会触发任何文本重绘,结构保留度最高。
方案2:其他可选第三方库
- borb:纯Python实现的PDF处理库,支持直接操作文本块的变换属性,对中文、日文、阿拉伯文等非拉丁文本支持完善,操作时不会破坏原有文本结构,缺点是大文件处理速度低于PyMuPDF。
- pikepdf+fontTools组合:pikepdf本身并非不支持非ASCII字符,非拉丁文本乱码的核心原因是操作时没有正确处理字体的ToUnicode映射表和字体子集嵌入,搭配fontTools补全字体相关配置后即可正常处理非拉丁文本,该方案也是ocrmypdf处理多语言PDF的底层实现逻辑,缺点是配置复杂度较高。
不推荐使用PyPDF2、pdfplumber等库,前者块级操作支持极差,后者仅适合文本提取,不具备完善的PDF写入修改能力。
内容的提问来源于stack exchange,提问作者asymptote
相关产品推荐
相关产品推荐

