QR-Codes mit Access erzeugen, Teil III

Wenn Sie ein Abonnement des Magazins 'Access im Unternehmen' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

QR-Codes mit Access erzeugen, Teil III

Im dritten und letzten Teil der Beitragsreihe zum Thema QR-Codes wollen wir den in den ersten beiden Teilen ermittelten Code zusammen mit den benötigten Markierungen in eine Bilddatei gießen und den dabei eingeschlagenen Weg zur besseren Nachvollziehbarkeit Schritt für Schritt in einer Excel-Datei darstellen. Die entstandenen Bilder können Sie dann entweder als Bilddatei speichern oder aber direkt in Formularen und Berichten einsetzen.

Der Start

Die Erstellung der Bilddatei führen wir mithilfe einer Funktion namens ImageErzeugen durch, die folgende Parameter erwartet:

  • intVersion: Version des QR-Codes, liefert Informationen über die Dimension der zu erstellenden Bilddatei.
  • intErrorCorrectionLevel: Liefert den Grad der Fehlerkorrektur.
  • strCode: Enthält den in der Bilddatei anzuzeigenden QR-Code.

Zwei Arrays

Für das Füllen des QR-Codes benötigen wir zwei Arrays. Dazu wollen wir uns kurz die notwendigen Schritte ansehen: Zuerst fügen wir den Array nämlich die Markierungen hinzu, mit denen der Scanner später erkennen kann, in welcher Position sich der QR-Code befindet. Diese landen typischerweise links oben, rechts oben und links unten im Bild. Außerdem benötigen wir je nach Version noch ein sogenanntes Alignment Pattern. Auch dieses wird gleich zu Beginn eingefügt. Schließlich werden einige allgemeine Informationen zum QRCode-Bild hinzugefügt â€" und zwar in Form einer horizontalen und einer vertikalen Anordnung. Wie dies aussieht, können Sie Bild 1 entnehmen â€" in diesem Fall für die Versionen 1 (21x21), 2 (25x25), 7 (45x45) und 14 (73x73).

Die Elemente des QR-Codes ohne die eigentlichen Daten für verschiedene Versionen beziehungsweise Kantenlängen

Bild 1: Die Elemente des QR-Codes ohne die eigentlichen Daten für verschiedene Versionen beziehungsweise Kantenlängen

Nun müssen nur noch die weißen und schwarzen Pixel hinzugefügt werden, welche den eigentlichen Code ausmachen â€" plus den Fehlerkorrekturcode. Diese nehmen den übrigen Platz ein.

Allerdings werden die Pixel nicht einfach Zeile für Zeile und Spalte für Spalte zum Bild hinzugefügt, sondern ausgehend von unten rechts, und dann auch nur dort, wo noch keine Informationen etwa in Form von Markierungen abgelegt wurden â€" plus einem kleinen Puffer von einem Pixel.

Wie aber können wir beim Einfüllen der Einsen und Nullen für den Code erkennen, welches Pixel bereits gefüllt ist und welches nicht â€" wenn diese doch teilweise mit einer Eins und teilweise mit einer Nulll gefüllt sind? Dazu benötigen wir ein zweites Array, das wir zu Beginn mit einer Maske füllen. Diese sieht wiederum wie in Bild 2 aus.

Diese Bereiche der QR-Codes werden mit den eigentlichen Daten gefüllt.

Bild 2: Diese Bereiche der QR-Codes werden mit den eigentlichen Daten gefüllt.

Die Bereiche, die nicht durch eine der Markierungen belegt sind, werden mit einer Art Muster gefüllt. Warum ein Muster und nicht einfach nur eine schwarze Fläche? Weil dieses Muster, die sogenannte Maske, wiederum eine spezielle Funktion hat.

Wenn eines der Pixel weiß ist und das Bit des Codes, das dort abgelegt werden soll, den Wert 0 hat, bleibt das Pixel weiß. Hat der Code den Wert 1, wird das Pixel schwarz. War das Pixel zuvor schwarz, sorgt der Wert 0 dafür, dass das Pixel schwarz bleibt, beim Wert 1 hingegen wird das Pixel weiß.

