CLOUD
AI

Self-Hosted Arabic OCR: Reading Documents Automatically

CLOUD|October 25, 2026|7 min read|87 views
Self-Hosted Arabic OCR: Reading Documents Automatically

For clean printed Arabic, Tesseract or PaddleOCR reads a page on CPU in a second or two, while poor photos and tables need a vision model. Preprocess images first, read the e-invoice QR code before anything else, and keep customer documents on a Saudi server.

Reading Arabic documents automatically is possible today with open tools running on your own server, but the right tool depends on the type of document, not on which tool is most popular. The direct answer: if your documents are printed and clean (contracts generated from Word, official letters scanned at 300 DPI), Tesseract 5 with the best Arabic model or PaddleOCR reads a page on CPU in a second or two with high character-level accuracy. If they are phone photos at odd angles with poor lighting, or contain tables, stamps and handwriting, traditional tools fall apart, and you need a modern vision-language model such as Qwen2.5-VL that reads the image, understands its structure and returns fields directly as JSON, at a higher resource cost. In both cases, three rules improve results more than switching tools: preprocess the image before reading it (rotation, contrast, noise removal), validate extracted fields with arithmetic rules instead of trusting them blindly, and read the QR code on Saudi e-invoices before any OCR, since it carries the VAT number, total and tax amount with perfect accuracy. These documents hold IDs, account numbers and contracts, so their natural home is a server inside the Kingdom rather than an external OCR service.

Why Arabic is harder for OCR

Most OCR engines were designed for separate Latin letters, while Arabic is difficult in four ways:

  • Connected script: letters join, and each letter changes shape depending on its position (initial, medial, final, isolated), so a word cannot be split into letters by clear gaps.
  • Dots: the difference between ب, ت, ث, ن and ي is one or two dots, so any scanning noise or a stamp over the text turns one letter into another.
  • Mixed direction: a right-to-left Arabic line may contain a commercial registration number or email address written left to right, and weak engines reverse the digit order.
  • Two digit systems: one invoice can contain both ١٢٣ and 123, so you have to normalize them after reading.

There is also a big gap between printed text and handwriting. Printed Arabic is now read with character accuracy that usually exceeds 95% when the scan is good, while Arabic handwriting is still hard for every open tool, and you should expect uneven results even from large vision models.

Comparing the open tools

ToolTypePrinted ArabicTables and layoutResourcesTime per page approx.
Tesseract 5 (tessdata_best)Classic LSTMVery good on clean scansWeakCPU only1 to 3 seconds
PaddleOCRDeep detection and recognitionGood, better with skewed imagesMedium through its layout moduleCPU or GPU1 to 4 seconds
EasyOCRDeep detection and recognitionMediumWeakGPU preferred2 to 6 seconds on CPU
Qwen2.5-VL 7BVision-language modelVery good even on poor imagesStrong, outputs JSON or tablesGPU with 16GB or more3 to 10 seconds on GPU

These numbers are rough figures for an A4 page and vary a lot with text density and hardware. A 7B vision model does run on CPU, but slowly, at 30 to 90 seconds per page, which is fine for dozens of documents a day and not for thousands. Check each model's license before commercial use, since some recent OCR tools ship under licenses that restrict commercial use.

Preprocessing before reading

The biggest accuracy gain comes from this step, not from switching engines. A page scanned at 150 DPI and skewed by 3 degrees can lose half its accuracy. The basic pipeline with OpenCV:

import cv2, numpy as np

def preprocess(path: str):
    img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
    h, w = img.shape
    if w < 2000:                       # upscale small images
        img = cv2.resize(img, None, fx=2000/w, fy=2000/w,
                         interpolation=cv2.INTER_CUBIC)
    img = cv2.fastNlMeansDenoising(img, h=15)
    th = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                               cv2.THRESH_BINARY, 31, 15)
    coords = np.column_stack(np.where(th == 0))
    angle = cv2.minAreaRect(coords)[-1]
    angle = angle - 90 if angle > 45 else angle
    M = cv2.getRotationMatrix2D((w/2, h/2), angle, 1.0)
    return cv2.warpAffine(th, M, (th.shape[1], th.shape[0]),
                          flags=cv2.INTER_CUBIC, borderValue=255)

