@connectedxm/zpl-generator 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +422 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.es.js +2975 -0
- package/dist/vite.config.d.ts +2 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# ZPL Generator
|
|
2
|
+
|
|
3
|
+
A TypeScript library for generating ZPL (Zebra Programming Language) code from structured badge configurations. This library provides type-safe APIs for creating thermal printer labels with text fields, barcodes, and QR codes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Type-Safe Configuration**: Full TypeScript support with Zod validation schemas
|
|
8
|
+
- **Thermal Badge Support**: Generate ZPL for thermal transfer and direct thermal printers
|
|
9
|
+
- **Multiple Block Types**:
|
|
10
|
+
- Text fields with advanced formatting (word wrapping, alignment, vertical positioning)
|
|
11
|
+
- Barcodes (Code 128)
|
|
12
|
+
- QR codes with configurable error correction
|
|
13
|
+
- **Comprehensive Printer Settings**: Control print speed, darkness, orientation, media type, and more
|
|
14
|
+
- **Validation**: Built-in validation with detailed error messages
|
|
15
|
+
- **Word Wrapping**: Accurate line counting with optional word-width measurements for precise text layout
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install cxm-zpl-generator
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { generate } from 'cxm-zpl-generator';
|
|
27
|
+
import {
|
|
28
|
+
ThermalBadge,
|
|
29
|
+
MediaType,
|
|
30
|
+
ThermalMediaType,
|
|
31
|
+
PrintOrientation,
|
|
32
|
+
FontOrientation,
|
|
33
|
+
TextAlignment,
|
|
34
|
+
VerticalAlignment,
|
|
35
|
+
PostPrintMode,
|
|
36
|
+
PrepeelMode,
|
|
37
|
+
AllMediaMode,
|
|
38
|
+
BackfeedAction,
|
|
39
|
+
ReverseMode,
|
|
40
|
+
PrintDensityAdjustment,
|
|
41
|
+
} from 'cxm-zpl-generator';
|
|
42
|
+
|
|
43
|
+
const badge: ThermalBadge = {
|
|
44
|
+
type: 'thermal',
|
|
45
|
+
tearOffAdjustment: 0,
|
|
46
|
+
backfeedActions: BackfeedAction.Normal,
|
|
47
|
+
media: MediaType.NonContinuousMarkSensing,
|
|
48
|
+
mediaOffset: 0,
|
|
49
|
+
thermalMediaType: ThermalMediaType.ThermalTransfer,
|
|
50
|
+
printOrientation: PrintOrientation.Normal,
|
|
51
|
+
mirror: MirrorMode.Normal,
|
|
52
|
+
labelHomeX: 20,
|
|
53
|
+
labelHomeY: 0,
|
|
54
|
+
printDensityAdjustment: PrintDensityAdjustment.Normal,
|
|
55
|
+
printSpeed: 1,
|
|
56
|
+
slewSpeed: 1,
|
|
57
|
+
backfeedSpeed: 1,
|
|
58
|
+
darkness: 30,
|
|
59
|
+
reverse: ReverseMode.Disable,
|
|
60
|
+
postPrintMode: PostPrintMode.TearOff,
|
|
61
|
+
prepeel: PrepeelMode.NoPrepeel,
|
|
62
|
+
printWidth: 900,
|
|
63
|
+
labelLength: 600,
|
|
64
|
+
allMedia: AllMediaMode.ContinuousOnly,
|
|
65
|
+
blocks: [
|
|
66
|
+
{
|
|
67
|
+
type: 'field',
|
|
68
|
+
name: 'title',
|
|
69
|
+
x: 0,
|
|
70
|
+
y: 100,
|
|
71
|
+
data: 'Hello World',
|
|
72
|
+
font: 'A',
|
|
73
|
+
fontHeight: 100,
|
|
74
|
+
maxWidth: 900,
|
|
75
|
+
maxLines: 1,
|
|
76
|
+
lineSpacing: 100,
|
|
77
|
+
alignment: TextAlignment.Center,
|
|
78
|
+
fontOrientation: FontOrientation.NoRotation,
|
|
79
|
+
hangingIndent: 0,
|
|
80
|
+
verticalAlignment: VerticalAlignment.Start,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const zpl = generate(badge);
|
|
86
|
+
console.log(zpl);
|
|
87
|
+
// Output: ^XA
|
|
88
|
+
// ~TA000
|
|
89
|
+
// ~JSN
|
|
90
|
+
// ...
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Reference
|
|
94
|
+
|
|
95
|
+
### `generate(badge: Badge): string`
|
|
96
|
+
|
|
97
|
+
Generates ZPL code from a badge configuration.
|
|
98
|
+
|
|
99
|
+
**Parameters:**
|
|
100
|
+
- `badge`: A validated `Badge` object (must be of type `'thermal'`)
|
|
101
|
+
|
|
102
|
+
**Returns:** A string containing valid ZPL code
|
|
103
|
+
|
|
104
|
+
**Example:**
|
|
105
|
+
```typescript
|
|
106
|
+
const zpl = generate(badge);
|
|
107
|
+
// Send zpl to your Zebra printer
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `validateBadge(data: unknown)`
|
|
111
|
+
|
|
112
|
+
Validates a badge configuration object. Returns a Zod `SafeParseReturnType` with success/error information.
|
|
113
|
+
|
|
114
|
+
**Example:**
|
|
115
|
+
```typescript
|
|
116
|
+
import { validateBadge } from 'cxm-zpl-generator';
|
|
117
|
+
|
|
118
|
+
const result = validateBadge(jsonData);
|
|
119
|
+
if (result.success) {
|
|
120
|
+
const badge = result.data; // Type-safe Badge object
|
|
121
|
+
const zpl = generate(badge);
|
|
122
|
+
} else {
|
|
123
|
+
console.error(result.error.errors);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `validateBadgeOrThrow(data: unknown)`
|
|
128
|
+
|
|
129
|
+
Validates a badge configuration and throws a `ZodError` if validation fails.
|
|
130
|
+
|
|
131
|
+
**Example:**
|
|
132
|
+
```typescript
|
|
133
|
+
import { validateBadgeOrThrow } from 'cxm-zpl-generator';
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const badge = validateBadgeOrThrow(jsonData);
|
|
137
|
+
const zpl = generate(badge);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
// Handle validation error
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Badge Configuration
|
|
144
|
+
|
|
145
|
+
### Base Badge Properties
|
|
146
|
+
|
|
147
|
+
All thermal badges require the following configuration:
|
|
148
|
+
|
|
149
|
+
| Property | Type | Description |
|
|
150
|
+
|----------|------|-------------|
|
|
151
|
+
| `type` | `'thermal'` | Badge type (currently only thermal is supported) |
|
|
152
|
+
| `tearOffAdjustment` | `number` | Tear-off adjustment (0-120) |
|
|
153
|
+
| `backfeedActions` | `BackfeedAction` | Backfeed sequence configuration |
|
|
154
|
+
| `media` | `MediaType` | Media type (continuous, variable length, etc.) |
|
|
155
|
+
| `mediaOffset` | `number` | Media offset (-75 to 283) |
|
|
156
|
+
| `thermalMediaType` | `ThermalMediaType` | Direct thermal or thermal transfer |
|
|
157
|
+
| `printOrientation` | `PrintOrientation` | Normal or inverted |
|
|
158
|
+
| `mirror` | `MirrorMode` | Mirror mode setting |
|
|
159
|
+
| `labelHomeX` | `number` | Label home X coordinate (0-32000) |
|
|
160
|
+
| `labelHomeY` | `number` | Label home Y coordinate (0-32000) |
|
|
161
|
+
| `printDensityAdjustment` | `PrintDensityAdjustment?` | Optional print density adjustment |
|
|
162
|
+
| `printSpeed` | `number` | Print speed (1-14) |
|
|
163
|
+
| `slewSpeed` | `number` | Slew speed (1-14) |
|
|
164
|
+
| `backfeedSpeed` | `number` | Backfeed speed (1-14) |
|
|
165
|
+
| `darkness` | `number` | Print darkness (0-30) |
|
|
166
|
+
| `reverse` | `ReverseMode` | Reverse mode setting |
|
|
167
|
+
| `postPrintMode` | `PostPrintMode` | Post-print action (tear off, peel off, etc.) |
|
|
168
|
+
| `prepeel` | `PrepeelMode` | Prepeel mode setting |
|
|
169
|
+
| `printWidth` | `number` | Print width in dots (minimum 2) |
|
|
170
|
+
| `labelLength` | `number` | Label length in dots (1-32000) |
|
|
171
|
+
| `allMedia` | `AllMediaMode` | All media mode setting |
|
|
172
|
+
| `blocks` | `Block[]` | Array of content blocks |
|
|
173
|
+
|
|
174
|
+
### Block Types
|
|
175
|
+
|
|
176
|
+
#### Field Block (Text)
|
|
177
|
+
|
|
178
|
+
Displays text with configurable font, size, alignment, and wrapping.
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
{
|
|
182
|
+
type: 'field',
|
|
183
|
+
name: 'field_name', // Identifier for the block
|
|
184
|
+
x: 0, // X position in dots
|
|
185
|
+
y: 100, // Y position in dots
|
|
186
|
+
data: 'Text content', // Text to display
|
|
187
|
+
font: 'A', // Font identifier (A-Z, 0-9)
|
|
188
|
+
fontHeight: 100, // Font height in dots (1-32000)
|
|
189
|
+
fontWidth?: number, // Optional font width in dots
|
|
190
|
+
fontOrientation: FontOrientation, // Text rotation
|
|
191
|
+
maxWidth: 900, // Maximum width for wrapping (0-9999)
|
|
192
|
+
maxLines: 3, // Maximum number of lines (1-9999)
|
|
193
|
+
lineSpacing: 0, // Line spacing (-9999 to 9999)
|
|
194
|
+
alignment: TextAlignment, // Text alignment (Left, Right, Center, Justified)
|
|
195
|
+
hangingIndent: 0, // Hanging indent (0-9999)
|
|
196
|
+
verticalAlignment: VerticalAlignment, // Vertical alignment (Start, End)
|
|
197
|
+
wordWidths?: WordWidth[], // Optional word width measurements for accurate wrapping
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Vertical Alignment:**
|
|
202
|
+
- `VerticalAlignment.Start`: Text starts at the top of the field
|
|
203
|
+
- `VerticalAlignment.End`: Text is aligned to the bottom (padded with line breaks)
|
|
204
|
+
|
|
205
|
+
**Word Widths:**
|
|
206
|
+
For accurate text wrapping with proportional fonts, provide `wordWidths`:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
wordWidths: [
|
|
210
|
+
{ word: 'Hello', width: 50, spaceWidth: 10 },
|
|
211
|
+
{ word: 'World', width: 60, spaceWidth: 10 },
|
|
212
|
+
]
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Barcode Block
|
|
216
|
+
|
|
217
|
+
Generates a Code 128 barcode.
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
{
|
|
221
|
+
type: 'barcode',
|
|
222
|
+
name: 'barcode_name',
|
|
223
|
+
x: 0,
|
|
224
|
+
y: 200,
|
|
225
|
+
data: '1234567890', // Barcode data
|
|
226
|
+
barWidth: 5, // Bar width multiplier (4-9999)
|
|
227
|
+
orientation: FontOrientation, // Barcode orientation
|
|
228
|
+
height: 60, // Barcode height in dots (1-32000)
|
|
229
|
+
line: YesNo, // Show human-readable line below
|
|
230
|
+
lineAbove: YesNo, // Show human-readable line above
|
|
231
|
+
checkDigit: YesNo, // Include check digit
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### QR Code Block
|
|
236
|
+
|
|
237
|
+
Generates a QR code.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
{
|
|
241
|
+
type: 'qrcode',
|
|
242
|
+
name: 'qrcode_name',
|
|
243
|
+
x: 0,
|
|
244
|
+
y: 300,
|
|
245
|
+
data: 'https://example.com', // QR code data
|
|
246
|
+
orientation: 'N', // Must be 'N' (no rotation)
|
|
247
|
+
model: '2', // Must be '2' (Model 2)
|
|
248
|
+
magnification: 5, // Magnification factor (1-100)
|
|
249
|
+
errorCorrection: QRCodeErrorCorrection, // Error correction level
|
|
250
|
+
mask: 0, // Mask pattern (0-7)
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Enums and Constants
|
|
255
|
+
|
|
256
|
+
### Media Types
|
|
257
|
+
- `MediaType.Continuous` - Continuous media
|
|
258
|
+
- `MediaType.VariableLengthContinuous` - Variable length continuous
|
|
259
|
+
- `MediaType.NonContinuousWebSensing` - Non-continuous web sensing
|
|
260
|
+
- `MediaType.NonContinuousWebSensingAlt` - Non-continuous web sensing (alternate)
|
|
261
|
+
- `MediaType.NonContinuousMarkSensing` - Non-continuous mark sensing
|
|
262
|
+
- `MediaType.AutoDetect` - Auto-detect media type
|
|
263
|
+
|
|
264
|
+
### Thermal Media Types
|
|
265
|
+
- `ThermalMediaType.DirectThermal` - Direct thermal printing
|
|
266
|
+
- `ThermalMediaType.ThermalTransfer` - Thermal transfer printing
|
|
267
|
+
|
|
268
|
+
### Text Alignment
|
|
269
|
+
- `TextAlignment.Left` - Left-aligned text
|
|
270
|
+
- `TextAlignment.Right` - Right-aligned text
|
|
271
|
+
- `TextAlignment.Center` - Center-aligned text
|
|
272
|
+
- `TextAlignment.Justified` - Justified text
|
|
273
|
+
|
|
274
|
+
### Font Orientation
|
|
275
|
+
- `FontOrientation.NoRotation` - No rotation (0°)
|
|
276
|
+
- `FontOrientation.Rotate90` - Rotate 90° clockwise
|
|
277
|
+
- `FontOrientation.Rotate180` - Rotate 180°
|
|
278
|
+
- `FontOrientation.Rotate270` - Rotate 270° clockwise
|
|
279
|
+
|
|
280
|
+
### Post Print Modes
|
|
281
|
+
- `PostPrintMode.TearOff` - Tear off after printing
|
|
282
|
+
- `PostPrintMode.PeelOff` - Peel off after printing
|
|
283
|
+
- `PostPrintMode.Rewind` - Rewind after printing
|
|
284
|
+
- `PostPrintMode.Applicator` - Applicator mode
|
|
285
|
+
- `PostPrintMode.Cut` - Cut after printing
|
|
286
|
+
- `PostPrintMode.DelayedCut` - Delayed cut
|
|
287
|
+
- `PostPrintMode.EncodeRFID` - Encode RFID
|
|
288
|
+
- `PostPrintMode.Kiosk` - Kiosk mode
|
|
289
|
+
|
|
290
|
+
### QR Code Error Correction
|
|
291
|
+
- `QRCodeErrorCorrection.Highest` - Highest error correction (~30%)
|
|
292
|
+
- `QRCodeErrorCorrection.High` - High error correction (~25%)
|
|
293
|
+
- `QRCodeErrorCorrection.Medium` - Medium error correction (~15%)
|
|
294
|
+
- `QRCodeErrorCorrection.Lower` - Lower error correction (~7%)
|
|
295
|
+
|
|
296
|
+
## Advanced Usage
|
|
297
|
+
|
|
298
|
+
### Dynamic Content with Templates
|
|
299
|
+
|
|
300
|
+
You can use template strings in your badge data and replace them before generating ZPL:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
const badge: ThermalBadge = {
|
|
304
|
+
// ... configuration
|
|
305
|
+
blocks: [
|
|
306
|
+
{
|
|
307
|
+
type: 'field',
|
|
308
|
+
name: 'name',
|
|
309
|
+
x: 0,
|
|
310
|
+
y: 100,
|
|
311
|
+
data: '{{firstName}} {{lastName}}', // Template string
|
|
312
|
+
// ... other properties
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// Replace template variables
|
|
318
|
+
const badgeWithData = {
|
|
319
|
+
...badge,
|
|
320
|
+
blocks: badge.blocks.map(block => ({
|
|
321
|
+
...block,
|
|
322
|
+
data: block.data
|
|
323
|
+
.replace('{{firstName}}', 'John')
|
|
324
|
+
.replace('{{lastName}}', 'Doe'),
|
|
325
|
+
})),
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const zpl = generate(badgeWithData);
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Accurate Text Wrapping
|
|
332
|
+
|
|
333
|
+
For precise text layout with proportional fonts, measure word widths and provide them:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
import { countLines } from 'cxm-zpl-generator';
|
|
337
|
+
|
|
338
|
+
// Measure word widths (e.g., using canvas or font metrics)
|
|
339
|
+
const wordWidths: WordWidth[] = [
|
|
340
|
+
{ word: 'Hello', width: 50, spaceWidth: 10 },
|
|
341
|
+
{ word: 'World', width: 60, spaceWidth: 10 },
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
const fieldBlock: FieldBlock = {
|
|
345
|
+
type: 'field',
|
|
346
|
+
name: 'text',
|
|
347
|
+
x: 0,
|
|
348
|
+
y: 0,
|
|
349
|
+
data: 'Hello World',
|
|
350
|
+
font: 'A',
|
|
351
|
+
fontHeight: 20,
|
|
352
|
+
maxWidth: 100,
|
|
353
|
+
maxLines: 5,
|
|
354
|
+
lineSpacing: 0,
|
|
355
|
+
alignment: TextAlignment.Left,
|
|
356
|
+
fontOrientation: FontOrientation.NoRotation,
|
|
357
|
+
hangingIndent: 0,
|
|
358
|
+
verticalAlignment: VerticalAlignment.End,
|
|
359
|
+
wordWidths, // Provide measured widths
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// countLines can be used to preview line count
|
|
363
|
+
const lines = countLines(fieldBlock);
|
|
364
|
+
console.log(`Text will occupy ${lines} lines`);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Validating JSON Configuration
|
|
368
|
+
|
|
369
|
+
When loading badge configurations from JSON files or APIs:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import { validateBadgeOrThrow, generate } from 'cxm-zpl-generator';
|
|
373
|
+
|
|
374
|
+
// Load from JSON file or API
|
|
375
|
+
const jsonData = await fetch('/api/badge-config').then(r => r.json());
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
const badge = validateBadgeOrThrow(jsonData);
|
|
379
|
+
const zpl = generate(badge);
|
|
380
|
+
// Send to printer
|
|
381
|
+
} catch (error) {
|
|
382
|
+
if (error instanceof ZodError) {
|
|
383
|
+
console.error('Validation errors:', error.errors);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## Examples
|
|
389
|
+
|
|
390
|
+
See the `examples/` directory for complete working examples:
|
|
391
|
+
|
|
392
|
+
- `simple-label.ts` - Basic label with text fields and barcode
|
|
393
|
+
|
|
394
|
+
## Testing
|
|
395
|
+
|
|
396
|
+
Run tests with:
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
npm test
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Watch mode:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
npm run test:watch
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## Building
|
|
409
|
+
|
|
410
|
+
Build the library:
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
npm run build
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## License
|
|
417
|
+
|
|
418
|
+
ISC
|
|
419
|
+
|
|
420
|
+
## Contributing
|
|
421
|
+
|
|
422
|
+
Contributions are welcome! Please ensure all tests pass and follow the existing code style.
|
package/dist/index.d.ts
ADDED