PDF to Text with Python
A step-by-step tutorial for converting entire PDF documents into plain text using the NextOCR API — including scanned books, historical Khmer documents, and multilingual PDFs.
1. How it works
PDF files cannot be sent directly to the OCR API — the API accepts images. The workflow is:
PDF → convert each page to PNG image → send each image to NextOCR API → merge all text → save to
output.txt
The script below automates the whole process in a single command, including automatic repair of corrupted PDFs using Ghostscript.
2. Requirements
Python packages:
pip install requests pdf2image
System dependencies:
-
Poppler — required by
pdf2imageto render PDF pages
sudo apt install poppler-utils(Ubuntu/Debian) ·brew install poppler(macOS) -
Ghostscript (optional) — used to auto-repair corrupted PDFs
sudo apt install ghostscript(Ubuntu/Debian) ·brew install ghostscript(macOS)
You also need a NextOCR username and secret key. To get your personal access key, please contact:
3. Download the script
Save the following file as nextocr_pdf2text.py:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
NextOCR PDF to Text - All-in-one CLI tool
Convert a PDF file to text using the NextOCR API.
Usage:
python3 nextocr_pdf2text.py 'username' 'secretkey' 'file.pdf' 'output.txt'
"""
import os
import sys
import json
import time
import shutil
import tempfile
import subprocess
import requests
from pdf2image import convert_from_path, pdfinfo_from_path
from pdf2image.exceptions import PDFPageCountError
OCR_API_URL = "https://developer.nextocr.org/ocr_api"
TIMEOUT = 120 # seconds
def call_ocr_api(image_path, username, secret_key):
"""Send one image to the NextOCR API and return recognized text."""
try:
with open(image_path, "rb") as f:
files = {"file": f}
headers = {
"X-Username": username,
"X-Secret-Key": secret_key,
}
resp = requests.post(
OCR_API_URL,
files=files,
headers=headers,
timeout=TIMEOUT,
)
if resp.status_code != 200:
return f"[HTTP {resp.status_code}: {resp.text}]"
text = resp.text.strip()
# Try JSON decode first (API may return structured JSON)
try:
data = json.loads(text)
return data.get("msg", text)
except json.JSONDecodeError:
return text
except Exception as e:
return f"[Error calling OCR API: {e}]"
def ocr_image(image_path, username, secret_key):
"""Wrapper: normalize OCR output for one image."""
text = call_ocr_api(image_path, username, secret_key)
if not text:
return ""
text = str(text).replace("\r\n", "\n")
if not text.endswith("\n"):
text += "\n"
return text
def repair_pdf(input_pdf, output_pdf):
"""Attempt to repair a corrupted PDF using Ghostscript."""
if shutil.which("gs") is None:
print("Ghostscript (gs) not found, cannot repair PDF.")
return False
try:
subprocess.run(
["gs", "-o", output_pdf, "-sDEVICE=pdfwrite",
"-dPDFSETTINGS=/prepress", input_pdf],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
return True
except subprocess.CalledProcessError:
return False
def pdf_to_text(pdf_path, output_path, username, secret_key):
if not os.path.exists(pdf_path):
print(f"Error: PDF file not found: {pdf_path}")
sys.exit(1)
start = time.time()
repaired_pdf = None
# Check if PDF is corrupted; try to repair if so
try:
pdf_info = pdfinfo_from_path(pdf_path)
if pdf_info.get("Pages", 0) == 0:
raise PDFPageCountError("No pages found in the PDF.")
except PDFPageCountError:
print(f"Corrupted PDF detected: {pdf_path}. Attempting to repair...")
repaired_pdf = pdf_path + ".repaired.pdf"
if repair_pdf(pdf_path, repaired_pdf):
print("Successfully repaired.")
pdf_path = repaired_pdf
else:
print("Error: Could not repair the PDF file.")
sys.exit(1)
# Convert PDF pages to images in a temporary folder
try:
with tempfile.TemporaryDirectory(prefix="nextocr_") as tmp_dir:
print(f"Converting PDF to images: {pdf_path}")
images = convert_from_path(pdf_path)
total = len(images)
print(f"Total pages: {total}")
all_text = []
for idx, image in enumerate(images):
img_file = os.path.join(tmp_dir, f"{1000 + idx}.png")
image.save(img_file, "PNG")
page_num = idx + 1
print(f"OCR page {page_num}/{total} ...", end=" ", flush=True)
text = ocr_image(img_file, username, secret_key)
print("done")
all_text.append(f"--- Page {page_num} ---\n{text}")
# Write all text to output file
with open(output_path, "w", encoding="utf-8") as f:
f.write("\n".join(all_text))
except Exception as e:
print(f"Error during conversion: {e}")
sys.exit(1)
finally:
# Clean up repaired temp PDF if created
if repaired_pdf and os.path.exists(repaired_pdf):
os.remove(repaired_pdf)
elapsed = time.time() - start
print(f"\nSaved output to: {output_path}")
print(f"Processed {total} pages in {elapsed:.2f} seconds")
def main():
if len(sys.argv) != 5:
print("Usage: python3 nextocr_pdf2text.py 'username' 'secretkey' 'file.pdf' 'output.txt'")
sys.exit(1)
username = sys.argv[1]
secret_key = sys.argv[2]
pdf_path = sys.argv[3]
output_path = sys.argv[4]
pdf_to_text(pdf_path, output_path, username, secret_key)
if __name__ == "__main__":
main()
4. Usage
python3 nextocr_pdf2text.py 'your_username' 'your_secret_key' 'file.pdf' 'output.txt'
If your secret key contains special characters such as
!, $, or *,
always wrap arguments in single quotes '...'.With double quotes, bash interprets
! as history expansion and you will see an error like:
bash: !ABC: event not found
Example output while running:
Converting PDF to images: file.pdf
Total pages: 12
OCR page 1/12 ... done
OCR page 2/12 ... done
...
OCR page 12/12 ... done
Saved output to: output.txt
Processed 12 pages in 45.32 seconds
5. Output format
The output text file contains the extracted text of every page, separated by page markers:
--- Page 1 ---
(text of page 1)
--- Page 2 ---
(text of page 2)
...
This makes it easy to post-process the file — for example, splitting by page, feeding it into a search index, or building a training corpus.
6. Features & notes
- Automatic PDF repair — corrupted PDFs are repaired with Ghostscript before conversion.
- No leftover files — page images are stored in a temporary folder and deleted automatically when finished.
- Per-page progress — the script prints the OCR status of each page in real time.
- Request usage — each PDF page counts as one API request. A 100-page PDF uses 100 requests from your monthly quota.
- Large PDFs — for very large documents, consider splitting the PDF first or running the script overnight; each page typically takes a few seconds.
7. Prefer a library instead?
If you want to integrate OCR into a larger application rather than use a CLI script, check out the official Python client:
pip install nextocr-python
See the API Documentation & Guide for full details on the client library, streaming events mode, and the Bank Receipt API.
Powered by NextOCR — vision-first OCR with continual learning
Developer portal: developer.nextocr.org/
For API keys, custom models, demos, higher request limits, or support:
danhhong@gmail.com