Producing stickers and QR codes on a label printer with Perl

Label Maker

Article from Issue 183/2016

Labels bring order to the mess of wires hiding behind the Perlmeister's home routers and organize the treasures hoarded in a multitude of boxes. With just some tweaking, the Dymo LabelWriter even prints on Linux.

For decades, I have organized my network cables with permanent labels (Figure 1) printed on a portable device by Brother. However, it bugs me that every time I print a label the device wastes raw material (Figure 2), which I have to buy in the form of fairly expensive cartridges.

Figure 1: Fast diagnostics thanks to systematic organization: the configured IP address and WiFi SSID on the router.
Figure 2: The portable Brother label printer wastes expensive raw material for labels.

Brother's engineers deliberately seem to have built the machine to use twice as much label ribbon as I actually need, boosting the ribbon cartridge turnover as a side effect! If there is a hell for committing such wanton waste, I expect that the product managers responsible for this feature will be there some time soon. Apart from this, typing strings like takes ages on the unorthodox keyboard; using a desktop computer would be far quicker.

Faster than Manual

Recently, I found a label printer on eBay that I was able to connect with my Ubuntu desktop via a USB port. The LabelWriter 450 Turbo by Dymo (Figure 3) cost me around $40 secondhand, and I got it working in no time. What you need is the printer's CUPS system driver, which is available as source code [1].

Figure 3: The LabelWriter 450 Turbo also prints on Linux.

After installing a couple of additional packages, such as libcups2-dev and libcupsimage2-dev, with apt-get, I was able to complete the build using ./configure; make without any trouble. Then, sudo make install installed the CUPS files. It wasn't until later that I discovered that my Ubuntu distribution has an easy-to-install package and that

sudo apt-get install printer-driver-dymo

would do the job in one fell swoop.

After installing the CUPS drivers, Ubuntu detected the new label printer without much ado. A call to

lpstat -p -d

shows the available printers and their states (e.g., printer LabelWriter-450-Turbo is idle). If you are interested in the label sizes the driver supports, you can type

lpoptions -p LabelWriter-450-Turbo -l

to query them. Below Printers in the system settings, you should see a dialog like that in Figure 4 with a Printer Options submenu, in which you can configure the dimensions of the labels you are using.

Figure 4: Ubuntu detects the label printer and serves up the label size configuration options.

I bought a roll of labels numbered "30330" featuring 500 return address labels. I wasn't really worried about what professional shippers use these labels for; it was the handy 19x51mm format that interested me.

The labels are shot vertically out of the thermo-printer, so I needed to adjust the lettering to match; in other words, rotated through 90 degrees in landscape. Now, how does the computer actually send the text to be printed to the device?

The CUPS printing system [2] uses the lpr command for this:

lpr -P LabelWriter-450-Turbo -o PageSize=w54h144.1 label.pdf

As you can see, the option -P LabelWriter-450-Turbo tells CUPS to select the label printer if some other device is configured as the default printer. The label dimensions are set using the PageSize option, which expects the length and width – not in millimeters or inches, but in points.

Some brief research on Google showed that you need to multiply millimeters by around 1.8 to convert to points (Figure 5). To match the 19x51mm labels, I thus needed rectangles in a format of 54x144.1 points; the parameter I used – PageSize= w54h144.1 – defined precisely that size.

Figure 5: According to the conversion table I used, 19 millimeters are about 54 points.

The final argument the lpr command expects is the name of the PDF file with the text to be printed.

Formatting as PDF

Listing 1 [3] generates the required print layout with the help of the CPAN PDF::Create module. The script expects a string at the command line, for example,

label-format "Huzzah!"

and dumps it into the middle of a PDF document in landscape format measuring 144.1x54 points. Note that the text needs to be quoted if it contains blanks. Some shell dialects also expect you to quote the exclamation marks, which the shell would otherwise interpret as a call to its history function. Looking at the code, the reasoning behind the expression

20 / length( $string ) * 11;

in line 16 of Listing 1 is that my experiments revealed that a string of eleven 20-point characters would precisely fill out the label lengthwise. Longer strings necessitate a linear font size reduction to fit on the label. This simple formula works amazingly well even though the Helvetica font I chose does not use fixed-width spacing but assigns a proportional amount of space to each letter in the document.

Listing 1


01 #!/usr/local/bin/perl -w
02 use strict;
03 use PDF::Create;
04 use Encode qw( _utf8_on );
06 my( $string ) = @ARGV;
07 _utf8_on( $string );
09 die "usage: $0 string" if !defined $string;
11 my $pdf = PDF::Create->new(
12   filename => 'label.pdf' );
14 my $width     = 144;
15 my $height    = 54;
16 my $font_size = 20 / length( $string ) * 11;
17 my $adjust    = $font_size/7;
19 my $page = $pdf->new_page(
20  'MediaBox' => [ 0, 0, $width, $height ] );
22 my $f1 = $pdf->font(
23   Subtype  => 'Type1',
24   Encoding => 'WinAnsiEncoding',
25   BaseFont => 'Helvetica'
26 );
28 $page->stringc( $f1,
29   $font_size,
30   $width/2 + 10,
31   ($height - $font_size)/2 + $adjust,
32   $string );
34 $pdf->close;
36 system( "acroread label.pdf" );

