zpl.tools
Serialisation

^SF - Serialization Field

Increments the current field's data between copies according to a per-character mask.

^SF is the general form of serialization. ^SN counts in decimal; ^SF advances an existing ^FD string one character position at a time, in the base each position declares. That renders a sequence such as BL0000 … BL9999, BM0000, or one in hexadecimal, or one with a fixed separator in the middle. Unlike ^SN it does not replace ^FD; it masks the ^FD in its own field.

Syntax

^SFa,b

Support

Supportedparsed: full · state: full · rendered: full

Every documented parameter renders the same output Labelary produces.

The renderer parses ^SF and advances the data of the field it sits in, once per copy ^PQ prints. Each mask character counts its position through its own alphabet, a carry runs leftwards through the held positions and stops at the region's left edge, and the increment is read one character per position. ^PQ's replicates argument r sets how many output pages share one value. Labelary's report about a mask or increment width that differs from the field data has no counterpart here. Measured against Labelary on 2026-08-21. — not pinned by a test

Labelary renders this command.

Parameters

aSerialisation maskrequired · text

Sets the serialization scheme, one mask character per character of the field data. The mask is left-aligned to the data, so the characters it covers are the leading ones. Case matters: h counts through lower-case hex digits and H through upper-case ones. The guide limits mask and increment together to 3K.

Invalid value
not possible — any value is accepted. A mask character outside dDoOhHaAnN% holds its position unchanged, exactly as % does, and a carry still passes through it. A mask of nothing but such characters, or an empty mask, serializes nothing. Measured against Labelary on 2026-08-21.
Test coverage
bIncrementoptional · text

How much to add at each copy, written in the alphabets the mask selects. It is right-aligned inside the region the mask covers, so a one-character increment steps that region's last position. Alphabetic positions count from A or a as zero, so B steps a letter by one, and a % holds a position unchanged.

Default
1
Invalid value
not possible — any value is accepted. A character the position's alphabet does not hold counts as zero there, and so does a %, a comma or a line break. ^SF has no third parameter: everything after the first comma is the increment. Measured against Labelary on 2026-08-21.
Test coverage

Behavior

Each mask character declares the alphabet its position counts through, and the case it is written in is the case the counter prints. d and D count the ten digits, o and O the eight octal ones, h the lower-case hexadecimal digits and H the upper-case ones, a the lower-case letters and A the upper-case ones, n and N all 36 alphanumerics — 0 to 9 followed by the letters. A % holds its position, and so does any other character, which keeps a separator or a fixed prefix out of the count.

A position that runs past the end of its alphabet carries into the position to its left. The carry passes straight through a held position without changing it, and it stops at the left edge of the region the mask covers, so the counter wraps rather than grows: ^FD99^SFdd,1 prints 99, then 00, then 01.

The mask is left-aligned to the field data, so the characters it serializes are the leading ones. A character the mask's alphabet cannot hold freezes its own position and leaves the rest of the region counting: ^FD000E^SFhhhh,0010 prints 000E, 001E, 002E, because E is not a lower-case hexadecimal digit.

The increment uses the same alphabets as the mask, position by position, and it is right-aligned inside the region the mask covers. A one-character increment therefore steps that region's last position. In a letter position the count starts at A or a as zero, so an increment of B steps a letter by one and F steps it by five. A position that must not advance needs a % in the increment string as well as an alphabet character in the mask. Any character the position cannot represent counts as zero there.

^SF sits inside the field whose data it advances, either side of the ^FD, and its reach ends at the ^FS that closes that field. Where a field carries both an ^SN and an ^SF, the last of the two is the one whose increment runs.

The mask and the increment together must not exceed 3K, which no realistic field approaches. All of the above was measured against Labelary on 2026-08-21.

Differences from the specification

Labelary against the ZPL II guide

Where Labelary — our compatibility target — behaves differently from the printed specification.

  • The guide aligns the mask to the right-most characters of the ^FD string. Labelary aligns it to the left-most ones instead, and reports the width difference without refusing the field. We measured this on 2026-08-21: ^FD12345^SFdd,1 with ^PQ3 prints 12345, 13345, 14345 on Labelary, where the guide's rule would give 12345, 12346, 12347. Labelary also answers Field data length (5) does not match ^SF mask length (2). Give the mask the full width of the field data and the two rules agree. — pinned by test_sf_mask_narrower_than_the_data_counts_from_the_left
  • ^SF takes a mask and an increment, and Labelary reads everything after the first comma as the increment. We measured this on 2026-08-21: ^FD0000^SFdddd,0001,9 reports ^SF increment length (6) and steps by 109 a copy, because the comma counts as a zero in its position. — not pinned by a test
  • ^SN indexes at most the twelve right-most digits of its counter. ^SF has no such limit on Labelary: measured 2026-08-21, all fourteen digits of ^FD99999999999999^SFdddddddddddddd,00000000000001 take part and the value wraps to fourteen zeros. — not pinned by a test

zpl.tools against Labelary

Where our output differs from Labelary for the same input. These are the differences that break a migration.

  • Labelary reports a mask or an increment whose length differs from the field data: Field data length (5) does not match ^SF mask length (2) and the same sentence for the increment, both on the ^FD. The renderer serializes the field the same way and says nothing. Measured 2026-08-21; the rendered label is identical either way. — pinned by test_sf_mask_narrower_than_the_data_counts_from_the_left

Example

Two serialised fields on the same label. The first counts the four trailing digits and carries into the letters, so the three copies read BL0000, BL0001, BL0002. The second holds its hyphen with a % in the mask and a matching % in the increment, giving BL00-0, BL01-1, BL02-2:

^XA
^FO40,40^A0N,50,50^FDBL0000^SFAAdddd,1^FS
^FO40,120^A0N,50,50^FDBL00-0^SFAAdd%d,1%1^FS
^PQ3
^XZ

Open in viewer

  • ^SN Serialization DataMakes the current field a counter that steps by a fixed amount for each copy `^PQ` prints.
  • ^FD Field DataSupplies the text or barcode content of the field.
  • ^PQ Print QuantityPrints the label more than once, the one command here that changes how many pages come back.
  • ^FN Field NumberNumbers a field so a stored format can fill it in later.