كيفية العمل مع بيانات PDF في Python

كيفية العمل مع بيانات PDF في Python

Aspose.PDF FOSS لـ Python يمثل بيانات XMP الوصفية لمستند PDF كنموذج بيانات في الذاكرة مبنيًا من كائنات XmpPacket، XmpField، XmpArray، XmpStruct، وXmpProperty، مع NamespaceProvider الذي يحل بادئات المساحات الاسمية إلى عناوين URI. يتيح لك ذلك قراءة، بناء وإعادة كتابة البيانات الوصفية المنظمة — العناوين، التواريخ، قوائم الكلمات المفتاحية، الحقول المخصصة — دون كتابة RDF/XML يدوياً. المكتبة نقيّة Python ويتم تثبيتها بالأمر أدناه.

دليل خطوة بخطوة

الخطوة 1: تثبيت الحزمة

تثبيت حزمة Aspose.PDF FOSS:

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. كل getter يُعيد 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 القياسية (دبلن كور، 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/إلخ يطابق تمامًا ما تم تمريره إلى استدعاء 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 لقيمة أعلم أنها مُعيَّنة

تُعيد هذه getters المكتوبة None عندما لا يتم تحليل النص المخزن كنوع متوقع — على سبيل المثال، get_int على خاصية تم تعيين قيمتها باستخدام set_value كنص حر بدلاً من set_int. استخدم setter المكتوب المطابق (set_int، set_real، set_bool) بحيث يتطابق منطق التحليل للgetter مع مسار الكتابة.

XmpPacket.parse raises ValueError على حزمة من مصدر غير موثوق

parse يرفض أي إعلان <!DOCTYPE أو <!ENTITY كحماية ضد مدخلات عدائية ضد هجمات XXE وهجمات الضحك بالمليارات. لا تحتاج حزمة 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 داخليًا وتُقارن مع مُستخرج مكتوب بنوع مطابق يعيد تحليله، لذا استخدمها عندما تحتاج إلى أمان نوع أثناء النقل ذهابًا وإيابًا بدلاً من السلاسل الخام.

كيف تختلف أنواع 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 بالفعل.

انظر أيضاً

 العربية