^GF - Graphic Field
Draws a bitmap sent inline with the label.
^GF carries a bitmap inside the label format itself, as a field like any other, and
draws it at the current origin. It draws every logo on a shipping label that the
printer does not hold already. The three byte counts are not interchangeable: the
image height comes from c divided by d, and b gives only how many bytes
travel on the wire. A wrong d shears the raster. A wrong c crops the image.
Syntax
^GFa,b,c,d,dataSupport
The command renders, with the caveat below.
Compression types A and B render, including the :Z64:/:B64: and ACS-repeat encodings of an A payload. The renderer rejects type C, as Labelary does. — not pinned by a test
Labelary renders this command.
Parameters
aCompression typeoptional · one of a setHow the payload in data is encoded.
- Default
- A
- Invalid value
- enough to stop the field being printed. The renderer reports
ARGUMENT_INVALIDfor any other value and ignores the field. It accepts lower case, as Labelary does. - Test coverage
- Values exercised: A, B, C. The invalid-value rule is pinned by 1 test, including the diagnostic it emits.
A- ASCII hexadecimal, optionally ACS run-length compressed or wrapped as
:Z64:/:B64:(default) B- Raw binary bytes
C- Compressed binary. The renderer rejects it; see the deviations — not honoured by zpl.tools
bBinary byte countrequired · integerNumber of bytes actually transmitted in data. For a :Z64: payload this counts the compressed bytes on the wire, not the decoded raster.
- Accepted range
- 1 to 99999 bytes
- Invalid value
- enough to stop the field being printed
- Test coverage
- No test at the minimum, maximum or one step outside the range. The invalid-value rule is pinned by 1 test.
cGraphic field countrequired · integerTotal size of the uncompressed raster in bytes: bytes per row times number of rows. This, not b, determines the image height.
- Accepted range
- 1 to 99999 bytes
- Invalid value
- discarded, and the current value is kept. When
cis absent or zero, the renderer sizes the bitmap frombinstead, as older writers frame an uncompressed^GFA. - Test coverage
- Bounds pinned: above the maximum (4294967295). No test at the minimum or maximum. The invalid-value rule is not pinned by a test.
dBytes per rowrequired · integerNumber of bytes in one row of the raster. Each byte carries 8 horizontal pixels, so the image is 8 × d dots wide.
- Accepted range
- 1 to 99999 bytes
- Invalid value
- enough to stop the field being printed
- Test coverage
- No test at the minimum, maximum or one step outside the range. The invalid-value rule is not pinned by a test.
dataImage datarequired · textThe raster itself, in the encoding named by a. One bit per pixel, 1 meaning black.
- Invalid value
- enough to stop the field being printed. The renderer reports
ARGUMENT_MISSINGfor an empty payload and ignores the field. - Test coverage
- The invalid-value rule is pinned by 2 tests.
Behavior
The raster is one bit per pixel and is black where the bit is 1. ^GF has no
magnification arguments and does not scale, so its dot dimensions are its printed
dimensions. The width is 8 × d dots, always a multiple of eight, so a bitmap
written for ^GF needs a pad to a byte boundary on the right.
The preceding ^FO or ^FT positions the field, and ^FS ends it. ^FO places the
top-left corner of the raster, and ^FT places the bottom-left corner.
The renderer truncates a payload longer than the declared geometry needs, and does not reject it. A shorter payload is a decode error: the renderer ignores the field and reports a diagnostic, and draws no half image.
Compression and readability
A type A payload is text and can wrap across lines, because the decoder ignores a
space, a carriage return and a line feed inside it. ASCII-hex is also why a ^GF
field runs to kilobytes. Four run-length operators and two base64 wrappers shorten it,
and the renderer accepts all six:
| Written as | Means |
|---|---|
, | Fill the rest of this row with white |
! | Fill the rest of this row with black |
: | Repeat the previous row verbatim |
G–Y, g–z | Repeat the next character 1 to 19, or 20 to 400 by twenties |
:Z64:…:crc | The deflated raster, base64-encoded |
:B64:…:crc | The raster base64-encoded, without the deflate step |
The repeat letters accumulate, so vM asks for 320 + 7 copies. In front of : the
two engines differ: the renderer expands the count, and Labelary ignores the count and
repeats the row once. We measured this on 2026-07-29. ^GFA,48,48,16,!H: renders
three rows here and no label at all on Labelary, which stays two rows short of the
declared byte count. Write a run of repeated rows as a run of colons.
In a :Z64: or :B64: payload, b counts the encoded bytes, and c stays the size
of the decoded raster. The :crc suffix is mandatory, but neither engine checks its
value. Labelary does the same, and a stricter check would reject labels that print
today.
A type B payload is raw binary, and the parser reads it by length. It takes the b
bytes after the fourth comma verbatim, so a ^, a ~ and a , among them are image
data and not the start of the next command. This holds only while b is correct. On a
printer, ~DN or any caret or tilde that reaches the parser aborts a binary download.
That is the other reason a wrong byte count prints rubbish rather than an error.
Tested behavior
Differences from the specification
Labelary against the ZPL II guide
Where Labelary — our compatibility target — behaves differently from the printed specification.
- Labelary rejects compression type
C, which the ZPL II guide defines. — not pinned by a test - Labelary accepts lower-case compression types (
^GFa), which the guide writes only in upper case. — not pinned by a test
zpl.tools against the ZPL II guide
Where our renderer behaves differently from the specification.
- The renderer rejects compression type
Cto match Labelary, and reportsUNSUPPORTED_COMMANDinstead. — pinned bytest_gf_compressed_binary_type_c
zpl.tools against Labelary
Where our output differs from Labelary for the same input. These are the differences that break a migration.
- The renderer expands an ACS repeat count before
:; Labelary ignores it. We measured this on 2026-07-29.^GFA,48,48,16,!H:asks for three rows and gets them here. Labelary draws two rows, falls short of the declared byte count and returns no label. Write one colon per row for a portable format. — not pinned by a test
Example
An 80 × 8 dot checkerboard as plain ASCII hex, one raster row per source line:
^XA
^FO50,50^GFA,80,80,10,
FFFFFFFFFFFFFFFFFFFF
8000FFFF0000FFFF0001
8000FFFF0000FFFF0001
8000FFFF0000FFFF0001
FFFF0000FFFF0000FFFF
FFFF0000FFFF0000FFFF
FFFF0000FFFF0000FFFF
FFFFFFFFFFFFFFFFFFFF^FS
^XZ
The same command with the compression operators. This is a 64 × 16 dot hollow frame in twenty-one characters — a solid black row, a row carrying one black byte at each end and twelve zero bytes between them, thirteen repeats of that row, and a closing solid row:
^XA
^FO50,50^GFA,128,128,8,!FFR0FF:::::::::::::!^FS
^XZ
Related commands
~DGDownload GraphicDownloads an ASCII-hex bitmap into printer memory as a `.GRF` object.^XGRecall GraphicDraws a previously downloaded graphic, optionally magnified.^GSGraphic SymbolPrints one of the built-in legal symbols, selected by the following `^FD`.^FOField OriginSets the top-left corner of the next field.