Es gibt acht verschiedene Masken, von denen eine ausgewählt werden muss. Die Masken stehen deshalb zur Verfügung, damit man eine andere Maske verwenden kann, wenn das fertige Bild zu große weiße oder schwarze Bereiche zur Darstellung des Inhalts enthält. In der Beispieldatenbank ist dies auf die Maske mit der Nummer Sieben voreingestellt.

Mit dieser Maske wissen wir aber immer noch nicht, welche Pixel des Bildes mit dem QR-Code belegt werden können und welche nicht â€" sie liefert doch nur Einsen und Nullen? Eben nicht: Was in der Abbildung nicht zu erkennen ist, sind die Unterschiede, die leer sind, weil die Maske für diese keinen Wert enthält (also weder Eins noch Null) und denen, die den Wert Null enthalten. Wir haben dies mit einer kleinen Hilfsfunktion wie in Bild 3 dargestellt.

Die Maske mit den per x als leer markierten Pixeln

Bild 3: Die Maske mit den per x als leer markierten Pixeln

Beim Einfüllen des QR-Codes in die Maske werden also nur diejenigen Stellen gefüllt beziehungsweise geswitcht, die bereits einen der Werte 0 oder 1 enthalten. Die mit x markierten Stellen, die ja die Positionsmarken und weitere Informationen zum Aufbau des QR-Codes enthalten, werden nicht geändert.

Steuerfunktion

Kommen wir zum Formular frmQRCodes zurück und zu den Schritten, die zur Erstellung des Bildes für den QR-Code erforderlich sind (s. Bild 4).

Formular zum Erstellen der QR-Codes

Bild 4: Formular zum Erstellen der QR-Codes

Ein Klick auf die Schaltfläche ImagesErzeugen ruft die Prozedur aus Listing 1 auf. Diese Prozedur deklariert die drei Steuerelemente zur Anzeige der verschiedenen Stadien des QR-Codes, die später mit Verweisen auf die Steuerelemente imgBasis, imgBelegung und imgQRCode gefüllt werden. Bei diesen Steuerelementen handelt es sich um das Image-Steuerelement der MSForms-Bibliothek, die Sie über die Liste der ActiveX-Steuerelemente zum Formular hinzufügen können.

Private Sub cmdImageErzeugen_Click()
     Dim ctlImageBelegung As MSForms.Image, ctlImageBasis As MSForms.Image
     Dim ctlImageQRCode As MSForms.Image
     Dim picQRCode As StdPicture, picBelegung As StdPicture, picBasis As StdPicture
     Set picQRCode = ImageErzeugen(Me!cboVersionID, Me!cboErrorCorrectionID, _
         Me!txtQRCode, picBelegung, picBasis)
     If Not picQRCode Is Nothing Then
         SavePicGDIPlus picQRCode, CurrentProject.Path & "QRCode_" & Me!QRCodeID & ".png", pictypePNG
     Else
         MsgBox "Bild nicht erzeugt."
     End If
     Set ctlImageBasis = Me!imgBasis.Object
     With ctlImageBasis
         .PictureSizeMode = fmPictureSizeModeStretch
         .Picture = picBasis
     End With
     Set ctlImageBelegung = Me!imgBelegung.Object
     With ctlImageBelegung
         .PictureSizeMode = fmPictureSizeModeStretch
         .Picture = picBelegung
     End With
     Set ctlImageQRCode = Me!imgQRCode.Object
     With ctlImageQRCode
         .PictureSizeMode = fmPictureSizeModeStretch
         .Picture = picQRCode
     End With
End Sub

Listing 1: Die Steuerprozedur zum Erzeugen des Bildes mit dem QR-Code

Außerdem deklariert die Prozedur drei StdPicture-Objekte, welche die Bilder aufnehmen, die von einer Funktion erzeugt und dann in den Image-Steuerelementen angezeigt werden.

