aboutsummaryrefslogtreecommitdiff
path: root/ocr-scan
blob: 36994b534f46277407aea8d6d55ec3a12c000b3b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env bash
# ocr-scan.sh — recursively OCR un-OCR'd PDFs
# Deps: ocrmypdf, poppler-utils (pdftotext), tesseract-ocr
# Optional: python3-langdetect (for -a auto-detect)

set -euo pipefail

LANG_CODE="eng"
AUTO_DETECT=false
MIN_CHARS=50        # chars per page threshold — below = needs OCR
DIR="."

usage() {
    cat <<EOF
Usage: $(basename "$0") [OPTIONS] [DIRECTORY]

Recursively find un-OCR'd PDFs and produce OCR'd copies with _OCR suffix.

OPTIONS:
  -l LANG     Tesseract language code(s)  (default: eng)
              Single:       -l pol
              Multiple:     -l pol+eng
              Examples:     eng pol tha deu fra rus chi_sim jpn ara
  -a          Auto-detect language via langdetect (requires: pip install langdetect)
              Note: falls back to eng for fully un-OCR'd scans (no text layer)
  -h          Show this help and exit

ARGUMENTS:
  DIRECTORY   Root directory to search recursively (default: current directory)

OUTPUT:
  Each processed file is saved alongside the original:
    original.pdf → original_OCR.pdf
  Files already having an _OCR copy or sufficient text are skipped.

DEPENDENCIES:
  Required:   ocrmypdf, poppler-utils (pdftotext, pdfinfo), tesseract-ocr
  Per-lang:   apt install tesseract-ocr-pol tesseract-ocr-tha ...
  Optional:   pip install langdetect  (for -a flag)

EXAMPLES:
  $(basename "$0")                        # current dir, English
  $(basename "$0") ~/docs                 # ~/docs recursively, English
  $(basename "$0") -l pol ~/docs          # Polish
  $(basename "$0") -l pol+eng ~/docs      # Polish + English mixed
  $(basename "$0") -a ~/docs             # auto-detect language
EOF
    exit 0
}

while getopts ":l:ah" opt; do
    case $opt in
        l) LANG_CODE="$OPTARG" ;;
        a) AUTO_DETECT=true ;;
        h) usage ;;
        :) echo "Option -$OPTARG requires an argument."; usage ;;
        \?) echo "Unknown option: -$OPTARG"; usage ;;
    esac
done
shift $((OPTIND - 1))
[[ $# -gt 0 ]] && DIR="$1"

# Map ISO 639-1 → Tesseract lang codes
map_lang() {
    case "$1" in
        pl|pol) echo "pol" ;;
        en|eng) echo "eng" ;;
        th|tha) echo "tha" ;;
        de|deu) echo "deu" ;;
        fr|fra) echo "fra" ;;
        ru|rus) echo "rus" ;;
        zh|chi) echo "chi_sim" ;;
        ja|jpn) echo "jpn" ;;
        ar|ara) echo "ara" ;;
        *)       echo "eng" ;;
    esac
}

detect_lang() {
    local pdf="$1"
    local sample
    sample=$(pdftotext "$pdf" - 2>/dev/null | head -c 500)
    if [[ -z "$sample" ]]; then
        echo "eng"; return
    fi
    python3 - <<EOF 2>/dev/null || echo "eng"
from langdetect import detect
try:
    print("$(echo "$sample" | tr -d "'" | head -c 300)")
except:
    print("eng")
EOF
    # simpler: call python directly
    python3 -c "
from langdetect import detect
import sys
try:
    lang = detect('''$sample''')
    print(lang)
except:
    print('eng')
" 2>/dev/null || echo "eng"
}

needs_ocr() {
    local pdf="$1"
    local pages
    pages=$(pdfinfo "$pdf" 2>/dev/null | awk '/^Pages:/{print $2}')
    [[ -z "$pages" || "$pages" -eq 0 ]] && pages=1
    local chars
    chars=$(pdftotext "$pdf" - 2>/dev/null | wc -c)
    local threshold=$(( pages * MIN_CHARS ))
    [[ "$chars" -lt "$threshold" ]]
}

find "$DIR" -type f -iname "*.pdf" ! -iname "*_OCR.pdf" | while read -r pdf; do
    out="${pdf%.pdf}_OCR.pdf"

    if [[ -f "$out" ]]; then
        echo "[SKIP] Already has OCR copy: $out"
        continue
    fi

    if ! needs_ocr "$pdf"; then
        echo "[SKIP] Already has text: $pdf"
        continue
    fi

    lang="$LANG_CODE"
    if $AUTO_DETECT; then
        detected=$(detect_lang "$pdf")
        lang=$(map_lang "$detected")
        echo "[DETECT] $pdf → $detected → $lang"
    fi

    echo "[OCR] $pdf (lang: $lang) → $out"
    ocrmypdf \
        --language "$lang" \
        --output-type pdfa \
        --skip-text \
        --rotate-pages \
        --deskew \
        "$pdf" "$out" \
        && echo "[DONE] $out" \
        || echo "[FAIL] $pdf"
done