Tags: crystal, programming
This is a writeup for my fourth Crystal library: qrencode.cr.
qrencode.cr is a wrapper for libqrencode, the C library behind
qrencode. It supports QR
Code model 2 and Micro QR (the latter experimentally).
Binding libqrencode to Crystal was nice and straightforward, as there are only a handful
of methods needed for the most common QR encoding tasks. The main libqrencode structure
is also primitive, with no references to other structures:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @[Link("qrencode")] lib LibQRencode struct QRcode version : LibC::Int width : LibC::Int data : LibC::UChar* end fun encode_string = QRcode_encodeString(string : LibC::Char*, version : LibC::Int, level : ::QRencode::ECLevel, hint : ::QRencode::EncodeMode, casesensitive : LibC::Int) : QRcode* fun encode_string_mqr = QRcode_encodeStringMQR(string : LibC::Char*, version : LibC::Int, level : ::QRencode::ECLevel, hint : ::QRencode::EncodeMode, casesensitive : LibC::Int) : QRcode* fun encode_data = QRcode_encodeData(size : LibC::Int, data : LibC::UChar*, version : LibC::Int, level : ::QRencode::ECLevel) : QRcode* fun encode_data_mqr = QRcode_encodeDataMQR(size : LibC::Int, data : LibC::UChar*, version : LibC::Int, level : ::QRencode::ECLevel) : QRcode* fun free = QRcode_free(qrcode : QRcode*) : Void end
From there, I just wrapped the bindings into a
QRencode::QRcode object and introduced some
enums equivalent to those in libqrencode:
…that’s about it!
The easiest way to install qrencode.cr is via Crystal’s
shards dependency manager.
You can find the relevant steps in the repository
Once installed, generating a QR code involves a single instantiation:
1 2 3 require "qrencode" qr = QRencode::QRcode.new("this is my QR code!")
Arbitrary binary data can be passed as well, via a
1 2 data = "this is a slice-ified string".to_slice qr = QRencode::QRcode.new(data)
Once created, the
QRcode instance contains the actual QR symbol data in the
This field can be accessed directly, or via
each_row (which yields each row):
1 2 3 4 5 6 7 # Since QR codes are square, `qr.data.size == qr.width * qr.width`. puts qr.data.inspect qr.each_row do |row| # Each row is `qr.width` bytes. puts row.inspect end
Each byte in
data has various bits set to indicate state, all of which are documented
here. The most important one
for most applications is the LSB, which indicated whether the module/dot corresponding to that
byte is black or white.
QRencode::Util provides two simple methods for exactly that:
1 2 3 4 5 6 7 qr.data.each do |byte| if QRencode::Util.black? byte puts "Black" else puts "White" end end
This forms the basis for most QR rendering code. Importantly, neither libqrencode nor qrencode.cr
provides rendering functions (e.g., to a PNG or SVG) — it’s up to the user (or a QR rendering
library) to take the data inside a
QRCode object and turn it into something presentable.
By way of education, the
examples/ansiqr.cr program is a simple example of this sort of rendering.
It takes an input string and writes a QR code to the terminal using ANSI color codes:
…which works as expected. Taken from my phone with a camera scanner:
Full (or nearly full) API documentation is also available here. I’ll try to keep it synced with the latest released version.
This was a fun little afternoon project — after reading the libqrencode project, writing these bindings only took about an hour. It also gave me a bit of insight into the structure of QR codes (e.g., the QR “version,” which actually corresponds to the size of the code and not its specification).
Thanks for reading!