QR Codes on IBM i
QR Codes are a very convenient solution for many use cases. In contrast to barcodes a very broad range of data can be encoded into a QR Code. Today you will encounter many QR codes which have a URL encoded into it which can be scanned with your smartphone to open a web site.
QR codes are 2 dimensional in constrast to barcodes which just use one dimension.
Another difference to barcodes is the size of a QR codes. The size of a QR code depends on some factors:
- Size of encoded data
- Type of encoded data
- Level or error correction
For a better explaination and more details on this see Information capacity and versions of the QR Code.
And if this is not enough motivation to learn something about QR codes and how to create them on IBM i natively … have you ever heard of the “GS1 Sunrise 2027 Initiative”?
Transitioning to 2D barcodes for POS is a multi-step process. The GS1 US Sunrise 2027 initiative lays out a plan to help ensure 2D barcodes will be scanned and processed at any retail POS by the end of 2027.
Quote from the GS1 website.
And by 2D barcodes they mean QR codes and data matrix.
Native Support
There is native support to print QR codes on IBM i. But this doesn’t help if you need to output the QR code to another medium like the web or a stream file.
There are solutions but none of them are freely available and directly usable from the ILE environment. Most incorporate other programming languages like Python or Node.js.
And don’t even start with any kind of printer files … yak.
But a free ILE solution is not that hard to implement. There are a lot open source libraries available on the internet which can help in generating a QR code and can be used in the ILE environment.
The Steps
Generating a QR code is more or less done in two steps.
- calculate which blocks needed to be black / white
- draw the blocks (on a canvas like the browser window or into a stream file)
For step 1 we use the C library libqrencode from Kentaro Fukuchi available as an open source library on GitHub.
The library is mostly C89 compatible and therefore can be compiled as ILE modules and bound to an ILE service program. There is a precompiled version available in the RPM repository at repo.rpgnextgen.com and installable via iPKG.
1 | ipkg install libqrencode |
The RPG prototypes can also be installed with iPKG.
1 | ipkg install 'libqrencode-devel' loc('/home/mihael/include') |
Getting the first step done in an RPG program is not that difficult.
1 | dcl-proc main; |
The C function QRcode_encodeData
takes some raw data and encodes it into the QR code.
For that it needs to know how large the data is (size) and where data starts (address of
the variable, data part).
The parameter 3 and 4 are QR code specific. The version
(parameter 3) defines the number
of “modules” used in the QR code. Modules are the blocks in the QR code. More modules = more
data can be encoded. The value 0
means the minimum number of modules for the passed data
will be used.
Parameter 4 - ecLevel
- defines the level or error correction. The higher the level the
less data can be encoded into QR code with the same version
. The available values range
from 0 to 3 where 0 is the lowest level of error correction and 3 the highest.
The library libqrencode
offers many more functions to encode data but this is the easiest
one to use.
Output on Screen
We can output the data on the screen with just some lines of code. We take the variable
qrCode
from the previous block of code and pass it to the procedure drawOnConsole
. The
filled modules will be drawn as an X
.
1 | dcl-proc drawOnConsole; |
The returned QR code data from QRcode_encodeData
is really just a matrix with some bit flags
switched on or off, f. e. for the fill state of a block.
From the libqrencode documentation:
1 | // MSB 76543210 LSB |
To test if a block is filled (black) or not we just need to define some constants and test it with
the bitand
built-in function. The QR code data structure contains the used width which is the
number of modules in one row. The rows of the QR code are contained in the data
subfield of the
data structure. The data of each row is immediately followed by the data of the next row. There is
no separator. But we got the width of each row so we can compute the start and end of each row.
To get the data for each module (block) we kind of dynamically overlay the QR code data with a character field with a length of just 1.
qrCode.data
is just a pointer to the data and not the data itself. By assigning the data pointer
to the pointer the char(1)
field is based on the field holds the first module data. And just by
incrementing this pointer (qrBlockPtr
) by one we can move the field to the next module information.
The output looks something like this:
1 | DSPLY XXXXXXX X X XXXXXXX |
This is a QR code with 21 x 21 modules (blocks) and equals the QR code version 0
.
Not too bad :)
Output to Image
But in the modern world we seldom need to output a QR codes onto a console screen. But what is much more common is the output to an image file f. e. in PNG format.
There is no support out-of-the-box available in the ILE environment for dynamically creating PNG images so we need to extend our toolbox by another software library.
libpng is a good candidate as it boasts to be written in ANSI C (C89) but the project is quite big and I only need to output some black and white blocks. So I keep looking for another candidate.
lodepng is a very small library which can be easily embedded in a project as it only consists of a single header file and a single source file.
In addition to C++, LodePNG also supports ANSI C (C89), with all the same functionality: C++ only adds extra convenience API.
That sounds promising!
And the project really delivered! It hasn’t promised too much. Though I had to work around some CCSID problems (as always ;-), it compiled out of the box (not counting the CCSID problems) and we can install it and the corresponding copybooks with iPKG.
1 | ipkg install lodepng |
Note: You can also compile all these software libraries by yourself and don’t have to use iPKG. You can find the libraries with their Makefile at my Bitbucket page.
So far we can create the QR code data and know which blocks are filled and which are not filled. Now we need to output that data to a PNG image.
First we need to convert the QR code data to pixel data. The raw pixel data is a block of memory
where each pixel consists of 3 or 4 values (RBG - Red Green Blue or RBGA - Red Green Blue Alpha).
Each value needs to hold a number from 0 to 255. We declare this as uns(3)
– a one byte unsigned
integer. So our block of memory has the size of image width
x image height
x 4 (we use RGBA).
Now we just need to loop through our QR code data and create pixel data from each module data
(which is too lengthy to show here). The raw pixel data can now be passed to a C function from
the lodepng
project.
1 | lodepng_encode_file(filename : pixels : imageWidth : imageHeight : 6 : 8); |
Most parameters are self describing. Only the 5th and 6th parameter needs some explaination.
The 5th parameter defines the color type used in the image, 6 = RGBA.
The 6th parameter defines the color depth in bit. The range of values depend on the used color type. Valid values for the color type RBGA are either 8 or 16.
This whole process has been wrapped into a nice procedure in my project qrtools which can be installed via iPKG.
1 | ipkg install qrtools |
And the usage of the procedure looks like this.
1 | dcl-proc main; |
In this example the created image has a size of 200 x 200 px. The QR code is automatically scaled to the maximum size inside the given image dimension. Any not used space in the image will be drawn as 100% transparent pixels.
Easy peasy! ;-)
Wrap Up
Now we can natively create QR codes in our beloved ILE environment without having to switch to any environment which is not integrated as well as our polyglot environment.
Currently the project qrtools covers only the creation of a PNG image. Possibly other output formats could be SVG, EPS, JPEG. This might get implemented depending on the feedback.
Demand determines supply. ;-)
Happy QR (en)coding!
Mihael