Case Study: See how Formable built DOCX redlining with Apryse in two months

Extract Text from PDFs Programmatically

Reading order and layout recovery decide whether extracted PDF text is usable. Smart Data Extraction pairs a deterministic structure foundation with purpose-built AI for key-value extraction and classification. It is part of Apryse document extraction and parsing, and it runs inside your own infrastructure.

How to Extract Text from a PDF Programmatically

Reading order and layout recovery decide whether text pulled from a PDF is usable. A PDF has no concept of a paragraph, sentence, or table, so every extractor infers structure from where text sits on the page. To extract text programmatically, initialize PDFNet with your license key, open the file with the Apryse Server SDK, and read each page with TextExtractor. That step belongs to the base Server SDK, runs on Windows, Linux, and macOS, and returns Unicode text with word and line positions. When your code needs headers, footers, lists, and tables identified as separate blocks, add Smart Data Extraction. It returns JSON with page numbers and bounding rectangles, needs a separate add-on license, and runs on Windows and Linux (x64 only). A scanned PDF has no text layer, so the OCR Module, also a separate add-on, creates one first. All of it runs inside your own infrastructure.

Why Reading Order Decides Whether Extracted Text is Usable

Copied to clipboard

A PDF stores positioned text runs, not paragraphs. The ISO PDF standard does not define reading order, and a typical PDF has no concept of a sentence, paragraph, or table. The run order follows the program that wrote the file, not the order a person reads.

Three layouts expose the gap: multi-column pages, running headers and footers, and tables set inside running text. Reading order also changes with document type. A magazine page, a newspaper article, and an academic paper each ask for a different order.

Each failure carries into whatever consumes the text. A language model that ingests interleaved columns reads sentences the author never wrote. A search index that stores footers inside paragraphs returns the wrong passages. A review screen that highlights the wrong region teaches your users to distrust it.

Three separate jobs sit under the phrase text extraction. Reading the text layer, with words, lines, paragraphs, and positions, is a base Server SDK operation. Recovering structure, meaning which blocks are headers, footers, lists, and tables, is the job of Smart Data Extraction, an add-on that handles document structure recovery. Generating a text layer where none exists is OCR, a separate add-on. Scope them separately.

The structure foundation is deterministic: the same PDF produces the same recovered structure on every run. Purpose-built AI handles the fields that need interpretation, such as key-value pairs and page classification.

Tables inside running text are a parsing problem with their own method. This page stops at that boundary, and document parsing covers tables.

Extracting the Text Layer

Copied to clipboard

This section covers Apryse Server SDK integration using C#, Java, and Python 3 on Windows and Linux. For other languages or platforms, see the Server SDK get-started guide.

TextExtractor is part of the base Server SDK. It needs no add-on module and no add-on license. It normalizes text to Unicode, assembles words, lines, and paragraphs, removes duplicated strings, and reconstructs reading order. The text extraction guide states the limit: the reconstructed order is not guaranteed to match the order a person reading the page would follow.

You need a trial key from dev.apryse.com and a call to PDFNet.initialize() before any extraction call. It sets up the library, registers your license key, and runs once per process. The call name changes by binding, as the table shows.

Getting Started with Text Extraction

Copied to clipboard
Language
Install the Base SDK
Initialize PDFNet
Get-Started Guide
C# (.NET 8 and 9)
Download PDFNetC64.zip, extract it, and reference it from your project.
pdftron.PDFNet.Initialize ("YOUR_APRYSE_LICENSE_KEY");
Java
Add the Maven dependency with groupId com.pdftron, or download PDFNetJava.zip. Requires JDK 25.
PDFNet.initialize ("YOUR_APRYSE_LICENSE_KEY");
Python 3
pip3 install apryse-sdk --extra-index-url=https://pypi.apryse.com
PDFNet.Initialize ("YOUR_APRYSE_LICENSE_KEY")
Node.js
npm i @pdftron/pdfnet-node
PDFNet.runWithCleanup(main, "YOUR_APRYSE_LICENSE_KEY")

Keep the license key out of source control. The examples read it from the APRYSE_LICENSE_KEY environment variable.

Text Extraction Code Examples

Text Extraction in C#

Copied to clipboard

Reference the extracted PDFNetC64 files from your project. The program initializes PDFNet once, reads page 1, and releases PDFNet in a finally block.

Text Extraction in Java

Copied to clipboard

Call doc.close() after each document and PDFNet.terminate() at shutdown. The native library must be on the Java library path when you run the class, or the JVM throws an UnsatisfiedLinkError.

Text Extraction in Python 3

Copied to clipboard

Install from the Apryse package index, not public PyPI. The legacy PDFNetPython3 package on public PyPI is no longer updated.

