Repository navigation
Fonts
Legacy generation draws Khmer and English text from separate pools. Opt-in
--multilingual searches all recursively discovered font directories or the
explicit --font-pool NAME. Arbitrary top-level directories form named pools.
Whole-line coverage is checked before selection; the new mode requires RAQM and
does not use legacy Khmer/ASCII spans for general bidi text. See the
multilingual capability matrix.
fonts/
├── khmer/ <- .ttf / .otf / .ttc / .woff / .woff2 Khmer fonts
├── english/ <- .ttf / .otf / .ttc / .woff / .woff2 English fonts
└── *.ttf <- files directly here join BOTH pools as fallbacks
The root directory is set with --fonts DIR (default fonts/) or the YAML key fonts. The
khmer/ and english/ subdirectories are scanned recursively, so you can organise fonts into
further subfolders.
| Extension | Notes |
|---|---|
.ttf |
TrueType — the common case |
.otf |
OpenType |
.ttc, .otc
|
Font collections; face index 0 for both rendering and cmap coverage |
.woff |
Web Open Font Format |
.woff2 |
Web Open Font Format 2 |
For each text line the generator decides which pool to draw from:
- Text containing Khmer characters → the Khmer pool.
- Pure Latin text → the English pool.
- Mixed Khmer/English → the pool for the dominant script, plus per-span font switching when
--mixed-font-probis non-zero (see below).
Fonts sitting directly in fonts/ are always eligible, which makes them a useful fallback when one
pool is thin.
Not every font covers every glyph. Before using a font the renderer checks that it can actually
draw the text via ImageRenderer._is_text_supported:
Real font files use a shared fontTools Unicode cmap policy with or without Rust, cached by path and collection face index (bounded at 256 faces). Shaping/bidi controls and variation selectors need no standalone glyph but remain in logical text. Other controls fail closed; errors do not silently assert coverage. Non-file font objects retain a raster signature fallback. See the multilingual guide for the explicit control list and variation-sequence limitation.
Legacy selection still retries up to --retry-limit N (default 10). Multilingual
selection first enumerates eligible whole-line faces, independently of random
retry luck; unsupported lines produce a preflight error. Cmap coverage alone
cannot certify linguistic correctness or cluster shaping.
Fonts are registered at a set of standard sizes (28, 32, 36, 40, 44, 48). Actual FreeType faces
are loaded lazily on first use and kept in a bounded LRU cache (1024 entries) so a long run
does not pin every face it has ever touched in memory.
With --font-size-mode proportional, sizes are instead derived per image from the sampled canvas
height. See Variable Line Height.
Bold and italic text decorations use real variant fonts, not synthesised weight/slant. A font file is tagged by inspecting its filename stem and its internal style name:
-
boldin the name (or style name) → taggedbold -
italicorobliquein the name (or style name) → taggeditalic
Selection rules for --text-deco-bold-prob / --text-deco-italic-prob:
| Requested | Behaviour |
|---|---|
| bold | any font tagged bold (a bold-italic font qualifies) |
| italic | any font tagged italic/oblique |
| bold and italic | prefer a font tagged both, else fall back to a bold font |
If no matching variant exists in the pool, the decoration is skipped for that line rather than
faking it. So --text-deco-bold-prob 0.5 on a pool with no bold fonts simply produces no bold
lines. Ship real variant files (e.g. NotoSansKhmer-Bold.ttf, NotoSansKhmer-Italic.ttf) if you
want these decorations to fire.
--mixed-font-prob F (default 0.0) sets the probability that a line containing both scripts is
rendered with per-span font selection: each contiguous run of Khmer or Latin text gets a font
drawn from its own pool. At 0.0 the whole line uses a single font chosen for the dominant script.
khocr-gen generate --corpus corpus/corpus.txt --fonts fonts --mixed-font-prob 0.6Span splitting is accelerated in Rust (_rust_accel.split_text_spans) with an equivalent pure-Python
fallback. See Rust Acceleration.
| Value | Behaviour |
|---|---|
random (default)
|
Render --copies augmented copies per text line, each with a randomly chosen font |
all |
Render one image per font per line |
font-mode=all touches every font at every standard size, which is the heaviest path for the font
cache. Expect a longer warm-up and more memory than random.
-
More fonts beat more copies. A pool of 3–5 Khmer fonts gives far more useful variation than
raising
--copieson a single font. - Include one broadly-covering Khmer font as a fallback. Khmer coverage varies a lot between fonts, and missing glyphs cause retries and skipped lines.
-
Check what you have. Run a tiny generation with
--count-onlyfirst, then generate a few hundred samples and inspect them withkhocr-gen view. - Add variant files deliberately if you plan to use bold/italic decorations.
- Getting Started: first setup walkthrough
- Text Decorations and Effects: what bold/italic do
- Variable Line Height: proportional font sizing
- Troubleshooting: what to do when lines come out empty
OCR Foundry — synthetic OCR training data with Khmer/English and opt-in multilingual rendering · MIT · Repository · Issues · PyPI · Changelog
Using OCR Foundry
Data controls
- Corpus and Normalization
- Fonts
- Variable Line Height
- Text Decorations and Effects
- Augmentation
- Output and Storage
- Page Layout
Internals
Help