QRCodes are virtually ubiquitous these days. They're those two dimenional barcodes you see on everything you can read with your smartphone. Everything seems to use them, including manufacturing processes so you may even see them on many products you buy. And of course they're featured in many ads, and on many websites to allow you to easily access something related with your smartphone.
With just a few libraries, generating QRCodes is extremely simple. In this article we'll look at the rqrcode gem, as well as a pure-Ruby library for working with PNG files.
The rqrcode gem is a rather straightforward gem that allows you to generate QRCodes for any text or data. It doesn't provide much in the way of output, but it's easily enough to do the output yourself. It does provide a text output, but this is mainly for debugging, as QRCode readers likely cannot read the text output. They really need a black and white image with clear squares.
Installing the rqrcode gem is very easy, it's pure Ruby and doesn't really require much in the way of dependencies. Once installed, the API (at least for simple use) is very straightforward.
require 'rqrcode' qr = RQRCode::QRCode.new("Some data")
QRCodes must be square, so you can assume the data held in this qr object to be square. You can print out the QRCode in a text representation by simply converting the object to a string (or implicitly as puts does).
irb(main):003:0> puts qr xxxxxxx x x x xxxxxx x xxxxxxx x x xx xxxxx xx x x x xxx x x x xxxx xx x xxx x x xxx x x x x x x x xx x xxx x x xxx x x xxx xxx x x xxx x x x xxx x xx x x xxxxxxx x x x x x x x x x xxxxxxx x x xx x x xxxx xxxxx xxxx x xx x x xx xxxx xxxx xxxx x xxx xx x xx x x xxxx xx x x x xxx xxxxxxx x xx xxx x x xxxxxxx x x x xxxx xx xx xx x x x xx xxx x x xxxx xx xxx xxx xxx x xxxxx xxx xx x xx xxx xx x x x xxxx xx xxxx x x x x x x xxx x x x xx x xx xx x xxx xx xx x x xx xx x x x x xx xx xx x x xxxx xx xx x x x xxxx xx x x x xxx x xx xxx xxx x xx x xxx x x x xx xx x xxx xxxx x x x x x xx x xxxx xx x x xxxxxxxxx x xx xx x x xx xx x xxxxxxx xx x xxxxx x xxx x x xx xx x xxxx xx x xx x xxx x xx x xxx xxxxx xxx x xxx x xx x xxxxxxxx xxx x xxx x x xx xx x x x xxxxxxxxx x xx xx xx xxxxxxx x xx x xx x x x xx x => nil
The main API for iterating over the QRCode data is is_dark. Given an X and Y coordinate (starting from the top left) this will return true if the QRCode is dark at the location, false if it is light. We'll be using that in the next section.
Working with PNG files can be a bit difficult, but not for the reasons you might think. The primary library for working with PNG files is called RMagick. This is a wrapper around ImageMagick, which requires you to have ImageMagick, and compiled bindings to ImageMagick. To get around that when performance is not an issue, I like to use Chunky PNG. This is a library that implements PNG image handling, as well as some drawing functions, in pure Ruby. Predictably, this is slow. Quite slow. Using this in a web application with a lot of users is not a good idea. But for testing and development, it's just so easy to throw into the mix.
Installing it is very simple, just install the chunky_png gem. It doesn't require much in the way of dependencies. To create a new PNG image in memory, it's quite simple, just call ChunkyPNG::Image.new(width, height). The returned image object will be drawn to using a rect method (in this case, there are a few other drawing methods, but it's certainly not the focus of ChunkyPNG). And finally, to write the image out to disk, the save method.
Putting it Together
Iterating over the QR Code is a bit odd. Instead of providing some method to iterate over it, we simply use the each_index to iterate over the indices and the is_dark method to peer into the QR Code. Putting it together, we get this.
require 'rqrcode' require 'chunky_png' CELL_SIZE = 32 qr = RQRCode::QRCode.new(ARGV.join(' ')) img_size = qr.module_count*CELL_SIZE png = ChunkyPNG::Image.new(img_size, img_size) qr.modules.each_index do|x| qr.modules.each_index do|y| color = if qr.is_dark(x,y) ChunkyPNG::Color('black') else ChunkyPNG::Color('white') end png.rect( x*CELL_SIZE, y*CELL_SIZE, x*CELL_SIZE+CELL_SIZE, y*CELL_SIZE+CELL_SIZE, color, color ) end end png.save("qr.png")
The beginning part was covered above. We added a CELL_SIZE constant (if you want to expand this, you might want to add a command line argument) that defines the cell size in pixels. The iteration is done a bit strangely. The modules method is iterated over twice. The QR Code only gives one dimension, since all QR Codes must be square. We then simply test if it's dark, assign a color, then draw that color using the rect method. The rect method takes the two x,y coordinates of the top left and bottom right of the rect to draw. It also takes two colors, one being the stroke color and the other being the fill color. In this case we want them to be the same.
Finally, the save method is used. If you want to expand this to a more complete tool, you may want to move that to a command line argument as well, instead of relying on a hardcoded filename.