Skip to main content

PDF editing and rendering for Python, powered by Rust (lopdf + hayro). MIT, zero dependencies, lightweight wheels.

Project description

pylopdf

PyPI CI Python License: MIT

日本語版 README はこちら

PDF editing and rendering for Python, powered by Rust — lopdf for editing and hayro (the pure-Rust PDF renderer adopted by typst) for rendering.

MIT licensed, zero runtime dependencies, lightweight wheels. Covers the common pymupdf use cases without the AGPL.

Why pylopdf?

pylopdf pymupdf pypdf pypdfium2
License MIT AGPL / commercial BSD Apache/BSD
Wheel size ~3.5 MB ~40 MB+ small (pure Python) ~8 MB
Editing (merge / split / rotate / outlines) limited
Rendering (PNG / SVG) ✅ (PNG)
Text extraction ✅ (basic) ✅ (advanced)
Encryption (AES-256) ✅ read & write
CJK font fallback ✅ ([cjk] extra) manual
Implementation pure Rust C Python C++ (PDFium)
  • Fits size-constrained environments such as AWS Lambda
  • Safe for commercial projects that need to avoid the AGPL
  • abi3: one wheel covers Python 3.10–3.14
  • API modeled after pymupdf

Limitations: no precise layout analysis, no annotation/form editing. Use pymupdf if you need those.

Install

pip install pylopdf

To render Japanese PDFs without embedded fonts, install the optional CJK fonts (Noto Sans/Serif JP, auto-detected at render time):

pip install pylopdf[cjk]

Building from source (requires a Rust toolchain):

uv sync

Usage

import pylopdf

# Open from a path or bytes
doc = pylopdf.open("input.pdf")
doc = pylopdf.open(stream=pdf_bytes)

# Page count
print(doc.page_count)  # same as len(doc)

# Metadata
print(doc.metadata["title"])
doc.set_metadata({"title": "Monthly Report", "author": "Alice"})

# Text extraction (0-based page numbers)
text = doc.get_page_text(0)

# Rendering
png: bytes = doc.render_page(0)             # 72 dpi
png2x: bytes = doc.render_page(0, scale=2)  # 144 dpi
png300 = doc.render_page(0, dpi=300)        # by resolution
png_bg = doc.render_page(0, background=(255, 255, 255))  # white background (default: transparent)
svg: str = doc.render_page_svg(0)

# Delete pages (split)
doc.delete_page(0)
doc.delete_pages([1, 2])

# Keep/reorder pages (repeating a page duplicates it)
doc.select([2, 0])

# Page objects (0-based; negative counts from the end)
page = doc[0]
for page in doc:
    print(page.number, page.rect)
page.set_rotation(90)                # display rotation (multiples of 90)
page.set_mediabox((0, 0, 300, 400))  # page boxes

# Insert / copy pages
doc.new_page()          # blank A4 appended
doc.copy_page(0, to=1)  # duplicate page 0 in front of page 1

# Table of contents (page numbers are 1-based here, pymupdf-compatible)
doc.set_toc([[1, "Chapter 1", 1], [2, "Section 1.1", 2]])
print(doc.get_toc())

# Merge (with ranges, reversed order, and an insertion position)
merged = pylopdf.Document()
merged.insert_pdf(pylopdf.open("a.pdf"))
merged.insert_pdf(pylopdf.open("b.pdf"), from_page=0, to_page=2, start_at=0)

# Save
merged.save("merged.pdf")
data: bytes = merged.tobytes()

# Optimized save (prune unreferenced objects + compress + object streams)
merged.save("small.pdf", garbage=True, deflate=True, object_streams=True)

# Encrypted save (AES-256; owner_pw alone = open freely, restricted permissions)
merged.save("locked.pdf", user_pw="secret", permissions=pylopdf.Permissions.PRINT)

# Fast metadata probe without parsing the whole file
info = pylopdf.peek_metadata("input.pdf")
print(info["title"], info["page_count"], info["encrypted"])

