pylopdf 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pylopdf-0.4.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Yusuke Hayashi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pylopdf-0.4.1/PKG-INFO ADDED
@@ -0,0 +1,157 @@
1
+ Metadata-Version: 2.4
2
+ Name: pylopdf
3
+ Version: 0.4.1
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Programming Language :: Rust
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Classifier: Typing :: Typed
16
+ License-File: LICENSE
17
+ Summary: PDF editing and rendering for Python, powered by Rust (lopdf + hayro). MIT, zero dependencies, lightweight wheels.
18
+ Home-Page: https://github.com/yhay81/pylopdf
19
+ Author-email: Yusuke Hayashi <yusuke8h@gmail.com>
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
22
+
23
+ # pylopdf
24
+
25
+ [日本語版 README はこちら](README.ja.md)
26
+
27
+ PDF editing and rendering for Python, powered by Rust — [lopdf](https://github.com/J-F-Liu/lopdf) for editing and [hayro](https://github.com/LaurenzV/hayro) (the pure-Rust PDF renderer adopted by typst) for rendering.
28
+
29
+ **MIT licensed, zero runtime dependencies, lightweight wheels.** Covers the common pymupdf use cases without the AGPL.
30
+
31
+ ## Why pylopdf?
32
+
33
+ | | pylopdf | pymupdf | pypdf | pypdfium2 |
34
+ |---|---|---|---|---|
35
+ | License | **MIT** | AGPL / commercial | BSD | Apache/BSD |
36
+ | Wheel size | **~3.5 MB** | ~40 MB+ | small (pure Python) | ~8 MB |
37
+ | Editing (merge / split / metadata) | ✅ | ✅ | ✅ | limited |
38
+ | Rendering (PNG / SVG) | ✅ | ✅ | ❌ | ✅ (PNG) |
39
+ | Text extraction | ✅ (basic) | ✅ (advanced) | ✅ | ✅ |
40
+ | Implementation | **pure Rust** | C | Python | C++ (PDFium) |
41
+
42
+ - Fits size-constrained environments such as AWS Lambda
43
+ - Safe for commercial projects that need to avoid the AGPL
44
+ - abi3: one wheel covers Python 3.10–3.14
45
+ - API modeled after [pymupdf](https://github.com/pymupdf/PyMuPDF)
46
+
47
+ **Limitations**: no precise layout analysis, no annotation/form editing, no encrypted PDFs. Use pymupdf if you need those.
48
+
49
+ ## Install
50
+
51
+ ```bash
52
+ pip install pylopdf # (after PyPI release)
53
+ ```
54
+
55
+ Building from source (requires a Rust toolchain):
56
+
57
+ ```bash
58
+ uv sync
59
+ ```
60
+
61
+ ## Usage
62
+
63
+ ```python
64
+ import pylopdf
65
+
66
+ # Open from a path or bytes
67
+ doc = pylopdf.open("input.pdf")
68
+ doc = pylopdf.open(stream=pdf_bytes)
69
+
70
+ # Page count
71
+ print(doc.page_count) # same as len(doc)
72
+
73
+ # Metadata
74
+ print(doc.metadata["title"])
75
+ doc.set_metadata({"title": "Monthly Report", "author": "Alice"})
76
+
77
+ # Text extraction (0-based page numbers)
78
+ text = doc.get_page_text(0)
79
+
80
+ # Rendering
81
+ png: bytes = doc.render_page(0) # 72 dpi
82
+ png2x: bytes = doc.render_page(0, scale=2) # 144 dpi
83
+ svg: str = doc.render_page_svg(0)
84
+
85
+ # Delete pages (split)
86
+ doc.delete_page(0)
87
+ doc.delete_pages([1, 2])
88
+
89
+ # Keep/reorder pages
90
+ doc.select([2, 0])
91
+
92
+ # Merge
93
+ merged = pylopdf.Document()
94
+ merged.insert_pdf(pylopdf.open("a.pdf"))
95
+ merged.insert_pdf(pylopdf.open("b.pdf"))
96
+
97
+ # Save
98
+ merged.save("merged.pdf")
99
+ data: bytes = merged.tobytes()
100
+
101
+ # Context manager
102
+ with pylopdf.open("input.pdf") as doc:
103
+ print(doc.metadata)
104
+ ```
105
+
106
+ ## API
107
+
108
+ `pylopdf.Document` (`pylopdf.open()` is an alias constructor):
109
+
110
+ | Method / property | Description |
111
+ |---|---|
112
+ | `Document(filename=None, stream=None)` | Open from a path or bytes; empty document if both are None |
113
+ | `page_count` / `len(doc)` | Number of pages |
114
+ | `metadata` | Metadata dict (title, author, subject, keywords, creator, producer, creationDate, modDate, format) |
115
+ | `set_metadata(dict)` | Set metadata (empty string deletes the entry) |
116
+ | `get_page_text(pno)` | Extract text from a page |
117
+ | `render_page(pno, scale=1.0)` | Render a page to PNG bytes |
118
+ | `render_page_svg(pno)` | Render a page to an SVG string |
119
+ | `select(page_numbers)` | Keep only the given pages, in the given order |
120
+ | `delete_page(pno)` / `delete_pages(iterable)` | Delete pages |
121
+ | `insert_pdf(other)` | Append all pages of another document |
122
+ | `save(filename)` / `tobytes()` | Save |
123
+ | `close()` | Close (supports `with`) |
124
+
125
+ For low-level access, use `pylopdf.pylopdf_core._Document` (a thin lopdf wrapper) directly.
126
+
127
+ ## Architecture
128
+
129
+ Follows the division of labor in the 2026 Rust PDF ecosystem:
130
+
131
+ ```
132
+ pylopdf.Document (Python, pymupdf-style API)
133
+ └─ _Document (PyO3)
134
+ ├─ lopdf 0.44 … editing: open → modify → save
135
+ └─ hayro 0.7 … rendering: PNG / SVG (standard fonts embedded)
136
+ ```
137
+
138
+ ```
139
+ rust/ # PyO3 bindings
140
+ src/pylopdf/ # Python high-level API
141
+ tests/ # pytest (Rust behavior is verified through Python tests)
142
+ ```
143
+
144
+ ```bash
145
+ uv sync # build + install dependencies
146
+ uv run pytest # tests
147
+ uv run ruff check . # lint
148
+ uv run mypy src tests # type check
149
+ uv build --wheel # build a wheel
150
+ ```
151
+
152
+ `uv sync` detects Rust source changes and rebuilds automatically (via `tool.uv.cache-keys`).
153
+
154
+ ## License
155
+
156
+ MIT (lopdf is MIT; hayro is MIT/Apache-2.0)
157
+
@@ -0,0 +1,134 @@
1
+ # pylopdf
2
+
3
+ [日本語版 README はこちら](README.ja.md)
4
+
5
+ PDF editing and rendering for Python, powered by Rust — [lopdf](https://github.com/J-F-Liu/lopdf) for editing and [hayro](https://github.com/LaurenzV/hayro) (the pure-Rust PDF renderer adopted by typst) for rendering.
6
+
7
+ **MIT licensed, zero runtime dependencies, lightweight wheels.** Covers the common pymupdf use cases without the AGPL.
8
+
9
+ ## Why pylopdf?
10
+
11
+ | | pylopdf | pymupdf | pypdf | pypdfium2 |
12
+ |---|---|---|---|---|
13
+ | License | **MIT** | AGPL / commercial | BSD | Apache/BSD |
14
+ | Wheel size | **~3.5 MB** | ~40 MB+ | small (pure Python) | ~8 MB |
15
+ | Editing (merge / split / metadata) | ✅ | ✅ | ✅ | limited |
16
+ | Rendering (PNG / SVG) | ✅ | ✅ | ❌ | ✅ (PNG) |
17
+ | Text extraction | ✅ (basic) | ✅ (advanced) | ✅ | ✅ |
18
+ | Implementation | **pure Rust** | C | Python | C++ (PDFium) |
19
+
20
+ - Fits size-constrained environments such as AWS Lambda
21
+ - Safe for commercial projects that need to avoid the AGPL
22
+ - abi3: one wheel covers Python 3.10–3.14
23
+ - API modeled after [pymupdf](https://github.com/pymupdf/PyMuPDF)
24
+
25
+ **Limitations**: no precise layout analysis, no annotation/form editing, no encrypted PDFs. Use pymupdf if you need those.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install pylopdf # (after PyPI release)
31
+ ```
32
+
33
+ Building from source (requires a Rust toolchain):
34
+
35
+ ```bash
36
+ uv sync
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ```python
42
+ import pylopdf
43
+
44
+ # Open from a path or bytes
45
+ doc = pylopdf.open("input.pdf")
46
+ doc = pylopdf.open(stream=pdf_bytes)
47
+
48
+ # Page count
49
+ print(doc.page_count) # same as len(doc)
50
+
51
+ # Metadata
52
+ print(doc.metadata["title"])
53
+ doc.set_metadata({"title": "Monthly Report", "author": "Alice"})
54
+
55
+ # Text extraction (0-based page numbers)
56
+ text = doc.get_page_text(0)
57
+
58
+ # Rendering
59
+ png: bytes = doc.render_page(0) # 72 dpi
60
+ png2x: bytes = doc.render_page(0, scale=2) # 144 dpi
61
+ svg: str = doc.render_page_svg(0)
62
+
63
+ # Delete pages (split)
64
+ doc.delete_page(0)
65
+ doc.delete_pages([1, 2])
66
+
67
+ # Keep/reorder pages
68
+ doc.select([2, 0])
69
+
70
+ # Merge
71
+ merged = pylopdf.Document()
72
+ merged.insert_pdf(pylopdf.open("a.pdf"))
73
+ merged.insert_pdf(pylopdf.open("b.pdf"))
74
+
75
+ # Save
76
+ merged.save("merged.pdf")
77
+ data: bytes = merged.tobytes()
78
+
79
+ # Context manager
80
+ with pylopdf.open("input.pdf") as doc:
81
+ print(doc.metadata)
82
+ ```
83
+
84
+ ## API
85
+
86
+ `pylopdf.Document` (`pylopdf.open()` is an alias constructor):
87
+
88
+ | Method / property | Description |
89
+ |---|---|
90
+ | `Document(filename=None, stream=None)` | Open from a path or bytes; empty document if both are None |
91
+ | `page_count` / `len(doc)` | Number of pages |
92
+ | `metadata` | Metadata dict (title, author, subject, keywords, creator, producer, creationDate, modDate, format) |
93
+ | `set_metadata(dict)` | Set metadata (empty string deletes the entry) |
94
+ | `get_page_text(pno)` | Extract text from a page |
95
+ | `render_page(pno, scale=1.0)` | Render a page to PNG bytes |
96
+ | `render_page_svg(pno)` | Render a page to an SVG string |
97
+ | `select(page_numbers)` | Keep only the given pages, in the given order |
98
+ | `delete_page(pno)` / `delete_pages(iterable)` | Delete pages |
99
+ | `insert_pdf(other)` | Append all pages of another document |
100
+ | `save(filename)` / `tobytes()` | Save |
101
+ | `close()` | Close (supports `with`) |
102
+
103
+ For low-level access, use `pylopdf.pylopdf_core._Document` (a thin lopdf wrapper) directly.
104
+
105
+ ## Architecture
106
+
107
+ Follows the division of labor in the 2026 Rust PDF ecosystem:
108
+
109
+ ```
110
+ pylopdf.Document (Python, pymupdf-style API)
111
+ └─ _Document (PyO3)
112
+ ├─ lopdf 0.44 … editing: open → modify → save
113
+ └─ hayro 0.7 … rendering: PNG / SVG (standard fonts embedded)
114
+ ```
115
+
116
+ ```
117
+ rust/ # PyO3 bindings
118
+ src/pylopdf/ # Python high-level API
119
+ tests/ # pytest (Rust behavior is verified through Python tests)
120
+ ```
121
+
122
+ ```bash
123
+ uv sync # build + install dependencies
124
+ uv run pytest # tests
125
+ uv run ruff check . # lint
126
+ uv run mypy src tests # type check
127
+ uv build --wheel # build a wheel
128
+ ```
129
+
130
+ `uv sync` detects Rust source changes and rebuilds automatically (via `tool.uv.cache-keys`).
131
+
132
+ ## License
133
+
134
+ MIT (lopdf is MIT; hayro is MIT/Apache-2.0)
@@ -0,0 +1,82 @@
1
+ [project]
2
+ name = "pylopdf"
3
+ version = "0.4.1"
4
+ description = "PDF editing and rendering for Python, powered by Rust (lopdf + hayro). MIT, zero dependencies, lightweight wheels."
5
+ authors = [{ "name" = "Yusuke Hayashi", "email" = "yusuke8h@gmail.com" }]
6
+ readme = "README.md"
7
+ requires-python = ">=3.10"
8
+ license = { file = "LICENSE" }
9
+ dependencies = []
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
20
+ "Programming Language :: Rust",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ "Typing :: Typed",
23
+ ]
24
+
25
+ [build-system]
26
+ requires = ["maturin>=1.14,<2.0"]
27
+ build-backend = "maturin"
28
+
29
+ [tool.maturin]
30
+ manifest-path = "rust/Cargo.toml"
31
+ python-source = "src"
32
+ module-name = "pylopdf.pylopdf_core"
33
+ features = ["pyo3/extension-module"]
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ "maturin>=1.14.1",
38
+ "mypy>=1.14",
39
+ "pre-commit>=4.6.1",
40
+ "pytest>=8.3",
41
+ "ruff>=0.9",
42
+ ]
43
+
44
+ [tool.uv]
45
+ # Rust ソース変更時に uv が拡張モジュールを再ビルドするためのキャッシュキー
46
+ cache-keys = [
47
+ { file = "pyproject.toml" },
48
+ { file = "rust/Cargo.toml" },
49
+ { file = "rust/src/*.rs" },
50
+ ]
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]
54
+
55
+ [tool.ruff]
56
+ line-length = 120
57
+ src = ["src"]
58
+ target-version = "py310"
59
+ # python/ と lambda_test/ は本ライブラリの対象外(別作業の同居ファイル)
60
+ extend-exclude = ["python", "lambda_test"]
61
+
62
+ [tool.ruff.lint]
63
+ select = ["ALL"]
64
+ ignore = [
65
+ "D100",
66
+ "D104",
67
+ # ruff format と競合するルール
68
+ "COM812",
69
+ "ISC001",
70
+ "D203",
71
+ "D213",
72
+ # 日本語ドキュメント方針(CLAUDE.md)と衝突するルール
73
+ "D400", # 「。」を終端句読点と認識しない
74
+ "D415", # 同上
75
+ "D403", # 日本語文中の英単語先頭を大文字化してしまう
76
+ "RUF001", # 全角記号を ambiguous unicode として誤検出する
77
+ "RUF002",
78
+ "RUF003",
79
+ ]
80
+
81
+ [tool.ruff.lint.per-file-ignores]
82
+ "tests/*" = ["INP001", "S101", "D103", "PLR2004", "TC003"]