Logo Dennis' Tech Blog

QR-Codes automatisiert in Dokumente einbetten und auslesen

Dennis Schwerdel  Qr-code  Pdf
Post Image

QR-Codes sind praktisch um Daten auszudrucken und sie später wieder einfach digitalisieren zu können. Das ist nützlich um Dokumente, die man selbst erzeugt hat beim späteren Einscannen einfach wieder zuzuordnen. Ein Anwendungsfall wäre beispielsweise Dokumente für einen Kunden automatisiert zu generieren und dann mit der Unterschrift des Kunden wieder einzuscannen und dabei automatisch dem Kunden wieder zuzuordnen. In diesem Tutorial zeige ich wie man automatisiert einen QR-Code auf jede Seite eines Dokumentes einbettet und diese aus den gescannten Dokumenten wieder zuverlässig auslesen kann.

QR-Codes in Dokument einbetten

Der erste Schritt ist das Einbetten von QR-Codes in ein Dokument. Für dieses Szenario gehen wir davon aus, dass das Dokument bereits als PDF vorliegt und document.pdf heißt. Als Beispiel verwende ich hier passenderweise den Wikipedia-Artikel zu PDF als PDF.

QR-Code erzeugen

Als erstes müssen nun QR-Codes für das Dokument erzeugt werden. Hierfür gibt es das Kommandozeilentool qrencode. Mit folgendem Befehl kann man einen QR-Code mit vorgegebenem Inhalt erzeugen:

$> qrencode -t eps -o code.eps -lQ "Text für QR-Code"

Der Parameter -lQ steuert die Redundanz des QR-Codes, die mit den Codes L, M, Q, H in vier Stufen angegeben werden kann. Je höher die Stufe, desto größer der erzeugte QR-Code und desto höher die Wahrscheinlichkeit, dass ein beschädigter Code noch gelesen werden kann.

Mit dem Parameter -t eps wird ein embedded Postscript statt einer PNG Bilddatei erzeugt. Dieses Format wird benötigt, weil es ein Vektorformat ist und so keine Qualitätsverluste entstehen. Außerdem kann man EPS-Dateien recht einfach in PDFs umwandeln:

$> ps2pdf -dEPSCrop code.eps code.pdf

Auf diese Weise können nun QR-Codes für jede Seite des Dokuments einzeln oder ein Code für das gesamte Dokument erzeugt werden.

QR-Code in PDF Dokument einbetten

Um nun einen QR-Code in das PDF einzubetten müssen noch einige Vorbereitungen getroffen werden. Das verwendete Tool pdftk kann ein PDF über ein anderes PDF "stempeln". Hierbei kann wahlweise das Stempel-Dokument aus nur einer Seite bestehen, die dann auf alle Seiten des Originaldokuments gestempelt wird oder aus genau so vielen Seiten wie das Original, wobei dann jede Seite ihren eignen Stempel bekommt. In beiden Fällen muss der Stempel aber die gleiche Seitengröße wie das Originaldokument haben.

Als erster Schritt muss nun also der QR-Code in ein PDF der Größe DIN A4 umgewandelt werden. Dabei soll der Code natürlich nicht einfach vergrößert werden sondern soll klein in einer Ecke des Dokuments positioniert werden. Das erledigt folgender Befehl:

$> gs -sDEVICE=pdfwrite -o code_a4_page_1.pdf -sPAPERSIZE=a4 -dFIXEDMEDIA -c "<</BeginPage{0.5 0.5 scale}/PageOffset [525 5]>> setpagedevice" -f code.pdf

In diesem Befehl steht eine magische Codesequenz. Der erste Teil (/BeginPage{0.5 0.5 scale}) skaliert den QR-Code auf 50% seiner Größe. Der zweite Teil (/PageOffset [525 5]) setzt den QR-Code in die untere rechte Ecke (als Offset von links unten aus gerechnet). Je nach Größe des QR-Codes müssen die Parameter evtl. leicht angepasst werden damit der Code nicht rechts über den Rand hinausragt.

So kann nun für jede Seite ein QR-Code als DIN A4 Seite erzeugt werden. Jetzt müssen diese Seiten in ein fortlaufendes PDF zusammengefügt werden. Das geschieht mit folgendem Befehl:

gs -sDEVICE=pdfwrite -sOutputFile=codes.pdf code_a4_page_*.pdf

Als letzter Schritt muss nun nur noch das Original-PDF mit den QR-Codes kombiniert werden. Das geschieht wie erwähnt mit dem Befehl pdftk:

$> pdftk document.pdf multistamp codes.pdf output document_with_codes.pdf

Alles zusammen in einem Skript

Um diese ganzen Schritte zu automatisieren habe ich ein kleines Skript erstellt (qr_embed.sh):

#!/bin/bash
set -e

INPUT=$1
OUTPUT=$2
CONTENT=${3:-"$(basename "$INPUT" .pdf)[%PAGE%/%PAGES%]"}
LEVEL=${4:-Q}

SCALE="0.5 0.5"
OFFSET="525 5"