# Context manager
with pylopdf.open("input.pdf") as doc:
    print(doc.metadata)

# Encrypted PDFs (RC4-40/128, AES-128, AES-256; empty user passwords open transparently)
doc = pylopdf.open("locked.pdf", password="secret")
doc = pylopdf.open("locked.pdf")
if doc.needs_pass:
    doc.authenticate("secret")  # 0=failed, 2=user, 4=owner, 6=both

# CJK fallback font for PDFs without embedded fonts
# (automatic with pylopdf[cjk]; or bring your own font)
doc.set_fallback_font("NotoSansJP-Regular.otf")
doc.set_fallback_font(font_bytes, kind="serif")

API

pylopdf.Document (pylopdf.open() is an alias constructor):

Method / property Description
Document(filename=None, stream=None, password=None, max_decompressed_size=None) Open from a path or bytes; empty document if both are None. max_decompressed_size guards against decompression bombs
doc[i] / load_page(pno) / for page in doc Get a Page view (negative indices count from the end; re-fetch after structural changes)
needs_pass / is_encrypted Encryption status (pymupdf-compatible semantics)
authenticate(password) Decrypt with a password (returns 0/1/2/4/6, pymupdf-compatible)
page_count / len(doc) Number of pages
metadata Metadata dict (title, author, subject, keywords, creator, producer, creationDate, modDate, format)
set_metadata(dict) Set metadata (empty string deletes the entry)
get_page_text(pno) Extract text from a page
render_page(pno, scale=1.0, dpi=None, background=None) Render a page to PNG bytes; dpi replaces scale, background is an RGB(A) fill (max 65,535 px per side / 64 MP total)
render_page_svg(pno) Render a page to an SVG string
set_fallback_font(font, kind="sans", index=0) Set a fallback font (path/bytes) for non-embedded CJK fonts; None disables auto-detection
select(page_numbers) Keep only the given pages, in the given order (repeats duplicate the page)
delete_page(pno) / delete_pages(iterable) Delete pages
insert_pdf(other, from_page=0, to_page=-1, start_at=-1) Merge a page range (negative / reversed ranges; start_at sets the insertion position)
new_page(pno=-1, width=595, height=842) / copy_page(pno, to=-1) Insert a blank page / duplicate a page
get_toc() / set_toc(toc) Read/write outlines as [[level, title, page], ...] (page numbers are 1-based here)
save(filename, garbage=, deflate=, object_streams=, user_pw=, owner_pw=, permissions=) / tobytes(same) Save; prune / compress / object streams, or AES-256 encryption via user_pw / owner_pw (the in-memory document stays plain)
close() Close (supports with)

pylopdf.Page (obtained via doc[i]):

Method / property Description
number / parent 0-based page number and owning Document
get_text() / render(scale, dpi=, background=) / render_svg() Extraction and rendering
rotation / set_rotation(deg) Display rotation (multiples of 90, inheritance-resolved)
mediabox / cropbox / rect Page boxes (Rect); rect is the rotation-aware visible rectangle
set_mediabox(rect) / set_cropbox(rect) Set page boxes

Module level:

Name Description
peek_metadata(filename/stream, password=None) Fast metadata / page-count / encryption probe without parsing the whole file
Permissions Encryption permission flags (IntFlag)
Rect Rectangle NamedTuple with width / height
Exceptions PdfError (ValueError-compatible base), PasswordError, DocumentClosedError, EncryptedDocumentError, StalePageError

For low-level access, use pylopdf.pylopdf_core._Document (a thin lopdf wrapper) directly.

Architecture

Follows the division of labor in the 2026 Rust PDF ecosystem:

pylopdf.Document (Python, pymupdf-style API)
   └─ _Document (PyO3)
        ├─ lopdf 0.44   … editing: open → modify → save
        └─ hayro 0.7    … rendering: PNG / SVG (standard fonts embedded)
