Metadata-Version: 2.3
Name: pdf-helper
Version: 0.9.0
Summary: A simple python script that helps with doing simple stuff with PDFs.
Author: CodeWriter21(Mehrad Pooryoussof)
Author-email: CodeWriter21(Mehrad Pooryoussof) <CodeWriter21@gmail.com>
License: MIT
Requires-Dist: log21>=3.12.2
Requires-Dist: pypdfium2>=4.30.0
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: arabic-reshaper>=3.0.0
Requires-Dist: python-bidi>=0.5
Requires-Dist: pikepdf>=10 ; extra == 'all'
Requires-Dist: pillow>=11.0.0 ; extra == 'all'
Requires-Dist: pikepdf>=10 ; extra == 'pikepdf'
Requires-Dist: pillow>=11.0.0 ; extra == 'pillow'
Requires-Dist: pytest>=9.1.1 ; extra == 'test'
Requires-Python: >=3.10
Project-URL: Homepage, https://gitlab.com/CodeWriter21/pdf-helper
Project-URL: Issues, https://gitlab.com/CodeWriter21/pdf-helper/-/issues
Project-URL: Repository, https://gitlab.com/CodeWriter21/pdf-helper
Provides-Extra: all
Provides-Extra: pikepdf
Provides-Extra: pillow
Provides-Extra: test
Description-Content-Type: text/markdown

PDF-Helper
==========

A simple python package that helps with doing simple stuff with PDFs.

Features
--------

+ [x] **Bundle**: Bundle multiple files into one PDF
  + [x] PDF inputs
  + [x] Image inputs (e.g. PNG, JPG, etc.)
  + [ ] Markdown inputs
+ [x] **Merge PDFs**: Merge multiple PDFs into one PDF
+ [x] **Split PDFs**: Split a PDF into multiple PDFs, each containing a range of pages
+ [x] **Export as image**: Export designated pages from a PDF as image files
+ [x] **Remove pages**: Remove designated pages from a PDF
+ [x] **Extract text**: Export text from a PDF file and optionally save it to a text file
+ [x] **Recipe system**: Chain multiple operations together using YAML recipe files
+ [x] **Add watermark**: Overlay selectable vector text or an image on PDF pages
+ [x] **Encrypt a PDF** (user/owner passwords, AES-256/128, RC4)
+ [x] **Decrypt a PDF** (user or owner password)
+ [ ] Extract images from a PDF
+ [ ] Extract links from a PDF
+ [x] **Set PDF metadata** (title, author, etc.)

