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

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

Основные положения этой статьи

— Текст и комментарии — это два независимых слоя данных. Page.get_text() по умолчанию не содержит комментариев и его необходимо извлечь отдельно. — Выделенные, подчеркнутые и другие аннотации разметки не содержат текста, а исходный текстовый контекст необходимо получить путем обратной проверки текста на основе координат. – При извлечении аннотаций контент следует проверять на наличие конфиденциальной информации, а элементы, соответствующие правилам, необходимо дезинтегрировать и помечать.

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

Прежде чем передать аннотации 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, а содержимое заменяется заполнителем.

Три типа границ, которые нужно защищать

  • Пустая аннотация к содержимому: рукописные (чернила) или графические аннотации, созданные некоторыми читателями, не имеют текстовых атрибутов. Прямой доступ к .info["content"] может привести к появлению пустой строки или None. Код должен выполнять защитные проверки и не может предполагать, что все аннотации имеют читаемый текст.
  • Координаты повернутой страницы: после поворота страницы на 90 градусов прямоугольник аннотации по-прежнему основан на неповернутой странице. Если вы напрямую используете эти координаты для обрезки или позиционирования текста, позиция будет полностью перепутана, и сначала необходимо выполнить преобразование координат.
  • Особый объект: если документ закрыт или страница перезагружена во время процесса обхода, текущий объект аннотации немедленно станет недействительным. Все операции чтения должны быть завершены в течение времени жизни одной страницы.

Реализация 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.

Сравнение категорий аннотаций

Тип аннотацииПриносить ли свой собственный текстКак получить контентНа что обратить внимание при сдаче
Выделить, подчеркнутьНет, записываются только области (квадрицепсы/прямые мышцы)Проверьте текст по координатамСоответствующий исходный текстовый контекст должен быть сохранен.
Примечания, произвольный текстДа, имеет собственную строку содержимогочитать напрямуюПроверьте конфиденциальный контент
Чернила, графика, печатиЧасто нет текстового атрибутаСделайте защитный короткий коллВручную подтвердите, является ли это подписью или печатью утверждения.

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

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

Наблюдайте за явлениямиОбработка на уровне кодаЧеловеческий обзор или принятие решений
Содержимое комментария пусто, но значок существует.Тип записи — «Чернила» или «Штамп», содержимое остается пустым.Подтвердите, является ли это подписью или штампом утверждения, и решите, следует ли сохранять снимок изображения.
Позиция комментария превышает CropBoxКоординаты все еще действительны, но рендеринг не виден.Определите, не является ли это заброшенной аннотацией, оставшейся от истории, которую обычно можно игнорировать.
Комментарии с форматированным текстом содержат HTML-теги.Извлечь исходную строку, сохранить тегиОчистите теги или преобразуйте их в обычный текст перед переводом, чтобы избежать переводов, содержащих код.
Имя автора аннотации искаженоИзвлекайте как есть, без догадок о кодировкеПроверьте метаданные исходного файла, чтобы убедиться, что это имя конкретной кодировки.
Вложенная цепочка ответовPyMuPDF возвращает плоский списокПерестроить дерево диалогов на основе ParentID, иначе контекст теряется

Неприменимо. Это решение не применимо к зашифрованным PDF-файлам без указания пароля, PDF-файлам со структурными повреждениями и файлам, которые используют нестандартные частные расширения для определения типов аннотаций. В таких ситуациях сценарий может молча пропустить или выдать низкоуровневое исключение C++, что необходимо подтвердить, открыв исходный файл вручную с помощью профессиональных инструментов.

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

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

OmniDoc предоставляет многоформатный портал для перевода документов, а на официальном сайте представлены функции оптического распознавания символов, глоссария и двуязычного сравнения. Если команда хочет начать обработку непосредственно из файла, этот путь можно оценить на реальных образцах. Используйте десенсибилизированные файлы, на загрузку которых у вас есть разрешение, и просмотрите результаты в соответствии с пунктами проверки в этой статье. Здесь не утверждается, что 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