Die Funktion ImageErzeugen erwartet als Parameter die Version, den Fehlerkorrekturlevel und den QR-Code sowie die beiden Variablen picBelegung und picBasis als Parameter. Die letzten beiden werden mit den StdPicture-Objekten zu diesen beiden Status des QR-Code-Bildes gefüllt, die Funktion selbst liefert das StdPicture-Objekt mit dem vollständigen QR-Code-Bild zurück.

Nachdem die drei StdPicture-Objekte mit den Bildern gefüllt und zurückgegeben wurden, speichert die Prozedur cmd­Image­Er­zeugen_Click das fertige Bild im Datenbankverzeichnis unter dem Namen QRCode_123.png (wobei 123 der Nummer des Datensatzes der Tabelle tblQRCodes entspricht, in der die Basisinformationen zu den jeweiligen QR-Codes gespeichert werden).

Außerdem landen die Bilder aus den StdPicture-Objekten in den Image-Steuerelementen. Fertig! Nun müssen wir uns nur noch die paar hundert Codezeilen ansehen, die den QR-Code in ein Bildobjekt umwandeln.

Die Funktion ImageErzeugen

Diese Funktion steuert die Erzeugung der QR-Code-Bilder. Sie erwartet die bereits weiter oben beschriebenen, von der Prozedur cmdImageErzeugen_Click übergebenen Parameter (s. Listing 2).

Public Function ImageErzeugen(intVersion As Integer, lngErrorCorrectionLevelID As Long, strcode As String, _
     Optional picBelegung As StdPicture, Optional picBasis As StdPicture) As StdPicture
     Dim intXKoordinate(32000) As Integer
     Dim intYKoordinate(32000) As Integer
     Dim intModulesToPack As Integer
     Dim intVersionWidth As Integer
     Dim bolQRBild() As Boolean
     Dim strMaske() As Variant
     Dim objPicture As StdPicture
     Dim intMaske As Integer
     intMaske = 7
     intVersionWidth = DLookup("VersionWidth", "tblVersions", "VersionID = " & intVersion)
     Call BasisErzeugen(intVersion, intVersionWidth, lngErrorCorrectionLevelID, intMaske, _
         bolQRBild, strMaske)
     Set picBasis = CreatePicture(intVersionWidth, intVersionWidth)
     SetQRCode picBasis, bolQRBild(), intVersionWidth
     Set picBelegung = CreatePicture(intVersionWidth, intVersionWidth)
     SetBelegung picBelegung, strMaske(), intVersionWidth
     intModulesToPack = DLookup("ModulesToPack", "tblVersions", "VersionID = " & intVersion)
     KoordinatenErmitteln intVersionWidth, strMaske, intXKoordinate, _
         intYKoordinate, intModulesToPack
     CodeHinzufuegen strcode, intModulesToPack, bolQRBild(), strMaske, _
         intXKoordinate, intYKoordinate
     Set objPicture = CreatePicture(intVersionWidth, intVersionWidth)
     SetQRCode objPicture, bolQRBild(), intVersionWidth
     Set ImageErzeugen = objPicture
End Function

Listing 2: Prozedur zum Aufruf der Routinen zum Erzeugen des QR-Code-Bilds

Die Funktion verwendet einige Variablen, von denen vier hier erläutert werden sollen:

  • intXKoordinate(32000): nimmt die X-Koordinaten für den Pfad des QR-Codes im Bild auf.
  • intYKoordinate(32000): nimmt die Y-Koordinaten für den Pfad des QR-Codes im Bild auf.
  • bolQRBild(): Nimmt in einem zweidimensionalen Array vom Datentyp Boolean die bereits fertigen Bits für das QR-Code-Bild auf, also nur die Werte -1 und 0.
  • strMaske(): Nimmt in einem ebenfalls zweidimensionalen Array, diesmal vom Datentyp String, die Maske auf, die aus 1, 0 und Leerzeichen besteht.

