Metadata-Version: 2.4
Name: pdf-label-crop
Version: 0.1.0
Summary: Crop print-label PDFs down to the single label quadrant
Author: Branislav Remen
License-Expression: MIT
Project-URL: Homepage, https://github.com/branislav-remen/pdf-label-crop
Project-URL: Repository, https://github.com/branislav-remen/pdf-label-crop
Project-URL: Issues, https://github.com/branislav-remen/pdf-label-crop/issues
Keywords: pdf,label,crop,a4,print,whitespace
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyMuPDF>=1.24
Dynamic: license-file

# pdf-label-crop

A CLI tool for cropping print-label PDFs. It takes a PDF, decides whether and
how it needs to be cropped, and returns a new PDF containing only the label
itself, without the surrounding whitespace.

## Installation

```bash
pip install -e .
```

Requires Python 3.9+ and [PyMuPDF](https://pymupdf.readthedocs.io/).

## Usage

```bash
pdf-label-crop input.pdf [-o output.pdf]
```

Without `-o` the output is written next to the input as `<input>-crop.pdf`.

Examples:

```bash
# finished label -> copied through unchanged
pdf-label-crop label.pdf

# A4 sheet -> rotated to portrait and cropped to the label quadrant
pdf-label-crop sheet.pdf -o label.pdf
```

The tool reports which case applied on stdout:

```
label: already a finished label, copied unchanged -> label-crop.pdf
sheet: cropped to label quadrant -> label.pdf
```

## How it works

The input is classified by page size (MediaBox, tolerance ±5 mm):

1. **Finished label** — ~105 × 148 mm (a quarter of A4). Returned unchanged
   (byte-identical copy).
2. **A4 sheet** — 210 × 297 mm (portrait or landscape). The sheet is rotated to
   portrait, split into four quadrants (A top-left, B top-right, C bottom-left,
   D bottom-right) and reduced to the single non-blank quadrant (~105 × 148 mm).
3. Any other size → error.

The quadrant holding the label is located by rasterizing the page at low
resolution and counting non-white pixels. The actual crop is done from the
original vector content (`show_pdf_page`), so no quality is lost — the raster
is used only to decide which quadrant to keep.

### Note on orientation

The tool always produces a portrait output of ~105 × 148 mm. If the label is
placed on the sheet in landscape orientation, rotating to portrait turns its
content by 90° — this matches the "always portrait" strategy.

## Project layout

```
pdf_label_crop/
  cli.py        # argparse, orchestration
  classify.py   # size-based classification
  transform.py  # rotation, quadrant detection, vector crop
  core.py       # wiring and output writing
tests/          # pytest + synthetic fixture PDFs
```

## Tests

```bash
python -m pytest -q
```

Tests cover size classification, cropping for all four quadrants (portrait and
landscape), pass-through of finished labels, and the error case for unknown
sizes.
