Jump to content

QRCode UDF


Recommended Posts

Posted (edited)

This QRCode UDF makes it possible to generate QRCodes and create an image or print it to the console.
It provides functions to create a 2D-Boolean-Array of the QRCode, get a Bitmap (_GDIPlus) of that QRCode or to draw it directly at a GraphicsContext (_GDIPlus).
To achieve this, I created a .dll in rust, which uses the "QR Code generator library (Rust)" by Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library).

Currently the following functions are included in the UDF:

_QRCode_StartUp                       - Startup needed before using most other functions (Includes _GDIPlus_Startup)
_QRCode_Shutdown                      - Shutdown should be called at the and to clean up (Includes _GDIPlus_Shutdown)

_QRCode_GetQRCode                     - Generate a QRCode as 2D-Boolean-Array, where the first index = lines, the second index=cols, normally False=White, True=Black
_QRCode_GetBitmap                     - Creates a GDIPlus-Bitmap with the QRCode
_QRCode_DrawQRCode                    - Draws a QRCode on a provides GDIPlus-GraphicsContext
_QRCode_DrawQRCodeFast                - Same as _QRCode_DrawQRCode, but without error checks and requires brushes for the colors to be provided (=> higher FPS)
_QRCode_ConsoleWrite                  - Print the QRCode to the console
_QRCode_GetSize                       - Get the size of the generated QRCode
_QRCode_GetECLMaxLoss                 - Get the maximum allowed loss before the qrcode becomes unreadable (decimal multiplier)

Possible error correction levels:
$_QRCode_ECL_LOW = 1
$_QRCode_ECL_MEDIUM = 2
$_QRCode_ECL_QUARTILE = 3
$_QRCode_ECL_HIGH = 4                 - Default

Here is an example, how the UDF could be used:

Spoiler
#cs ----------------------------------------------------------------------------

 AutoIt Version: 3.3.16.1
 Author:         Kanashius

 Script Function:
    Example script to show the usages of the QRCode UDF.

#ce ----------------------------------------------------------------------------
#include <Array.au3>
#include "QRCode.au3"

; Startup to initialize the dll and call _GDIPlus_Startup
_QRCode_StartUp()
If @error Then
    ConsoleWrite("Error QRCode startup: "&@error&" >> "&@extended&@crlf)
    Exit
EndIf

; Generate a 2D-Array with boolean values representing the QRCode
; The $_QRCode_ECL_HIGH provides a high error correction level. But a higher ECL also creates larger QRCodes and allows for less characters. So if any of that is an issue, try decreasing the ECL.
; The $_QRCode_ECL_HIGH is the default to be more robust and allow for images to be placed on top of the qrcode (like a logo in the middle) without making the qrcode unreadable.
;Local $arQRCode = _QRCode_GetQRCode("A very long String to test", $_QRCode_ECL_HIGH)
Local $arQRCode = _QRCode_GetQRCode(Binary("0xFEED1337"), $_QRCode_ECL_HIGH)
If @error Then
    ConsoleWrite("Error creating QRCode: "&@error&" >> "&@extended&@crlf)
    Exit
EndIf

; Print the QRCode to the console.
_QRCode_ConsoleWrite($arQrCode, "#", " ", @crlf)

; Create a Bitmap with the QRCode and a 50 pixel border.
Local $hBitmap = _QRCode_GetBitmap($arQRCode, 4, 50, 50, 50, 50)
; _GDIPlus_ImageSaveToFile($hBitmap, "qrcode.png")
_GDIPlus_BitmapDispose($hBitmap)

; ==> GuiExample drawing directly on the graphics context
_guiExample()

; Shutdown to close the dll, free memory of structs and call _GDIPlus_Shutdown
_QRCode_Shutdown()
If @error Then ConsoleWrite("Error QRCode shutdown: "&@error&" >> "&@extended&@crlf)

