Come Lavorare con i Dati PDF in Python
Aspose.PDF FOSS per Python rappresenta i metadati XMP di un documento PDF come un modello di dati in memoria costruito a partire da oggetti XmpPacket, XmpField, XmpArray, XmpStruct e XmpProperty, con NamespaceProvider che risolve i prefissi degli spazi dei nomi in URI. Questo ti consente di leggere, costruire e riscrivere metadati strutturati — titoli, date, elenchi di parole chiave, campi personalizzati — senza scrivere manualmente RDF/XML. La libreria è pura Python e viene installata con il comando qui sotto.
Guida passo passo
Passo 1: Installa il pacchetto
Installa il pacchetto 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 .Verifica l’installazione importando XmpPacket e stampando un messaggio di conferma:
from aspose_pdf import XmpPacket
print("aspose-pdf-foss-for-python is ready.")Passo 2: Importa le classi richieste
Importa le classi del modello di dati XMP e il risolutore di namespace:
from aspose_pdf import (
NamespaceProvider,
XmpArray,
XmpField,
XmpPacket,
XmpProperty,
XmpStruct,
)Passo 3: Crea un Packet e imposta le proprietà semplici
XmpPacket() inizia con un elenco di campi vuoto. set_value(prefix, name, value, uri=...) imposta o sostituisce una proprietà semplice, e get(prefix_or_uri, name) la legge nuovamente come un XmpField; get accetta sia il prefisso registrato sia l’URI completo del namespace:
from aspose_pdf import XmpPacket
packet = XmpPacket()
packet.set_value("dc", "title", "Quarterly Report")
packet.set_value(
"dc", "creator", "Automation Pipeline",
uri="http://purl.org/dc/elements/1.1/",
)
title_field = packet.get("dc", "title")
print(title_field.value) # "Quarterly Report"Passo 4: Memorizza valori tipizzati
XmpPacket fornisce accessor di comodità tipizzati che convertono da e verso la forma di testo semplice che XMP memorizza internamente: set_date/get_date, set_int/get_int, set_real/get_real e set_bool/get_bool. Ogni getter restituisce None quando la proprietà è assente invece di sollevare:
from datetime import datetime
packet.set_date("xmp", "CreateDate", datetime(2026, 7, 29, 9, 30))
packet.set_int("pdf", "PageCount", 42)
packet.set_real("custom", "ConfidenceScore", 0.97, uri="https://example.com/ns/custom/1.0/")
packet.set_bool("custom", "IsFinal", True, uri="https://example.com/ns/custom/1.0/")
print(packet.get_date("xmp", "CreateDate"))
print(packet.get_int("pdf", "PageCount"))
print(packet.get_real("custom", "ConfidenceScore"))
print(packet.get_bool("custom", "IsFinal"))Passo 5: Memorizza testo localizzato e array ordinati
set_localized_text scrive una proprietà alternativa per lingua (rdf:Alt) come dc:title in una lingua specifica, predefinita a "x-default". set_array scrive un array ordinato (Seq), non ordinato (Bag) o alternativo (Alt) da un semplice elenco di valori, e get_array lo legge nuovamente come un elenco di stringhe:
packet.set_localized_text("dc", "description", "Quarterly summary", lang="en")
packet.set_localized_text(
"dc", "description", "Resumen trimestral",
uri="http://purl.org/dc/elements/1.1/", lang="es",
)
print(packet.get_localized_text("dc", "description", lang="es"))
packet.set_array("dc", "subject", ["finance", "quarterly", "internal"], kind="Bag")
print(packet.get_array("dc", "subject"))Passo 6: Crea valori strutturati con XmpStruct
Alcune proprietà XMP (record di dimensione, voci di cronologia) sono valori strutturati anziché testo semplice. Creane uno con XmpStruct, aggiungi oggetti membro XmpField con add, e leggi un membro per nome con get, quindi allega la struttura al pacchetto con add:
dimensions = XmpStruct()
dimensions.add(XmpField(prefix="stDim", name="w", value="612"))
dimensions.add(XmpField(prefix="stDim", name="h", value="792"))
dimensions.add(XmpField(prefix="stDim", name="unit", value="pt"))
print(dimensions.get("w").value) # "612"
packet.add(XmpField(prefix="xmpTPg", name="MaxPageSize", value=dimensions))Passo 7: Registra spazi dei nomi XMP personalizzati
NamespaceProvider è pre-caricato con gli spazi dei nomi XMP standard (Dublin Core, Adobe XMP, PDF e altri). Chiama register(prefix, uri) per aggiungere una mappatura personalizzata — restituisce lo stesso provider così le chiamate possono essere concatenate — quindi allega il provider a un pacchetto affinché la serializzazione possa risolvere il prefisso personalizzato al suo URI:
from aspose_pdf import NamespaceProvider, XmpPacket
provider = NamespaceProvider()
provider.register("custom", "https://example.com/ns/custom/1.0/")
packet = XmpPacket(namespace_provider=provider)
packet.set_value("custom", "batch_id", "run-2026-07-29")Passo 8: Analizza e serializza un pacchetto
XmpPacket.parse(data, provider=...) legge i byte grezzi del pacchetto XMP o il testo in un XmpPacket. serialize() e to_bytes() rendono entrambi il pacchetto nuovamente in byte del pacchetto XMP:
xmp_bytes = packet.to_bytes()
restored = XmpPacket.parse(xmp_bytes)
print(restored.get("dc", "title").value)Problemi comuni e soluzioni
get() oppure un getter tipizzato restituisce None anche se ho appena impostato la proprietà
Conferma che l’argomento prefix (o URI) per get/get_int/get_date/etc. corrisponda esattamente a quello passato alla chiamata corrispondente set_* — get verifica sia il prefisso del campo sia il suo URI di namespace, ma un prefisso personalizzato non corrispondente in una chiamata e un URI nell’altra causerà un mancato riconoscimento.
Il prefisso di namespace personalizzato produce output incompleto quando serializzato
Un prefisso che non è uno dei namespace XMP standard (dc, xmp, pdf e simili) richiede o un argomento esplicito uri= in ogni chiamata set_*, oppure un NamespaceProvider con quel prefisso registrato e collegato al pacchetto tramite XmpPacket(namespace_provider=provider) prima di chiamare serialize()/to_bytes().
get_bool, get_int, o get_real returns None per un valore che so sia impostato
Questi getter tipizzati restituiscono None quando il testo memorizzato non viene analizzato come il tipo previsto — ad esempio, get_int su una proprietà il cui valore è stato impostato con set_value come testo libero anziché con set_int. Usa il setter tipizzato corrispondente (set_int, set_real, set_bool) affinché la logica di parsing del getter corrisponda al percorso di scrittura.
XmpPacket.parse raises ValueError su un pacchetto da una fonte non attendibile
parse rifiuta qualsiasi dichiarazione <!DOCTYPE o <!ENTITY come protezione contro input ostili per prevenire attacchi XXE e billion-laughs. Un pacchetto XMP legittimo non ha mai bisogno di un DTD; considera l’eccezione come un segnale che lo stream di origine è malformato o non sicuro anziché aggirare la protezione.
get_array returns None invece di un elenco
get_array restituisce None quando la proprietà denominata non esiste o non è stata memorizzata come un XmpArray (ad esempio, se è stata impostata con set_value anziché set_array). Usa set_array quando scrivi la proprietà in modo che il valore memorizzato corrisponda alla forma che get_array si aspetta.
Domande frequenti
Qual è la differenza tra set_value e i setter tipizzati come set_int?
set_value memorizza qualsiasi value tu passi così com’è. I setter tipizzati (set_date, set_int, set_real, set_bool) convertono il loro input nella forma di testo semplice che XMP utilizza internamente e si abbinano a un getter tipizzato corrispondente che lo analizza nuovamente, quindi usali quando hai bisogno di sicurezza di tipo end-to-end invece di stringhe grezze.
Come differiscono le tipologie di XmpArray (Bag, Seq, Alt)?
Bag è un insieme non ordinato di valori, Seq è un elenco ordinato e Alt contiene valori alternativi (spesso alternative linguistiche, come usato da set_localized_text). Passa il tipo desiderato a set_array(..., kind="Bag") o crea direttamente un XmpArray(kind=...).
Posso leggere l’elenco grezzo delle proprietà di un pacchetto senza conoscerne in anticipo i nomi?
Sì — itera packet.fields, che contiene ogni XmpField, XmpArray e XmpProperty aggiunti al pacchetto in ordine di inserimento.
Ho bisogno di un NamespaceProvider per gli spazi dei nomi XMP standard?
No. dc, xmp, pdf e gli altri prefissi standard si risolvono automaticamente. Un NamespaceProvider è necessario solo per i prefissi personalizzati che inventi tu stesso.
In che modo i qualificatori XmpProperty differiscono da un semplice XmpField?
XmpProperty avvolge un XmpField di base insieme a un elenco di campi qualificatori (aggiunti con add_qualifier), utilizzato per il caso più raro in cui una proprietà stessa necessita di metadati RDF aggiuntivi collegati al suo valore, oltre al qualificatore xml:lang che XmpField.language già copre.