Como trabalhar com anotações PDF em Python
Aspose.PDF FOSS para Python expõe cada anotação em uma página como um objeto Annotation ao vivo através do AnnotationCollection da página, permitindo adicionar anotações de marcação, link e 3D, inspecionar suas propriedades específicas de tipo e gerar os fluxos de aparência que os visualizadores de PDF precisam para renderizá-las — tudo a partir de puro Python. Este guia mostra como instalar a biblioteca, adicionar anotações, lê-las de volta e gerar seus fluxos de aparência.
Guia passo a passo
Etapa 1: Instalar o pacote
Instale o pacote 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 .Verifique a instalação importando o módulo de nível superior aspose_pdf, que expõe a classe Document usada nas etapas abaixo. O trecho imprime aspose_pdf OK quando a importação tem sucesso; um ModuleNotFoundError geralmente indica que o pacote foi instalado em um ambiente Python diferente daquele em que seu script está sendo executado:
import aspose_pdf
print("aspose_pdf OK")Etapa 2: Importar Classes Necessárias
Importe Document para conter o PDF, além das classes de anotação que você usará para criar e inspecionar anotações:
from aspose_pdf import Document
from aspose_pdf.annotations import (
Annotation,
AnnotationType,
AnnotationFlags,
MarkupAnnotation,
LinkAnnotation,
)Etapa 3: Adicionar uma Anotação a uma Página
Cada Page expõe suas anotações através da propriedade annotations, um AnnotationCollection. Chame add(subtype, rect, contents) com um nome de subtipo, um retângulo (left, bottom, right, top) e o conteúdo de texto da anotação. Subtipos de marcação padrão, como "Highlight", são retornados como uma instância de 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 clauseEtapa 4: Criar Anotações a partir de Subtipos Enum
AnnotationType enumera cada subtipo padrão de anotação PDF (PDF 32000-1:2008, Tabela 169), então você pode passar um membro enum em vez de uma string bruta. Dados específicos de tipo — como os vértices de um polígono — vão no dicionário properties e são lidos novamente com 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]Etapa 5: Ler e Iterar Anotações Existentes
AnnotationCollection é iterável, então você pode percorrer cada anotação já presente em uma página — incluindo as carregadas de um PDF existente — e ler as propriedades comuns que cada Annotation expõe (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)Etapa 6: Gerar fluxos de aparência de anotação
Uma anotação criada sem um appearance_normal explícito não tem renderização visível (/AP /N) até que um seja gerado. Chame generate_appearance(force) em um único Annotation, ou generate_appearances(force) em todo o AnnotationCollection para sintetizar todos os fluxos de aparência ausentes na página de uma só vez:
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)")Etapa 7: Diferenciar subclasses de anotação
AnnotationCollection.add() despacha para uma subclasse específica com base no subtipo: subtipos de estilo markup (Highlight, Square, Stamp e similares) retornam como MarkupAnnotation, e "Link" retorna como LinkAnnotation. Ambos herdam todos os métodos e propriedades de Annotation, portanto as verificações de isinstance permitem que você faça ramificações segundo o tipo de anotação sem inspecionar diretamente as strings de 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)) # TrueProblemas comuns e correções
get_property returns None para uma propriedade que acabei de definir
As chaves properties são nomes de campo PDF exatos e sensíveis a maiúsculas/minúsculas ("Vertices", "Name" e similares) — um erro de digitação ou caso incorreto é ignorado silenciosamente em vez de gerar erro. Passe um argumento default para get_property(name, default) e verifique-o explicitamente ao depurar um novo subtipo.
generate_appearance() returns False
Nem toda combinação de subtipo e propriedade pode ser sintetizada em um fluxo de aparência pelo gerador interno. Verifique has_appearance antes de assumir que a chamada teve sucesso, e forneça um appearance_normal pré-renderizado (bytes) diretamente em add() para subtipos que o gerador não cobre.
Subclasse de anotação inesperada após add()
A string de subtipo (ou membro AnnotationType) que você fornece determina a classe retornada: subtipos de marcação retornam como MarkupAnnotation, "Link" retorna como LinkAnnotation, e qualquer outro subtipo reconhecido retorna como o Annotation base. Use isinstance() contra MarkupAnnotation/LinkAnnotation ao invés de assumir uma string de subtipo específica.
AnnotationFlags os valores não parecem mudar a renderização
AnnotationFlags (PRINT, HIDDEN, NO_ZOOM, READ_ONLY e similares) é um enum padrão Python IntFlag para compor e interpretar os bits de comportamento de uma anotação com o operador | — é um tipo de valor, não uma propriedade que Annotation.add() grava automaticamente. Combine as flags que você precisa e passe-as através do mesmo mecanismo properties específico de tipo usado para outros dados específicos de subtipo.
Trabalhando com uma página que ainda não tem anotações
page.annotations é sempre um AnnotationCollection válido (possivelmente vazio) — você nunca precisa verificar None antes de chamar add(), iterar ou chamar clear().
Perguntas Frequentes
Quais subtipos de anotação Aspose.PDF FOSS para Python suporta?
AnnotationType enumera todos os 25 subtipos padrão da PDF 32000-1:2008 Tabela 169, incluindo TEXT, LINK, FREE_TEXT, LINE, SQUARE, CIRCLE, POLYGON, HIGHLIGHT, STAMP, INK, FILE_ATTACHMENT, REDACT e mais.
Esta biblioteca suporta anotações 3D?
Sim. PDF3DAnnotation, juntamente com PDF3DArtwork, PDF3DContent, PDF3DView, PDF3DLightingScheme, e PDF3DRenderMode, modela uma superfície mínima de anotação PDF 3D (um retângulo, arte incorporada e visualizações nomeadas) para fluxos de trabalho PDF 3D em pré-lançamento. Veja o referência PDF3DAnnotation para seu conjunto completo de propriedades.
Como remover uma anotação de uma página?
Chame page.annotations.delete(index) para remover uma anotação por posição, ou page.annotations.clear() para remover todas as anotações da página.
Posso inserir uma anotação em uma posição específica em vez de adicioná-la ao final?
Sim — AnnotationCollection.insert(index, subtype, rect, contents, title, appearance_normal, properties) aceita os mesmos argumentos que add() mais o index de destino.
Como a cor de uma anotação é representada?
A propriedade color em Annotation (e suas subclasses) é um tuple[float, ...] que corresponde ao número de componentes da entrada /C do PDF (vazio quando a cor não está definida) — não um objeto de cor dedicado.