إدارة المستندات

إدارة المستندات

إدارة المستندات

الفئة Document هي نقطة الدخول لمعظم العمليات في Aspose.PDF FOSS لـ Python: إنشاء PDF جديد، تحميل ملف موجود، تحرير صفحاته، وكتابة النتيجة مرة أخرى. يوضح هذا الدليل دورة حياة المستند، عمليات مجموعة الصفحات، التحسين، سير العمل متعدد الملفات، التشفير، والاستثناءات التي يجب أن تتوقع التعامل معها.


دورة حياة المستند: إنشاء، فتح، وحفظ

Document() بدون أي حجج ينشئ مستندًا فارغًا في الذاكرة. مرّر مسار ملف، bytes الخام، أو أي تدفق ثنائي قابل للقراءة كالحجة الأولى (أو استدعِ load_from() صراحة) لتحميل PDF موجود بدلاً من ذلك. save() يقبل مسارًا أو تدفقًا ثنائيًا قابلًا للكتابة مثل io.BytesIO.

from aspose_pdf import Document

# Create a new, empty document and add a blank page
doc = Document()
doc.pages.add()
doc.save("hello.pdf")

# Re-open it — a path, bytes, or a binary stream all work
reopened = Document("hello.pdf")
print(reopened.page_count)   # 1

# ...or load explicitly onto an existing instance
another = Document()
another.load_from("hello.pdf")

reopened.close()
another.dispose()
doc.dispose()

save() يثير FileExistsError إذا كان مسار الوجهة موجودًا بالفعل، ما لم تمرّر overwrite=True. close() هو اسم مستعار لـ dispose()؛ كلاهما متطابق (idempotent)، لذا فإن استدعائهما أكثر من مرة آمن.

فقط PDF هو الهدف المدعوم للحفظ — هذا هو جوهر المكتبة، وظيفة تعمل بالكامل. تمرير قيمة تصدير مثل SaveFormat.PPTX أو DocFormat.HTML إلى save() يثير UnsupportedFeatureException بدلاً من كتابة ملف مسمى خطأ، لذا فإن فشل التصدير يكون دائمًا واضحًا وليس صامتًا.


إدارة مجموعة الصفحات

doc.pages هو PageCollection. يدعم len()، التكرار، وفهرسة صفرية القاعدة (doc.pages[0])، بالإضافة إلى add()، insert(index, page)، وdelete(index) لتعديلات هيكلية. كل Page يكشف عن index، rect (الـ MediaBox)، وrotation.

from aspose_pdf import Document

doc = Document()
doc.pages.add()            # page 0
doc.pages.add()            # page 1
doc.pages.insert(1, None)  # insert a blank page at index 1

print(doc.page_count)      # 3

first_page = doc.pages[0]
print(first_page.rect)     # (0, 0, 612, 792)

for page in doc.pages:
    print(page.index, page.rotation)

doc.pages.delete(1)        # remove the page we inserted
doc.save("pages_demo.pdf", overwrite=True)

pages.add(page=None) يضيف صفحة فارغة عندما يُستدعى بدون أي وسيط. pages.insert() يقيد الفهرس خارج النطاق إلى أقرب موقع صالح بدلاً من إلقاء استثناء.


تحسين وضغط المستندات

Document.optimize ينفّذ إزالة التكرار للصور/المجاري، جمع القمامة للكائنات غير المستخدمة، وضغط التدفق في استدعاء واحد. مرّر كائن OptimizationOptions للتحكم في التقنيات التي تُشغل؛ احذفها لاستخدام ملف التنظيف القياسي.

from aspose_pdf import Document, OptimizationOptions

doc = Document("large_report.pdf")

options = OptimizationOptions()
options.remove_unused_objects = True
options.link_duplicate_streams = True
options.image_compression_quality = 60
options.subset_fonts = True

doc.optimize(options)
doc.save("large_report_optimized.pdf", overwrite=True)

