| OCR is entered for the whole file when native parsing fails or no page has alphanumeric text. Otherwise a per-page rule picks the pages to OCR. | Read from the code | superdocuments/src/pdf/mod.rs (extract_file_with_ocr_inner); superdocuments/src/pdf/page_profile.rs | extract_file_with_ocr_inner parses the pages and their geometry first. If any page has embedded text, page_profile::verdicts flags the pages with no text and the scans whose only text is a stamp, and those pages go to merge_native_and_ocr. A failed native parse, or a file with no text on any page, goes straight to tesseract_ocr_with_observer on the whole file. |
| In a file that has some text, the native text of each page the rule does not flag is kept, and the flagged pages are OCRed and spliced in page order. | Read from the code | superdocuments/src/pdf/mod.rs (merge_native_and_ocr, page-level routing); superdocuments/src/pdf/page_profile.rs | extract_pages_markdown gives per-page text, and merge_native_and_ocr writes <!-- page N --> before each page with text. A page is flagged when it has no alphanumeric native text, or when images cover half the page or more and its text is under 0.4 alphanumeric characters per 1000 pt². |
| Pages are rendered with pdftoppm using a page range, a longest-edge size and PNG output, and no DPI flag. | Read from the code | superdocuments/src/pdf/mod.rs:1078-1088 | Arguments: -f first, -l last, -scale-to max_image_dimension, -png, the input path and a temporary prefix. stdout and stderr are discarded (run_bounded, mod.rs:1360-1366). |
| Pages are recognised with tesseract in PSM 3, with the language list from the options, and with OpenMP limited to one thread when more than one worker runs. | Read from the code | superdocuments/src/pdf/mod.rs:1143-1157 | Arguments: IMAGE BASE -l LANGUAGES --psm 3. The text is read from BASE.txt. OMP_THREAD_LIMIT=1 is set when workers > 1. |
| Option ranges are checked before any process starts: language string, page count 1 to 2,000, image size 256 to 4,000, workers 1 to 16, timeout 1 to 3,600 seconds. | Read from the code | superdocuments/src/pdf/mod.rs:713-752 | OcrOptions::validate returns InvalidInput errors. Its limit constants are at superdocuments/src/pdf/mod.rs:22-29. |
| Every child process runs under one shared deadline. A child that passes the deadline is killed and reported as timed out. | Read from the code | superdocuments/src/pdf/mod.rs:1360-1384 | run_bounded polls try_wait every 10 ms and calls kill and wait at the deadline. The deadline is set once per request (mod.rs:1054). |
| Temporary files live in a per-request directory under the system temp directory, created with mode 0700 on Unix and removed on drop. | Read from the code | superdocuments/src/pdf/mod.rs:1320-1358 | OcrTempDir::create uses std::env::temp_dir() and a process id plus counter name. Drop calls remove_dir_all. |
| The webfold CLI sets OCR defaults from environment variables: language eng, 20 pages, 2,400 px, 120 seconds, and one worker. Its help text says the timeout is at most 600 seconds, but the code does not enforce that limit. | Read from the code | webfold/src/cli/document.rs:256-277 | ocr_options_from_env reads WEBFOLD_OCR_LANG, WEBFOLD_OCR_MAX_PAGES, WEBFOLD_OCR_MAX_DIM and WEBFOLD_OCR_TIMEOUT_SECS. The doc comment at line 258 says at most 600. The library accepts up to 3,600 seconds. |
| The webfold pdf command takes the OCR flags and writes each into its environment variable before extraction. --no-ocr starts neither pdftoppm nor tesseract. | Read from the code | webfold/src/main.rs (the pdf arm) | The flags --ocr-lang, --ocr-max-pages, --ocr-max-dim and --ocr-timeout map to WEBFOLD_OCR_LANG, WEBFOLD_OCR_MAX_PAGES, WEBFOLD_OCR_MAX_DIM and WEBFOLD_OCR_TIMEOUT_SECS. --no-ocr, before or after the file name, returns the native text and names on stderr the pages it did not read. The MCP tool webfold_pdf (src/bin/webfold_mcp.rs) takes no OCR flags and runs webfold pdf. |
| The input is a PDF path only. No image-file arm exists in the document converter. | Read from the code | superdocuments/src/document.rs:849-867 | convert_bytes handles office, ebook, CSV, HTML and text extensions and returns unsupported document extension for anything else, including png, jpg and tif. README.md format matrix says the OCR entry points are file paths only. |
| The test for the OCR path is ignored by default and needs pdftoppm and tesseract on PATH. It checks a raster-only fixture against its phrase and a Flate and ASCII85 fixture. | Covered by a test | superdocuments/src/pdf/mod.rs:1629 and superdocuments/tests/fixtures/README.md | Named test: tesseract_fallback_handles_raster_and_unsupported_filter_pdfs. It asserts that scanned-text.pdf recovers the phrase Scanned fixture: OCR works. after normalising case and punctuation. Not run for this sheet. |
| The mixed-file page cap keeps native text and notes the truncation. | Covered by a test | superdocuments/src/pdf/mod.rs:1599 | Named test: mixed_fixture_scan_beyond_the_page_cap_keeps_native_text_and_notes_truncation, with max_pages set to 1. It expects the native first page and the limit notice. It is not marked ignored, and it calls pdftoppm. Not run for this sheet. |
| A child process that passes its deadline is killed and reported as an error. | Covered by a test | superdocuments/src/pdf/mod.rs:1691 | Named test: ocr_child_process_respects_deadline. It uses sleep and sh, not pdftoppm or tesseract. Not run for this sheet. |
| Ranges of OCR pages are split into windows with no gaps and no overlaps. | Covered by a test | superdocuments/src/pdf/mod.rs:1536 | Named test: ocr_windows_cover_the_page_range_without_gaps_or_overlap. It is pure and needs no tools. Not run for this sheet. |
| On the scanned fixtures the route's character error rate equals Tesseract run directly on page 1: 0.000 on a scan and 0.023 on a blurred scan. On the mixed file it is 0.119 against 0.212. | Measured | PDF text and OCR benchmark; webfold-bench repository, tests/pdf-extractors-v1/results/SUMMARY.md | Eight PDFs from superdocuments/tests/fixtures; character error rate against each file's expected text with HTML tags removed first; median of 6 calls after 1 warm-up, on a shared 4-core machine at a load average of 12 to 30; Poppler 24.02.0 and Tesseract 5.3.4. The route calls Tesseract, so it cannot be more accurate than it. Tesseract direct reads page 1 only, so it cannot read the scanned second page of the mixed file. The Webfold rows come from a build with an interim OCR page rule that was never committed, not the rule that merged. The results record no commit hash for that build. On the scanned sets of olmOCR-bench the route passes 47.2% of tests and Tesseract alone 47.1%. |
| A page that goes to OCR takes 0.6 to 1.0 seconds on these fixtures, against 19 to 26 ms for a pdftotext process that cannot read the scans. | Measured | PDF text and OCR benchmark; webfold-bench repository, tests/pdf-extractors-v1/results/SUMMARY.md | Eight PDFs from superdocuments/tests/fixtures; character error rate against each file's expected text with HTML tags removed first; median of 6 calls after 1 warm-up, on a shared 4-core machine at a load average of 12 to 30; Poppler 24.02.0 and Tesseract 5.3.4. The route took 640, 776 and 1,005 ms on the three files it OCRed. Tesseract direct took 1,759 to 1,915 ms at 300 dpi on page 1, which is not like for like because the route scales pages to a longest edge of 2,400 px by default. |
| A blurred scan scores 0.023 through OCR against 0.628 for pdftotext and MarkItDown, which recover only the native title. | Measured | PDF text and OCR benchmark; webfold-bench repository, tests/pdf-extractors-v1/results/SUMMARY.md | Eight PDFs from superdocuments/tests/fixtures; character error rate against each file's expected text with HTML tags removed first; median of 6 calls after 1 warm-up, on a shared 4-core machine at a load average of 12 to 30; Poppler 24.02.0 and Tesseract 5.3.4. One fixture; 0.023 is one error in 43 characters of expected text. |