Skip to content

Commit 3fa0f97

Browse files
jet52claude
andcommitted
Support pypdf 3.x and bump to 2.0.2
Guard the pypdf 4.0+ compress_identical_objects call with hasattr so it is skipped on older pypdf rather than crashing. Lower the dependency floor to pypdf>=3.17.0 and update CLAUDE.md (stale pikepdf references left over from the 2.0.1 switch back to pypdf). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3023b4f commit 3fa0f97

3 files changed

Lines changed: 13 additions & 10 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ python splitmarks.py <input.pdf> [options]
1919
splitmarks <input.pdf> [options]
2020

2121
# Build standalone executable (local: --onedir for fast startup; CI uses --onefile)
22-
pip install pyinstaller pikepdf
22+
pip install pyinstaller pypdf
2323
pyinstaller --onedir --name splitmarks splitmarks.py
2424

2525
# Install local build: stash the bundle in ~/.local/share and symlink into ~/bin
@@ -44,7 +44,7 @@ Single-file tool: everything lives in `splitmarks.py` (no package structure).
4444

4545
**Key components in `splitmarks.py`:**
4646
- `Bookmark` dataclass — tree structure preserving PDF outline hierarchy
47-
- `parse_outline_tree()` / `_parse_outline_items()` — recursive pikepdf outline parsing, handles both `/Dest` and `/A` (GoTo action) destinations
47+
- `parse_outline_tree()` / `_parse_outline_items()` — recursive pypdf outline parsing, handles both `/Dest` and `/A` (GoTo action) destinations
4848
- `calculate_page_ranges()` — maps each top-level bookmark to its page span
4949
- `split_pdf()` — main logic: open PDF, parse, filter, split, write output files
5050
- `add_bookmarks_to_writer()` — promotes child bookmarks to top level in split output files
@@ -55,7 +55,7 @@ Single-file tool: everything lives in `splitmarks.py` (no package structure).
5555
## Key Details
5656

5757
- **Python >= 3.10** required (uses `str | None` union syntax)
58-
- **pikepdf** is the sole external dependency (migrated from pypdf for smaller output files via `remove_unreferenced_resources()` and `ObjectStreamMode.generate`)
58+
- **pypdf** (`>=3.17.0`) is the sole external dependency. Output files are slimmed via `writer.compress_identical_objects(remove_identicals=True, remove_orphans=True)`, which only exists in pypdf 4.0+, so the call is guarded with `hasattr` and skipped on 3.x (splitting still works, just without that compression)
5959
- Version is tracked in **two places**: `__version__` in `splitmarks.py` and `version` in `pyproject.toml` — keep them in sync
6060
- Entry point registered in pyproject.toml: `splitmarks = "splitmarks:main"`
6161

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "splitmarks"
7-
version = "2.0.1"
7+
version = "2.0.2"
88
description = "Split PDF files at top-level bookmarks into separate files"
99
readme = "README.md"
1010
requires-python = ">=3.10"
1111
license = {text = "MIT"}
1212
dependencies = [
13-
"pypdf>=4.0.0",
13+
"pypdf>=3.17.0",
1414
]
1515

1616
[project.scripts]

splitmarks.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
print("Error: pypdf is required. Install with: pip install pypdf", file=sys.stderr)
1717
sys.exit(1)
1818

19-
__version__ = "2.0.1"
19+
__version__ = "2.0.2"
2020

2121

2222
@dataclass
@@ -427,10 +427,13 @@ def split_pdf(
427427
writer, child, start_page, end_page
428428
)
429429

430-
# Remove unreferenced objects (images/fonts from other sections)
431-
writer.compress_identical_objects(
432-
remove_identicals=True, remove_orphans=True
433-
)
430+
# Remove unreferenced objects (images/fonts from other sections).
431+
# compress_identical_objects was added in pypdf 4.0; skip it on
432+
# older versions rather than crashing.
433+
if hasattr(writer, "compress_identical_objects"):
434+
writer.compress_identical_objects(
435+
remove_identicals=True, remove_orphans=True
436+
)
434437

435438
try:
436439
with open(output_path, "wb") as f:

0 commit comments

Comments
 (0)