Initial commit
This commit is contained in:
92
README.md
Normal file
92
README.md
Normal 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
93
README_DE.md
Normal 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
138
impose_booklet.py
Normal 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
|
||||
Reference in New Issue
Block a user