Document Management
Document Management
The Document class is the entry point for almost every operation in Aspose.PDF FOSS for Python: creating a new PDF, loading an existing one, editing its pages, and writing the result back out. This guide walks through the document lifecycle, page-collection operations, optimization, multi-file workflows, encryption, and the exceptions you should expect to handle.
Document Lifecycle: Create, Open, and Save
Document() with no arguments creates an empty, in-memory document. Pass a file path, raw bytes, or any readable binary stream as the first argument (or call load_from() explicitly) to load an existing PDF instead. save() accepts a path or a writable binary stream such as 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() raises FileExistsError if the destination path already exists, unless you pass overwrite=True. close() is an alias for dispose(); both are idempotent, so calling them more than once is safe.
Only PDF is implemented as a save target — this is the library’s core, fully working function. Passing an export value such as SaveFormat.PPTX or DocFormat.HTML to save() raises UnsupportedFeatureException instead of writing a mislabeled file, so a failed export is always loud rather than silent.
Managing the Page Collection
doc.pages is a PageCollection. It supports len(), iteration, and 0-based indexing (doc.pages[0]), plus add(), insert(index, page), and delete(index) for structural edits. Each Page exposes index, rect (the MediaBox), and 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) appends a blank page when called with no argument. pages.insert() clamps an out-of-range index to the nearest valid position rather than raising.
Optimizing and Compressing Documents
Document.optimize() runs image/stream deduplication, unused-object garbage collection, and stream compression in one call. Pass an OptimizationOptions instance to control which techniques run; omit it to use the standard cleanup profile.
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() is an alias for optimize(). If you only want stream compression without the structural cleanup pass, call doc.compress_streams() directly.
Merging, Splitting, and Editing Files
For documents you already have open, Document.merge() appends other Document instances onto the current one:
from aspose_pdf import Document
base = Document("part1.pdf")
extra = Document("part2.pdf")
base.merge(extra)
base.save("combined.pdf", overwrite=True)For file-to-file workflows without opening a Document yourself, the low-code Merger and Splitter plugins take a MergeOptions/SplitOptions object built from FileDataSource inputs and outputs:
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 offers the same family of operations as a facade that returns True/False instead of raising, which is convenient for batch scripts:
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-based page numbers, unlike PageCollection’s 0-based indexing — see Common Issues below.
Encryption and Document Security
Document.encrypt(user_password, owner_password=None, permissions=-4) encrypts the in-memory document; decrypt(password) and change_passwords(old, new_user, new_owner=None) reverse or rotate passwords. is_encrypted and permissions report the current state.
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)Opening an encrypted document with no password, or the wrong one, raises PdfSecurityException — catch this class (from aspose_pdf.exceptions) rather than assuming a load always succeeds.
Metadata, Validation, and Exception Handling
Document metadata lives on doc.info (a plain dict[str, str]), and doc.version / doc.id expose the PDF header version and trailer file-identifier. validate() (aliased as check()) reports structural integrity; repair() attempts to fix common problems such as a missing page list or an out-of-range 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 bounds memory and object counts for untrusted files; call PdfLoadLimits.unlimited() to disable every limit when you fully trust the source. AsposePdfException is the base class for the whole exception hierarchy (including PdfIOException and PdfSecurityException), so a single except AsposePdfException catches any library-raised error.
Tips and Best Practices
- Always call
dispose()(orclose()) on aDocumentwhen you are done with it, or use it as a short-lived local variable — the engine holds decoded page content and images in memory until disposal. - Pass
overwrite=Truetosave()when re-writing a path you already created in the same run; the default isFalseand raisesFileExistsError. - Prefer
doc.optimize()before shipping a generated PDF — it is a single call that removes unused objects and compresses streams, and typically shrinks output size noticeably. - Set a
PdfLoadLimitspolicy explicitly whenever you load PDFs from an untrusted source (uploads, email attachments, web scrapes); the defaults are generous but finite, not a security boundary you should rely on blindly. - Catch
AsposePdfException(or a specific subclass such asPdfSecurityException) around load/save calls rather than bareException— it is the common base for every error the library raises.
Common Issues
| Issue | Cause | Fix |
|---|---|---|
FileExistsError on save() | Destination path already exists and overwrite was left at its default False | Pass save(path, overwrite=True) |
UnsupportedFeatureException on save() | A non-PDF save_format (e.g. SaveFormat.PPTX, DocFormat.HTML) was requested | Save as PDF — any other SaveFormat/DocFormat value raises UnsupportedFeatureException instead of writing output |
PdfSecurityException: Password required for encrypted document | Opened an encrypted PDF without a password argument | Pass Document(path, password="...") or call load_from(path, password="...") |
Off-by-one page numbers between PageCollection and PdfFileEditor | doc.pages[i] is 0-based; PdfFileEditor.extract()/.insert() page arguments are 1-based | Add or subtract 1 when converting between the two APIs |
IndexError: Page index out of range. from pages.delete() | Index passed to delete() does not exist in the collection | Check doc.page_count (or len(doc.pages)) before deleting |
FAQ
Do I need a license to use Aspose.PDF FOSS for Python?
No. This is the open-source (MIT-licensed) edition; there is no license file or activation step to configure.
Can I export a Document to formats other than PDF?
Not in this release. save() only implements PDF output — passing another SaveFormat/DocFormat value raises UnsupportedFeatureException rather than producing a mislabeled file.
What is the difference between Document.merge() and the Merger plugin?
Document.merge() combines Document instances you already have open in memory. Merger (with MergeOptions and FileDataSource) is a file-to-file convenience wrapper that opens, merges, and saves for you in one call — useful for simple batch scripts that never need the intermediate Document object.
Why does pages.insert() never raise for an out-of-range index?
PageCollection.insert() clamps the index into the valid range (negative values become 0, values past the end become len(doc.pages)) instead of raising, so an insert never fails purely because of the index value.
How do I safely load a PDF from an untrusted source?
Construct a PdfLoadLimits with explicit caps (max_input_bytes, max_pages, max_objects, and so on) and pass it as the limits= argument to Document(...) or load_from(). Every field defaults to a finite value already, but tightening them to your expected input size reduces the resources a malformed file can consume.
API Reference Summary
| Class / Method | Description |
|---|---|
Document() / Document.load_from() | Create an empty document or load one from a path, bytes, or a binary stream |
Document.save() | Write the document to a path or writable stream (PDF only) |
Document.dispose() / Document.close() | Release engine resources; idempotent |
Document.pages | The document’s PageCollection |
Document.info | Document metadata as a dict[str, str] |
Document.optimize() / Document.optimize_resources() / Document.compress_streams() | Remove unused resources and compress streams |
Document.merge() | Append other Document instances onto this one |
Document.encrypt() / Document.decrypt() / Document.change_passwords() | Apply, remove, or rotate document passwords |
Document.validate() / Document.check() / Document.repair() | Check and attempt to fix structural integrity |
PageCollection.add() / .insert() / .delete() / .item() | Structural page-collection edits (0-based) |
Page.rect / Page.rotation / Page.index | Per-page geometry and position |
OptimizationOptions | Fine-grained flags consumed by Document.optimize() |
MergeOptions / Merger | File-to-file merge plugin |
SplitOptions / Splitter | File-to-file one-page-per-output split plugin |
FileDataSource | File-backed input/output for the plugin APIs |
PdfFileEditor | Facade for concatenate(), extract(), insert(), delete(), append() (1-based pages) |
PdfLoadLimits | Immutable resource-limit policy for untrusted input |
AsposePdfException | Base class for every exception the library raises |
PdfSecurityException | Raised for missing/incorrect passwords and permission errors |
PdfIOException | Raised for I/O errors during PDF processing |
See Also
- API Reference: Full class and method documentation for
aspose_pdf - Knowledge Base: Task-oriented how-to guides
- Product Overview: Features and capabilities summary
- Getting Started / Installation: pip install and setup
- Aspose.PDF — Enterprise Documentation