PDF extraction
How to Convert PDF to TXT: 4 Developer Methods
Learn how to convert PDF to TXT using command-line tools, Python, or a scalable API. Covers native text extraction, OCR for scans, and batch processing.
You have a PDF in front of you, you need a TXT file now, and the first converter you try gives you either a clean dump of text or complete junk. That split isn't random. It comes from the way PDFs were designed in the first place.
PDF as a format goes back to the PDF 1.0 specification in 1993, and from the start it was built to preserve page layout, not to behave like a plain text file, which is why text extraction became its own technical problem rather than a simple file conversion step, as noted in the LADAL PDF-to-text tutorial. If you treat every PDF the same, you'll waste time. If you classify the file first, the rest of the workflow gets simpler.
For developers, the essential question isn't just how to convert PDF to TXT. It's which extraction path fits the file you have, and whether the output needs to be good enough for indexing, downstream parsing, or customer-facing features.
Table of Contents
- Understanding Your PDF Before You Convert
- Native text PDFs and scanned PDFs
- A fast triage checklist
- The Command Line Method for Quick Local Conversions
- When pdftotext is the right tool
- Useful commands and flags
- Batching a directory
- Programmatic Conversion Using Python
- PyPDF2 for simple extraction
- pdfminer.six for layout-aware parsing
- How to choose between them
- Handling Scanned PDFs with OCR
- Why OCR is a different workflow
- A practical Tesseract pipeline
- What usually breaks OCR output
- An API-First Workflow for Production-Grade Conversion
- Why local scripts stop being enough
- A typical hosted workflow
- What to validate before shipping
- Troubleshooting Common Extraction Problems
- Fixing bad encoding
- Repairing broken line wraps
- Dealing with columns tables and page boundaries
Understanding Your PDF Before You Convert
The first step is inspection, not conversion. Before you write code or install tools, find out whether the PDF already has a text layer.
Native text PDFs and scanned PDFs
A native text PDF usually comes from software like Word, Google Docs, an invoicing system, or a browser print flow. The characters exist as embedded text objects inside the file. In that case, extraction is mostly about reading those objects and cleaning up line breaks, hyphenation, and reading order.
A scanned PDF is different. It's often a page image wrapped in a PDF container. You see words on the page, but the file may contain no selectable text at all. Adobe's guidance is straightforward: if text can be highlighted or copied, direct extraction is usually the fastest route; if not, OCR is required. Treating a scanned PDF like a text PDF often produces empty or garbled output, as Adobe explains in its guide on exporting PDFs to plain text.
Practical rule: If you can't drag your cursor across the words and copy them, don't start with a text extractor. Start with OCR.
A fast triage checklist
Use this before picking a tool:
- Try selection first: Open the PDF and highlight a sentence. If selection works cleanly, start with direct extraction.
- Check for weird copy behavior: If copied text comes out in the wrong order, the PDF has a text layer, but layout reconstruction may still be messy.
- Zoom in on page quality: Blurry edges, shadows, skew, and dark backgrounds usually mean OCR will need cleanup.
- Look for mixed documents: A single PDF can contain both native text pages and scanned pages. These are common in legal packets, bank exports, and assembled reports.
That distinction drives everything that follows. pdftotext, PyPDF2, and pdfminer.six work best when text is already present. Tesseract and other OCR tools exist because many PDFs aren't really text documents at all.
The Command Line Method for Quick Local Conversions
If you just need a fast local conversion, the terminal is still the shortest path. For native text PDFs, pdftotext is the default tool I reach for before writing code.

