I thought that the duty of frontend developers only contained dealing with browsers, or doing some client-side and server-side work; I never thought that, as the title suggests, I actually need to solve the problem of printers.
Strange Requirement
We have a platform for printing shipping labels, about which I have written several articles before, so I won't go into details here.
One day, the product manager of the shipping label suddenly added me to a group chat, because our business PM received an urgent requirement: when we started to cooperate with a seller, they requested that we must support printing through a printer called Zebra, otherwise they would not cooperate with us. Since that was a big seller, this seemed to be an irresistible requirement. Although we had never heard of this type of printer, we still went ahead with it.
After investigation, we found that Zebra printers are different from ordinary printers. They cannot print through the system's print dialog, nor do they use COM or USB interfaces. Instead, they listen on a TCP port to communicate with the computer. What is sent to the printer is not a general format, but a private format called ZPL, which looks like plain text (example from English Wikipedia):
^XA
^LH30,60
^FO20,10
^ADN,90,50
^FDWikipedia^FS
^XZAt first glance, it seems not to be easy to understand. But through Google, we found various tutorials on ZPL, as well as a simple preview tool: Online ZPL Viewer.
Start Research
At the beginning of the research, I encountered a multiple-choice question: should I generate ZPL directly, or convert it through some intermediate format? Both methods seem feasible:
- Generate directly: The original format of the shipping label generated by the platform is JSON, which can be conveniently stored, imported and exported. We wrote a converter that, when rendering the shipping label, can call the converter to convert JSON to HTML, and then call Headless Chrome to convert HTML to PDF and return it to the third party. Since we have this converter, and ZPL is plain text with a fixed syntax, it seems to be the best way to learn a little ZPL and write another converter.
- Convert and generate: No one on our team has ever touched ZPL, and I can't even find a Zebra printer around me. How do we know how much difference there is between the preview tool and the actual printout? If there is a mature library that can help with the conversion, both our support speed and the conversion effect will be much better.
After discussing with my colleagues, we decided to convert and generate, because if we generate directly, there will be a learning cost and unpredictable compatibility issues.
Technical Solution and Implementation
After searching the entire web, the only available library was kylemacfarlane/zplgrf, a Python library. The shipping label platform had basically switched from Python to Golang, but for this library, we still need to start a Python service. I studied this library, and found that it can convert PDF to a format called GRF, and then convert it to ZPL. It can be implemented with very simple code:
from zplgrf import GRF
import sys
with open(sys.argv[1], 'rb') as pdf:
data = pdf.read()
# 203 dpi is 8 dpmm, a relatively low resolution
pages = GRF.from_pdf(data, 'DEMO', dpi=203)
for grf in pages:
print(grf.to_zpl())I randomly got a shipping label from the test environment, exported it as a PDF file, and it looks like this:

The converted ZPL, although it looks like a bunch of Base64 inside, seems to have no problem except for a little blur:

There are many options for the resolution of ZPL, but higher resolution means slower conversion. We cooperated with BPM to learn the printer model held by the target users, and converted several real shipping labels in the UAT environment using resolutions of 8 dpmm, 12 dpmm and 24 dpmm, and recorded the conversion time. After some thoughts, we decided to use a resolution of 8 dpmm.
After some time of joint debugging and testing with the Seller Centre team, the feature was successfully launched on UAT.
Fixing the Paper Swallowing Issue
I thought everything was going well, but the business team reported a problem: for a specific model of printer, after printing, the printer will spit out a little more paper, and then roll it back. All sellers with that model of printer can reproduce this problem stably, but they have no problem printing ZPL shipping labels generated from other platforms. This makes our users dissatisfied, so we must solve it.
A tricky problem is that we don't have a Zebra printer at all, and it's too slow to buy one now. I had to look at the generated files and ask BPM to provide a ZPL from another platform, so I could know what caused the problem.
After comparison, I found that the ZPL file we generated had some strange instructions. Referring to the ZPL instruction set document, I found an instruction called ^MM that can control the behavior of the printer when printing is completed. If it is set to R, it can prevent the printer with the paper roll control from rolling the paper back. I searched for that model of printer (the top results on Google were actually our competitors Lazada and Tokopedia), and indeed it has a paper roll control, so this should be the solution.
In the above code, the GRF.from_pdf function can accept a parameter print_mode. After changing it to R, the paper roll problem was solved, but the business team reported a misalignment problem... Later, I found the official driver document of the Zebra printer, and found that the default behavior of the printer should be T, so I set print_mode='T', and the problem was solved.
Since we have some time difference with the business team, all our attempts need to be verified by the business the next day, so the time to solve the problem was a little longer.
Quickly Fixing the Blurry Text Issue
Finally, the feature was launched. But shortly after it went live, a large number of sellers reported that the text was not clear, and some parts of the text were missing:

The previous experience immediately told me that it was caused by pure black/white and resolution: some "strokes" of this font will be directly converted to white because they are too thin at 8 dpmm resolution. The solution is also very simple: either increase the resolution (spend more time and need to re-release) or try to bold the text on the shipping label.
We first asked the business team to help modify the shipping label and bold the text. The problem was surprisingly solved, and the effect was good:

Immediately, the business team sent a thank-you letter, and many sellers thanked us for solving the problem so quickly.
But this is only a temporary solution after all, and the root cause of the problem is still the low resolution (we cannot solve the problem that the printer only supports pure black and white). In order to avoid similar problems in the future, we need to provide a high-resolution option. After estimation, if we increase the resolution from 8 dpmm to 12 dpmm, since the area is doubled, the conversion time should also be doubled. Our backend colleagues will also conduct further testing with the backend of the Seller Centre to evaluate the impact of this modification.
What I Learned from This Requirement
- In theory, all problems can be solved, but you need to have certain search skills and an exploratory spirit.
- Learning English well is very important. All the materials I searched for were not in Chinese.
- In necessary cases, you may need to read the official driver document or instruction set document in depth.
In general, it was also a good experience, and the problem was finally solved. If I encounter similar requirements in the future, I should be more calm than this time.