Initial commit

This commit is contained in:
2025-05-12 17:47:32 +02:00
commit 363094029c
3 changed files with 323 additions and 0 deletions

92
README.md Normal file
View File

@@ -0,0 +1,92 @@
# impose\_booklet
`impose_booklet.py` is a lightweight Python script that reorders A5 single pages from a PDF into booklet signatures for duplex printing on A4 sheets and subsequent folding into A5 booklets. With the latest updates, it also supports **border trimming** and a **Manga mode**.
## Features
* Flexible signature size via `--sheets N` (default: 5 sheets → 20 pages per signature)
* Automatic padding with blank pages if the total page count isn’t divisible by the signature size
* Correct page arrangement for the front and back of each A4 sheet
* **Border trim** via `--trim X` (in mm): cuts X mm off each edge of every page
* **Skip trim** via `--skip-trim P1 P2 ...`: list of page numbers to exclude from trimming (e.g., title page)
* **Manga mode** via `--manga-mode`: horizontally mirrors the imposition layout for right-to-left reading
## Requirements
* Python 3.7 or newer
* `pypdf` (for PDF read/write)
* `pymupdf` (for border trim):
```bash
pip install pypdf pymupdf
```
## Installation
```bash
pip install pypdf pymupdf
```
## Usage
```bash
python impose_booklet.py <input.pdf> <output.pdf> [--sheets N] [--trim X] [--skip-trim S1 S2 ...] [--manga-mode]
```
* `<input.pdf>`: path to the source PDF containing A5 single pages
* `<output.pdf>`: path for the generated booklet-ready PDF
* `--sheets N` / `-s N`: number of sheets per signature (default: 5)
* `--trim X`: border trim in millimeters (default: 0)
* `--skip-trim S1 S2 ...`: pages to exclude from trimming (default: `1`)
* `--manga-mode`: mirror the imposition for right-to-left reading order
### Examples
1. **Standard booklet** (60 pages, 5 sheets per signature):
```bash
python impose_booklet.py mybook.pdf mybook_booklet.pdf --sheets 5
```
2. **With border trim** (10 mm on all edges, title page excluded):
```bash
python impose_booklet.py mybook.pdf mybook_trim.pdf --trim 10 --skip-trim 1
```
3. **Manga mode** (right-to-left):
```bash
python impose_booklet.py manga.pdf manga_booklet.pdf --sheets 4 --manga-mode
```
4. **Combined** (border trim + Manga mode):
```bash
python impose_booklet.py manga.pdf manga_trim_booklet.pdf --trim 5 --skip-trim 1 --manga-mode
```
## How It Works
1. **Load**: all A5 pages are read from the input PDF.
2. **Optional border trim**: if `--trim X` is set, every page (except those in `--skip-trim`) is cropped by X mm on each side.
3. **Calculate**: compute `pages_per_sig = sheets * 4` and determine how many signatures are needed: `ceil(total_pages / pages_per_sig)`.
4. **Pad**: append blank pages so each signature has exactly `pages_per_sig` pages.
5. **Impose**:
* **Normal mode**: place pages per sheet in the order: front-left (outermost), front-right (first), back-left (second), back-right (penultimate).
* **Manga mode**: horizontally mirror those four slots so the reading order follows right-to-left layout.
6. **Save**: write the reordered pages into the output PDF.
## Troubleshooting
* **Mixed page sizes**: the script uses the MediaBox of the first page as the template for blank pages.
* **No duplex printing**: ensure your printer driver supports two-sided printing.
## License & Disclaimer
This project is released under the MIT License. No warranty is provided for commercial suitability.
---
*Happy printing and folding!*

93
README_DE.md Normal file
View File

