Одна и та же строка текста разбивается PyMuPDF на более чем десять промежутков? Это определяет, следует ли его объединить

PyMuPDF разбивает одну и ту же строку слов на более чем дюжину промежутков. В большинстве случаев менялся шрифт, размер или стиль шрифта, а не обязательно сегментация. В этой статье объясняется, как различать изменения стиля и границы абзацев, а также приводятся примеры объединения логики и формы принятия, чтобы избежать дробления предложения во время перевода.

— Граница абзаца должна определяться на основе конца списка строк и начала нового блока, а не количества промежутков.

  • Если координаты y промежутков в одной строке согласованы, span.text можно безопасно соединить; когда координаты y сильно различаются, исходная структура должна быть сохранена.
  • Отсканированные документы или страницы с промежутками из одного слова и неупорядоченными bbox должны быть помечены как низкокачественные и переданы на ручную корректуру или распознавание текста. Принудительное объединение нецелесообразно.
  • Автоматическое объединение не следует использовать для документов, чувствительных к формату, таких как юридические контракты и медицинские записи. Десенсибилизированные образцы необходимо проверять по отдельности перед использованием.

Когда вы используете extractDICT() PyMuPDF для извлечения текста PDF, вы можете обнаружить, что связное предложение в возвращаемом результате разрезается на семь, восемь или более span. Обычно это не ошибка синтаксического анализа, а истинное отражение базовой структуры PDF-файла: переключение шрифтов, изменение цвета или локальные настройки стиля могут нарушить поток текста при рендеринге.

Если это «нарезка стилей» ошибочно принять за «сегментацию абзацев», последующая очистка, перевод или поиск легко вызовут ошибки сегментации предложений. В этой статье объясняется, как отличить изменения стиля от границ абзаца, приводится объединенный пример логики слияния и предоставляется форма принятия, которая поможет вам не разбивать предложение на части.

Строка текста в PDF-файле разбивается на множество промежутков. Стоит ли их объединить? Вывод в одно предложение

Когда в одном и том же line имеется несколько span и координаты y одинаковы, в основном это переключение шрифта, размера шрифта или цвета, которые можно безопасно соединить; граница абзаца должна основываться на структуре block и line, а не на количестве span.

твоя ситуацияРекомендуемые практикипричина
Несколько промежутков в одной строке имеют одинаковые координаты y.Безопасное сращивание span.textВ основном переключение шрифта или цвета
Между пролетами имеется очевидный горизонтальный зазор.Проверьте расстояние между bbox и при необходимости вставьте пробелы или табуляции.Может быть символом табуляции или остатком разрыва столбца, нужны правила
Разница в координатах y промежутков внутри одной строки превышает 10% размера шрифта.Сохраните исходную структуру диапазона без слияния.Могут быть встроенными формулами или индексами.
Диапазон одного слова и bbox неупорядочены.Отметить как низкое качество, преобразовать в ручной формат или распознавание текстаОтсутствие надежной основы для сортировки и группировки.
Семантически непрерывный текст в блокахТребуется контекстное суждение, и автоматическое слияние не выполняется.Это может быть фрагмент пейджинга

Сначала уточните иерархические отношения между блоком, линией и интервалом.

Структура словаря PyMuPDF

Вывод словаря PyMuPDF строго соответствует структуре потока содержимого PDF, а иерархия снаружи внутрь выглядит следующим образом:

  • block: независимая область набора текста на странице, например абзац текста, ячейка таблицы или легенда.
  • line: визуальная одна строка текста внутри block.
  • span: line — это внутренняя последовательность последовательных символов одного и того же шрифта, размера, цвета и стиля.

Дополнительные описания полей на каждом уровне см. в Официальная документация PyMuPDF TextPage.

Почему при переключении стиля создается новый диапазон?

Пока в середине строки текста присутствует жирный шрифт, курсив, изменение цвета или возврат шрифта (например, китайские иероглифы смешаны с английскими и цифрами), будет создан новый span. Эти span принадлежат одному и тому же line и имеют одну и ту же базовую линию и вертикальную позицию.

На что обратить внимание на границах абзацев

Истинный конец абзаца отмечается концом списка line и началом нового block, а не номером span. Их путаница может вызвать практические проблемы: например, «Важный совет» ошибочно распознается как два отдельных предложения, в результате чего память переводов не может сопоставить весь термин.

Пример синтеза: правильный вывод и крайние случаи

Правильный вывод

Для иллюстрации проблемы построим синтетический сценарий. Предположим, во входном PDF-файле есть строка текста: «Обратите внимание: система перезагрузится через 5 минут». Среди них «Обратите внимание:» используется жирный шрифт, «система перезагрузится через 5 минут» возвращается к шрифту Song.

