Sign In
Products
ZPL ViewerBarcode GeneratorImage → ZPLZPL cheat sheetLabel DesignerTemplate Gallery
Developers
API QuickstartAPI ReferenceMCP ServerBulk GenerationWebhooksSDKs & ExamplesLimitsDirectPrint agent NewGitHub ActionVS Code extensionOn-Premise
Solutions
Logistics & ShippingE-CommerceERP & WMSManufacturingRetailHealthcare & LaboratoryAll solutions
Tools
ZPL Diagnostics HubZPL DebuggerSize & DPI AnalyzerDPI ConverterBarcode fit calculatorShipping Label AnalyzerFont InspectorOrientation DebuggerCalibration AssistantPrinter Compatibility CheckerBarcode Readability CheckerPrinter Language DetectorThermal Printer Test PackZPL ToolsEPL ToolsTSPL ToolsCPCL ToolsPrinter Language ConverterAll ToolsGuidesPricing
Language
EnglishTürkçeDeutsch
Home›Guides›Using custom fonts in ZPL

Using custom fonts in ZPL

Published: · Updated:

Fonts

#FontTypeH × W (dots)H × W (mm)RotationText

Downloaded fonts in this file

    Notes and findings

      Zebra's built-in fonts (^A0, ^AD…) cover most labels. But if you need your brand typeface, or characters outside the built-in sets, you have to supply the font yourself. ZPL has two commands for this: ~DU loads a font, ^A@ calls it.

      ~DU: the font download command

      Converting a TrueType font to ZPL produces one enormous line:

      ~DUR:BRAND.TTF,48213,00010000000A0080...

      Three parts: the destination drive and name (R:BRAND.TTF), the byte count, and the whole font as hex. The command is the font — a 50 KB font takes roughly 100 KB in ZPL, because every byte is written as two characters.

      ^A@: using the loaded font

      Once the font is in place, the field command looks like this:

      ^XA
      ~DUR:BRAND.TTF,48213,0001...
      ^FO50,50^A@N,60,60,R:BRAND.TTF^FDHello^FS
      ^XZ

      Open this label in the ZPL viewer →

      ^A@ takes orientation (N/R/ I/B), height, width and the font path. Omit the path and the last selected font is used. Give a path for a font that was never loaded and the text is not drawn — you get a warning instead. A silently empty field usually goes unnoticed until the label is in someone's hand.

      The real problem: one font, fifty templates

      The example above works but does not scale. ~DU travels inside the label, so using one font across fifty templates means shipping the same 100 KB hex block fifty times. On every print. Across the network, through the queue, and through the printer's parser.

      Nobody works that way on a real Zebra printer: the font is loaded once into the printer's R: memory and labels only call it with ^A@. The label becomes 40 bytes instead of 100 KB.

      Virtual printer memory

      Labelixa's equivalent is account-level font memory: upload the font once to /v1/fonts/library, and your labels carry only ^A@. At render time your account's fonts are prepended to the stream — so your labels are written as if real printer memory existed.

      # once: upload the font to your account
      curl -X POST https://api.labelixa.com/v1/fonts/library   -H "X-API-Key: $KEY" -H "X-Font-Name: BRAND.TTF"   -F file=@Brand-Bold.ttf
      
      # from now on, every label is just the call
      ^XA
      ^FO50,50^A@N,60,60,R:BRAND.TTF^FDHello^FS
      ^XZ

      If a label still carries its own ~DU, that one wins: the in-stream font overrides the stored one. This is deliberate — a real printer behaves the same way, and it gives you an escape hatch when a template must be independent of account state.

      Making the font smaller: subsetting

      A font carries thousands of glyphs; your label might use fifty. Both endpoints accept a chars parameter that keeps only the characters you list. On a label with fixed text this often cuts the file to a tenth.

      curl -X POST "https://api.labelixa.com/v1/fonts?chars=ABCDEFGHI0123456789"   -H "X-API-Key: $KEY" -F file=@Brand-Bold.ttf

      Limits, and why they are where they are

      Loading the font into the printer itself: ~DY, ^CW and E: vs R:

      If the labels go to a real Zebra rather than through an account, the "upload once" path is the printer's own memory. Three details decide whether that works on the first try.

      ~DY or ~DU. ~DU is the older, TrueType-only download in ASCII hex. Zebra's current general-purpose download is ~DY, which also carries graphics and can send the data in binary — half the size on the wire. ZebraDesigner and Zebra Setup Utilities emit ~DY; most online converters emit ~DU. Both end up as the same file in printer memory, and ^A@ calls either.

      ~DYE:BRAND.TTF,B,T,48213,,<binary data>
      ^XA^HWE:*.*^XZ            ; list the E: drive — is BRAND.TTF there?

      E: or R:. E: is flash and survives a power cycle; R: is DRAM and is cleared when the printer is switched off. A font sent to R: in the morning is gone after the first restart — the most common reason a working label starts printing in the default font a week later. Put permanent fonts on E:; the ^HW directory listing above confirms what is really stored.

      ^CW: give the font a letter. ^CW binds a stored font to a single-character identifier, so a template that was written with the built-in ^A letters can switch typeface without touching every field:

      ^XA
      ^CWQ,E:BRAND.TTF
      ^FO50,50^AQN,60,60^FDHello^FS
      ^XZ

      Open this label in the ZPL viewer →

      The identifier must be a single letter A–Z or digit 0–9; a two-character or lowercase identifier is silently ignored and the field falls back to the default font — the mismatch described in the special characters guide. Whichever route you take, pair the font with ^CI28 when the text is UTF-8, otherwise the glyphs are present but the bytes are decoded as a single-byte code page. The font inspector reads a TTF and shows which characters it actually contains before you send it anywhere.

      Which one to pick

      Embed the font when the label has to travel on its own — archived as a file, handed to a third party, or sent to a printer that never goes through your account. Upload it when the same font is used across templates and printed regularly; there the saving repeats on every print.

      Frequently asked questions

      How do I print my own font on a Zebra printer?

      Two ways: embed the font into every job with ~DU, or upload it once to printer memory and reference it with ^A@. Upload-once wins when many templates share the same font.

      Why did my uploaded font disappear after a power cycle?

      It was stored in DRAM (R:), which clears on power-off. Store fonts in flash (E:) so they survive restarts.

      My font file is too large — what can I do?

      Subset it: strip the glyphs you never print. Label fonts rarely need more than a few dozen characters, and subsetting cuts the file size dramatically.

      What does ^CW do in ZPL?

      ^CW binds a font stored in printer memory to a one-character identifier (^CWQ,E:BRAND.TTF), so fields written with ^AQ use that font. The identifier must be a single letter A-Z or digit 0-9; anything else is ignored and the default font prints.

      Related guides

      Why can a label format set media type?

      A label format can switch the printer to thermal transfer — by design. Why ^MT exists, when it helps, and what disabling it costs your fleet.

      How to center text in ZPL

      ZPL has no center command for plain text. Use a ^FB block with C justification, or measure the text width and compute the x position yourself.

      PDF or ZPL: choosing a label pipeline

      Two pipelines for the same label: a rendered PDF through a driver, or native ZPL to the printer. What each costs in speed, fidelity and support.

      ZPL dots, millimetres and inches

      Every ZPL coordinate is a dot. Conversion tables for 152/203/300/600 dpi, common label sizes in dots, and what really changes when a 203 dpi file moves to 300.

      Try your own ZPL code →