# PDF Toolkit A small command-line toolkit for common PDF operations, built with Python and [PyMuPDF](https://pymupdf.readthedocs.io/) (`fitz`). The scripts can: - extract a page range from a PDF; - join two PDF files; - place consecutive pages side by side while preserving links; and - remove repeated text such as a text-based watermark. ## Project structure ```text PDF-Toolkit/ ├── requirements.txt └── scripts/ ├── extract_pages.py ├── join_files.py ├── side_by_side.py └── watermark_remover.py ``` ## Requirements - Python 3.10 or later is recommended. - Dependencies listed in `requirements.txt`. - PyMuPDF, imported by the scripts as `fitz`. ## Installation Clone or download the project, then create and activate a virtual environment. ### macOS and Linux ```bash python3 -m venv .venv source .venv/bin/activate python -m pip install -r requirements.txt ``` ### Windows PowerShell ```powershell python -m venv .venv .venv\Scripts\Activate.ps1 python -m pip install -r requirements.txt ``` Run the examples below from the project root. Quote paths that contain spaces. ## Tools ### Extract pages `extract_pages.py` copies an inclusive range of pages from a source PDF into a new PDF. Page numbers are one-based, so the first page is page `1`. ```bash python scripts/extract_pages.py INPUT_PDF OUTPUT_PDF START END ``` Arguments: | Argument | Description | |---|---| | `INPUT_PDF` | Path to the source PDF. | | `OUTPUT_PDF` | Path for the extracted PDF. | | `START` | First page to extract, inclusive. | | `END` | Last page to extract, inclusive. | Example—extract pages 3 through 8: ```bash python scripts/extract_pages.py documents/report.pdf output/report-pages-3-8.pdf 3 8 ``` The output is saved with garbage collection, compression, and font/image deflation enabled. ### Join two PDFs `join_files.py` appends the complete second PDF to the complete first PDF and saves the result as a third file. ```bash python scripts/join_files.py FIRST_PDF SECOND_PDF OUTPUT_PDF ``` Arguments: | Argument | Description | |---|---| | `FIRST_PDF` | PDF whose pages appear first. | | `SECOND_PDF` | PDF appended after the first file. | | `OUTPUT_PDF` | Path for the combined PDF. | Example: ```bash python scripts/join_files.py documents/part-1.pdf documents/part-2.pdf output/combined.pdf ``` This tool accepts exactly two input PDFs per invocation. To combine more files, run it repeatedly using the previous output as the next first input. ### Place pages side by side `side_by_side.py` creates a two-page-wide, or “2-up,” PDF. It places consecutive source pages next to each other: - the first page in each pair is placed on the left; - the second page is placed on the right; and - an unpaired final page is placed by itself. The script also remaps link annotations so that external URLs and internal page links continue to work in the new layout. Other link types are copied on a best-effort basis. ```bash python scripts/side_by_side.py INPUT_PDF OUTPUT_PDF START_PAGE ``` Arguments: | Argument | Description | |---|---| | `INPUT_PDF` | Path to the source PDF. | | `OUTPUT_PDF` | Path for the side-by-side PDF. | | `START_PAGE` | One-based source page at which processing begins. Earlier pages are omitted. | Example—start at page 1: ```bash python scripts/side_by_side.py documents/manual.pdf output/manual-2up.pdf 1 ``` Example—skip the cover and start at page 2: ```bash python scripts/side_by_side.py documents/manual.pdf output/manual-2up.pdf 2 ``` Because the output contains only pages from `START_PAGE` onward, internal links that point to an omitted earlier page are not expected to have a valid destination. The current implementation is best suited to files whose relevant link targets are included in the processed range. ### Remove a text watermark `watermark_remover.py` searches every page for an exact text string, adds redaction annotations over every match, applies the redactions, and saves a cleaned copy. ```bash python scripts/watermark_remover.py INPUT_PDF OUTPUT_PDF WATERMARK_TEXT ``` Arguments: | Argument | Description | |---|---| | `INPUT_PDF` | Path to the source PDF. | | `OUTPUT_PDF` | Path for the processed PDF. | | `WATERMARK_TEXT` | Exact text to find and redact. Quote text containing spaces. | Example: ```bash python scripts/watermark_remover.py documents/sample.pdf output/sample-clean.pdf "CONFIDENTIAL" ``` The script prints the rectangle of each match and reports the output path when processing is complete. Images intersecting a redaction are preserved because redactions are applied with `PDF_REDACT_IMAGE_NONE`. > **Important:** This tool works on searchable PDF text. It will not detect a watermark that is embedded only as an image, converted to outlines, or otherwise absent from the PDF text layer. Redaction permanently removes matched content from the generated output, so keep the original file as a backup. ## Getting command help Each script uses `argparse`. Run a script with `--help` to see its command syntax: ```bash python scripts/extract_pages.py --help python scripts/join_files.py --help python scripts/side_by_side.py --help python scripts/watermark_remover.py --help ``` ## Notes - Input files must exist and be readable. - The destination directory must already exist. - Existing output files may be overwritten or may cause an error depending on the platform and PyMuPDF behavior, so use a new output path when possible. - These scripts do not currently perform explicit validation for page ranges, encrypted PDFs, damaged files, or an output path that matches an input path. - Always verify the generated PDF before deleting or replacing the original. ## Dependency The scripts use PyMuPDF: ```text PyMuPDF ``` Keep the project’s `requirements.txt` as the authoritative dependency list and install it with: ```bash python -m pip install -r requirements.txt ```