Install OneOcr for .NET 10, read the text of an invoice image, walk the page model with its boxes and confidences, turn the page into Markdown, and set a default engine. No native binaries, no tessdata, no network.
Install
OneOcr targets net10.0, so you need the .NET 10 SDK. Every package shares one version; this guide uses 1.0.0.
dotnet new console -o OneOcrStart cd OneOcrStart dotnet add package RecursiveAI.OneOcr --version 1.0.0
RecursiveAI.OneOcr brings in RecursiveAI.OneOcr.Models, which embeds the recognizer, the page detector and the two word models and checks them against their SHA-256 pins on every load. There is nothing to download and no model path to set. Pixels come from OneImage, so any OneImage.Image (PNG, JPEG, WebP, TIFF and so on) is an input. The same weights are also attached to the v1.0.0 release on GitHub for OcrEngine.Load(path) and .WithDetector(path).
The sample images
The guide and the cookbook read two images. invoice.png is a one-page A4 invoice rendered from HTML at 300 DPI (2481 × 3509 pixels), with a seller and buyer block, a table of items and totals. receipt.png is a till receipt rendered at twice screen resolution (920 × 940). Any PNG or JPEG of a flat document works the same way; the outputs below are what 1.0.0 printed for these two.
Read the text
OcrEngine.LoadBundled() loads the bundled models. An engine is immutable and thread-safe: load it once and share it. ToText returns the page in reading order, with lines separated by a newline and table cells by a tab:
using OneImage; using OneOcr; OcrEngine engine = OcrEngine.LoadBundled(); // once at startup; immutable and thread-safe Image image = Image.Load("invoice.png"); Console.WriteLine(image.ToText(engine));
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 Due date Order no. 14/10/2026 13/11/2026 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 Payment terms: Payable within 30 days Subtotal VAT total Total £206.90 £8.88 £215.78 IBAN: GB33 BUKB 2020 1555 5555 55 BIC: BUKBGB22 Tel. +44 117 496 0321
The page detector found the table and the blocks; reading order goes block by block, so the labels of the meta grid and the totals come before their values.
The page model
Ocr runs the same pipeline and returns the whole OcrPage: blocks, lines, words and characters with boxes, tables, key–value pairs and diagnostics. Coordinates are pixels from the top left, y down. A word’s Confidence is the calibrated probability that it is exactly right:
using OneImage; using OneOcr; OcrEngine engine = OcrEngine.LoadBundled(); OcrPage page = Image.Load("invoice.png").Ocr(engine); Console.WriteLine($"{page.Width} x {page.Height}, {page.Blocks.Count} blocks, {page.Words.Count} words, {page.Tables.Count} table"); foreach (OcrWord word in page.Blocks[0].Lines[0].Words) Console.WriteLine($"{word.Text,-10} {word.Confidence:F3} ({word.Box.Left:F0}, {word.Box.Top:F0}) to ({word.Box.Right:F0}, {word.Box.Bottom:F0})"); foreach (OcrDiagnostic diagnostic in page.Diagnostics) Console.WriteLine($"{diagnostic.Code}: {diagnostic.Message}");
2481 x 3509, 19 blocks, 98 words, 1 table Northwind 0.986 (231, 198) to (580, 247) Supply 1.000 (614, 198) to (845, 260) Ltd 0.974 (878, 200) to (963, 247) OCR101: UnitsPerInch is estimated at 200 from the size of the text; pass OcrOptions.Dpi to set it. OCR107: 1 word(s) read from specks, grain or stray marks were dropped as noise; pass OcrOptions.DropNoise = false to keep them.
Diagnostics say what the reader decided on its own. This PNG doesn’t declare a resolution, so OCR101 says it was estimated; pass new OcrOptions { Dpi = 300 } when you know it. It only sets UnitsPerInch: recognition doesn’t depend on it.
Markdown
ToMarkdown builds Markdown from the page model with no model call: headings from text size, lists, and pipe tables from detected tables, with headers, footers and page numbers dropped.
using OneImage; using OneOcr; OcrEngine engine = OcrEngine.LoadBundled(); Console.WriteLine(Image.Load("invoice.png").ToMarkdown(engine));
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 Due date Order no. 14/10/2026 13/11/2026 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 | Payment terms: Payable within 30 days Subtotal VAT total Total £206.90 £8.88 £215.78 IBAN: GB33 BUKB 2020 1555 5555 55 BIC: BUKBGB22 Tel. +44 117 496 0321
A default engine
If you’d rather not pass the engine around, set a process-wide default once. The extension methods without an engine argument then use it; until you call ConfigureDefault, OcrEngine.Default throws InvalidOperationException.
using OneImage; using OneOcr; OcrEngine.ConfigureDefault(); // the bundled models become OcrEngine.Default Console.WriteLine($"Kernels: {OcrEngine.Default.KernelPath}"); string text = Image.Load("receipt.png").ToText(); // no engine argument: the default is used Console.WriteLine(text);
Kernels: Avx512 Corner Market 1402 Oak Street 78704 Austin +1 512 555 0142 Date: 24/10/2026 Time: 14:32 Coffee beans 12.99 2x Oat milk 8.98 Bagels 5.25 Subtotal 27.22 Tax 8.25 % 2.07 TOTAL $ 29.29 Paid by Card **** 4821 Thank you!
KernelPath says which kernels run: Avx512, Avx2, Vector128 or Scalar. The text, boxes and confidences are the same on all four (Same bytes on every machine).
Good to know
- Every local call takes a
CancellationTokenand has anAsynctwin (OcrAsync,ToTextAsync,ToMarkdownAsync) that returns the same result. Ocr(Image)reads one page and throwsArgumentExceptionfor an image with several frames;OcrPagesreads every frame of a TIFF.- Errors are typed:
OcrFormatExceptionfor bytes that aren’t what they claim to be,OcrLimitExceptionwhen anOcrLimitsbound is crossed (checked before anything large is allocated),OcrModelExceptionfor a bad model file. - Scripts: Latin (including the EU languages, Turkish and Vietnamese), Greek and Cyrillic in one recognizer. CJK, Arabic and Indic scripts, handwriting and scene text are not supported yet.
Next
- Invoices as fields: typed fields with EN 16931 ids, each with the words and box it was read from.
- Words to check: use calibrated confidence to find what a person should look at.
- PDF pages: text-layer words and OCR of scanned regions in one page model.
- Moving from Tesseract: commands, modes and outputs side by side.
- Overview and benchmarks: the README, with results against Tesseract, PaddleOCR, docTR and Apple Vision.