gescannte Bilder zu PDFs zusammenfassen / sed im Einsatz

Für ein privates Projekt wollte ich neulich eingescannte Bilder in PDF-Dokumente umwandeln. Dafür hab ich zunächst die Bilder hochauflösend gescannt und dann als PNG gespeichert. Abgelegt wurden die Scans jeweils getrennt in ca. 40 Ordner (nach Personen benannt) zu je drei Files. Ein Scan schlug dabei mit 2,1 Mb zu Buche.

Da ich dies für einen Export ins PDF als zu groß empfand, habe ich in einem ersten Schritt die Bilder mit XnView in JPGs mit geringerer Qualität umgewandelt. XnView ist ein Bildbetrachter mit sehr interessanten Zusatzfeatures, der bei mir unter Wine läuft. Für die Konvertierung aller Files verwendete ich die Option „mehrfaches konvertieren“, bei der man über eine Option die vorhandene Verzeichnisstruktur komplett übernehmen kann. Nachdem ich die Qualität der Bilder mit verschiedenen Qualitätsstufen geprüft hatte, entschied ich mich für eine Variante bei der ein JPG ca. 200 Kb belegt.

Diese jeweils drei JPGs wollte ich zu einem PDF zusammenfassen, welches den Verzeichnisnamen übernimmt. Zunächst prüfte ich wie ich überhaupt JPGs zu PDFs machen kann und fand mit dem mir bereits bekannten ImageMagick den richtigen Partner. Ein einfaches convert name.jpg name.pdf reicht hier aus um die Konvertierung durchzuführen. Zum Zusammenfügen kann man in Debian einfach das Paket pdftk installieren, welches zahlreiche PDF-Manipulationen ermöglicht. In meinen Fall kann ich pdftk 1.pdf 2.pdf 3.pdf output 123.pdf verwenden um die PDFs wie gewünscht zusammenzusetzen. Soweit so einfach.

Nun kam die deutlich schwierigere Aufgabe dies über ein Script so zu verbinden, dass am Ende alle Unterverzeichnisse automatisch durchlaufen werden und der Name jeweils in den Export übernommen wird. Da ich ein ziemlicher Neuling im Bereich Shell-Scripting bin, hab ich mich hier ein wenig inspirieren lassen. Zunächst brauchte ich eine Möglichkeit alle Unterverzeichnisse eines Ordners anzuzeigen.

Anders als in Windows bietet Linux im normalen Listbefehl (ls) keine Option nur Verzeichnisse anzuzeigen. Man kann sich hierbei mit find . -type d behelfen, aber das brachte mich nicht wirklich weiter. Im Forum von SelfHTML fand ich dann mit ls -d1 */ | cut -d '/' -f 1 | sort -g eine besseren Ansatz, der ls verwendet. Damit werden alle Verzeichniseinträge (-d) die „*/“ heißen (also alle) jeweils einzeilig (-1) angezeigt, dann alle durch Schrägstrich getrennten Einträge bis auf den ersten ausgeschnitten und anschließend (alphanumerisch) sortiert, kurz man bekommt alle Unterverzeichnisse angezeigt.

Diese sollten nun Teil einer For-Schleife werden. Da jedoch Verzeichnisse auch Leerzeichen enthalten können, gibt es hier das Problem, dass die For-Schleife dann Verzeichnisnamen zerreißt, etwa aus „Vorname Name“ zwei separate Durchläufe macht: „Vorname“ und „Name“. Um das Problem zu vermeiden kann man IFS="," setzen, wodurch der Internal Field Separator (von Whitespaces) auf Komma gesetzt wird.

Damit die Schleife dann wieder funktioniert, muss nun noch das ls-Kommando entsprechend erweitert werden, indem man sed "s/.*/&,/g" als weitere Pipe-Aktion anfügt. Die komplette Zeile lautet nun: ls -d1 */ | cut -d '/' -f 1 | sort -g | sed "s/.*/&,/g". Dadurch werden mittels sed alle Einträge (.*) durch sich selbst plus angefügtem Komma ersetzt (&,) und zwar beliebig oft (g). Das am Ende ein Komma übrig bleibt (Verz1,Verz2,Verz3,) scheint die Schleife nicht weiter zu interessieren.

Leider werden die Verzeichnisse (bis auf das erste) mit Newline zu Beginn übergeben, weil dies nun im IFS nicht mehr enthalten ist. Dies kann man mit einem weiteren sed-Ausdruck korrigieren: sed ":a;N;$!ba;s/\n//g", den ich in einem Linux-Forum fand. Damit wäre auch dieses Problem gelöst.