rust/          # PyO3 bindings
src/pylopdf/   # Python high-level API
tests/         # pytest (Rust behavior is verified through Python tests)
uv sync                    # build + install dependencies
uv run pytest              # tests
uv run ruff check .        # lint
uv run mypy src tests      # type check
uv build --wheel           # build a wheel

uv sync detects Rust source changes and rebuilds automatically (via tool.uv.cache-keys).

License

MIT (lopdf is MIT; hayro is MIT/Apache-2.0)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pylopdf-0.6.0.tar.gz (41.6 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pylopdf-0.6.0-cp310-abi3-win_amd64.whl (4.0 MB view details)

Uploaded CPython 3.10+Windows x86-64

pylopdf-0.6.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.5 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

pylopdf-0.6.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (4.3 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

pylopdf-0.6.0-cp310-abi3-macosx_11_0_arm64.whl (3.8 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

pylopdf-0.6.0-cp310-abi3-macosx_10_12_x86_64.whl (4.2 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file pylopdf-0.6.0.tar.gz.

File metadata

  • Download URL: pylopdf-0.6.0.tar.gz
  • Upload date:
  • Size: 41.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pylopdf-0.6.0.tar.gz
Algorithm Hash digest
SHA256 d4f023169570e23cb33e6573db734dc51628d73c9e1f067074a81ec88b5b7be9
MD5 b3dc37537e8b3a98142a9dfe1ea1f6c0
BLAKE2b-256 9fc76871691a9f7ff6aaff3f507c0a1f6b176274df596796fa9a9c15a4d348b9

See more details on using hashes here.

Provenance

The following attestation bundles were made for pylopdf-0.6.0.tar.gz:

Publisher: release.yml on yhay81/pylopdf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pylopdf-0.6.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: pylopdf-0.6.0-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 4.0 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pylopdf-0.6.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 28a3ebfc421f87de7fdbc0e626a2c97c177abe033ecda523c44877926db6cad3
MD5 cafc9c37ba6efe730e3def4b3f75c619
BLAKE2b-256 c3768af80e7709b1c71be79f13ffd075dc9a7fd009cccd5b9eea76e3467bba52

See more details on using hashes here.

Provenance

The following attestation bundles were made for pylopdf-0.6.0-cp310-abi3-win_amd64.whl:

Publisher: release.yml on yhay81/pylopdf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pylopdf-0.6.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pylopdf-0.6.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 6a9b66e1addf11147ea1ad042c68a7cdc741c0842033c002321c1734daba0154
MD5 f9fa9c448c22cfbf7f6fb422726c6907
BLAKE2b-256 8899442f429701fa25d4d7a812c4a3651325d8b635eda4c4a16358937580992d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pylopdf-0.6.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on yhay81/pylopdf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pylopdf-0.6.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pylopdf-0.6.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 f6e3b1c855e7b2a08957ac545b408b15df9c01967309b085207e8a17fc61ccc3
MD5 7236dd1576f4df5f1db6eb621a64da3f
BLAKE2b-256 c59b3a111edc3deeed22551f7c1ab074de22ab984b77d1dea3b60e70657ec5b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pylopdf-0.6.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on yhay81/pylopdf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pylopdf-0.6.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pylopdf-0.6.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b3e16d1449ecf9fb0f1e4c149e9797339b6b42cd2f297192b92e02753c184a73
MD5 b17d97dd4b36ce7871bb256bf36fe63e
BLAKE2b-256 7633cb7b770dd8ee62f04ca6a09dc04903a76b93e099a39aa7b09f8b82d2d65a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pylopdf-0.6.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on yhay81/pylopdf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pylopdf-0.6.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pylopdf-0.6.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 5f0051d7342cdb346207554b058bc0e93dd97e956e60e30558a5fd215561a594
MD5 4ee416168f70d32f3a76ae46090ebabf
BLAKE2b-256 e9a3613029d64cd169f7e72c625d3920d6ba48ceace7e5f6f537d8472661f16f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pylopdf-0.6.0-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on yhay81/pylopdf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page