Tag Archives: printing

How to print PDF417 codes with escpos-php

This post is a reference for printing PDF417 2-dimensional codes to a receipt printer, using the escpos-php.

I’ve got an older post about printing QR codes with escpos-php, which follows the same format and has some more background and links if you haven’t printed receipts from PHP before.

Straight the documentation, the syntax for the command that I’m demonstrating is:

pdf417Code($content, $width, $heightMultiplier, $dataColumnCount, $ec, $options)

Print a two-dimensional data code using the PDF417 standard.

  • string $content: Text or numbers to store in the code
  • number $width: Width of a module (pixel) in the printed code. Default is 3 dots.
  • number $heightMultiplier: Multiplier for height of a module. Default is 3 times the width.
  • number $dataColumnCount: Number of data columns to use. 0 (default) is to auto-calculate. Smaller numbers will result in a narrower code, making larger pixel sizes possible. Larger numbers require smaller pixel sizes.
  • real $ec: Error correction ratio, from 0.01 to 4.00. Default is 0.10 (10%).
  • number $options: Standard code Printer::PDF417_STANDARD with start/end bars, or truncated code Printer::PDF417_TRUNCATED with start bars only.

These PDF417 snippets above appear in the examples of escpos-php.

Simple example

A basic code that just says ‘testing 123’, and a demonstration of a narrower code that has been aligned:

2016-09-escpos-pdf417-01-demo
// Most simple example
title($printer, "PDF417 code demo\n");
$testStr = "Testing 123";
$printer -> pdf417Code($testStr);
$printer -> text("Most simple example\n");
$printer -> feed();

// Demo that alignment is the same as text
$printer -> setJustification(Printer::JUSTIFY_CENTER);
$printer -> pdf417Code($testStr, 3, 3, 2);
$printer -> text("Same content, narrow and centred\n");
$printer -> setJustification();
$printer -> feed();

Error correction

This implementation accepts an error correction ratio as a percentage. The minimum is 1%, the highest is 400%, expressed as a decimal (0.01 – 4.00).

Higher error correction settings create lager codes that are more resilient to scanning errors due to damage.

2016-09-escpos-pdf417-02-ec
// Demo of error correction
title($printer, "Error correction\n");
$testStr = "Testing 123";
$ec = array(0.1, 0.5, 1.0, 2.0, 4.0);
foreach ($ec as $level) {
    $printer -> pdf417Code($testStr, 3, 3, 0, $level);
    $printer -> text("Error correction ratio $level\n");
    $printer -> feed();
}

Pixel size (width)

The same example string, with some different module widths. Note that the blocks in the code scale in bot directions when the width is changed.

Larger print is easier for a scanner to read, but takes up more paper.

2016-09-escpos-pdf417-02-pixel
// Change size
title($printer, "Pixel size\n");
$testStr = "Testing 123";
$sizes = array(
    2 => "(minimum)",
    3 => "(default)",
    4 => "",
    8 => "(maximum)");
foreach ($sizes as $size => $label) {
    $printer -> pdf417Code($testStr, $size);
    $printer -> text("Module width $size dots $label\n");
    $printer -> feed();
}

Height multiplier

The height of the modules in the code can also be changed, stretching it do a different degree. PDF417 that are too vertically squashy are more prone to scanning errors.

2016-09-escpos-pdf417-03-height
// Change height
title($printer, "Height multiplier\n");
$testStr = "Testing 123";
$sizes = array(
    2 => "(minimum)",
    3 => "(default)",
    4 => "",
    8 => "(maximum)");
foreach ($sizes as $size => $label) {
    $printer -> pdf417Code($testStr, 3, $size);
    $printer -> text("Height multiplier $size $label\n");
    $printer -> feed();
}

Data column count

The of data columns to print in the code can be customised to produce a narrower code. But beware, if you request a code that’s too big for the paper, nothing will be printed!

2016-09-escpos-pdf417-04-datacol
// Change data column count
title($printer, "Data column count\n");
$testStr = "Testing 123";
$columnCounts = array(
    0 => "(auto, default)",
    1 => "",
    2 => "",
    3 => "",
    4 => "",
    5 => "",
    30 => "(maximum, doesnt fit!)");
foreach ($columnCounts as $columnCount => $label) {
    $printer -> pdf417Code($testStr, 3, 3, $columnCount);
    $printer -> text("Column count $columnCount $label\n");
    $printer -> feed();
}

Truncated code option

Use this setting to select the alternative, ‘truncated’ code format.

2016-09-escpos-pdf417-05-options
// Change options
title($printer, "Options\n");
$testStr = "Testing 123";
$models = array(
    Printer::PDF417_STANDARD => "Standard",
    Printer::PDF417_TRUNCATED => "Truncated");
foreach ($models as $model => $name) {
    $printer -> pdf417Code($testStr, 3, 3, 0, 0.10, $model);
    $printer -> text("$name\n");
    $printer -> feed();
}

Notes

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();
}

In the QR code post, I posted a fallback which used software rendering. As I don’t have a PHP-based PDF417 code library, you will need a printer which supports them to be blue to use these examples.

Good luck!

How to print red/black on an impact receipt printer

