Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

fotobuch

fotobuch turns one (or more) folder of photos into a print-ready PDF photo book, in a highly automated way.

Point it at your photos and fotobuch figures out the layout automatically:

  • aspect ratios stay intact
  • reading order flows naturally from top-left to bottom-right
  • photos from the same event stay together on the same page
  • more important photos get more space (you decide)
  • optionally add titles and an appendix with metadata for each photo, automatically generated from your images’ EXIF data

Start fully automatic, then fine-tune individual pages by hand as needed.

How fotobuch works

fotobuch has a very simple data model. It stores each photo book as a pair of two files: a YAML file (.yaml) and a Typst template (.typ) tracked by Git inside a vault folder. The photo book can be read and modified through:

  • the GUI — a visual, interactive interface for browsing and adjusting pages
  • the CLI — scriptable commands for batch operations and automation

Both tools operate on exactly the same project data. You can mix and match: import photos with the CLI, review and tweak in the GUI, then kick off the release build from either side.

Your original photos are never modified. fotobuch only reads your source files to create cached copies at the configured DPI. Commands like remove delete photos from the project YAML — your originals on disk are untouched.

Example project

The repository ships with a complete example in docs/examples/ — a ready-made project with sample images and both preview and release PDFs. A good way to explore the YAML, the Typst template, and the output without creating your own project first.

Source code

github.com/EddyXorb/fotobuch

Installation

Download the latest binary for your platform from the Releases page:

PlatformFile
Linux x86_64fotobuch-linux-x86_64.tar.gz
Windows x86_64fotobuch-windows-x86_64.zip
macOS arm64 (M1 and up)fotobuch-macos-arm64.tar.gz

Each archive contains both binaries: fotobuch (CLI) and fotobuch-gui (GUI). Extract the archive and place them somewhere on your PATH.

Build from source

Requirements: Rust (stable).

git clone https://github.com/EddyXorb/fotobuch.git
cd fotobuch
cargo build --release --features gui
# binaries:
#   cli: ./target/release/fotobuch
#   gui: ./target/release/fotobuch-gui

Working with the CLI

Verify the install:

fotobuch --version

fotobuch writes a Typst source file alongside the PDF. For a live preview while you work, install VS Code with the Typst Preview extension. Open the .typ file and the preview updates every time you run fotobuch build.

Alternatively, just keep any PDF viewer open and reload after each build.

Shell completions

fotobuch completions --shell bash   >> ~/.bash_completion
fotobuch completions --shell zsh    >> ~/.zshrc
fotobuch completions --shell fish   > ~/.config/fish/completions/fotobuch.fish
fotobuch completions --shell powershell >> $PROFILE

macOS: “Apple could not verify that fotobuch is free of malware”

fotobuch is not signed with an Apple Developer ID and therefore not notarized by Apple. Anything downloaded through a browser gets the com.apple.quarantine attribute, and Gatekeeper blocks quarantined binaries that carry no notarization ticket — the dialog only offers Move to Trash and Done. The binary is fine; macOS simply cannot ask Apple whether it has been checked.

Pick whichever way you prefer:

1. Install from the terminal (recommended). curl does not set the quarantine attribute, so nothing is ever blocked:

curl -fsSL https://github.com/EddyXorb/fotobuch/releases/latest/download/fotobuch-macos-arm64.tar.gz | tar -xz
mkdir -p ~/.local/bin && mv fotobuch fotobuch-gui ~/.local/bin/
~/.local/bin/fotobuch --version

Add ~/.local/bin to your PATH if it is not there yet:

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc

2. Remove the quarantine flag from an existing download:

xattr -dr com.apple.quarantine ~/Downloads/fotobuch-macos-arm64

3. Without the terminal: double-click the binary, dismiss the warning, then open System Settings → Privacy & Security, scroll to the security section and click Open Anyway next to the message about fotobuch. Confirm once per binary — fotobuch and fotobuch-gui are separate executables.

Apple Silicon only: the release contains an arm64 build. Intel Macs need a build from source.

Core Concepts

Projects and vaults

A fotobuch project is a pair of files tracked by Git — a YAML config, a Typst template. Multiple projects can live in the same Git repository (called a vault), each on its own branch. Switching projects is a Git checkout under the hood.

See the Glossary for precise definitions of vault, project, and template.

Photos and groups

When you import a folder, all photos in that folder become a group. Groups matter because fotobuch tries to keep photos from the same group together on the same page (or on neighbouring pages). Think of a group as “photos from one occasion”.

Each subfolder you import is a separate group. Groups are sorted chronologically — by the date in the folder name if there is one, otherwise by the oldest photo’s timestamp.

Placed vs. unplaced

A photo can be in one of two states:

StateMeaning
unplacedIn the project, but not assigned to any page yet
placedAssigned to a specific slot on a specific page

When you build or place photos for the first time, all unplaced photos are distributed automatically across pages. After that first build, newly added photos start as unplaced and need to be placed before the next build.

Pages and slots

fotobuch arranges photos into a grid-like layout on each page. Each photo occupies a slot — a rectangular area with a specific position and size.

Both pages and slots are numbered from 0:

Page 0          Page 1          Page 2
┌──┬──┬──┐     ┌─────┬──┐     ┌──┬─────┐
│0 │1 │2 │     │  0  │1 │     │0 │     │
├──┴──┼──┤     │     ├──┤     ├──┤  2  │
│  3  │4 │     ├─────┤2 │     │1 │     │
└─────┴──┘     └─────┴──┘     └──┴─────┘

Slots are ordered left-to-right, top-to-bottom (reading order).

Weights

Every photo has an area weight (default: 1.0). fotobuch uses weights to decide how much space each photo gets relative to its neighbours on the same page. But beware: the actual chosen layout can considerably deviate from the ideal weight ratios, because the solver also has to respect aspect ratios and other constraints.

  • Weight 2.0 → roughly twice the area of a weight-1.0 photo
  • Weight 0.5 → roughly half

Set weights at import time when you already know some photos should dominate:

fotobuch add /photos/2024-Italy --weight 2.0   # all photos in this folder get weight 2

Adjust weights per slot after placement:

fotobuch page weight 3:2 2.0   # slot 2 on page 3 gets weight 2

In the GUI: select a slot and press W, or right-click → Set weight…

Weights only affect photos on the same page. A photo with weight 3.0 gets more space than its page-neighbours, but it has no influence on other pages. After changing weights, run fotobuch rebuild --page N (or press R in the GUI) to let the solver recalculate the layout for that page.

Cover

If you create a project with a cover enabled, page 0 becomes the cover. The cover spans the full width (front + spine + back) and has its own bleed and margin settings. See Cover Modes for the layout options.

The .fotobuch cache

fotobuch stores resized preview and final images in a .fotobuch/cache/ directory inside your project folder. This is purely a cache — delete it at any time without losing data. The next build regenerates whatever it needs.

Vaults and Multiple Projects

A fotobuch vault is a folder that is also a Git repository containing at least one git branch that starts with “fotobuch/”. It holds any number of fotobuch projects — each on its own branch named fotobuch/<project-name>. When using the CLI, fotobuch looks for the vault in the current working directory only. When using the GUI, fotobuch looks for the vault using a priority chain (see below) and opens it automatically on startup.

Branch layout

All projects share the same repository root. At any time the working tree shows the files of the currently checked-out project:

vault/
├── .git/
├── Italy-2024.yaml        ← current project (checked out)
├── Italy-2024.typ
└── .fotobuch/             ← ignored by git, shared cache for all projects 
    └── cache/
        ├── Italy-2024/
        └── Hiking-2025/   ← cache for project Hiking-2025, not visible in the working tree

GUI: Default vault and how fotobuch finds it

When you open fotobuch in the GUI it resolves the vault using this priority chain:

