Purpose
The Zebra ISV technical team has been working with developers for several years. Part of the group’s charter is to provide developer support and testing services. We created these best practices based on common issues we see regularly. We wish to provide developers with guidance on:
- Common performance and stability mistakes that we have seen many times.
- How to create the best user experience for their customers.
- How to best utilize Zebra value added features
Since we originally published best practices to help avoid some of these issues, the number of test failures has decreased and we recieved more questions about how to implement them. This document is an attempt to improve on the original and make it easier to develop with them.
Who Should Use This
There are several primary audiences for this document.
- Software developers writing code to print to or manage Zebra printers.
- Test Engineers looking for the best methods to test applications that involve Zebra printers.
- User Experience Engineers looking for advice in providing better UX when printing is involved.
- Zebra Partners who are looking to have their software validated by Zebra.
How This Document Is Organized
Each section has a name and description of the best practice. This is followed by a ‘How to Do It’ section for developers and a ‘How to Test It’ section for testers. Either section may have code snippets or other notes to elaborate on how to implement each portion.
Anything with the validation logo must be implemented if possible for Zebra Validated Program.
The code samples in this document are in Java for Android, assuming the use of the Link-OS Multiplatform SDK and working with ZPL print language. You can implement these best practices in nearly any programming language for almost any OS, with any Zebra printer, any print page description language, with or without the SDK. The SDK makes it easier. The sample code herein is meant to be an example and may need to adjust to fit your use case. More examples of implementing Best Practices can be found on the Zebra Github repo. If you have implementation questions, ask at the Zebra Developer Portal.
1. Out Of Box Behavior
Your app should work with the Zebra Printer you specify. It should work with that printer if the printer is brand new out of the box. Not all printers have the same default behaviors. The default settings may not meet the needs of your application.
How to Do It:
- Set up correct connectivity (TCP, Cloud Connect, BT, BTLE, USB, etc)
Note: Examples of different connection types can be found in the API Documentation.
- Set up correct Zebra Print Page Description Language (ZPL, CPCL, line_print, PDF, etc.)
Note: See code sample in Page Description Language section.
- Set up label & print settings (print speed, darkness, label width, length, etc.)
// set the printed darkness to 15 String darkness = 15; SGD.SET("print.tone", darkness, statusConnection); // set the print speed to 6 inches per second SGD.SET("media.speed", "6", statusConnection); // set the label width to 4 inches (on a 203 dpi printer) SGD.SET("ezpl.print_width", "812", statusConnection);
Note: For a full list of SGD (Set-Get-Do) settings, see the ZPL Programming Guide. It is recommended that you give your users an interface to adjust these settings for their printers in your app. You may want to adjust your printout based on these settings. To set them in the printer, you can use the preceding code. Also see Blogs on Developer Portal.
- (optional- if required) Set up Remote Management settings (connectors, mirror, profile manager)
Note: See the ZPL Programming Guide on Weblink commands for information on setting up Cloud Connect, Connectors, and Profile Manager in the printer. See the section on Mirror for information on setting printer management through FTP on the printer.
- (optional- if required) Set up Special features (ZBI, virtual devices, etc.)
- (optional- if required) Verify printer firmware version
String firmware = SGD.GET("appl.name", statusConnection); String linkosVersion = SGD.GET("appl.link_os_version", statusConnection);
- Verify the best practices on the following sections are being met
How to Test It:
- Get a new printer or default a printer to factory specifications. Test your app thoroughly to the best practices on the following pages.
- There are several methods for defaulting Zebra printers, but a common one is to use the Setup Utility software.
- Install the Setup Utility software
- Connect to the printer
- Select Printer Tools
- Under ‘Actions’, select ‘Load Factory Defaults’
2. Stability
The app should not crash or freeze when attempting to print. This includes failure to print without notifying the user of cause.
How to Do It:
- Always communicate to the printer on a thread other than the UI.
- Always use try/catch statements around any printer communications.
- Close the printer connection when done printing.
public class PrintTask extends AsyncTask
How to Test It:
- While running the app, turn off the printer at random times in the process. Verify the app doesn’t crash or hang.
- Turn it on at different times. Make sure the app is still stable and notifies about connection issues.
3. Security
Communication to a printer should be treated as a potential security risk. Follow security best practices for your organization or customers. The printers are open to most communication by default.
How to Do It:
- Use secure communication methods to the printer as much as possible, from Bluetooth security levels to utilize Wi-Fi Certificates and HTTPS communications.
- Turn off communication methods that you do not intend to use. Ask about specific protocols/systems you need to support.
- Do not include personal information as part of a stored format or stored image
Note: A more complete set of security best practices will be published soon
How to Test It:
- Assume communication to a printer is a potential security risk and test accordingly.
See SGD Communication commands starting here.
- Assume all data (Except passwords, certs, and other known security files and settings) being stored in the printer is extract-able and test accordingly.
4. Formatted Output
Customers want their printouts to be easy to read and scan. They also want their branding to be noticeable.
How to Do It:
- Use ZPL print page description language wherever possible.
Note: See code sample in Page Description Language section.
- Images should be used at a minimum, especially those that contain a barcode. Images use a lot of data so are slower to print. Barcodes are sometimes distorted when converted to images. If you have to use images, verify the DPI is the same as the printer.
- Use a design tool to help create label formats. See theZebra Designer application or contact one of our design partners
- Select media (paper or label stock) based on customer feedback. We recommend Zebra Certified Supplies for best print quality and printer life.
- Use the Setup Utility app to set your printer’s print quality and set the same settings in your app.
- Set print darkness appropriately. If barcodes are the most important data to read, set the darkness lighter than optimal for reading text. Consider a bold font for lighter text.
How to Test It:
- The app produces the expected printed output with acceptable print quality.
- Barcodes should be at a grade B or higher. Barcode grading is done with a barcode verifier tool. This ensures the scannability of the barcode. If you do not have one, we can work with you to ensure your barcodes are scannable.
- Printed output should not be off the page, too small to read, or too tightly spaced
5. Page Description Language
Zebra printers support several different formatting languages. Different printers use different default formatting languages.
How to Do It:
- Set the page description language you intend to use in the SDK.
// Set the page description language in the SDK to ensure images are converted properly and // correct formats are retrieved. Do not let the SDK decide on a language by not // putting it in the factory. It may not choose what you want. ZebraPrinter zPrinter = ZebraPrinterFactory.getInstance(PrinterLanguage.ZPL, connection);
- Set the page description language you intend to use in the printer. These are two different steps. See the ZPL Guide for options.
private boolean setPrintLanguage(Connection connection) { String pageDescriptionLanguage = "zpl"; // Set the print language in the printer SGD.SET("device.languages", pageDescriptionLanguage, connection); // Get the print language in the printer to verify the printer was able to switch String s = SGD.GET("device.languages", connection); if (!s.contains(pageDescriptionLanguage)) { System.out.println("Not a ZPL printer."); return false; } return true; }
- Verify the language is what you want, as not all printers support all command languages. .
Note: ZPL is recommended because:
- It's the language Zebra is committed to continuously improving. Most of other languages are only supported for bug fixing.
- It's the language with the best font and localization capabilities.
- It's the one language that is shipped with almost every Zebra printer so you can shift from one printer to another without changing your app.
How to Test It:
- Test with different printer models you intend to support.
- Change the page description language and confirm the app still works. Verify printer accepted the change.
Use Setup Utility to set and verify page description language. See the ZPL Guide for options.
- Install the Setup Utility software
- Connect to the printer
- Set the page description language
- On Android - Select ‘Device Language’, then ‘Set Device Language’ to select the language to "ZPL". Select ‘APPLY’ and verify the ‘Current Device Language’ shown.
- On PC - Select ‘Open Communication to Printer’. Type in the top box:
Hit Enter. The newlines are very important and use straight double quotes exactly as shown. Click ‘Send to Printer’ twice. Response should be: "hybrid_xml_zpl" on most ZPL printers.! U1 setvar "device.languages" "zpl" ! U1 getvar "device.languages"
- Test the printing in your app.
- Change the device language to "line_print" and verify as shown in step 3.
- Test the printing in your app.
6. Status
The status will tell you if the printer is ready to print or in an error state.
How to Do It:
- Check the status of the printer before printing to ensure the printer is in a ready state-to-print.
// check prior to printing private boolean checkPrinterStatus(Connection connection) { ZebraPrinter printer = ZebraPrinterFactory.getLinkOsPrinter(connection, PrinterLanguage.ZPL); if (null == printer) { printer = ZebraPrinterFactory.getInstance(PrinterLanguage.ZPL, connection); } PrinterStatus printerStatus = printer.getCurrentStatus(); if (printerStatus.isReadyToPrint) { System.out.println("Ready To Print"); return true; } else if (printerStatus.isPaused) { System.out.println("Cannot Print because the printer is paused."); } else if (printerStatus.isHeadOpen) { System.out.println("Cannot Print because the printer head is open."); } else if (printerStatus.isPaperOut) { System.out.println("Cannot Print because the paper is out."); } else { System.out.println("Cannot Print."); } return false; }
- Check the status during and after printing to ensure the print happened successfully.
// Check during / after printing private boolean postPrintCheckPrinterStatus(Connection connection) { ZebraPrinter printer = ZebraPrinterFactory.getLinkOsPrinter(connection, PrinterLanguage.ZPL); if (null == printer) { printer = ZebraPrinterFactory.getInstance(PrinterLanguage.ZPL, connection); } PrinterStatus printerStatus = printer.getCurrentStatus(); // loop while printing until print is complete or there is an error while ((printerStatus.numberOfFormatsInReceiveBuffer > 0) && (printerStatus.isReadyToPrint)) { Thread.sleep(500); printerStatus = printer.getCurrentStatus(); } if (printerStatus.isReadyToPrint) { System.out.println("Ready To Print"); return true; } else if (printerStatus.isPaused) { System.out.println("Cannot Print because the printer is paused."); } else if (printerStatus.isHeadOpen) { System.out.println("Cannot Print because the printer head is open."); } else if (printerStatus.isPaperOut) { System.out.println("Cannot Print because the paper is out."); } else { System.out.println("Cannot Print."); } return false; }
- Another option is to use the printer alert system. The printer can be set up to send a notification on specific ports when the printer enters an error state. This is most useful for server-side applications that are always connected to the printer (24/7 use case).
- It is recommended that the app wait to send print jobs until all errors are cleared.
- Offer to reprint on errors occurred during printing.
- The Status Channel is the preferred method to check status. This should increase performance.
// With Bluetooth you have to open a raw connection before opening a status connection. Connection btConnection = new BluetoothConnection(theBtMacAddress); btConnection.open(); Connection btStatusConnection = new BluetoothStatusConnection(theBtMacAddress); // or Connection nwStatusConnection = new TcpStatusConnection(theIpAddress, TcpStatusConnection.DEFAULT_STATUS_TCP_PORT);
- You can also use the printer odometers to check if print is complete. This can be more accurate than the status check for this verification, but it may also be unnecessary.
// Set settings, check status, print, etc. if (setPrintLanguage(statusConnection) && checkPrinterStatus(statusConnection)) { int labelCount = Integer.parseInt(SGD.GET("odometer.total_label_count", statusConnection)); // Send Print Job (1 label) String zplData = "^XA^FO20,20^A0N,25,25^FDThis is a ZPL test.^FS^XZ"; printerConnection.write(zplData.getBytes()); if (postPrintCheckPrinterStatus(statusConnection)) { int newLabelCount = Integer.parseInt(SGD.GET("odometer.total_label_count", statusConnection)); if (labelCount + 1 == newLabelCount) { System.out.println("Print Successful."); } } //else reprint? }
How to Test It:
- Before printing, open the media door or remove the paper.
- In the middle of printing, open the media door. Do these issues cause instability in the app?
- Verify the following:
- Media door open
- Paper out
- Printer is unreachable (off or disconnected / out of range) .
- Ribbon Out
- Print-head is too hot (High darkness, high speed, lot’s of black)
- Paused
- Receive Buffer Full (send multiple jobs quickly)
7. Display Printer Error Status
The user should be alerted to status issues with the printer. They should not have to look at the printer to find this out.
How to Do It:
- When checking status, alert the user to the precise error that is occurring on the printer within your app.
In Android you can use Toast, Java and C# - ShowDialog, etc…
How to Test It:
- While testing status issues, does the app alert the user in clear, end user words, what the exact printer issue is? Verify the following status:
- Media door open
- Paper out
- Printer is unreachable (off or disconnected / out of range) .
- Ribbon Out
- Print-head is too hot (High darkness, high speed, high amounts of black)
- Paused
- Receive Buffer Full (Send multiple jobs quickly)
8. Performance
Printer performance is measured on several factors. Time to print (the time between user activating the print action and the printer starting to print) and mobile printer battery life are some of the most important performance issues that are affected by software interaction
How to Do It:
- Use printing page description language to optimize performance, if possible avoid using graphics, we recommend using templates or formats. See API Reference for details on format printing.
- RFID encoding or BTLE connections -
- Do not send graphics inline in each format.
- Use print page description language instead of images for printout.
- Store graphics and formats in printer to be recalled with minimal data transfer.
- In mobile printers, set up Sleep Mode and close connections to save battery life. Not available on all printers
// tell the printer to sleep after 20 minutes of inactivity SGD.SET("power.sleep.enable", "on", statusConnection); SGD.SET("power.sleep.timeout", 1200, statusConnection);
- Use status channel to optimize printing time. See Status section for details.
- For quick connections and pairing, you can use NFC (Print Touch) or scan to connect. Sample code
- When using Virtual Devices, verify the virtual device is running on the printer. Sample Code
How to Test It:
-
Acceptable time to print is based on your use case.
-
Battery lifetime health is measured by the charge cycle count.
To check your battery health, Use the Setup Utility software. Check with the command:
! U1 getvar "power.health"
Followed by Enter
• Health is “good” if the power.cycle_count < 300 and capacity ratio (the ratio of actual capacity to the
design capacity) is greater or equal to 0.80.
• Health is “replace” if power.cycle_count is between 300 and 600. If # of Cycles is < 550 but > 300,
the printer will display a message “Please Replace Battery Pack” followed by three beeps. If the
number of charge cycles is ≤550 but < 600, the reminder shall be: “Warning - Battery is Past its
Useful Life” followed by three beeps.
• Health is “poor” if the power.cycle_count is greater than 600. Printer will flash a message: “Please
Replace Battery Before Proceeding – Shutting Down” accompanied by a beep for thirty seconds and
then shut down.
9. Branding
If present, usage of Zebra branding (logo, model number, etc.) must meet Zebra Technologies Global Brand Standards.
How to Do It:
- Follow the Master Brand Guidelines
- Email brand@zebra.com for current creative images.
How to Test It:
- Email brand@zebra.com to verify brand guidelines and creative images.
FAQ
What do I do if…
My app crashed while communicating to the printer?
If you think the app is crashing because you are communicating to the printer:
- The device may be unable to communicate because it is out of range or the printer is off, therefore it throws an exception. Make sure all communication to the printer is in try/catch statements.
public class PrintTask extends AsyncTask
- Verify you have the proper permissions to communicate (see documentation)
- Communicating to the printer can sometimes take time, sometimes seconds. Handle it as a long running task. Always communicate to the printer on a thread other than the UI. (see code above)
If you are concerned the print jobs were not printed:
- To verify if the print jobs were sent and printed, there are two options. Used in combination, they can help you in mission critical print applications:
- Check print status during printing
// Check during / after printing private boolean postPrintCheckPrinterStatus(Connection connection) { ZebraPrinter printer = ZebraPrinterFactory.getLinkOsPrinter(connection, PrinterLanguage.ZPL); if (null == printer) { printer = ZebraPrinterFactory.getInstance(PrinterLanguage.ZPL, connection); } PrinterStatus printerStatus = printer.getCurrentStatus(); // loop while printing until print is complete or there is an error while ((printerStatus.numberOfFormatsInReceiveBuffer > 0) && (printerStatus.isReadyToPrint)) { Thread.sleep(500); printerStatus = printer.getCurrentStatus(); } if (printerStatus.isReadyToPrint) { System.out.println("Ready To Print"); return true; } else if (printerStatus.isPaused) { System.out.println("Cannot Print because the printer is paused."); } else if (printerStatus.isHeadOpen) { System.out.println("Cannot Print because the printer head is open."); } else if (printerStatus.isPaperOut) { System.out.println("Cannot Print because the paper is out."); } else { System.out.println("Cannot Print."); } return false; }
- Keep track of the odometer in the printer to check labels printed.
// Set settings, check status, print, etc. if (setPrintLanguage(statusConnection) && checkPrinterStatus(statusConnection)) { int labelCount = Integer.parseInt(SGD.GET("odometer.total_label_count", statusConnection)); // Send Print Job (1 label) String zplData = "^XA^FO20,20^A0N,25,25^FDThis is a ZPL test.^FS^XZ"; printerConnection.write(zplData.getBytes()); if (postPrintCheckPrinterStatus(statusConnection)) { int newLabelCount = Integer.parseInt(SGD.GET("odometer.total_label_count", statusConnection)); if (newLabelCount == labelCount + 1) { System.out.println("Print Successful."); } } //else reprint? }
- Check print status during printing
What do I do if…
My printer doesn’t print or doesn’t print what I want it to?
- Check and display the printer status. The printer may not be in a good state to print.
// Check during / after printing private boolean postPrintCheckPrinterStatus(Connection connection) { ZebraPrinter printer = ZebraPrinterFactory.getLinkOsPrinter(connection, PrinterLanguage.ZPL); if (null == printer) { printer = ZebraPrinterFactory.getInstance(PrinterLanguage.ZPL, connection); } PrinterStatus printerStatus = printer.getCurrentStatus(); if (printerStatus.isReadyToPrint) { System.out.println("Ready To Print"); return true; } else if (printerStatus.isPaused) { System.out.println("Cannot Print because the printer is paused."); } else if (printerStatus.isHeadOpen) { System.out.println("Cannot Print because the printer head is open."); } else if (printerStatus.isPaperOut) { System.out.println("Cannot Print because the paper is out."); } else { System.out.println("Cannot Print."); } return false; }
- Verify the page description language is set to what you want it to be.
private boolean setPrintLanguage(Connection connection) { String pageDescriptionLanguage = "zpl"; // Set the print language in the printer SGD.SET("device.languages", pageDescriptionLanguage, connection); // Get the print language in the printer to verify the printer was able to switch String s = SGD.GET("device.languages", connection); if (!s.contains(pageDescriptionLanguage)) { System.out.println("Not a ZPL printer."); return false; } return true; }
- Verify you have the proper permissions in your app to communicate (see documentation)
- Verify your ZPL is correct
- Preview with the printer web page (see this article for details)
- Design your label with Label Design tools such as ZebraDesigner .
- Calibrate the printer (ZPL command ^JC, or see your specific printer User Guide)
- Verify the darkness, label size and other settings are correct (see Setup Utility)
What do I do if…
It takes a long time to print?
- The slowest part of printing is often the data transmittal speed. To optimize this, reduce the amount of data being sent to the printer by:
- Use a page description language
- Avoid the use of graphics and confirm necessary graphics are small
- Use templates or stored formats (See code on Github)
- Use the status channel to check status and settings
// With Bluetooth you have to open a raw connection before opening a status connection. Connection btConnection = new BluetoothConnection(theBtMacAddress); btConnection.open(); Connection btStatusConnection = new BluetoothStatusConnection(theBtMacAddress); // or Connection nwStatusConnection = new TcpStatusConnection(theIpAddress, TcpStatusConnection.DEFAULT_STATUS_TCP_PORT);
- Set the print speed to the fastest setting that doesn’t compromise print quality.
- If you are printing multiple jobs in short amount of time, don’t close the connection after each print. Use a timer to decide when to close the connection.