Як працювати з даними PDF у Python
Aspose.PDF FOSS для Python представляє XMP-метадані PDF-документа як модель даних у пам’яті, побудовану з об’єктів XmpPacket, XmpField, XmpArray, XmpStruct і XmpProperty, при цьому NamespaceProvider розв’язує префікси просторів імен у URI. Це дозволяє читати, створювати та переписувати структуровані метадані — назви, дати, списки ключових слів, користувацькі поля — без ручного написання RDF/XML. Бібліотека чисто Python і встановлюється за допомогою команди нижче.
Покроковий посібник
Крок 1: Встановіть пакет
Встановіть 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 .Перевірте встановлення, імпортуючи XmpPacket і виводячи повідомлення підтвердження:
from aspose_pdf import XmpPacket
print("aspose-pdf-foss-for-python is ready.")Крок 2: Імпорт необхідних класів
Імпортуйте класи моделі даних XMP та розв’язувач просторів імен:
from aspose_pdf import (
NamespaceProvider,
XmpArray,
XmpField,
XmpPacket,
XmpProperty,
XmpStruct,
)Крок 3: Створіть пакет і встановіть прості властивості
XmpPacket() починається з порожнього списку полів. set_value(prefix, name, value, uri=...) встановлює або замінює просту властивість, а get(prefix_or_uri, name) зчитує її назад як XmpField; get приймає як зареєстрований префікс, так і повний URI простору імен:
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"Крок 4: Зберігання типізованих значень
XmpPacket надає типізовані зручні доступники, які конвертують до і з простого текстового формату, який XMP зберігає внутрішньо: set_date/get_date, set_int/get_int, set_real/get_real та set_bool/get_bool. Кожен геттер повертає None, коли властивість відсутня, замість того, щоб викликати помилку:
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"))Крок 5: Зберігання локалізованого тексту та упорядкованих масивів
set_localized_text записує властивість з альтернативою мови (rdf:Alt), наприклад dc:title, у певній мові, за замовчуванням "x-default". set_array записує упорядкований (Seq), неупорядкований (Bag) або альтернативний (Alt) масив з простого списку значень, а get_array зчитує його назад як список рядків:
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"))Крок 6: Створити структуровані значення за допомогою XmpStruct
Деякі властивості XMP (записи розмірів, записи історії) є структурованими значеннями, а не простим текстом. Створіть таке за допомогою XmpStruct, додайте об’єкти XmpField за допомогою add, і прочитайте елемент за назвою за допомогою get, потім приєднайте структуру до пакета за допомогою 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))Крок 7: Зареєструвати користувацькі простори імен XMP
NamespaceProvider попередньо завантажений зі стандартними просторами імен XMP (Dublin Core, Adobe XMP, PDF та інші). Викличте register(prefix, uri), щоб додати власне відображення — він повертає сам провайдер, тож виклики можна ланцюжити — потім приєднайте провайдера до пакета, щоб процес серіалізації міг розв’язати користувацький префікс у його 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")Крок 8: Розпарсити та серіалізувати пакет
XmpPacket.parse(data, provider=...) читає необроблені байти або текст XMP-пакета у XmpPacket. serialize() і to_bytes() обидва формують пакет назад у байти XMP-пакета:
xmp_bytes = packet.to_bytes()
restored = XmpPacket.parse(xmp_bytes)
print(restored.get("dc", "title").value)Типові проблеми та їх вирішення
get() або типізований getter повертає None навіть якщо я щойно встановив властивість
Переконайтеся, що аргумент prefix (або URI) у виклику get/get_int/get_date/etc. точно відповідає тому, що був переданий у відповідний виклик set_* — get порівнює і префікс поля, і його URI простору імен, але невідповідний користувацький префікс в одному виклику та URI в іншому призведе до помилки.
Користувацький префікс простору імен призводить до неповного виводу при серіалізації
Префікс, який не входить до стандартних просторів імен XMP (dc, xmp, pdf тощо), потребує або явного аргументу uri= у кожному виклику set_*, або NamespaceProvider з цим префіксом, зареєстрованим і приєднаним до пакету через XmpPacket(namespace_provider=provider) перед викликом serialize()/to_bytes().
get_bool, get_int, або get_real returns None для значення, про яке я знаю, що воно встановлене
Ці типізовані геттери повертають None, коли збережений текст не може бути розпарсений як очікуваний тип — наприклад, get_int у властивості, значення якої було встановлено за допомогою set_value як вільного тексту, а не set_int. Використовуйте відповідний типізований сетер (set_int, set_real, set_bool), щоб логіка парсингу геттера відповідала шляху запису.
XmpPacket.parse raises ValueError на пакеті з ненадійного джерела
parse відхиляє будь-яку декларацію <!DOCTYPE чи <!ENTITY як захист від ворожого вводу проти атак XXE та billion-laughs. Легітимний XMP пакет ніколи не потребує DTD; сприймайте виключення як сигнал, що вихідний потік пошкоджений або небезпечний, а не намагайтеся обійти захист.
get_array returns None замість списку
get_array повертає None, коли названа властивість або не існує, або не збережена як XmpArray (наприклад, якщо вона була встановлена за допомогою set_value замість set_array). Використовуйте set_array під час запису властивості, щоб збережене значення відповідало формі, яку очікує get_array.
Поширені запитання
Яка різниця між set_value та типізованими встановлювачами, такими як set_int?
set_value зберігає будь-яке value, яке ви передаєте без змін. Типізовані встановлювачі (set_date, set_int, set_real, set_bool) конвертують свій ввід у простий текстовий формат, який XMP використовує внутрішньо, і супроводжуються відповідним типізованим getter’ом, який розбирає його назад, тому використовуйте їх, коли потрібна безпека типу при проходженні даних у обох напрямках, а не сирі рядки.
Чим відрізняються типи XmpArray (Bag, Seq, Alt)?
Bag — це невпорядкований набір значень, Seq — упорядкований список, а Alt містить альтернативні значення (найчастіше мовні альтернативи, як у set_localized_text). Передайте потрібний тип у set_array(..., kind="Bag") або створіть XmpArray(kind=...) безпосередньо.
Чи можу я прочитати необроблений список властивостей у пакеті, не знаючи їхніх імен заздалегідь?
Так — ітеруйте packet.fields, який містить усі XmpField, XmpArray і XmpProperty, додані до пакету у порядку вставки.
Чи потрібен мені NamespaceProvider для стандартних просторів імен XMP?
Ні. dc, xmp, pdf та інші стандартні префікси розв’язуються автоматично. NamespaceProvider потрібен лише для користувацьких префіксів, які ви вигадуєте самостійно.
Чим XmpProperty кваліфікатори відрізняються від простого XmpField?
XmpProperty обгортає базовий XmpField разом зі списком полів-кваліфікаторів (доданих за допомогою add_qualifier), використовується в рідкісному випадку, коли сам властивість потребує додаткових метаданих RDF, прикріплених до її значення, понад xml:lang кваліфікатор, який вже охоплює XmpField.language.