optimize_resources() هو اسم مستعار لـ optimize(). إذا كنت تريد فقط ضغط التدفق دون مرحلة التنظيف الهيكلية، استدعِ doc.compress_streams() مباشرةً.


دمج، تقسيم، وتحرير الملفات

بالنسبة للمستندات المفتوحة بالفعل، Document.merge() يضيف مثيلات Document أخرى إلى الحالية:

from aspose_pdf import Document

base = Document("part1.pdf")
extra = Document("part2.pdf")

base.merge(extra)
base.save("combined.pdf", overwrite=True)

لتدفقات من ملف إلى ملف دون فتح Document بنفسك، الإضافات منخفضة الكود Merger وSplitter تأخذ كائن MergeOptions/SplitOptions مُنشأ من مدخلات ومخرجات FileDataSource:

from aspose_pdf import Merger, MergeOptions, Splitter, SplitOptions, FileDataSource

# Merge two files into one
merge_options = MergeOptions()
merge_options.add_input(FileDataSource("part1.pdf"))
merge_options.add_input(FileDataSource("part2.pdf"))
merge_options.add_output(FileDataSource("combined.pdf"))
Merger().process(merge_options)

# Split the result into one file per page
split_options = SplitOptions()
split_options.add_input(FileDataSource("combined.pdf"))
split_options.add_output(FileDataSource("combined_page1.pdf"))
split_options.add_output(FileDataSource("combined_page2.pdf"))
Splitter().process(split_options)

PdfFileEditor يقدم نفس عائلة العمليات كواجهة تُعيد True/False بدلاً من رفع الاستثناء، وهو ما يُسهّل كتابة سكريبتات الدُفعات:

from aspose_pdf import PdfFileEditor

with PdfFileEditor() as editor:
    ok = editor.concatenate(["part1.pdf", "part2.pdf"], "combined.pdf")
    if not ok:
        print("concatenate failed:", editor.last_exception)

    editor.extract("combined.pdf", "first_page_only.pdf", page_from=1, page_to=1)

PdfFileEditor.extract() and .insert() take مستند إلى 1 أرقام الصفحات، على عكس PageCollectionفهرسة تبدأ من 0 — انظر المشكلات الشائعة أدناه.


التشفير وأمن المستند

Document.encrypt(user_password, owner_password=None, permissions=-4) يشفر المستند الموجود في الذاكرة؛ decrypt(password) و change_passwords(old, new_user, new_owner=None) يعيدان أو يدوّران كلمات المرور. is_encrypted و permissions تُبلغ عن الحالة الحالية.

from aspose_pdf import Document
from aspose_pdf.exceptions import PdfSecurityException

doc = Document()
doc.pages.add()
doc.encrypt("user-pass", "owner-pass", permissions=-4)
doc.save("secured.pdf", overwrite=True)

try:
    Document("secured.pdf", password="wrong-pass")
except PdfSecurityException as exc:
    print("could not open:", exc)

reopened = Document("secured.pdf", password="user-pass")
print(reopened.is_encrypted)   # True
reopened.decrypt("user-pass")
reopened.save("unsecured.pdf", overwrite=True)

فتح مستند مُشفّر بدون كلمة مرور، أو بكلمة خاطئة، يرفع PdfSecurityException — امسك بهذه الفئة (من aspose_pdf.exceptions) بدلاً من افتراض أن التحميل ينجح دائمًا.


البيانات الوصفية، التحقق، ومعالجة الاستثناءات

البيانات الوصفية للمستند موجودة على doc.info (قائمة dict[str, str] عادية)، وdoc.version / doc.id تُظهر إصدار رأس PDF ومعرّف ملف المُلحق. validate() (المعروف باسم check()) يُبلغ عن سلامة البنية؛ repair() يحاول إصلاح المشكلات الشائعة مثل قائمة صفحات مفقودة أو MediaBox خارج النطاق.

from aspose_pdf import Document, PdfLoadLimits
from aspose_pdf.exceptions import AsposePdfException, PdfIOException