PrioritySourceDescription
1--vault <path> CLI flagExplicit path, overrides everything
2FOTOBUCH_VAULT env variableSet in shell or CI environment
3Current working directoryUsed if the directory already contains fotobuch/* branches
4Last-used vaultRemembered from the previous session
5Default: ~/Pictures/FotobuchCreated automatically on first use

The default vault path is ~/Pictures/Fotobuch (or the platform equivalent of the Pictures directory).

Configuration

Every project has a {project-name}.yaml file that controls the entire book. You don’t need to write this file from scratch — creating a new project generates it with sensible defaults. Edit only the parts you want to change.

For a quick overview of the most important settings to check before your first build, see Step 2 in the CLI Quickstart.

Tip: If the solver produces poor results, the most common fixes are: increase search_timeout (more time), or disable enable_local_search. You may also want to tweak the solver weights according to your needs (e.g. increase weight_split if you want groups to be respected more strictly).


Full reference

The YAML has this structure:

config:
  book:                  # page dimensions, margins, bleed, cover
    cover:               # cover-specific settings
  book_layout_solver:    # photo-to-page distribution
  page_layout_solver:    # single-page layout (genetic algorithm)
    weights:             # fitness function weights (nested)
  preview:               # preview rendering options

All fields below are optional unless marked otherwise. Defaults are applied automatically for any field you don’t set.


config.book — Page dimensions and layout

FieldDefaultDescription
title"Untitled"Book title. Used as the default spine text on the cover.
page_width_mm210.0Page width in mm. Set at project creation with --width. The resulting PDF’s width will have exactly this width plus any bleed. If you want double-page spreads, use the combined width (e.g. 420).
page_height_mm297.0Page height in mm. Set at project creation with --height.
bleed_mm3.0Bleed area in mm added around each page. Only active when margin_mm is 0. Cut off by the printer. Most services require 3 mm.
margin_mm0.0Minimum inset from the page edge. 0 = edge-to-edge (photos may bleed). > 0 = white border (bleed extension is disabled).
gap_mm5.0Space in mm between photos on the same page.
bleed_threshold_mm3.0Only active when margin_mm is 0. If a photo’s edge is closer to the page edge than this value, the layout is scaled so the photo extends fully into the bleed area. Prevents thin white strips at the page edge after cutting.
dpi300.0DPI for the final release PDF. Controls the resolution of cached images used in the release build.

config.book.cover — Cover page

All cover fields are optional. The cover is inactive by default. Enable it by setting active: true and providing dimensions.

FieldDefaultDescription
activefalseEnable the cover. When true, the first layout entry (page 0) becomes the cover page.
front_back_width_mm0.0Total width of front + back panel combined, without the spine. Required when active: true.
height_mm0.0Cover height in mm. Required when active: true.
modefreeCover layout mode. Controls how page 0 is solved. free = GA solver optimises freely (default). See Cover modes below.
spine_clearance_mm5.0Gap in mm between the photo edge and the spine for front, back, and split modes. Ignored for spread modes.
spine_textbook titleText on the spine. Set to ~ (null) for no text. Font size is auto-calculated from the spine width (max 80% of spine width).
spine_modeautoSpine width mode — see below.
spine_mm_per_10_pages1.4Auto mode only. Spine thickness per 10 inner pages. Spine width = (inner_pages / 10) * spine_mm_per_10_pages. In auto mode the spine width affects the total cover canvas width that the solver uses.
spine_width_mm—Fixed mode only. A fixed spine width in mm. In fixed mode the spine does not affect the cover canvas width in the solver — it is only used by the template for display and text sizing.
bleed_mm3.0Same behavior as for inner pages
margin_mm0.0Same behavior as for inner pages
gap_mm5.0Same behavior as for inner pages
bleed_threshold_mm3.0Same behavior as for inner pages

Cover modes

When mode is not free, the GA solver is bypassed and slot positions are calculated deterministically from the cover geometry. A warning is printed if the number of photos on the cover does not match what the mode expects.

See Cover Modes — Visual Guide for visual examples of each mode.

ModePhotosBehaviour
freeanyGA solver optimises freely (default)
front1Photo on the front panel, aspect ratio preserved and centred
front-full1Photo fills the entire front panel (may crop)
back1Photo on the back panel, aspect ratio preserved and centred
back-full1Photo fills the entire back panel (may crop)
spread1Photo spans the full spread (over spine), aspect ratio preserved and centred
spread-full1Photo fills the full spread (may crop)
split2Slot 0 → front, slot 1 → back, aspect ratio preserved and centred
split-full2Slot 0 → front, slot 1 → back, each half fully filled (may crop)

Workflow example — single photo on the front (CLI):

fotobuch place cover.jpg --into 0
# cover: { mode: front }
fotobuch rebuild --page 0

Workflow example — panorama across full spread (CLI):

fotobuch place panorama.jpg --into 0
# cover: { mode: spread-full }
fotobuch rebuild --page 0

Spine modes explained:

  • auto (default): Spine width is calculated from the number of inner pages. The formula is spine_width = (inner_pages / 10) * spine_mm_per_10_pages. The spine width is added to front_back_width_mm to form the total cover canvas — meaning the solver accounts for the spine.

  • fixed: You provide spine_width_mm directly. The spine is not added to the canvas width — the solver uses front_back_width_mm as-is for the cover width. The fixed spine width is only used by the template for positioning the spine text.


config.book_layout_solver — Photo-to-page distribution

The book layout solver distributes your photos across pages. It first runs an exact dynamic program (DP) to find a globally optimal assignment, then refines it with a local search that evaluates actual layout quality per page. You can tweak the following parameters to control the solvers behavior in detail, but you usually only need to adjust page_target, page_max, and search_timeout.

FieldDefaultDescription
page_target12Target number of pages. The solver tries to hit this count. This is the most important solver setting.
page_min1Hard minimum number of pages.
page_max26Hard maximum number of pages. Setting this above page_target gives the solver room to add pages when that improves layout quality significantly.
photos_per_page_min1Minimum number of photos on any single page.
photos_per_page_max20Maximum number of photos on any single page. Very important: assure that your page_max multiplied by this value is greater or equal to the total number of photos.
group_max_per_page5Maximum number of different groups that may share a single page. Lower values keep groups more separated.
group_min_photos1When a group is split across two pages, each part must have at least this many photos. Prevents a single “orphan” photo appearing alone on the next page.
weight_even1.0Objective weight for even photo distribution across pages. Higher = more uniform page fill.
weight_split10.0Objective weight penalising group splits. Higher = groups are less likely to be split across pages.
weight_pages5.0Objective weight penalising deviation from page_target. Higher = result stays closer to the target.
search_timeout30sTime budget for the local search. The DP assignment phase is exact and runs in milliseconds; this budget bounds only the refinement. YAML format: {secs: 60, nanos: 0}.
enable_local_searchfalseWhether to run the local search after the DP. The local search shifts page boundaries to improve per-page layout quality, but ignores group cohesion. Enable to trade group cohesion for tighter per-page layouts.
max_coverage_cost0.95(Currently unused — will be removed in a future version.) Was intended as a threshold for the local search to identify “bad” pages, but is not read by the solver.

Deprecated (optional, ignored): mip_rel_gap, max_photos_for_split and split_group_boundary_slack belonged to the former MIP solver, which has been replaced by an exact dynamic program. They are still accepted in existing config files for backwards compatibility but have no effect, and you can safely remove them.


config.page_layout_solver — Single-page layout (genetic algorithm)

The page layout solver arranges photos within a single page using a genetic algorithm with island-model parallelism. These are advanced tuning parameters — the defaults work well for most cases.

FieldDefaultDescription
seed42Random seed for the genetic algorithm. Due to the parallelism has not a great influence on the layout as it is however not deterministic, except you are running on one core only.
population_size750Number of individuals (candidate layouts) per island. Larger = better results but slower.
max_generations100Maximum number of generations the algorithm runs.
mutation_rate0.3Probability that an individual is mutated per generation.
crossover_rate0.7Probability that two individuals are recombined per generation.
elite_count20Number of best individuals carried over unchanged to the next generation.
no_improvement_limit15Stop early if no improvement is found for this many generations. Set to ~ (null) to disable early stopping.
enforce_ordertrueEnforce chronological reading order (top-left to bottom-right) on each page. When true, photos are arranged so earlier photos appear before later ones in a somehow natural reading direction. Set to false if you don’t care about photo order and want the solver to optimise purely for visual quality — this often produces tighter layouts.
islands_nrCPU coresNumber of independent populations evolved in parallel. Defaults to the number of available CPU cores.
islands_migration_interval5Generations between migration events (best individuals are copied between islands).
islands_nr_migrants2Number of individuals migrated per island per migration event.

config.page_layout_solver.weights — Fitness function

The fitness function evaluates how good a single-page layout is. It combines three cost components, each multiplied by its weight. Lower cost = better layout.

FieldDefaultDescription
w_coverage1.0Weight for canvas coverage cost. Penalises unused white space on the page. This is the dominant term.
w_size0.2Weight for size distribution cost. Penalises photos that deviate from their target size (determined by their area_weight).
w_barycenter0.0Weight for barycenter centering cost. Penalises layouts whose visual centre of mass is far from the page centre. Disabled by default (0.0).

config.preview — Preview rendering

All preview overlay settings are automatically suppressed in the release build.

FieldDefaultDescription
show_filenamesfalseShow the photo filename as a caption on each photo. Useful for identifying photos when adjusting the layout.
max_preview_px800Maximum pixel size (longest edge) of cached preview images. Lower = faster builds, less disk space, blurrier preview.
show_borderstrueShow red bleed border and blue margin border overlays on each page.
show_slot_infotrueShow slot address and area weight on each photo (e.g. 3:2 (1.5)).
show_preview_watermarktrueShow a watermark on the preview images. Useful to easily distinguish preview images from final output.
write_pdftrueWhether build/rebuild (re)write the preview PDF. Set to false to skip PDF generation (e.g. in the GUI, which renders pages directly) for faster builds.

config.book.appendix — Photo indexu

The appendix is a compact photo index appended at the end of both the preview and release PDFs, listing every photo with its group, timestamp, and a page-position reference.

FieldDefaultDescription
activefalseEnable the photo index.
columns7Number of columns in the listing.
ref_mode"positions"Reference style: "positions" (page.slot, e.g. 2.3) or "counter" (sequential number badge on each photo).
page_separatorfalseShow a page-number header between pages in the listing.
strip_timestampstrueTries to strip leading ISO timestamps from filenames in the listing.
label_title"Photo Index"Localization: Title text of the appendix.
label_page"Page"Localization: “Page” label used in the cross-reference legend and page separators.
date_format"{day}. {month} {year} {hour}:{min}"Localization: Format string for timestamps. Placeholders: {day}, {month}, {year}, {hour}, {min}.
date_months["Jan", …, "Dec"]Localization: Month abbreviations (12 entries, January–December).

Example: a typical YAML

config:
  book:
    title: "Italy 2024"
    page_width_mm: 420.0    # double-page spread
    page_height_mm: 297.0
    bleed_mm: 3.0
    margin_mm: 0.0
    gap_mm: 5.0
    cover:
      active: true
      front_back_width_mm: 594.0
      height_mm: 297.0
      spine_mode: auto
      spine_mm_per_10_pages: 1.4
      spine_text: "Italy 2024"
    appendix:
      active: true
      label_title: "Photo Index"
      label_page: "Page"
  book_layout_solver:
    page_target: 20
    page_max: 24
    photos_per_page_max: 8
    search_timeout:
      secs: 60
      nanos: 0
  preview:
    show_filenames: true
    show_borders: true
    show_slot_info: true
    max_preview_px: 800

Solver Tuning

fotobuch has two solvers: the book layout solver (exact DP + local search) distributes photos across pages; the page layout solver (genetic algorithm) arranges photos within each page. Most problems can be fixed by changing a few values in the YAML.

Curious how these solvers work under the hood? See How the Layout Engine Works.

Quick checklist

  1. Set page_target to the desired page count.
  2. Groups splitting? → raise weight_split.
  3. Build too slow? → raise search_timeout, or set enable_local_search: false.

Wrong page count

SymptomFix
Too many pagesLower book_layout_solver.page_target
Too few pagesRaise book_layout_solver.page_target
Solver ignores the targetRaise book_layout_solver.weight_pages (default 5.0)
Groups split across pagesRaise book_layout_solver.weight_split (default 10.0) and/or set book_layout_solver.enable_local_search: false

Always keep page_max a few pages above page_target so the solver can use an extra page when that improves the layout significantly.


Solver too slow

The page assignment phase uses an exact dynamic program that solves even thousand-photo books in milliseconds, so it is no longer a bottleneck. The search_timeout (default 30 s) now bounds only the local search phase:

book_layout_solver:
  search_timeout:
    secs: 60
    nanos: 0
  enable_local_search: false   # skip local search for a faster (coarser) result

Poor single-page layouts

SymptomFix
Much white spaceRaise page_layout_solver.weights.w_coverage (default 1.0)
Photo sizes don’t match weightsRaise page_layout_solver.weights.w_size (default 0.2)
Chronological order wrongSet page_layout_solver.enforce_order: true (default)

Cover Modes

Each cover mode determines how photos are positioned and sized on the cover. The examples below show the result of each mode with a sample photo.


Mode: front

A single photo on the front panel, with its aspect ratio preserved and centred.


Mode: front-full

A single photo fills the entire front panel (may crop to fit).


Mode: back

A single photo on the back panel, with its aspect ratio preserved and centred.


Mode: back-full

A single photo fills the entire back panel (may crop to fit).


Mode: spread

A single photo spans the full spread (front, spine, and back), with its aspect ratio preserved and centred.


Mode: spread-full

A single photo fills the full spread without cropping space for the spine (may crop the photo).


Mode: split

Two photos: slot 0 goes on the front panel, slot 1 on the back panel. Both have their aspect ratios preserved and are centred.


Mode: split-full

Two photos: slot 0 fills the front panel, slot 1 fills the back panel (each may crop independently).


Mode: free

The genetic algorithm solver optimises photo placement freely without constraints. Use any number of photos.

Customizing the Template

This is a quick introduction to customizing the Typst template for advanced users. You can skip this section if you are happy with the default layout.

Every project has a {name}.typ file — a Typst template that controls how the PDF looks. fotobuch generates this file for you when you create a new project (via the GUI New project dialog or the CLI fotobuch project new command), but you are free to edit it. fotobuch will not overwrite your template once created, so your changes stay safe and can be reused across projects.

Make sure that during your edits you do not change the lines

#let is_final = false
#let project_name = "{project_name}"
#let data = yaml(project_name + ".yaml")
#set text(font: "Libertinus Serif")

otherwise it won’t compile.

Important: Always edit {name}.typ, never final.typ. The final template is auto-generated from yours during a release build (with is_final = true). Your changes in final.typ would be overwritten.


Preview overlays

These settings only affect the preview PDF. They are automatically disabled in the release build. Configure them in {name}.yaml:

config:
  preview:
    show_filenames: false    # show filename caption on each photo
    show_borders: true       # red bleed border + blue margin border
    show_slot_info: true     # slot address and weight on each photo

Turn on show_filenames when you’re trying to identify which photo is where. Turn off show_slot_info once you’re happy with the layout and just want a clean preview.


Photo index (appendix)

The template can append a photo index at the end of the book — a compact reference listing every photo with its group, timestamp, and a reference back to its page position. Configure it under config.book.appendix in {name}.yaml:

config:
  book:
    appendix:
      active: true
      columns: 7
      ref_mode: "positions"   # or "counter"
      label_title: "Photo Index"
      label_page: "Page"
SettingDefaultEffect
activefalseEnable the appendix
columns7Number of columns in the listing
ref_mode"positions"How photos are referenced (see below)
page_separatorfalseShow a page-number header between pages
strip_timestampstrueTry to strip leading timestamps from filenames
label_title"Photo Index"Localization: Title text
label_page"Page"Localization: “Page” label
date_format"{day}. {month} {year} {hour}:{min}"Timestamp format
date_months["Jan", …, "Dec"]Month abbreviations

Reference modes

"positions" (default) — Each photo is referenced as page.slot, e.g. 2.3 means page 2, slot 3. No visual badge is added to the photos.

"counter" — Photos are numbered sequentially (1, 2, 3, …) and a small badge with the number appears in the bottom-right corner of each photo in the PDF.

Localization example

To produce an appendix in a different language, override the label fields and month abbreviations:

config:
  book:
    appendix:
      active: true
      columns: 6
      ref_mode: "counter"
      label_title: "Photo Index"
      label_page: "Page"
      date_format: "{day} {month} {year}"
      date_months: ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]

Going further

The template reads layout data from {name}.yaml via #let data = yaml(…).

A few things to keep in mind:

  • is_final controls preview vs. release mode. Use it to conditionally show or hide elements: #if not is_final [Draft watermark].
  • Image paths are resolved relative to the project root via cache_prefix. Preview images live in .fotobuch/cache/{name}/preview/, final images in .fotobuch/cache/{name}/final/.

If you want to start over with the default template, create a fresh project and copy the generated {name}.typ back.

Printing & Export

General checklist

Before uploading your PDF, verify these things:

  • Trigger a release build — only the release PDF has full 300 DPI. Do NOT upload the preview PDF to a print service; it will look blurry.
    • GUI: click Release in the toolbar (or press Ctrl+Shift+B)
    • CLI: fotobuch build release
  • Check the output for DPI warnings (photos that are too small for their slot will be listed)
  • Open the final PDF ({name}_final.pdf) and spot-check a few pages

Saal Digital

fotobuch generates PDFs that meet Saal Digital’s technical requirements out of the box:

  • Bleed: 3 mm on all sides (configurable via config.book.bleed_mm in the YAML)
  • PDF boxes: MediaBox, TrimBox, and BleedBox are set correctly — matching what InDesign would produce
  • Resolution: 300 DPI for the release build (configurable via config.book.dpi)

I tested the output with Saal Digital’s online preview tool and it passed without any issues. You can upload the PDF directly to their website for printing. The only issue I had sometimes was that I did not know their spine widths exactly so I had to tweak the config.book.spine_mm setting a few times to get the preview to look right. If you know your spine width in advance, set it in the config before building and the preview should be correct on the first try. I printed 7 photo books using fotobuch and I am quite happy with the results.

Other print services

Most print-on-demand services accept standard PDF with bleed. Adjust config.book.bleed_mm if your provider requires a different bleed size. The default 3 mm works for the majority of European providers.

How the Layout Engine Works (Advanced)

fotobuch lays out a book in two stages, each solved by its own algorithm:

  1. The book layout solver decides how many photos go on which page and which photos belong together — an exact dynamic program (DP) refined by a local search.
  2. The page layout solver decides how the photos on a single page are arranged — a genetic algorithm operating on slicing trees.

This page explains the ideas behind both, including a novel contribution that does not appear in the published literature. It is background reading — you never need to understand any of this to use fotobuch. For practical knobs, see Solver Tuning.

Stage 1 — Book layout solver (page assignment)

Given a chronologically ordered, grouped sequence of photos, the book layout solver partitions it into pages. A page is a contiguous slice of the sequence, so the whole problem reduces to choosing cut points in the sequence.

It runs in two phases:

  • DP phase. Choosing cut points is a sequence-partitioning problem, solved exactly by a dynamic program over states “first i photos on m pages”. The objective balances the target page count, keeping photo groups coherent, and respecting per-page photo limits. Hard constraints (page count, photos per page, groups per page, minimum group share on a split) are enforced exactly. The DP is optimal, deterministic and runs in milliseconds even for thousand-photo books. The derivation is documented in docs/design/book_layout_solver_dp/dp.typ.
  • Local search phase. The DP optimizes a proxy objective; the local search then moves cut points based on the actual rendered layout quality, targeting pages with too much white space first (worst-first). A layout cache prevents redundant page-solver calls.

Stage 2 — Page layout solver (slicing-tree GA)

Arranging the photos within a page is the hard, visually visible part. fotobuch builds on the slicing-tree genetic algorithm described in:

O. Fan, “Photo Layout with a Fast Evaluation Method and Genetic Algorithm”, IEEE ICMEW 2012. IEEE Xplore.

A big thank-you also to @masse for collage-solver, whose work was an inspiring starting point.

Slicing trees

A page layout is encoded as a full binary tree:

  • Leaves are photos.
  • Internal nodes are cuts: V (vertical cut, children side by side) or H (horizontal cut, children stacked).

For N photos the tree has N leaves and N−1 internal nodes. This structure guarantees — without any cost term — that slots align along cut lines, gaps are uniform, and nothing overlaps. The genetic algorithm only has to evolve the tree topology and the cut directions.

The genetic algorithm

  • Population & islands. Several independent populations evolve in parallel on separate threads (island model), periodically migrating their best individuals. This needs no locking during evolution and converges better than a single large population.
  • Mutation flips a single cut (V ↔ H), which can change a page’s appearance dramatically.
  • Crossover swaps two compatible subtrees between parents, creating genuinely new topologies.
  • Cost function balances coverage (minimize white space), how closely each photo’s area matches its weight, and optional centering — while aspect ratios are always kept intact.

Two contributions beyond the paper

fotobuch extends the published algorithm in two ways that materially improve both speed and result quality.

1. Exact gap computation in O(N)

The original algorithm either approximates the inter-photo gap (β) or recomputes it in O(N³) per fitness evaluation. fotobuch derives an exact closed-form solution instead.

With a gap, the relationship between a node’s width and height becomes affine: w = α·h + γ. Each node carries a coefficient pair (α, γ) that is propagated bottom-up through the tree:

  • Leaf with aspect ratio a: α = a, γ = 0
  • V-node (children share height, widths add): α = αₗ + αᵣ, γ = γₗ + γᵣ + β
  • H-node (children share width, heights add): α = αₗ·αᵣ / (αₗ + αᵣ), γ = (γₗ/αₗ + γᵣ/αᵣ − β)·αₗ·αᵣ / (αₗ + αᵣ)

A single top-down pass then assigns exact dimensions and positions. Because α > 0 is provably invariant for every node, the computation is always well-defined. This reduces the per-evaluation cost from O(N³) to O(N) while guaranteeing pixel-accurate placement with the precise gap that fills the page with no overlap or leftover space. This formulation does not appear in the literature.

2. Reading-order preservation via DFS indexing

A photo book tells a story, so the visual order on a page should match the chronological order of the photos. Instead of paying a fitness penalty to nudge the algorithm toward this, fotobuch makes it structural: photos are assigned to leaves in depth-first pre-order. A V cut visits left before right, an H cut visits top before bottom — so the oldest photo always lands top-left and the sequence flows naturally to the bottom-right.

This makes correct reading order impossible to violate and removes a whole cost term. It also enables a cheaper mutation: a cut flip changes the spatial layout but never the depth-first leaf order, so no re-assignment is needed. You can turn this off with enforce_order: false (see Solver Tuning).

Why it matters

Together these two ideas mean fotobuch evaluates far more candidate layouts per second and keeps your story in order by construction — which is why its many-photos-per-page results tend to look better than those of off-the-shelf tools.

Known Limitations

  • photo weights are not respected cross-page-wise — will maybe be addressed in a future version
  • text labels below photos/at the beginning of a new photo group are not supported — will be added in a future version

What fotobuch deliberately does not do

  • No mixed page sizes within one project.
  • No image editing (cropping, colour correction, rotation). Prepare your photos beforehand.

GUI Quickstart

This walkthrough takes you from zero to a print-ready PDF using the GUI.


Step 1 — Create a project

When you open fotobuch for the first time, a welcome screen appears. Click New project, give it a name (e.g. Italy-2024), choose a folder to save it in, and set the page dimensions in millimetres (e.g. 297 × 210 mm = A4 landscape).

fotobuch creates the project and opens it automatically.


Step 2 — Add photos

Click Add in the toolbar (or press Ctrl+O). Select a folder — all photos in that folder are imported into the Photo Pool on the left. You can add more folders at any time; each folder becomes a separate group.

The Photo Pool shows all your imported photos. Unplaced photos are available for placement; the count is shown at the top.


Step 3 — Fine-tune the configuration

Click Config in the toolbar to open the project configuration. Key settings:

  • page_target — how many pages you want
  • search_timeout — how long the solver runs (increase for large books)
  • gap_mm — space between photos on a page
  • book.margin_mm — inner margin around the page content (default 0.0)
  • book.bleed_mm — bleed zone added at the page edges, trimmed in the final PDF (default 3.0); set to 0 if your print service does not require bleed

Most settings preview instantly. The page and per-page targets only change how the layout is built, so rebuild the affected pages (R) to apply them to existing pages.


Step 4 — Create pages and place photos

Click Rebuild in the toolbar (or press R) with no pages selected, then confirm Rebuild all in the dialog. fotobuch creates all necessary pages and distributes your photos across them automatically.

Alternatively, drag photos from the Photo Pool onto the dashed drop zones between pages in the Central Panel to create pages and place photos manually one by one.


Step 5 — Browse and adjust

Use the Nav Bar on the right to scroll through all pages and jump to any page with a click.

If a page doesn’t look right, hover over it in the Central Panel and click the ↻ button in the HUD to rebuild just that page. fotobuch re-runs the layout solver for that page and shows the updated result.

Moving and swapping photos between pages:

  • Right-drag a slot in the Central Panel to start a drag.
  • The toolbar mode switch (or press M) controls the drag behaviour:
    • Swap — the dragged slot and the drop target exchange positions.
    • Move — the dragged slot is inserted at the drop target; other slots shift.

Adjusting photo weight:

  • Select a slot and press W to open the weight slider.
  • Or right-click a slot and choose Set weight…
  • Higher weight = more space for that photo on the page.

Undoing changes:

  • Press Ctrl+Z to undo, Ctrl+Y to redo.
  • Click History in the toolbar to browse and jump to any earlier state.

Step 6 — Export for print

When you’re happy with the layout, click Release in the toolbar (or press Ctrl+Shift+B).

fotobuch re-renders all images at full 300 DPI and writes {project-name}_final.pdf in your project folder. The file opens automatically when the build is done; you can also find it in the project folder as {project-name}_final.pdf.

Do not upload the regular preview PDF ({project-name}.pdf) to a print service — it renders at screen resolution only.

See Printing & Export for print-service requirements such as bleed and colour profiles.

GUI Overview

fotobuch is a photo book layout tool. You bring in your photos, the program arranges them across pages automatically, and you export a print-ready PDF — all without any manual design work. The GUI is the main way to work with fotobuch interactively.

Window layout

The window has five areas:

AreaPositionPurpose
ToolbarTopQuick access to all main actions
Photo PoolLeftYour imported photos
Central PanelCentreThe page canvas — what your book will look like
Nav BarRightMiniature page overview for jumping around
Status barBottomCurrent state and progress messages

How to create a photo book

See quickstart.

Multiple projects

You can keep several photo books — for example one per year or one per event. Use the project dropdown at the top-left of the toolbar to switch between projects or create new ones. All projects in the same folder are kept together, so opening that folder next time shows all of them.

CLI and GUI interoperability

The GUI and the fotobuch command-line tool operate on exactly the same project data. You can open a project in the GUI that was created and populated entirely from the CLI, or use the CLI and GUI on the same project in sequence — for example, run CLI commands for scripting or batch operations, then open the GUI for visual review and fine-tuning. The only rule is not to run both tools on the same project at the exact same time; alternating between them is completely fine.

The shared format is a YAML file and a Typst template tracked by git inside the vault, so every tool that understands git can inspect the full change history.

Central Panel

The central panel is the main editing canvas. It shows all pages in a vertical scroll column.

Canvas and zoom

  • Ctrl+scroll — zoom in/out around the cursor.
  • Ctrl+0 — fit page width to the available canvas.
  • Zoom is stored per-session and does not affect the output.

Page image

Each page displays a preview rendered by the Typst compiler. While a re-render is pending the page shows a grey overlay with a spinner icon.

Page HUD

A small heads-up display floats below each page. It appears faintly at rest and expands on hover.

ElementAction
Page numberDisplay only
Mode pill (✦ AUTO / ✋ MANUAL)Click to toggle Auto ↔ Manual
↻ buttonRebuild this page
✕ buttonDelete this page

Drop zones

A drop zone appears between pages (and at the top/bottom of the list) when you are dragging photos from the Pool. Dropping here creates a new page and places the dragged photos onto it.

Slot overlays

When the cursor is over a page the individual photo slots are outlined. Hovering a slot highlights it; clicking selects it (add more with Ctrl+click; extend a range with Shift+click).

Selected slots can be:

  • Deleted — Delete key unplaces photos from the selected slots.
  • Weight-adjusted — W opens the weight slider for fine-grained area control.

Manual mode

In Manual mode each slot shows a yellow ✦ handle in the SE corner. Right-mouse-drag the handle to resize, or right-mouse-drag the slot body to move. A live preview outline follows the pointer while dragging.

Photo Pool

The Photo Pool is the panel on the left side of the window. It shows all the photos you have imported into the current project. Think of it as your working collection — photos wait here until you put them on a page, and you can add more at any time.

Working with photos

ActionResult
Click a photoSelect it (deselects others)
Ctrl+clickAdd or remove a photo from the selection
Shift+clickSelect a range of photos at once
Ctrl+A (cursor in Pool)Select all photos
Drag one or more photosCarry them over to a page or a new-page drop area
Delete (with photos selected)Remove selected photos from the project

Dragging photos onto pages

To place a photo on a specific page, click it to select it (or select several), then drag it over to that page in the Central Panel. When you release the mouse, fotobuch places the photo in the next available slot.

You can also drag photos to the dashed area between pages. Dropping there creates a brand new page and puts your photos on it.

If pages already exist, select the photos and press P — fotobuch will distribute them across the existing pages by timestamp. To create the initial layout, click Rebuild in the toolbar (with nothing selected) and confirm.

Photo states

  • Unplaced — the photo has not been put on any page yet; shown normally.
  • Placed — the photo is already on one or more pages; shown with a small badge.

Adding more photos

Click Add in the toolbar (or press Ctrl+O) to open a folder picker and import additional photos. Photos you have already imported are recognised automatically and skipped, even if you pick the same folder again.

Toolbar

The toolbar gives you quick access to everything you need: importing photos, laying them out, undoing mistakes, and exporting the finished book. It runs along the very top of the window and is always visible.

Buttons at a glance

ButtonShortcutWhat it does
Project dropdown—Switch between projects or create a new one → details
AddCtrl+OImport photos from a folder → details
PlacePPut selected photos onto a page → details
RebuildRRe-calculate the layout for selected pages → details
ReleaseCtrl+Shift+BBuild and save the final PDF → details
↩ Undo / ↪ RedoCtrl+Z / Ctrl+YStep backwards or forwards through your changes → details
⏱ History—Show a list of recent changes → details
⚙ ConfigCtrl+,Open project settings → details
Slot info—Show technical slot information on page previews
⇄ Swap / → MoveMChoose how dragging rearranges slots → details
?F1Open in-app help

Project Switcher

The project dropdown at the left of the toolbar shows the name of the currently open project. Click it to open a menu.

What you can do

  • Switch project — click any project name in the list to open it. Your current project is saved automatically first.
  • New project — click + New project … and enter a name. fotobuch creates a fresh empty project and opens it.
  • Switch folder — click ⇄ Switch vault … to pick a different folder on your computer. All projects in that folder become available in the dropdown.

Projects and folders

All your photo books live inside a single folder on your computer. You choose that folder once and fotobuch remembers it. Each project is a separate photo book — you can have as many as you like in the same folder.

Project naming rules

  • Must start with a letter.
  • May contain letters, digits, ., _, and -.
  • No .. sequence allowed.
  • Maximum 50 characters.
  • Must be unique within the folder.
  • The exact name fotobuch is reserved (names like fotobuch-2025 are allowed).

Add Photos

The Add button (keyboard shortcut Ctrl+O) opens a folder picker so you can import photos into the current project.

How it works

  1. Click Add or press Ctrl+O.
  2. A folder picker opens. Navigate to the folder that contains your photos and confirm.
  3. fotobuch scans the folder and adds all supported image files to the Photo Pool.

You can import from several different folders — just click Add again. Photos you have already imported are recognised automatically and not added twice, even if you pick the same folder again.

Supported formats

JPEG, PNG, TIFF, and other common image formats are supported.

Place Photos

The Place button (keyboard shortcut P) takes the photos you have selected in the Photo Pool and puts them onto pages. The button is disabled when no photos are selected — use Ctrl+A in the Photo Pool to select all.

How it works

  • With photos dragged to a page — drop photos from the Photo Pool directly onto a page in the canvas to place them there.
  • Without a specific target — press P and fotobuch distributes the selected photos across existing pages, choosing the best fit based on photo timestamps.

Tip

For the fastest workflow: import your photos, press R (Rebuild) to let fotobuch create all pages and fill them automatically. Then use Place or drag-and-drop to adjust specific pages.

Rebuild Layout

The Rebuild button tells fotobuch to recalculate the layout for the selected pages. Each page in the Central Panel also has a small ↻ button in its HUD — clicking it rebuilds that single page directly without selecting it first.

Targeted rebuild — pages selected

Select one or more pages in the Nav Bar or Central Panel, then press R (or click Rebuild in the toolbar). fotobuch recalculates the arrangement of photos already on those pages. Photo assignments between pages are not changed.

The button label shows how many pages will be rebuilt.

Full rebuild — nothing selected

Click the Rebuild button in the toolbar with no pages selected and confirm when prompted. fotobuch redistributes all photos across pages from scratch, then recalculates the arrangement for every page. Use this to create the initial layout after adding photos, or to start fresh.

Note: The R keyboard shortcut only triggers a rebuild when at least one page is selected. With no selection, use the toolbar button to open the confirmation dialog.

Note on results

Rebuild may produce the same layout as before if the current arrangement is already a good fit for the given photos and settings.

Release (Export PDF)

The Release button (keyboard shortcut Ctrl+Shift+B) builds the final, high-resolution PDF of your photo book and saves it to your project folder.

What “release” means

While you are working, fotobuch shows quick preview renders at screen resolution. The Release export renders all pages at full print quality — the file you would send to a print shop.

Where the PDF is saved

The PDF is saved inside your project folder as {project-name}_final.pdf. After the export finishes, the file opens automatically with your system’s default PDF viewer.

Undo, Redo and History

fotobuch keeps a full history of every change you make, so you can always go back.

Undo and Redo

ButtonShortcutAction
↩Ctrl+ZUndo the last change
↪Ctrl+Y or Ctrl+Shift+ZRedo (bring back an undone change)

You can undo and redo as many steps as you like.

History panel

Click ⏱ History in the toolbar to open the history panel on the right side. It lists every saved change in order, newest first, so you can see exactly what was done and when.

The history is stored as a series of snapshots. Each change you make — placing photos, rebuilding a page, changing settings — is a separate entry in the list.

Technical: git as the underlying mechanism

fotobuch stores each project as a git repository. Every command that modifies the project creates a git commit. This means:

  • The CLI and the GUI share the same history. A change made from the command line appears in the GUI history panel, and vice versa.
  • Undo uses git reset --hard to move HEAD back. Undone commits are no longer visible in git log but are saved in .fotobuch/redo-stack so redo can restore them. Making a new change after an undo clears the redo stack permanently.
  • The full history up to the current HEAD is visible in any git client via git log.

Configuration Panel

The ⚙ Config button (keyboard shortcut Ctrl+,) opens the project configuration panel on the right side of the window.

What you can configure

The configuration panel lets you adjust the settings for the current project:

  • Paper size — the physical dimensions of your book pages (e.g. A4, square).
  • Margins and bleed — how much white space to leave around photos and how much extra to add for the print edge.
  • Photos per page — the target number of photos on each page when the layout is calculated automatically.
  • Preview options — whether to show slot information on the page previews.

Applying changes

Changes you make in the configuration panel are applied right away — the page previews update on their own, with no separate save or apply step.

The one exception is the photos per page target: it only influences how the automatic layout distributes photos across pages, so existing pages keep their current arrangement until you rebuild them (press R or click Rebuild).

For a full list of all available configuration keys and their accepted values, see the Configuration reference.

Drag Mode (Swap and Move)

The ⇄ Swap and → Move buttons control what happens when you drag an image with the right mouse button in the Central Panel. Toggle between them with the M key.

Swap mode (⇄)

Dragging an image and dropping it on another exchanges the two photos.

When both images have the same aspect ratio, the exchange is immediate and both positions stay the same.

When the images have different aspect ratios:

  • Same page — the swap is blocked. Auto mode re-sorts photos by aspect ratio when the page is recalculated, so the exchange would be undone immediately. Use Move mode to relocate photos between slots on the same page.
  • Different pages — the swap goes through, but both affected pages may be recalculated to fit the new photo proportions, which can change the visual layout of those pages.

Move mode (→)

Dragging an image and dropping it elsewhere relocates it there. Only the photo itself and its aspect ratio travel with it — on an automatic page the exact size and position are recalculated to fit the destination, so the result won’t necessarily match the spot where you dropped it.

  • Drop on a slot on a different page to move the photo there.
  • Drop on the Nav Bar to move the photo to that page.
  • Moving within the same page is only possible in Manual mode. In Auto mode, use Swap instead (if ratios match), or switch to Manual mode first.

Which mode to use

  • Swap for quick reordering of same-ratio images — page layout stays intact.
  • Move to shift a photo to a completely different page or to reposition in Manual mode.

Nav Bar

The Nav Bar (right side) shows thumbnail previews of all pages. It is useful for an overview when zoomed in on the canvas.

Interaction

ActionResult
Click a thumbnailSelect that page for navigation
Ctrl+clickToggle page in the multi-selection
Shift+clickExtend page selection
Drag selected thumbnailsReorder pages (drop between other thumbnails)
Delete (with pages selected)Delete selected pages (cover page is protected)

Scroll sync

Clicking a thumbnail scrolls the central canvas to that page. The canvas scroll position does not feed back into the Nav Bar (the bar is always fully visible).

Keyboard Shortcuts

KeyAction
HomeScroll to first page
EndScroll to last page
Ctrl+GOpen Go-to-page dialog
Ctrl+0Fit page width to canvas
Ctrl+scrollZoom in/out

Editing

KeyAction
PPlace selected pool photos (auto-distribute if no page hovered)
RRebuild selected pages (requires at least one page selected; use toolbar button for full rebuild)
AToggle Auto/Manual mode for hovered or selected page
DeleteUnplace selected slots / remove selected pool photos / delete selected pages
WOpen weight slider for selected slots
MToggle drag mode (Swap ↔ Move)

History

KeyAction
Ctrl+ZUndo
Ctrl+YRedo
Ctrl+Shift+ZRedo (alternative)

Selection

KeyAction
Ctrl+ASelect all (pool photos or page slots, depending on focus)
Shift+clickExtend selection
Ctrl+clickToggle item in selection
EscapeClear all selections and cancel active drag

Application

KeyAction
Ctrl+OAdd photos (folder picker)
Ctrl+,Toggle configuration panel
Ctrl+Shift+BBuild release PDF
F1Open in-app help
F2Toggle frame-timing panel (debug)

CLI Quickstart

This walkthrough takes you from zero to a print-ready PDF using the command line. Prerequisites: fotobuch installed, a folder of photos on your machine.

Prefer learning by example? Check out the complete project in docs/examples/ — it has sample images, YAML config, template, and generated PDFs.


Step 1 — Create a project

fotobuch project new "Italy-2024" --width 297 --height 210

This creates a directory Italy-2024/ with a Git repo, a YAML config, and a Typst template. The --width and --height values are in millimetres (297 × 210 mm = A4 landscape).

Switch into the project folder:

cd "Italy-2024"

Project names must start with a letter and can only contain letters, digits, or dashes.


Step 2 — Review the configuration

Before adding photos, open Italy-2024.yaml in a text editor and check the most important settings. The file already has sensible defaults, but you should set the page count to match the size of book you want:

config:
  book:
    page_width_mm: 297.0
    page_height_mm: 210.0
    bleed_mm: 3.0        # required by most print services
    margin_mm: 0.0       # 0 = edge-to-edge; set to e.g. 10 for a white border
    gap_mm: 5.0          # the gap between the photos in your layout
  book_layout_solver:
    page_target: 20      # how many pages you want
    page_max: 24         # upper limit — give the solver some room above the target

Setting page_max a few pages above page_target gives the solver freedom to use an extra page when that produces a significantly better layout.

If you plan to use a cover:

    cover:
      active: true
      front_back_width_mm: 594.0   # total width of front + back panel
      height_mm: 297.0
      spine_text: "Italy 2024"
      spine_width_mm: 15.0         # spine width (does not add to front_back_width_mm when spine_mode = fixed)
      spine_mode: fixed

See Configuration for a full reference of all settings.


Step 3 — Add photos

Point fotobuch at one or more folders. Each folder becomes a group — photos from the same group are kept together on pages.

fotobuch add /photos/2024-07-Italy
fotobuch add /photos/2024-08-Hiking

Folders with a date in the name are sorted chronologically. You can also add single files, add recursively, or filter by filename or XMP metadata:

# recursive — each subfolder becomes a group
fotobuch add --recursive /photos/2024-summer

# only 3-to-5-star photos, giving them more space
fotobuch add /photos/2024-07-Italy --filter-xmp "Rating.*[3-5]" --weight 5

Check what was imported:

fotobuch status

Step 4 — Build a preview

fotobuch build

On the first run, fotobuch distributes all photos across pages automatically and renders a preview PDF at lower DPI (fast). Open Italy-2024.pdf to review the result.


Step 5 — Adjust the layout

Move a photo to another page:

fotobuch page move 3:2 to 5

Swap two photos:

fotobuch page swap 2:3 2:7

Swap two pages:

fotobuch page swap 3 7

Split a page:

fotobuch page split 3:2

Combine pages:

fotobuch page combine 3..7

Give a photo more space (weight > 1 = relatively larger):

fotobuch page weight 3:2 2.0

Re-solve a single page:

fotobuch rebuild --page 6

Undo any change:

fotobuch undo

See CLI Concepts for slot address syntax. Rebuild the preview after changes: fotobuch build.


Step 6 — Adding more photos later

After the first build, newly added photos start as unplaced. Place them before building:

fotobuch add /photos/bonus-shots
fotobuch place
fotobuch build

You can also place onto a specific page:

fotobuch place --into 4
fotobuch place --filter "DSC_00.*\.jpg" --into 6

Step 7 — Export for print

When you’re happy with the layout:

fotobuch build release

This re-renders all images at 300 DPI and writes Italy-2024_final.pdf. The file is ready to upload to your print service.

See Printing for Saal Digital-specific details.

Command Overview

All commands follow the pattern fotobuch <command> [options]. Run fotobuch --help or fotobuch <command> --help for details, or see the Full Flag Reference.

Commands at a glance

CommandWhat it does
project newCreate a new photobook project
project listList all projects in the current repo
project switchSwitch to another project (checks out its Git branch)
initAlias for project new (shorthand for first-time setup)
addImport photos or folders into the project
removeDelete photos from the project entirely
placeAssign unplaced photos to pages
unplaceRemove photos from their page slots (they stay in the project)
buildSolve layout and render preview PDF
build releaseRender final PDF at full resolution (300 DPI)
rebuildRe-run the solver on specific pages
page moveMove photos between pages
page swapSwap pages or slots
page splitSplit a page at a slot
page combineMerge pages together
page infoShow photo metadata for slots on a page
page weightSet the area weight for one or more slots
page modeToggle a page between auto (solver) and manual placement
page posMove or scale slots on a manual-mode page
statusShow project overview (or single-page detail)
config showPrint the resolved configuration with all defaults
config setSet a config value using dot-notation (e.g. book.dpi 150)
historyShow the project change log
undoUndo the last N changes
redoRedo N undone changes

For all flags and exact syntax see the Full Flag Reference. For available config keys see Configuration.

remove vs. unplace

  • remove deletes photos from the project. They are gone (unless you undo).
  • unplace takes photos off their page but keeps them in the project. They become unplaced and can be re-placed with fotobuch place.

Use remove --keep-files if you want remove-like pattern matching but unplace-like behaviour (photos stay, just lose their page assignment).

Use remove --unplaced to delete all photos that are not yet assigned to any page in one step (no pattern argument needed):

fotobuch remove --unplaced

build vs. rebuild

  • build renders the PDF and only re-solves pages that changed since the last build. On the first run it solves everything.

  • rebuild --page N forces the solver to re-optimize page N from scratch, even if nothing changed. Useful when you’re not happy with a layout.

  • rebuild --all re-solves every page.

  • rebuild --range-start A --range-end B re-solves pages A through B (0-based, inclusive). Add --flex N to let the solver vary the page count in that range by ±N:

    fotobuch rebuild --range-start 3 --range-end 7 --flex 2
    

place --into-new-page-at

place --into places photos onto an existing page. --into-new-page-at <POS> creates a new page at position POS and places the selected photos there:

fotobuch place --filter "panorama.*" --into-new-page-at 4

CLI Concepts

Slot addresses

A slot address identifies one or more placed photos on a specific page:

AddressMeaning
3All slots on page 3
3:2Slot 2 on page 3
3:2..5Slots 2 through 5 on page 3
3:2..5,7Slots 2–5 and slot 7 on page 3
4+New page inserted after page 4 (move destination only)

Use fotobuch status <page> to see which slot numbers are on a given page.

Two ways to address photos

MethodUse caseExamples
Filename / patternPhotos not yet placed, or matching by source pathadd, remove, place --filter
Slot addressPhotos already placed on a pagepage move, page swap, page weight

Rule of thumb: use filename patterns for unplaced photos (add, place, remove); use slot addresses for placed photos (page move, page swap, page weight, unplace).

Build pipeline

fotobuch add     →  photos enter the project (unplaced)
fotobuch place   →  unplaced photos get assigned to pages
fotobuch build   →  solver optimises layout, renders preview PDF
fotobuch build release  →  renders final PDF at print resolution

The first build implicitly places all photos, so you can skip place on a fresh project.

Every command that changes the layout creates a Git commit automatically. Use fotobuch undo / fotobuch redo to navigate history, and fotobuch history to see the log.

XMP metadata filtering

fotobuch add supports --filter-xmp <REGEX> to import only photos whose XMP metadata matches a pattern. The regex is applied to the raw XMP XML string embedded in the image file — not to parsed fields.

Because XMP is XML, a tag like xmp:Rating appears in the raw text as e.g. <xmp:Rating>4</xmp:Rating>. A regex therefore needs to match against that XML text. Use exiftool -xmp -b <file.jpg> to inspect the exact XMP of a specific photo and build your pattern from it.

Common use cases:

# Only photos with a star rating element present (any value)
fotobuch add /photos --filter-xmp "xmp:Rating"

# Only photos rated 3, 4, or 5 stars
fotobuch add /photos --filter-xmp "xmp:Rating>[3-5]<"

# Multiple filters (all must match — AND logic)
fotobuch add /photos --filter-xmp "xmp:Rating>[4-5]" --filter-xmp "dc:subject"

Photos without any XMP data are excluded when --filter-xmp is used. Use --filter instead if you want to match by filename or source path.

Use --dry (or -d) to preview which photos would be imported without making any changes — essential for iterating on XMP regexes before committing:

fotobuch add /photos --filter-xmp "xmp:Rating>[4-5]" --dry

Manual Mode

In Auto mode (default) the solver controls every slot on a page. Manual mode freezes the slot layout so the solver never touches it — useful for a specific page that needs a custom arrangement.


Switching a page to manual mode

fotobuch page mode 3 manual     # page 3 → Manual
fotobuch page mode 3 auto       # page 3 → Auto (solver takes over again)
fotobuch page mode 3,5,7 manual # several pages at once
fotobuch page mode 2..6 manual  # page range

Shorthand: m = manual, a = auto.


Repositioning slots (page pos)

The page must already be in Manual mode. All values are in millimetres relative to the page content area (excluding bleed):

# Relative move: right +20 mm, down +10 mm
fotobuch page pos 3:2 --by 20,10

# Absolute position: top-left corner at (50, 30) mm
fotobuch page pos 3:1 --at 50,30

# Scale to 1.5× (grows right and downward from current position)
fotobuch page pos 3:0 --scale 1.5

# Combine absolute position and scale
fotobuch page pos 3:0 --at 10,10 --scale 2.0

--by and --at are mutually exclusive. --scale can be combined with either.


Notes

  • rebuild --page N on a Manual page is a no-op — the solver is bypassed. Switch to Auto first if you want a fresh solver run.
  • The page mode is stored in the YAML. Auto is the implicit default (the field is omitted for Auto pages).

Headless / CI Usage

The fotobuch binary has no GUI dependencies and runs in any headless environment — CI servers, Docker containers, SSH sessions.


Minimal build script

#!/bin/bash
set -e

fotobuch project new MyBook --width 297 --height 210 --quiet
cd MyBook
fotobuch add /mnt/photos/2024-Summer
fotobuch build
fotobuch build release
# MyBook_final.pdf is ready

Docker example

FROM debian:bookworm-slim
COPY fotobuch /usr/local/bin/fotobuch
WORKDIR /vault
ENTRYPOINT ["fotobuch"]

Mount the vault as a volume:

docker run --rm -v /data/vault:/vault fotobuch build release

Full Flag Reference

This page is auto-generated from the CLI source. Run cargo run --bin generate-cli-docs --features cli-docs to regenerate.

Command-Line Help for fotobuch

This document contains the help content for the fotobuch command-line program.

Command Overview:

fotobuch

Photobook layout solver and project manager

Usage: fotobuch <COMMAND>

Subcommands:
  • add — Add photos to the project
  • build — Calculate layout and generate preview or final PDF
  • rebuild — Force re-optimization of pages or page ranges
  • place — Place unplaced photos into the book
  • unplace — Remove photos from the layout at a page:slot address (they stay in the project)
  • page — Page manipulation commands (move, split, combine, swap)
  • remove — Remove photos or groups from the book
  • status — Show project status
  • config — Configuration commands (show or mutate)
  • history — Show project change history
  • undo — Undo the last N commits (default: 1)
  • redo — Redo N previously undone commits (default: 1)
  • project — Project management commands
  • init — Create a new photobook project (alias for project new)
  • completions — Print shell completion script to stdout

fotobuch add

Add photos to the project

Usage: fotobuch add [OPTIONS] [PATHS]...

Arguments:
  • <PATHS> — Directories or files containing photos to add
Options:
  • --allow-duplicates — Allow adding duplicate photos (by hash)

  • --filter-xmp <REGEX> — Only include photos whose XMP metadata matches this regex (can be repeated, all must match)

  • --filter <REGEX> — Only include photos whose source path matches this regex pattern (can be repeated, all must match)

  • -d, --dry — Preview what would be added without writing anything

  • --update — Re-add photos whose path already exists but whose content has changed

  • -r, --recursive — Scan directories recursively (each subdir becomes its own group)

  • --weight <WEIGHT> — Area weight for all imported photos (default: 1.0)

    Default value: 1

fotobuch build

Calculate layout and generate preview or final PDF

Usage: fotobuch build [OPTIONS] [COMMAND]

Subcommands:
  • release — Generate final high-quality PDF at 300 DPI
Options:
  • --pages <PAGES> — Only rebuild specific pages (0-based, comma-separated or repeated flag)

fotobuch build release

Generate final high-quality PDF at 300 DPI

Usage: fotobuch build release [OPTIONS]

Options:
  • --force — Force release even if layout has uncommitted changes

fotobuch rebuild

Force re-optimization of pages or page ranges

Usage: fotobuch rebuild [OPTIONS]

Options:
  • --page <PAGE> — Single page to rebuild (0-based index)

  • --range-start <RANGE_START> — Start of page range (0-based index, requires –range-end)

  • --range-end <RANGE_END> — End of page range (0-based index, inclusive, requires –range-start)

  • --flex <FLEX> — Allow page count to vary by +/- N (only with range)

    Default value: 0

  • --all — Rebuild all pages from scratch

fotobuch place

Place unplaced photos into the book

Usage: fotobuch place [OPTIONS]

Options:
  • --filter <REGEX> — Only place photos matching this regex pattern (can be repeated, all must match)
  • --into <INTO> — Place all matching photos onto this specific page (0-based index)
  • --into-new-page-at <POS> — Create a new page at this position and place photos there (0-based index)

fotobuch unplace

Remove photos from the layout at a page:slot address (they stay in the project)

The page is deleted automatically if it becomes empty.

Usage: fotobuch unplace <ADDRESS>

Arguments:
  • <ADDRESS> — Slot address: “3:2” (slot 2 on page 3), “3:2,7”, “3:2..5”, “3:2..5,7”

fotobuch page

Page manipulation commands (move, split, combine, swap)

Usage: fotobuch page <COMMAND>

Subcommands:
  • move — Move or unplace photos between pages
  • split — Split a page at a slot: photos from that slot onwards move to a new page inserted after
  • combine — Merge pages onto the first one, then delete the now-empty source pages
  • swap — Swap photos between two addresses (only single numbers or ranges, no comma lists)
  • info — Show photo metadata for slots on a page
  • weight — Set area_weight for one or more slots
  • mode — Toggle page mode between auto (solver) and manual (user-placed)
  • pos — Reposition or rescale slots on a Manual-mode page

fotobuch page move

Move or unplace photos between pages

Two forms: “SRC to DST” (move) and “SRC out” (unplace).

Addressing: 3 = whole page, 3:2 = slot 2 on page 3, 3:1..3,7 = slots 1-3 and 7, 4+ = new page after 4.

Move examples: “3:2 to 5”, “3,4 to 5”, “3:2 to 4+”. Unplace examples: “3 out”, “3:2 out”.

See the documentation for the full addressing syntax.

Usage: fotobuch page move [ARGS]...

Arguments:
  • <ARGS> — Expression passed as space-separated tokens, e.g.: 3:2 to 5

fotobuch page split

Split a page at a slot: photos from that slot onwards move to a new page inserted after

Shortcut for page move PAGE:SLOT.. to PAGE+. Error if SLOT is the first slot (would leave the original page empty).

Usage: fotobuch page split <ADDRESS>

Arguments:
  • <ADDRESS> — Address “PAGE:SLOT”, e.g. “3:4” splits page 3 at slot 4

fotobuch page combine

Merge pages onto the first one, then delete the now-empty source pages

All following page numbers shift down accordingly.

Usage: fotobuch page combine <PAGES>

Arguments:
  • <PAGES> — Pages expression: “3,5” (page 5 onto 3) or “3..5” (pages 4-5 onto 3)

fotobuch page swap

Swap photos between two addresses (only single numbers or ranges, no comma lists)

Page swap: “3 5” swaps pages, “1..2 5..9” swaps blocks. Slot swap: “3:2 5:6” swaps individual slots, “3:2..4 5:6..9” swaps slot ranges (different sizes ok).

Errors on overlapping ranges or comma-separated lists as operands.

Usage: fotobuch page swap <LEFT> <RIGHT>

Arguments:
  • <LEFT> — Left address: “3:2”, “3:1..3”, “3”, “3..6”
  • <RIGHT> — Right address: “5:6”, “5:2..4”, “5”, “8..11”

fotobuch page info

Show photo metadata for slots on a page

Address forms: 3 (all slots), 3:2 (single slot), 3:1..3,7 (slots 1–3 and 7).

Without flags: full table (or vertical view for a single slot). With a flag: machine-readable single-field output.

Usage: fotobuch page info [OPTIONS] <ADDRESS>

Arguments:
  • <ADDRESS> — Address: “3”, “3:2”, “3:1..3,7”
Options:
  • --weights — Output only area weights (format: page:slot=weight)
  • --ids — Output only photo IDs
  • --pixels — Output only pixel dimensions

fotobuch page weight

Set area_weight for one or more slots

Examples: 3:2 2.0 (single slot), 3:1..3,7 2.0 (multiple slots), 3 2.0 (whole page).

Usage: fotobuch page weight <ADDRESS> <WEIGHT>

Arguments:
  • <ADDRESS> — Address: “3”, “3:2”, “3:1..3,7”
  • <WEIGHT> — Weight value (must be > 0)

fotobuch page mode

Toggle page mode between auto (solver) and manual (user-placed)

Syntax: fotobuch page mode <pages> <a|m|auto|manual>

Examples: 3 m (page 3 to manual), 3..5 a (pages 3-5 to auto).

Usage: fotobuch page mode <PAGES> <MODE>

Arguments:
  • <PAGES> — Pages to change: “3”, “3..5”, “3,5”
  • <MODE> — Mode: ‘a’ or ‘auto’ for auto-solver, ‘m’ or ‘manual’ for manual placement

fotobuch page pos

Reposition or rescale slots on a Manual-mode page.

Syntax: fotobuch page pos <address> [--by dx,dy] [--at x,y] [--scale s]

Examples: 4:2 --by -20,30 — move slot 2 on page 4 relatively 4:2 --at 100,50 — set slot 2 origin to (100mm, 50mm) 4:2 --scale 1.5 — scale slot 2 by 1.5× 4:2..5 --by -20,30 — move slots 2–5 together 4:2 --at 100,50 --scale 2 — absolute position + scale

At least one of –by, –at, –scale is required. –by and –at are mutually exclusive. The page must be in manual mode.

Usage: fotobuch page pos <--by <BY>|--at <AT>|--scale <SCALE>> <ADDRESS>

Arguments:
  • <ADDRESS> — Address: “4:2”, “4:2..5”, “4:1,3”
Options:
  • --by <BY> — Relative move in mm: “dx,dy” (e.g. “-20,30”)
  • --at <AT> — Absolute position in mm: “x,y” (e.g. “100,50”)
  • --scale <SCALE> — Scale factor applied to width and height (origin stays fixed)

fotobuch remove

Remove photos or groups from the book

Usage: fotobuch remove [OPTIONS] [PATTERNS]...

Arguments:
  • <PATTERNS> — Photos, group names, or regex patterns to remove (can be repeated)
Options:
  • --keep-files — Only remove from layout, keep photos in the project (makes them unplaced)
  • --unplaced — Remove all photos that are not placed in any layout page

fotobuch status

Show project status

Usage: fotobuch status [PAGE]

Arguments:
  • <PAGE> — Show detailed information for a specific page (0-based index)

fotobuch config

Configuration commands (show or mutate)

Usage: fotobuch config <COMMAND>

Subcommands:
  • show — Show resolved configuration with defaults
  • set — Set a config value using dot-notation (e.g. book.dpi 300)

fotobuch config show

Show resolved configuration with defaults

Usage: fotobuch config show

fotobuch config set

Set a config value using dot-notation (e.g. book.dpi 300)

Supported keys mirror the YAML config hierarchy. Types are auto-detected: true/false → bool, integers → int, decimals → float, else string.

Usage: fotobuch config set <KEY> <VALUE>

Arguments:
  • <KEY> — Dot-notation key, e.g. “book.dpi” or “book.cover.active”
  • <VALUE> — New value, e.g. “300”, “true”, “3.5”, “spread”

fotobuch history

Show project change history

Usage: fotobuch history [OPTIONS]

Options:
  • -n <COUNT> — Number of entries to show (0 = all)

    Default value: 5

fotobuch undo

Undo the last N commits (default: 1)

Usage: fotobuch undo [STEPS]

Arguments:
  • <STEPS> — Number of steps to undo

    Default value: 1

fotobuch redo

Redo N previously undone commits (default: 1)

Usage: fotobuch redo [STEPS]

Arguments:
  • <STEPS> — Number of steps to redo

    Default value: 1

fotobuch project

Project management commands

Usage: fotobuch project <COMMAND>

Subcommands:
  • new — Create a new photobook project
  • list — List all photobook projects
  • switch — Switch to another photobook project

fotobuch project new

Create a new photobook project

Usage: fotobuch project new [OPTIONS] --width <WIDTH> --height <HEIGHT> <NAME>

Arguments:
  • <NAME> — Project name
Options:
  • --width <WIDTH> — Page width in millimeters

  • --height <HEIGHT> — Page height in millimeters

  • --bleed <BLEED> — Bleed margin in millimeters

    Default value: 3

  • --parent-dir <PARENT_DIR> — Parent directory where project will be created (default: current directory)

  • --quiet — Suppress welcome message

    Default value: false

  • --with-cover — Create project with an active cover page

    Default value: false

  • --cover-width <COVER_WIDTH> — Cover width in millimeters (defaults to page_width * 2 if –with-cover is set, with warning)

  • --cover-height <COVER_HEIGHT> — Cover height in millimeters (defaults to page_height if –with-cover is set, with warning)

  • --spine-grow-per-10-pages-mm <SPINE_GROW_PER_10_PAGES_MM> — Spine width growth per 10 inner pages in mm (auto mode, conflicts with –spine-mm)

  • --spine-mm <SPINE_MM> — Fixed spine width in mm (conflicts with –spine-grow-per-10-pages-mm)

  • --margin-mm <MARGIN_MM> — Inner margin in millimeters (default: 0)

    Default value: 0

fotobuch project list

List all photobook projects

Usage: fotobuch project list

fotobuch project switch

Switch to another photobook project

Usage: fotobuch project switch <NAME>

Arguments:
  • <NAME> — Project name to switch to

fotobuch init

Create a new photobook project (alias for project new)

Usage: fotobuch init [OPTIONS] --width <WIDTH> --height <HEIGHT> <NAME>

Arguments:
  • <NAME> — Project name
Options:
  • --width <WIDTH> — Page width in millimeters

  • --height <HEIGHT> — Page height in millimeters

  • --bleed <BLEED> — Bleed margin in millimeters

    Default value: 3

  • --parent-dir <PARENT_DIR> — Parent directory where project will be created (default: current directory)

  • --quiet — Suppress welcome message

    Default value: false

  • --with-cover — Create project with an active cover page

    Default value: false

  • --cover-width <COVER_WIDTH> — Cover width in millimeters

  • --cover-height <COVER_HEIGHT> — Cover height in millimeters

  • --spine-grow-per-10-pages-mm <SPINE_GROW_PER_10_PAGES_MM> — Spine width growth per 10 inner pages in mm

  • --spine-mm <SPINE_MM> — Fixed spine width in mm

  • --margin-mm <MARGIN_MM> — Inner margin in millimeters (default: 0)

    Default value: 0

fotobuch completions

Print shell completion script to stdout

Usage: fotobuch completions –shell bash >> ~/.bash_completion fotobuch completions –shell zsh >> ~/.zshrc fotobuch completions –shell fish > ~/.config/fish/completions/fotobuch.fish fotobuch completions –shell powershell >> $PROFILE

Usage: fotobuch completions --shell <SHELL>

Options:
  • --shell <SHELL> — Shell to generate completions for

    Possible values: bash, elvish, fish, powershell, zsh


This document was generated automatically by clap-markdown.

Glossary

Domain-specific terms used throughout fotobuch. This is the authoritative definition source — all code, documentation, and communication should use these terms consistently.


Data and Storage

TermDefinition
VaultA folder on disk that holds one or more projects as a single git repository.
ProjectA single photo book — one YAML file (photo list, layout, configuration) plus a Typst template — stored alongside other projects in a vault.
TemplateThe Typst file that defines the visual design of a project — page size, fonts, spacing, and how slots are rendered in the PDF.
PhotoA single image file imported into a project, belonging to exactly one photo group.
Photo GroupA named collection of photos, typically corresponding to a source folder.
Photo WeightA numeric value on a photo that controls how much space the layout gives it relative to other photos on the same page.
HistoryThe complete record of all changes to a project — stored as git commits, shared between GUI and CLI, and the basis for undo and redo.

Layout

TermDefinition
PageA single double-sided spread holding one or more slots, each spread carrying its own page mode.
CoverThe first page (index 0) of a project, which may use a different size or layout than the inner pages.
SlotA positioned, sized rectangle on a page that holds exactly one photo.
Aspect RatioThe width-to-height ratio of a photo or slot.
Page ModeA per-page flag: Auto lets fotobuch recalculate the slot arrangement freely; Manual locks slot positions so they are not changed during rebuild.
BleedExtra image area printed beyond the trim line, ensuring no white border appears after cutting.
MarginWhite space kept between the content area and the page edge (inside the trim line).
DPIDots per inch — the print resolution of a rendered page in the release build.

GUI Areas

TermDefinition
ToolbarThe horizontal bar at the top of the window containing buttons for all main actions.
Photo PoolThe panel on the left side of the window listing all imported photos available for placement.
Central PanelThe main canvas area in the centre of the window where pages are displayed and edited.
HUDThe overlay controls that appear below a page in the central panel when the pointer hovers over it — includes a rebuild button, mode toggle, and delete button.
New Page AreaA drop zone shown in the central panel between pages where photos can be dropped to create a new page at that position.
Nav BarThe panel on the right side of the window showing miniature thumbnails of all pages for quick navigation and selection.
Status BarThe bar at the bottom of the window showing the current operation state and progress messages.
ThumbnailA small preview image of a page or photo, shown in the nav bar and photo pool.
ZoomA scaling factor applied to the central panel view that changes how large pages appear on screen without affecting the actual layout.

Actions

TermDefinition
Photo AddImporting one or more image files from disk into the photo pool of the current project.
Photo PlaceAssigning selected photos to slots on existing pages by timestamp (use rebuild first to create the pages).
UnplaceRemoving a photo from its slot, returning it to the photo pool as unplaced.
Slot SwapExchanging the photos in two slots; blocked within one page when their aspect ratios differ.
Slot MoveRelocating a photo from one slot to a slot on a different page, or to a Manual-mode slot at an arbitrary position.
DraggingMoving a photo with the mouse — left-drag from the photo pool to place, right-drag a slot in the central panel to swap or move.
RebuildRecalculating the page layout — targeted (only the selected pages, keeping photo assignments) or full (redistributing all photos from scratch).
Undo / RedoNavigating backwards or forwards through the history of a project, reverting or re-applying changes one step at a time.
SelectionA set of pages or photos currently marked for the next action (rebuild, place, delete, etc.).
MultiselectionSelecting more than one page or photo at once, for example with Ctrl+Click or Shift+Click.

Output

TermDefinition
PreviewA fast, screen-resolution render of a page shown in the central panel and nav bar while working.
Release BuildA full print-quality PDF export of the complete photo book, saved as {project-name}_final.pdf in the project folder.