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
0 Responses to “gescannte Bilder zu PDFs zusammenfassen / sed im Einsatz”