@@ -0,0 +1,93 @@
# impose\_booklet
`impose_booklet.py` ist ein kleines Python‑Skript, das A5‑Einzelseiten aus einer PDF so umsortiert, dass sie auf A4‑Bögen im Booklet‑Format duplex gedruckt und anschließend zu A5‑Heftchen gefaltet werden können.
Mit den neuen Updates unterstützt es jetzt zusätzlich **Border-Trim** und einen **Manga-Modus**.
## Features
* Flexible Größe der Signaturen (Lagen) über `--sheets N` (Standard: 5 Blätter → 20 Seiten pro Lage)
* Automatisches Auffüllen mit leeren Seiten, falls die Gesamtseitenzahl nicht durch eine Lage teilbar ist
* Korrekte Anordnung der Seiten für Vorder- und Rückseite jedes A4‑Bogens
* **Randbeschnitt** über `--trim X` (in mm): schneidet an allen Rändern X mm ab
* **Skip-Trim** mit `--skip-trim P1 P2 ...`: Seiten‑Nummern, die vom Trim ausgenommen werden (z. B. Titelblatt)
* **Manga-Modus** per `--manga-mode`: spiegelt die Imposition horizontal, damit rechts‑nach‑links gelesene Werke richtig aufgehängt werden
## Voraussetzungen
* Python 3.7 oder neuer
* `pypdf` (für das Lesen und Schreiben von PDFs)
* `pymupdf` (für Border-Trim-Funktion):
```bash
pip install pypdf pymupdf
```
## Installation
```bash
pip install pypdf pymupdf
```
## Verwendung
```bash
python impose_booklet.py <input.pdf> <output.pdf> [--sheets N] [--trim X] [--skip-trim S1 S2 ...] [--manga-mode]
```
* `<input.pdf>`: Pfad zur Eingabe‑PDF mit A5‑Einzelseiten
* `<output.pdf>`: Pfad zur Ausgabedatei (umsortierte PDF)
* `--sheets N` / `-s N`: Anzahl der Blätter pro Signatur/Lage (Default: 5)
* `--trim X`: Randbeschnitt in Millimetern (Default: 0)
* `--skip-trim S1 S2 ...`: Seiten, die nicht beschnitten werden sollen (Default: `1`)
* `--manga-mode`: aktiviert Spiegel‑Imposition für rechts‑nach‑links
### Beispiele
1. **Standard‑Booklet** (60 Seiten, 5 Blätter pro Lage):
```bash
python impose_booklet.py meinbuch.pdf meinbuch_booklet.pdf --sheets 5
```
2. **Mit Randbeschnitt** (10 mm an allen Rändern, Titelblatt ausgenommen):
```bash
python impose_booklet.py meinbuch.pdf meinbuch_trim.pdf --trim 10 --skip-trim 1
```
3. **Manga‑Modus** (rechts‑nach‑links Lesung):
```bash
python impose_booklet.py manga.pdf manga_booklet.pdf --sheets 4 --manga-mode
```
4. **Kombiniert** (Randbeschnitt + Manga):
```bash
python impose_booklet.py manga.pdf manga_trim_booklet.pdf --trim 5 --skip-trim 1 --manga-mode
```
## Funktionsweise im Detail
1. **Einlesen**: Lädt alle Seiten der Eingabe‑PDF als A5‑Einzelseiten.
2. **(Optional) Randbeschnitt**: Bei `--trim X` werden alle Seiten (außer `--skip-trim`) um X mm an jedem Rand beschnitten.
3. **Berechnung**: Bestimmt `pages_per_sig = sheets * 4` und die Anzahl der Signaturen = `ceil(total_pages / pages_per_sig)`.
4. **Auffüllen**: Falls nötig, werden leere Seiten angehängt, damit jede Signatur gleich groß ist.
5. **Imposition**:
* **Normal-Modus**: Reihenfolge je Blatt: Front‑Left = äußerste Seite, Front‑Right = erste, Back‑Left = zweite, Back‑Right = vorletzte.
* **Manga-Modus**: horizontale Spiegelung der vier Slots (damit die Lesesequenz rechts → links erhalten bleibt).
6. **Speichern**: Schreibt die neu geordnete PDF in die Ausgabedatei.
## Troubleshooting
* **Unterschiedliche Seitengrößen**: Das Skript nutzt die MediaBox der ersten Seite als Vorlage für Leerseiten.
* **Keine Duplexdruck-Funktion** im Druckertreiber: Sicherstellen, dass beidseitiger Druck unterstützt wird.
## Lizenz & Haftungsausschluss
Dieses Projekt steht unter der MIT-Lizenz. Keine Gewährleistung für kommerzielle Eignung.
---
*Viel Erfolg beim Drucken und Falten!*

138
impose_booklet.py Normal file
View File

