How to train Tesseract on a new font
0.1%
letters wrong on new lines, after training
For printed scans: $0.006 a page
The commands, on a Mac
Start from tessdata_best. Training needs its float
weights, and the fast models are integers.
brew install tesseract make wget
git clone https://github.com/tesseract-ocr/tesstrain
cd tesstrain
python3 -m venv .venv
source .venv/bin/activate
pip install pillow
mkdir -p tessdata
curl -L -o tessdata/eng.traineddata https://github.com/tesseract-ocr/tessdata_best/raw/main/eng.traineddata
Put one PNG per line in data/brush-ground-truth/, each
with a .gt.txt of its exact text:
gmake training MODEL_NAME=brush START_MODEL=eng TESSDATA=tessdata MAX_ITERATIONS=2000
macOS ships make 3.81 and tesstrain needs 4.2 or newer, so call
gmake. Read a line with your model:
tesseract line.png - --tessdata-dir data -l brush --psm 13
Use --psm 13 on single lines, as tesstrain does. At psm
7, every model returned nothing for 14 of 200 lines.
How I tested it
- 800 training lines and 200 test lines in Brush Script, drawn with Pillow at about 300 dpi. The test lines have new text.
- Each line got a small tilt, blur, noise and JPEG, like a scan.
- The text: invoice rows, addresses, dates, phone numbers and short sentences.
- Tesseract 5.5.4, tesstrain from April 2025, one Mac. Letters wrong is CER, as on how to measure OCR accuracy.
- The commands above, run as printed in a new folder, took 147 seconds from line images to model.
Cheap fixes did not fix the font
Upscaling moved the error rate by a tenth of a point, and Otsu black and white made it worse. The image was fine. The letter shapes were the problem.
| Stock best read | Trained read |
|---|---|
| 9719 Zud Aue, Boise, TD 55490 | 9719 2nd Ave, Boise, ID 58490 |
| Delivery oun in weekly the setumns local! | Delivery our in weekly the returns local! |
100 lines already beat every cheap fix
| Training lines | Letters wrong | Seconds |
|---|---|---|
| 100 | 1.2% | 115 |
| 300 | 0.51% | 119 |
| 800 | 0.1% | 147 |
Each run did 2,000 iterations, so more lines cost little time. The errors kept falling.
Keep the stock model for plain fonts
The trained model drifted on fonts it already read well.
| Font, mode | Stock best | Trained |
|---|---|---|
| Arial, psm 7 | 0.05% | 0.34% |
| Arial, psm 13 | 1.03% | 0.56% |
| Times New Roman, psm 7 | 0.02% | 0.39% |
| Times New Roman, psm 13 | 1.10% | 0.78% |
At psm 7, stock best beat it on both. Use your model only on pages in your font.
Limits, and what I did not test
- Full pages. I tested single lines, so how page layout copes with a script font is open.
- Real scans. Every line here is made up, in one font.
- Training on a mix of your font and plain fonts.
- Training from scratch, or a new language.
Sources
Our Actor uses the stock models
Our Apify Actor uses stock models: Tesseract fast, best for the numbers fast is unsure of, and RapidOCR on pages Tesseract reads badly. It cannot load a model you trained.
Two ways to skip the work
Run it yourself. Our Apify Actor turns scanned PDFs into Markdown, tables included.
Or send us the scan. Tack, our AI, does the job for $29, up to 50 pages. First page free.
Questions people ask
How do I train Tesseract on a new font?
Make an image of each text line in that font, with a .gt.txt file that holds its text. Run tesstrain with START_MODEL=eng from tessdata_best. Here 800 lines took 147 seconds on a Mac.
How many lines do I need to train Tesseract?
For one font, fewer than you might think. Here 100 lines cut letters wrong about tenfold on new lines, and 800 lines got it to 0.1%.
Can I fine-tune tessdata_fast?
No. Its models are 8-bit integers, and training needs the float weights in tessdata_best. The trained model comes out about the size of best, 11.7 MB here.