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.

  1. Software developers writing code to print to or manage Zebra printers.
  2. Test Engineers looking for the best methods to test applications that involve Zebra printers.
  3. User Experience Engineers looking for advice in providing better UX when printing is involved.
  4. 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 Zebra Validation Program 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 nearly 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 adjusted 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)
        // Instantiate connection for ZPL TCP port at given address
        Connection printerConnection = new TcpConnection(theIpAddress, TcpConnection.DEFAULT_ZPL_TCP_PORT);
    
        try {
            // Open the connection - physical connection is established here.
            printerConnection.open();
    
            // Set settings, check status, print, etc.
        } catch (ConnectionException e) {
            // Handle communications error here.
            e.printStackTrace();
        } finally {
            // Close the connection to release resources.
            printerConnection.close();
        }
        

    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) Zebra Validation Program

    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 interfaces to adjust these settings for their printers in your app. You may want to make adjustments to your printout based on these settings. To set them in the printer, you can use the preceeding 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 mehtods for defaulting Zebra printers, but a common one is to use the Setup Utility software.
    1. Install the Setup Utility software
    2. Connect to the printer
    3. Select Printer Tools
    4. Under ‘Actions’, select ‘Load Factory Defaults’

Top


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. Zebra Validation Program
  • Always use try/catch statements around any printer communications. Zebra Validation Program
  • Close the printer connection when done printing.
         public void print()
         {
             new Thread(new Runnable() {
                public void run() {
                    Looper.prepare();
                    _print();
                    Looper.loop();
                    Looper.myLooper().quit();
                }
            }).start();
         }
         private void _print()
         {    
             // Instantiate connection for ZPL TCP port at given address
             Connection printerConnection = new TcpConnection(theIpAddress, TcpConnection.DEFAULT_ZPL_TCP_PORT);
    
             try {
                 // Open the connection - physical connection is established here.
                 printerConnection.open();
    
                 // Set settings, check status, print, etc.
             } catch (ConnectionException e) {
                 // Handle communications error here.
                 e.printStackTrace();
             } finally {
                 // Close the connection to release resources.
                 printerConnection.close();
             }
         }
        

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. Zebra Validation Program
  • Turn it on at different times. Make sure the app is still stable and notifies about connection issues. Zebra Validation Program

Top


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 utilizing Wi-Fi Certificates and HTTPS communications.
  • Turn off communication methods that you are not intending to use. Ask about specific protocols/systems you need to support.
  • Do not put 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.

    See ZPL commands ^HF, ^HG, and SGD file.type

Top


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.

  • Use images minimally, especially for barcodes.
  • Use a design tool to help create label formats. See Zebra Designer or one of our design partners
  • Select media based on customer feedback.
  • 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.

How To Test It:

  • The app produces the expected printed output with acceptable print quality. Zebra Validation Program
  • Barcodes should grade at a B or higher.
  • Printed output should not be off the page, too small to read, or too tightly spaced

Top


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. Zebra Validation Program
        // Set the page description language in the SDK to ensure images are converted properly and 
        // correct formats are retireved.  Do not let the SDK decide on a language by not 
        // putting it in the factory.  It will often choose incorrectly.
        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. Zebra Validation Program
        private boolean setPrintLanguage(Connection connection)
        {
            // Set the print language in the printer
            // Can also use the SGD class
            String setLanguage = "! U1 setvar \"device.languages\" \"zpl\"\r\n\r\n! U1 getvar \"device.languages\"\r\n\r\n";
            byte[] response = connection.sendAndWaitForResponse(setLanguage.getBytes(), 500, 500);
            String s = new String(response, "UTF-8");
            if (!s.contains("zpl"))
            {
                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:

    1. It is the language Zebra is committed to continuously improving. Most of other languages are only supported for bug fixing.
    2. It is the language with the best font and localization capabilities so if your business takes your apps to other countries, the printer is right there with you.
    3. It is the one language that is shipped in nearly every Zebra printer so you can shift from one to another without changing your app.

How To Test It:

  • Test with different printer models you intend to support. Zebra Validation Program
  • Change the page description language and make sure the app still works. Verify printer took the change. Zebra Validation Program

    Use Setup Utility to set and verify page description language. See the ZPL Guide for options.

    1. Install the Setup Utility software
    2. Connect to the printer
    3. Set the page description language
    • On Android - Select ‘Device Language’, then ‘Set Device Language’ to select the language you want to test. Select ‘APPLY’ and verify the ‘Current Device Language’ shown.
    • On PC - Select ‘Open Communication to Printer’. Type in the top box:
      ! U1 setvar "device.languages" "zpl"
      ! U1 getvar "device.languages"
    • Hit Enter. The newlines are very important and use straight double quotes excactly as shown. Click ‘Send to Printer’ twice. Response should be: “hybrid_xml_zpl” on most ZPL printers.

    Note: If you intend to use a specific format language, test with a sample label/receipt in the expected format language.

Top


6. Status

Status tells you if the printer is actually ready to print or in an error state.

How To Do It:

  • Check the status of the printer before printing in order to ensure the printer is in a ready state to print. Zebra Validation Program
        // check prior to printing
        private boolean checkPrinterStatus(Connection connection)
        {
            ZebraPrinter 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.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))
            {
                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 to be notified when the printer enters an error state. This is most useful in server side applications that are always connected to the printer (24/7 use case).
  • Recommended that the app wait to send the print job until all errors are cleared.
  • Offer to reprint on errors occurred during print.
  • Use of the Status Channel to check status is the preferred method. This should increase performance.
        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 may 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 (newLabelCount == labelCount + 1)
                        {
                            System.out.println("Print Successful.");
                        }
                    }
                    //else reprint?
                }
        

