#!/bin/bash
#
##########################################################################################
#  
#  Skript zur Texterkennung der Bilddokumente in einem Ordner.
#
#  Voraussetzungen: (1) Tesseract muß installiert sein.
#                   (2) PDFTK muß installiert sein, wenn eine einzelne PDF-Datei generiert werden soll.
#
#
#  Benutzung:  Diese Datei ins Skript-Verzeichnis kopieren und ausführbar machen:
#              chmod +x ocr.sh
#              Danach starten: ./ocr.sh [Optionen]
#
#  Version: v1.1, 30. Mai 2024
#
#  
##########################################################################################
PATH=${PATH}:/usr/local/bin
set -u

## vordefinierte Variablen.
bild_aktuell=0
bilder_gesamt=0
ZIEL="ocr"
DATEIEN=0
SPRACHE="deu"
FONTTYP="Fraktur"
MODUS=1
ERGEBNIS="alto"
DATEIENDUNG="tif" # jpg|jpeg|png|gif|tif

## Meldungen.
DIENST="Texterkennung"
MELDUNG="🤝 Stets zu Diensten !"
WERBUNG="👉 https://staatsbibliothek.ewigerbund.org 👈"
HILFE="
$0 - Skript zur $DIENST der Bilddokumente in einem Ordner.

obligatorische Optionen:
    -v VERZEICHNIS         Pfad zu den Bild-Dateien

weitere Optionen:
    -Z ZIEL                Zielverzeichnis der Ausgabe (Standard: ocr)
    -D DATEIEN             auszulesende Bilddateien, durch Komma getrennt [seite_001.tif,seite_005.tif,[...]] (Standard: 0 - alle)
    -S SPRACHE             Sprache des Dokuments [deu|eng|fra] (Standard: deu)
    -F FONTTYP             Schrifttyp des Dokuments [Fraktur|Antiqua] (Standard: Fraktur)
    -M MODUS               Seitenaufteilungsmodus [1-6] (Standard: 1)
    -E ERGEBNIS            Ausgabeformat [alto|txt|pdf] (Standard: alto)
    -B                     Bei Aufruf dieser Option werden alle Bild-Dateien ausgelesen, ansonsten nur TIFF-Dateien
    -h Hilfe               Anzeige dieses Hilfetextes

beispielhafter Skript-Aufruf:
    $(basename $0) -v \"/PFAD/ZU/BILDERN\" -Z text -S eng -F Antiqua -E txt
"
## Statusanzeige
function status {

  # Prozessdaten.
  let _status=(${1}*100/${2}*100)/100
  let _erledigt=(${_status}*4)/10
  let _verbleibend=40-$_erledigt

  # Längen für die Statusleiste erstellen.
  _voll=$(printf "%${_erledigt}s")
  _leer=$(printf "%${_verbleibend}s")

  # Statusleiste ausgeben.
  printf "\rFortschritt: [\e[1;32m${_voll// /"\u25a0"}\e[0m${_leer// /"\u25a0"}] ${_status}%% ($1/$2) Bearbeite: $3"
}

## Optionen und Argumente prüfen.
[ "$#" -ge "1" ] || { echo "$HILFE"; exit 1; }

