如何在pdfminer中反转倒置文本 解决倒置文本框提取顺序错误问题
问题根源
pdfminer 仅按PDF绘制顺序返回布局元素,不会自动校正旋转、倒置的文本块。你当前全局用(-b.y1, b.x0)做排序的逻辑,只适配从上到下、从左到右的正向横排文本。180度倒置的文本块,视觉上的顶部实际对应坐标值更小的y轴位置,直接套用正向排序规则就会出现整段行序颠倒的问题。
修正方法
核心是先判断单个文本块的方向,再匹配对应的排序、拼接规则:
- 遍历文本块内的字符元素,通过字符的变换矩阵
matrix判断旋转角度,180度倒置的字符旋转弧度值接近π(允许0.1左右的浮点误差) - 对正向文本块,保持原有从上到下、从左到右的排序逻辑
- 对倒置文本块,调整行排序为y值升序,同时反转每行的字符顺序,得到正确阅读顺序
修正后的可运行代码如下:
from io import BytesIO import math from pdfminer.converter import PDFPageAggregator from pdfminer.layout import LAParams, LTContainer, LTTextBox, LTChar, LTTextLine from pdfminer.pdfinterp import PDFPageInterpreter, PDFResourceManager from pdfminer.pdfpage import PDFPage def find_textboxes_recursively(layout_obj): if isinstance(layout_obj, LTTextBox): return [layout_obj] if isinstance(layout_obj, LTContainer): boxes = [] for child in layout_obj: boxes.extend(find_textboxes_recursively(child)) return boxes return [] def is_upside_down_textbox(text_box): """判断文本块是否为180度倒置""" char_count = 0 upside_down_count = 0 for element in text_box: if isinstance(element, LTTextLine): for char in element: if isinstance(char, LTChar): char_count += 1 # 从变换矩阵计算旋转角度,180度旋转对应角度值接近π rotation = math.atan2(char.matrix[1], char.matrix[0]) if abs(abs(rotation) - math.pi) < 0.1: upside_down_count += 1 # 超过半数字符为倒置则判定为倒置文本块,避免个别特殊字符误判 return char_count > 0 and upside_down_count / char_count > 0.5 def get_correct_text(text_box, is_upside_down): """按文本方向提取正确顺序的文本""" lines = [line for line in text_box if isinstance(line, LTTextLine)] if is_upside_down: # 倒置文本行按y坐标升序排(对应视觉从上到下),每行文本反转顺序 lines.sort(key=lambda l: l.y0) processed_lines = [] for line in lines: line_text = line.get_text().strip()[::-1] processed_lines.append(line_text) return "\n".join(processed_lines).strip() else: # 正向文本保持原有y降序、x升序规则 lines.sort(key=lambda l: (-l.y1, l.x0)) return "\n".join([line.get_text().strip() for line in lines]).strip() def inspect_character(doc_binary): laparams = LAParams(detect_vertical=True) resource_manager = PDFResourceManager() device = PDFPageAggregator(resource_manager, laparams=laparams) interpreter = PDFPageInterpreter(resource_manager, device) for page_num, page in enumerate(PDFPage.get_pages(BytesIO(doc_binary)), 1): interpreter.process_page(page) layout = device.get_result() boxes = find_textboxes_recursively(layout) # 块间按整体坐标排序,块内单独根据方向处理内容顺序 boxes.sort(key=lambda b: (-b.y1, b.x0)) for box in boxes: box_is_upside_down = is_upside_down_textbox(box) correct_text = get_correct_text(box, box_is_upside_down) print(correct_text)
注意事项
- 角度判断加0.1的容错值,是为了兼容部分PDF生成时的矩阵浮点计算误差
- 用块内超过半数字符为倒置作为判定条件,能避免个别旋转符号、特殊字符导致的方向误判
- 如果需要处理90度、270度旋转的竖排文本,只需要在方向判断函数里增加对应旋转角度的分支,匹配对应的排序规则即可
内容的提问来源于stack exchange,提问作者Berry Evangeline
相关产品推荐
相关产品推荐

