DEVONthink 4.3.2: OCR’d PDFs stay “PDF document” with 0 words — three interacting text-extraction bugs
Summary
After running OCR (ABBYY) in DEVONthink 4.3.2 on macOS 27, the resulting record stays “PDF document” with word count 0 — not searchable, silently. The PDF itself has a perfectly valid invisible text layer: macOS PDFKit, pypdf, Spotlight (mdimport -t) all extract the full text. Re-importing the identical bytes into a fresh database gives 0 words again, while a non-ABBYY control PDF indexes immediately — so this is specific to DT’s text extractor choking on ABBYY’s output.
Environment
- DEVONthink 4.3.2, macOS 27.0 (Tahoe), Apple Silicon
- PDFs produced by DT’s own OCR (ABBYY FineReader Engine): JBIG2/JPX image + invisible text layer (Tr 3), subset TrueType fonts (ArialMT etc., no FontFile), custom /Encoding objects with /Differences, ToUnicode CMaps
What I tested (systematic bisection, 50+ controlled variants)
Each variant was rebuilt with pypdf and imported into DT while reading word count immediately:
- Not the file, post or index: fresh import of byte-identical file → 0 words. Control PDF → 1169 words instantly.
- Not the images: removing the JBIG2/JPX image changes nothing; conversely the working files also contain JPX images.
- Not the encoding form: replacing the indirect
/Encoding N 0 R(Differences array) with/WinAnsiEncodingfixes SOME files (those with natural-language text) but not others. - Not FontDescriptor/Flags/Widths/FirstChar: byte-level dumps identical between working and failing files.
- Not render mode 3, NUL bytes in Tj strings, octal escapes, non-WinAnsi ToUnicode targets, xref/object-stream form — all individually exonerated.
The three bugs (each proven with a minimal reproducer)
Bug 1 — ToUnicode CMap poisoning. Two byte-identical PDFs where the ONLY difference is the ToUnicode targets for glyph codes 00/01 (00AB/003E vs 0041/0042) — same content stream, same fonts, same everything: one indexes 169 words, the other 0. Something in the CMap parser state fails silently and kills extraction for the whole document.
Bug 2 — decimal-number collapse. Two consecutive text blocks containing ONLY decimal numbers ((1,85) Tj then (0,3) Tj) → 0 words for the entire document. Integers (185, 3) are fine; decimals surrounded by words (verdi 1,85) are fine. Looks like a number/date tokenizer that fails on comma-decimals.
Bug 3 — language heuristic rejects fragmentary text. A text layer consisting of component labels (K EfiA L JC 0 1106 I CO MIVr EDO) or number tables → 0 words. Adding ONE real word doesn’t help; adding a full sentence does. DT appears to discard the whole extraction when the text doesn’t look like natural language. This explains why OCR’d datasheets/schematics/tables are hit hardest, and why prose documents (e.g. a Couch-to-5K plan) index fine after the encoding fix.
Also observed along the way: in some paths the extractor reads only the first BT-block / clips long lines (a 20-line synthetic layer yielded exactly one line’s words until each line got its own BT/ET block).
Reproducers
Minimal single-page PDFs with a Helvetica text layer (built via pypdf, happy to share):
A.pdf:(ii 80 Mat 20 7 nl F k) Tj→ 0 wordsB.pdf: same +oscillator circuit diagram→ 11 wordsC.pdf:(1,85) Tjblock +(0,3) Tjblock + words → 0 wordsD.pdf: same numbers as(185)(3)→ words counted fine
Impact
Any ABBYY-OCR’d document with fragmentary content (schematics, datasheets, tables, labels) silently loses full-text search in DT. Re-running OCR doesn’t help (same ABBYY output). No error is logged.
Workaround
We rebuild the text layer: extract text via PDFKit, write a new layer (one BT/ET block per line group, standard Helvetica, decimals normalized, fragment-only lines filtered, document title sentence first). All 8 affected files now index as PDF+Text. Script available on request.
Happy to provide the reproducer PDFs and full test matrix if useful.