Ein weiteres Problem, welches sich ebenfalls auf die Verzeichnisnamen mit Leerzeichen zurückführen lässt, bedarf einer weiteren Korrektur. So müssen die Leerzeichen bei den Verzeichnisnamen bzw. Dateinamen für Betriebssystemoperationen quotiert werden, was wiederum sed erledigt: sed "s/ /\\\\\ /". Die ersten vier Backslashes werden übrigens zu real einem, dh. aus einem „Vorname Name“ wird einfach „Vorname\ Name“ und nicht etwa „Vorname\\ Name“ oder schlimmeres.

Mit einem Probieren und Recherchieren kam ich meinem Ziel dann schon sehr nahe. Als ich dachte ich hätte es schon, kam dann noch ein letztes Problem zum Vorschein, welches mit dem Umlauten zu tun hat. Leider unterstützt pdftk keine (deutschen) Umlaute, weswegen ich hier noch einen letzten sed-Ausdruck einbaute, der alle Umlaute der Ausgabe-PDFs entsprechend abwandelt: sed "s/ä/ae/g;s/ö/oe/g;s/ü/ue/g;s/Ä/Ae/g;s/Ö/Oe/g;s/Ü/Ue/g;s/ß/ss/g".

Am Ende hat mein Script 73 Zeilen und neben der Lösung des eigentlichen Problems hab ich ne Menge über Shell-Programmierung und sed gelernt. Sicherlich hab ich weder den elegantesten Weg gewählt, aber für meinen Zweck habe ich eine sehr handliche Lösung gefunden.

Hier das gesamte Script:

# Arbeitsverzeichnis feststellen
WORKDIR=`pwd`

# Verzeichnistrennung auf Komma setzen
IFS=','

# Verzeichnisse finden, sortieren und Komma als Trenner verwenden
SUBDIRS=`ls -d1 */ | cut -d '/' -f 1 | sort -g | sed "s/.*/&,/g" `

# für alle Sub-Verzeichnisse Konvertierung starten
echo "Konvertiere jpgs aller Unterverzeichnisse zu pdf ..."
for i in $SUBDIRS; do

    if [ "$i" != "." ]; then

	# PDF-List sammelt die Ergebnisse der Konvertierungen
	PDFLIST="empty"
    
	# der DIR-Name darf keine Newlines mehr enthalten
	DIRNAME=`echo $i | sed ":a;N;$!ba;s/\n//g"`;
	
	# Leerzeichen im Verzeichnisnamen müssen zusätzlich maskiert werden
	QUOTEDDIRNAME=`echo $DIRNAME | sed "s/ /\\\\\ /"`;
    
	# Liste aller JPGs holen und sortieren
	JPGLIST=`ls $WORKDIR/$QUOTEDDIRNAME/*.jpg | sort -g | sed "s/.*/&,/g" `

	echo -n "Verzeichnis: [$DIRNAME] .. Seiten: "	

	# Für alle gefundenen JPGs die Konvertierung durchführen	
	COUNTER=0;
	for j in $JPGLIST; do

	    COUNTER=$(($COUNTER + 1))
	    
	    echo -n $COUNTER

	    # der JPG-Name darf keine Newlines mehr enthalten
	    JPGNAME=`echo $j | sed ":a;N;$!ba;s/\n//g"`;
	    
	    # das Seiten-PDF wird unter TMP gespeichert
	    PDFNAME="/tmp/jpgs2pdf_page$COUNTER.pdf"

	    #echo "convert $JPGNAME $PDFNAME";
	    convert $JPGNAME $PDFNAME
	    
	    # Leerzeichen im Dateinamen müssen zusätzlich maskiert werden
	    QUOTEDJPGNAME=`echo $JPGNAME | sed "s/ /\\\\\ /"`;	    
	    
	    # Konvertergebnis an PDF-List anhängen
	    if [ "$PDFLIST" == "empty" ]; then
		PDFLIST="$PDFNAME"
	    else
		PDFLIST="$PDFLIST,$PDFNAME";
	    fi
	    
	done

        echo -n " .. $COUNTER Seiten .. "

	# der PDF-Name darf keine Newlines mehr enthalten
	OUTPUTNAME="$DIRNAME.pdf";

        # Umlaute im Dateinamen müssen zusätzlich maskiert werden
        QUOTEDOUTPUTNAME=`echo $OUTPUTNAME | sed "s/ä/ae/g;s/ö/oe/g;s/ü/ue/g;s/Ä/Ae/g;s/Ö/Oe/g;s/Ü/Ue/g;s/ß/ss/g"`;
	
	# PDF-List wird zu PDF zusammen gesetzt
	#echo "pdftk $PDFLIST output $QUOTEDOUTPUTNAME";
	pdftk $PDFLIST output $QUOTEDOUTPUTNAME
	
	echo "[$QUOTEDOUTPUTNAME]"
    fi
done
Advertisements

0 Responses to “gescannte Bilder zu PDFs zusammenfassen / sed im Einsatz”



  1. Schreibe einen Kommentar

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s





%d Bloggern gefällt das: