You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 19:57:21