If you want any other feature to be added, feel free to open an [issue](https://gitlab.com/CodeWriter21/pdf-helper/-/issues)
or fork the repo and make a [merge request](https://gitlab.com/CodeWriter21/pdf-helper/-/merge_requests)
after adding your contribution.

Usage
-----

### Installation

You can install PDF-Helper via pip:

```bash
pip install pdf-helper

# Or use uv to install the tool
uv tool install pdf-helper
```

Document operations (metadata editing, encryption, and the automatic
metadata stamping) need the optional `pikepdf` dependency:

```bash
pip install "pdf-helper[pikepdf]"
```

Image operations (rendering pages to images, image watermarks, bundling or
converting image files) need the optional `Pillow` dependency:

```bash
pip install "pdf-helper[pillow]"
```

Or everything at once:

```bash
pip install "pdf-helper[all]"
```

Everything else — bundling PDFs, splitting, text watermarking, text
extraction, and recipes not using those operations — works without them. If
a feature needs the missing extra, the command fails with the install
instruction above instead of a traceback.

And run it using the command line:

```bash
pdf-helper <command> [options]
```

Or you can use uvx to run the package without installing it in a specific python environment:

```bash
uvx pdf-helper <command> [options]
```

You can also clone the repository and use `uv run`:

```bash
git clone https://gitlab.com/CodeWriter21/pdf-helper.git
cd pdf-helper
uv run pdf-helper <command> [options]
```

### Bundle PDFs

Bundle multiple files into one PDF:

```bash
pdf-helper bundle <input_file_1> <input_file_2>... <input_file_n> <output_file>

# E.g. Bundle PDFs 1, 2 and 3 into a new PDF
pdf-helper bundle 1.pdf 2.pdf 3.pdf new.pdf

# E.g. Take 1.png, 2.jpg, and 3.png and create a PDF named 123.pdf and override
# if already exists
pdf-helper bundle 1.png 2.jpg 3.png 123.pdf -f

# E.g. Take part1.pdf, image1.png, ending.pdf and bundle them into a PDF named final.pdf
pdf-helper bundle part1.pdf image1.png ending.pdf final.pdf -v
```

### Split PDFs

Split a PDF into multiple PDFs, each containing a range of pages:

```bash
pdf-helper split <input_file> <output_folder> -s <split_point_1>,<split_point_2>

# E.g. Split a PDF into three PDFs, one with pages 1-10, the second with pages 11-20 and
# the third with pages 21-end
pdf-helper split my-pdf.pdf my-split-pdfs -s 10,20

# E.g. Split a PDF into PDFs each containing one page
pdf-helper split my-pdf.pdf my-split-pdfs  # No need to specify split points
```

### Export PDF pages as image files

Export PDF pages as image files:

```bash
pdf-helper to-image <input_file> <output_folder> \
        -p <page_number_1>,<page_number_2>,...,<page_number_n> -s <scale_factor>

# E.g. Export pages 1, 2, 3 and 6 from a PDF with scale factor 1
pdf-helper to-image 1.pdf images -p 1:3,6 -s 1

# E.g. Export the last three pages with scale factor 1
pdf-helper to-image 1.pdf images -p -3:-1 -s 1

# E.g. Export all pages from a PDF with scale 2
pdf-helper to-image my-pdf.pdf my-images
```

### Remove pages from a PDF

Remove pages from a PDF:

```bash
pdf-helper remove-pages <input_file> <output_file> <page_number_1>,<page_number_2>,...,<page_number_n>

# E.g. Remove pages 1, 2, 3 and 6 from a PDF
pdf-helper remove-pages 1.pdf new.pdf 1:3,6
```

### Add watermark to a PDF

Overlay selectable vector text or an image file on PDF pages:

```bash
# E.g. Stamp diagonal DRAFT text across all pages
pdf-helper watermark add my-pdf.pdf watermarked.pdf DRAFT

# E.g. Overlay a logo in the bottom-right corner of pages 1:3
pdf-helper watermark add my-pdf.pdf watermarked.pdf --watermark-image logo.png \
    --position bottom-right --image-scale 0.5 --pages 1:3

# E.g. Stamp 5% above the bottom edge, at the left edge
pdf-helper watermark add my-pdf.pdf watermarked.pdf "DRAFT" --position "0 -5%"

# E.g. Stamp text with a custom font (file path or installed name like Arial)
pdf-helper watermark add my-pdf.pdf watermarked.pdf "DRAFT" --font ./fonts/MyFont.ttf

# E.g. Stamp red text (CSS name, hex, or RGB tuple)
pdf-helper watermark add my-pdf.pdf watermarked.pdf "DRAFT" --color red

# E.g. Scale the text with the page: '50%' spans half the page width
pdf-helper watermark add my-pdf.pdf watermarked.pdf "DRAFT" --font-size "50%" --rotation 0

# E.g. Remove watermarks added by pdf-helper
pdf-helper watermark clear watermarked.pdf clean.pdf

# E.g. Swap one watermark for another in a single run
pdf-helper watermark replace watermarked.pdf new.pdf NEWMARK --remove-text DRAFT
```

Watermarks placed by pdf-helper carry an invisible mark, so `clear` removes exactly
those; with `text`, matching text objects are stripped too (best effort).

`--font-size` accepts points (`48`) or a percent (`"50%"`, quoted so the
shell passes it through): a percent scales the text so its width spans that
fraction of the page extent along the watermark direction — `'50%'` with
`--rotation 0` is a horizontal text half as wide as the page. Percents
resolve per page, keeping mixed portrait/landscape documents proportional.
Recipes accept the same values in `font_size`.

Text watermarks use Arial when it is installed (covering Latin, Greek,
Cyrillic, Arabic and more), falling back to built-in Helvetica
(Latin only) otherwise. RTL text (Persian/Arabic) is shaped into
visual order automatically:

```bash
# E.g. Stamp a Persian watermark with an installed font
pdf-helper watermark add my-pdf.pdf watermarked.pdf "سلام" --font "Vazirmatn Regular"
```

Not sure which fonts are available? Query them (`name` or `path` both work
with `--font`):

```bash
pdf-helper list-fonts
pdf-helper list-fonts arial
```

See [`examples/recipes/watermark-showcase.yaml`](examples/recipes) for a recipe
that chains a text watermark and an image watermark in one run.

### Manage PDF metadata

Read, edit, or clear the metadata of a PDF file:

```bash
# E.g. Show all set metadata fields (accepts multiple files)
pdf-helper metadata show my-pdf.pdf other.pdf

# E.g. Set the title and author
pdf-helper metadata edit my-pdf.pdf tagged.pdf --title "Report" --author "Me"

# E.g. Remove all metadata (Info dictionary and XMP packet)
pdf-helper metadata clear my-pdf.pdf clean.pdf
```

Settable fields: `title`, `author`, `subject`, `keywords`, `creator`,
`producer`, `creationdate`, `moddate`. Dates accept PDF date strings, ISO
dates (`2026-09-04`, `2026-09-04 12:30`), or datetimes, and `show` prints
them in ISO format. Writes keep the XMP packet synchronized with the Info
dictionary (and `show` prefers XMP values when present). Every file
pdf-helper writes gets
`Producer` (`PDF-Helper <version>`, unless already set), a `CreationDate`
if missing, and a fresh `ModDate` automatically.

### Encrypt and decrypt PDFs

Protect a PDF with a password, or remove the protection again:

```bash
# E.g. Require a password to open the file
pdf-helper encrypt my-pdf.pdf secure.pdf --password s3cret

# E.g. Acrobat-style lock: anyone may open, only the owner may edit
pdf-helper encrypt my-pdf.pdf secure.pdf --password "" --owner-password admin

# E.g. Decrypt with either password
pdf-helper decrypt secure.pdf open.pdf --password s3cret
```

The user password opens the file; the owner password (defaults to the user
password) additionally grants full control. Passwords can also come from the
`PDF_HELPER_PASSWORD` / `PDF_HELPER_OWNER_PASSWORD` environment variables,
and are never echoed back. Algorithms: `AES-256` (default), `AES-128`, `RC4`
(recipes additionally support per-operation `permissions`).

Every command that reads PDFs accepts `--password` (or `PDF_HELPER_PASSWORD`)
for encrypted inputs — including `metadata edit`/`clear`, `watermark`,
`extract-text`, and friends. In recipes, add a `password:` key (plain value
or `{input: name}` reference) to any step that opens an encrypted file.

### Export text from a PDF

To extract text from a PDF file and export them to text files you can do as follows:

```bash
pdf-helper extract-text <input_file> -o <output_file_name>

# E.g. Extract text from a PDF named my-pdf.pdf and save it to my-text.txt
pdf-helper extract-text my-pdf.pdf -o my-text.txt
```

### Run Recipes

The recipe system lets you chain multiple PDF operations together in a single run
using a YAML file. This unlocks features not available through individual CLI
commands (e.g. selecting specific pages per file when bundling).

```bash
pdf-helper recipe run <recipe_file.yaml>

# E.g. Run a simple recipe
pdf-helper recipe run remove-pages.yaml

# E.g. Run with force overwrite and verbose logging
pdf-helper recipe run bundle-workflow.yaml --force --verbose

# E.g. Run a built-in recipe template instead of a file
pdf-helper recipe run --builtin remove-pages --input report.pdf --output cleaned.pdf

# E.g. Run a custom recipe from ~/.local/pdf-helper/recipes by name
pdf-helper recipe run --custom my-clean --input report.pdf --output cleaned.pdf
```

Custom recipes are plain YAML recipe files stored in
`~/.local/pdf-helper/recipes` (override with the `PDF_HELPER_RECIPES_DIR`
environment variable) and run with the same `--input`/`--output`/`--defines`
bindings. `recipe generate` lists them alongside the built-in templates.

#### Parameterized Recipes

Recipes can take their main input/output paths (and any extra values) from
the command line instead of hardcoding them, so one recipe works on many
files. Write `{input}`, `{output}`, or any `{name}` placeholder in the YAML
and bind it at runtime:

```yaml
name: "Remove specific pages"
version: "1.0"
parameters: [input, output]

steps:
  - id: clean
    operation: remove_pages
    input: "{input}"
    pages_to_remove: [2, 4, 6]
    output: "{output}"
```

```bash
pdf-helper recipe run clean.yaml --input report.pdf --output cleaned.pdf
pdf-helper recipe run clean.yaml --input a.pdf --output a-clean.pdf \
    --defines pages=1:3 --defines mode=strict
```

`--input`/`--output` bind `{input}`/`{output}`; repeatable `--defines
KEY=VALUE` binds any other `{name}`. The optional `parameters:` list
declares what a recipe expects — bare names are required, while
`name: default` mappings provide defaults that CLI flags override:

```yaml
parameters:
  - input
  - output
  - label: PDF-Helper
  - pages: "1,-1"
```

Path parts are available as `{input.stem}`, `{input.name}`,
`{input.suffix}`, and `{input.parent}`, so outputs can be named after the
input (e.g. `output: "{input.stem}-pages"`). Defaults may reference other
placeholders, so `- output: "{input.stem}-stamped-pages"` works as an
overridable default.

Repeat `--input` to pass multiple files as one array input: a bare
`{input}` item inside a step list (e.g. bundle `inputs: ["{input}"]`)
expands to every file. Array inputs need scalar outputs (path attributes
like `{input.stem}` and embedding in longer strings are rejected with a
clear error):

```bash
# E.g. Bundle two images, then stamp a label on every page
pdf-helper recipe run stamp-on-images.yaml --input a.png --input b.png \
    --output stamped.pdf --defines label=DRAFT
```

See [`examples/recipes/stamp-on-images.yaml`](examples/recipes) for the
full recipe.

Page selections use comma-separated items where `start:end` selects an
inclusive range: `1:3,6` means pages 1, 2, 3 and 6. Numbers are 1-based
(`0` is rejected), and negatives count back from the last page, so `5:-2`
means from page 5 to one page before the last. Values starting with `-`
must use the `--option=value` form (`--pages="-2:-1"`) so the CLI does not
mistake them for flags. Built-in templates (`recipe generate` lists them)
accept bindings the same way via `--builtin <name>`.

#### Recipe File Format

A recipe is a YAML file with a `steps` list. Each step has an `id`, an
`operation`, input/output paths, and operation-specific options. Steps can
reference each other's outputs using `{ step: step_id }`.

```yaml
name: "Remove specific pages"
description: "Removes pages 2, 4, 6 from a PDF."
version: "1.0"

steps:
  - id: clean
    operation: remove_pages
    input: document.pdf
    pages_to_remove: [2, 4, 6]
    output: cleaned.pdf
```

#### Supported Operations

| Operation | Status | Description |
|---|---|---|
| `bundle` | Available | Bundle files with optional per-file page selection |
| `remove_pages` | Available | Remove pages by 1-based index |
| `split_pdf` | Available | Split at given page boundaries |
| `pdf_to_image` | Available | Render pages as PNG images |
| `extract_text` | Available | Extract text content |
| `watermark` | Available | Overlay vector text (`text`) or an image (`image`) |
| `clear_watermark` | Available | Remove pdf-helper watermarks (plus `text` matches) |
| `replace_watermark` | Available | Clear old watermarks and add new ones in one run |
| `encrypt` | Available | Password-protect PDF (AES-256/128, RC4) |
| `decrypt` | Available | Remove password protection |
| `metadata` | Available | Set title/author/keywords (+creator/dates) |
| `clear_metadata` | Available | Remove all metadata (Info + XMP) |

Operations marked *Planned* are not yet implemented — the recipe runner
logs a warning and copies the input file through, so pipelines don't break.

Watermark text is inserted as selectable vector objects; image watermarks are
raster overlays. Both support `position`, `opacity`, `rotation`, and `pages`.

#### Advanced Example: Multi-step Pipeline

```yaml
# yaml-language-server: $schema=https://gitlab.com/CodeWriter21/pdf-helper/-/raw/master/schemas/recipe-schema.json
name: "Split, Convert, and Extract Pipeline"
version: "1.0"

settings:
  temp_dir: "./.recipe-tmp"

steps:
  # Step 1: Split the PDF at pages 5 and 10
  - id: split
    operation: split_pdf
    input: report.pdf
    split_points: [5, 10]
    output_dir: .
    output_prefix: "report_part_"

  # Step 2: Convert the second chunk to images
  - id: to_images
    operation: pdf_to_image
    input:
      step: split
      file: report_part_2.pdf
    pages: "1:3"
    scale: 3
    output: ./output/images

  # Step 3: Extract text from the first chunk
  - id: extract
    operation: extract_text
    input:
      step: split
      file: report_part_1.pdf
    pages: "1:4"
    max_characters: 5000
    reverse_lines: true
    output: ./output/chapter-1-text.txt
```

#### Recipe Settings

| Setting | Default | Description |
|---|---|---|
| `temp_dir` | `./.recipe-tmp` | Directory for intermediate files |
| `overwrite` | `false` | Overwrite existing output files |
| `cleanup_temp` | `false` | Remove temp directory after completion |

Input values (e.g. passwords) can be sourced from environment variables or
prompted at runtime:

```yaml
inputs:
  password:
    env: PDF_PASSWORD
    prompt: "Enter output PDF password"
```

See [`examples/recipes/`](examples/recipes) for more example recipe files.

About
-----

Author: [CodeWriter21](https://gitlab.com/CodeWriter21)

GitLab: [CodeWriter21/pdf-helper](https://gitlab.com/CodeWriter21/pdf-helper)

Donations
---------

Your donations are very welcome: [nowpayments.io](https://nowpayments.io/donation/MehradP21)

You can also consider donating a
[Star](https://gitlab.com/CodeWriter21/pdf-helper) to the repo.

License
-------

This project is licensed under the MIT License.

See the [LICENSE](LICENSE)

References
----------

+ [pypdfium2](https://pypdfium2.readthedocs.io/en/stable/readme.html)
+ [PILlow](https://pillow.readthedocs.io/en/stable/)
+ [log21](https://gitlab.com/CodeWriter21/log21)