The stringc() method called in line 28 prints the string passed in as its fifth argument in the middle of the PDF document. The font size is the second parameter, followed by the x and y coordinates of the center of the label. The x value runs from left to right in the document, and the y value from the bottom to the top edge. For some strange reason, the text string was always too low in the document in my experiments; this prompted me to introduce the $adjust variable in line 17 to move the string upward by one seventh of the font size.

Thanks to these simple tricks, the label printer positions both short and slightly longer lines of text with up to 25 characters perfectly at the center of the document; the call to cmd-print fires up the printer, and the label comes flying out.

The process for handling non-standard characters, like umlauts, is something special. Because the string passed in at the command line is already UTF-8 encoded, but Perl interprets it as ASCII by default, you would normally see the printer output cryptic characters rather than what you wanted if left to its own devices. The _utf8_on function from the Encode module tells the interpreter that the string you passed in is already UTF-8 encoded and that there is thus no need to recode.

The CPAN module for creating PDFs can do more than just position strings on a white background; it can also position images. Of course, a thermal printer that can only print black without grayscale limits your artistic options somewhat, but what about printing sticky labels with barcode?

You can scan QR codes with a smartphone, opening up the option of assigning inventory numbers to devices printed as QR codes on labels that you could then scan and, if needed, store next to an asset number in a database or a filing system like Evernote, say, for a scanned owner's manual.

Listing 2 processes the text passed in at the command line,

dymo-qr "Linux Pro Magazine!"

and uses the CPAN Image::PNG::QRCode module to generate the QR code shown in Figure 6. The free QR Reader iPhone scanner app detected the QR code on the label still in the printer, even without me tearing off the label. Figure 7 shows the plain text version in the app.

Figure 6: The scanning tool looks for the QR code in the photo, …
Figure 7: … identifies the code, and outputs the encoded message.

Because the CPAN module I used to generate the QR code can only output PNG-formatted images, Listing 2 uses a clone of the CPAN Image::Magick module as an easy option for converting image formats. Installing the module involves some manual attention because it needs a number of developer packages from various libraries. Fortunately, a kind person from Ubuntu went through the trouble of generating a perlmagick package that provides the feature set with the Graphics::Magick module.

Listing 2


01 #!/usr/local/bin/perl -w
02 use strict;
03 use Image::PNG::QRCode 'qrpng';
04 use Graphics::Magick;
05 use File::Temp qw( tempfile );
06 use PDF::Create;
08 my( $text ) = @ARGV;
09 die "usage: $0 text" if !defined $text;
11 my $width     = 144;
12 my $height    = 54;
13 my $pdffile   = "label.pdf";
15 my( $fh, $pngfile ) =
16   tempfile( UNLINK => 1,
17             SUFFIX => ".png" );
19 ( my $jpgfile = $pngfile ) =~
20   s/\.png$/.jpg/;
22 qrpng ( text => $text,
23         out  => $pngfile );
25 my $img = Graphics::Magick->new;
26 $img->Read( $pngfile );
27 $img->Write( $jpgfile );
29 my $pdf = PDF::Create->new(
30   filename => $pdffile );
32 my $jpg = $pdf->image( $jpgfile );
34 my $page = $pdf->new_page(
35  'MediaBox' => [ 0, 0, $width, $height ] );
37 $page->image( 'image' => $jpg,
38     'xscale' => 0.5, 'yscale' => 0.5,
39     'xpos'   => 50,  'ypos'   => 0 );
41 $pdf->close;

The module converts the temporary PNG file managed using the File::Temp module into a JPG file, which is then picked up by PDF::Create and dumped into a PDF document. The label.pdf file that this process creates is then forwarded to the label printer with the previously shown lpr command.

The only disadvantage with my label printer is that you can't buy rugged plastic labels for it. It only does black thermal printing on white paper or transparent film, both of which are self-adhesive. That said, however, the more expensive printer model called LabelWriter Duo, seems to have more options. I might just go bargain hunting again in the near future.


  1. Linux SDK for the label printer:
  2. Sweet, Michael. CUPS: Common UNIX Printing System. Sams Publishing, 2001.
  3. Code for this article:

The Author

Mike Schilli works as a software engineer in the San Francisco Bay Area. He can be contacted at Mike's homepage can be found at

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Perl: OpenOffice Label Merge

    OpenOffice offers a selection of preconfigured formats for users who need to print their own self-adhesive labels. Perl feeds the address data to the document.

  • KTools: KBarcode


    Having trouble creating price tags, address labels, or business cards? Help is at hand with the KDE program KBarcode.

  • gLabels

    Add an individual touch to invitations or cards to help your event start with a bang.

  • Command Line – CUPS

    Using the Common Unix Printing System, you can configure and manage your printer from the command line.

  • AirPrint in Linux

    If your home network includes a Linux machine, you have access to everything you need to share your printers on the network as AirPrint-enabled devices.

comments powered by Disqus

Direct Download

Read full article as PDF:

Price $2.95


njobs Europe
Njobs Netherlands Njobs Deutschland Njobs United Kingdom Njobs Italia Njobs France Njobs Espana Njobs Poland
Njobs Austria Njobs Denmark Njobs Belgium Njobs Czech Republic Njobs Mexico Njobs India Njobs Colombia