doc = Document()
doc.pages.add()
doc.info["Title"] = "Quarterly Report"
doc.info["Author"] = "Reporting Bot"
doc.save("report.pdf", overwrite=True)

# Load untrusted input under an explicit resource-limit policy
safe_limits = PdfLoadLimits(max_input_bytes=50 * 1024 * 1024, max_pages=1000)

try:
    untrusted = Document("incoming.pdf", limits=safe_limits)
    if not untrusted.validate():
        untrusted.repair()
except (AsposePdfException, PdfIOException) as exc:
    print("failed to process incoming.pdf:", exc)

PdfLoadLimits يفرض حدودًا على الذاكرة وعدد الكائنات للملفات غير الموثوقة؛ استدعِ PdfLoadLimits.unlimited() لتعطيل كل الحدود عندما تثق تمامًا بالمصدر. AsposePdfException هي الفئة الأساسية لكل شجرة الاستثناءات (بما في ذلك PdfIOException و PdfSecurityException)، لذا فـ except AsposePdfException واحد يلتقط أي خطأ يُرفع من المكتبة.


نصائح وأفضل الممارسات

  • دائمًا استدعِ dispose() (أو close()) على Document عندما تنتهي منه، أو استخدمه كمتغيّر محلي قصير الأمد — يحتفظ المحرّك بمحتوى الصفحة المُفكّك والصور في الذاكرة حتى يتم التخلص منه.
  • مرّر overwrite=True إلى save() عند إعادة كتابة مسار قمت بإنشائه بالفعل في نفس التنفيذ؛ القيمة الافتراضية هي False وتثير FileExistsError.
  • يفضَّل استخدام doc.optimize() قبل نشر ملف PDF مُولَّد — إنها استدعاءة واحدة تُزيل الكائنات غير المستخدمة وتضغط التدفقات، وعادةً ما تُقلِّص حجم الإخراج بشكل ملحوظ.
  • حدِّد سياسة PdfLoadLimits صراحةً كلما قمت بتحميل ملفات PDF من مصدر غير موثوق (التحميلات، مرفقات البريد الإلكتروني، استخراج الويب)؛ الإعدادات الافتراضية سخية لكن محدودة، وليست حدًا أمنيًا يجب الاعتماد عليه بشكل أعمى.
  • التقط AsposePdfException (أو فئة فرعية محددة مثل PdfSecurityException) حول استدعاءات التحميل/الحفظ بدلاً من Exception العارية — فهي القاعدة المشتركة لكل خطأ تُطلقه المكتبة.

مشكلات شائعة

المشكلةالسببالإصلاح
FileExistsError على save()مسار الوجهة موجود بالفعل وoverwrite تُرك على القيمة الافتراضية Falseتجاوز save(path, overwrite=True)
UnsupportedFeatureException على save()تم طلب save_format غير PDF (مثلاً SaveFormat.PPTX، DocFormat.HTML)احفظ كـ PDF — أي قيمة أخرى لـ SaveFormat/DocFormat تثير UnsupportedFeatureException بدلاً من كتابة المخرجات
PdfSecurityException: Password required for encrypted documentتم فتح ملف PDF مشفر دون وسيط passwordمرّر Document(path, password="...") أو استدعِ load_from(path, password="...")
إزاحة صفحة واحدة في أرقام الصفحات بين PageCollection و PdfFileEditordoc.pages[i] يبدأ من الصفر؛ معطيات الصفحات PdfFileEditor.extract()/.insert() تبدأ من الواحدأضف أو اطرح 1 عند التحويل بين واجهتي API
IndexError: Page index out of range. من pages.delete()الفهرس الممرَّ إلى delete() غير موجود في المجموعةتحقق من doc.page_count (أو len(doc.pages)) قبل الحذف

FAQ

هل أحتاج إلى ترخيص لاستخدام Aspose.PDF FOSS لـ Python؟

لا. هذه النسخة مفتوحة المصدر (مرخصة بموجب MIT)؛ لا يوجد ملف ترخيص أو خطوة تفعيل لتكوينها.