PAGES=$(pdfinfo "$INPUT" | grep -a Pages | awk '{print $2}')
GS="gs -q -dBATCH -dNOPAUSE -dSAFER -sDEVICE=pdfwrite"
GS_SCRIPT="<</BeginPage{$SCALE scale}/PageOffset [$OFFSET]>> setpagedevice"
TMP=$(mktemp -d .tmp-XXXXXXXX)
for PAGE in $(seq "$PAGES"); do
  DATA=$(echo -n "$CONTENT" | sed -e "s/%PAGE%/$PAGE/g;s/%PAGES%/$PAGES/g")
  qrencode -t eps -o - -l$LEVEL "$DATA" | ps2pdf -dEPSCrop - $TMP/code_$PAGE.pdf
  $GS -o $TMP/code_a4_$PAGE.pdf -sPAPERSIZE=a4 -dFIXEDMEDIA -c "$GS_SCRIPT" -f $TMP/code_$PAGE.pdf
done
$GS -sOutputFile=$TMP/codes.pdf $(seq -s " " -f "$TMP/code_a4_%g.pdf" "$PAGES")
pdftk "$INPUT" multistamp $TMP/codes.pdf output "$OUTPUT"
rm $TMP/*; rmdir $TMP

Das Skript hat noch ein paar Besonderheiten die über die eben erwähnten Befehle hinausgehen:

Mit dem Skript kann man nun relativ einfach die QR-Codes in das Dokument einfügen:

$> ./qr_embed.sh document.pdf document_with_codes.pdf "wikipedia:Portable Document Format,%PAGE%/%PAGES%"

Als Resultat erhält man ein PDF mit den Originalseiten und den QR-Codes auf jeder Seiten unten rechts. Im Beispiel des Wikipedia-Artikels sieht das so aus.

QR-Codes aus Dokument auslesen

Um nun die QR-Codes aus einem gescannten Dokument wieder auszulesen müssen die einzelnen Seiten des Scans nach QR-Codes durchsucht und diese dann eingelesen werden. Hierfür kann das Tool zbar verwendet werden.

Bilder aus PDF auslesen

Da das Tool allerdings nur mit Bilddateien und nicht mit PDF umgehen kann, müssen in einem ersten Schritt zuerst die Bilddaten aus dem PDF herausgelesen werden. In einem Scan ist normalerweise auf jeder Seite des PDFs ein großes Bild der Seite enthalten. Diese Bilder können mit dem Befehl pdfimages aus dem PDF ausgelesen werden:

pdfimages scan.pdf -j page

Dieser Befehl erzeugt pro Seite eine JPEG-Datei mit dem Namen page-001-000.jpg. Im Beispiel des Wikipedia-Artikels könnte das so aussehen.

Bilder aufbereiten

Um nun dem QR-Code-Reader zu helfen, muss das Bild nun noch aufbereitet werden. Hierzu wird das Bild in ein Schwarz-Weiß-Bild umgewandelt:

$> convert page-001-000.jpg -threshold 50% monochrome.png

Als Ausgabeformat wird hier PNG verwendet um weitere Konvertierungsverluste zu vermeiden. Im Beispiel des Wikipedia-Artikels würde das Ergebnis dann so aussehen.

QR-Codes aus Bildern auslesen

Nun kann mit dem Befehl zbarimg der QR-Code aus dem Bild ausgelesen werden:

$> zbarimg -q --raw monochrome.png

Alles in einem Skript

Um auch diese Schritte zu automatisieren habe ich wieder ein kleines Skript erstellt (qr_scan.sh):

#!/bin/bash
set -e

INPUT=$1

PAGES=$(pdfinfo "$INPUT" | grep -a Pages | awk '{print $2}')
TMP=$(mktemp -d .tmp-XXXXXXXX)
for PAGE in $(seq "$PAGES"); do
  pdfimages "$INPUT" -j -l $PAGE -f $PAGE $TMP/page
  for IMG in $(ls $TMP/page*); do
    convert "$IMG" -threshold 50% $TMP/monochrome.png
    echo -n "$PAGE "
    zbarimg -q --raw $TMP/monochrome.png
  done
  rm $TMP/page*
done
rm $TMP/*; rmdir $TMP

Auch dieses Skript hat wieder ein paar Besonderheiten die über die eben erwähnten Befehle hinausgehen:

Nun kann man mit folgendem Befehl ein eingescanntes Dokument nach QR-Codes absuchen:

$> ./qr_scan scan.pdf

Fazit

Das Tool qrencode erzeugt auf der Qualitätsstufe Q Codes mit hoher Redundanz, die auch bei starker Beschädigung noch erkannt werden können. Durch den Umweg über embedded Postscript werden die QR-Codes als Verktorgrafik und ohne Qualitätsverluste in das PDF eingebettet. Dadurch wird die höchstmögliche Qualität an den Drucker durchgereicht.

Das freie Tool zbar findet die QR-Codes auf der Seite automatisch auch wenn diese gedreht oder beschädigt sind. Die Aufbereitung der Bilder vor dem Auslesen des QR-Codes steigert die Fehlertoleranz des Readers noch weiter. Schließlich können sogar stark beschädigte QR-Codes noch eingelesen werden:

Worst-Case QR-Code

Die von mir entwickelten Skripte können frei verwendet werden. Ich bitte nur darum Verbesserungen der Skripte per Nachricht oder Mail an mich zurückzugeben.