Pass the PDF to PdfOcr.Read: each page comes back with its text, read with OCR where the page is a scan. The program below reads a scanned invoice, a PDF with no text layer that you can download to try it.
dotnet new console -o OcrPdf && cd OcrPdf dotnet add package RecursiveAI.OneOcr --version 1.0.0 dotnet add package RecursiveAI.OneOcr.Pdf --version 1.0.0 curl -O https://recursiveai.org/oneocr/samples/scanned-invoice.pdf
Put this in Program.cs and run dotnet run:
using OneOcr; using OneOcr.Pdf; OcrEngine engine = OcrEngine.LoadBundled(); IReadOnlyList<OcrPage> pages = PdfOcr.Read("scanned-invoice.pdf", engine); foreach (OcrPage page in pages) { Console.WriteLine($"--- page {page.Index + 1}, {page.Words.Count} words"); Console.WriteLine(page.ToText()); }
--- page 1, 99 words Northwind Supply Ltd 14 Harbour Street BS1 4DJ Bristol United Kingdom VAT ID: GB284193752 INVOICE Bill to Blue Heron Bakery - 88 Elm Road OX1 2JD Oxford United Kingdom Invoice no. INV-2041 Date 14/10/2026 Due date 13/11/2026 Order no. PO-7731 Description Qty Unit price VAT Amount Flour, 25 kg bag 4 pcs 31.50 0% 126.00 Cane sugar, 10 kg 2 pcs 18.25 0% 36.50 Baking parchment roll 6 pcs 7.40 20% 44.40 Subtotal VAT total Total £206.90 £8.88 £215.78 Payment terms: Payable within 30 days IBAN: GB33 BUKB 2020 1555 5555 55 BIC: BUKBGB22 Tel. +44 117 496 0321
The scan is a little tilted and grainy; the page was straightened before it was read, and one speck came through as the - before “88 Elm Road”. Each page is an OcrPage, the same model you get from an image, with coordinates in PDF points.
Pages with both text and scans
Many PDFs mix real text with scanned images. The text layer is used where it exists and only the images are read with OCR, so every word says where it came from. This expense report has a typed paragraph and a photo of a receipt:
curl -O https://recursiveai.org/oneocr/samples/expense.pdfusing OneOcr; using OneOcr.Pdf; OcrEngine engine = OcrEngine.LoadBundled(); OcrPage page = PdfOcr.Read("expense.pdf", engine)[0]; foreach (var group in page.Words.GroupBy(w => w.Source)) { string start = string.Join(" ", group.Take(7).Select(w => w.Text)); Console.WriteLine($"{group.Key}: {group.Count()} words: {start} ..."); } foreach (OcrDiagnostic d in page.Diagnostics) Console.WriteLine($"{d.Code}: {d.Message}");
TextLayer: 26 words: Expense report EX-118 Submitted by Dana Whitfield ... Ocr: 49 words: Corner Market 1402 Oak Street 78704 Austin ... OCR301: 1 regions were read from pixels at 300 DPI (49 words).
TextLayer words are copied from the PDF; Ocr words were read from the image, rendered at 300 DPI. A text layer isn’t trusted blindly: an invisible layer left by an earlier OCR pass is read again, and garbled lines are checked against a render. PDF pages has the rules.
Options
new PdfOcrOptions { … } as the third argument sets RenderDpi (300), FirstPage and PageCount to read part of a file, Password for an encrypted PDF, and TrustExistingOcrLayer to keep an earlier OCR pass as it is. If you already have a PdfDocument open in OnePDF, doc.Ocr(engine) and doc.ToText(engine) do the same.
Limits
- It reads; it doesn’t write. You get the text and the words with their positions, not a new searchable PDF.
- Printed Latin, Greek and Cyrillic text. No CJK, Arabic or Indic scripts, and no handwriting, yet.
- Text drawn as outlines and annotations aren’t read.
For a single image, see Extract text from an image. To pull fields out of a scanned invoice, see Extract data from an invoice.