Правильный вывод extractDICT() должен содержать четыре span под одним объектом line. bbox каждого span сильно перекрываются в вертикальном направлении, а координаты Y origin в основном совпадают. Эти четыре фрагмента склеены между собой, образуя целостную смысловую единицу.

Низкое качество ввода

Типичная ошибка заключается в том, что отсканированный PDF-файл не был распознан или генератор PDF-файлов записывает каждый китайский иероглиф как отдельный текстовый объект. В этом случае extractDICT() может возвращать десятки однословных span, и между их bbox могут быть небольшие пробелы или нерегулярные перекрытия.

Эту ситуацию невозможно исправить простой конкатенацией строк, поскольку нет надежной основы для сортировки и группировки. Вместо принудительного слияния безопаснее пометить страницу как «структурированные данные низкого качества» и отправить ее на ручную корректуру или специальный процесс оптического распознавания символов.

Логика слияния интервалов, основанная на геометрических объектах

Пример кода

В следующем примере Python показано, как эмулировать структуру вывода extractDICT() в памяти и реализовать безопасную функцию слияния одноранговых диапазонов. Примечание. В этом примере синтетические данные создаются исключительно из памяти и не считываются реальных PDF-файлов. Он используется только для проверки логики алгоритма слияния.

Для фактического использования убедитесь, что версия PyMuPDF поддерживает метод extractDICT (рекомендуется 1.23.x и выше) и запустите его в своей собственной среде заблокированных зависимостей.

# 合成示例:演示同行span合并逻辑,非真实PDF解析结果
# 依赖: PyMuPDF (pymupdf) >= 1.23.0,仅用于类型参考,本例纯内存构造

def merge_spans_in_line(line_dict, y_tolerance=2.0):
    """合并同一line内y坐标相近的span,保留原始顺序"""
    if not line_dict.get("spans"):
        return ""
    spans = sorted(line_dict["spans"], key=lambda s: s["bbox"][0])  # 按x排序
    merged_text = []
    current_y = spans[0]["origin"][1]
    buffer = [spans[0]["text"]]

    for span in spans[1:]:
        y = span["origin"][1]
        if abs(y - current_y) <= y_tolerance:
            buffer.append(span["text"])
        else:
            merged_text.append("".join(buffer))
            buffer = [span["text"]]
            current_y = y
    merged_text.append("".join(buffer))
    return "\n".join(merged_text)

# 构造合成line数据,模拟四种字体混排(正常情况)
synthetic_line = {
    "spans": [
        {"text": "请注意:", "bbox": (100, 200, 160, 220), "origin": (100, 215)},
        {"text": "系统将在", "bbox": (160, 200, 220, 220), "origin": (160, 215)},
        {"text": "5", "bbox": (220, 200, 230, 220), "origin": (220, 215)},
        {"text": "分钟后重启。", "bbox": (230, 200, 310, 220), "origin": (230, 215)}
    ]
}

result = merge_spans_in_line(synthetic_line)
assert result == "请注意:系统将在5分钟后重启。", f"合并失败: {result}"

# 边界测试:y坐标差异超过容差,应视为不同行片段(即使在同一line dict中也应分隔)
bad_line = {
    "spans": [
        {"text": "上行", "bbox": (100, 200, 140, 220), "origin": (100, 215)},
        {"text": "下行", "bbox": (100, 230, 140, 250), "origin": (100, 245)}
    ]
}
bad_result = merge_spans_in_line(bad_line, y_tolerance=2.0)
# y差值30 > 2.0,应被分隔为两行
assert bad_result == "上行\n下行", f"异常行未正确分隔: {repr(bad_result)}"

Меры предосторожности перед использованием

  • Значение 2,0 для y_tolerance является лишь синтетическим примером настройки и не является отраслевым стандартом.

— В реальных проектах это значение может рассчитываться динамически на основе среднего размера шрифта документа; если сам line был правильно разделен, обычно нет необходимости в дополнительной проверке координаты y.

  • Примеры в этой статье представляют собой только демонстрации на уровне кода и не являются гарантией всех реальных файлов или эффектов продукта.

📊 Сравнение ядра

В следующей таблице сравниваются несколько методов обработки и выясняется, подходят ли они для автоматического объединения в зависимости от наблюдаемой ситуации и правил приемки, приведенных выше.

Наблюдаемая ситуацияМетод обработкиПодходит ли он для автоматического слияния?
Координаты Y пролетов внутри одной линии совпадают.Безопасное сращиваниеда
Между пролетами имеется очевидный горизонтальный зазор.Вставляйте пробелы или табуляции после проверки пробеловБудьте осторожны и нуждайтесь в правилах
Встроенные формулы, верхние и нижние индексы приводят к большим различиям в координатах Y.Сохранять исходную структуру диапазонанет
Диапазон одного слова и bbox неупорядочены.Пометить низкое качество, преобразовать в ручной формат или OCRнет
Семантически непрерывный текст в блокахСуждение на основе контекстаНет, требуется помощь НЛП.

