Thanks to visit codestin.com
Credit goes to lib.rs

#pdf #printing #graphics #booklet #saddle-stitch #imposition

bin+lib imposer

A Rust library and CLI for imposing single-sheet PDFs into booklet layouts with configurable n-up binding

3 releases (breaking)

0.3.0 Nov 27, 2025
0.2.0 Nov 5, 2025
0.1.0 Nov 2, 2025

#951 in Command line utilities

MIT license

190KB
3K SLoC

imposer

A small, focused Rust library and CLI for arranging PDF pages into booklet layouts (n-up) with common binding options.

Features

  • Flexible n-up Imposition: Support for power-of-two pages-per-sheet (2-up, 4-up, 8-up, 16-up, 32-up, ...)
  • Multiple Binding Types: Saddle stitch and perfect binding with full mathematical accuracy
  • Automatic Padding: Intelligently pads to maintain proper sheet divisibility
  • Correct Page Ordering: Proper duplex booklet page ordering for accurate printing

Binding Types Explained

Saddle Stitch (Nested Binding)

Saddle stitch is the traditional booklet binding where pages are nested together. Each sheet wraps the previous one, creating a nested structure.

Structure:

Sheet 1 (outer):  Front: [Last, First]        Back: [Second, Last-1]
Sheet 2 (inner):  Front: [Last-2, Third]      Back: [Fourth, Last-3]
Sheet 3 (inner):  Front: [Last-4, Fifth]      Back: [Sixth, Last-5]
                  ... (continues inward)

Example: 8 pages, 2-up saddle stitch:

Sheet 1:  Front: [8, 1]    Back: [2, 7]
Sheet 2:  Front: [6, 3]    Back: [4, 5]
         ↓ Nest sheets (Sheet 2 inside Sheet 1)
Result: Pages fold correctly from outside to inside

Perfect Binding (Signatures Stacking)

Perfect binding groups pages into signatures, where each signature internally uses saddle-stitch ordering, but signatures are then stacked sequentially (not nested). This is how paperback books are bound.

Structure:

Signature 1: Pages 1-8    (internally: saddle-stitch ordered)
Signature 2: Pages 9-16   (internally: saddle-stitch ordered)
Signature 3: Pages 17-24  (internally: saddle-stitch ordered)
             ↓ Stack signatures (Sig 2 behind Sig 1, etc.)

Key Concept: Sheets Per Signature

The sheets_per_signature parameter controls how many physical sheets go into each signature:

  • 1 sheet per signature: Each signature contains a single nested sheet. The entire booklet is one large stacking of signatures. For 8 pages with 2-up and 1 sheet/sig, you get 2 signatures of 4 pages each.

  • 2+ sheets per signature: Multiple sheets are nested within each signature before stacking. For 8 pages with 2-up and 2 sheets/sig, you get 1 signature where both sheets are nested (identical to pure saddle stitch).

Important: Physical Sheets vs. Printing Sheets

The sheets_per_signature parameter refers to physical sheets after cutting. Different n-up values produce different numbers of physical sheets from one printing sheet:

  • 2-up: 1 printing sheet → 1 physical sheet
  • 4-up: 1 printing sheet → 2 physical sheets (cut horizontally)
  • 8-up: 1 printing sheet → 4 physical sheets (cut horizontally and vertically)

This means --perfect-binding --sheets-per-signature 2 --2up produces the same page grouping as --perfect-binding --sheets-per-signature 4 --4up, just with different grid layouts.

Example: 16 pages perfect binding comparison

Saddle Stitch (single signature):
  Sheet 1: [16, 1, 14, 3] / [2, 15, 4, 13]
  Sheet 2: [12, 5, 10, 7] / [6, 11, 8, 9]
  (all nested together)

Perfect Binding with 1 sheet/sig:
  Signature 1 (pages 1-8):
    Sheet 1: [8, 1, 6, 3] / [2, 7, 4, 5]
  Signature 2 (pages 9-16):
    Sheet 1: [16, 9, 14, 11] / [10, 15, 12, 13]
  (signatures stacked)

Perfect Binding with 2 sheets/sig:
  Signature 1 (pages 1-16):
    Sheet 1: [16, 1, 14, 3] / [2, 15, 4, 13]
    Sheet 2: [12, 5, 10, 7] / [6, 11, 8, 9]
  (identical to pure saddle stitch)