Die Funktion legt fest, dass die Maske mit der Nummer 7 verwendet wird. Sie können auch die anderen, von 0 bis 6 durchnummerierten Masken verwenden â€" die Funktion beschränkt sich auf die Maske 7. Jede Maske sorgt für ein anderes Ausgangsmuster, das durch das Hinzufügen des QR-Codes geändert wird. Je nach Maske und QR-Code entstehen so mitunter QR-Code-Bilder, die große weiße oder schwarze Bereiche aufweisen â€" schlecht zu lesen für QR-Code-Scanner. In diesem Fall könnten Sie eine andere Maske als Grundlage verwenden. Diese hier wird mit dem Wert 7 für die Variable intMaske fest vorgegeben. Danach ermittelt die Funktion die Ausmaße der zu erstellenden Pixelmatrix aus der Tabelle tblVersions. Die Kantenlänge landet in der Variablen intVersionWidth. Nun startet die Funktion eine weitere Routine namens BasisErzeugen. Diese füllt die beiden Arrays bolQRBild und strMaske mit den Daten für die Positionsmarken und die übrigen Grundinformationen zum verwendeten QR-Code (s. Listing 3).

Public Sub BasisErzeugen(intVersion As Integer, intVersionWidth As Integer, _
         lngErrorCorrectionLevelID As Long, intMaske As Integer, _
         bolQRBild() As Boolean, strMaske() As Variant)
     Dim i As Integer
     Dim intX As Integer
     Dim intY As Integer
     ReDim bolQRBild(intVersionWidth, intVersionWidth)
     ReDim strMaske(intVersionWidth, intVersionWidth)
     For intX = 1 To intVersionWidth
         For intY = 1 To intVersionWidth
             i = i + 1
             strMaske(intY, intX) = -(i Mod 2)
             bolQRBild(intY, intX) = -1
         Next intY
     Next intX
     MaskeAnwenden strMaske, intMaske, intVersionWidth
     PositionsmusterAnwenden strMaske, bolQRBild, intVersionWidth
     TimingPatternAnwenden strMaske, bolQRBild, intVersionWidth
     VersionsinformationenSchreiben strMaske, bolQRBild, intVersion, _
         intVersionWidth
     BereicheLeeren strMaske, bolQRBild, intVersionWidth
     FormatinformationenSchreiben bolQRBild(), lngErrorCorrectionLevelID, _
         intMaske, intVersionWidth
     AlignmentPatternAnwenden strMaske, bolQRBild, intVersion
End Sub

Listing 3: Diese Prozedur fügt die Positionsmarkierungen und andere Elemente hinzu.

Sie füllt also das als Parameter übergebene Array bolQRBild, das zuvor komplett mit dem Standardwert 0 gefüllt war und nun an den Stellen mit den Markierungen die Werte 0 und 1 enthält. Das Array strMaske wird auf ähnliche Weise gefüllt, allerdings wird dieses zunächst komplett mit der Maske gefüllt, bevor die Bereiche, die Markierungen und Informationen enthalten, jeweils mit Leerzeichen versehen werden.

Hieraus werden die beiden oberen Bilder des Formulars frmQRCode erzeugt. Das erste wird über die Funktion SetQRCode in der StdPicture-Variablen picBasis als Bild verewigt (und später im Formular als oberstes Bild angezeigt). Es enthält die aktuellen Werte des Array bolQRBild, also die bereits hinzugefügten Markierungen.

Das zweite Bild namens picBelegung soll nur den aktuellen Stand des Inhalts von strMaske liefern. Daher füllen wir die entsprechende StdPicture-Variable entsprechend. Schließlich folgt das eigentliche Füllen der Matrix in bolQRBild() mit dem QR-Code. Dazu ermittelt die Prozedur KoordinatenErmitteln zunächst den Pfad durch die Matrix, in dem die Werte des QR-Codes eingetragen werden sollen. Mehr dazu weiter unten. Schließlich fügt die Prozedur CodeHinzufuegen den QR-Code in Abhängigkeit von der in strMaske gespeicherten Vorgaben in das Array bolQRBild() ein. Das Ergebnis dieses Vorgangs landet dann schließlich in der StdPicture-Variablen objPicture, die auch gleichzeitig als Rückgabewert der Funktion ImageErzeugen dient.

Markierungen und Informationen einfügen

