PyMuPDF розбиває той самий рядок тексту на більше ніж десять проміжків? Це визначає, чи слід його об’єднати
PyMuPDF розбиває той самий рядок слів на більше ніж десяток проміжків. У більшості випадків шрифт, розмір або стиль змінюються, не обов’язково сегментація. У цій статті пояснюється, як розрізнити зміни стилю та межі абзаців, а також наводяться приклади логіки об’єднання та форми прийняття, щоб уникнути подрібнення речення під час перекладу.
Коли ви використовуєте extractDICT() PyMuPDF для вилучення тексту PDF, ви можете виявити, що зв’язне речення розрізається на сім, вісім або більше span у поверненому результаті. Зазвичай це не помилка синтаксичного аналізу, а справжнє відображення основної структури PDF-файлу: зміна шрифту, зміна кольору або локальне коригування стилю можуть порушити потік тексту під час візуалізації.
Якщо це «нарізання стилю» помилково прийнято за «сегментацію абзацу», подальше очищення, переклад або пошук легко спричинить помилки сегментації речення. У цій статті пояснюється, як відрізнити зміни стилю від меж абзаців, наводиться об’єднаний приклад логіки об’єднання та надається форма прийняття, яка допоможе вам уникнути розрізання речення.
Рядок тексту в PDF-файлі розділений на багато проміжків. Чи варто їх об’єднувати? Висновок одним реченням
Коли є кілька span в тому самому line і координати y однакові, це здебільшого шрифт, розмір шрифту або зміна кольорів, які можна безпечно з’єднати; межа абзацу має базуватися на структурі block і line, а не на кількості span.
| ваша ситуація | Рекомендовані практики | причина |
|---|---|---|
| Кілька прогонів в одній лінії мають однакові координати y | Безпечне зрощування span.text | Переважно шрифт або перемикання кольорів |
| Між прольотами є очевидний горизонтальний зазор | Перевірте відстань bbox і за потреби вставте пробіли або табуляцію | Може бути символом табуляції або залишком розриву стовпця, потрібні правила |
| Різниця в координатах y проміжків в одному рядку перевищує 10% розміру шрифту | Зберігайте початкову структуру прольоту без злиття | Можуть бути вбудовані формули або індекси |
| Проміжок одного слова та bbox не впорядковані | Позначити як низьку якість, конвертувати вручну або OCR | Відсутність надійної бази для сортування та групування |
| Семантично безперервний текст через блоки | Потрібне оцінювання контексту, і автоматичне об’єднання не виконується. | Це може бути фрагмент підкачки |
Спочатку з'ясуйте ієрархічний зв'язок між блоком, лінією та діапазоном
Структура словника 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 можуть бути невеликі проміжки або нерегулярні перекриття.
Цю ситуацію не можна виправити просто конкатенацією рядків, оскільки немає надійної основи для сортування та групування. Безпечніше позначити сторінку як "низькоякісні структуровані дані" та надіслати її на перевірку вручну або спеціальний процес OCR, а не примусово об’єднувати.
Логіка об'єднання діапазонів на основі геометричних особливостей
Зразок коду
Наступний приклад 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, вставте пробіли або табуляції | Будьте обережні і потрібні правила |
| Різниця в координатах span y в одному рядку > розмір шрифту 10% | Вбудовані формули, верхні та нижні індекси | Зберігайте початкову структуру прольоту без злиття | немає |
| Проміжок одного слова та bbox не впорядковані | Відсканований або пошкоджений PDF | Позначити як низьку якість, конвертувати вручну або OCR | немає |
| Смислова безперервність тексту між блоками | Розрив сторінки | Міжблокове злиття вимагає контекстуального оцінювання | Ні, потрібна допомога НЛП |
Ця таблиця охоплює структурну перевірку лише на рівні демонстрації коду, а наскрізний ефект потрібно поєднати з мовними моделями або вибіркою вручну. Автоматичне об’єднання не слід використовувати для чутливих до формату документів, таких як юридичні договори, медичні записи тощо, якщо це не підтверджено експертом домену.
Перед використанням самостійно підготуйте тестовий PDF-файл десенсибілізації, запустіть page.get_text("dict", sort=True), щоб експортувати JSON, а потім перевірте кожен елемент.
Використовується для перевірки перед перекладом документа
Спочатку перевірте порядок сторінок, розташування джерела та цілісність тексту, а потім надішліть той самий десенсибілізований зразок до процесу перекладу. Після перекладу все одно потрібно перевірити, чи коректно відповідають заголовок, текст та ілюстрації, а не лише гладкість тексту.
OmniDoc (портал продуктів) надає багатоформатний портал перекладу документів, а на офіційному веб-сайті представлені функції OCR, глосарій і двомовне порівняння. Для команд, які хочуть працювати безпосередньо з файлами, це шлях, який варто оцінити на прикладах із реального життя. Будь ласка, використовуйте десенсибілізовані файли, на завантаження яких у вас є дозвіл, і скористайтеся прапорцями в цій статті, щоб переглянути результати. Тут немає жодних тверджень про те, що OmniDoc використовує бібліотеки з відкритим кодом або внутрішні алгоритми в цій статті, а також приклад не замінює тестування продуктивності продукту.
🎯 Який спосіб вам підходить?
Якщо ви просто хочете організувати текст в одному рядку
- Групуйте за
lineі безпечно з’єднуйтеspan.text, коли координати y узгоджуються.
Якщо ви зіткнулися з вбудованими формулами або верхніми індексами
— Зберігайте оригінальну структуру span без злиття.
Якщо ви маєте справу зі сканованими файлами або PDF-файлами з великою кількістю окремих слів
- Спочатку позначте як низьку якість, а потім перейдіть до ручної корекції або спеціального процесу OCR.
Якщо ви хочете виконати міжблочний або міжсторінковий переклад
- На основі контекстуального судження недоцільно об’єднувати безпосередньо автоматично.
❓ Часті запитання (FAQ)
1. Рядок тексту розбивається на багато проміжків. Чи це має бути сегментація?
Не обов’язково, це здебільшого спричинено зміною шрифту, розміру або кольору шрифту. span в межах одного line мають однакову базову лінію та вертикальне положення.
2. Що слід прочитати в кінці абзацу?
Подивіться, чи закінчується список line і починається новий block, а не номер span.
3. Чи можна використати конкатенацію рядків для вирішення проблеми з охопленням одним словом?
Не рекомендується. Такі випадки не мають надійної основи для сортування та групування, тому доцільніше позначати їх як низьку якість і передати вручну або OCR.
4. Скільки потрібно встановити y_tolerance під час злиття?
2.0 у прикладі є лише синтетичним прикладом налаштування і не є галузевим стандартом. Фактичні елементи можна обчислювати динамічно на основі середнього розміру шрифту або безпосередньо довіряти групуванню line.
5. Чи можна використовувати автоматичне злиття в юридичних договорах?
Не підходить, якщо не перевірено експертами домену.
📝 Підсумок
Ключ до визначення того, чи span можна об’єднати, це спочатку подивитися на структуру line і block, а потім подивитися на геометричне положення. Коли ви зустрічаєте формули, відскановані документи чи міжблокову семантику, краще залишити їх як є або перетворити вручну, а не примусово з’єднувати.
Спробуйте зараз: завантажте документ, щоб розпочати переклад, зберігаючи вихідний макет


