OCR the image with Ocr, then read page.Tables: each table has its rows, columns and cells, and each cell lists the words in it. The program below turns the biggest table on a sample invoice, its items, into CSV.
dotnet new console -o TableFromImage && cd TableFromImage dotnet add package RecursiveAI.OneOcr --version 1.0.0 curl -O https://recursiveai.org/oneocr/samples/invoice.png
Put this in Program.cs and run dotnet run:
using OneImage; using OneOcr; OcrEngine engine = OcrEngine.LoadBundled(); OcrPage page = Image.Load("invoice.png").Ocr(engine); OcrTable table = page.Tables.MaxBy(t => t.Rows * t.Columns)!; Console.WriteLine($"{page.Tables.Count} tables; the biggest is " + $"{table.Rows} rows x {table.Columns} columns"); var grid = new string[table.Rows, table.Columns]; foreach (OcrTableCell cell in table.Cells) grid[cell.Row, cell.Column] = string.Join(" ", cell.WordIndices.Select(i => page.Words[i].Text)); for (int r = 0; r < table.Rows; r++) { var row = Enumerable.Range(0, table.Columns).Select(c => grid[r, c] ?? ""); Console.WriteLine(string.Join(",", row.Select(Csv))); } static string Csv(string s) => s.Contains(',') || s.Contains('"') ? $"\"{s.Replace("\"", "\"\"")}\"" : s;
2 tables; the biggest is 4 rows x 5 columns 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
The other table is the grid of invoice number and dates at the top right: aligned label–value pairs count as a table too. A cell’s WordIndices point into page.Words, so each cell also has its words’ boxes and confidences. Cells that span several rows or columns say so in RowSpan and ColumnSpan.
The table as Markdown
ToMarkdown writes the whole page as Markdown, with detected tables as pipe tables and headings from text size. Running headers and footers are left out, which is why the bank details at the foot of the page are missing. It is built from the page model, with no extra model call:
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 | 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
Limits
- Ruled and aligned tables. Tables are found from the layout of the words and lines; a table with no lines and ragged columns may come back as plain text blocks.
- The first row isn’t marked as a header. In CSV it is just the first row; in Markdown the first row becomes the header.
- Printed Latin, Greek and Cyrillic text, as for all of OneOcr.
To get the line items of an invoice as typed values instead of cells, see Extract data from an invoice. The rules for tables are in Table structure.