while getopts "v:Z:D:S:F:M:E:hB"  OPCOES; do
  case $OPCOES in
    v ) VERZEICHNIS="$OPTARG"
        while true; do
            if [ -d "$VERZEICHNIS" ]; then
                ## Pfad bereinigen, falls sich am Ende ein Schrägstrich befindet.
                [[ $VERZEICHNIS == */ ]] && VERZEICHNIS="${VERZEICHNIS::-1}"
                break
            else
                echo -e "\nFehler: Das angegebene Verzeichnis \"$VERZEICHNIS\" existiert nicht."
                read -p "Gib den Wert erneut ein: " VERZEICHNIS
            fi
        done
        ;;
    Z ) [ -n "$OPTARG" ] && ZIEL="$OPTARG"
        ;;
    D ) DATEIEN="$OPTARG";;
    S ) SPRACHE="$OPTARG"
        while true; do
            case $SPRACHE in
                deu|eng|fra)
                    break;;
                *)
                    echo -e "\nFehler: Erlaubte Werte für die Option -S sind [ deu | eng | fra ]. Eingegeben wurde: ${SPRACHE}.\n"
                    read -p "Gib den Wert erneut ein: " SPRACHE;;
            esac
        done;;
    F ) FONTTYP="$OPTARG"
        while true; do
            case $FONTTYP in
                Fraktur|Antiqua)
                    break;;
                *)
                    echo -e "\nFehler: Erlaubte Werte für die Option -F sind [ Fraktur | Antiqua ]. Eingegeben wurde: ${FONTTYP}.\n"
                    read -p "Gib den Wert erneut ein: " FONTTYP;;
            esac
        done
        ;;
    M ) MODUS="$OPTARG"
        while true; do
            case $MODUS in
                1|2|3|4|5|6)
                    break;;
                *)
                    echo -e "\nFehler: Erlaubte Werte für die Option -M sind [ 1 | 2 | 3 | 4 | 5 | 6 ]. Eingegeben wurde: ${MODUS}.\n"
                    read -p "Gib den Wert erneut ein: " MODUS;;
            esac
        done;;
    E ) ERGEBNIS="$OPTARG"
        while true; do
            case $ERGEBNIS in
                alto|txt|pdf)
                    break;;
                *)
                    echo -e "\nFehler: Erlaubte Werte für die Option -E sind [ alto | txt | pdf ]. Eingegeben wurde: ${ERGEBNIS}.\n"
                    read -p "Gib den Wert erneut ein: " ERGEBNIS;;
            esac
        done;;
    B ) DATEIENDUNG="jpg|jpeg|png|gif|tif";;
    h ) echo "$HILFE"; exit 1;;
    \? ) echo "Fehler: Ungültige Option -${OPCOES}." >&2 ; exit 1 ;;
    esac
done

## Tesseract-Installation prüfen.
type -P tesseract &>/dev/null || { echo -e "\Fehler: Tesseract-OCR wird benötigt, ist aber nicht installiert.\n" >&2; exit 1; }

## Installation des Tesseract-Sprachpakets testen.
dpkg-query -W -f='${Status}' "tesseract-ocr-${SPRACHE}" 2>/dev/null | grep -q "ok installed" || { echo -e "\nFehler: Das Sprachpaket \"tesseract-ocr-${SPRACHE}\" wird benötigt, ist aber nicht installiert.\n" >&2; exit 1; }

## Verzeichnisstruktur definieren.
ZIEL="${VERZEICHNIS}/${ZIEL}/${ERGEBNIS}"

## Ziel-Verzeichnis überprüfen bzw. anlegen.
[ -d "${ZIEL}" ]   || mkdir -p "${ZIEL}" || { echo "Fehler: Das Ausgabe-Verzeichnis ${ZIEL} konnte nicht angelegt werden." >&2; exit 1; }

