Tools,

Chandra OCR, tested on a real scanned PDF

1,911

of 1,955 scan words, read in order

Get the setup

On a Mac, add --method hf

chandra-ocr 0.2.0 needs a vLLM server by default. On a Mac, use pip install "chandra-ocr[hf]" and chandra.sh:

chandra book.pdf out --method hf

1,911 of 1,955 words, in order

169 s for 10 pages, Mac GPU

Run it next to book.pdf. The first run downloads a 10.6 GB model. The text is in out/book/book.md.

No --method: 0 words, no error

The README starts with chandra input.pdf ./output. That sends each page to a vLLM server on localhost:8000. With no server, it tried for 60 s. Then it wrote an empty book.md and exited with code 0.

A script that checks only the exit code will not see this. Check that book.md has text.

Typed pages: 12 of 13 cover words

Chandra reads every page as a picture, typed pages too. In the 11-page file, it read the typed cover page. But the CLI joins pages with no space, so the last cover word, 2026., ran into the first word of page 2.

The code has a --paginate_output flag that puts a line between pages. I did not run it.

German worked with no language setting

Chandra has no language option. I gave it 3 German pages. One is a magazine page with two columns.

199 of 206 umlauts

958 of 972 words in order, two columns

Chandra read more, our Actor was faster

Our Actor makes Markdown from a file URL, with no install:

curl -s -X POST "https://api.apify.com/v2/acts/brasspage~pdf-ocr-to-markdown/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"documentUrls": ["https://api.apify.com/v2/key-value-stores/sTnmNrGoSsi7tLQvs/records/anythingllm-mixed-11-pages.pdf"]}' \
  | jq -r '.[].markdown' > mixed.md
Same files, Chandra on the Mac GPU
Test Chandra Actor
Scan words in order 1,911 1,874
Typed cover words 12 13
Umlauts 199 192
Two columns, in order 958 954
Time, 11 pages 175 s 133 s
Time, 3 German pages 161 s 59 s

The Actor needs no GPU and no 11.5 GB setup. It costs $0.006 a page.

The model license has limits

Chandra's code is Apache-2.0. Its model weights are free for personal use, research and businesses under $2M in revenue and funding. A business that competes with Datalab cannot use them.

How I tested it
  • Scan: the 10 pages of OCR in n8n, proofread Wikisource book scans, 1,955 true words. The 11-page file adds the 13-word typed cover page of AnythingLLM OCR.
  • German: the 3 pages of Paperless-ngx OCR, 1,887 words, 206 umlauts.
  • Score: true words found in order in book.md, with the scorer of the tessdata test. German: the scorer of Tesseract German.
  • chandra-ocr 0.2.0, PyTorch 2.15.0, Python 3.12 arm64, on an Apple Silicon Mac GPU, at the lowest priority. One run each. Times include the model start.
  • The code blocks above are the files that ran. The Actor: two real runs earlier the same day, build 0.1.65, on the same files.
What I did not test
  • vLLM, NVIDIA GPUs, and a run with no GPU.
  • The GGUF and MLX copies of the model that others posted.
  • Handwriting, tables, math and forms.
Sources

Two ways to skip the work

Run it yourself. Our Apify Actor turns scanned PDFs into Markdown, tables included.

Try the Actor

Or send us the scan. Tack, our AI, does the job for $29, up to 50 pages. First page free.

Get one page free

Questions people ask

Does Chandra OCR run on a Mac?

Yes, with --method hf. On a Mac GPU it read 10 scanned pages in 169 s. The default method needs a vLLM server.

Why did chandra write an empty file?

With no --method, chandra sends each page to a vLLM server on localhost:8000. With no server, it wrote an empty book.md after 60 s and exited with code 0.

Can Chandra OCR read German?

Yes. Chandra has no language option. On 3 German pages with no setting, it kept 199 of 206 umlauts.