ETOOBUSY 🚀 minimal blogging for the impatient
QRate - iterators for encoding
TL;DR
Dealing with the iterators for encoding in QRate.
We already saw that assembling the PDF is done based on an iterator function that is supposed to provide PNG images:
sub encode ($input, $output) {
my $data = compress(path($input)->slurp_raw, 9) or die "compress()\n";
assemble_pdf(qrcoder_it(slicer_it($data)))->save($output);
return 0;
}
As we can see, there are two nested iterators at play:
qrcoder
returns the QR codes as PNG images, based on a right-sized slice of data provided byslicer
, which takes the overall data and slices it accordingly.
Function qrcoder_it
is a factory function, i.e. one that returns a
function to be called later:
sub qrcoder_it ($it) {
my $qrcode = Imager::QRCode->new(
size => 8,
margin => 2,
mode => '8-bit',
version => 1,
level => 'H',
casesensitive => 1,
lightcolor => Imager::Color->new(255, 255, 255),
darkcolor => Imager::Color->new(0, 0, 0),
);
return sub {
my $data = $it->() // return;
my $img = $qrcode->plot($data)->to_paletted;
my $retval;
$img->write(data => \$retval, type => 'png')
or die "Failed to write: " . $img->errstr;
return $retval;
}
}
As we can see, the whole point is to call the inner iterator to get the next chunk of data, and using Imager::QRCode to do the right magic. The returned value is a PNG image in memory.
Last, let’s take a look at the other factory function, i.e. the input data slicer:
use constant LINES_PER_SLICE => 16;
sub slicer_it ($data) {
my @encoded = split m{\n}mxs, encode_base64($data);
my $n_slice = 0;
return sub {
return unless @encoded;
my @payload = splice @encoded, 0, LINES_PER_SLICE;
my $header = $n_slice++ . (@encoded ? '+' : '.');
return join "\n", $header, @payload, '';
};
}
Again, it’s a factory because it returns a sub that can be called to
gather all the different pieces. In addition to slicing, ths function
also does some framing, i.e. it puts a small header on top of each
slice to keep track of which slice we are dealing with. In particular,
we’re adding a sequence number and an indicator of wheter more slices
are in line (with the +
mark) or not (the final slice has marker .
).
This will come handy later, when we will reassemble the whole thing
(possibly from unsorted scannerized pictures).
The choice to fit 16 lines per slice comes from the limit of a High-redundancy QR code for binary data, which for Imager::QRCode is set at 1268 bytes as we saw in a previous post
So… we’re done with the encoding!