When pdftotext is the right tool
pdftotext is part of the Poppler toolset and works well for quick extraction on your machine. It's good for ad hoc work, shell scripts, and preprocessing files before a larger pipeline.
Use it when:
- You have selectable text: This is its sweet spot.
- You want no app code yet: Great for debugging PDFs before committing to a library.
- You need batch conversion: Modern conversion workflows increasingly emphasize batching and throughput. Vendor guidance also reflects that shift, with tools supporting multiple file uploads at once, while page-oriented extraction patterns are common in programmatic pipelines. The LADAL example notes that
pdf_text(pdf_path)returns one element per page, so a 200-page PDF produces 200 text units to process, which is a useful mental model for large jobs, as summarized in this discussion of batch and page-level extraction workflows.
Useful commands and flags
Basic conversion:
pdftotext input.pdf output.txt
Write to stdout instead of a file:
pdftotext input.pdf -
Try to preserve visual placement a bit more:
pdftotext -layout input.pdf output.txt
Extract a page range:
pdftotext -f 3 -l 7 input.pdf output.txt
A few notes from actual use:
| Option | When to use it | Trade-off |
|---|---|---|
-layout | Reports, statements, forms | Keeps spacing better, but can add awkward gaps |
-f / -l | Spot-checking or partial extraction | Useful for debugging page-specific issues |
| stdout output | Piping into grep, sed, awk | Good for Unix workflows, harder to inspect later |
If
pdftotextreturns almost nothing, don't keep tweaking flags for an hour. That's usually the moment to verify whether the PDF is image-only.
Batching a directory
For a folder full of PDFs:
mkdir -p txt
for f in pdfs/*.pdf; do
base=$(basename "$f" .pdf)
pdftotext -layout "$f" "txt/$base.txt"
done
That gets you a repeatable local workflow with almost no overhead. It won't solve scanned files, and it won't magically fix columns or tables, but for native PDFs it's hard to beat for speed.
Programmatic Conversion Using Python
Once you need repeatable extraction inside an app or a data pipeline, Python is the practical choice. The two libraries I'd compare first are PyPDF2 and pdfminer.six. They solve the same broad problem, but they don't behave the same on difficult documents.

PyPDF2 for simple extraction
PyPDF2 is the lightweight option. If the PDF is straightforward and you mainly want to rip text page by page, it's enough.
from PyPDF2 import PdfReader
reader = PdfReader("input.pdf")
all_text = []
for page in reader.pages:
text = page.extract_text() or ""
all_text.append(text)
result = "\n\n".join(all_text)
with open("output.txt", "w", encoding="utf-8") as f:
f.write(result)
What this does well:
- Simple pages: Letters, contracts, invoices with clean text layers
- Low setup cost: Easy to drop into a script or API handler
- Page iteration: Natural for page-by-page validation
Where it struggles:
- Complex layouts: Multi-column reports and dense statements can come back in the wrong reading order
- Spacing and line breaks: You often need post-processing
- Font oddities: PDFs with unusual internal structure can produce inconsistent results
pdfminer.six for layout-aware parsing
If PyPDF2 feels too rough, pdfminer.six usually gives you more control. It's a better fit when the PDF contains columns, tighter positioning, or strange reading order.
from pdfminer.high_level import extract_text
text = extract_text("input.pdf")
with open("output.txt", "w", encoding="utf-8") as f:
f.write(text)
For many engineering teams, that one function gets you farther than expected. When you need more control, you can move into layout parameters.
from pdfminer.high_level import extract_text
from pdfminer.layout import LAParams
laparams = LAParams(
line_margin=0.4,
word_margin=0.1,
char_margin=2.0
)
text = extract_text("input.pdf", laparams=laparams)
with open("output.txt", "w", encoding="utf-8") as f:
f.write(text)
The trade-off is obvious once you use both libraries side by side:
| Library | Best for | Weak point |
|---|---|---|
| PyPDF2 | Fast implementation on simple PDFs | Limited layout handling |
| pdfminer.six | Better control over reading order and spacing | More tuning and more complexity |
For teams building document ingestion, this difference matters more than library popularity. The right tool depends on the structure of the PDFs you receive, not on which package has the friendlier quickstart.
A deeper walkthrough of Python-based extraction patterns is in this OkraPDF guide on extracting data from PDF with Python.
One more useful reference before you wire this into production:
<iframe width="100%" style="aspect-ratio: 16 / 9;" src="https://www.youtube.com/embed/G0PApj7YPBo" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
How to choose between them
Use PyPDF2 when you want the minimum amount of code and the PDFs are predictable.
Use pdfminer.six when any of these are true:
- The pages use columns: Annual reports and research PDFs often do.
- Whitespace carries meaning: Statements, schedules, and forms often rely on alignment.
- You care about text order: Search indexing and downstream NLP break quickly when lines are interleaved.
Good extraction code doesn't just write
output.txt. It also keeps enough page context that you can debug where the text came from.
Handling Scanned PDFs with OCR
Most PDF-to-TXT guides tend to be too casual on this point. A scanned PDF isn't missing a convenient export option. It's missing text.
Why OCR is a different workflow
OCR means optical character recognition. Instead of reading embedded text objects, the engine looks at page images, identifies glyphs, and reconstructs text from visual patterns. That makes it the correct method for scans, photos, faxed documents, and mixed PDFs with image-only pages.
Google Drive's OCR guidance is useful because it states the constraints plainly. Its conversion works best for files under 2 MB, with text at least 10 pixels high, on right-side-up documents with common fonts, according to Google's OCR support documentation for Drive. That's a good reminder that accuracy depends heavily on document conditions, not just the OCR engine you choose.

A practical Tesseract pipeline
For a local Python workflow, Tesseract plus pytesseract is the usual starting point. One common path is to render PDF pages to images, then OCR those images.
import pytesseract
from PIL import Image
image = Image.open("page.png")
text = pytesseract.image_to_string(image, lang="eng")
with open("output.txt", "w", encoding="utf-8") as f:
f.write(text)
If your source is a scanned PDF, the pipeline usually looks like this:
- Render each PDF page to an image.
- Preprocess the image.
- Run OCR with the correct language.
- Join page outputs into one TXT file.
- Spot-check fields that matter.
HiPDF's OCR instructions make one operational point that many teams miss: choose the document language before recognition, because OCR quality depends on language settings and layout assumptions, as described in its guide on converting scanned PDFs to text with OCR.
If your use case involves receipts and expense flows, this Booksmate post on automate receipts with OCR is worth reading because it focuses on the practical messiness of real-world scanned documents.
What usually breaks OCR output
The OCR engine often isn't the main problem. The page image is.
- Skewed pages: Even a slight rotation can hurt line recognition.
- Low contrast: Gray text on gray paper causes misses.
- Wrong language selection: Accents, punctuation, and character shapes drift.
- Multi-column layouts: OCR may read across columns instead of down them.
- Tables and headers: The text may be recognized, but structure usually won't survive.
Basic preprocessing helps a lot. Convert to grayscale, boost contrast, crop noisy borders, and deskew before running OCR. If you need a browser-based option for experiments, OkraPDF has an OCR tool for PDFs.
OCR speed and OCR quality aren't the same thing. Fast output is easy to get. Trustworthy output takes validation.
An API-First Workflow for Production-Grade Conversion
Local tools are great until your app starts ingesting mixed PDFs from users. Then the problem changes. You no longer need a clever script. You need a service boundary, predictable output, and a way to handle native and scanned documents without branching logic all over your codebase.

Why local scripts stop being enough
A production system usually has requirements that a local CLI workflow doesn't solve cleanly:
- Mixed file quality: Users upload exports, scans, screenshots, and assembled PDFs in the same feed.
- Concurrency: Multiple documents need processing at once.
- Observability: You need logs, retries, and file-level debugging.
- Security decisions: Some teams can't keep sending documents through random browser tools.
This is also where "how to convert PDF to TXT" stops being the full problem. Many teams need page mapping, structured extraction, or a reliable document URL they can pass between services.
For background on the broader platform side of this problem, this OkraPDF article on a document processing platform is useful.
A typical hosted workflow
A clean pattern is:
- Upload the PDF to a hosting layer.
- Get a stable URL for the file.
- Send that URL into your extraction step.
- Store the text result with page metadata if possible.
- Run validation on a sample before indexing or parsing.
If you're building this internally, your curl flow might look like this conceptually:
curl -X POST "https://your-api.example/upload" \
-F "file=@input.pdf"
Then pass the returned file URL into extraction:
curl -X POST "https://your-api.example/extract-text" \
-H "Content-Type: application/json" \
-d '{
"file_url": "https://files.example.com/input.pdf"
}'
And in JavaScript:
const uploadRes = await fetch("/upload", {
method: "POST",
body: formData
});
const { fileUrl } = await uploadRes.json();
const extractRes = await fetch("/extract-text", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ file_url: fileUrl })
});
const data = await extractRes.json();
console.log(data.text);
I'm intentionally keeping this section architecture-first rather than vendor-specific. The important design choice is separating file hosting, extraction, and post-processing so each step is debuggable.
What to validate before shipping
Don't accept extraction output just because the API returned a 200.
Use a small validation checklist:
- Compare page count to extracted units: Missing pages are easier to catch early.
- Spot-check numeric fields: IDs, dates, totals, and account numbers tend to expose OCR or ordering issues fast.
- Test mixed batches: Native PDFs and scans should both pass through the same system without manual triage.
- Keep the raw source: When text quality is challenged later, you need the original document.
For teams that need the simplest hosted path, a file hosting endpoint that gives you a shareable PDF link can be useful even before you solve extraction. That pattern is especially handy when you're wiring background jobs, webhook flows, or internal tools.
Troubleshooting Common Extraction Problems
Getting text out is only step one. The next problem is making the TXT file usable.
Fixing bad encoding
If your output contains mojibake like •, the text may have been decoded with the wrong character encoding at some stage after extraction.
In Python, normalize explicitly:
with open("output.txt", "r", encoding="utf-8", errors="replace") as f:
text = f.read()
with open("cleaned.txt", "w", encoding="utf-8") as f:
f.write(text)
If you're consuming text from multiple systems, log where decoding happens. A lot of encoding bugs don't originate in PDF parsing at all. They happen when text is re-saved, uploaded, or passed through legacy systems.
Repairing broken line wraps
Extracted text often breaks lines in the middle of sentences because the PDF stores page layout, not paragraph semantics.
A simple cleanup pass can help:
import re
with open("output.txt", "r", encoding="utf-8") as f:
text = f.read()
text = re.sub(r"-\n(?=\w)", "", text)
text = re.sub(r"(?<!\n)\n(?!\n)", " ", text)
text = re.sub(r"\n{3,}", "\n\n", text)
with open("normalized.txt", "w", encoding="utf-8") as f:
f.write(text)
That script removes line-end hyphenation, joins single line breaks into spaces, and preserves paragraph breaks.
Some PDFs don't need a better extractor. They need a cleanup pass designed for the way that document class wraps text.
Dealing with columns tables and page boundaries
This is the hard part. Plain text doesn't preserve structure. Tables flatten. Columns merge. Page boundaries disappear.
PDF Suite explicitly notes that TXT is continuous text and offers page dividers to mark page breaks, while Adobe suggests converting to Word first when layout matters, as described in PDF Suite's guide on converting PDF to TXT with page dividers. That advice matches real-world experience.
A few practical responses:
- For columns: Use a layout-aware extractor like pdfminer.six before trying regex cleanup.
- For tables: Don't force TXT if the actual goal is structured data. Use a table extraction workflow instead.
- For page mapping: Insert explicit page markers during extraction so downstream systems can trace text back to the source page.
- For legal or review workflows: If page fidelity matters, keep a parallel representation such as Word, JSON, or page images.
If the text order is irreparably wrong, stop polishing the TXT file and change the extraction method. Cleanup should refine output, not rescue the wrong pipeline.
If you're building PDF ingestion into a product, OkraPDF is worth a look for two practical pieces of the stack: free PDF hosting so you can upload a file and get a shareable link, and extraction workflows for teams that need more than a one-off converter.