Как работать с аннотациями PDF в Python
Aspose.PDF FOSS для Python раскрывает каждую аннотацию на странице как живой объект Annotation через AnnotationCollection страницы, так что вы можете добавлять разметку, ссылки и 3D-аннотации, просматривать их свойства, специфичные для типа, и генерировать потоки отображения, необходимые PDF-просмотрщикам для их рендеринга — всё это из чистого Python. Это руководство показывает, как установить библиотеку, добавить аннотации, прочитать их обратно и сгенерировать их потоки отображения.
Пошаговое руководство
Шаг 1: Установить пакет
Установите пакет Aspose.PDF FOSS:
git clone https://github.com/aspose-pdf-foss/Aspose-PDF-FOSS-for-Python.git
cd Aspose-PDF-FOSS-for-Python
pip install -e .Проверьте установку, импортировав верхнеуровневый модуль aspose_pdf, который раскрывает класс Document, используемый в последующих шагах. Фрагмент выводит aspose_pdf OK, когда импорт проходит успешно; ModuleNotFoundError обычно означает, что пакет был установлен в другую среду Python, чем та, в которой выполняется ваш скрипт:
import aspose_pdf
print("aspose_pdf OK")Шаг 2: Импортировать необходимые классы
Импортируйте Document, чтобы хранить PDF, а также классы аннотаций, которые вы будете использовать для создания и проверки аннотаций:
from aspose_pdf import Document
from aspose_pdf.annotations import (
Annotation,
AnnotationType,
AnnotationFlags,
MarkupAnnotation,
LinkAnnotation,
)Шаг 3: Добавить аннотацию на страницу
Каждый Page раскрывает свои аннотации через свойство annotations, которое является AnnotationCollection. Вызовите add(subtype, rect, contents), указав имя подтипа, прямоугольник (left, bottom, right, top) и текстовое содержимое аннотации. Стандартные подтипы разметки, такие как "Highlight", возвращаются как экземпляр MarkupAnnotation:
from aspose_pdf import Document
document = Document()
document.pages.add()
page = document.pages[0]
annotation = page.annotations.add(
"Highlight",
(100, 700, 300, 720),
"Key clause",
)
print(type(annotation).__name__) # MarkupAnnotation
print(annotation.contents) # Key clauseШаг 4: Создать аннотации из перечислений подтипов
AnnotationType перечисляет каждый стандартный подтип PDF-аннотации (PDF 32000-1:2008, таблица 169), поэтому вы можете передавать член перечисления вместо обычной строки. Данные, специфичные для типа — например, вершины многоугольника — помещаются в словарь properties и считываются обратно с помощью get_property:
from aspose_pdf import Document
from aspose_pdf.annotations import AnnotationType
document = Document()
document.pages.add()
page = document.pages[0]
annotation = page.annotations.add(
AnnotationType.POLYGON,
(0, 0, 10, 10),
"",
properties={"Vertices": [0, 0, 10, 0, 5, 10]},
)
print(annotation.subtype) # Polygon
print(annotation.get_property("Vertices")) # [0, 0, 10, 0, 5, 10]Шаг 5: Читать и перебирать существующие аннотации
AnnotationCollection является итерируемым, поэтому вы можете пройтись по каждой аннотации, уже находящейся на странице — включая загруженные из существующего PDF — и прочитать общие свойства, которые раскрывает каждый Annotation (subtype, contents, rect, title, author, color):
from aspose_pdf import Document
document = Document()
document.pages.add()
page = document.pages[0]
page.annotations.add("Text", (50, 50, 70, 70), "First note")
page.annotations.add("Text", (80, 80, 100, 100), "Second note")
for annotation in page.annotations:
print(annotation.subtype, "-", annotation.contents)Шаг 6: Сгенерировать потоки отображения аннотации
Аннотация, созданная без явного appearance_normal, не имеет видимого отображения (/AP /N), пока оно не будет сгенерировано. Вызовите generate_appearance(force) для отдельного Annotation или generate_appearances(force) для всего AnnotationCollection, чтобы синтезировать все недостающие потоки отображения на странице одновременно:
from aspose_pdf import Document
from aspose_pdf.engine.cos import AnnotationName
document = Document()
document.pages.add()
page = document.pages[0]
stamp = page.annotations.add(
"Stamp",
(100, 100, 260, 150),
"",
properties={"Name": AnnotationName("Approved")},
)
if stamp.generate_appearance():
print(f"Appearance stream: {len(stamp.appearance_normal)} byte(s)")
# Regenerate every missing appearance stream on the page in one call:
count = page.annotations.generate_appearances(force=True)
print(f"Regenerated {count} appearance stream(s)")Шаг 7: Различать подклассы аннотаций
AnnotationCollection.add() перенаправляет к конкретному подклассу в зависимости от подтипа: подтипы в стиле разметки (Highlight, Square, Stamp и подобные) возвращаются как MarkupAnnotation, а "Link" возвращается как LinkAnnotation. Оба наследуют каждый метод и свойство от Annotation, поэтому проверки isinstance позволяют вам ветвиться по типу аннотации, не проверяя строки subtype напрямую:
from aspose_pdf import Document
from aspose_pdf.annotations import LinkAnnotation, MarkupAnnotation
document = Document()
document.pages.add()
page = document.pages[0]
link = page.annotations.add("Link", (50, 750, 200, 770), "")
highlight = page.annotations.add("Highlight", (50, 700, 200, 720), "")
print(isinstance(link, LinkAnnotation)) # True
print(isinstance(highlight, MarkupAnnotation)) # TrueОбщие проблемы и решения
get_property returns None для свойства, которое я только что установил
properties ключи — это точные, чувствительные к регистру имена полей PDF ("Vertices", "Name" и подобные) — опечатка или неверный регистр игнорируются без предупреждения. Передайте аргумент default в get_property(name, default) и проверяйте его явно при отладке нового подтипа.
generate_appearance() returns False
Не каждая комбинация подтипа и свойства может быть синтезирована в поток отображения встроенным генератором. Проверьте has_appearance перед тем как предполагать, что вызов завершился успешно, и предоставьте предварительно отрисованный appearance_normal (bytes) непосредственно на add() для подтипов, которые генератор не охватывает.
Неожиданный подкласс аннотации после add()
Строка подтипа (или член AnnotationType), которую вы передаёте, определяет возвращаемый класс: подтипы разметки возвращаются как MarkupAnnotation, "Link" возвращается как LinkAnnotation, а любой другой распознанный подтип возвращается как базовый Annotation. Используйте isinstance() против MarkupAnnotation/LinkAnnotation, а не предполагая конкретную строку подтипа.
AnnotationFlags значения, похоже, не меняют рендеринг
AnnotationFlags (PRINT, HIDDEN, NO_ZOOM, READ_ONLY и подобные) — это стандартный Python IntFlag enum для составления и интерпретации бит поведения аннотации с оператором | — это тип значения, а не свойство, которое Annotation.add() записывает автоматически. Скомбинируйте необходимые флаги и передайте их через тот же типо-специфичный механизм properties, используемый для других данных, специфичных для подтипа.
Работа со страницей, на которой пока нет аннотаций
page.annotations всегда является допустимым (возможно пустым) AnnotationCollection — вам никогда не нужно проверять наличие None перед вызовом add(), при итерации или вызове clear().
Часто задаваемые вопросы
Какие подтипы аннотаций поддерживает Aspose.PDF FOSS для Python?
AnnotationType перечисляет все 25 стандартных подтипов PDF 32000-1:2008 Таблица 169, включая TEXT, LINK, FREE_TEXT, LINE, SQUARE, CIRCLE, POLYGON, HIGHLIGHT, STAMP, INK, FILE_ATTACHMENT, REDACT и др.
Поддерживает ли эта библиотека 3D-аннотации?
Да. PDF3DAnnotation, вместе с PDF3DArtwork, PDF3DContent, PDF3DView, PDF3DLightingScheme, и PDF3DRenderMode, моделирует минимальную поверхность PDF 3D аннотации (прямоугольник, встроенный графический объект и именованные представления) для предрелизных PDF 3D рабочих процессов. Смотрите справка PDF3DAnnotation для полного набора свойств.
Как удалить аннотацию со страницы?
Вызовите page.annotations.delete(index), чтобы удалить одну аннотацию по позиции, или page.annotations.clear(), чтобы удалить все аннотации на странице.
Могу ли я вставить аннотацию в конкретную позицию вместо добавления в конец?
Да — AnnotationCollection.insert(index, subtype, rect, contents, title, appearance_normal, properties) принимает те же аргументы, что и add(), плюс целевой index.
Как представляется цвет аннотации?
Свойство color у Annotation (и его подклассов) является tuple[float, ...], соответствующим количеству компонентов записи PDF /C (пустым, когда цвет не задан) — не отдельный объект цвета.