Recursive AI

Extract text from a PDF

All onepdf recipes

Read the text of every page of a PDF in C# in reading order, leave out running headers and footers, and keep the page layout when you need it, with OnePDF.

Terminal
dotnet add package RecursiveAI.OnePdf --version 1.11.0
C#
using OnePdf;
using OnePdf.Text;
 
using PdfDocument doc = PdfDocument.Open("report.pdf");
 
// Whole document: pages are read in parallel, and running headers and footers are found by their repetition.
var options = new PdfTextOptions
{
    Mode = PdfTextMode.ReadingOrder,       // the default; also ContentOrder and Layout
    ExcludeHeadersAndFooters = true,       // leave repeated headers and footers out of the text
};
IReadOnlyList<PdfPageText> pages = doc.ExtractText(options);
 
using (StreamWriter output = File.CreateText("report.txt"))
{
    for (int i = 0; i < pages.Count; i++)
    {
        output.WriteLine($"--- page {i + 1}");
        output.WriteLine(pages[i].Text);
    }
}
 
// One page, keeping the arrangement on the page, like pdftotext -layout.
PdfPageText page = doc.Pages[0].ExtractText(new PdfTextOptions { Mode = PdfTextMode.Layout });
Console.WriteLine(page.Text);

report.txt gets each page’s text, columns read one after the other. The console gets the first page in layout mode:

Text
Week       1
 
Prepared  by  Jane Doe,  account  4471-2290.  The harbor  trial ran for six weeks. Each crew logged its
...
Left column: crews on the north quay. Left column:  Right column: crews on the south quay. Right column:
crews on the north quay. Left column: crews on the  crews on the south quay. Right column: crews on the

The three modes

  • ReadingOrder (the default) orders blocks by the structure tree when the file is tagged and the tree covers the page, and by the page’s geometry otherwise. Blocks are separated by a blank line and lines by a line feed.
  • ContentOrder keeps the order in which the content stream draws the glyphs.
  • Layout puts words on a monospace grid that keeps the page’s arrangement, like pdftotext -layout.

pageText.GetText(PdfTextMode.ContentOrder) gives a page’s text in another mode, worked out from the same page while the document is still open.

Headers and footers

Whole-document extraction flags a block as a header or footer when it sits in the top or bottom 12% of the page and the same text appears there on at least half of the pages. Digits are ignored when comparing, so “Page 3 of 6” counts, and so does a “Week 3” title that opens every page: in the run above, ExcludeHeadersAndFooters dropped the “Week” titles along with the running header and the page numbers. Check the result before you exclude. Each block’s Role tells you what was flagged. Single-page page.ExtractText() doesn’t flag anything, because it has only one page to compare.

Good to know

  • Ligatures are split into letters, and Arabic and Hebrew presentation forms are folded to their base characters. Text marked with /ActualText replaces what it covers.
  • Invisible text, such as the text layer an OCR tool adds to a scan, is included and flagged: word.IsVisible is false.
  • JoinHyphenatedWords = true joins a word split by a hyphen at the end of a line.
  • Long documents stay small in memory: whole-document extraction doesn’t keep each page’s glyphs unless you set RetainGlyphs.
  • OnePDF does no OCR. A scanned page without a text layer gives no text.

For word boxes, see Get words and their positions. Every threshold of the layout analysis is in Text layout and reading order, and the versioned JSON output is in Extraction JSON.

For a longer walk-through with an ASP.NET Core upload endpoint and a comparison with PdfPig and iText, see Extract text from PDF in C#.

To pull out the embedded pictures as well: Extract images from a PDF in C#.