Die Prozedur BasisErzeugen ruft verschiedene Routinen auf, welche die unterschiedlichen Markierungen und Informationen zu der in bolQRBild() gespeicherten Matrix hinzufügen und die Matrix aus strMaske() zunächst mit der Maske füllen und dann die Bereiche leeren, die mit Markierungen und sonstigen Informationen gefüllt werden, damit diese später beim Hinzufügen des QR-Codes als gefüllt erkannt werden.

Im ersten Schritt redimensioniert die Prozedur die beiden Variablen bolQRBild und strMaske entsprechend der zuvor ermittelten Dimensionen der Matrix. Dann durchläuft die Prozedur zwei verschachtelte Schleifen, in denen alle Felder des Arrays bolQRBild mit dem Wert -1 gefüllt werden. Die Felder des Arrays strMaske werden abwechselnd mit den Werten -1 und 0 gefüllt.

Nun fügt die Prozedur MaskeAnwenden dem Array strMaske die als Grundlage zu verwendende Maske hinzu (s. Listing 4). Wir haben hier nur den letzten Zweig der Select Case-Bedingung abgebildet, in dem unsere zuvor festgelegte Maske angewendet wird. Die detaillierte Erläuterung sparen wir uns an dieser Stelle â€" die zwei verschachtelten Schleifen ändern die Werte des Array strMaske so, dass es anschließend wie in Bild 5 aussieht.

Die von der Beispieldatenbank verwendete Maske

Bild 5: Die von der Beispieldatenbank verwendete Maske

Hinzufügen der Finderpatterns

Nach dem Anwenden der Maske landen die Finderpatterns im Array bolQRBild. Außerdem werden die Bereiche, in denen sich die Finderpatterns befinden, im Array strMaske geleert (s. Bild 6). Für diese Aufgabe ist die Prozedur FinderPatternsAnwenden aus Listing 5 verantworlich.

bolQRBild und strMaske mit Positionsmustern

Bild 6: bolQRBild und strMaske mit Positionsmustern

Die Prozedur erstellt zunächst ein Array, das den Aufbau eines Finderpatterns enthält. Das Aussehen können Sie schematisch bereits dem Listing entnehmen. Die Prozedur durchläuft jeweils zwei ineinander verschachtelte Schleifen. Die Durchläufe, bei denen die Laufvariablen jeweils die Werte von 1 bis 7 annehmen, repräsentieren den Aufbau der einzufügenden Finderpatterns. Insgesamt gibt es drei Finderpatterns. Um Code zu sparen, werden die gleichen Schleifen für alle drei Finderpatterns verwendet.

Der Ablauf der Prozedur sieht so aus: Nach dem Start haben die beiden Schleifenvariablen i und j den Wert 1. Auch die zusätzliche Zählervariable namens intZaehler besitzt den Wert 1. Die Prozedur prüft dann in der If...Then-Bedingung, welchen Wert das Array für die durch intZaehler markierte Position hat. Im Falle des Wertes 1 wird das aktuelle Feld von bolQRBild, also das mit der Position 1|1, mit dem Wert 0 versehen. Das entsprechende Feld des Arrays strMaske wird mit einer leeren Zeichenkette gefüllt, damit dieses Feld später nicht mehr durch den eigentlichen QR-Code gefüllt werden kann. Dies war das erste der 7x7 Pixel für das Finderpattern oben links.

Die beiden folgenden Anweisungen kümmern sich um das Finderpattern oben rechts, die nächsten beiden um das Finderpattern unten links.

Anschließend wird intZaehler um Eins erhöht, damit im folgenden Durchlauf der inneren For...Next-Schleife das folgende Element des im Array varFinderPattern gespeicherte Finderpatterns oben links, oben rechts und unten links eingefügt werden kann.

Der Else-Teil der If...Then-Bedingung legt die entsprechenden Werte in bolQRBild und strMaske für den Fall fest, dass das aktuelle Element von varFinderPattern den Wert 0 hat.

Timingpattern und Dark Module einfügen