I recently deployed an Epson TM-U220 impact receipt printer. These printers work by striking a ribbon onto the paper, like a type-writer. One of the up-sides to using these intead of a thermal printer is the ability to install a red/black ribbon in place of the default (black) one:

2015-10-colour-receipt-printing-1

2015-10-colour-receipt-printing-2

I connected up my printer using a USB-parallel cable, so my previous posts (Linux, Windows) apply for the connector setup.

Using the escpos-php driver on GitHub, a line of red text is printed like this:

<?php
/*
 * Example of two-color printing, tested on an epson TM-U220 with two-color ribbon installed.
 */
require __DIR__ . '/autoload.php';
use Mike42\Escpos\Printer;
use Mike42\Escpos\PrintConnectors\FilePrintConnector;
$connector = new FilePrintConnector("php://stdout");
$printer = new Printer($connector);

try {
    $printer = new Escpos($connector);
    $printer -> text("Hello World!\n");
    $printer -> setColor(Printer::COLOR_2);
    $printer -> text("Red?!\n");
    $printer -> setColor(Printer::COLOR_1);
    $printer -> text("Default color again?!\n");
    $printer -> cut();
} finally {
    /* Always close the printer! */
    $printer -> close();
}

With this result:

2015-10-colour-receipt-printing-3

How to connect a USB receipt printer up on Mac OS X

This post will show you how to set up a USB receipt printer on Max OS X. These steps were written on Yosemite, but should work on 10.6 onwards (ie, also Snow Leopard through to El Capitan).

This is another post in a series, which has so far covered direct USB printing on Windows and Linux. The printer tested here is this Epson TM-T20:

2015-03-printer-back
2015-03-printer-top

CUPS is the printing system that’s used on Mac, but most users would be more familiar with the system print dialog:

2015-11-mac-usb-epson-12

In our case, we need to set up the printer via the CUPS web interface. This is accessed via a web browser at this address:

http://localhost:631

At first, you will get knocked back:

2015-11-mac-usb-epson-0

To fix this up, open up Applications → Utilities → Terminal and type in:

cupsctl WebInterface=yes

You can then reload the browser and click through to Administration:

2015-11-mac-usb-epson-1

Click Add Printer and log in:

2015-11-mac-usb-epson-2

2015-11-mac-usb-epson-3

Select the USB printer from the list, and optionally share it:

2015-11-mac-usb-epson-4

2015-11-mac-usb-epson-5

Click Select Another Make/Manufacturer, and select Raw → Raw Queue:

2015-11-mac-usb-epson-6

2015-11-mac-usb-epson-7

2015-11-mac-usb-epson-8

Use the defaults for the other options:

2015-11-mac-usb-epson-9

Test print

Type some junk into a file called foo.txt and attempt to print it, using the CUPS printer name:

nano foo.txt
lpr -o raw -H localhost -P EPSON_TM-T20 foo.txt

The prints will be delayed for a few moments, as CUPS spools the jobs.

Disable CUPS web

Once you’re done, for security reasons you should reset this option from before, to disable the web interface to CUPS:

cupsctl WebInterface=no

Howto: QR Codes on receipts with escpos-php

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:

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:

2015-04-escposqr-01-demo
// 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.

2015-04-escposqr-02-dataencoding
// 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:

2015-04-escposqr-03-errorcorrection
// 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:

2015-04-escposqr-04-pizelsize
// 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.

2015-04-escposqr-05-qrmodel
// 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.

How to use a Raspberry Pi as a print server

This post is designed for people who want to share a simple USB printer, such as this receipt printer, over the network.

Usually, you just connect up the printer to the computer like this:

2015-04-rpi-printer1

But if you are sending the print jobs from a central server, you would instead follow these steps, and hook up a Raspberry Pi near the printer to pass on the print-outs for you:

2015-04-rpi-printer2

This post will show you a very fuss-free way to do this. Because of its simplicity, if you have multiple computers printing (read: you need a server that can spool), or need two-way communication with the printer, then this setup will not be sufficient for your use case.

One-off setup

If your printer is /dev/usb/lp0, then the command to run is:

nohup nc -klp 9100 > /dev/usb/lp0 2> /dev/null&

There is quite a lot going on in this command, so I’m going to break it down into parts and explain what each one does.

nohup
Lets the command keep running after you log-out.
nc -klp 9100
Listens on port 9100 (-lp), and returns to listening after each connection (-k)
> /dev/usb/lp0
Redirects any incoming data to the printer device
2> /dev/null
Suppresses errors by sending them to /dev/null
&
Runs the command in the background so that you can keep using the terminal.

Run every boot

Simply schedule the command in cron as a @reboot task.

crontab -e

And add the line:

@reboot nohup nc -klp 9100 > /dev/usb/lp0 2> /dev/null&

Note that if you reboot the printer, you will also need to reboot the raspberry pi to get it to reconnect without logging in!

Send some tests

From a computer somewhere else on the network, send a test print-out:

echo "Hello world" | nc 10.x.x.x 9100

If the target printer is a thermal receipt printer, then you could also use escpos-php to send it more elaborate commands:

<?php
$fp = fsockopen("10.x.x.x", 9100);
/* Print a "Hello world" receipt" */
$printer = new Escpos($fp);
$printer -> text("Hello World!\n");
$printer -> cut();
fclose($fp);