Передаючи анотовані документи, не просто передавайте PDF-файл, який виглядає чистим.

У цій статті представлено рішення для багаторівневого вилучення та перевірки безпеки на основі PyMuPDF для вирішення проблем плутанини між текстом і коментарями, витоку конфіденційних даних і плутанини систем координат під час вилучення анотацій PDF. Вміст охоплює принцип незалежного рівня даних, приклади захисного програмування, обробку граничних умов і критерії прийняття вручну, а також пояснює, як правильно підключити OmniDoc до процесу перекладу документів для двомовного порівняння та прийняття.

Перш ніж передати PDF-анотації, багато людей безпосередньо використовуватимуть page.get_text(), щоб експортувати чистий текст для перекладу, архівування або передачі наступному рецензенту. Але цей текст зазвичай не містить анотацій. Деякі методи вилучення також вставлятимуть коментарі в середину тексту, перериваючи семантику; коментарі до модифікації та делікатні зауваження на бічній панелі можуть бути пропущені.

Ця стаття написана для розробників і власників документів, яким потрібно працювати з анотованими PDF-файлами. Після прочитання ви зрозумієте, чому анотації є незалежними шарами даних, як витягувати текст і анотації в шарах, які крайові випадки потрібно обробляти захисно, а які точки прийняття потрібно підтверджувати вручну.

Усі приклади в цій статті використовують синтетичні зразки. Зразок коду перевірено в ізольованому середовищі, але це не є гарантією всіх реальних файлів або продуктивності продукту. Перед обробкою важливих документів збережіть оригінали та перегляньте їх відповідно до меж і пунктів прийняття в тексті.

Як витягти анотований PDF перед передачею? Висновок одним реченням

Розглядайте текст і анотації як два незалежні потоки даних і витягуйте їх окремо, а також виконайте явну перевірку безпеки вмісту анотації.

ваша ситуаціяРекомендовані практикипричина
Для експорту тексту використовуйте лише get_text()Не сприймайте це як завершенеВихід за замовчуванням зазвичай не містить анотацій
Необхідно зберегти коментарі рецензентів до переглядуПереглядайте анотації окремо за допомогою page.annots()Анотації – це незалежні об’єкти, прикріплені до сторінки
Коментарі можуть містити ідентифікаційні номери та внутрішні коди.Зіставте відповідність за конфіденційними правилами, позначте redacted=True і замініть заповнювачемПримітки до огляду можуть містити конфіденційні дані
Сторінка перевернутаСпочатку використовуйте page.rotation_matrix або page.derotation_matrix для перетворення координатПрямокутник анотації на основі неповернутої сторінки
Готовий до процесу перекладуСпочатку очистіть теги форматованого тексту, а потім перевірте вручну після перекладуУникайте перекладів, що містять код

Чому анотації не залежать від рівня даних тексту

У специфікації PDF потік вмісту сторінки (Content Stream) і словник анотацій (Annots Array) структурно розділені. Тіло складається з текстових інструкцій для відображення; анотації — це незалежні об’єкти, прикріплені до сторінки, з власними координатами, типом і атрибутами вмісту. Метод Page.annots() PyMuPDF повертає генератор, який проходить цей незалежний список анотацій замість текстового потоку сторінки.

Це означає, що ви не можете вважати, що весь текст, який ви бачите, можна витягнути однаково.

Різниця між класом позначки та анотаціями класу вмісту

  • Анотації тегів (такі як виділення та підкреслення) самі по собі не містять тексту, а записують лише область (квадрати/прямокутники), що відповідає вихідному тексту. Справжній текст можна отримати лише шляхом пошуку тексту за координатами.
  • Анотації вмісту (наприклад, примітки, довільний текст) мають власні рядки вмісту, і їх можна прочитати безпосередньо.
  • Якщо ви читаєте annot.info["content"] безпосередньо, не розрізняючи тип, ви втратите оригінальний контекст, на який вказує анотація класу тегу, або створите виняток, якщо вміст порожній.

Приклад синтезу: пошарове вилучення та перехоплення конфіденційної інформації

Щоб продемонструвати правильну логіку обробки, ми створили синтетичний сценарій, що містить три типові анотації в коді. Наступні вхідні дані є зразками, побудованими в коді, а не з файлів реальних клієнтів, і призначені лише для освітніх демонстраційних цілей.

Очікуваний результат містить три структуровані поля:

  1. Текстовий фрагмент: містить лише оригінальний текст сторінки без анотацій.
  2. Список анотацій: кожен запис містить тип, місце, автора, час і вміст (якщо є).
  3. Знак безпеки: якщо вміст анотації відповідає попередньо встановленим конфіденційним правилам (таким як ідентифікаційний номер, внутрішній код), запис позначається як redacted=True, а вміст замінюється заповнювачем.