Das nächste einzufügende Element ist das Timingpattern. Dabei handelt es sich um eine horizontale gepunktete Linie zwischen den beiden oberen Finderpatterns und eine vertikale gepunktete Linie zwischen den beiden linken Finderpatterns. Außerdem benötigen wir das sogenannte Dark Module, ein schwarzes Bit rechts oben neben dem linken, unteren Finderpattern (s. Bild 7).

bolQRBild und strMaske mit Timing Pattern

Bild 7: bolQRBild und strMaske mit Timing Pattern

Das Hinzufügen dieser Elemente übernimmt die Prozedur TimingPatternAnwenden aus Listing 6. Der erste Teil legt das horizontale Timingpattern an.

Public Sub TimingPatternAnwenden(strMaske() As Variant, bolQRBild() As Boolean, _
         intVersionWidth As Integer)
     Dim i As Integer
     For i = 10 To intVersionWidth - 7 
         If i Mod 2 = 0 Then
             bolQRBild(i - 1, 7) = 0
             strMaske(i - 1, 7) = ""
         Else
             bolQRBild(i - 1, 7) = -1
             strMaske(i - 1, 7) = ""
         End If
     Next i
     For i = 10 To intVersionWidth - 7 
         If i Mod 2 = 0 Then
             bolQRBild(7, i - 1) = 0
             strMaske(7, i - 1) = ""
         Else
             bolQRBild(7, i - 1) = -1
             strMaske(7, i - 1) = ""
         End If
     Next i
     bolQRBild(9, intVersionWidth - 7) = 0
     strMaske(9, intVersionWidth - 7) = ""
End Sub

Listing 6: Hinzufügen des Timingpatterns

Dazu durchläuft die Prozedur eine For...Next-Schleife über die Werte von 10 bis zur Breite des QR-Codes minus sieben. Hier werden die Elemente im Array bolQRBild von links nach rechts abwechselnd mit den Werten 0 und -1 gefüllt.

Die entsprechenden Elemente des Arrays strMaske werden mit leeren Zeichenketten gefüllt, um diese als belegt zu markieren. Die zweite For...Next-Schleife erledigt dies für die vertikale Linie.

Die letzten beiden Anweisungen dieser Prozedur legen das Dark Module fest. Dieses liegt immer an der gleichen Position, nämlich an der neunten Stelle von links und der achten Stelle von unten. Das entsprechende Element der Maske wird mit einer leeren Zeichenkette gefüllt.

Versionsinformationen schreiben

Wenn die Version des QR-Codes größer als sieben ist (also für eine Dimension von 45x45 und mehr), fügt die Funktion VersionsinformationenSchreiben an zwei Stellen zusätzliche Informationen über die Version des verwendeten QR-Codes hinzu (siehe Listing 7).

Public Sub VersionsinformationenSchreiben(strMaske() As Variant, _
         bolQRBild() As Boolean, intVersion As Integer, intVersionWidth As Integer)
     Dim strVersionInfo As String
     Dim intZaehler As Integer, i As Integer, j As Integer
     If intVersion >= 7 Then
         strVersionInfo = DLookup("VersionInfo", "tblVersions", "VersionID = " _
             & intVersion)
         intZaehler = 1
         For i = 1 To 6
             For j = 1 To 3
                 If Mid$(strVersionInfo, intZaehler, 1) = "1" Then
                     bolQRBild(intVersionWidth - 11 + j, i) = 0
                     strMaske(intVersionWidth - 11 + j, i) = ""
                     intZaehler = intZaehler + 1
                 Else
                     bolQRBild(intVersionWidth - 11 + j, i) = -1
                     strMaske(intVersionWidth - 11 + j, i) = ""
                     intZaehler = intZaehler + 1
                 End If
             Next j
         Next i
         intZaehler = 1
         For j = 1 To 6
             For i = 1 To 3
                 If Mid$(strVersionInfo, intZaehler, 1) = "1" Then
                     bolQRBild(j, intVersionWidth - 11 + i) = 0
                     strMaske(j, intVersionWidth - 11 + i) = ""
                     intZaehler = intZaehler + 1
                 Else
                     bolQRBild(j, intVersionWidth - 11 + i) = -1

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.