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:

Pipeline:
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 pdf2image to 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:

Email: danhhong@gmail.com

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'
Important — use single quotes!
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