ETOOBUSY 🚀 minimal blogging for the impatient
QRate - High Level Design (provisional)
TL;DR
The main design for QRate, as it stands now.
I’m starting this little project with a few requirements:
- Encoding: this is where we take an input file to QRate and produce as
many QR images as needed to get all data.
- I’m not anticipating the need to handle very big files, so I assume that having the whole content in memory as a Perl variable is fine.
- I’d like to take a modular “pipeline-like” approach to be able to experiment with the insertion of additional steps, e.g. compression.
- The overall output should be a PDF file, so that it can be easily printed.
- Order of pages in the PDF MUST NOT matter. This is handy for a possible scanning phase later, so that the sheets might get scrambled but still be put together back in the right order.
- Protection level MUST be set to H (the maximum available).
- Data must be saved Base-64 encoded.
- Decoding: this is where we take something scanned and convert it back
to a file.
- images can be fed either as individual image files, in inside a single PDF.
- order of appearance of the images MUST NOT matter.
For the implementation:
- Encoding:
- the input file is optionally compressed with some
IO::Compress
module. - The result is encoded with [MIME::Base64][], which produces lines that are at most 77 octets long (76 of encoded payload plus a newline).
- This encoded form is divided into slices of at most 16 lines each, which corresponds to 1232 octets, leaving plenty of space (36 octets) for a header.
- The header will match the regular expression
qr{(?mxs: \A (?<n> 0 | [1-9]\d*) (?<more> [+.]) \n (?<data>.*) \z}
, where then
named capture represents the number of the slice (to allow for ordered reconstruction) andmore
is either+
(indicating that more chunks are expected after this one) or.
(indicating the last chunk).
- the input file is optionally compressed with some
- Decoding: well… everything in reverse order!
This will be the higher level program:
# ...
help_die() unless @ARGV == 3;
my ($command, $input, $output) = @ARGV;
if ($command eq 'encode') {
encode($input, $output);
}
elsif ($command eq 'decode') {
decode($input, $output);
}
else {
help_die();
}
sub help_die {
die "$0 <encode|decode> <input-file> <output-file>\n";
}
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;
}
sub decode ($input, $output) {
my $data = assemble_data(slice_reader_it(pdf_reader_it($input)));
path($output)->spew_raw(uncompress($data));
return 0;
}
The two $data
variables in encode()
and decode()
should hold the
same data, i.e. the compressed payload.
Cheers and stay safe!