Comment travailler avec les annotations PDF dans Python

Comment travailler avec les annotations PDF dans Python

Aspose.PDF FOSS pour Python expose chaque annotation sur une page comme un objet Annotation vivant via le AnnotationCollection de la page, ainsi vous pouvez ajouter des annotations de balisage, de lien et 3D, inspecter leurs propriétés spécifiques au type, et générer les flux d’apparence dont les visionneuses PDF ont besoin pour les rendre — le tout depuis du Python pur. Ce guide montre comment installer la bibliothèque, ajouter des annotations, les lire, et générer leurs flux d’apparence.

Guide étape par étape

Étape 1: installer le package

Installez le package FOSS Aspose.PDF:

git clone https://github.com/aspose-pdf-foss/Aspose-PDF-FOSS-for-Python.git
cd Aspose-PDF-FOSS-for-Python
pip install -e .

Vérifiez l’installation en important le module aspose_pdf de niveau supérieur, qui expose la classe Document utilisée dans les étapes suivantes. L’extrait affiche aspose_pdf OK lorsque l’import réussit; un ModuleNotFoundError signifie généralement que le package a été installé dans un environnement Python différent de celui dans lequel votre script s’exécute:

import aspose_pdf
print("aspose_pdf OK")

Étape 2 : Importer les classes requises

Importez Document pour contenir le PDF, ainsi que les classes d’annotation que vous utiliserez pour créer et inspecter les annotations:

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

Étape 3 : Ajouter une annotation à une page

Chaque Page expose ses annotations via la propriété annotations, un AnnotationCollection. Appelez add(subtype, rect, contents) avec un nom de sous-type, un rectangle (left, bottom, right, top) et le contenu texte de l’annotation. Les sous-types de balisage standard tels que "Highlight" sont retournés sous forme d’instance 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

Étape 4 : Créer des annotations à partir de sous-types d’énumération

AnnotationType répertorie chaque sous-type d’annotation PDF standard (PDF 32000-1:2008, tableau 169), de sorte que vous puissiez fournir un membre d’énumération au lieu d’une chaîne brute. Les données spécifiques au type — par exemple les sommets d’un polygone — sont placées dans le dictionnaire properties et sont récupérées avec 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]

Étape 5 : Lire et parcourir les annotations existantes

AnnotationCollection est itérable, vous pouvez donc parcourir chaque annotation déjà présente sur une page — y compris celles chargées depuis un PDF existant — et lire les propriétés communes que chaque Annotation expose (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)

Étape6: Générer les flux d’apparence des annotations

Une annotation créée sans appearance_normal explicite n’a aucun rendu visible (/AP /N) tant qu’il n’est pas généré. Appelez generate_appearance(force) sur une seule Annotation, ou generate_appearances(force) sur l’ensemble du AnnotationCollection pour synthétiser chaque flux d’apparence manquant sur la page en une fois:

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

Étape7: Distinguer les sous-classes d’annotation

AnnotationCollection.add() répartit vers une sous-classe spécifique en fonction du sous-type: les sous-types de type balisage (Highlight, Square, Stamp et similaires) reviennent en tant que MarkupAnnotation, et "Link" revient en tant que LinkAnnotation. Les deux héritent de toutes les méthodes et propriétés de Annotation, ainsi les vérifications isinstance vous permettent de vous ramifier selon le type d’annotation sans inspecter directement les chaînes 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

Problèmes courants et correctifs

get_property returns None pour une propriété que je viens de définir

Les clés properties sont des noms de champs PDF exacts, sensibles à la casse ("Vertices", "Name" et similaires) — une faute de frappe ou une mauvaise casse est silencieusement ignorée plutôt que levée. Passez un argument default à get_property(name, default) et vérifiez-le explicitement lors du débogage d’un nouveau sous-type.

generate_appearance() returns False

Toutes les combinaisons de sous-type et de propriété ne peuvent pas être synthétisées en flux d’apparence par le générateur intégré. Vérifiez has_appearance avant de supposer que l’appel a réussi, et fournissez un appearance_normal pré-rendu (bytes) directement sur add() pour les sous-types que le générateur ne couvre pas.

Sous-classe d’annotation inattendue après add()

La chaîne de sous-type (ou le membre AnnotationType) que vous transmettez détermine la classe renvoyée: les sous-types de balisage reviennent sous la forme MarkupAnnotation, "Link" revient sous la forme LinkAnnotation, et tout autre sous-type reconnu revient sous la forme de base Annotation. Utilisez isinstance() contre MarkupAnnotation/LinkAnnotation plutôt que de supposer une chaîne de sous-type spécifique.

AnnotationFlags les valeurs ne semblent pas modifier le rendu

AnnotationFlags (PRINT, HIDDEN, NO_ZOOM, READ_ONLY, et similaires) est une énumération Python IntFlag standard pour composer et interpréter les bits de comportement d’une annotation avec l’opérateur | — c’est un type valeur, pas une propriété que Annotation.add() écrit automatiquement. Combinez les indicateurs dont vous avez besoin et transmettez-les via le même mécanisme properties spécifique au type utilisé pour d’autres données spécifiques au sous-type.

Travailler avec une page qui n’a pas encore d’annotations

page.annotations est toujours un AnnotationCollection valide (éventuellement vide) — vous n’avez jamais besoin de vérifier None avant d’appeler add(), d’itérer, ou d’appeler clear().

Foire aux questions

Quels sous-types d’annotation Aspose.PDF FOSS pour Python prend-il en charge?

AnnotationType répertorie les 25 sous-types standards du PDF 32000-1:2008 Table 169, y compris TEXT, LINK, FREE_TEXT, LINE, SQUARE, CIRCLE, POLYGON, HIGHLIGHT, STAMP, INK, FILE_ATTACHMENT, REDACT, et d’autres.

Cette bibliothèque prend-elle en charge les annotations 3D?

Oui. PDF3DAnnotation, ainsi que PDF3DArtwork, PDF3DContent, PDF3DView, PDF3DLightingScheme, et PDF3DRenderMode, modélise une surface d’annotation PDF 3D minimale (un rectangle, un artwork intégré et des vues nommées) pour les flux de travail PDF 3D en préversion. Voir le référence PDF3DAnnotation pour son ensemble complet de propriétés.

Comment supprimer une annotation d’une page?

Appelez page.annotations.delete(index) pour supprimer une annotation par position, ou page.annotations.clear() pour supprimer toutes les annotations de la page.

Puis-je insérer une annotation à une position spécifique au lieu de l’ajouter à la fin?

Oui — AnnotationCollection.insert(index, subtype, rect, contents, title, appearance_normal, properties) prend les mêmes arguments que add() plus la cible index.

Comment la couleur d’une annotation est-elle représentée?

La propriété color sur Annotation (et ses sous-classes) est un tuple[float, ...] correspondant au nombre de composants de l’entrée PDF /C (vide lorsque la couleur n’est pas définie) — pas un objet couleur dédié.

Voir aussi

 Français