├── .gitignore ├── LICENSE ├── README.md ├── gulpfile.js ├── package.json └── src ├── commands ├── composite.js └── zpl.js ├── constants.js ├── label.js ├── print.js ├── printers.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | 3 | /node_modules 4 | /.idea/workspace.xml 5 | /.idea/tasks.xml 6 | *-compiled.js 7 | *-compiled.js.map 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Erik Anderson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZPL-JS 2 | 3 | [![Join the chat at https://gitter.im/ebpa/zpl-js](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ebpa/zpl-js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | ZPL-JS is a ZPL / ZPL II JavaScript library for printing to Zebra printers. It's goal is to make printing to Zebra printers as easy as possible with JavaScript while still leveraging the power of ZPL. 6 | 7 | **Note: This a rough early release. The CLI is probably not usable for you without modification. For the impatient: PR's welcome ;-**) 8 | 9 | var zpl = require('zpl'); 10 | zpl.print() 11 | 12 | ## Installation 13 | 14 | To install from github: 15 | 16 | $ git clone https://github.com/ebpa/zpl-js 17 | $ cd zpl-js 18 | $ npm install 19 | 20 | 24 | 25 | ## API Reference 26 | 27 | TODO: Explain dual API 28 | 29 | ## CLI Interface 30 | 31 | It's also possible to print directly from the command line via: 32 | 33 | $ babel-node print.js 34 | 35 | ## Tests 36 | 37 | To run the unit tests 38 | 39 | $ npm test 40 | 41 | ## Contributors 42 | 43 | You can help make this library better and more useful by: 44 | 45 | * Submit bugs via the [issue tracker](https://github.com/ebpa/zpl-js/issues) here on Github 46 | * [Add printers](https://github.com/ebpa/zpl-js/wiki/Adding-Printers) 47 | 48 | ## Getting Help 49 | 50 | Find me on gitter at gitter.im/zpl-js 51 | 52 | ## License 53 | 54 | ZPL-JS is freely distributable under the terms of the [MIT license](https://github.com/ebpa/zpl-js/blob/develop/LICENSE). 55 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var babel = require("gulp-babel"); 3 | var shell = require('gulp-shell'); 4 | var exec = require('gulp-exec'); 5 | 6 | gulp.task("default", function () { 7 | return gulp.src("src/*.js") 8 | .pipe(babel()) 9 | .pipe(gulp.dest("dist")); 10 | }); 11 | 12 | gulp.task('print', ['default'], shell.task('node dist/print.js')); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zpl", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "commander": "^2.8.1", 6 | "gulp-babel": "^5.1.0", 7 | "loglevel": "^1.4.0", 8 | "moment": "^2.10.6", 9 | "telnet-client": "^0.1.0" 10 | }, 11 | "devDependencies": { 12 | "babel-cli": "^6.1.18", 13 | "babel-core": "^6.1.21", 14 | "babel-preset-es2015": "^6.1.18", 15 | "gulp": "^3.9.0", 16 | "gulp-exec": "^2.1.1", 17 | "gulp-shell": "^0.4.2" 18 | }, 19 | "description": "ZPL-JS is a ZPL / ZPL II JavaScript library for printing to Zebra printers. It's goal is to make printing to Zebra printers as easy as possible with JavaScript while still leveraging the power of ZPL.", 20 | "main": "src/print.js", 21 | "scripts": { 22 | "test": "mocha" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+ssh://git@github.com/ebpa/zpl-js.git" 27 | }, 28 | "author": "Erik Anderson", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/ebpa/zpl-js/issues" 32 | }, 33 | "homepage": "https://github.com/ebpa/zpl-js#readme" 34 | } 35 | -------------------------------------------------------------------------------- /src/commands/composite.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | command: 'qr', 4 | parameters: ['text', 'errorCorrectionLevel', 'model', 'magnificationFactor', 'd', 'e', 'inputMode', 'x', 'y'], 5 | description: '2-dimensional barcode', 6 | fn: function ({text = null, errorCorrectionLevel = null, model = null, magnificationFactor = 5 /* default for 300dpi printers */, d = null, e = null, inputMode = 'A', x = dots(1.4), y = null}) { 7 | if (text === null) return; 8 | 9 | // TODO Warn for invalid option values 10 | /* field position? */ 11 | 12 | //if (!(model === 1 || model === 2)) 13 | // invalidParameterError('model', model); 14 | 15 | if (!(magnificationFactor >= 1 && magnificationFactor <= 10)) 16 | invalidParameterError('magnificationFactor', magnificationFactor); 17 | 18 | //if (!(d === 'H' || d === 'Q' || d === 'M' || d === 'L')) 19 | // invalidParameterError('d', d); 20 | // 21 | //if (!(e >= 1, e <= 7)) 22 | // invalidParameterError('e', e); 23 | 24 | return `^BQN,2,${magnificationFactor}\n` + 25 | '^FH\\' + 26 | '^FD' + inputMode + ',' + text + '^FS\n'; 27 | } 28 | } 29 | ]; -------------------------------------------------------------------------------- /src/commands/zpl.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | command: '^A', 4 | alias: 'setFont', 5 | parameters: ['font','height','width'], 6 | description: 'Set font for current field', 7 | fn: (fontName, fieldOrientation, characterHeight, width) => `^A${fontName}${fieldOrientation},${characterHeight},${width}` 8 | // f /[A-Z0-9]/ 9 | // o /[NRIB]/ 10 | // 10 <= h <= 32000 11 | // 10 <= w <= 32000 12 | }, 13 | { 14 | command: '^A@', 15 | parameters: ['orientation, height, width, path'], 16 | description: 'Use font name to call font' 17 | }, 18 | { 19 | command: '^B1', 20 | parameters: ['orientation, checkDigit, height, intLine, intLineAbove'], 21 | description: 'Code 11 bar code' 22 | }, 23 | { 24 | command: '^B2', 25 | parameters: 'orientation, height, intLine, intLineAbove, checkDigit', 26 | description: 'Interleaved 2 of 5 bar code' 27 | }, 28 | { 29 | command: '^B3', 30 | parameters: 'orientation, checkDigit, height, intLine, intLineAbove', 31 | description: 'Code 39 bar code' 32 | }, 33 | { 34 | command: '^B4', 35 | parameters: 'orientation, heightMultiplier, intLine, mode', 36 | description: 'Code 49 bar code' 37 | }, 38 | { 39 | command: '^B5', 40 | parameters: 'orientation, height, intLine, intLineAbove', 41 | description: 'Planet Code bar code' 42 | }, 43 | { 44 | command: '^B7', 45 | parameters: 'orientation, rowHeight, security, columns, rows, truncate', 46 | description: 'PDF417 bar code' 47 | }, 48 | { 49 | command: '^B8', 50 | parameters: 'orientation, height, intLine, intLineAbove', 51 | description: 'EAN-8 bar code' 52 | }, 53 | { 54 | command: '^B9', 55 | parameters: 'orientation, height, intLine, intLineAbove, checkDigit', 56 | description: 'UPC-E bar code' 57 | }, 58 | { 59 | command: '^BA', 60 | parameters: 'orientation, height, intLine, intLineAbove, checkDigit', 61 | description: 'Code 93 bar code' 62 | }, 63 | { 64 | command: '^BB', 65 | parameters: 'orientation, height, security, columns, rows, mode', 66 | description: 'Codablock bar code' 67 | }, 68 | { 69 | command: '^BC', 70 | parameters: 'orientation, height, intLine, intLineAbove, checkDigit, mode', 71 | description: 'Code 128 bar code (modes N, U and A only); UCC check digit not supported)' 72 | }, 73 | { 74 | command: '^BD', 75 | parameters: 'mode, position, total', 76 | description: 'UPS MaxiCode bar code' 77 | }, 78 | { 79 | command: '^BE', 80 | parameters: 'orientation, height, intLine, intLineAbove', 81 | description: 'EAN-13 bar code' 82 | }, 83 | { 84 | command: '^BF', 85 | parameters: 'orientation, height, mode', 86 | description: 'Micro-PDF417 bar code' 87 | }, 88 | { 89 | command: '^BI', 90 | parameters: 'orientation, height, intLine, intLineAbove', 91 | description: 'Industrial 2 of 5 bar code' 92 | }, 93 | { 94 | command: '^BJ', 95 | parameters: 'orientation, height, intLine, intLineAbove', 96 | description: 'Standard 2 of 5 bar code' 97 | }, 98 | { 99 | command: '^BK', 100 | parameters: 'orientation, checkDigit, height, intLine, intLineAbove, startChar, stopChar', 101 | description: 'ANSI Codabar bar code' 102 | }, 103 | { 104 | command: '^BL', 105 | parameters: 'orientation, height, intLineAbove', 106 | description: 'LOGMARS bar code' 107 | }, 108 | { 109 | command: '^BM', 110 | parameters: 'orientation, checkDigitType, height, intLine, intLineAbove, checkDigit', 111 | description: 'MSI bar code' 112 | }, 113 | { 114 | command: '^BO', 115 | parameters: 'orientation, magnification, ecic, err, menuSymbol, symbols, id', 116 | description: 'Aztec bar code' 117 | }, 118 | { 119 | command: '^BP', 120 | parameters: 'orientation, checkDigit, height, intLine, intLineAbove', 121 | description: 'Plessey bar code' 122 | }, 123 | { 124 | command: '^BQ', 125 | parameters: 'position, model, magnification, hqml, nabk', 126 | description: 'QR code bar code' 127 | }, 128 | { 129 | command: '^BR', 130 | parameters: 'orientation, symbology, magnification, separatorHeight, height, segmentWidth', 131 | description: 'RSS bar code' 132 | }, 133 | { 134 | command: '^BS', 135 | parameters: 'orientation, height, intLine, intLineAbove', 136 | description: 'UPC/EAN extension' 137 | }, 138 | { 139 | command: '^BT', 140 | parameters: 'orientation, width, widthRatio, height, narrowWidth, rowHeight', 141 | description: 'TLC39 bar code' 142 | }, 143 | { 144 | command: '^BU', 145 | parameters: 'orientation, height, intLine, intLineAbove, printCheckDigit', 146 | description: 'UPC-A bar code' 147 | }, 148 | { 149 | command: '^BX', 150 | parameters: 'orientation, height, quality, columns, rows, format, escape', 151 | description: 'Data matrix bar code (quality 200 only)' 152 | }, 153 | { 154 | command: '^BY', 155 | parameters: 'width, widthRatio, height', 156 | description: 'Bar code field defaults' 157 | }, 158 | { 159 | command: '^BZ', 160 | parameters: 'orientation, height, intLine, intLineAbove, type', 161 | description: 'Postal bar code' 162 | }, 163 | { 164 | command: ['^CC', '~CC'], 165 | parameters: 'char', 166 | description: 'Change caret' 167 | }, 168 | { 169 | command: ['^CD', '~CD'], 170 | parameters: 'char', 171 | description: 'Change delimiter' 172 | }, 173 | { 174 | command: '^CF', 175 | parameters: 'fontName, height, width', 176 | description: 'Change default font' 177 | }, 178 | { 179 | command: '^CI', 180 | parameters: 'charset, src1, dest1, src2, dest2, ...', 181 | description: 'Change international font (charsets 0-13, 27, 28, 31, 33-36 only)' 182 | }, 183 | { 184 | command: '^CM', 185 | parameters: 'memoryDevice, memoryDevice, memoryDevice, memoryDevice', 186 | description: 'Change memory letter designation' 187 | }, 188 | { 189 | command: '^CO', 190 | parameters: 'on, memory, type', 191 | description: 'Cache on' 192 | }, 193 | { 194 | command: '^CT', 195 | parameters: 'char', 196 | description: 'Change tilde (alternate command:�~CT)' 197 | }, 198 | { 199 | command: '^CV', 200 | parameters: 'validation', 201 | description: 'Code validation' 202 | }, 203 | { 204 | command: '^CW', 205 | parameters: 'fontName, path', 206 | description: 'Set font identifier' 207 | }, 208 | { 209 | command: '~DB', 210 | parameters: 'path, orientation, maxHeight, maxWidth, base, space, chars, copyright, data', 211 | description: 'Download bitmap font' 212 | }, 213 | { 214 | command: '~DE', 215 | parameters: 'path, tableSize, data', 216 | description: 'Download encoding' 217 | }, 218 | { 219 | command: '^DF', 220 | parameters: 'path', 221 | description: 'Download format' 222 | }, 223 | { 224 | command: '~DG', 225 | parameters: 'path, totalBytes, rowBytes, data', 226 | description: 'Download graphics' 227 | }, 228 | { 229 | command: '~DN', 230 | parameters:'', 231 | description: 'Abort download graphics' 232 | }, 233 | { 234 | command: '~DS', 235 | parameters: 'path, size, data', 236 | description: 'Download scalable font' 237 | }, 238 | { 239 | command: '~DT', 240 | parameters: 'path, size, data', 241 | description: 'Download TrueType font' 242 | }, 243 | { 244 | command: '~DU', 245 | parameters: 'path, size, data', 246 | description: 'Download unbounded TrueType font' 247 | }, 248 | { 249 | command: '~DY', 250 | parameters: 'path, format, extension, totalBytes, rowBytes, data', 251 | description: 'Download objects (extensions G, B, P and T only)' 252 | }, 253 | { 254 | command: '~EG', 255 | parameters:'', 256 | description: 'Erase all graphics (alternate command:�^EG)' 257 | }, 258 | { 259 | command: '^FB', 260 | alias: 'fieldBlock', 261 | parameters: 'maxWidth, maxLines, lineSpacing, alignment, hangingIndent', 262 | description: 'Field block', 263 | fn: (a,b,c,d,e) => `^FB${a},${b},${c},${d},${e}` 264 | // 0 <= a <= [width of label] 265 | // 1 <= b <= 9999 266 | // -9999 <= c <= 9999 267 | // d in ['L','C','R','J'] 268 | // 0 <= e <= 9999 269 | }, 270 | { 271 | command: '^FC', 272 | parameters: 'indicator1, indicator2, indicator3', 273 | description: 'Field clock' 274 | }, 275 | { 276 | command: '^FD', 277 | alias: 'fieldData', 278 | parameters: 'data', 279 | description: 'Field data', 280 | fn: (data) => `^FD${data}^FS` 281 | }, 282 | { 283 | command: '^FH', 284 | parameters: 'hexIndicator', 285 | description: 'Field hexadecimaml indicator' 286 | }, 287 | { 288 | command: '^FL', 289 | parameters: 'extensionPath, basePath, link', 290 | description: 'Font link' 291 | }, 292 | { 293 | command: '^FM', 294 | parameters: 'x1, y2, x2, y2, ...', 295 | description: 'Field multiple origin locations' 296 | }, 297 | { 298 | command: '^FN', 299 | parameters: 'fieldNumber', 300 | description: 'Field number' 301 | }, 302 | { 303 | command: '^FO', 304 | alias: 'fieldOrigin', 305 | parameters: 'x, y', 306 | description: 'Field origin', 307 | fn: (x,y,z) => `^FO${x},${y},${z}` 308 | // 0 <= x <= 32000 309 | // 0 <= y <= 32000 310 | // z in [0, 1, 2] 311 | }, 312 | { 313 | command: '^FP', 314 | parameters: 'direction, characterSpacing', 315 | description: 'Field parameter' 316 | }, 317 | { 318 | command: '^FR', 319 | parameters:'', 320 | description: 'Field reverse print' 321 | }, 322 | { 323 | command: '^FS', 324 | parameters:'', 325 | description: 'Field separator (alternate command:�0x0F)' 326 | }, 327 | { 328 | command: '^FT', 329 | parameters: 'x, y', 330 | description: 'Field typeset' 331 | }, 332 | { 333 | command: '^FV', 334 | parameters: 'data', 335 | description: 'Field variable' 336 | }, 337 | { 338 | command: '^FW', 339 | parameters: 'orientation', 340 | description: 'Field orientation' 341 | }, 342 | { 343 | command: '^FX', 344 | parameters: 'comment', 345 | description: 'Comment' 346 | }, 347 | { 348 | command: '^GB', 349 | parameters: 'width, height, thickness, color, rounding', 350 | description: 'Graphic box' 351 | }, 352 | { 353 | command: '^GC', 354 | parameters: 'diameter, thickness, color', 355 | description: 'Graphic circle' 356 | }, 357 | { 358 | command: '^GD', 359 | parameters: 'width, height, thickness, color, orientation', 360 | description: 'Graphic diagonal line' 361 | }, 362 | { 363 | command: '^GE', 364 | parameters: 'width, height, thickness, color', 365 | description: 'Graphic ellipse' 366 | }, 367 | { 368 | command: '^GF', 369 | parameters: 'format, dataBytes, totalBytes, rowBytes, data', 370 | description: 'Graphic field' 371 | }, 372 | { 373 | command: '^GS', 374 | parameters: 'orientation, height, width', 375 | description: 'Graphic symbol' 376 | }, 377 | { 378 | command: '~HB', 379 | parameters:'', 380 | description: 'Battery status' 381 | }, 382 | { 383 | command: '~HD', 384 | parameters:'', 385 | description: 'Head diagnostic' 386 | }, 387 | { 388 | command: '^HF', 389 | parameters: 'path', 390 | description: 'Host format' 391 | }, 392 | { 393 | command: '^HG', 394 | parameters: 'path', 395 | description: 'Host graphic' 396 | }, 397 | { 398 | command: '^HH', 399 | parameters:'', 400 | description: 'Configuration label return' 401 | }, 402 | { 403 | command: '~HI', 404 | parameters:'', 405 | description: 'Host identification' 406 | }, 407 | { 408 | command: '~HM', 409 | parameters:'', 410 | description: 'Host RAM status' 411 | }, 412 | { 413 | command: '~HS', 414 | parameters:'', 415 | description: 'Host status return' 416 | }, 417 | { 418 | command: '~HU', 419 | parameters:'', 420 | description: 'Host alert configuration' 421 | }, 422 | { 423 | command: '^HV', 424 | parameters:'', 425 | description: 'Host verification' 426 | }, 427 | { 428 | command: '^HW', 429 | parameters: 'path', 430 | description: 'Host directory list' 431 | }, 432 | { 433 | command: '^HY', 434 | parameters: 'path', 435 | description: 'Upload graphics' 436 | }, 437 | { 438 | command: '^HZ', 439 | parameters: 'param', 440 | description: 'Display description information' 441 | }, 442 | { 443 | command: '^ID', 444 | parameters: 'path', 445 | description: 'Delete object' 446 | }, 447 | { 448 | command: '^IL', 449 | parameters: 'path', 450 | description: 'Image load' 451 | }, 452 | { 453 | command: '^IM', 454 | parameters: 'path', 455 | description: 'Image move' 456 | }, 457 | { 458 | command: '^IS', 459 | parameters: 'path, print', 460 | description: 'Image save' 461 | }, 462 | { 463 | command: '~JA', 464 | parameters:'', 465 | description: 'Cancel all' 466 | }, 467 | { 468 | command: '^JB', 469 | parameters: 'device', 470 | description: 'Initialize flash memory' 471 | }, 472 | { 473 | command: '~JB', 474 | parameters:'', 475 | description: 'Reset optional memory' 476 | }, 477 | { 478 | command: '~JC', 479 | parameters:'', 480 | description: 'Set media sensor calibration' 481 | }, 482 | { 483 | command: '~JD', 484 | parameters:'', 485 | description: 'Enable communications diagnostics' 486 | }, 487 | { 488 | command: '~JE', 489 | parameters:'', 490 | description: 'Disable communications diagnostics' 491 | }, 492 | { 493 | command: '~JF', 494 | parameters: 'pause', 495 | description: 'Set battery condition' 496 | }, 497 | { 498 | command: '~JG', 499 | parameters:'', 500 | description: 'Graphing sensor calibration' 501 | }, 502 | { 503 | command: '^JJ', 504 | parameters: 'opMode, appMode, printSignalMode, errorMode, reprintMode, ribbonMode', 505 | description: 'Set auxiliary port' 506 | }, 507 | { 508 | command: '~JL', 509 | parameters:'', 510 | description: 'Set label length' 511 | }, 512 | { 513 | command: '^JM', 514 | parameters: 'adjustment', 515 | description: 'Set print density' 516 | }, 517 | { 518 | command: '~JN', 519 | parameters:'', 520 | description: 'Head test fatal' 521 | }, 522 | { 523 | command: '~JO', 524 | parameters:'', 525 | description: 'Head test not fatal' 526 | }, 527 | { 528 | command: '~JP', 529 | parameters:'', 530 | description: 'Pause and cancel format' 531 | }, 532 | { 533 | command: '~JR', 534 | parameters:'', 535 | description: 'Power on reset' 536 | }, 537 | { 538 | command: '^JS', 539 | parameters: 'sensor', 540 | description: 'Sensor select' 541 | }, 542 | { 543 | command: '~JS', 544 | parameters: 'sequence', 545 | description: 'Change backfeed sequence' 546 | }, 547 | { 548 | command: '^JT', 549 | parameters: 'labels, manualSelection, first, last', 550 | description: 'Head test interval' 551 | }, 552 | { 553 | command: '^JU', 554 | parameters: 'configuration', 555 | description: 'Configuration update' 556 | }, 557 | { 558 | command: '^JW', 559 | parameters: 'tension', 560 | description: 'Set ribbon tension' 561 | }, 562 | { 563 | command: '~JX', 564 | parameters:'', 565 | description: 'Cancel current format' 566 | }, 567 | { 568 | command: '^JZ', 569 | parameters: 'reprint', 570 | description: 'Reprint after error' 571 | }, 572 | { 573 | command: '~KB', 574 | parameters:'', 575 | description: 'Kill battery' 576 | }, 577 | { 578 | command: '^KD', 579 | parameters: 'format', 580 | description: 'Select date and time format' 581 | }, 582 | { 583 | command: '^KL', 584 | parameters: 'language', 585 | description: 'Select language' 586 | }, 587 | { 588 | command: '^KN', 589 | parameters: 'name, description', 590 | description: 'Set printer name' 591 | }, 592 | { 593 | command: '^KP', 594 | parameters: 'password', 595 | description: 'Set password' 596 | }, 597 | { 598 | command: '^LF', 599 | parameters:'', 600 | description: 'List font links' 601 | }, 602 | { 603 | command: '^LH', 604 | alias: 'labelHome', 605 | parameters: 'x, y', 606 | description: 'Label home', 607 | fn: (x, y) => `^LH${x},${y}` 608 | // 0 < x < 32000 609 | // 0 < y < 32000 610 | }, 611 | { 612 | command: '^LL', 613 | parameters: 'length', 614 | description: 'Label length' 615 | }, 616 | { 617 | command: '^LR', 618 | parameters: 'reverse', 619 | description: 'Label reverse print' 620 | }, 621 | { 622 | command: '^LS', 623 | parameters: 'shift', 624 | description: 'Label shift' 625 | }, 626 | { 627 | command: '^LT', 628 | parameters: 'top', 629 | description: 'Label top' 630 | }, 631 | { 632 | command: '^MC', 633 | parameters: 'clear', 634 | description: 'Map clear' 635 | }, 636 | { 637 | command: '^MD', 638 | parameters: 'darknessModifier', 639 | description: 'Modify darkness' 640 | }, 641 | { 642 | command: '^MF', 643 | parameters: 'powerupAction, closingAction', 644 | description: 'Media feed' 645 | }, 646 | { 647 | command: '^ML', 648 | parameters: 'maxLength, maxLogicalPaper, maxPhysicalPaper, maxRibbon', 649 | description: 'Max label length' 650 | }, 651 | { 652 | command: '^MM', 653 | parameters: 'mode, prepeel', 654 | description: 'Print mode' 655 | }, 656 | { 657 | command: '^MN', 658 | parameters: 'media', 659 | description: 'Media tracking' 660 | }, 661 | { 662 | command: '^MP', 663 | parameters: 'mode', 664 | description: 'Mode protection' 665 | }, 666 | { 667 | command: '^MT', 668 | parameters: 'mediaType', 669 | description: 'Media type' 670 | }, 671 | { 672 | command: '^MU', 673 | parameters: 'units, baseDpi, dpiConversion', 674 | description: 'Set units of measurement' 675 | }, 676 | { 677 | command: '^MW', 678 | parameters: 'enable', 679 | description: 'Modify head cold warning' 680 | }, 681 | { 682 | command: '~NC', 683 | parameters: 'networkId', 684 | description: 'Network connect' 685 | }, 686 | { 687 | command: '^NI', 688 | parameters: 'networkId', 689 | description: 'Network ID number' 690 | }, 691 | { 692 | command: '~NR', 693 | parameters:'', 694 | description: 'Set all network printers transparent' 695 | }, 696 | { 697 | command: '^NS', 698 | parameters: 'setting, ip, subnetMask, gateway', 699 | description: 'Change network settings' 700 | }, 701 | { 702 | command: '~NT', 703 | parameters:'', 704 | description: 'Set current printer transparent' 705 | }, 706 | { 707 | command: '^PA', 708 | parameters: 'defaultGlyph, bidi, charShaping, openTypeSupport', 709 | description: 'Advanced text properties' 710 | }, 711 | { 712 | command: '^PF', 713 | parameters: 'rows', 714 | description: 'Slew rows' 715 | }, 716 | { 717 | command: '^PH', 718 | parameters:'', 719 | description: 'Slew to home position (alternate command:�~PH)' 720 | }, 721 | { 722 | command: '^PM', 723 | parameters: 'mirror', 724 | description: 'Print mirror image' 725 | }, 726 | { 727 | command: '^PO', 728 | parameters: 'orientation', 729 | description: 'Print orientation' 730 | }, 731 | { 732 | command: '^PP', 733 | parameters:'', 734 | description: 'Programmable pause (alternate command:�~PP)' 735 | }, 736 | { 737 | command: '^PQ', 738 | parameters: 'labels, labelsBetweenPauses, replicates, noPause, cutOnError', 739 | description: 'Print quantity' 740 | }, 741 | { 742 | command: '^PR', 743 | parameters: 'printSpeed, slewSpeed, backfeedSpeed', 744 | description: 'Print rate' 745 | }, 746 | { 747 | command: '~PR', 748 | parameters:'', 749 | description: 'Applicator reprint' 750 | }, 751 | { 752 | command: '~PS', 753 | parameters:'', 754 | description: 'Print start' 755 | }, 756 | { 757 | command: '^PW', 758 | parameters: 'width', 759 | description: 'Print width' 760 | }, 761 | { 762 | command: '~RO', 763 | parameters: 'counter', 764 | description: 'Reset counter' 765 | }, 766 | { 767 | command: '^SC', 768 | parameters: 'baud, wordLength, parity, stopBits, protocolMode, protocol', 769 | description: 'Set serial communications' 770 | }, 771 | { 772 | command: '~SD', 773 | parameters: 'darkness', 774 | description: 'Set darkness' 775 | }, 776 | { 777 | command: '^SE', 778 | parameters: 'path', 779 | description: 'Select encoding' 780 | }, 781 | { 782 | command: '^SF', 783 | parameters: 'mask, increment', 784 | description: 'Serialized field' 785 | }, 786 | { 787 | command: '^SL', 788 | parameters: 'mode, language', 789 | description: 'Set RTC mode and language' 790 | }, 791 | { 792 | command: '^SN', 793 | parameters: 'start, increment, pad', 794 | description: 'Serialized data' 795 | }, 796 | { 797 | command: '^SO', 798 | parameters: 'clock, months, days, years, hours, minutes, seconds', 799 | description: 'Set RTC offset' 800 | }, 801 | { 802 | command: '^SP', 803 | parameters: 'row', 804 | description: 'Start print' 805 | }, 806 | { 807 | command: '^SQ', 808 | parameters: 'condition, destination, halt', 809 | description: 'Halt alert' 810 | }, 811 | { 812 | command: '^SR', 813 | parameters: 'resistance', 814 | description: 'Set printhead resistance' 815 | }, 816 | { 817 | command: '^SS', 818 | parameters: 'web, media, ribbon, length, intensity1, intensity2, sensing, mediaSensing, ledSensing', 819 | description: 'Set media sensors' 820 | }, 821 | { 822 | command: '^ST', 823 | parameters: 'month, day, year, hour, minute, second, format', 824 | description: 'Set RTC date and time' 825 | }, 826 | { 827 | command: '^SX', 828 | parameters: 'condition, destination, set, clear, setting, port', 829 | description: 'Set alert' 830 | }, 831 | { 832 | command: '^SZ', 833 | parameters: 'version', 834 | description: 'Set ZPL version' 835 | }, 836 | { 837 | command: '~TA', 838 | parameters: 'change', 839 | description: 'Tear-off adjust position' 840 | }, 841 | { 842 | command: '^TB', 843 | parameters: 'orientation, maxWidth, maxHeight', 844 | description: 'Text block' 845 | }, 846 | { 847 | command: '^TO', 848 | parameters: 'from, to', 849 | description: 'Transfer object' 850 | }, 851 | { 852 | command: '~WC', 853 | parameters:'', 854 | description: 'Print configuration label' 855 | }, 856 | { 857 | command: '^WD', 858 | parameters: 'path', 859 | description: 'Print directory label' 860 | }, 861 | { 862 | command: '^XA', 863 | alias: ['startFormat','start'], 864 | parameters:'', 865 | description: 'Start format (alternate command:�0x02)', 866 | fn: () => '^XA' 867 | }, 868 | { 869 | command: '^XB', 870 | parameters:'', 871 | description: 'Supress backfeed' 872 | }, 873 | { 874 | command: '^XF', 875 | parameters: 'path', 876 | description: 'Recall format' 877 | }, 878 | { 879 | command: '^XG', 880 | parameters: 'path, magnificationX, magnificationY', 881 | description: 'Recall graphics' 882 | }, 883 | { 884 | command: '^XZ', 885 | alias: ['endFormat','end'], 886 | parameters: '', 887 | description: 'End format (alternate command:�0x03)', 888 | fn: () => '^XZ' 889 | }, 890 | { 891 | command: '^ZZ', 892 | parameters: 'idle, status', 893 | description: 'Printer sleep' 894 | } 895 | ]; 896 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | const PRINT_METHODS = { 2 | DIRECT_THERMAL: 1, 3 | THERMAL_TRANSFER: 2 4 | }; 5 | 6 | const SYMBOLOGY = { 7 | LINEAR_CODABAR : { 8 | name: 'Linear Codabar', 9 | description: '', 10 | toString: () => 1 11 | }, 12 | CODE_11 : { 13 | name: 'Code 11', 14 | description: '', 15 | toString: () => 2 16 | }, 17 | CODE_128 : { 18 | name: 'Code 128', 19 | description: '', 20 | toString: () => 3 21 | }, 22 | CODE_128_WITH_ABC : { 23 | name: 'Code 128 with subsets A/B/C', 24 | description: '', 25 | toString: () => 4 26 | }, 27 | CODE_39 : { 28 | name: 'Code 39', 29 | description: '', 30 | toString: () => 5 31 | }, 32 | CODE_93 : { 33 | name: 'Code 93', 34 | description: '', 35 | toString: () => 6 36 | }, 37 | EAN_13 : { 38 | name: 'EAN-13', 39 | description: '', 40 | toString: () => 7 41 | }, 42 | EAN_14 : { 43 | name: 'EAN-14', 44 | description: '', 45 | toString: () => 8 46 | }, 47 | EAN_8 : { 48 | name: 'EAN-8', 49 | description: '', 50 | toString: () => 9 51 | }, 52 | EAN_8_AND_EAN_13_W2OR5 : { 53 | name: 'EAN-8 and EAN-13 with 2 or 5 digit extensions', 54 | description: '', 55 | toString: () => 10 56 | }, 57 | GERMAN_POST_CODE : { 58 | name: 'German Post Code', 59 | description: '', 60 | toString: () => 11 61 | }, 62 | INDUSTRIAL_2OF5 : { 63 | name: 'Industrial 2-of-5', 64 | description: '', 65 | toString: () => 12 66 | }, 67 | INTERLEAVED_2OF5 : { 68 | name: 'Interleaved 2-of-5', 69 | description: '', 70 | toString: () => 13 71 | }, 72 | JAPANESE_POSTNET : { 73 | name: 'Japanese Postnet', 74 | description: '', 75 | toString: () => 14 76 | }, 77 | LOGMARS : { 78 | name: 'Logmars', 79 | description: '', 80 | toString: () => 15 81 | }, 82 | MSI : { 83 | name: 'MSI', 84 | description: '', 85 | toString: () => 16 86 | }, 87 | MSI_3 : { 88 | name: 'MSI-3', 89 | description: '', 90 | toString: () => 17 91 | }, 92 | PLESSEY : { 93 | name: 'Plessey', 94 | description: '', 95 | toString: () => 18 96 | }, 97 | POSTNET : { 98 | name: 'Postnet', 99 | description: '', 100 | toString: () => 19 101 | }, 102 | RSS : { 103 | name: 'RSS (reduced space symbology)', 104 | description: '', 105 | toString: () => 20 106 | }, 107 | STANDARD_2OF5 : { 108 | name: 'Standard 2-of-5', 109 | description: '', 110 | toString: () => 21 111 | }, 112 | UCC_EAN_128 : { 113 | name: 'UCC/EAN-128', 114 | description: '', 115 | toString: () => 22 116 | }, 117 | UPC_EAN_2OR5 : { 118 | name: 'UPC and EAN 2 or 5 digit extensions', 119 | description: '', 120 | toString: () => 23 121 | }, 122 | UPC_A : { 123 | name: 'UPC-A', 124 | description: '', 125 | toString: () => 24 126 | }, 127 | UPC_A_UPC_E_2OR5 : { 128 | name: 'UPC-A and UPC-E with 2 or 5 digit extensions', 129 | description: '', 130 | toString: () => 25 131 | }, 132 | UPC_A_UPC_E_EAN_2OR5 : { 133 | name: 'UPC-A and UPC-E with EAN 2 or 5 digit extensions', 134 | description: '', 135 | toString: () => 26 136 | }, 137 | UPC_E : { 138 | name: 'UPC-E', 139 | description: '', 140 | toString: () => 27 141 | }, 142 | AZTEC_2D : { 143 | name: '2-dimensional Aztec', 144 | description: '', 145 | toString: () => 28 146 | }, 147 | CODABLOCK : { 148 | name: 'Codablock', 149 | description: '', 150 | toString: () => 29 151 | }, 152 | CODE_49 : { 153 | name: 'Code 49', 154 | description: '', 155 | toString: () => 30 156 | }, 157 | DATA_MATRIX : { 158 | name: 'Data Matrix', 159 | description: '', 160 | toString: () => 31 161 | }, 162 | MACROPDF417 : { 163 | name: 'MacroPDF417', 164 | description: '', 165 | toString: () => 32 166 | }, 167 | MAXICODE : { 168 | name: 'MaxiCode', 169 | description: '', 170 | toString: () => 33 171 | }, 172 | MICROPDF417 : { 173 | name: 'MicroPDF417', 174 | description: '', 175 | toString: () => 34 176 | }, 177 | PDF417 : { 178 | name: 'PDF417', 179 | description: '', 180 | toString: () => 35 181 | }, 182 | QR_CODE : { 183 | name: 'QR Code', 184 | description: '', 185 | toString: () => 36 186 | }, 187 | FONTS_GRAPHICS : { 188 | name: 'Fonts and Graphics', 189 | description: '', 190 | toString: () => 37 191 | } 192 | }; 193 | 194 | var MEDIA_TYPES = { 195 | BLACK_BAR : { 196 | name: 'black_bar', 197 | description: '', 198 | toString: () => 1 199 | }, 200 | BLACK_MARK : { 201 | name: 'black_mark', 202 | description: '', 203 | toString: () => 2 204 | }, 205 | CONTINUOUS : { 206 | name: 'continuous', 207 | description: '', 208 | toString: () => 3 209 | }, 210 | CONTINUOUS_RECEIPT : { 211 | name: 'continuous_receipt', 212 | description: '', 213 | toString: () => 4 214 | }, 215 | DIE_CUT : { 216 | name: 'die-cut', 217 | description: '', 218 | toString: () => 5 219 | }, 220 | FANFOLD : { 221 | name: 'fanfold', 222 | description: '', 223 | toString: () => 6 224 | }, 225 | GAP : { 226 | name: 'gap', 227 | description: '', 228 | toString: () => 7 229 | }, 230 | NOTCHED : { 231 | name: 'notched', 232 | description: '', 233 | toString: () => 8 234 | }, 235 | PERFORATED : { 236 | name: 'perforated', 237 | description: '', 238 | toString: () => 9 239 | }, 240 | RECEIPT : { 241 | name: 'receipt', 242 | description: '', 243 | toString: () => 10 244 | }, 245 | ROLL_FED : { 246 | name: 'roll-fed', 247 | description: '', 248 | toString: () => 11 249 | }, 250 | TAG : { 251 | name: 'tag', 252 | description: '', 253 | toString: () => 12 254 | }, 255 | TAG_STOCK : { 256 | name: 'tag_stock', 257 | description: '', 258 | toString: () => 13 259 | } 260 | }; 261 | 262 | module.exports = { 263 | PRINT_METHODS, 264 | SYMBOLOGY, 265 | MEDIA_TYPES}; -------------------------------------------------------------------------------- /src/label.js: -------------------------------------------------------------------------------- 1 | var commands = require('./commands/zpl.js') 2 | .concat(require('./commands/zpl.js')); 3 | 4 | function Label() { 5 | this.text = ""; 6 | this.startFormat(); 7 | return this; 8 | } 9 | 10 | Label.prototype = { 11 | raw: function(data) { this.text += data; return this; } 12 | }; 13 | 14 | commands.map(function(defn) { 15 | var command = function() { 16 | var args = new Array(defn.fn.length).map((val, idx) => arguments[idx] || ""); 17 | console.log('arguments: '+args.length); 18 | console.log('fn: '+defn.fn); 19 | this.text += defn.fn(...arguments); 20 | return this; 21 | }; 22 | Object.assign(command, defn); 23 | 24 | // Raw API 25 | if (command.command instanceof Array) 26 | command.command.map((c) => Label.prototype[c] = command); 27 | else 28 | Label.prototype[command.command] = command; 29 | 30 | // Friendly API 31 | if (typeof command.alias !== undefined) { 32 | if (command.alias instanceof Array) 33 | command.alias.map((c) => Label.prototype[c] = command); 34 | else 35 | Label.prototype[command.alias] = command; 36 | } 37 | }); 38 | 39 | module.exports = Label; 40 | -------------------------------------------------------------------------------- /src/print.js: -------------------------------------------------------------------------------- 1 | var program = require('commander'); 2 | var telnet = require('telnet-client'); 3 | var PRINTERS = require('./printers'); 4 | var moment = require('moment'); 5 | var log = require('loglevel'); 6 | var Label = require('./label'); 7 | var utils = require('./utils'); 8 | 9 | // TODO: save configuration via nconf or similar? 10 | // TODO: be able to refer to printers by name (to leverage storage of configuration) 11 | // TODO: throw warnings when attempting to set fonts outside allowed bounds (and rounding is taking place) 12 | // TODO: label template storage 13 | // TODO: command chaining 14 | // TODO: command wiring to label object 15 | // TODO: label may also contain width/height (for continuous media) 16 | 17 | program 18 | .version('0.0.1') 19 | .option('-c, --content ', 'label content as JSON') 20 | .option('-j, --json ', 'JSON description of printing parameters') 21 | .option('-v, --verbose', 'ouput useful information', (v, total) => (total === 0) ? 0 : total-1, 2) 22 | .option('-t, --test', 'print a test label') 23 | .option('-c, --check', 'dry-run; do not send label to printer') 24 | .parse(process.argv); 25 | 26 | console.log('loglevel: '+program.verbose); 27 | log.setLevel(program.verbose); 28 | 29 | var defaultPrinterModel = PRINTERS.ZEBRA_GX430T; 30 | var defaultPrinter = Object.assign({ 31 | name: 'Default Printer', 32 | address: '192.168.1.14' 33 | }, defaultPrinterModel, program.json && program.json.printer); 34 | var defaultMedia = { 35 | width: 2 * 25.4, 36 | length: 1 * 25.4, 37 | // [thermal type] 38 | // [thickness?] 39 | // [price/label?] 40 | // [spool size] 41 | // [supply quantity] 42 | // [model / reorder number?] 43 | }; 44 | 45 | var dots = (l) => utils.dots(l, defaultPrinter); 46 | 47 | var labels = { 48 | "test": { 49 | defaultContent: { 50 | title: 'Hello world!', 51 | properties: { 52 | "date": moment().format(), 53 | "owner": "" 54 | }, 55 | url: 'example.com/labels/HelloWorld' 56 | }, 57 | template: qrWithProperties 58 | } 59 | }; 60 | 61 | if (program.test) { 62 | log.info('Printing a test label...'); 63 | print(); 64 | } else if (program.json) { 65 | log.trace('Using provided JSON printing parameters'); 66 | log.trace(`Provided parameters: ${JSON.stringify(program.json)}`); 67 | 68 | print(JSON.parse(program.json)); 69 | } else { 70 | log.error('No label content provided'); 71 | } 72 | 73 | 74 | function print({printer = defaultPrinter, template = labels.test.template, content = labels.test.defaultContent, media = defaultMedia} = {}) { 75 | 76 | if (typeof template === "string" && template in labels) 77 | template = labels[template].template; 78 | 79 | log.info("Connecting to printer at " + printer.address + ':' + printer.port + "\n"); 80 | 81 | var telnetParams = { 82 | host: printer.address, 83 | port: printer.port, 84 | //username: 'admin', 85 | password: printer.password 86 | }; 87 | 88 | var label = template(content); 89 | var cmd = label.text; 90 | 91 | log.debug("Command: "+cmd); 92 | console.log('program keys: '+ JSON.stringify(Object.keys(program))); 93 | if (program.check) { 94 | log.info('dry-run; exiting...'); 95 | process.exit(0); 96 | } 97 | 98 | var connection = new telnet(); 99 | 100 | connection.on('connect', function(prompt) { 101 | connection.exec(cmd, {shellPrompt: ""}, function(response) { 102 | log.debug(response); 103 | }); 104 | }); 105 | 106 | connection.on('timeout', function() { 107 | log.error('socket timeout!'); 108 | connection.end(); 109 | }); 110 | 111 | connection.on('close', function() { 112 | log.info('connection closed'); 113 | }); 114 | 115 | connection.connect(telnetParams); 116 | } 117 | 118 | function invalidParameterError(name, arg) { 119 | log.error("Invalid value for for parameter '" + name + "'"); 120 | } 121 | 122 | function basicTemplate() { 123 | return new Label() 124 | .fieldOrigin(dots(0.1),0, 0) 125 | .setFont('D','N',dots(0.120),dots(0.067)) 126 | .fieldData("Test Label") 127 | .end(); 128 | } 129 | 130 | function qrWithProperties(label) { 131 | return new Label() 132 | .labelHome(dots(0.1),dots(0.1)) 133 | // Title 134 | .fieldOrigin(dots(0.1),0, 0) 135 | .setFont('D','N',dots(0.120),dots(0.067)) 136 | .fieldData(label.title) 137 | // QR Code 138 | .fieldOrigin(0,dots(0.15)) 139 | .qr({text: label.url}) 140 | // Properties 141 | .fieldOrigin(dots(0.6),dots(0.15)) 142 | .setFont('D','N',dots(0.07),dots(0.035)) 143 | .fieldBlock(dots(1.6),8) 144 | .fieldData(Object.keys(label.properties).map((key) => key + ": " + label.properties[key]).join('\\&')) 145 | // URI 146 | .fieldOrigin(dots(0.1),dots(0.85)) 147 | .setFont('0','N',22,18) 148 | .fieldData(label.url) 149 | .end() 150 | } 151 | -------------------------------------------------------------------------------- /src/printers.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebpa/zpl-js/65b9758ffe16da7534c1f1c504cc6fcb16e830f3/src/printers.js -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | // TODO: accept units (currently defaulting to inches) 2 | export function dots(l, printer = defaultPrinter) { 3 | return Math.round(l * printer.resolutionDPI); 4 | } 5 | --------------------------------------------------------------------------------