Expected Output

Copied to clipboard

Run against a sample contract, each program prints page 1 as plain text. Words are separated by spaces or new lines:

The footer arrives as one more line. Plain text tells you what was written on the page, not which block is a heading, a body paragraph, or a footer.

The full working reference is the text extraction sample, and the classes are listed in the extraction API reference.

At volume, initialize once per worker and keep PDFNet alive for the life of the process. The Server SDK has no work queue, job scheduler, or batch orchestration, so your code owns the loop. Open one PDFDoc per thread or lock a shared document with the documented Lock and LockRead calls.

Extracting Text with Position and Layout Data

Copied to clipboard

The base TextExtractor already returns positions. Every line and word carries a bounding box and style, and getAsXML returns paragraphs, lines, and words as XML when you pass the flags below.

A bounding box tells you where text sits but not what a block is. Smart Data Extraction answers that, and it returns each block with its page number and bounding rectangle as JSON.

This feature requires the Smart Data Extraction add-on module, which is a separate download from the base Server SDK. See the modules documentation for download and installation instructions.

The module needs an add-on license. Trial keys include it, but trial output is limited to 100 pages and a random evaluation page is inserted into the output. Once you license the module, neither limit applies.

Install Smart Data Extraction by language

Copied to clipboard
Language
Install Smart Data Extraction
Register with the SDK
Node.js
npm install @pdftron/data-extraction
Call PDFNet.addResourceSearchPath() with the package lib folder after initializing.
Python
pip install --extra-index-url=https://pypi.apryse.com apryse-data-extraction
Import apryse_data_extraction. The import registers the module.
Java
Download the module archive, or add the DataExtractionModuleWindows or DataExtractionModuleLinux Maven dependency (groupId com.pdftron).
Call PDFNet.addResourceSearchPath() with the module Lib folder after initializing.
C#
Download the Windows or Linux module archive and extract it into your project. Keep the Lib folder intact.
Call PDFNet.AddResourceSearchPath() with the module Lib folder after initializing.
C++
Extract the module archive into the SDK directory.
None. The SDK finds modules placed next to its own library.

A DataExtractionTest sample application ships in the main SDK download. The programs below follow the same pattern: initialize PDFNet, register the module, confirm it is available, then extract.

Document Structure as JSON

Document Structure as JSON in C#

Copied to clipboard

Document Structure as JSON in Java

Copied to clipboard

Document Structure as JSON in Python 3

Copied to clipboard

Expected JSON Output

Copied to clipboard

Each program prints the same structure. The excerpt below is trimmed to page 1 of the sample contract. Every element carries a type and a rect of the form [left, top, right, bottom] in PDF points, and each page object carries its pageNumber. The root object also holds document properties: the coordinate system, the producer, and the schema version. The JSON specification defines every element type.

Your application uses the page number and rect to overlay or annotate the source page, to route a paragraph to a reviewer, or to cite the page a passage came from. The same JSON feeds semantic search and RAG preprocessing.

Confidence scores differ by mode. Form field identification, key-value extraction, CAD title block extraction, and document classification each return a confidence score. Table extraction does not, so treat table output as unscored.

Apryse supplies the extraction building blocks. Your application owns the pipeline around them, including queues, review, and storage.

Multi-Column Pages and Reading Order

Copied to clipboard

Multi-column pages, sidebars, and running headers break order-based extraction first. The base TextExtractor reconstructs reading order from word and line positions, and the e_extract_using_zorder flag switches it to the order the content was drawn in. Both return text. Neither labels a block.

Smart Data Extraction recognizes the page the way a person sees it. Its Document Structure Recognition engine separates paragraphs and lists, headers and footers, section columns from table columns, and tables embedded inside paragraphs. It reads visual positioning, spacing, indentation, and structural boundaries to do so. Your code receives blocks instead of rebuilding them from coordinates.

SDK 12.0 added Markdown output to the Document Structure API. It carries paragraphs in reading order, heading levels 1 through 6, nested lists, and tables into the format RAG and LLM pipelines ingest. The output is data for a pipeline, not a visual reproduction of the page. It runs on Windows and Linux in every supported SDK language and is included in an existing Smart Data Extraction license.

What this does not cover: Smart Data Extraction does not convert PDFs to Word, Excel, or PowerPoint. The Structured Output Module does that.

When a PDF Has No Text Layer

Copied to clipboard

A scanned PDF stores each page as an image. TextExtractor analyzes textual content only, so rasterized text in a scan, and vectorized text where glyphs are converted to outlines, returns nothing. If a page shows words on screen and extraction returns an empty string, the page is an image.

