Skip to content

Commit dc1c69c

Browse files
committed
feat(zpl): emit partial ^CF for round-trip fidelity
The ^CF parameters are individually optional per Zebra spec: ^CF0 sets the font without changing the height, ^CF,30 sets the height without changing the font. Previously the generator required both fields and would silently drop a partial command on round-trip. Now emits whichever fields are set, with explicit round-trip tests for both partial forms.
1 parent 846441d commit dc1c69c

2 files changed

Lines changed: 32 additions & 7 deletions

File tree

‎src/lib/zplGenerator.test.ts‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,19 @@ describe('generateZPL — printer params', () => {
9696
expect(zpl).toContain('^CF0,30');
9797
});
9898

99-
it('omits ^CF when defaultFontId is set without defaultFontHeight', () => {
100-
expect(generateZPL({ ...BASE_LABEL, defaultFontId: '0' }, []))
101-
.not.toContain('^CF');
99+
it('emits ^CF{id} when only defaultFontId is set', () => {
100+
const zpl = generateZPL({ ...BASE_LABEL, defaultFontId: '0' }, []);
101+
expect(zpl).toContain('^CF0');
102+
expect(zpl).not.toContain('^CF0,');
102103
});
103104

104-
it('omits ^CF when defaultFontHeight is set without defaultFontId', () => {
105+
it('emits ^CF,{height} when only defaultFontHeight is set', () => {
105106
expect(generateZPL({ ...BASE_LABEL, defaultFontHeight: 30 }, []))
106-
.not.toContain('^CF');
107+
.toContain('^CF,30');
108+
});
109+
110+
it('omits ^CF when neither defaultFont field is set', () => {
111+
expect(generateZPL(BASE_LABEL, [])).not.toContain('^CF');
107112
});
108113

109114
it('emits printer params in canonical header order before ^LS', () => {
@@ -238,4 +243,18 @@ describe('generateZPL — parse/generate roundtrip', () => {
238243
expect(labelConfig.defaultFontId).toBe('0');
239244
expect(labelConfig.defaultFontHeight).toBe(30);
240245
});
246+
247+
it('preserves partial ^CF (id only) through generate -> parse', () => {
248+
const regenerated = generateZPL({ ...BASE_LABEL, defaultFontId: 'A' }, []);
249+
const { labelConfig } = parseZPL(regenerated, BASE_LABEL.dpmm);
250+
expect(labelConfig.defaultFontId).toBe('A');
251+
expect(labelConfig.defaultFontHeight).toBeUndefined();
252+
});
253+
254+
it('preserves partial ^CF (height only) through generate -> parse', () => {
255+
const regenerated = generateZPL({ ...BASE_LABEL, defaultFontHeight: 25 }, []);
256+
const { labelConfig } = parseZPL(regenerated, BASE_LABEL.dpmm);
257+
expect(labelConfig.defaultFontId).toBeUndefined();
258+
expect(labelConfig.defaultFontHeight).toBe(25);
259+
});
241260
});

‎src/lib/zplGenerator.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,14 @@ export function generateZPL(label: LabelConfig, objects: LabelObject[]): string
3030
// darkness=0 is a valid value (printer baseline), so check undefined explicitly.
3131
if (label.darkness !== undefined) lines.push(`^MD${label.darkness}`);
3232
if (label.printOrientation) lines.push(`^PO${label.printOrientation}`);
33-
if (label.defaultFontId && label.defaultFontHeight !== undefined) {
34-
lines.push(`^CF${label.defaultFontId},${label.defaultFontHeight}`);
33+
// ^CF parameters are individually optional per Zebra spec: ^CF0 sets the
34+
// font only, ^CF,30 sets the height only. Preserves round-trip fidelity
35+
// when an imported label used a partial command.
36+
if (label.defaultFontId || label.defaultFontHeight !== undefined) {
37+
const id = label.defaultFontId ?? "";
38+
const height =
39+
label.defaultFontHeight !== undefined ? `,${label.defaultFontHeight}` : "";
40+
lines.push(`^CF${id}${height}`);
3541
}
3642
if (label.labelShift) lines.push(`^LS${label.labelShift}`);
3743

0 commit comments

Comments
 (0)