هل يمكنني تصدير Document إلى صيغ غير PDF؟

ليس في هذا الإصدار. save() يقتصر على تنفيذ إخراج PDF فقط — تمرير قيمة SaveFormat/DocFormat أخرى يثير UnsupportedFeatureException بدلاً من إنتاج ملف مسمى بشكل خاطئ.

ما الفرق بين Document.merge() وملحق Merger؟

Document.merge() يجمع بين حالات Document التي لديك مفتوحة بالفعل في الذاكرة. Merger (مع MergeOptions وFileDataSource) هو غلاف ملائم من ملف إلى ملف يفتح، يدمج، ويحفظ لك في استدعاء واحد — مفيد لسكربتات الدفعات البسيطة التي لا تحتاج أبداً إلى كائن Document الوسيط.

لماذا لا يثير pages.insert() خطأً أبداً عندما يكون الفهرس خارج النطاق؟

PageCollection.insert() يحد الفهرس إلى النطاق الصالح (القيم السالبة تصبح 0، والقيم التي تتجاوز النهاية تصبح len(doc.pages)) بدلاً من إثارة خطأ، لذا لا يفشل الإدراج أبداً لمجرد قيمة الفهرس.

كيف يمكنني تحميل ملف PDF بأمان من مصدر غير موثوق؟

قم بإنشاء PdfLoadLimits مع حدود صريحة (max_input_bytes، max_pages، max_objects، وما إلى ذلك) ومرره كمعامل limits= إلى Document(...) أو load_from(). كل حقل له قيمة افتراضية محدودة بالفعل، ولكن تضييقها لتتناسب مع حجم الإدخال المتوقع يقلل من الموارد التي يمكن أن يستهلكها ملف غير صالح.


API Reference ملخص

فئة / طريقةوصف
Document() / Document.load_fromأنشئ مستندًا فارغًا أو حمّله من مسار أو بايتات أو تدفق ثنائي
Document.saveاكتب المستند إلى مسار أو تدفق قابل للكتابة (PDF فقط)
Document.dispose() / Document.close()إطلاق موارد المحرك؛ idempotent
Document.pagesPageCollection الخاص بالمستند
Document.infoبيانات تعريف المستند كـ dict[str, str]
Document.optimize / Document.optimize_resources / Document.compress_streams()إزالة الموارد غير المستخدمة وضغط التدفقات
Document.merge()أضف حالات Document أخرى إلى هذا
Document.encrypt / Document.decrypt / Document.change_passwordsتطبيق أو إزالة أو تدوير كلمات مرور المستند
Document.validate() / Document.check() / Document.repair()تحقق وحاول إصلاح سلامة الهيكل
PageCollection.add() / .insert() / .delete() / .item()تحريرات تجميع الصفحات الهيكلية (بدءًا من الصفر)
Page.rect / Page.rotation / Page.indexالهندسة والموضع لكل صفحة
OptimizationOptionsأعلام دقيقة المستوى يستخدمها Document.optimize
MergeOptions / Mergerملحق دمج من ملف إلى ملف
SplitOptions / Splitterملحق تقسيم من ملف إلى ملف صفحة واحدة لكل ناتج
FileDataSourceإدخال/إخراج مدعوم بالملف لواجهات برمجة التطبيقات الخاصة بالملحق
PdfFileEditorواجهة أمامية لـ concatenate()، extract()، insert()، delete()، append() (صفحات مرقمة من 1)
PdfLoadLimitsسياسة حد الموارد غير القابلة للتغيير للمدخلات غير الموثوقة
AsposePdfExceptionالفئة الأساسية لكل استثناء تطرحه المكتبة
PdfSecurityExceptionيتم رفعها عند عدم وجود كلمة مرور أو كلمة مرور غير صحيحة وأخطاء الأذونات
PdfIOExceptionيتم رفعها عند حدوث أخطاء الإدخال/الإخراج أثناء معالجة PDF

انظر أيضاً

 العربية