Sticker Generator
At KAM MFF CUNI, we have about ~60 to ~70 computers scattered over the department, be them at desks of professors, older ones stocked in the basements or near-identical rack servers. Especially when older machines are shuffled around, it is difficult to track them, and the standard tiny inventory stickers keep falling off (and you have to keep looking up the number).
Therefore, we are making stickers to put on all our computers with some details about them. I used that as a pretense for learning some basic Cairo and I really like the paradigm (though I wish text manipulation was be easier). In case anyone would find this ever useful (or just a good scaffolding for their own Cairo Perl script), the script goes here; the default sticker size is to have height of a 5.25″ drive bay and width so that two rows fit nicely on A4.
#!/usr/bin/perl # (c) Petr Baudis 2011 MIT licence # # Generate PDF with stickers. # Expects tab-delimited columns on stdin: # hostname mac invnumber buydate use strict; use warnings; # -- User configuration -- our @paper = (598, 842); # A4, 72dpi our @margin = (72, 72); our @sticker = (227, 116); our $filename = 'nalepky.pdf'; our $fontface = 'Arial'; our $ffontface = 'Courier New'; # large, normal, small our @fontsize = (26, 15, 10); our @linespacing = (18, 4, 18); our $topmargin = 2; our $contact = '<contact information>'; # -- User configuration end -- use lib 'perl'; use List::Util qw(min); use List::MoreUtils qw(pairwise); use Cairo; our $surface = Cairo::PdfSurface->create ($filename, @paper); # Effective surface area our @surfsize = pairwise { $a - $b * 2 } @paper, @margin; # Grid layout on effective surface our @grid = pairwise { int($a / $b) } @surfsize, @sticker; # Grid surface area our @gridsurfsize = pairwise { $a * $b } @grid, @sticker; # Start of grid surface so that it is centered on the paper our @gridsurfstart = pairwise { ($a - $b) / 2 } @paper, @gridsurfsize; # Produce a context for single sticker, starting at coordinates [0,0] sub nalepka_cr { my ($surface, $cell) = @_; my @startM = pairwise { $a * $b } @sticker, @$cell; my @start = pairwise { $a + $b } @gridsurfstart, @startM; my $cr = Cairo::Context->create($surface); $cr->translate(@start); $cr->set_source_rgb(0, 0, 0); $cr->set_line_width(1); $cr; } # Centered text with top border at $$y. $size is index in font config above. sub nalepka_text { my ($cr, $y, $face, $slant, $weight, $size, $text) = @_; $$y += $linespacing[$size] / 2; $cr->select_font_face($face, $slant, $weight); $cr->set_font_size($fontsize[$size]); my $textents = $cr->text_extents($text); my $fextents = $cr->font_extents(); $$y += $fextents->{height}; $cr->move_to(($sticker[0] - $textents->{width}) / 2, $$y); $cr->show_text($text); $$y += $linespacing[$size] / 2; } sub nalepka { my ($cr, $host, $mac, $inv, $since) = @_; $cr->rectangle(0, 0, @sticker); $cr->stroke; my $invs = $since ? "$inv $since" : $inv; my $ypos = $topmargin - $linespacing[0] / 2; nalepka_text($cr, \$ypos, $fontface, 'normal', 'bold', 0, $host); nalepka_text($cr, \$ypos, $ffontface, 'normal', 'normal', 1, $mac); nalepka_text($cr, \$ypos, $fontface, 'normal', 'normal', 1, $invs); nalepka_text($cr, \$ypos, $fontface, 'italic', 'normal', 2, $contact); } my @table; while (<>) { chomp; my @col = split(/\t/); push @table, \@col; } my ($x, $y) = (0, 0); for my $c (@table) { nalepka(nalepka_cr($surface, [$x, $y]), @$c); $y++; if ($y >= $grid[1]) { $y = 0; $x++; if ($x >= $grid[0]) { my $cr = Cairo::Context->create($surface); $cr->show_page; ($x, $y) = (0, 0); } } }
It is a nice intro to Cairo, you sucked me in :)
by the way, you have an extra line:
use KAM::Machines;
I guess you edited an existing file to start.
Thanks for sharing -F2
Here is a question – why did you not use PDF::Labels or PostScript::MailLabels? looks like the latter is simpler layout-wise but does not allow to set font sizes, the former however I found no issues with scanning the docs.
Just curious if you ran into some limitation…if you just wanted to learn Perl+Cairo, that’s a fine answer too :)
Good catch – the original script does not use stdin for input, but the database of machines accessed through KAM::Machines. I removed the useless use now.
Admittedly, I have done no research in existing CPAN modules. My original idea has been to use latex, but failing to find a trivial way to do it (I did not get the labels class to work at all, somehow), a friend suggested Cairo so I went straight for that. :-)
But looking at the modules, even PDF::Labels does not allow any kind of rich formatting – I’m not even sure if it can center text! To me it seems that making the hostname big and bold and the MAC fixed-width wouldn’t be possible with these modules.