## alle Dateien im Ordner auswerten.
if [[ "$DATEIEN" == 0 ]]; then

    for datei in "$VERZEICHNIS"/*; do
        if [ -f "$datei" ] && [[ $datei =~ \.(${DATEIENDUNG})$ ]]; then
            BILDER[$bilder_gesamt]=$(basename "$datei")
            ((bilder_gesamt++))
        fi
    done

    #bilder_gesamt=$(ls ${VERZEICHNIS} | grep -Ei ".(jpg|jpeg|png|gif)$" | wc -l)
    [ $bilder_gesamt == 0 ] && { echo "Fehler: Keine [${DATEIENDUNG}] - Dateien im angegebenen Verzeichnis: ${VERZEICHNIS}" >&2; exit 1; }

## mehrere übergebene Dateien auswerten.
elif [[ "$DATEIEN" == *,* ]]; then
    # Leerzeichen entfernen
    DATEIEN=($(echo "$DATEIEN" | tr -d ' '))
    # Komma-Trennung
    DATEIEN=($(echo "$DATEIEN" | tr ',' "\n"))
    
    for datei in "${DATEIEN[@]}"; do
        if [ -f "$VERZEICHNIS/$datei" ] && [[ "$VERZEICHNIS/$datei" =~ \.(jpg|jpeg|png|gif|tif)$ ]]; then
            BILDER[$bilder_gesamt]=$datei
            ((bilder_gesamt++))
        else
            echo "Fehler: Die angegebene Datei ${datei} existiert in Verzeichnis ${VERZEICHNIS} nicht." >&2; exit 1;
        fi
    done

## einzelne übergebene Datei auswerten.
else
    if [ -f "$VERZEICHNIS/$DATEIEN" ] && [[ "$VERZEICHNIS/$DATEIEN" =~ \.(jpg|jpeg|png|gif|tif)$ ]]; then
    #if [ -f "$VERZEICHNIS/$DATEIEN" ] && [[ $(file -b --mime-type "$VERZEICHNIS/$DATEIEN") =~ ^image/ ]]; then
        BILDER[0]=$DATEIEN
        bilder_gesamt=1
    else
        echo "Fehler: Die angegebene Datei ${DATEIEN} existiert in ${VERZEICHNIS} nicht." >&2; exit 1;
    fi
fi

## Bilder sortieren.
IFS=$'\n' BILDER=($(sort -n <<<"${BILDER[*]}"))
unset IFS

## Sprach-Argumente für Fraktur erzeugen.
[ ${SPRACHE} == "deu" -a ${FONTTYP} == "Fraktur" ] && SPRACHE="Fraktur_1+Fraktur_2+deu_frak"

echo -e "\n$DIENST läuft...\n"

## Bilder im Ordner durchlaufen.
for bild in "${BILDER[@]}"; do
    ((bild_aktuell ++))

    # Fortschrittsbalken und Dateinamen anzeigen.
    status $bild_aktuell $bilder_gesamt ${bild}

    # Texterkennung.<fileName></fileName>
    tesseract "${VERZEICHNIS}/${bild}" "${ZIEL}/${bild%.*}" --psm ${MODUS} -l ${SPRACHE} ${ERGEBNIS} 1>/dev/null 2>&1
    case $ERGEBNIS in
        alto)
            sed -i 's/ſ/s/g;s/="/@@/g;s/=/-/g;s/@@/="/g;s/¬/-/g;s/⸗/-/g;s/ns-v3#/ns-v2#/g;s/v3\/alto-3-0.xsd/v2\/alto-2-0.xsd/g' "${ZIEL}/${bild%.*}.xml"
            ;;
        txt)
            sed -i ':a;N;$!ba;s/ſ/s/g;s/¬/-/g;s/⸗/-/g;s/-\n//g;s/\n\n/@@/g;s/\n/ /g;s/@@/\n\n/g' "${ZIEL}/${bild%.*}.txt"
            ;;
        pdf)
            PDF+=("${bild%.*}.pdf")
    esac
done

## Erfolgsmeldung anzeigen.
echo -e "\n\n$DIENST abgeschlossen!\n"
echo -e "Die erzeugten Dateien befinden sich unter folgendem Pfad:\n"
echo -e "$ZIEL"

## optional mehrseitige PDF erstellen.
if [ ${ERGEBNIS} == "pdf" ]; then
    echo -e "\n"
    read -p "Sollen die einzelnen PDF-Dateien zu einer mehrseitigen PDF-Datei zusamengeführt werden? [j/N] " -r
    if [[ $REPLY =~ ^[Jj]$ ]]; then
        type -P pdftk &>/dev/null || { echo -e "\Fehler: PDFTK wird benötigt, ist aber nicht installiert.\n" >&2; exit 1; }
        # PDF-Dateien zusammenführen
        cd ${ZIEL}
        pdftk ${PDF[@]} output "$(date +%Y-%m-%d).pdf"
        rm ${PDF[@]}
    fi
fi

## abschließende Meldung ausgeben.
echo -e "\n"
for ((i=0; i<${#WERBUNG}; i++)); do
    echo -en "${WERBUNG:$i:1}"
    sleep .1
done
echo -e "\n"
for ((i=0; i<${#MELDUNG}; i++)); do
    echo -en "${MELDUNG:$i:1}"
    sleep .1
done
echo -e "\n\n\033[0;30m\u25a0\u25a0\u25a0\033[0;37m\u25a0\u25a0\u25a0\033[0;31m\u25a0\u25a0\u25a0\033[0;37m\n"
