ESC/POS is a binary protocol for speaking to receipt printers. It contains a command for printing QR Codes on compatible printers. The PHP library escpos-php is used for generating these commands in PHP. This post will show you how to use it to generate QR codes on your receipt printer.
For printers which don’t support this command, a second option is available: sending the QR code as an image.
Getting started
First up, you need your receipt printer to be working with escpos-php. Here are some resources about how to go about that:
- mike42/escpos-php on github (the most up-to-date reference)
- What is ESC/POS, and how do I use it?
- Setting up an Epson receipt printer
- Getting a USB receipt printer working on Linux (or Windows)
Option 1: Direct printing
This method sends QR codes directly. From the documentation, the syntax for this command is:
qrCode($content, $ec, $size, $model)
Print the given data as a QR code on the printer.
- string $content: The content of the code. Numeric data will be more efficiently compacted.
- int $ec Error-correction level to use. One of Printer::QR_ECLEVEL_L (default), Printer::QR_ECLEVEL_M, Printer::QR_ECLEVEL_Q or Printer::QR_ECLEVEL_H. Higher error correction results in a less compact code.
- int $size: Pixel size to use. Must be 1-16 (default 3)
- int $model: QR code model to use. Must be one of Printer::QR_MODEL_1, Printer::QR_MODEL_2 (default) or Printer::QR_MICRO (not supported by all printers).
The below code snippets are directly from the QR code printing demo, showing how the output changes with the options given.
Simple example
This is the simplest use, with all default options. QR codes can be aligned in the same way as text or images on the page:
// Most simple example
title($printer, "QR code demo\n");
$testStr = "Testing 123";
$printer -> qrCode($testStr);
$printer -> text("Most simple example\n");
$printer -> feed();
// Demo that alignment is the same as text
$printer -> setJustification(Printer::JUSTIFY_CENTER);
$printer -> qrCode($testStr);
$printer -> text("Same example, centred\n");
$printer -> setJustification();
$printer -> feed();
Data encoding
This is a demonstration of saving different types of data in a code. Numeric data is packed more efficiently than text. Binary data can also be stored.
// Demo of numeric data being packed more densly
title($printer, "Data encoding\n");
$test = array(
"Numeric" => "0123456789012345678901234567890123456789",
"Alphanumeric" => "abcdefghijklmnopqrstuvwxyzabcdefghijklmn",
"Binary" => str_repeat("\0", 40));
foreach($test as $type => $data) {
$printer -> qrCode($data);
$printer -> text("$type\n");
$printer -> feed();
}
Error correction levels
QR codes support fout levels of error correction. More error correction results in larger, but more durable codes:
// Demo of error correction
title($printer, "Error correction\n");
$ec = array(
Printer::QR_ECLEVEL_L => "L",
Printer::QR_ECLEVEL_M => "M",
Printer::QR_ECLEVEL_Q => "Q",
Printer::QR_ECLEVEL_H => "H");
foreach($ec as $level => $name) {
$printer -> qrCode($testStr, $level);
$printer -> text("Error correction $name\n");
$printer -> feed();
}
Code size
The defauly codes are quite small. Each pixel can be blown up, up to 16x, using the size option:
// Change size
title($printer, "Pixel size\n");
$sizes = array(
1 => "(minimum)",
2 => "",
3 => "(default)",
4 => "",
5 => "",
10 => "",
16 => "(maximum)");
foreach($sizes as $size => $label) {
$printer -> qrCode($testStr, Printer::QR_ECLEVEL_L, $size);
$printer -> text("Pixel size $size $label\n");
$printer -> feed();
}
QR models
QR models have different appearances, storage parameters and physical sizes. The default (Model 2) is most common. The printer used here does not support micro QR codes, and used Model 2 as a fallback.
// Change model
title($printer, "QR model\n");
$models = array(
Printer::QR_MODEL_1 => "QR Model 1",
Printer::QR_MODEL_2 => "QR Model 2 (default)",
Printer::QR_MICRO => "Micro QR code\n(not supported on all printers)");
foreach($models as $model => $name) {
$printer -> qrCode($testStr, Printer::QR_ECLEVEL_L, 3, $model);
$printer -> text("$name\n");
$printer -> feed();
}
Note
To run the snippets, you need to initialise the printer, and define a title() function to print headings, like so:
<?php
/* Demonstration of available options on the qrCode() command */
require __DIR__ . '/autoload.php';
use Mike42\Escpos\Printer;
use Mike42\Escpos\PrintConnectors\FilePrintConnector;
$connector = new FilePrintConnector("/dev/usb/lp0");
$printer = new Printer($connector);
// ....
// Cut & close
$printer -> cut();
$printer -> close();
function title(Escpos $printer, $str) {
$printer -> selectPrintMode(Printer::MODE_DOUBLE_HEIGHT | Printer::MODE_DOUBLE_WIDTH);
$printer -> text($str);
$printer -> selectPrintMode();
}
Option 2: Printing codes as images
Not all printers can generate QR codes natively. The work-around is to generate a QR code as an image on the computer, and then send that image to the printer. This is slightly slower, so if you print a lot of codes, you should consider upgrading your printer.
First up, fetch a copy of phpqrcode and generate some codes. I wont attempt to document the whole library here, but in short, it supports most of the same features as the native QR command. To generate a code, you simply use QRcode::png:
require_once("phpqrcode/qrlib.php");
QRcode::png("testing123", "test.png", 'L', 10, 0);
To print a PNG image, use the bitImage() command (the graphics command is also only available on newer printers):
require __DIR__ . '/autoload.php';
use Mike42\Escpos\EscposImage;
use Mike42\Escpos\PrintConnectors\FilePrintConnector;
$img = EscposImage::load("test.png"); // Load image
$connector = new FilePrintConnector("/dev/usb/lp0"); // Add connector to your printer here
$printer = new Printer($connector);
$printer -> bitImage($img);
$printer -> feed();
$printer -> text("Code printed from image\n");
$printer -> cut();
$printer -> close();
A more sophisticated way to hack in phpqrcode would be to add this new code as a different implementaton of the qrCode function. Other improvements are:
- Use temporary files to avoid concurrency issues:
- Where possible, expand the code on the printer, to send less data
<?php
require __DIR__ . '/autoload.php';
use Mike42\Escpos\Printer;
require_once("phpqrcode/qrlib.php");
class EscposQrImgPrinter extends Printer {
function qrCode($content, $ec = self::QR_ECLEVEL_L, $size = 3, $model = self::QR_MODEL_2) {
// Validate inputs
self::validateString($content, __FUNCTION__);
self::validateInteger($ec, 0, 3, __FUNCTION__);
self::validateInteger($size, 1, 16, __FUNCTION__);
$model = self::QR_MODEL_2; // Only Model 2 supported in phpqrcode, change back to it.
$sizeMod = 0;
if($size % 2 == 0) { // Optimisation to enlarge codes on the priner, sending 1/4 of the data.
$size /= 2;
$sizeMod = self::IMG_DOUBLE_HEIGHT | self::IMG_DOUBLE_WIDTH;
}
// Map error-correction to phpqrcode levels
$ecMap = array(QR_ECLEVEL_L => 'L',
QR_ECLEVEL_M => 'M',
QR_ECLEVEL_Q => 'Q',
QR_ECLEVEL_H => 'H');
// Create QR code in temp file, and print it.
$tmpfname = tempnam(sys_get_temp_dir(), "escpos-php");
QRcode::png("testing123", $tmpfname, $ecMap[$ec], $size, 0);
$img = new EscposImage($tmpfname);
$this -> bitImage($img, $sizeMod);
unlink($tmpfname);
}
}
This new class uses phpqrcode in the background instead, and can be accessed with the same function calls as the parent class:
require __DIR__ . '/autoload.php';
use Mike42\Escpos\Printer;
use Mike42\Escpos\PrintConnectors\FilePrintConnector;
$connector = new FilePrintConnector("/dev/usb/lp0");
$printer = new EscposQrImgPrinter($connector);
$testStr = "Testing 123";
$printer -> qrCode($testStr);
$printer -> text("Most simple example\n");
$printer -> feed();
$printer -> cut();
$printer -> close();
The only visible difference between the implementations is a few pixels of spacing below the image.