Func _guiExample()
    Local $iWidth = 800, $iHeight = @DesktopHeight-100, $iSpace = 10, $iQRCodePixelSize = 4
    Local $hGui = GUICreate("QRCode example", $iWidth, $iHeight)
     Local $iInput = GUICtrlCreateInput("This is a text example", $iSpace, $iSpace, $iWidth-$iSpace*2, 25)

    ; create buffer image
    Local $iQRCodeAreaLeft = $iSpace, $iQRCodeAreaTop = $iSpace*2+25, $iQRCodeAreaWidth = $iWidth-$iSpace*2, $iQRCodeAreaHeight = $iHeight-$iSpace*3-25
    Local $hGraphics = _GDIPlus_GraphicsCreateFromHWND($hGui)
    Local $hBitmap = _GDIPlus_BitmapCreateFromGraphics($iQRCodeAreaWidth, $iQRCodeAreaHeight, $hGraphics)
    Local $hBitmapGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _GDIPlus_GraphicsClear($hBitmapGraphics, 0xFFFFFFFF)

    GUISetState(@SW_SHOW, $hGui)
    ; create brushes for the rectangles
    Local $hBrushBlack = _GDIPlus_BrushCreateSolid(0xFF000000)
    Local $hBrushWhite = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
    Local $hPenBlack = _GDIPlus_PenCreate(0xFF000000, 2)
    _GDIPlus_GraphicsSetSmoothingMode($hBitmapGraphics, 2)

    ; generate the first bottom qrcode
    Local $arQRCodeFast = _QRCode_GetQRCode("Current date and time: "&@YEAR&"/"&@MON&"/"&@MDAY&" "&@HOUR&":"&@MIN&":"&@SEC, $_QRCode_ECL_HIGH)
    If @error Then
        MsgBox(16, "Error", "QRCode generation failed."&@crlf&"Most probable error is the qrcode.exe not being in the correct directory: "&@error&" >> "&@extended)
        return false
    EndIf
    ; save the size of the bottom qrcode
    Local $arSizeQRCodeFast = _QRCode_GetSize($arQRCodeFast, $iQRCodePixelSize*2)
    ; initialize loop variables
    Local $iMsg, $sQRCodeText = "", $arSizeQRCode = [0, 0], $PI = 3.141592653589793
    While 1
        $iMsg = GUIGetMsg()
        If $iMsg = -3 Then ExitLoop

        ; ==> Top QRCode
        ; draw the top qrcode, if the input changes
        If GUICtrlRead($iInput)<>$sQRCodeText Then
            ; clear the area of the old qrcode
            _GDIPlus_GraphicsSetClipRect($hBitmapGraphics, ($iQRCodeAreaWidth-$arSizeQRCode[0])/2, 0, $arSizeQRCode[0], $arSizeQRCode[1])
            _GDIPlus_GraphicsClear($hBitmapGraphics, 0xFFFFFFFF)
            _GDIPlus_GraphicsResetClip($hBitmapGraphics)

            ; generate the new qrcode
            $sQRCodeText = GUICtrlRead($iInput)
             Local $arQRCode = _QRCode_GetQRCode($sQRCodeText, $_QRCode_ECL_HIGH)
            if @error Then
                Local $iErr = @error, $iExt = @extended
                _GDIPlus_GraphicsClear($hBitmapGraphics, 0xFFFFFFFF)
                If $iErr=5 Then
                    MsgBox(16, "Error", "QRCode generation failed."&@crlf&"The given Text is ~"&($iExt/8)&" characters to long")
                Else
                    MsgBox(16, "Error", "QRCode generation failed."&@crlf&"Most probable errors are a to long text or the qrcode.exe not being in the correct directory: "&$iErr&" >> "&$iExt)
                EndIf
            Else
                ; save the size of the new qrcode
                $arSizeQRCode = _QRCode_GetSize($arQRCode, $iQRCodePixelSize)

                ; draw the new qrcode
                Local $iQRCodeLeft = ($iQRCodeAreaWidth-$arSizeQRCode[0])/2
                _QRCode_DrawQRCode($hBitmapGraphics, $arQRCode, $iQRCodePixelSize, $iQRCodeLeft, 0)
                If @error Then
                    MsgBox(16, "Error", "QRCode drawing failed: "&@error&" "&@extended)
                    _GDIPlus_GraphicsClear($hBitmapGraphics, 0xFFFFFFFF)
                EndIf
            EndIf
        EndIf

        ; ==> Bottom QRCode
        Local $sText = "Current date and time: "&@YEAR&"/"&@MON&"/"&@MDAY&" "&@HOUR&":"&@MIN&":"&@SEC&":"&@MSEC
        Local $iTime = @SEC+@MSEC*0.001

        ; generate the qrcode for the new time
        $arQRCodeFast = _QRCode_GetQRCode($sText, $_QRCode_ECL_HIGH)
        If @error Then
            MsgBox(16, "Error", "QRCode generation failed."&@crlf&"Most probable error is the qrcode.exe not being in the correct directory: "&@error&" >> "&@extended)
            ExitLoop
        EndIf
        Local $iTextHeight = 20
        ; clear the area of the old qrcode
        _GDIPlus_GraphicsSetClipRect($hBitmapGraphics, ($iQRCodeAreaWidth-$arSizeQRCodeFast[0])/2, $iQRCodeAreaHeight-$arSizeQRCodeFast[1]-$iTextHeight, $arSizeQRCodeFast[0], $arSizeQRCodeFast[1]+$iTextHeight)
        _GDIPlus_GraphicsClear($hBitmapGraphics, 0xFFFFFFFF)
        _GDIPlus_GraphicsResetClip($hBitmapGraphics)
        ; save the size of the new qrcode
        $arSizeQRCodeFast = _QRCode_GetSize($arQRCodeFast, $iQRCodePixelSize*2)
        ; draw the time as string above the qrcode
        _GDIPlus_GraphicsDrawString($hBitmapGraphics, $sText, ($iQRCodeAreaWidth-$arSizeQRCodeFast[0])/2+15, $iQRCodeAreaHeight-$arSizeQRCodeFast[1]-$iTextHeight)
        ; Draw the second qrcode
        Local $iLeft = ($iQRCodeAreaWidth-$arSizeQRCodeFast[0])/2, $iTop = $iQRCodeAreaHeight-$arSizeQRCodeFast[1]
        _QRCode_DrawQRCodeFast($hBitmapGraphics, $arQRCodeFast, $hBrushBlack, $hBrushWhite, $iQRCodePixelSize*2, $iLeft, $iTop)
        ; draw the white rectangle in the middle of the qrcode
        Local $iRectSize = $arSizeQRCodeFast[0]*0.25, $iRectMidX = $iLeft+$arSizeQRCodeFast[0]/2, $iRectMidY = $iTop+$arSizeQRCodeFast[1]/2
        _GDIPlus_GraphicsFillRect($hBitmapGraphics, $iRectMidX-$iRectSize/2, $iRectMidY-$iRectSize/2, $iRectSize, $iRectSize, $hBrushWhite)
        ; draw the line in the rectangle
        Local $iTimeSec = Mod($iTime+45, 60)
        Local $iRad = (360/60*$iTimeSec)*$PI/180
        _GDIPlus_GraphicsDrawLine($hBitmapGraphics, $iRectMidX, $iRectMidY, $iRectMidX+$iRectSize/2*Cos($iRad), $iRectMidY+$iRectSize/2*Sin($iRad), $hPenBlack)

        ; draw bitmap to gui
        _GDIPlus_GraphicsDrawImageRect($hGraphics, $hBitmap, $iQRCodeAreaLeft, $iQRCodeAreaTop, $iQRCodeAreaWidth, $iQRCodeAreaHeight)
    WEnd
    ; cleanup time
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_PenDispose($hPenBlack)
    _GDIPlus_BrushDispose($hBrushWhite)
    _GDIPlus_BrushDispose($hBrushBlack)
    GUIDelete($hGui)
EndFunc

 


Another example can be found with the QRCodeGenerator-cli: QRCode generator cli
That is a console application with detailed possibilities for settings to generate qrcodes from the commandline.

While developing in rust, I first created a .exe instead of the .dll. Calling the .exe and parsing the result was relatively slow (~1 sec), so I ditched that for the .dll.
You can find the created .exe in the files as Rust-Executable (QRCode-Wrapper-Console-Rust-Exe.zip).
It is a commandline utility to be called with a text with the corresponding qrcode being written to the console with 0 (white), 1 (black), 2 (new line).
With --human the qrcode will be printed humanreadable and with --errorlvl low/medium/quartile/high, the error correction level can be provided.
But this appilcation is inferior to the QRCodeGenerator-cli mentioned above and is just included for completeness.

Files:
    The QRCode UDF with all needed files: qrcode.zip
    The Rust-Sourcecode of the .dll and .exe: QRCode-Dll-Exe-Rust-Src.zip
    The Rust-Executable: QRCode-Wrapper-Console-Rust.zip

QRCode-Wrapper-Console-Rust-Exe.zip qrcode.zip QRCode-Dll-Exe-Rust-Src.zip

Edited by Kanashius
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...