How to generate and print advanced ZPL commands from Javascript and ThermalLabel Web API
Product JSPrintManager Published 02/02/2021 Updated 11/05/2026 Author Neodynamic
Overview
In this article, you'll learn how to generate and print advanced ZPL commands from Javascript directly to the client printer without displaying a print dialog at all. You'll be able to print advanced ZPL commands to the Default client printer as well as to any other installed printer at the client machine. This solution works with any popular browser like Chrome, Edge, Firefox & Safari on Windows, Linux, Raspberry Pi and Mac systems!
If you don't know how to write advanced ZPL commands, then you can take advantage of our On-premise ThermalLabel Web API for Docker to generate the advanced ZPL commands by writing simple and dev-friendly Javascript code to finally print it from any Web Platform/Framework you use (ASP.NET, PHP, Django, Ruby On Rails (RoR), Express.js, AngularJS/SPA, etc.) right to the client printer through JSPrintManager solution.
ThermalLabel Web API for Docker is a on-premise REST API available as a Linux Docker image that you can host and run anywhere i.e. locally or in the cloud.
AVAILABLE DISCOUNT If you find this API usefull then we're providing a great discount for buying it bundled with JSPrintManager. Please contact sales team for further details.
In this article we'll generate and print out a simple label that will look like this:

Step-by-step
-
JSPrintManager Client App
Be sure you install in your dev machine JSPrintManager (JSPM) (Available for Windows, Linux, Raspberry Pi & Mac)
IMPORTANT: This small app must be installed on each client!
Since Chrome 142, browsers ask the user to allow Local Network Access the first time your website connects to the JSPrintManager App. -
HTML & Script References
- Download and run the ThermalLabel Web API for Docker image. In the source code below, we'll suppose that the URL to access it is
https://localhost:8080(change it in the code below if you are running the Docker image under another URL) - Download JSPrintManager.js
- Download cptable.js & cputils.js
- Copy all of these *.js files to the same folder where the next
index.htmlpage sample will be located in. - By using your favorite Web Development IDE or Text Editor, create a new HTML file naming it
index.html
Copy/paste the following code inside that html file:
<!DOCTYPE html> <html> <head> <title>JSPrintManager</title> <meta charset="utf-8" /> </head> <body> <div style="text-align:center"> <h1>Print Advanced Zebra ZPL Commands From ThermalLabel Web API</h1> <hr /> <label for="printerType">Client printer:</label> <select id="printerType" onchange="showPrinterSettings();"> <option value="0">Default printer</option> <option value="1">Let the user select it (OS print dialog)</option> <option value="2" selected>Installed printer</option> <option value="3">IP/Ethernet printer</option> <option value="4">Parallel port (LPT) printer</option> <option value="5">Serial port (RS-232) printer</option> <option value="6">Bluetooth printer</option> </select> <div id="printer-settings-2" class="printer-settings"> <label>Installed printer: <select id="installedPrinterName"></select></label> </div> <div id="printer-settings-3" class="printer-settings" style="display:none"> <label>IP address: <input id="netIp" value="10.0.0.1" /></label> <label>Port: <input id="netPort" value="9100" /></label> <label>or DNS name: <input id="netDns" /></label> </div> <div id="printer-settings-4" class="printer-settings" style="display:none"> <label>Parallel port: <input id="lptPort" value="LPT1" /></label> </div> <div id="printer-settings-5" class="printer-settings" style="display:none"> <label>Serial port: <select id="serialPort"></select></label> <label>Baud rate: <input id="serialBaudRate" value="9600" /></label> <label>Data bits: <select id="serialDataBits"> <option value="Eight">8</option> <option value="Seven">7</option> <option value="Six">6</option> <option value="Five">5</option> </select> </label> <label>Parity: <select id="serialParity"> <option>None</option> <option>Odd</option> <option>Even</option> <option>Mark</option> <option>Space</option> </select> </label> <label>Stop bits: <select id="serialStopBits"> <option value="One">1</option> <option value="OnePointFive">1.5</option> <option value="Two">2</option> </select> </label> <label>Flow control: <select id="serialFlowControl"> <option value="None">None</option> <option value="XOnXOff">XOn/XOff</option> <option value="RequestToSend">RTS (Request to send)</option> <option value="RequestToSendXOnXOff">RTS + XOn/XOff</option> </select> </label> </div> <div id="printer-settings-6" class="printer-settings" style="display:none"> <label>Bluetooth address: <input id="btAddress" placeholder="00:00:00:00:00:00" /></label> <label>Channel: <input id="btChannel" value="1" /></label> </div> <br /><br /> <button type="button" onclick="printLabel();">Print Now...</button> </div> <!-- JSPrintManager.js (download it or install it with: npm install jsprintmanager) --> <script src="JSPrintManager.js"></script> <script> // The URL to the ThermalLabel Web API instance const API_URL = 'https://localhost:8080'; // the label items objs var myBarcode = null; var myText = null; var myImage = null; var myThermalLabel = null; // http request obj for API comm var request = new XMLHttpRequest(); // a generic func to request the API function reqApi(objUrl, method, acceptHeader, reqType, respType, data) { return new Promise(function (resolve, reject) { const URL = API_URL + objUrl; request.open(method, URL); if (respType) request.responseType = respType; if (reqType) request.setRequestHeader('Content-Type', reqType); if (acceptHeader) request.setRequestHeader('Accept', acceptHeader); request.onload = function () { if (this.status === 200) { resolve(this.response); } else { reject(Error(request.statusText)); } } if (data) request.send(data); else request.send(); }); } // Start the connection with the JSPrintManager Client App JSPM.JSPrintManager.auto_reconnect = true; JSPM.JSPrintManager.start(); JSPM.JSPrintManager.WS.onStatusChanged = function () { if (jspmWSStatus()) { // get the client installed printers JSPM.JSPrintManager.getPrinters().then(function (printersList) { fillSelect('installedPrinterName', printersList); }); // get the client serial ports (if any) JSPM.JSPrintManager.getSerialPorts().then(function (portsList) { fillSelect('serialPort', portsList); }); } }; // Check the connection status with the JSPrintManager Client App function jspmWSStatus() { var status = JSPM.JSPrintManager.websocket_status; if (status == JSPM.WSStatus.Open) { return true; } else if (status == JSPM.WSStatus.WaitingForUserResponse) { console.info('Waiting for the user to allow this website (JSPrintManager dialog or browser Local Network Access prompt)...'); } else if (status == JSPM.WSStatus.Blocked) { alert('JSPrintManager has blocked this website!'); } else if (status == JSPM.WSStatus.NotInstalled || status == JSPM.WSStatus.Closed) { alert('JSPrintManager Client App is not installed or not running! Download it from https://neodynamic.com/downloads/jspm'); } else if (status == JSPM.WSStatus.CertificateError) { alert('A TLS/SSL certificate error prevented the connection with JSPrintManager Client App.'); } else if (status == JSPM.WSStatus.ConnectionError) { alert('A network error (e.g. a firewall) prevented the connection with JSPrintManager Client App.'); } return false; } // Show the settings of the selected printer type function showPrinterSettings() { var panels = document.querySelectorAll('.printer-settings'); for (var i = 0; i < panels.length; i++) { panels[i].style.display = (panels[i].id == 'printer-settings-' + val('printerType')) ? '' : 'none'; } } // Create the client printer for the selected printer type function getClientPrinter() { switch (val('printerType')) { case '0': return new JSPM.DefaultPrinter(); case '1': return new JSPM.UserSelectedPrinter(); case '2': return new JSPM.InstalledPrinter(val('installedPrinterName')); case '3': return new JSPM.NetworkPrinter(parseInt(val('netPort')), val('netIp'), val('netDns')); case '4': return new JSPM.ParallelPortPrinter(val('lptPort')); case '5': return new JSPM.SerialPortPrinter(val('serialPort'), parseInt(val('serialBaudRate')), JSPM.Serial.Parity[val('serialParity')], JSPM.Serial.StopBits[val('serialStopBits')], JSPM.Serial.DataBits[val('serialDataBits')], JSPM.Serial.Handshake[val('serialFlowControl')]); case '6': return new JSPM.BluetoothPrinter(val('btAddress'), parseInt(val('btChannel'))); } } // Log the print job status updates (optional) function traceJob(cpj) { cpj.onUpdated = function (data) { console.info(data); }; cpj.onFinished = function (data) { console.info(data); }; cpj.onError = function (data, isCritical) { console.error(data); }; } // Helpers function val(id) { return document.getElementById(id).value; } function checked(id) { return document.getElementById(id).checked; } // Fill a <select> element with a list of items function fillSelect(id, items, getText) { var select = document.getElementById(id); select.innerHTML = ''; for (var i = 0; i < items.length; i++) { var text = getText ? getText(items[i]) : items[i]; select.add(new Option(text, text)); } } // Do printing... function printLabel() { if (jspmWSStatus()) { // create a BarcodeItem obj reqApi('/Items/BarcodeItem', 'GET', null, null, 'json').then(function (response) { console.log(response); myBarcode = response; // create a TextItem obj reqApi('/Items/TextItem', 'GET', null, null, 'json').then(function (response) { console.log(response); myText = response; // create an ImageItem obj reqApi('/Items/ImageItem', 'GET', null, null, 'json').then(function (response) { console.log(response); myImage = response; // create a ThermalLabel obj reqApi('/ThermalLabel', 'GET', null, null, 'json').then(function (response) { console.log(response); // COMPOSE the label positioning the different items created above // ThermalLabel settings myThermalLabel = response; // default unit is INCH myThermalLabel.width = 4; myThermalLabel.height = 3; myThermalLabel.gapLength = 0.05; // Image settings myImage.x = 0.1; myImage.y = 0.1; myImage.width = 2.66; myImage.height = 0.72; myImage.sourceBase64 = "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"; // Text settings myText.x = 0.1; myText.y = 1; myText.width = 3; myText.height = 0.25; myText.text = "Sample Text 1234567890"; myText.font.name = "ZPL Font 0"; myText.font.size = 14; // Barcode settings myBarcode.x = 0.1; myBarcode.y = 1.5; myBarcode.width = 1; myBarcode.height = 1; myBarcode.sizing = "FitProportional"; myBarcode.symbology = "QRCode"; myBarcode.code = "Data to encode here"; // add items to the label myThermalLabel.items = []; myThermalLabel.items.push(myImage); myThermalLabel.items.push(myText); myThermalLabel.items.push(myBarcode); console.log(myThermalLabel); let convertParam = {}; convertParam["thermalLabel"] = JSON.stringify(myThermalLabel); convertParam["dpi"] = 203; console.log(convertParam); // convert label to ZPL commands!!! reqApi('/ThermalLabel/Convert', 'POST', 'application/vnd.zpl', 'application/json', 'arraybuffer', JSON.stringify(convertParam)).then(function (response) { console.log(response); // Create a ClientPrintJob for printing the ZPL commands var cpj = new JSPM.ClientPrintJob(); // Set the target client printer cpj.clientPrinter = getClientPrinter(); // Set printer commands... cpj.binaryPrinterCommands = new Uint8Array(response); // Trace the print job status (optional) traceJob(cpj); // Send print job to printer! cpj.sendToClient(); }, function (Error) { console.log(Error); }); }, function (Error) { console.log(Error); }); }, function (Error) { console.log(Error); }); }, function (Error) { console.log(Error); }); }, function (Error) { console.log(Error); }); } } </script> </body> </html>
- Download and run the ThermalLabel Web API for Docker image. In the source code below, we'll suppose that the URL to access it is
- That's it! Run your website and test it.
Useful? Share it!
| Tweet |
|
Related Articles
How to print raw Zebra ZPL commands from JavascriptPrint Unicode UTF8 Text to Zebra ZPL printer from Javascript
How to configure RAW printing mode in Windows
How to configure RAW printing mode in Linux and Raspberry Pi
Need help? Contact Support
We provide best-in-class customer service and support directly from members of our dev team! If we are available when you contact us, you will get a response in few minutes; otherwise the maximum turnaround is 24hs in most cases.