klarbild
Self-hosted · AGPL-3.0

Print pictures at the size you actually asked for.

Your printer driver has a checkbox called “fit to page”. Tick it and a 35 × 45 mm passport photo comes out 34 × 44. Untick it and you are guessing. Klarbild lays the sheet out in millimetres and hands you a PDF that measures what it says.

Lay out a sheet

Runs the application's own layout code, here in your browser. Nothing is uploaded.

0 mm
5 mm
Rotate pictures to save space
Fit
Sheets
Cell
Paper
Gap

The demo draws outlines instead of photographs — the real thing puts your pictures in these boxes at the resolution you choose. Measure the PDF: the page and every box are exactly the millimetres shown above.

The print studio

Everything a photo shop's software does, minus the photo shop.

86 image sizes

In ten groups — passport photo, small prints, photo formats, US inches, instant-camera, poster and frame sizes, squares, DIN, cards, plain aspect ratios. Or type your own: 25x35, 13x18, 10x7.5.

19 paper formats

A6 to A1 including A3+, photo paper from 9 × 13 to 20 × 30 cm, US Letter and Legal — or a custom sheet.

Crop marks

Corner marks at 0.25 pt outside the trim, or continuous lines when every cell is the same size. Bleed 0 to 10 mm.

Oversize, handled

A 20 × 30 print on A4 is not an error. Fit tells you it will not go; overflow centres it over the whole sheet and quantifies the loss first; tile spreads it over several sheets with a glue flap and a page number.

Crop or letterbox

When a picture does not match the target ratio you choose per picture — crop with a zoom-and-pan preview, or a border in a colour you pick. Nothing is ever stretched.

One engine

The browser preview and the server-side PDF run the same arithmetic. That file is printlayout.ts, it has no dependencies, and it has its own tests.

In the application

Eight passport photos, one sheet, one cut line each.

Pick the size, say how many, and the sheet fills itself. Equal sizes snap to an exact grid; mixed sizes go through a packer. Every cell carries its measurement as a caption, and the caption gets out of the way when there is no room at that edge.

The same picture may appear twice at two different sizes. Layouts you repeat become presets. The last hundred runs are kept as instructions rather than as PDFs — one to three kilobytes each instead of eighty megabytes — so “print that again” costs nothing.

Eight passport photos, one sheet, one cut line each.
The print studio. Print-only install — no library, no queue, no model.
Optional module: ai

And if you do want a model, it is one switch and your own key.

Image generation is a module. It is off until you turn it on, it needs a key you bring yourself, and it costs money per image — which is exactly why it is not the default. With it on, the studio gets four tasks and every combination of them can be saved as a preset.

Requests go to {base}/images in the OpenAI image shape. The default base is OpenRouter — one key, many models, and the model list fills itself in. Point IMAGE_API_BASE_URL at a LiteLLM gateway, a company proxy or a server of your own and Klarbild talks to that instead. Either way the pictures go to exactly one endpoint, and you chose which.

The studio with the module on: three modes, four tasks, saved presets, exact output size.
The studio with the module on: three modes, four tasks, saved presets, exact output size.

Build a preset

Not a generator — this composes the recipe and shows what it would run. No key, no request, no picture leaves this page.

Result

 

The demo stops at the recipe on purpose. Generating here would mean a key on a public page and a bill for whoever is curious — and it would show you a picture from a model rather than anything about Klarbild. What Klarbild does is the part above: the tasks, the exact target size, and the preset you save once and reuse.

Two languages

English by default, German all the way through.

Not a half-translated interface: 1,067 entries, covering every translatable string in the source. The English sentence is the translation key, so a missing entry falls back to readable English rather than to print.sheet.title.

One environment variable sets the default; visitors switch with the button in the corner and the choice sticks. Adding a third language means copying one JSON file and translating its values.

English by default, German all the way through.
The same screen, ?lang=de.
Modules

You decide what this install is.

Klarbild is one codebase but not one product. The print studio is the core. Everything that needs an account somewhere else is a module, and every module is off until you switch it on.

print

Print studio — Exact physical sizes, sheet layout with crop marks, bleed, tiling, 1:1 PDF.

Nothing. Always on.
ai

Image generation — Clean up a screenshot, convert a photo, combine several, generate from text.

A key for an images API. Paid per image.
delivery

Delivery — Push finished files to an SFTP or FTPS server, plus extra targets per folder.

A server you already have.
mirror

Backup mirror — A second copy of every result, filed by month.

A second SFTP/FTPS target.
share

Share links — Public links that expire, can be revoked, can carry a passphrase, and ZIP up a whole set.

A publicly reachable address.
mail

Email — Password reset, job notices, sending share links.

Your own SMTP mailbox.
telegram

Telegram bot — The whole tool from a chat window instead of a browser.

A bot token from BotFather.

A module that is off is off: no navigation entry, no settings card, the page redirects, and its API routes answer 404 rather than 403 — a disabled instance does not advertise what it could have done.

# the default: the print studio and nothing else
KLARBILD_MODULES=print

# pick what you want
KLARBILD_MODULES=print,ai,share

# everything
KLARBILD_MODULES=all
Install

Four lines, then a password in the log.

The default compose file runs the application and a PostgreSQL, with files on a volume. No object store, no API key, no account anywhere. A second compose file adds MinIO and turns every module on, for people who want that.

There are no built-in accounts. The first boot creates one administrator — from ADMIN_USERNAME and ADMIN_PASSWORD if you set them, and otherwise with a generated password printed to the log exactly once.

git clone https://github.com/tillheidrich/klarbild.git
cd klarbild
cp .env.example .env    # four values at the top; the file says which
docker compose up -d

docker compose logs app # your admin password, printed once

Astro on Node, PostgreSQL, sharp and pdf-lib. About 13,700 lines across 117 files. Sixteen forward-only migrations that run themselves on boot. Type check, tests and a secret scan run in CI on every push.

Why self-hosted

These are passport photos and family pictures.

The print studio never makes an outbound request at all. With the image module on, only what you hand the model leaves the building, and the key is yours.

Share links are built to be careful rather than convenient: they expire after thirty days by default, can be revoked, can carry a passphrase, and are noindex. Access hangs on the link rather than on the picture, so guessing an image id returns a 404. The view counter records when and with what, never who — the address is hashed with a server secret and the link id, one row per visitor per hour, deleted after ninety days.