cp2up.sh — fits the important part of Canada Post print labels two per sheet

blurred (for privacy) 2-up landscape page of Canada Post Tracked Package (to USA) shipping labels made by this script
no you will not read my 2-up shipping labels

If you need to ship things, you’re probably not too keen on queuing at the post office right now. Canada Post’s Ship Online service is pretty handy if you have a printer. The PDFs it produces are okay to print on plain paper, but if you’re using full-sheet labels like Avery 5165 you’re going to waste half a sheet of expensive labels.

If you’ve got two parcels to mail, this shell script will extract the right side of each page and create a single 2-up PDF with both your labels on the same page. You will need:

On my Ubuntu system, you can get good-enough¹ versions by doing this:

sudo apt install poppler-utils netpbm img2pdf

The code:

#!/bin/bash
# cp2up.sh - fits the important part of Canada Post print labels 2 per sheet
# scruss, 2021-05 - CC-BY-SA
# hard-coded input name (document.pdf)
# hard-coded output name (labels-2up.pdf)
# accepts exactly two labels (sorry)

dpi=600
width_in=11
height_in=8.5
# png intermediate format uses pixels per metre
dpm=$(echo "scale=3; $dpi * 1000 / 25.4" | bc)
# calculated pixel sizes, truncated to integer
half_width_px=$(echo "$width_in * $dpi / 2" | bc | sed 's/\..*$//')
height_px=$(echo "$height_in * $dpi" | bc | sed 's/\..*$//')

pdftoppm -mono -r "$dpi" -x "$half_width_px" -y 0 \
	 -W  "$half_width_px" -H "$height_px" document.pdf labels
pnmcat -lr labels-1.pbm labels-2.pbm |\
    pnmtopng -compression 9 -phys "$dpm" "$dpm" 1 > labels.png \
    && rm labels-1.pbm labels-2.pbm
# fix PDF time stamps
now=$(date --utc --iso-8601=seconds)
img2pdf -o labels-2up.pdf --creationdate "$now" --moddate "$now" \
	--pagesize "Letter^T" labels.png \
    && rm labels.png 

# saved from:
# history | tail | awk '{$1=""; print}' |\ 
#           perl -pwle 'chomp;s/^\s+//;' > cp2up.sh

It’s got a few hard-coded assumptions:

  • input name (document.pdf);
  • output name (labels-2up.pdf);
  • accepts exactly two labels (sorry).

Clever people could write code to work around these. Really clever people could modify this to feed a dedicated label printer.

Yes, I could probably have done all this with one ImageMagick command. When ImageMagick’s command line syntax begins to make sense, however, it’s probably time to retire to that remote mountain cabin and write that million-word thesis on a manual typewriter. Also, ImageMagick’s PDF generation is best described as pish.

One of the issues that this script avoids is aliasing in the bar-codes. For reasons known only to the anonymous PDF rendering library used by Canada Post, their shipping bar-codes are stored as smallish (780 × 54 px) bitmaps that are scaled up to a 59 × 19 mm print size. Most PDF viewers (and Adobe Viewer is one of these) will anti-alias scaled images, making them slightly soft. If you’re really unlucky, your printer driver will output these as fuzzy lines that no bar-code scanner could ever read. Rendering them to high resolution mono images may still render the edges a little roughly, but they’ll be crisply rough, and scanners don’t seem to mind that.

split image of simulated printed barcode: top image is five indistinct black-grey bars merging into a white background, bottom image is the same vertical lines, rendered crisply but showing some slightly rough edges
fuzzy vs crisply rough: scaled image (top) vs direct-rendered (bottom), at simulated 600 dpi laser print resolution

¹: Debian/Ubuntu’s netpbm package is roughly 20 years out of date for reasons that only a very few nerds care about, and the much better package is blocked by Debian’s baroque and gatekeepery packaging protocol. I usually build it from source for those times I need the new features.