Special Characters or Accents Printing Wrong in ZPL
Why accents, symbols and non-English characters print as the wrong characters on a Zebra printer, and how to fix the encoding mismatch.
Almost every “wrong character” problem is an encoding mismatch: the application encodes the text one way and the printer decodes it another way.
Recognise the pattern
You send é and get… |
Meaning |
|---|---|
é |
UTF-8 bytes read as single-byte code page. Fix: add ^CI28 |
? or a block |
The character isn’t in the code page or font |
| a different accented letter | Wrong code page, e.g. ^CI0 vs ^CI27 |
| two characters for each accent | Same as the first row |
| correct on screen, wrong on label | The label ZPL was re-encoded on the way to the printer |
Fix 1: set the matching character set
If your application outputs UTF-8, start the label with ^CI28:
^XA
^CI28
^FO50,50^A0N,50,50^FDCafé crème brûlée^FS
^XZ
If it outputs Windows-1252, use ^CI27. Use the one that matches your data, not the one that “looks right” on a single test.
Fix 2: send the characters as hex
If you can’t control the encoding of the file, use ^FH and underscore escapes:
^XA
^CI28
^FO50,50^A0N,50,50^FH^FDCaf_C3_A9^FS
^XZ
_C3_A9 is UTF-8 for é. With ^CI27 use _E9.
Fix 3: use a font that has the character
Font 0 covers common Western European accents. For symbols, Greek, Cyrillic and more, download a Unicode TrueType font.
Fix 4: check the whole path
- Is the file saved as UTF-8 without a byte-order mark? A BOM (the bytes
EF BB BF) can print stray characters before your text. - Does the print spooler or driver convert text? Send raw ZPL to port 9100 or use a raw/text-only queue to test.
- Is your language’s script supported? See language pages.
Characters that need special handling
^ and ~ are command characters. To print them, enable hex mode with ^FH and send _5E for ^ or _7E for ~. The underscore itself is _5F.
Test it
Paste your ZPL into the live preview to check the text, and use the label generator which writes the hex escapes automatically.
The quick diagnosis
Look at what you get instead of what you wanted, and you can usually name the cause in seconds. If every accented letter becomes two junk characters, your text is UTF-8 and the printer is reading it as something older. If an accent turns into a different accent, you’ve got a mismatch between two legacy code pages. If you see only question marks or boxes, the character is missing from the font or from the code page you selected.
Why does this matter? Because fixing the wrong layer wastes time. People often download a new font when the real problem is the character set, or change the character set when the problem is the file’s encoding. Identify the symptom first.
Layer one: how the file is saved
Start with the source. Open your ZPL or template in an editor that shows its encoding. If it says UTF-8, fine. If it says ANSI, Windows-1252 or Latin-1, then the printer needs a matching character set and a UTF-8 setting will break it. Saving in the wrong encoding is common when somebody opens a UTF-8 file in an older program, edits one line and saves.
Check for a byte-order mark too. It’s a few invisible bytes at the start of a file, and some programs insert it automatically. Printers don’t expect it, and it can show up as a stray character or cause the first command to fail. Most editors have a “UTF-8 without BOM” option.
Layer two: how the data reaches the printer
Between your program and the printer there may be a spooler, a driver, a gateway or a web service, any of which could re-encode text. If the ZPL file looks right on disk and prints wrong, test by sending it straight to the printer’s raw port. If it prints correctly that way, the transformation is in the path you were using. The fix is to switch to a raw queue or send the data directly from your application.
Layer three: the printer’s character set
Now ^CI matters. Use ^CI28 for UTF-8 and the matching value for any other encoding. Place it at the top of the label, after ^XA, and don’t count on an earlier job having set it. There’s a table of common values in Unicode and code pages. If you see a printer ignoring the setting, confirm that your firmware supports that value.
Layer four: the font
Last, check that the font has the glyph. Font 0 covers common Western European letters but not everything. Greek, Cyrillic, many symbols and anything non-Latin may need another font. A downloaded Unicode font is the usual answer, as shown in the download guide. If the script itself needs special handling, see language support.
When you can’t control the encoding
Sometimes the pipeline is out of your hands. Perhaps a third-party system generates the ZPL and you can’t change how it encodes text. In that case, ^FH is your safety net: you write each troublesome character as underscore and hex digits, which are plain ASCII and travel safely through anything. It looks ugly, but it works. Our label generator builds this for you, and the UTF-8 checklist on the blog goes through the steps with examples.
Special cases worth knowing
The caret and tilde can’t appear in a field without escaping because the printer treats them as commands. The underscore needs care once ^FH is on, since it becomes the escape character. Currency symbols such as the euro sign sit in different positions across old code pages, and are a classic source of trouble. Typographic quotes and dashes copied from word processors are another repeat offender, because they don’t exist in basic character sets.
Build yourself a test string
Keep a line of text that contains the awkward characters your business really uses. Print it after every change to fonts, firmware or software. If it comes out right, you can be reasonably confident. If it doesn’t, you’ve caught the problem before a full production run.
Questions people ask
Why do only some characters break? Plain ASCII is the same in nearly every encoding, so only the extra characters show the mismatch.
Does it matter which program created the file? Yes, because each one has its own default encoding.
Can I fix it in the printer alone? Only by choosing the right ^CI. The data has to match.
What if nothing works? Turn the text into a bitmap using the text-to-graphic tool.
Last reviewed 2026-10-20