@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""
impose_booklet.py
Reorders pages einer Eingabe-PDF mit A5-Einzelseiten zu Booklet-Signaturen
für den Duplexdruck auf A4 (Booklet-Faltung), schneidet optional
einen festen Rand ab und unterstützt einen Manga-Modus (Reihenfolge gespiegelt).
Beispiel:
python impose_booklet.py input.pdf output.pdf --sheets 5 --trim 5 --skip-trim 1 --manga-mode
"""
import argparse
import os
import tempfile
import fitz # PyMuPDF: pip install pymupdf
from pypdf import PdfReader, PdfWriter # pip install pypdf
# Umrechnung Millimeter → Punkte (1 pt = 1/72 in, 1 in = 25.4 mm)
MM_TO_PT = 72.0 / 25.4
def trim_pdf(input_path: str, skip_pages: set, trim_mm: float) -> str:
"""
Trimmt jede Seite am Rand um trim_mm mm (alle außer skip_pages, 1-basiert).
Gibt Pfad zur neuen, zugeschnittenen PDF zurück.
"""
doc = fitz.open(input_path)
trim_pt = trim_mm * MM_TO_PT
for idx, page in enumerate(doc, start=1):
if idx in skip_pages:
continue
full = page.rect
page.set_cropbox(fitz.Rect(
full.x0 + trim_pt,
full.y0 + trim_pt,
full.x1 - trim_pt,
full.y1 - trim_pt
))
fd, tmp_path = tempfile.mkstemp(suffix="_trimmed.pdf")
os.close(fd)
doc.save(tmp_path)
doc.close()
return tmp_path
def impose_booklet(input_pdf: str, output_pdf: str,
sheets_per_sig: int = 5, manga_mode: bool = False):
"""
Liest alle A5-Seiten, optional trimmt sie, und schreibt
sie in der Reihenfolge für Booklet-Druck (linksbündig oder manga-spiegel).
"""
# 1) PDF einlesen
reader = PdfReader(input_pdf)
pages = list(reader.pages) # A5-Einzelseiten
# 2) vorbereiten: wie viele Signaturen, Blank-Pages
num_pages = len(pages)
pages_per_sig = sheets_per_sig * 4
num_sigs = (num_pages + pages_per_sig - 1) // pages_per_sig
blanks = num_sigs * pages_per_sig - num_pages
pages += [None] * blanks # am Ende fehlende Seiten
# 3) Maße für leere Seiten
w = float(reader.pages[0].mediabox.width)
h = float(reader.pages[0].mediabox.height)
writer = PdfWriter()
# 4) Imposition pro Signatur
for sig in range(num_sigs):
start = sig * pages_per_sig
for j in range(sheets_per_sig):
# Basis-Indizes in der Signatur
i1 = start + 2*j
i2 = start + 2*j + 1
i3 = start + pages_per_sig - (2*j + 2)
i4 = start + pages_per_sig - (2*j + 1)
if not manga_mode:
order = (i4, i1, i2, i3) # FL, FR, BL, BR
else:
order = (i1, i4, i3, i2) # gespiegelt: FL←i1, FR←i4, BL←i3, BR←i2
for idx in order:
pg = pages[idx]
if pg is None:
writer.add_blank_page(width=w, height=h)
else:
writer.add_page(pg)
# 5) Ausgabe schreiben
with open(output_pdf, "wb") as f:
writer.write(f)
mode_txt = " (Manga-Modus)" if manga_mode else ""
print(f"Erstellt: '{output_pdf}' mit {num_sigs} Lagen à {sheets_per_sig} Blättern{mode_txt}.")
if __name__ == "__main__":
p = argparse.ArgumentParser(
description="A5-Booklet-Imposition auf A4 mit optionalem Border-Trim & Manga-Modus"
)
p.add_argument("input_pdf", help="Eingabe-PDF mit A5-Einzelseiten")
p.add_argument("output_pdf", help="Ausgabe-PDF (umsortiert)")
p.add_argument(
"-s","--sheets", type=int, default=5,
help="Anzahl Blätter pro Signatur (Default: 5)"
)
p.add_argument(
"--trim", type=float, default=0.0,
help="Trimmt Rand um X mm (Default: 0 mm)"
)
p.add_argument(
"--skip-trim", type=int, nargs="+", default=[1],
help="Seiten-Nummern, die nicht getrimmt werden (Default: [1])"
)
p.add_argument(
"--manga-mode", action="store_true",
help="Spiegelt die Imposition horizontal für rechts-nach-links Lesung"
)
args = p.parse_args()
work_in = args.input_pdf
if args.trim > 0:
work_in = trim_pdf(work_in, set(args.skip_trim), args.trim)
impose_booklet(
work_in,
args.output_pdf,
sheets_per_sig=args.sheets,
manga_mode=args.manga_mode
)
if args.trim > 0:
try:
os.remove(work_in)
except:
pass