Algorithm Implementation

The imposition algorithms correctly:

  • Pad odd-page inputs to the next multiple of pages_per_sheet
  • Calculate optimal grid layout for page arrangement
  • Apply proper page reversal for duplex printing (varies by n-up value)
  • Handle arbitrary power-of-2 n-up values
  • Preserve blank pages in mathematically correct positions for proper folding
  • Verified with 109-page test across multiple n-up values (all pages present, no duplicates)

Documentation

Find the crate and detailed documentation online:

Notes and Capabilities

  • Assumes reasonably uniform page sizes across input PDF
  • Pads output with blank pages for proper sheet divisibility
  • Blank pages appear in mathematically correct positions (not consolidated)

Command-line options reference

Required options

  • -i, --input <PATH>: Input PDF file path. Must be a valid PDF.
  • -o, --output <PATH>: Output PDF file path. The generated booklet will be written here.

Layout and binding options

  • -n, --pages-per-sheet <N> (default: 2): Number of pages to arrange per sheet side. Must be a power of 2 for saddle stitch (2, 4, 8, 16, 32, 64, ...). No restriction for perfect binding.

  • -p, --page-size <SIZE> (default: a4): Output page size. Valid options: a4, a3, a5, letter, legal, tabloid.

  • --perfect-binding: Use perfect binding instead of saddle stitch. Perfect binding stacks signatures sequentially (like a paperback book) rather than nesting them. Cannot be used with saddle-stitch-specific settings.

Perfect binding signature control

Note: These options only apply when --perfect-binding is used.

  • --sheets-per-signature <N> (default: 1): Number of physical sheets to nest within each signature. A value of 1 creates simple stacking; higher values create sewn signatures with nested sheets inside.

  • --num-signatures <N> (optional): If specified, pages are evenly distributed across this many signatures. This takes precedence over --sheets-per-signature. Useful when you want a specific number of signature sections regardless of content.

Important note on --sheets-per-signature and --num-signatures: These are mutually exclusive concepts:

  • Use --sheets-per-signature when you want to control the physical structure of each signature (how many sheets are nested per signature).
  • Use --num-signatures when you want to control the number of signature sections the document is divided into. If both are specified, --num-signatures takes precedence.

Scaling and appearance options

  • --no-scale-to-fit: Do not scale source pages to fit the output page size. By default, pages are scaled to fill the available space while respecting margins.

  • --no-preserve-aspect-ratio: Allow pages to be stretched or compressed to fill the output page. By default, aspect ratio is preserved, which may result in letterboxing or pillarboxing.

  • --draw-guides: Draw fold and cut guide lines on the output. Useful for debugging and visualizing the page layout before printing.

  • --number-pages: Replace page content with page numbers. Useful for visualizing the page order without needing to view the actual PDF content.

Default behavior

If you run imposer -i input.pdf -o output.pdf with no other options:

  • Uses saddle stitch binding
  • Creates a 2-up layout (2 pages per sheet)
  • Output size is A4
  • Pages are scaled to fit the output size while preserving aspect ratio
  • No guides or page numbers are drawn

Building

cargo build --release

Development

This project uses cargo-make to replicate CI checks locally.

Install cargo-make

cargo install cargo-make

Run all CI checks

cargo make

This runs all checks that CI performs:

  • Code formatting (cargo fmt --check)
  • Clippy linting (cargo clippy)
  • Rust tests (cargo nextest run)
  • Python bindings build and tests
  • Python bindings synchronization check

Available tasks

  • cargo make - Run all CI checks (default)
  • cargo make quick - Quick checks (format + clippy only)
  • cargo make rust-only - Rust checks only (skip Python)
  • cargo make all-continue - Run all checks, continue on errors (useful for debugging)
  • cargo make fmt - Auto-format code
  • cargo make clippy-fix - Auto-fix clippy issues
  • cargo make test - Run Rust tests only
  • cargo make python-test - Run Python tests only
  • cargo make --list-all-steps - List all available tasks

Note: The default task stops on first error (matching CI behavior). If you want to run all checks even when some fail, use cargo make all-continue.

Pre-commit workflow

Before committing, ensure all checks pass:

cargo make

If cargo make passes locally, CI should also pass.

Try it

cargo run --bin imposer -- -i input.pdf -o booklet.pdf

Installation

Using the binary (when Cargo bin is in your PATH)

If you prefer to run imposer directly from your shell without cargo run, install the binary into your Cargo bin directory (usually ~/.cargo/bin) and make sure that directory is on your PATH.

  • Install locally from the repository (installs into ~/.cargo/bin):
cargo install --path .
  • Or install the published crate from crates.io:
cargo install imposer
  • Ensure ~/.cargo/bin is on your PATH (add to your shell profile if necessary):
export PATH="$HOME/.cargo/bin:$PATH"
# Add the line above to ~/.bashrc, ~/.zshrc, or your shell profile to make it persistent

Using imposer from the command line

Once imposer is installed and in your PATH, you can run it directly:

imposer -i input.pdf -o output.pdf

Common usage examples

Saddle stitch 2-up (default):

imposer -i input.pdf -o booklet.pdf

Saddle stitch 4-up (4 pages per sheet):

imposer -i input.pdf -o booklet.pdf -n 4

Perfect binding with 2 sheets per signature, 2-up layout:

imposer -i input.pdf -o book.pdf --perfect-binding --sheets-per-signature 2

Perfect binding 4-up (larger pages per sheet):

imposer -i input.pdf -o book.pdf --perfect-binding -n 4

All options and defaults are available via the help command:

imposer --help

Python bindings

imposer also provides Python bindings via PyO3, allowing you to use the library directly in Python code. This is useful for:

  • Integrating PDF imposition into Python applications
  • Batch processing PDFs programmatically
  • Embedding booklet generation in web services
  • Automating workflows in tools like Scribus

Installation

Install the Python package from PyPI:

pip install imposer

Or install locally from the repository:

pip install .

Basic usage

import imposer

# Create a default 2-up A4 saddle-stitch booklet
config = imposer.BookletConfig()
imposer.generate_booklet_from_file("input.pdf", "output.pdf", config)

Configuration options

import imposer

# Configure a custom booklet
binding = imposer.BindingType.perfect_bound(
    sheets_per_signature=8,  # 8 sheets per signature
    num_signatures=3         # 3 total signatures
)

config = (
    imposer.BookletConfig()
    .with_page_size(imposer.PageSize.a4())
    .with_pages_per_sheet(4)
    .with_binding_type(binding)
    .with_draw_guides(True)       # Draw cut/fold lines
    .with_number_pages(True)      # Add page numbers
    .with_scale_to_fit(True)      # Scale pages to fit
    .with_preserve_aspect_ratio(True)
)

imposer.generate_booklet_from_file("input.pdf", "output.pdf", config)

In-memory processing

Process PDFs without intermediate files:

import imposer

# Read input PDF
with open("input.pdf", "rb") as f:
    input_bytes = f.read()

# Generate booklet configuration
config = imposer.BookletConfig()

# Process in memory
output_bytes = imposer.generate_booklet(input_bytes, config)

# Write output
with open("output.pdf", "wb") as f:
    f.write(output_bytes)

Available page sizes

imposer.PageSize.a4()      # A4 (210 × 297 mm)
imposer.PageSize.a3()      # A3 (297 × 420 mm)
imposer.PageSize.a5()      # A5 (148 × 210 mm)
imposer.PageSize.letter()  # Letter (8.5 × 11 in)
imposer.PageSize.legal()   # Legal (8.5 × 14 in)
imposer.PageSize.tabloid() # Tabloid (11 × 17 in)

Binding types

# Saddle-stitch (default): pages nested, stapled in the middle
binding = imposer.BindingType.saddle_stitch()

# Perfect binding: signatures stacked, glued at spine
binding = imposer.BindingType.perfect_bound(
    sheets_per_signature=4,    # Pages per signature (default: 1)
    num_signatures=None        # Override with specific signature count
)

Examples

See examples/python_example.py for detailed examples including:

  • Basic booklet creation
  • Different page sizes
  • Various n-up layouts
  • Perfect binding with signatures
  • Guides and page numbers
  • In-memory processing
  • Scribus integration

Run the example:

python examples/python_example.py input.pdf

Dependencies

~35MB
~580K SLoC