Три типи кордонів, які потрібно захищати

  • Пуста анотація вмісту: Рукописні (Ink) або графічні анотації, створені деякими читачами, не мають текстових атрибутів. Прямий доступ до .info["content"] може призвести до порожнього рядка або None, код повинен виконувати захисні перевірки та не може вважати, що всі анотації мають читабельний текст.
  • Координати повернутої сторінки: після того, як сторінку повернуто на 90 градусів, прямокутник анотації все ще базується на неповернутій сторінці. Якщо ви безпосередньо використовуєте ці координати для обрізання або позиціонування тексту, положення буде повністю зіпсовано, і спочатку потрібно виконати перетворення координат.
  • Orphan Object: якщо документ закрито або сторінку перезавантажено під час процесу проходження, поточний об’єкт annot негайно стане недійсним. Усі операції читання мають бути виконані протягом однієї сторінки.

Реалізація Python: безпечний обхід і перевірка анотацій

У прикладі використовується PyMuPDF для створення тестового PDF-файлу в пам’яті, демонструючи ієрархічне вилучення та базову логіку маскування. Залежною бібліотекою є PyMuPDF (тобто пакет pymupdf). Запустіть його в заблокованому середовищі залежностей. Для повного доступу до атрибута annot версія має бути >=1.23.0. Цей код не виконує мережевих операцій або запису файлів, він використовується лише для демонстрації логіки.

import fitz  # PyMuPDF
import re

# 合成示例:仅在内存中构造,无需外部文件
def create_sample_pdf():
    doc = fitz.open()
    page = doc.new_page()
    # 插入正文
    page.insert_text((72, 72), "Confidential Project Alpha Report", fontsize=12)
    # 添加便签批注(含敏感信息模拟)
    page.add_text_annot((200, 72), "Reviewer: SSN 123-45-6789 approved.")
    # 添加高亮批注(无自身文本,关联正文)
    highlight = page.add_highlight_annot(fitz.Rect(72, 60, 250, 80))
    # 必须调用update()使info变更生效
    highlight.set_info(title="Editor", content="Check this term")
    highlight.update()
    return doc

def extract_with_safety(page, sensitive_pattern=r"\d{3}-\d{2}-\d{4}"):
    body_text = page.get_text("text")
    annotations = []

    # 显式遍历批注生成器
    for annot in page.annots():
        info = annot.info
        # 防御性获取内容,处理None或空字符串
        content = info.get("content", "") or ""

        # 边界处理:敏感词检测与脱敏
        is_sensitive = bool(re.search(sensitive_pattern, content))
        safe_content = "[REDACTED]" if is_sensitive else content

        annotations.append({
            "type": annot.type[1],
            "author": info.get("title", "Unknown"),
            "content": safe_content,
            "is_redacted": is_sensitive,
            # 注意:此为未旋转坐标系,若页面有旋转需配合rotation_matrix使用
            "rect": list(annot.rect)
        })
    return body_text, annotations

# 执行校验
doc = create_sample_pdf()
page = doc[0]
text, annots = extract_with_safety(page)

assert "SSN 123-45-6789" not in str(annots), "敏感信息未被拦截"
assert any(a["type"] == "Highlight" for a in annots), "标记类批注丢失"
assert len(text.strip()) > 0, "正文提取失败"
print(f"Extracted {len(annots)} annotations safely.")

За своєю суттю код є захисним програмуванням: не можна довіряти чистоті вмісту анотації, а також не передбачається цілісність типу анотації. Оператор assert використовується для перевірки очікуваної логіки, але це лише демонстрація та не гарантує наскрізну продуктивність для всіх варіантів PDF.

Порівняння категорій анотацій

Тип анотаціїЧи принести власний текстЯк отримати контентНа що слід звернути увагу при передачі
Виділіть, підкреслітьНі, лише зони запису (Quads/Rects)Перевірте текст за допомогою координатНеобхідно зберегти відповідний контекст вихідного тексту
Примітки, вільний текстТак, поставляється з власним рядком вмістучитати прямоПеревірте наявність конфіденційного вмісту
Туш, графіка, печаткиЧасто відсутній текстовий атрибутЗробіть захисний короткий викликВручну перевірте, чи це підпис чи печатка затвердження

Критерії прийняття та межі перевірки вручну

Автоматизоване вилучення не може охопити всі крайові випадки. У наведеній нижче таблиці наведено точки прийняття, на які слід звернути увагу під час передачі, розрізняючи обсяг, який може обробляти код, і сценарії, які потребують ручного втручання.

