Documentation
Raw Legacy
- ✅ 2.0 | ⛔ 1.9 | ...
qz-tray.js
, which is fully backwards compatible with the 2.0 version.
The following code can be used for raw printing only. If you are unsure what raw printing is, please refer to What is Raw Printing?
- Note: Some Windows drivers, such as Zebra's ZDesigner driver for Microsoft Windows, will support raw printing alongside pixel-based/PostScript printing. You may use those drivers or a raw-generic/text-only driver to send raw commands, but the generic driver will not be able to print images/PDF/HTML.
-
An attached printer is configured for raw printing: Windows | Mac | Linux
- Raw printing output may be tested without hardware via a printer emulator
- Language Aliases have moved to the 2.1 tutorial
- Base64 Printing
- Base64 Raw Images
- XML Printing
- Printing from File
- Printing to File/Filesystem
- Printing to URL
- Advanced Print Spooling
- Special Characters
- Right-To-Left Languages
- Combining Techniques
- Custom Job Name
- Unix/Linux Alternate Printing
- Chaining Print Requests
- Queuing Print Requests
- Data Chunking
Boilerplate code to send raw commands to any printer. See also ZPL, EPL, ESC/POS for specific examples. If your raw language is missing, please contact support.
Note: Reminder to supply <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
if printing Unicode.
var config = qz.configs.create("Printer Name");
var data = [
'Raw Data\n',
'More Raw Data\n',
'Even More Raw Data\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
Note: The raw commands provided may not work with your printer. Please reference your printer's programming guide for more information.
- Albeit obvious, each example needs a trigger, such as a button, .e.g.
<input type="button" onclick="printStuff()" value="Print"></input>
Example for sending raw ZPL commands to a printer
Note:
language: "foo"
is only required if providing a raw image type in the data stream. Note: Labels are too small? Try this experimental code to force a 203dpi label to 300dpi.
var config = qz.configs.create("Printer Name");
var data = [
'^XA\n',
'^FO50,50^ADN,36,20^FDPRINTED USING QZ TRAY PLUGIN\n',
{
type: 'raw', format: 'image', data: 'assets/img/image_sample_bw.png',
options: { language: "ZPL" }
},
'^FS\n',
'^XZ\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
Example for sending EPL commands to a printer.
Note:
language: "foo"
is only required if providing a raw image type in the data stream.
var config = qz.configs.create("Printer Name");
var data = [
'\nN\n',
{ type: 'raw', format: 'image', data: 'assets/img/image_sample_bw.png', options: { language: "EPL", x: 20, y: 0 } },
'\nP1,1\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
var config = qz.configs.create("Printer Name");
var data = [
{ type: 'raw', format: 'image', data: 'assets/img/image_sample_bw.png', options: { language: "ESCPOS", dotDensity: 'double' } },
'\x1B' + '\x40', // init
'\x1B' + '\x61' + '\x31', // center align
'Beverly Hills, CA 90210' + '\x0A',
'\x0A', // line break
'www.qz.io' + '\x0A', // text and line break
'\x0A', // line break
'\x0A', // line break
'May 18, 2016 10:30 AM' + '\x0A',
'\x0A', // line break
'\x0A', // line break
'\x0A',
'Transaction # 123456 Register: 3' + '\x0A',
'\x0A',
'\x0A',
'\x0A',
'\x1B' + '\x61' + '\x30', // left align
'Baklava (Qty 4) 9.00' + '\x1B' + '\x74' + '\x13' + '\xAA', //print special char symbol after numeric
'\x0A',
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + '\x0A',
'\x1B' + '\x45' + '\x0D', // bold on
'Here\'s some bold text!',
'\x0A',
'\x1B' + '\x45' + '\x0A', // bold off
'\x1D' + '\x21' + '\x11', // double font size
'Here\'s large text!',
'\x0A',
'\x1D' + '\x21' + '\x00', // standard font size
'\x1B' + '\x61' + '\x32', // right align
'\x1B' + '\x21' + '\x30', // em mode on
'DRINK ME',
'\x1B' + '\x21' + '\x0A' + '\x1B' + '\x45' + '\x0A', // em mode off
'\x0A' + '\x0A',
'\x1B' + '\x61' + '\x30', // left align
'------------------------------------------' + '\x0A',
'\x1B' + '\x4D' + '\x31', // small text
'EAT ME' + '\x0A',
'\x1B' + '\x4D' + '\x30', // normal text
'------------------------------------------' + '\x0A',
'normal text',
'\x1B' + '\x61' + '\x30', // left align
'\x0A' + '\x0A' + '\x0A' + '\x0A' + '\x0A' + '\x0A' + '\x0A',
'\x1B' + '\x69', // cut paper (old syntax)
// '\x1D' + '\x56' + '\x00' // full cut (new syntax)
// '\x1D' + '\x56' + '\x30' // full cut (new syntax)
// '\x1D' + '\x56' + '\x01' // partial cut (new syntax)
// '\x1D' + '\x56' + '\x31' // partial cut (new syntax)
'\x10' + '\x14' + '\x01' + '\x00' + '\x05', // Generate Pulse to kick-out cash drawer**
// **for legacy drawer cable CD-005A. Research before using.
// see also http://keyhut.com/popopen4.htm
];
qz.print(config, data).catch(function(e) { console.error(e); });
//barcode data
var code = '12345';
//convenience method
var chr = function(n) { return String.fromCharCode(n); };
var barcode = '\x1D' + 'h' + chr(80) + //barcode height
'\x1D' + 'f' + chr(0) + //font for printed number
'\x1D' + 'k' + chr(69) + chr(code.length) + code + chr(0); //code39
qz.websocket.connect().then(function() {
var config = qz.configs.create("Epson TM88V");
return qz.print(config, ['\n\n\n\n\n' + barcode + '\n\n\n\n\n']);
}).catch(function(err) { alert(err); });
⚠️ Important: The defaultUTF8
encoding can corrupt QR codes,'ISO-8859-1'
is recommended instead. See #484, #155 for details. Also, if using macOS or Linux,{ altPrinting : true }
may be required, see #415 for details.
var config = qz.configs.create("Epson TM88V", { encoding: 'ISO-8859-1' });
// The QR data
var qr = 'https://qz.io';
// The dot size of the QR code
var dots = '\x09';
// Some proprietary size calculation
var qrLength = qr.length + 3;
var size1 = String.fromCharCode(qrLength % 256);
var size0 = String.fromCharCode(Math.floor(qrLength / 256));
var data = [
'\x1B' + '\x40' + 'John Doe' +'\x0A' + '\x0A' + '\x0A' + '\x0A' + '\x0A' + '\x0A', // Some text and a few line feeds to make sure the initiation and first line are coming through
// <!-- BEGIN QR DATA -->
'\x1D' + '\x28' + '\x6B' + '\x04' + '\x00' + '\x31' + '\x41' + '\x32' + '\x00', // <Function 165> select the model (model 2 is widely supported)
'\x1D' + '\x28' + '\x6B' + '\x03' + '\x00' + '\x31' + '\x43' + dots, // <Function 167> set the size of the module
'\x1D' + '\x28' + '\x6B' + '\x03' + '\x00' + '\x31' + '\x45' + '\x30', // <Function 169> select level of error correction (48,49,50,51) printer-dependent
'\x1D' + '\x28' + '\x6B' + size1 + size0 + '\x31' + '\x50' + '\x30' + qr, // <Function 080> send your data (testing 123) to the image storage area in the printer
'\x1D' + '\x28' + '\x6B' + '\x03' + '\x00' + '\x31' + '\x51' +'\x30', // <Function 081> print the symbol data in the symbol storage area
'\x1D' + '\x28' + '\x6B' + '\x03' + '\x00' + '\x31' + '\x52' +'\x30', // <Function 082> Transmit the size information of the symbol data in the symbol storage area
// <!-- END QR DATA -->
'New York, NY' + '\x0A' + '\x0A' + '\x0A' + '\x0A' + '\x0A' + '\x0A' // more line feeds and text to see if we messed up the QR Code syntax
];
Example for sending 648 x 1016 pixels of graphic data to an Evolis printer. Take special note of precision: 128
, which is 128 color precision (the highest) and overlay: true
which will use the clear overlay film after printing as a light protective coating.
This technique can be combined with magnetic encoding and text printing.
var config = qz.configs.create('Evolis');
var data = [
'\x1BPps;0\x0D', // Enable raw/disable driver printer parameter supervision
'\x1BPwr;0\x0D', // Landscape (zero degree) orientation
'\x1BWcb;k;0\x0D', // Clear card memory
'\x1BSs\x0D', // Start of sequence
{ type: 'raw', format: 'image', data: 'assets/img/fade-test.png', options: { language: "EVOLIS", precision: 128, overlay: true } }
'\x1BSe\x0D' // End of sequence
];
qz.print(config, data).catch(function(err) { alert(err); });
If available, the overlay is a light protective coating that is applied by clear film of the print ribbon. In special cases, such as dual-sided printing, the overlay may need to be finely controlled (e.g. avoiding the magnetic stripe on the back side of the card). Take special note of overlay: overlay
in code example.
Overlay can be the path to an image (black pixels will print as overlay) or can be a series of coordinates.
var config = qz.configs.create('Evolis');
// Technique 1 - Use rectangles to draw overlay
// Supplied rectangle will print as overlay
// Typical mag-stripe provided as example
var overlay = [
[0, 0, 439, 1016],
[588, 0, 648, 1016]
// or if rotated 180 degrees
// [0, 0, 60, 1016],
// [209, 0, 648, 1016]
];
// Technique 2 - Use black pixels in 648 x 1016 image to draw overlay
// Black pixels will print as overlay
// var overlay = 'path/to/overlay.png';
var data = [
'\x1BPps;0\x0D', // Enable raw/disable driver printer parameter supervision
'\x1BPwr;0\x0D', // Landscape (zero degree) orientation
'\x1BWcb;k;0\x0D', // Clear card memory
'\x1BSs\x0D', // Start of sequence
// Overlay provided with image command
{ type: 'raw', format: 'image', data: 'assets/img/fade-test.png', options: { language: "EVOLIS", precision: 128, overlay: overlay } },
'\x1BSe\x0D' // End of sequence
];
qz.print(config, data).catch(function(err) { alert(err); });
Draws the given monochrome text at the specified coordinates and font style.
Note: If combining with image printing, this should generally come after the image commands.
var config = qz.configs.create('Evolis');
var data = [
// Wt = Monochrome text in bitmap
// 50 = X Position
// 60 = Y Position
// 0 = Font Type. 0 = Arial Normal 100 dots, 1 = Arial Bold 100 dots
// <text>
'\x1B' + 'Wt;50;60;0;30;Printed using QZ Tray' + '\x0D',
'\x1BSe\x0D' // End of sequence
];
qz.print(config, data).catch(function(err) { alert(err); });
Magnetic stripe encoding is available through the Ss
and Dm;<track>;<data>
commands. This technique can be combined with image printing and text printing
var config = qz.configs.create('Evolis');
// Magnetic Encoding example
var data = [
'\x1BPps;0\x0D', // Enable raw/disable driver printer parameter supervision
'\x1BWcb;k;0\x0D', // Clear card memory
'\x1BSs\x0D', // Start of sequence
// Download magnetic track data using ISO 7811
'\x1B' + 'Pps;0' + '\x0D', // Enable raw/disable driver printer parameter supervision
// '\x1B' + 'Pmc;h' + '\x0D', // Enable high coercivity
// '\x1B' + 'Pmc;l' + '\x0D', // Enable low coercivity
'\x1B' + 'Ss' + '\x0D' // Start recording and clear the current magnetic buffer
'\x1B' + 'Dm;1;foo' + '\x0D', // Track 1 = Alpha (ASCII Range 20 - 95) Len: 79 (including start/stop/lrc)
'\x1B' + 'Dm;2;12345' + '\x0D', // Track 2 = Numeric, Len 40 (including start/stop/lrc)
'\x1B' + 'Dm;3;67890' + '\x0D', // Track 3 = Numeric, Len 107 (including start/stop/lrc)
'\x1B' + 'Smw' + '\x0D' // Write the magnetic data aka "Sequence write magnetic track"
'\x1BSe\x0D' // End of sequence
];
qz.print(config, data).catch(function(err) { alert(err); });
Manual card flipping is done by telling the printer where to insert and where to output the card using Pcim
and Pcem
commands respectively. Remember when printing overlay ribbon to a magnetic strip or chip area to use the evolis overlay mask area.
var config = qz.configs.create('Evolis');
// Manual flip example
var data = [
// Front side
'\x1BPcim;F\x0D' // F Position: Feeder insertion
'\x1BPcem;M\x0D' // M Position: Manual ejection
'\x1BSs\x0D', // Start of sequence
// ... etc
'\x1BSe\x0D' // End of sequence
// Back side
'\x1BPcim;M\x0D', // M Position: Manual insertion
'\x1BPcem;D\x0D', // D Position: Default ejection(output hopper)
'\x1BSs\x0D', // Start of sequence
// ... etc
'\x1BSe\x0D' // End of sequence
];
qz.print(config, data).catch(function(err) { alert(err); });
//barcode data
var code = '1234567890';
var text = 'Hello world';
//convenience method
var chr = function(n) { return String.fromCharCode(n); };
var data = [
chr(02) + 'n\n',
' M0500\n',
' KcLW0422;\n',
' O0220\n',
' d\n',
' L\n',
'D11\n',
'ySPM\n',
'A2\n',
'1911A1200650118' + text +'\n',
'1eA505401290091C' + code + '\n',
'1911A1201090155'+ code + '\n',
'Q0001\n',
'E\n'
];
qz.websocket.connect().then(function() {
var config = qz.configs.create("I-Class Mark II");
return qz.print(config, data);
}).catch(function(err) { console.error(err); });
With this function, you can send base64 encoded characters/raw commands to qz using "base64". This will automatically convert provided base64 encoded text into text/ascii/bytes, etc.
function printBase64() {
var config = qz.configs.create("Printer Name");
var data = [
{
type: 'raw',
format: 'base64',
data: 'Ck4KcTYwOQpRMjAzLDI2CkI1LDI2LDAsMUEsMyw3LDE1MixCLCIxMjM0IgpBMzEwLDI2LDAsMywx' +
'LDEsTiwiU0tVIDAwMDAwIE1GRyAwMDAwIgpBMzEwLDU2LDAsMywxLDEsTiwiUVogUFJJTlQgQVBQ' +
'TEVUIgpBMzEwLDg2LDAsMywxLDEsTiwiVEVTVCBQUklOVCBTVUNDRVNTRlVMIgpBMzEwLDExNiww'
}
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
Since 2.0.3
, you can send base64 encoded images to qz. This will automatically convert provided base64 encoded image data into a supported raw image type. Base64 image data must begin with data:image/png;base64,
.
function printBase64Image() {
var config = qz.configs.create("Printer Name");
var data = [
{
type: 'raw',
format: 'image',
data: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAJFBMVEX' +
'///8kIiQAAADMzMyZmZlmZmb8/vxcXlz//8z//5lmzAAzZgBFSkm0AAAAVklEQVQYlYWPUQ6AMAhD' +
'ATvZ5v3va6duYybG90P60gQQ+cBuZs47KVG4exA/BVXLbRZT1ZZle1YYQMM8UVBEgKWBRdR6UKRXg' +
'5kCAx7QrkidK48/+7MnGPwCSdhQpuEAAAAASUVORK5CYII=',
options: { language: "ESCPOS", dotDensity: 'double' }
}
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
This feature allows a raw file to be spooled directly to the printer.
function printFile() {
var config = qz.configs.create("Printer Name");
var data = [
{
type: 'raw',
format: 'file',
data: 'assets/raw-data.txt'
}
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
The ability to read the contents of an XML file containing Base64 encoded commands and send these commands to the printer.
function printXML() {
var config = qz.configs.create("Printer Name");
var data = [
{
type: 'raw',
format: 'xml',
data: 'assets/zpl_sample.xml',
options: { xmlTag: 'v7:Image' }
}
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
Print directly to a HTTP url (such as a CUPS shared printer, or to a print server device).
function printToURL() {
var config = qz.configs.create({ host: "192.168.254.254", port: 9100 }); // or http://<cups-server>/printers/<my-printer>
var data = [
'Raw Data\n',
'More Raw Data\n',
'Even More Raw Data\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
function printToFile() {
var config = qz.configs.create({ file: "/path/to/output.txt" }); // or "C:\\path\\to\\output.text", etc
var data = [
'Raw Data\n',
'More Raw Data\n',
'Even More Raw Data\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
function printToUNC() {
var config = qz.configs.create({ file: "\\\\server\\printer" });
var data = [
'Raw Data\n',
'More Raw Data\n',
'Even More Raw Data\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
Leverage printing API for writing to a Win32 Device Namespace physical device such as a Bematech/Logic Controls Pole Display mapped as \\.\LCLD9
function printToDevice() {
var config = qz.configs.create({ file: "\\\\.\\LCLD9\\dummy" });
var data = [
'"\r\nMMM... COFFEE! :)\r\n"'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
Spools jobs between specified endOfDoc
delimiter. Useful for avoiding buffer overflows on unusually large quantity print jobs by spooling each raw job individually. Defaults to 1
job per spool, but can be overridden using perSpool
option.
function advancedSpooling() {
var config = qz.configs.create("Printer Name", { endOfDoc : '\n' });
var data = [
'First\n',
'Second\n',
'Third\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
function printHex() {
var config = qz.configs.create("Printer Name");
var data = [
{ type: 'raw', format: 'hex', data: 'x1Bx00' } // or data: '1B00'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
function printDecimal() {
var config = qz.configs.create("Printer Name");
var data = [
String.fromCharCode(27) + String.fromCharCode(0) // e.g. CHR(27) CHR(0) or Hex x1Bx00
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
Some languages such as Arabic are right-to-left and not interpreted properly by raw printers. Although Java can render these languages properly, not all printers are capable of rendering RTL correctly. The workaround is to reverse the text so that it renders properly.
function rightToLeft() {
var config = qz.configs.create("Printer Name");
var data = [
'Lorem: ',
' الرياض'.split('').reverse().join(''), // reverse Arabic text for certain printer models
'Ipsum. '
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
Note: The
split('')
method can corrupt data. For a more comprehensive reverse solution, seeesrever
project.
Force Java to use a specific code-page for printing.
function printCodePage() {
var config = qz.configs.create("Printer Name", { encoding: 'Cp1253' }); // MS-DOS Greek
var data = [
"Σ β θ"
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
function printCombined() {
var config = qz.configs.create("Printer Name");
// Example only, not valid ESC/POS
var data = [
"My Raw Commands",
String.fromCharCode(27) + String.fromCharCode(0),
{ type: 'raw', format: 'hex', data: 'x1Bx00' },
{ type: 'raw', format: 'image', data: 'assets/img/image_sample_bw.png',
options: { language: "ESCPOS", dotDensity: 'double' }
},
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
A config
parameter copies
can be provided to send the specified raw commands multiple times. This is a convenience function as most raw languages already have an internal copies/quantity command.
function copies() {
var config = qz.configs.create("Printer Name", { copies: 4 });
var data = [
'Raw Data\n',
'More Raw Data\n',
'Even More Raw Data\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
A config
parameter jobName
can be provided to change the name listed in the print queue.
function customJobName() {
var config = qz.configs.create("Printer Name", { jobName: "Receipt #123456" });
var data = [
'Raw Data\n',
'More Raw Data\n',
'Even More Raw Data\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
}
When using CUPS (Linux/Unix/Mac), the printer driver can be bypassed completely by executing a command-line option
lpr -o raw my-raw-file.txt
This may be useful when troubleshooting issues with non-raw printer drivers. QZ Tray can substitute the standard printing behavior with this technique by adding a config
parameter altPrinting
. This parameter has no effect in a Windows environment.
var config = qz.configs.create("Printer Name", { altPrinting: true });
var data = [
'Raw Data\n',
'More Raw Data\n',
'Even More Raw Data\n'
];
qz.print(config, data).catch(function(e) { console.error(e); });
Print requests can be chained together to print several raw items at once, or to print to several different printers.
var config = qz.configs.create();
var data = [{ type: 'raw', data: null, }];
data.data = 'Some Raw Data #1'; ////// First raw item
config.setPrinter('First Printer'); ////// First printer
qz.print(config, data)
.then(function() {
data.data = 'Some Raw Data #2'; ////// Second raw item
config.setPrinter('Second Printer'); ////// First printer
return qz.print(config, data);
})
.then(function() {
data.data = 'Some Raw Data #3'; ////// Third raw item
config.setPrinter('Third Printer'); ////// Third printer
return qz.print(config, data);
})
.catch(function(e) {
console.error(e); // Exceptions throw all the way up the stack
});
Using a Promise Loop, we can defer items (printers, data)
- So that different chunks of data can be sent to different printers
- So that an arbitrary (non-fixed) amount of data can be sent to a printer
function promiseLoop() {
var data = [
"^XA\n^FO50,50^ADN,36,20^FDPRINT 1 ^FS\n^XZ\n",
"^XA\n^FO50,50^ADN,36,20^FDPRINT 2 ^FS\n^XZ\n",
"^XA\n^FO50,50^ADN,36,20^FDPRINT 3 ^FS\n^XZ\n",
"^XA\n^FO50,50^ADN,36,20^FDPRINT 4 ^FS\n^XZ\n"
];
var configs = [
{ "printer": "ZDesigner LP2844-Z" },
{ "printer": "ZDesigner LP2844-Z" },
{ "printer": "ZDesigner LP2844-Z" },
{ "printer": "ZDesigner LP2844-Z" }
];
var chain = [];
for(var i = 0; i < data.length; i++) {
(function(i_) {
//setup this chain link
var link = function() {
return qz.printers.find(configs[i_].printer).then(function(found) {
return qz.print(qz.configs.create(found), [data[i_]]);
});
};
chain.push(link);
})(i);
//closure ensures this promise's concept of `i` doesn't change
}
//can be .connect or `Promise.resolve()`, etc
var firstLink = new RSVP.Promise(function(r, e) { r(); });
var lastLink = null;
chain.reduce(function(sequence, link) {
lastLink = sequence.then(link);
return lastLink;
}, firstLink);
//this will be the very last link in the chain
lastLink.catch(function(err) {
console.error(err);
});
}
Since 2.0.3
, you can use a new DIRECT
option for larger raw files. Large raw files may result in large spikes in memory consumption. Use DIRECT
to reduce memory consumption by bypassing some of the internal String
handling procedures.
Note: This CANNOT be combined with features like Advanced Print Spooling, raw images.
var config = qz.configs.create("Printer Name");
var data = [{
type: 'direct',
format: 'file',
data: 'path/to/large_file.txt'
}];
qz.print(config, data).catch(function(e) { console.error(e); });