Thin Bun CLI over Tesseract. Takes PDFs and images, dumps text. Defaults to Turkish.
- Bun
- Tesseract + language data
- Poppler (for
pdftoppm,pdfinfo,pdftotext,pdfimages)
Tested on macOS and Linux. Windows isn't tested but shouldn't be architecturally blocked. Bun supports Windows natively and Tesseract and Poppler are both available there. The --copy flag is the only piece that won't work on non-macOS (uses pbcopy).
brew install tesseract tesseract-lang poppler
sudo apt install tesseract-ocr tesseract-ocr-all poppler-utils
On Fedora the package names are tesseract, tesseract-langpack-*, and poppler-utils. On Arch they're tesseract, tesseract-data-*, and poppler. tesseract-ocr-all (or installing every langpack-*) pulls every supported language; swap for specific ones if you'd rather keep the footprint small.
git clone https://github.com/jericho909/ocr-now.git
cd ocr-now
bun install
bun link
ocr-now is now on your PATH.
ocr-now start [opts] # batch project's input/ folder
ocr-now <file> [opts] # OCR a single file in place
ocr-now config [list|get|set|unset] ... # inspect or change settings
ocr-now langs # list installed tesseract languages
ocr-now -h | --help # show help
ocr-now -v | --version # print version
PDF pages are processed in a streaming pipeline: rasterization and OCR run concurrently, so OCR begins on early pages while later pages are still being rasterized. Both phases are capped at 6 workers or your CPU core count, whichever is lower. Page order in the output is preserved regardless of completion order.
If a PDF has embedded text on the selected pages, ocr-now extracts it directly via pdftotext and skips OCR for those pages. A 144-page fully text-embedded PDF goes from ~75s to <1s. Hybrid PDFs (e.g., a scanned page inserted into an otherwise digital doc) mix the two paths: text pages extracted directly, scanned pages OCR'd. Fully scanned PDFs go through the full OCR pipeline.
Drop PDFs/images into input/, then:
ocr-now start
Writes one combined file to output/ocr-now <LANG> <timestamp>.txt. The input/ and output/ folders are created automatically on first run.
ocr-now ~/Downloads/invoice.pdf
Writes ocr-now <LANG> invoice.txt next to the source file.
Filenames with spaces or special chars: quote them, or use tab completion.
| Flag | Purpose |
|---|---|
--lang=xxx |
Tesseract lang code. Multi-language with +: --lang=tur+eng. Use auto to detect |
--dpi=N |
Rasterize PDFs at N dpi. Range 72–600. Default 300 |
--pages=1-3,7 |
PDF only. OCR a subset of pages. Non-contiguous allowed |
--out=<path> |
Override output. Treated as a directory if it ends in / or already exists as one; otherwise as a file path |
--copy |
Also copy result to clipboard. macOS only (uses pbcopy) |
--stdout |
Write result to stdout instead of a file. Status logs go to stderr so output pipes cleanly: ocr-now foo.pdf --stdout | grep keyword |
--quiet |
Suppress progress, spinner, and the final "wrote" line. Errors still print to stderr |
--json |
Emit structured JSON instead of the plain-text section format. Output file extension switches to .json. Pipes cleanly: ocr-now foo.pdf --json --stdout | jq .pages[0] |
All stackable. Example:
ocr-now ~/Downloads/long.pdf --pages=1-2,5 --dpi=400 --copy
Persistent settings live in config.json at the project root.
ocr-now config list
ocr-now config get defaultLang
ocr-now config set defaultLang eng
ocr-now config set defaultDpi 400
ocr-now config unset defaultDpi
Valid keys:
| Key | Default | Notes |
|---|---|---|
defaultLang |
tur |
Tesseract code, multi-language with +, or auto |
defaultDpi |
300 |
Integer in 72..600 |
autoMinConfidence |
0.2 |
Min franc score to accept the detected language; below this, fall back to the baseline |
autoMinSampleChars |
20 |
Min sample-text length before franc is consulted at all |
Resolution order: flag → config → built-in default.
defaultLang accepts auto as a special value. See below.
ocr-now ~/Downloads/foo.pdf --lang=auto
ocr-now config set defaultLang auto
Auto mode runs a quick low-DPI sample pass, runs franc-min on the resulting text, then re-OCRs with the detected language. Cost: ~1–3s extra per file.
Sample-pass baseline picks your configured defaultLang if set (so Turkish diacritics survive when reading Turkish docs), otherwise eng, otherwise the first installed language. The baseline is also used as the fallback when detection fails.
Notes:
- Picks the single best match. If your doc is genuinely bilingual, pass
--lang=tur+engexplicitly. Single-pass detection isn't suited to multi-language inference. - Falls back to the baseline if the sample text is too short, no installed language matches, or detection confidence is below the
autoMinConfidencethreshold (default0.2). - Detected language is shown per file and used in the output filename. Batch-mode combined output uses
AUTOin the filename since files may differ; the per-file section header ([LANG]) shows what was actually used.
Tesseract uses ISO 639-2/T codes: eng, tur, deu, fra, spa, ita, por, rus, ara, chi_sim, jpn, kor, etc. Run tesseract --list-langs to see what's installed.
If you pass a code that isn't installed, ocr-now aborts and prints your installed list before doing any work.
One file per run. Each source file gets a section:
========== filename [TUR] ==========
--- Page 1 ---
<text>
--- Page 2 ---
<text>
The [LANG] suffix in the section header shows the language actually used for that file (useful with --lang=auto).
Single-file mode skips the ======== header (single source).
ocr-now is a thin wrapper. The actual work is done by:
| Tool | License | Link |
|---|---|---|
| Tesseract | Apache 2.0 | invoked as a subprocess |
Poppler (pdftoppm, pdfinfo, pdftotext, pdfimages) |
GPL | invoked as a subprocess |
| franc-min | MIT | npm dependency |
MIT. See LICENSE.