Comment travailler avec les données PDF dans Python
Aspose.PDF FOSS pour Python représente les métadonnées XMP d’un document PDF sous la forme d’un modèle de données en mémoire construit à partir des objets XmpPacket, XmpField, XmpArray, XmpStruct et XmpProperty, avec NamespaceProvider qui résout les préfixes d’espace de noms en URI. Cela vous permet de lire, créer et réécrire des métadonnées structurées — titres, dates, listes de mots-clés, champs personnalisés — sans écrire manuellement RDF/XML. La bibliothèque est pure Python et s’installe avec la commande ci-dessous.
Guide étape par étape
Étape 1: Installer le paquet
Installez le paquet 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 XmpPacket et en affichant un message de confirmation:
from aspose_pdf import XmpPacket
print("aspose-pdf-foss-for-python is ready.")Étape 2: Importer les classes requises
Importez les classes du modèle de données XMP et le résolveur d’espaces de noms:
from aspose_pdf import (
NamespaceProvider,
XmpArray,
XmpField,
XmpPacket,
XmpProperty,
XmpStruct,
)Étape 3: Créer un paquet et définir des propriétés simples
XmpPacket() commence avec une liste de champs vide. set_value(prefix, name, value, uri=...) définit ou remplace une propriété simple, et get(prefix_or_uri, name) la lit à nouveau en tant que XmpField; get accepte soit le préfixe enregistré, soit l’URI complet de l’espace de noms:
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"Étape 4: Stocker des valeurs typées
XmpPacket fournit des accesseurs de commodité typés qui convertissent vers et depuis la forme texte brut que XMP stocke en interne: set_date/get_date, set_int/get_int, set_real/get_real et set_bool/get_bool. Chaque getter renvoie None lorsque la propriété est absente au lieu de lever une exception:
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"))Étape 5: Stocker du texte localisé et des tableaux ordonnés
set_localized_text écrit une propriété alternative de langue (rdf:Alt) telle que dc:title dans une langue spécifique, par défaut "x-default". set_array écrit un tableau ordonné (Seq), non ordonné (Bag) ou alternatif (Alt) à partir d’une liste simple de valeurs, et get_array le lit à nouveau sous forme de liste de chaînes:
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"))Étape 6 : Construire des valeurs structurées avec XmpStruct
Certaines propriétés XMP (enregistrements de dimension, entrées d’historique) sont des valeurs structurées plutôt que du texte simple. Créez-en une avec XmpStruct, ajoutez des objets membres XmpField avec add, et lisez un membre par son nom avec get, puis attachez la structure au paquet avec 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))Étape 7 : Enregistrer des espaces de noms XMP personnalisés
NamespaceProvider est préchargé avec les espaces de noms XMP standards (Dublin Core, Adobe XMP, PDF, et d’autres). Appelez register(prefix, uri) pour ajouter un mappage personnalisé — il renvoie le fournisseur lui-même afin que les appels puissent être enchaînés — puis attachez le fournisseur à un paquet afin que la sérialisation puisse résoudre le préfixe personnalisé vers son 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")Étape 8 : Analyser et sérialiser un paquet
XmpPacket.parse(data, provider=...) lit les octets bruts d’un paquet XMP ou le texte dans un XmpPacket. serialize() et to_bytes() rendent tous deux le paquet en octets de paquet XMP :
xmp_bytes = packet.to_bytes()
restored = XmpPacket.parse(xmp_bytes)
print(restored.get("dc", "title").value)Problèmes courants et solutions
get() ou un getter typé renvoie None même si je viens de définir la propriété
Confirmez que l’argument prefix (ou URI) passé à get/get_int/get_date/etc. correspond exactement à ce qui a été fourni à l’appel set_* correspondant — get correspond à la fois au préfixe du champ et à son URI de namespace, mais un préfixe personnalisé incohérent dans un appel et un URI dans l’autre entraînera un échec.
Un préfixe de namespace personnalisé produit une sortie incomplète lors de la sérialisation
Un préfixe qui ne fait pas partie des namespaces XMP standards (dc, xmp, pdf, etc.) nécessite soit un argument uri= explicite à chaque appel set_*, soit un NamespaceProvider avec ce préfixe enregistré et attaché au paquet via XmpPacket(namespace_provider=provider) avant d’appeler serialize()/to_bytes().
get_bool, get_int, ou get_real returns None pour une valeur que je sais être définie
Ces getters typés renvoient None lorsque le texte stocké ne peut pas être analysé comme le type attendu — par exemple, get_int sur une propriété dont la valeur a été définie avec set_value comme texte libre plutôt qu’avec set_int. Utilisez le setter typé correspondant (set_int, set_real, set_bool) afin que la logique d’analyse du getter corresponde au chemin d’écriture.
XmpPacket.parse raises ValueError sur un paquet provenant d’une source non fiable
parse rejette toute déclaration <!DOCTYPE ou <!ENTITY en tant que garde contre les entrées hostiles contre les attaques XXE et billion-laughs. Un paquet XMP légitime n’a jamais besoin d’un DTD; considérez l’exception comme un signal que le flux source est malformé ou dangereux plutôt que de contourner la garde.
get_array returns None au lieu d’une liste
get_array renvoie None lorsque la propriété nommée n’existe pas ou n’a pas été stockée en tant que XmpArray (par exemple, si elle a été définie avec set_value au lieu de set_array). Utilisez set_array lors de l’écriture de la propriété afin que la valeur stockée corresponde à la forme attendue par get_array.
Foire aux questions
Quelle est la différence entre set_value et les setters typés comme set_int?
set_value stocke tel quel tout ce que value vous transmettez. Les setters typés (set_date, set_int, set_real, set_bool) convertissent leur entrée en la forme texte brute utilisée en interne par XMP et s’associent à un getter typé correspondant qui la reconvertit, utilisez-les donc lorsque vous avez besoin d’une sécurité de type aller-retour plutôt que de simples chaînes.
En quoi les types XmpArray (Bag, Seq, Alt) diffèrent-ils?
Bag est un ensemble non ordonné de valeurs, Seq est une liste ordonnée, et Alt contient des valeurs alternatives (le plus souvent des alternatives linguistiques, comme le utilise set_localized_text). Transmettez le type souhaité à set_array(..., kind="Bag") ou créez directement un XmpArray(kind=...).
Puis-je lire la liste brute des propriétés d’un paquet sans connaître leurs noms à l’avance?
Oui — itérez packet.fields, qui contient chaque XmpField, XmpArray et XmpProperty ajoutés au paquet dans l’ordre d’insertion.
Ai-je besoin d’un NamespaceProvider pour les espaces de noms XMP standard?
Non. dc, xmp, pdf et les autres préfixes standard se résolvent automatiquement. Un NamespaceProvider n’est nécessaire que pour les préfixes personnalisés que vous créez vous-même.
En quoi les qualificateurs XmpProperty diffèrent-ils d’un simple XmpField?
XmpProperty enveloppe un XmpField de base avec une liste de champs de qualification (ajoutés avec add_qualifier), utilisé pour le cas plus rare où une propriété elle-même nécessite des métadonnées RDF supplémentaires attachées à sa valeur, au-delà du qualificateur xml:lang que XmpField.language couvre déjà.