How To Test It:

  • Before printing, open the media door or remove the paper. Zebra Validation Program
  • In the middle of printing, open the media door. Zebra Validation Program Do these issues cause instability in the app?
  • Verify the following:
    • Media door open Zebra Validation Program
    • Paper out Zebra Validation Program
    • Printer is unreachable (off or disconnected / out of range) . Zebra Validation Program
    • Ribbon Out
    • Print-head is too hot (High darkness, high speed, lot’s of black)
    • Paused Zebra Validation Program
    • Receive Buffer Full (send multiple jobs quickly)

Top


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.Zebra Validation Program

    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? Zebra Validation Program 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)

Top


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 that are affected by software interaction

How To Do It:

  • Use printing page description language to optimize performance, if possible avoid to use graphics, recommended to use 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 in the printer. Sample Code

How To Test It:

  • Acceptable time to print is based on your use case.

  • Battery lifetime health is measured by 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.

Top


9. Branding

If present, usage of Zebra branding (logo, model number, etc.) must meet Zebra Technologies Global Brand Standards.

How To Do It:

How To Test It:

Top


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:
  • If the device is unable to communicate because it is out of range or the printer is off, it will throw an exception. Make sure all communication to the printer is in try/catch statements.
        public void print()
         {
             new Thread(new Runnable() {
                public void run() {
                    Looper.prepare();
                    _print();
                    Looper.loop();
                    Looper.myLooper().quit();
                }
            }).start();
         }
         private void _print()
         {    
             // Instantiate connection for ZPL TCP port at given address
             Connection printerConnection = new TcpConnection(theIpAddress, TcpConnection.DEFAULT_ZPL_TCP_PORT);
    
             try {
                 // Open the connection - physical connection is established here.
                 printerConnection.open();
    
                 // Set settings, check status, print, etc.
             } catch (ConnectionException e) {
                 // Handle communications error here.
                 e.printStackTrace();
             } finally {
                 // Close the connection to release resources.
                 printerConnection.close();
             }
         }
        
  • 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:
  • If you want 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:
    1. Check print status during printing
          // Check during / after printing
          private boolean postPrintCheckPrinterStatus(Connection connection)
          {
              ZebraPrinter 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))
              {
                  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;
          }
          
    2. 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?
          }
          

Top


What do I do if…

My printer doesn’t print or doesn’t print what I want it to?

  • The printer may not be in a good state to print. Check and display the printer status.
        // check prior to printing
        private boolean checkPrinterStatus(Connection connection)
        {
            ZebraPrinter 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)
        {
            //  Set the print language in the printer
            // Can also use the SGD class
            String setLanguage = "! U1 setvar \"device.languages\" \"zpl\"\r\n\r\n! U1 getvar \"device.languages\"\r\n\r\n";
            byte[] response = connection.sendAndWaitForResponse(setLanguage.getBytes(), 500, 500);
            String s = new String(response, "UTF-8");
            if (!s.contains("zpl"))
            {
                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)

Top


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
    • Avoiding the use of graphics and making sure necessary graphics are small
    • Use templates or stored formats (See code on Github)
  • Use the status channel to check status and settings
       Connection btStatusConnection = new BluetoothStatusConnection(theBtMacAddress);
        // or
        Connection nwStatusConnection = new TcpStatusConnection(theIpAddress, TcpStatusConnection.DEFAULT_STATUS_TCP_PORT);
        
  • Set the print speed to fastest 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.

Top


We hope you found this helpful and let us know on the Developer Portal if there are ways we can improve this document!