Jak pracować z adnotacjami PDF w Python

Jak pracować z adnotacjami PDF w Python

Aspose.PDF FOSS dla Python ujawnia każdą adnotację na stronie jako żywy obiekt Annotation poprzez AnnotationCollection strony, dzięki czemu możesz dodawać adnotacje znakowania, linki i 3D, przeglądać ich właściwości specyficzne dla typu oraz generować strumienie wyglądu, które przeglądarki PDF potrzebują do ich renderowania — wszystko z czystego Python. Ten przewodnik pokazuje, jak zainstalować bibliotekę, dodać adnotacje, odczytać je i wygenerować ich strumienie wyglądu.

Przewodnik krok po kroku

Krok 1: Zainstaluj pakiet

Zainstaluj pakiet 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 .

Zweryfikuj instalację, importując moduł aspose_pdf najwyższego poziomu, który udostępnia klasę Document używaną w poniższych krokach. Fragment kodu wypisuje aspose_pdf OK, gdy import się powiedzie; ModuleNotFoundError zazwyczaj oznacza, że pakiet został zainstalowany w innym środowisku Python niż to, w którym uruchamiany jest Twój skrypt:

import aspose_pdf
print("aspose_pdf OK")

Krok 2: Importuj wymagane klasy

Importuj Document, aby przechowywać PDF, oraz klasy adnotacji, których użyjesz do tworzenia i przeglądania adnotacji:

from aspose_pdf import Document
from aspose_pdf.annotations import (
    Annotation,
    AnnotationType,
    AnnotationFlags,
    MarkupAnnotation,
    LinkAnnotation,
)

Krok 3: Dodaj adnotację do strony

Każdy Page udostępnia swoje adnotacje poprzez właściwość annotations, będącą AnnotationCollection. Wywołaj add(subtype, rect, contents) z nazwą podtypu, prostokątem (left, bottom, right, top) oraz treścią tekstową adnotacji. Standardowe podtypy znaczników, takie jak "Highlight", są zwracane jako instancja 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

Krok 4: Tworzenie adnotacji z podtypów wyliczeniowych

AnnotationType wylicza każdy standardowy podtyp adnotacji PDF (PDF 32000-1:2008, Tabela 169), więc możesz przekazać członka wyliczenia zamiast surowego łańcucha. Dane specyficzne dla typu — takie jak wierzchołki wielokąta — umieszczane są w słowniku properties i odczytywane przy pomocy 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]

Krok 5: Odczyt i iteracja istniejących adnotacji

AnnotationCollection jest iterowalny, więc możesz przejść przez każdą adnotację już znajdującą się na stronie — w tym te załadowane z istniejącego PDF — i odczytać wspólne właściwości, które udostępnia każdy 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)

Krok 6: Wygeneruj strumienie wyglądu adnotacji

Adnotacja utworzona bez wyraźnego appearance_normal nie ma widocznego renderowania (/AP /N), dopóki nie zostanie wygenerowane. Wywołaj generate_appearance(force) na pojedynczym Annotation lub generate_appearances(force) na całym AnnotationCollection, aby jednorazowo stworzyć wszystkie brakujące strumienie wyglądu na stronie:

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)")

Krok 7: Rozróżnij podklasy adnotacji

AnnotationCollection.add() przekierowuje do konkretnej podklasy w zależności od podtypu: podtypy w stylu markup (Highlight, Square, Stamp i podobne) zwracają MarkupAnnotation, a "Link" zwraca LinkAnnotation. Oba dziedziczą wszystkie metody i właściwości z Annotation, więc sprawdzenia isinstance pozwalają rozgałęzić się w zależności od rodzaju adnotacji bez bezpośredniego sprawdzania ciągów subtype bezpośrednio:

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

Typowe problemy i rozwiązania

get_property returns None dla właściwości, którą właśnie ustawiłem

Klucze properties są dokładnymi, rozróżniającymi wielkość liter nazwami pól PDF ("Vertices", "Name" i podobne) — literówka lub niewłaściwa wielkość liter jest cicho ignorowana zamiast zgłosić błąd. Przekaż argument default do get_property(name, default) i sprawdź go explicite podczas debugowania nowego podtypu.

generate_appearance() returns False

Nie każda kombinacja podtypu i właściwości może być syntetyzowana do strumienia wyglądu przez wbudowany generator. Sprawdź has_appearance przed założeniem, że wywołanie się powiodło, i dostarcz wstępnie wyrenderowany appearance_normal (bytes) bezpośrednio na add() dla podtypów, których generator nie obsługuje.

Nieoczekiwana podklasa adnotacji po add()

Ciąg podtypu (lub członek AnnotationType), który przekazujesz, określa zwracaną klasę: podtypy znaczników zwracają MarkupAnnotation, "Link" zwraca LinkAnnotation, a każdy inny rozpoznany podtyp zwraca podstawowy Annotation. Użyj isinstance() względem MarkupAnnotation/LinkAnnotation zamiast zakładać konkretny ciąg podtypu.

AnnotationFlags wartości nie wydają się zmieniać renderowanie

AnnotationFlags (PRINT, HIDDEN, NO_ZOOM, READ_ONLY i podobne) jest standardowym Python IntFlag wyliczeniem służącym do komponowania i interpretowania bitów zachowania adnotacji przy użyciu operatora | — jest typem wartości, a nie właściwością, którą Annotation.add() zapisuje automatycznie. Połącz potrzebne flagi i przekaż je przez ten sam typowo-specyficzny mechanizm properties używany dla innych danych specyficznych dla podtypu.

Praca ze stroną, która nie ma jeszcze adnotacji

page.annotations jest zawsze prawidłowym (możliwe że pustym) AnnotationCollection — nigdy nie musisz sprawdzać None przed wywołaniem add(), iteracją lub wywołaniem clear().

Najczęściej zadawane pytania

Jakie podtypy adnotacji obsługuje Aspose.PDF FOSS dla Python?

AnnotationType wymienia wszystkie 25 standardowych podtypów tabeli 169 PDF 32000-1:2008, w tym TEXT, LINK, FREE_TEXT, LINE, SQUARE, CIRCLE, POLYGON, HIGHLIGHT, STAMP, INK, FILE_ATTACHMENT, REDACT oraz inne.

Czy ta biblioteka obsługuje adnotacje 3D?

Tak. PDF3DAnnotation, razem z PDF3DArtwork, PDF3DContent, PDF3DView, PDF3DLightingScheme, oraz PDF3DRenderMode, modeluje minimalną powierzchnię adnotacji PDF 3D (prostokąt, osadzona grafika i nazwane widoki) dla wstępnych wersji przepływów pracy PDF 3D. Zobacz odwołanie PDF3DAnnotation dla pełnego zestawu właściwości.

Jak usunąć adnotację ze strony?

Wywołaj page.annotations.delete(index), aby usunąć jedną adnotację według pozycji, lub page.annotations.clear(), aby usunąć wszystkie adnotacje na stronie.

Czy mogę wstawić adnotację w określonej pozycji zamiast dołączania jej na końcu?

Tak — AnnotationCollection.insert(index, subtype, rect, contents, title, appearance_normal, properties) przyjmuje te same argumenty co add() plus docelowy index.

Jak reprezentowany jest kolor adnotacji?

Właściwość color w Annotation (i jej podklasach) jest tuple[float, ...] dopasowaną do liczby komponentów wpisu PDF /C (pusta, gdy kolor nie jest ustawiony) — nie dedykowanym obiektem koloru.

Zobacz także

 Polski