Критерии приемки и границы ручной проверки

Наблюдайте за явлениямиВозможные причинырешение по обработкеПрименять ли автоматическое слияние
Несколько промежутков в одной строке имеют одинаковые координаты y.Переключение шрифта/цветаБезопасное сращивание span.textда
Между пролетами имеется очевидный горизонтальный зазор (>ширина пространства).Символы табуляции и остатки столбцовПроверьте расстояние между bbox, вставьте пробелы или табуляцииБудьте осторожны и нуждайтесь в правилах
Разница в координатах диапазона y в пределах одной линии > размер шрифта 10 %.Встроенные формулы, верхние и нижние индексыСохраните исходную структуру диапазона без слияния.нет
Диапазон одного слова и bbox неупорядочены.Отсканированный или поврежденный PDF-файлОтметить как низкое качество, преобразовать в ручной формат или распознавание текстанет
Семантическая непрерывность текста между блокамиРазрыв страницыМежблоковое слияние требует контекстуального сужденияНет, требуется помощь НЛП.

Эта таблица охватывает только структурную проверку на уровне демонстрации кода, а сквозной эффект необходимо сочетать с языковыми моделями или ручной выборкой. Автоматическое объединение не следует использовать для документов, чувствительных к формату, таких как юридические контракты, медицинские записи и т. д., если это не проверено экспертом в предметной области.

Перед использованием самостоятельно подготовьте PDF-файл теста десенсибилизации, запустите page.get_text("dict", sort=True) для экспорта JSON, а затем проверьте каждый элемент.

Используется для проверки перед переводом документа

Сначала проверьте порядок страниц, расположение источника и целостность текста, а затем отправьте тот же десенсибилизированный образец в процесс перевода. После перевода еще нужно проверить, правильно ли соответствуют заголовок, текст и иллюстрации, а не просто ровный ли текст.

OmniDoc (портал продуктов) предоставляет многоформатный портал для перевода документов, а на официальном сайте представлены функции оптического распознавания символов, глоссария и двуязычного сравнения. Для команд, которые хотят работать непосредственно с файлами, этот путь стоит оценить на реальных примерах. Используйте десенсибилизированные файлы, на загрузку которых у вас есть разрешение, и используйте элементы проверки в этой статье, чтобы просмотреть результаты. Здесь не утверждается, что OmniDoc использует библиотеки с открытым исходным кодом или внутренние алгоритмы в этой статье, и этот пример не заменяет тестирование производительности продукта.

🎯 Какой метод подходит вам?

Если вы просто хотите упорядочить текст в одной строке

  • Группируйте по line и безопасно соединяйте span.text, когда координаты y совпадают.

Если вы столкнулись со встроенными формулами или надстрочными индексами

  • Сохраните исходную структуру span без слияния.

Если вы имеете дело со сканами или PDF-файлами с большим количеством отдельных слов.

  • Сначала пометьте качество как низкое, затем перенесите на ручную корректуру или специальный процесс оптического распознавания символов.

Если вы хотите выполнить кросс-блочный или межстраничный перевод

  • Исходя из контекстуального суждения, автоматическое слияние нецелесообразно.

❓ Часто задаваемые вопросы (FAQ)

1. Строка текста разбивается на множество промежутков. Должна ли быть сегментация?

Не обязательно, в основном это вызвано изменениями шрифта, размера или цвета шрифта. span внутри одного и того же line имеют одну и ту же базовую линию и вертикальное положение.

2. Что прочитать в конце абзаца?

Посмотрите, заканчивается ли список line и начинается новый block, а не номер span.

3. Можно ли использовать конкатенацию строк для решения проблемы с интервалом в одно слово?

Не рекомендуется. В таких случаях отсутствует надежная основа для сортировки и группировки, и разумнее пометить их как некачественные и передать на обработку вручную или с помощью оптического распознавания символов.

4. Сколько должно быть установлено y_tolerance при слиянии?

Значение 2.0 в примере представляет собой синтетический пример настройки и не является отраслевым стандартом. Фактические элементы можно рассчитывать динамически на основе среднего размера шрифта или напрямую доверять группировке line.

5. Можно ли использовать автоматическое слияние в юридических контрактах?

Не подходит, если не проверено экспертами в предметной области.

📝 Резюме

Чтобы решить, можно ли объединить span, нужно сначала взглянуть на структуру line и block, а затем посмотреть на геометрическое положение. При работе с формулами, отсканированными документами или семантикой перекрестных блоков лучше оставить их как есть или преобразовать вручную, а не объединять принудительно.

Попробуйте сейчас: загрузите документ, чтобы начать перевод, сохранив исходный макет.

Обновлено 2026-11-15