Repository navigation
Home
The library's domain model, pipeline and contracts. This is the developer documentation: the end-user manual for the Platemaker application lives in the Platemaker-qt wiki.
Split out of a single 1225-line document once it stopped being readable end to end. The four foundations are the intended reading order; everything else is reference you consult.
Applies to: libplatemaker 0.6.0 (unreleased) and the platemaker CLI.
| Page | What it settles | |
|---|---|---|
| 1 | Building a workspace | What a workspace holds, how it is persisted and repaired, and where its files live. |
| 2 | Building a project | What one chapter is, what state it carries between renders, and what makes it stale. |
| 3 | Rendering a project | The pipeline from input page to output slice, and the contract with whoever calls it. |
| 4 | The preview workflow | How a consumer shows a chapter — and authors edits against it — before any render exists. |
| Page | What it is |
|---|---|
| Canvas profile matching | Which profile a page renders with, and how a profile edit is noticed. The most intricate rule in the library. |
| Optional processing steps | Colour correction and text/bubble overlays — the two seams, and why they sit where they do. |
| Components | Class by class: what each one does and what it does not. |
| CLI reference | Command grammar, exit codes, JSON output. |
| Platform & distribution | Targets, toolchains, dependencies, packaging. |
| Roadmap & history | Planned work, the staged build order, and what the Clip2l prototype taught. |
| Diagrams | Nine draw.io diagrams and the conventions they follow. |
Platemaker is a cross-platform desktop application (with CLI) for comic artists. It handles both pre-processing (generating canvas templates with margin guides) and post-processing (scaling and slicing finished artwork for distribution platforms).
The name draws from the printing trade: a platemaker crafts the printing plates from which pages are born. Platemaker does the same — it shapes canvases before drawing and prepares finished pages for publication.
Primary use case (v1): Processing comic pages for Webtoon-style vertical scroll format. The application treats an ordered set of source images as a single virtual strip, scales them to a target width, and slices the strip into fixed-height output panels.
Future scope (not in current requirements): Template generation and margin-aware processing for traditional comic formats — American (6.625" × 10.25"), European (various), manga (B4/A4). The architecture is designed to support multiple CanvasProfile presets without code changes.
The project is open-source (LGPL 3.0). Monetization via donations may be added in the future — LGPL 3.0 permits this as long as libplatemaker remains dynamically linked and unmodified by the end user.
The artist works on one or more canvases in Procreate and exports them as PNG files. These files arrive at Platemaker pre-sliced at arbitrary heights (e.g. 2560 px, 2000 px, 1800 px) — they are already "pieces of a strip" from the artist's perspective.
Platemaker treats the entire ordered file list as a single continuous virtual strip. Slice boundaries in the output are determined solely by the target slice height and are completely independent of source file boundaries.
Pipeline overview:
- Load an ordered list of image files (directory or explicit list).
- Allow reordering of pages (drag & drop or manual sort).
- Scale each file to the target width (Webtoon standard: 800 px), preserving aspect ratio.
- Concatenate scaled images conceptually into one virtual strip.
- Slice the strip at every N pixels (Webtoon standard: 1280 px).
- Export output slices as sequentially numbered PNG files.
- One tail slice at the end (shorter than sliceHeight) — handled per LastSlicePolicy.
Goal: minimise the number of output files. Webtoon does not respect upload order reliably, so fewer files = less manual reordering after upload.
Note: "Sequence mode" in the Clip2l prototype (
--sequence/-sflag) corresponds to this pipeline. In Platemaker this is the only and default mode — there is no per-file-independent processing mode.
Canvas math example:
Input files (after scaling to 800 px wide):
page_01.png → 800 × 5120 px
page_02.png → 800 × 4000 px
page_03.png → 800 × 3600 px
Virtual strip total height: 12720 px
Slices at 1280 px: 9 full slices (11520 px) + tail (1200 px)
Output files: output_001.png … output_010.png
output_003.png may contain the bottom N px of page_01 and top (1280-N) px of page_02.
Slice boundaries never need to align with source file boundaries.
┌──────────────────────────────────────────────┐
│ Qt 6 GUI (separate repository) │ Tabs/panels, preview, drag&drop
├──────────────────────────────────────────────┤
│ CLI binary (`platemaker`) │ Standalone tool; future web backend
├──────────────────────────────────────────────┤
│ Core Library (`libplatemaker`) │ Pure C++, zero Qt dependency
│ Scaler │ Strip │ Cropper │ TplGen │ Match │
├──────────────────────────────────────────────┤
│ Infrastructure / IO │ WorkspaceSerializer, ImageIO, Cache
├──────────────────────────────────────────────┤
│ libvips │ nlohmann/json │ std:: │ Third-party dependencies
└──────────────────────────────────────────────┘
| Decision | Choice | Rationale |
|---|---|---|
| Language | C++ 17/20 | Developer is expert; best performance for image processing |
| Image processing | libvips | Multi-threaded, lazy/streaming pipeline, Lanczos3 resampling, low RAM |
| JSON serialisation | nlohmann/json | Header-only, MIT licence, clean API |
| Build system | CMake + CMakePresets.json | Standard for C++ cross-platform; preset per platform |
| Package manager | vcpkg (manifest mode) | vcpkg.json in repo; reproducible on every machine |
| File integrity | SHA-256 | Per-input-file hashing for incremental processing |
| Library distribution | LGPL 3.0 shared library | End users can relink; GUI repo links .dll/.so via find_package
|
libplatemakermust have zero Qt dependencies.
This keeps the Core testable without a display, usable from CLI without Qt, and portable to future backends (web server, Python bindings, etc.). Qt types (QImage, QPixmap, etc.) must only appear in the GUI layer.
The strip must never be fully materialised in RAM.
ScaledStrip processes files one at a time and keeps in memory only the minimum number of scaled images needed to complete the current output slice. For example: if a slice boundary falls mid-file, two files may be in memory simultaneously (tail of file N + head of file N+1). As soon as a file's contribution to all remaining slices is exhausted, it is released. This is critical for large projects where the total input may be several gigabytes — the file list and thumbnails are metadata only; raw pixel data is loaded on demand.
Every path in the API is UTF-8, and the conversion to
std::filesystemis explicit.
Workspace JSON stores UTF-8 and the GUI hands over UTF-8, but what a narrow std::string
means is not agreed upon across the standard library: on Windows std::ifstream passes the
bytes to fopen(), which reads them in the ANSI code page, while libstdc++'s
std::filesystem::path reads the same bytes as UTF-8 (MSVC reads them as ANSI in both
places). A program mixing the two conventions is not portable and not even self-consistent.
Nothing in the library may construct a std::filesystem::path from a narrow string, or call
path::string(). Use utf8ToPath() / pathToUtf8() from
platemaker/infrastructure/file/path_utf8.hpp, which go through std::u8string and are
therefore defined by C++20 rather than by the toolchain or the machine's code page.
path::string() is additionally unsafe on MSVC: it throws on a character the ANSI page
cannot represent.
This was not hypothetical. Under a path such as G:/Mój dysk/… — the folder name Google
Drive creates on a Polish system — hashing an input silently returned nothing, so inputs
stayed Pending after a successful render and every render redid all the work; and a
workspace saved through an fs::path could never be reopened through a std::string. See
tests/lib-unit-tests/test_path_encoding.cpp.
CLI is not a wrapper added after the fact — it is a primary interface and the intended backend for a future web application. A web server can call platemaker process --json as a subprocess and parse its stdout. The layer hierarchy is:
libplatemaker (shared library, pure C++)
├── platemaker (CLI binary — links libplatemaker)
└── platemaker-gui (Qt GUI binary — separate repo, links libplatemaker .dll/.so)
id/uidare local identifiers, not RFC 4122 UUIDs — random<prefix>-<hex>strings minted byInfrastructure::id_generator(profiles useid; projects/inputs/outputs useuid). The: uidtype annotation below means exactly that.
See also: Home · Workspace · Project · Render · Preview · Canvas-Profiles · Processing-Steps · Components · CLI · Platform · Roadmap · Diagrams