Tools,

Zotero OCR, tested on a real scan

1,879

of 1,955 words found, 10-page scan

See its two steps

Zotero cannot search a scan

Zotero has no OCR. It reads PDF text with pdf.js, which found 0 words in my scan. The zotero-ocr plugin adds OCR with Tesseract.

What plugin 0.9.7 adds, default settings
Added For 10 scanned pages
A note All the text
A new PDF, title.ocr Hidden text layer, 4.0 MB
HTML pages 5 of 10 pages
Page images, kept 10 JPEGs, 4.0 MB

pdf.js read 1,876 words from the new PDF, so search has text now. The original PDF stays as it was.

Not working? Run its two steps

With its default settings, the plugin runs pdftoppm, then Tesseract. I saved the same commands as zotero-ocr.sh and ran sh zotero-ocr.sh eng next to book.pdf:

pdftoppm -progress -cropbox -jpeg -jpegopt quality=70,progressive=y,optimize=y -r 300 book.pdf KEY-page
ls KEY-page-*.jpg > KEY-list.txt
tesseract KEY-list.txt book.ocr --psm 3 -l "$1" txt pdf hocr

It took 29 s for the 10 pages, and Terminal shows the whole error.

Zotero 7 and later make the page images with pdf.js, and use pdftoppm only when that fails.

PNG pages: same words, 9 times bigger

A plugin request asks for PNG page images instead of JPEG. I set the hidden imageFormat to png and ran the scan again.

10 scanned pages, 6.5 MB
Page images Words New PDF
JPEG, quality 70 (default) 1,879 4.0 MB
PNG 1,879 36.8 MB

PNG also took 141 s, 5 times as long. My scan was clean, so test a poor one yourself.

To drop the kept JPEGs, untick "Save the intermediate images as well in the folder".

Other languages on a Mac

With eng, the 3 German pages lost all 206 umlauts. With deu, Tesseract stopped: Failed loading language 'deu'. Homebrew Tesseract had only eng and osd.

lang.sh puts the German file where that Tesseract looks:

curl -sL -o "$(brew --prefix)/share/tessdata/deu.traineddata" https://github.com/tesseract-ocr/tessdata/raw/main/deu.traineddata

Then set the language to deu in the plugin settings. For Spanish, change deu to spa in both places.

189 of 206 umlauts

913 of 972 words in order, two columns

Nothing to install: the Actor

Our OCR Actor runs in the cloud. On the 11-page file:

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' > book.md

1,874 of 1,955 words, plugin 1,879

954 of 972 in order on two columns, plugin 913

With deu it got 192 umlauts, the plugin 189. With no language set, 0.

It was slower: 142 s for 11 pages, the plugin 31 s.

It costs $0.006 a page. It gives Markdown, not a PDF that Zotero can search. The plugin is free and offline.

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. One is a two-column magazine page.
  • The plugin: I read the code of zotero-ocr 0.9.7 and ran its commands with its defaults. Homebrew Tesseract 5.5.4 and pdftoppm 26.03.0 on an arm64 Mac, 2 cores at the lowest priority.
  • Search: pdf.js 6.4.299, the library in Zotero's PDF worker. Score: true words found in order, with the scorer of the tessdata test. German: the scorer of Tesseract German.
  • The code blocks above are the files that ran. I removed the German file after. The Actor: three real runs, build 0.1.65, one German run with no language.
What I did not test
  • Zotero itself. I ran the plugin's commands, not the plugin.
  • The pdf.js page images of Zotero 7 and later. I used pdftoppm with the same settings.
  • Windows, and PNG on a poor scan.
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 Zotero have built-in OCR?

No. Zotero indexes the text that a PDF already has. In my scanned PDF, pdf.js found 0 words. The zotero-ocr plugin adds OCR with Tesseract.

Why does Zotero OCR say no tesseract executable found?

The plugin looks in a few folders, like /opt/homebrew/bin and /usr/local/bin. MacPorts uses /opt/local/bin, which it does not check. Run which tesseract and paste that path into the plugin settings.

Does the Zotero OCR plugin replace my PDF?

Not by default. It attaches a new PDF with .ocr at the end of the title. The setting Overwrite the initial PDF with the output replaces the original.