Спостерігайте за явищамиОбробка рівня кодуЛюдський аналіз або прийняття рішень
Вміст коментаря порожній, але значок існуєТип запису – чорнило або штамп, вміст залишається порожнімПеревірте, чи це підпис чи печатка затвердження, і вирішіть, чи зберігати знімок зображення
Позиція коментаря перевищує CropBoxКоординати все ще дійсні, але відображення не видноВизначте, чи це залишена анотація, що залишилася з історії, яку зазвичай можна ігнорувати.
Текстові коментарі містять теги HTMLВилучити оригінальний рядок, зберегти тегиОчистіть теги або перетворите їх на звичайний текст перед перекладом, щоб уникнути перекладів, що містять код.
Ім’я автора анотації спотвореноВитягніть як є, без припущень щодо кодуванняПеревірте метадані вихідного файлу, щоб переконатися, що це назва певного кодування
Вкладена ланцюжок відповідейPyMuPDF повертає плоский списокПеребудуйте діалогове дерево на основі ParentID, інакше контекст буде втрачено

Не застосовується: це рішення не застосовується до зашифрованих PDF-файлів без надання пароля, структурно пошкоджених PDF-файлів і файлів, які використовують нестандартні приватні розширення для визначення типів анотацій. У таких ситуаціях сценарій може мовчки пропустити або створити виняток C++ низького рівня, який потрібно підтвердити, відкривши вихідний файл вручну за допомогою професійних інструментів.

Використовуйте контрольні списки для перекладу документів

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

OmniDoc надає багатоформатний портал перекладу документів, а на офіційному веб-сайті представлені функції OCR, глосарій і двомовне порівняння. Якщо команда хоче розпочати обробку безпосередньо з файлу, цей шлях можна оцінити за допомогою реальних зразків. Використовуйте десенсибілізовані файли, на завантаження яких у вас є дозвіл, і переглядайте результати відповідно до пунктів перевірки в цій статті. Тут немає жодних тверджень про те, що OmniDoc використовує бібліотеки з відкритим кодом або внутрішні алгоритми цієї статті, і цей приклад не замінює тестування продуктивності продукту.

Посилання та наступні кроки

  1. Виберіть історичний файл проекту, який містить змішані типи анотацій.
  2. Запустіть наведений вище сценарій перевірки та підрахуйте частку записів, які потребують перегляду вручну.
  3. Якщо співвідношення перевищує очікування, додайте етап попередньої обробки до процесу передачі замість того, щоб покладатися на наступні інструменти для надання відповіді.

🎯 Який спосіб вам підходить?

Якщо потрібно лише перекласти текст

Ви можете безпосередньо витягти текст, але вам потрібно підтвердити, чи немає жодних анотацій у експортованому вмісті.

Якщо ви хочете передати документ комусь іншому для архівування

Водночас збережіть основний текст і список анотацій і позначте конфіденційний вміст як десенсибілізований.

Якщо ваш PDF-файл обертав сторінки

Спочатку виконайте перетворення координат, а потім використовуйте позицію анотації, щоб знайти текст.

Якщо файл зашифрований або структурно пошкоджений

Це рішення не застосовується. Будь ласка, вручну скористайтеся професійними інструментами, щоб відкрити вихідний файл для підтвердження.

❓ Часті запитання (FAQ)

1. Чому get_text() не містить анотацій?

Типовий вихід зазвичай містить лише тіло сторінки. Анотації є об’єктами, незалежними від текстового потоку сторінки, і їх потрібно обробляти окремо через page.annots().

2. Чи можна прочитати текст усіх коментарів безпосередньо?

не можна. Анотації типу позначок, такі як виділення та підкреслення, самі по собі не містять тексту, а лише записують області; рукописні та графічні анотації також можуть не мати текстових атрибутів і вимагати захисного судження.

3. Чому позиція сторінки змінюється після її повороту?

Прямокутник анотації базується на неповернутій сторінці. Якщо його використовувати безпосередньо для розміщення тексту, це спричинить помилки. Для перетворення координат спочатку потрібно використати page.rotation_matrix або page.derotation_matrix.

4. Чи буде втрачено вкладені відповіді?

PyMuPDF повертає плоский список, і якщо діалогове дерево не реконструювати на основі ParentID, контекст буде втрачено.

5. Чи можна обробити зашифрований PDF за допомогою цього сценарію?

PDF-файли, які зашифровані та не містять пароля, не входять до сфери дії цього рішення та потребують підтвердження вручну.

📝 Підсумок

Анотації є незалежним рівнем даних у PDF. Перед передачею текст і анотації слід витягти окремо, а анотації слід переглянути на наявність конфіденційного вмісту. Автоматизовані сценарії можуть охоплювати звичайні випадки, але крайні випадки та остаточне прийняття все одно потрібно виконувати вручну.

Спробуйте зараз: завантажте документ, щоб розпочати переклад, зберігаючи вихідний макет

Оновлено 2026-11-14