The steps are grayscale conversion, upscaling small images, denoising, adaptive thresholding to handle shadows in phone photos, and deskewing. For blue or red stamps over text, try picking the color channel where the stamp disappears before converting to grayscale.

Tesseract in practice

sudo apt install tesseract-ocr
sudo wget -O /usr/share/tesseract-ocr/5/tessdata/ara.traineddata \
  https://github.com/tesseract-ocr/tessdata_best/raw/main/ara.traineddata
tesseract page.png out -l ara+eng --psm 6 --oem 1

Use the tessdata_best model instead of the default fast one, as the difference in Arabic is noticeable. The ara+eng option is essential for documents that mix in English company names or email addresses. psm 6 assumes a single block of text and suits letters, while psm 4 works better for invoices with columns. After reading, convert Arabic-Indic digits to Western digits before any further processing:

text = text.translate(str.maketrans("٠١٢٣٤٥٦٧٨٩", "0123456789"))

Extracting fields from invoices and contracts

Start with the QR code

A Saudi simplified tax invoice carries a QR code encoded as TLV and then Base64, containing the seller name, VAT number, invoice timestamp, total including VAT and the VAT amount. Reading it with pyzbar and decoding the TLV gives you those five fields with perfect accuracy in under 100 milliseconds, with no OCR at all. Make this the first step of your pipeline and fall back to text recognition only when the code is missing or damaged.

Decoding takes a few lines, since each field starts with one byte for the tag, one byte for the length, then the value:

from pyzbar.pyzbar import decode
from PIL import Image
import base64

def read_zatca_qr(path: str) -> dict:
    raw = base64.b64decode(decode(Image.open(path))[0].data)
    names = {1: "seller", 2: "vat_no", 3: "timestamp", 4: "total", 5: "vat"}
    out, i = {}, 0
    while i < len(raw):
        tag, ln = raw[i], raw[i+1]
        out[names.get(tag, tag)] = raw[i+2:i+2+ln].decode("utf-8")
        i += 2 + ln
    return out

If the function returns five valid fields, the invoice is done without an OCR engine ever touching it, and text recognition is only needed for the line items table if you want that detail.

Then validate with rules

When fields come from OCR or a vision model, do not accept them as is. Three rules catch most errors: the VAT number is 15 digits starting and ending with 3, the pre-tax amount multiplied by 1.15 equals the total within a halala or two, and the date falls in a sensible range. Any invoice that fails a rule goes to human review instead of entering the accounting system wrong.

A vision model for the hard cases

For contracts and invoices without a QR code or with complex tables, ask the vision model for structured output: "Extract from the image the supplier name, VAT number, date, line items table (description, quantity, price) and total. Return JSON only." Serve it through vLLM or Ollama on the same server and run the output through the same validation rules. The ideal pipeline is hybrid: QR first, then PaddleOCR or Tesseract for clean pages, then the vision model only for pages that fail validation, which saves around 80% of the heavy processing time.

Why documents stay on a Saudi server

The documents that go through OCR are the most sensitive in any company: ID scans, contracts, bank statements and supplier invoices. External cloud OCR services mean uploading all of this to another country, which conflicts with the Personal Data Protection Law and with the requirements of many government bodies. Running the pipeline on a cloud server in Riyadh keeps each document on a local path from upload to deletion.

On sizing, a Tesseract or PaddleOCR pipeline with QR reading handles about 2,000 pages a day on the Numuw plan with 2 cores and 4GB at 6,900 SAR per year, and the Business plan with 4 cores and 8GB at 11,500 SAR roughly doubles that. Add 100GB of SSD for 500 SAR per year for every 200 thousand or so pages archived as compressed PDF. If you want a vision model on the same infrastructure, start from the AI hosting templates, which come with Docker and Jupyter ready for testing.

A five-step rollout plan

  1. Classify your documents: how many are clean prints, how many are phone photos, and how many are invoices with QR codes.
  2. Build the pipeline with QR first, then image preprocessing, then Tesseract with the best model or PaddleOCR.
  3. Add validation rules for VAT numbers, totals and dates, and send failures to human review.
  4. Test on 200 real documents, measure the share of correct fields, then decide whether a vision model is worth its cost for the hard pages.
  5. Choose your server size from pricing and plans, and delete original images after extraction according to a written retention policy.
Share