^CI - Change International Font / Encoding
Selects the character encoding the field data is written in.
^CI tells the printer how to decode the bytes that arrive in ^FD. Almost
every use of it today is ^CI28, which selects UTF-8. The low-numbered sets are
the 1980s arrangement, where a single byte position held a different accented
letter in each country. ^CI does not change the font, so it cannot reach a
glyph the selected font never had. A correct encoding and a missing glyph look
much the same on the label.
Syntax
^CIa,s1,d1,s2,d2Support
The command renders, with the caveat below.
The renderer honors encodings 0–17, 24, 26–31 and 33–36. A number outside that set defaults to encoding 0 and reports ARGUMENT_INVALID with the value. Earlier versions aborted the render. The parser reads the character remapping (s1,d1,…) and ignores it. — not pinned by a test
Labelary renders this command.
Parameters
aCharacter setoptional · one of a setEncoding used to read the bytes in ^FD. A format is a byte stream, so the encoding applies one byte at a time: a no-break space written as the UTF-8 bytes C2 A0 prints two characters under every set but 28, ┬á under 0 and 13 and  plus a no-break space under 27. A format whose first three bytes are the UTF-8 byte order mark reads as though it opened with ^CI28, and any later ^CI overrides that. We measured both against Labelary on 2026-08-17.
- Default
- 0
- Invalid value
- discarded, and the default applies. Values 18–23, 25, 32 and 37 or above name no encoding. They reset the character set to 0, the set a format starts in, and report
ARGUMENT_INVALID. We measured this against Labelary on 2026-07-30: Labelary resets it too but stays silent. So^CI27^CI99renders pixel-identically to^CI27^CI0and differently from^CI27. - Test coverage
- Bounds pinned: past what the shape can hold (99999999999999999999). Values exercised: 1–12, 27, 28. Not exercised: 0, 13, 14, 15, 16, 17, 24, 26, 29, 30, 31, 33–36. The invalid-value rule is pinned by 4 tests, including the diagnostic it emits.
0- Single-byte U.S.A. 1 (Zebra code page 850 with US replacements) (default)
1–12- Single-byte national variants of code page 850, in order: U.S.A. 2, U.K., Holland, Denmark/Norway, Sweden/Finland, Germany, France 1, France 2, Italy, Spain, miscellaneous, and Japan (ASCII with the yen sign)
13- Zebra code page 850
14- Double-byte Asian encodings
15- Shift-JIS
16- EUC-JP and EUC-CN
17- UCS-2 big endian, deprecated
24- Single-byte Asian encodings
26- Multibyte Asian encodings, ASCII transparent
27- Zebra code page 1252
28- UTF-8, the usual choice for anything beyond ASCII
29- UTF-16 big endian
30- UTF-16 little endian
31- Zebra code page 1250
33–36- Code pages 1251, 1253, 1254 and 1255
s1Source characteroptional · integernot honoured by zpl.toolsThe glyph to print. Up to 256 s,d pairs can follow the character set. The renderer ignores them.
- Default
- none, which leaves the encoding's own mapping in place
- Accepted range
- 0 to 255
- Invalid value
- discarded, and the default applies
- Test coverage
- No test at the minimum, maximum or one step outside the range. The invalid-value rule is pinned by 1 test.
d1Destination characteroptional · integernot honoured by zpl.toolsThe byte in ^FD that must print as s1. The renderer ignores it.
- Default
- none
- Accepted range
- 0 to 255
- Invalid value
- discarded, and the default applies
- Test coverage
- No test at the minimum, maximum or one step outside the range. The invalid-value rule is not pinned by a test.
Behavior
The setting applies to the fields that follow it, and persists until the next
^CI, including into the next label of the same data stream. One label can mix
sets: put a ^CI immediately before the field that needs it.
Sets 0 to 13 are all Zebra code page 850, with a few byte positions changed
for the national letters. 28 and above are the modern encodings, where one
character can occupy several bytes. For UTF-8 field data, ^CI28 is the only
correct choice, and it belongs at the top of the format before any ^FD.
Character remap
The arguments after the character set are source, destination pairs, and they
run in the direction the names do not suggest. The destination is the byte the
format sends, and the source is the glyph that prints. ^CI0,65,66 prints every
B in the field data as an A. Up to 256 pairs can follow. A remap reaches a
glyph that has no byte position of its own in a single-byte code page, and it has
no purpose under ^CI28.
Hexadecimal escapes
^CI decides how the printer interprets bytes, not how a format types them. To
put a byte into a format that cannot carry it literally, use ^FH and write it as
_xx in the field data. _ is the default indicator and always takes two
hexadecimal digits. ^CI then decodes the result as usual.
A character set that does not exist
Not every number names an encoding: 18 to 23, 25, 32 and everything from
37 upwards are reserved or invented. Such a value resets the character set to
0, the set a format starts in, and the renderer reports ARGUMENT_INVALID with
the number. The label still renders.
A measurement settles that, rather than a choice. On Labelary (2026-07-30)
^CI27^CI99 gives the same pixels as ^CI27^CI0 and different pixels from
^CI27, so Labelary resets the set rather than ignores the unusable value, and it
does so silently. Until 2026-07-30 the same input aborted the whole render here,
so a one-character typo — ^CI19 for ^CI9 — cost a conversion.
When correct encoding still prints wrong
A correct ^CI guarantees only that the printer knows which character the format
means. If the selected font has no glyph for that character, nothing appears, and
the two failures look identical on a label. The bitmapped fonts are the usual
cause. Zebra drew their cells for code page 850, so a Latin Extended-A letter such
as ť is absent from font D on real hardware and on Labelary alike. Use font
0, or a TrueType face, for anything outside the Latin-1 range.
How far each slot reaches into Latin Extended-A was swept position by position on
2026-08-19. Slot N draws all 128, J draws 79, 0 L P Q R S T U V draw the 61
Central European positions, A B C D F G Z draw 10, E draws 2 and H none. A
character outside its slot's set prints as a space and the field reports
Field data contains characters which the field font cannot display. The
differences below record where the fonts here fall short of Labelary's.
Known issues
Tested behavior
Differences from the specification
Labelary against the ZPL II guide
Where Labelary — our compatibility target — behaves differently from the printed specification.
- Labelary honors only character sets 0–13, 15, 27, 28, 31 and 33–36. — not pinned by a test
- The 2006 guide documents 0 to 27 only, and reserves 18–23 and 25. Sets 28 to 36 (the UTF encodings and the Windows code pages) come from later firmware, and Labelary documents them rather than the guide. — not pinned by a test
- Labelary's reference states the remapping pairs apply only to the code page 850 variants, character sets 0 to 13. The guide places no such restriction on them. — not pinned by a test
zpl.tools against the ZPL II guide
Where our renderer behaves differently from the specification.
- The renderer accepts character remapping pairs and ignores them. We measured that
^CI0,65,66^FDBBB^FSprintsBBBhere andAAAon Labelary. — not pinned by a test - A character-set number the renderer does not map (18–23, 25, 32, ≥37) resets the character set to 0. The guide has the printer keep its current set; Labelary resets it, and the renderer follows Labelary. We measured this on 2026-07-30. — pinned by
test_unmappable_character_set_resets_a_set_already_in_force,test_unmappable_character_sets_render_like_ci0
zpl.tools against Labelary
Where our output differs from Labelary for the same input. These are the differences that break a migration.
- A diagnostic that quotes a raw argument value reads its bytes as code page 850, whatever
^CIis set to. Labelary follows the active set: we measured on 2026-08-19 that^CI28^CF0,28<C3 A9>XYquotes28éXYthere and two code page 850 characters here. Under^CI0and^CI13, the sets a format starts in, the two agree. — not pinned by a test - The renderer also honors sets 14, 16, 17, 24, 26, 29 and 30, which Labelary does not. — not pinned by a test
- An unmappable character-set number reports
ARGUMENT_INVALID; Labelary accepts it silently. The pixels are the same either way. — not pinned by a test - Fonts
A,C,D,F,GandZdraw-and|from DejaVu Sans Mono rather than from Labelary. Measured off Labelary's own vector page on 2026-08-19: slotA's hyphen inks (0,662,1133,897) of a 2048 unit em against DejaVu's (356,508,877,682), so it is more than twice as wide and centered on the box rule's line rather than below it, and its|inks (480,-512,663,1660) against (530,-512,702,1660). The tweak pass that fitted these faces matched the letterforms.HandMtrace back onto the shipped outlines to the unit, and these two did not come with them. The box-drawing characters they were found beside are Labelary's now. — not pinned by a test - The warning for an undisplayable character spans the whole
^FDcommand. Labelary spans only the field data, three bytes further in and three bytes shorter. The message, the command and the one-warning-per-field count all agree, so only the byte span differs. Every warning this renderer attributes to^FDsits three bytes wide of Labelary's the same way. We measured this on 2026-08-18. — not pinned by a test
Example
UTF-8 field data, with the euro sign and accented letters:
^XA
^CI28
^FO50,40^A0N,40^FDPrix : 12,50 €^FS
^FO50,110^A0N,40^FDMüller, Ñuñez, Åsa, Ægir^FS
^XZ
Related commands
^FHField Hexadecimal IndicatorLets the following `^FD` carry bytes as hexadecimal escapes.^FDField DataSupplies the text or barcode content of the field.^AScalable / Bitmapped FontSelects the font, rotation and size for the following field.^SESelect EncodingSelects a downloaded encoding table.