OCR generates the missing text layer. The OCR Module is a separate add-on module with its own license. Run it first, then extract. OCR text is stored as invisible text, and TextExtractor extracts invisible text by default. The OCR capability page covers engines and setup, so this page does not repeat them.

For the full pipeline from scan to usable text:

Encoding, Ligatures, and Character Edge Cases

Copied to clipboard

A PDF font maps glyph codes to characters. When the mapping is complete, extraction returns the characters you see. When it is missing or wrong, any extractor that reads the text layer hits the same limit: the glyph is on the page, and the character is not recoverable from the file.

TextExtractor normalizes all text to Unicode and expands ligatures by default. The default mapping covers fi, ff, fl, ffi, ffl, ch, cl, ct, ll, ss, fs, st, oe, and OE, so a fi glyph extracts as two letters and search matching stays consistent. Pass the e_no_ligature_exp flag to keep ligature characters as they are.

Hidden and duplicated text get the same treatment. The extractor removes duplicated strings that generators use to fake bold or drop shadows, and it extracts invisible text. Flags change each default:

Flag
What It Does
Default Without the Flag
e_no_ligature_exp
Keeps ligature glyphs unexpanded.
Ligatures expand to letters.
e_no_dup_remove
Keeps duplicated text used for drop shadow and fake bold.
Duplicated text is removed.
e_remove_hidden_text
Removes text obscured by images or rectangles.
Obscured text is extracted.
e_no_invisible_text
Removes invisible text (rendering mode 3).
Invisible text is extracted.
e_extract_using_zorder
Uses drawing order as reading order.
Reading order is reconstructed.

Extraction Flags in Code

Two more options matter for prose. Passing true for the dehyphen argument of the text call removes hyphens that split a word across two lines, and it has no effect on Tagged PDF files. Right-to-left documents need the extractor set to right-to-left mode before you read the page.

Test with the fonts your documents use. Extract one page from each source system and compare the strings with the rendered page before you scale out.

Language and Platform Support

Copied to clipboard

The Server SDK exposes text extraction through native bindings for C#, Java, Python 3, Node.js, and C++. Each has its own get-started guide:

Get Started in Your Language

Sanity Image
Sanity Image
Sanity Image
Sanity Image

Step-by-step code in one language

Base text layer extraction:
Copied to clipboard

Windows, Linux, and macOS. Windows support is x64 only (AMD64), and Windows on ARM is not supported.

Smart Data Extraction:
Copied to clipboard

Windows and Linux, x64 only. ARM and 32-bit platforms are not supported, and macOS is not supported.

Deployment:
Copied to clipboard

On-premises server, cloud VM, container, or Kubernetes. The SDK processes documents locally, so no document content leaves your environment.

For step-by-step code in one language, read the walkthroughs:

Extract text from a PDF in Java

Extract text from a PDF in Node.js

Extract text from a PDF in C# and .NET

Evaluation Notes for Your Technical Lead

A single-column text layer is a reasonable job for an open-source library. The commercial case rests on recovering structure from messy layouts, which is where plain text extraction stops.

Sanity Image

Deployment model:

A self-hosted library embedded in your application. Document content is never transmitted to Apryse.

Licensing:

The base license covers text layer extraction. Smart Data Extraction and the OCR Module each need an add-on license.

Scope:

Text, word and line positions, document structure, and bounding rectangles. Conversion to Word, Excel, or PowerPoint is a different module.

Ownership:

Your code owns queues, scheduling, retries, and storage. The SDK provides no work queue, scheduler, or batch orchestration.

Frequently Asked Questions

No. TextExtractor, which returns Unicode text with word and line positions, is part of the base Server SDK license. Smart Data Extraction, which returns document structure as JSON or Markdown, requires its own add-on license. OCR for scanned PDFs requires the OCR Module add-on.

Base extraction returns plain text, or XML with bounding boxes and style when you pass the output flags. Smart Data Extraction returns JSON with a page number and bounding rectangle for each element, and SDK 12.0 adds Markdown output from the Document Structure API. Choose text for simple layouts and JSON when your code needs to know what each block is.

Start with TextExtractor, which reconstructs reading order from word and line positions. For columns, headers, footers, lists, and tables, use Smart Data Extraction, which recognizes them as separate blocks, and its Markdown output carries paragraphs in reading order. The PDF standard does not define reading order, so test on your own documents.

Extraction returns no text for a page that is only an image. The OCR Module, a separate add-on, creates the text layer, and you extract after it runs. The OCR capability page covers setup.

The Server SDK supports C# (.NET), Java, Python 3, Node.js, and C++. Base extraction runs on Windows, Linux, and macOS, and Smart Data Extraction runs on Windows and Linux, x64 only. See the per-language guides for step-by-step code.

Run text extraction on your own PDFs