scripts/pdf_split.py
scripts/pdf_split.pyBrowse 21 files
728 tokens
3,045 bytes
Token encoding: o200k_base
Snapshot 24fd22b
← Back to SKILL.md
1#!/usr/bin/env python32"""Extract page ranges from a PDF, optionally rotating and/or compressing pages."""3from __future__ import annotations4 5import argparse6import json7import sys8 9 10def parse_pages(spec: str, page_count: int) -> list[int]:11 """Parse a 1-based page spec like '1-3,5,9-' into 0-based indices."""12 indices: list[int] = []13 for part in spec.split(","):14 part = part.strip()15 if not part:16 continue17 if "-" in part:18 start_s, _, end_s = part.partition("-")19 start = int(start_s) if start_s else 120 end = int(end_s) if end_s else page_count21 else:22 start = end = int(part)23 if start < 1 or end > page_count or start > end:24 raise ValueError(f"Page range {part!r} out of bounds (1-{page_count})")25 indices.extend(range(start - 1, end))26 return indices27 28 29def main() -> int:30 for stream in (sys.stdout, sys.stderr):31 try:32 stream.reconfigure(encoding="utf-8")33 except Exception:34 pass35 parser = argparse.ArgumentParser(36 description="Split/extract pages from a PDF (pypdf). Pages are 1-based: '1-3,5,9-'.")37 parser.add_argument("pdf", help="Input PDF path")38 parser.add_argument("--pages", required=True, help="1-based page spec, e.g. '1-3,5,9-'")39 parser.add_argument("-o", "--output", required=True, help="Output PDF path")40 parser.add_argument("--rotate", type=int, default=0,41 help="Rotate extracted pages clockwise (multiple of 90)")42 parser.add_argument("--compress", action="store_true",43 help="Deflate content streams (modest savings; does not recompress images)")44 parser.add_argument("--password", help="Password if the input is encrypted")45 args = parser.parse_args()46 47 if args.rotate % 90 != 0:48 print("Error: --rotate must be a multiple of 90", file=sys.stderr)49 return 250 51 try:52 from pypdf import PdfReader, PdfWriter53 except ImportError:54 print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)55 return 256 57 reader = PdfReader(args.pdf)58 if reader.is_encrypted:59 if args.password is None or not reader.decrypt(args.password):60 print("Error: input is encrypted; pass --password", file=sys.stderr)61 return 362 try:63 indices = parse_pages(args.pages, len(reader.pages))64 except ValueError as exc:65 print(f"Error: {exc}", file=sys.stderr)66 return 267 68 writer = PdfWriter()69 for idx in indices:70 page = reader.pages[idx]71 if args.rotate:72 page.rotate(args.rotate)73 writer.add_page(page)74 if args.compress:75 for page in writer.pages:76 page.compress_content_streams()77 with open(args.output, "wb") as fh:78 writer.write(fh)79 print(json.dumps({"output": args.output, "page_count": len(indices), "rotated": args.rotate}))80 return 081 82 83if __name__ == "__main__":84 sys.exit(main())85