使用fitz检测PDF布局:相邻文本块(含公式)被合并的解决咨询
解决fitz中普通文本与近间距公式的textblock拆分问题
针对你遇到的普通文本和独立公式因行间距小被识别为同一textblock的问题,确实可以通过细粒度坐标检测+特征区分+重新分组的方式解决,具体步骤如下:
先获取页面的细粒度单词/字符数据
用fitz的page.get_text("words")方法,能拿到每个单词的坐标(x0,y0,x1,y1)、文本内容、所属block/line编号等信息,这是拆分的基础。相比直接获取textblock,这个接口能让你拿到更底层的元素数据。区分普通文本与公式的特征
公式和普通文本通常有明显特征差异,你可以从这几个维度判断:- 字体:公式常用专门的数学字体(比如
Times New Roman Math、Latin Modern Math),普通文本多是常规宋体、Times New Roman等; - 字符集:公式包含大量数学符号(∑、∫、α等),可以用正则匹配这类特殊字符;
- 垂直位置:公式的基线可能和普通文本不一致,比如行内公式可能居中对齐,而普通文本是基线对齐,对比同一行元素的y坐标范围能发现差异。
- 字体:公式常用专门的数学字体(比如
按特征重新分组构建独立文本块
遍历同一个原始textblock下的所有单词,根据上述特征把普通文本和公式分开,再按阅读顺序(从左到右、从上到下)重新组合成新的独立文本块。
示例代码片段
import fitz import re def split_textblock_with_formula(page): # 获取所有单词数据,按block_no分组 words = page.get_text("words") block_groups = {} for word in words: x0, y0, x1, y1, text, block_no, line_no, word_no = word if block_no not in block_groups: block_groups[block_no] = [] block_groups[block_no].append(word) split_blocks = [] # 遍历每个原始block for block in block_groups.values(): normal_text = [] formula_text = [] # 匹配数学符号的正则 math_symbol_pattern = re.compile(r'[\u2100-\u214F\u2200-\u22FF\u2500-\u257F\u2600-\u26FF\u2700-\u27BF]') for word in block: x0, y0, x1, y1, text, _, _, _ = word # 检查是否包含数学符号,或者字体是否为数学字体 font_info = page.get_fonts()[word[5]] # 获取单词所属字体信息 if math_symbol_pattern.search(text) or ("Math" in font_info[3] if len(font_info)>=4 else False): formula_text.append(text) else: normal_text.append(text) # 如果两个分组都有内容,就拆分成两个独立块 if normal_text and formula_text: split_blocks.append(" ".join(normal_text)) split_blocks.append(" ".join(formula_text)) else: split_blocks.append(" ".join(normal_text + formula_text)) return split_blocks # 使用示例 doc = fitz.open("your_pdf_file.pdf") page = doc[0] result = split_textblock_with_formula(page) for idx, block in enumerate(result): print(f"拆分后的文本块 {idx+1}: {block}")
关于坐标与阅读顺序的说明
你提到的检测单词坐标并按常规阅读顺序排序是必要的:
- 坐标能帮你判断元素的上下左右位置关系,确保分组时不会把不同行的内容混在一起;
- 阅读顺序排序(比如按y坐标从小到大,同一y范围内按x坐标从左到右)能保证拆分后的文本块内容逻辑和原PDF一致。
内容的提问来源于stack exchange,提问作者CAO RUI
相关产品推荐
相关产品推荐

