├── README.md ├── index.js ├── lib ├── commands.js └── printer.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | chn-escpos 2 | = 3 | 4 | ## 介绍 5 | 6 | chn-escpos是一个nodejs的window POS机打印程序,使用`printer`的C组件与打印机通信,使用ESC/POS命令控制打印机。该版本已停止维护。 7 | 8 | ### 更新日志 9 | 2016-04-25 1.1.4 修复[钱箱命令在部分打印机不能使用](https://github.com/baka397/chn-escpos/issues/1)的问题 10 | 2016-04-22 1.1.3 修复一个打印二维码的BUG 11 | 2016-04-18 1.1.1 修改setSize方法,并修复setSize(1)还原样式错误的问题.增加beep(打印机蜂鸣),qrcode(打印二维码)功能. 12 | 2016-03-28 1.1.0 增加开钱箱命令,额外增加一个sendCmd命令,与开钱箱命令配合(print命令会额外发送一个切刀指令和初始化指令).与1.0.*的命令兼容 13 | 14 | ## 安装 15 | 16 | ``` 17 | npm install chn-escpos --save 18 | ``` 19 | 20 | ## 测试 21 | 你可以使用chn-escpos自带的测试程序进行测试 22 | ``` 23 | cd node_modules/chn-escpos 24 | npm test 25 | ``` 26 | 测试程序默认监听2520端口,你可以通过向`http://127.0.0.1:2520?action=print&printer=`POST raw数据进行打印测试(推荐使用POSTMAN进行测试) 27 | ``` 28 | //发送打印指令 29 | POST HTTP/1.1 30 | Host: 127.0.0.1:2520?action=print 31 | Content-Type: application/javascript 32 | Cache-Control: no-cache 33 | Postman-Token: 3ae7055f-8dd9-5386-3cfe-4355d7b7645a 34 | 35 | [ 36 | { 37 | "id": "1",//打印任务ID 38 | "group_id": "1",//打印队列ID 39 | "printer": "XP-80C",//打印机别名 40 | "content": "<% setAlign:c %>测试居中" 41 | } 42 | ] 43 | 44 | //发送钱箱指令 45 | POST HTTP/1.1 46 | Host: 127.0.0.1:2520?action=cmd 47 | Content-Type: application/javascript 48 | Cache-Control: no-cache 49 | Postman-Token: d04979f1-9e17-3647-90be-defa7430109d 50 | 51 | [ 52 | { 53 | "id": "957", 54 | "group_id": "322", 55 | "printer": "XP-80C", 56 | "content": "<% openCashbox:'' %>" 57 | } 58 | ] 59 | ``` 60 | ## 使用方法 61 | ``` 62 | var printer=require('chn-escpos'), 63 | printer_name='XP-80C'; 64 | new printer(printer_name,function(err,msg){ 65 | //调用this方法进行打印 66 | }); 67 | ``` 68 | ### 打印方法 69 | ####text(text,[inline]) 打印文字内容 70 | string `text`:打印内容,单行数据,如果超出自动换行 71 | boolen `inline`:是否自动换行,如果为true,则不会自动换行 72 | ``` 73 | this.text('测试打印'); 74 | ``` 75 | 76 | ####line(number) 空行 77 | number `number`:空行数 78 | ``` 79 | this.line(2); 80 | ``` 81 | 82 | ####setAlign(align) 设置对齐 83 | string `align`:`C/L/R`分别代表居中/居左/居右,不区分大小写 84 | ``` 85 | this.setAlign('c').text('这个是居中文字'); 86 | ``` 87 | 88 | ###setLineheight(hex) 设置行高 89 | string `hex`:16进制数字,如'\x05' 90 | ``` 91 | this.setLineheight('\x05'); 92 | ``` 93 | 94 | ###setStyle(type) 设置样式 95 | string `type`:`B/U/U2/BU/BU2`分别代表加粗/下划线/下划线样式2/加粗+下划线/加粗+下划线样式2,不区分大小写 96 | ``` 97 | this.setStyle('b').text('加粗'); 98 | ``` 99 | 100 | ###setSize(size) 设置文字大小 101 | number `size`:4/3/2/1/null,x代表x倍字体,1/null均为正常 102 | ``` 103 | this.setSize(2).text('大字体'); 104 | ``` 105 | 106 | ###qrcode(text,size,lsb,msb) 打印二维码 107 | 需要打印机支持QRCODE条码类型,否则会打印乱码,只支持英文字符和URL特殊符号(:/?=&.) 108 | string `text`:二维码内容 109 | string `size`:二维码大小,默认'\x06' 110 | string `lsb`:(text长度+3)%256转16进制后的字符,如'\x01' 111 | string `msb`:(text长度+3)/256取整转16进制后的字符,如'\x00' 112 | ``` 113 | this.qrcode(1,'\x0f','\x04','\x00'); 114 | ``` 115 | 注:compile中16进制参数请使用\转义,如`<% qrcode:1,'\\x0f','\\x04','\\x00' %>` 116 | 117 | ###beep(times,interval) 蜂鸣警报 118 | 注意:该蜂鸣警报在某些打印机会引起打印错误 119 | 目前测试 120 | 通过的打印机:芯烨打印机(XP 80C),佳博打印机(GP L80160, GP H80300) 121 | 不通过的打印机:有爱普生(EPSON TM81II) 122 | string `times`:蜂鸣次数,16进制,1-9.默认'\x09' 123 | string `interval`:蜂鸣间隔,16进制,实际间隔时间为interval*50ms,默认'\x01' 124 | ``` 125 | this.beep(); 126 | ``` 127 | 注:compile中16进制参数请使用\转义,如`<% beep:'\\x03','\\x01' %>` 128 | 129 | ###compile(string) 编译 130 | string `string`:编译整个字符串 131 | 使用<% 方法名:[参数] %>进行快速设置`\n`或`\r`表示换行. 132 | ``` 133 | this.compile('<% setAlign:c %><% setSize:2 %>这里开始是放大\n<% setSize:1 %>恢复正常大小'); 134 | ``` 135 | 136 | ###print(callback) 打印当前内容 137 | function `callback`:回传err以及msg,当成功时,err为null 138 | ``` 139 | this.print(function(err,msg){ 140 | if(err){ 141 | console.log('打印出错,回传信息:'); 142 | } 143 | console.log(msg); 144 | }); 145 | ``` 146 | ###empty() 清空当前内容 147 | ``` 148 | this.empty(); 149 | ``` 150 | 151 | ###openCashbox() 发送钱箱脉冲 152 | 钱箱脉冲不能同打印命令一同发送(钱箱脉冲命令会执行但不会进行打印) 153 | ``` 154 | this.openCashbox(); 155 | ``` 156 | 157 | ###sendCmd(callback) 发送打印指令 158 | function `callback`:回传err以及msg,当成功时,err为null 159 | ``` 160 | this.sendCmd(function(err,msg){ 161 | if(err){ 162 | console.log('打印出错,回传信息:'); 163 | } 164 | console.log(msg); 165 | }); 166 | ``` 167 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 测试请求 3 | */ 4 | 5 | var url = require("url"); 6 | var querystring = require("querystring"); 7 | var printer = require('./lib/printer.js'); 8 | 9 | /** 10 | * 设置打印状态 11 | * @param {Function} callback 回调函数,当打印成功后执行该函数 12 | */ 13 | function setPrinterStatus(callback) { 14 | this.return_status = []; //返回状态 15 | this.return_list = []; //需返回的列表 16 | if (callback) { 17 | this.callback = callback; 18 | } 19 | } 20 | setPrinterStatus.prototype = { 21 | /** 22 | * 增加状态列表 23 | * @param {object} data 增加数据 24 | * @return {number} 列表编号 25 | */ 26 | addList: function(data) { 27 | if (data) { 28 | this.return_list.push(data); 29 | } else { 30 | this.return_list.push({}); 31 | } 32 | return this.return_list.length - 1; 33 | }, 34 | /** 35 | * 更新列表 36 | * @param {number} number 列表编号 37 | * @param {object} data 更新数据 38 | * @return {boolen} 是否已全部更新 39 | */ 40 | updateList: function(number, data) { 41 | this.return_status.push(1); 42 | for (var i in data) { 43 | if (data.hasOwnProperty(i)) { 44 | this.return_list[number][i] = data[i]; 45 | } 46 | } 47 | if (this.return_status.length === this.return_list.length) { 48 | return true; 49 | } else { 50 | return false; 51 | } 52 | }, 53 | /** 54 | * 获取列表 55 | * @return {object} 列表数据 56 | */ 57 | getList: function() { 58 | return this.return_list; 59 | }, 60 | callEnd: function() { 61 | if (this.callback) { 62 | this.callback.call(this); 63 | } 64 | } 65 | } 66 | 67 | //http请求响应 68 | var app = require('http').createServer(function(req, res) { 69 | var objectUrl = url.parse(req.url); 70 | var objectQuery = querystring.parse(objectUrl.query); 71 | //接受数据 72 | var chunk = ''; 73 | req.on('data', function(data) { 74 | chunk += data; 75 | }); 76 | //回传内容 77 | req.on('end', function() { 78 | var data = { 79 | status: 0 80 | } 81 | if (!objectQuery.action) { 82 | data.msg = '没有指定动作'; 83 | } else if (chunk) { 84 | switch (objectQuery.action) { 85 | case 'print': 86 | var print_data = JSON.parse(chunk); 87 | //生成回调函数 88 | var print_status = new setPrinterStatus(function() { 89 | data = { 90 | status: 1, 91 | msg: this.getList() 92 | } 93 | res.setHeader('Content-Type', 'application/json'); 94 | res.end(JSON.stringify(data)); 95 | }); 96 | //执行批量打印 97 | for (var i = 0; i < print_data.length; i++) { 98 | var list_number = print_status.addList({ 99 | 'id': print_data[i].id, 100 | 'group_id': print_data[i].group_id 101 | }); 102 | //添加返回列表 103 | new printer(print_data[i].printer, function(err, msg) { 104 | //查找打印机出错 105 | if (err) { 106 | print_status.updateList(list_number, { 107 | status: 0, 108 | msg: msg + ':' + err.toString() 109 | }); 110 | //查找打印机成功 111 | } else { 112 | //开始打印 113 | this.compile(print_data[i].content).print(function(err, msg) { 114 | //打印失败 115 | if (err) { 116 | print_status.updateList(list_number, { 117 | status: 0, 118 | msg: msg + ':' + err.toString() 119 | }); 120 | //打印成功 121 | } else { 122 | print_status.updateList(list_number, { 123 | status: 1, 124 | msg: msg 125 | }); 126 | } 127 | }); 128 | } 129 | }); 130 | } 131 | print_status.callEnd(); 132 | break; 133 | case 'cmd': 134 | var print_data = JSON.parse(chunk); 135 | //生成回调函数 136 | var print_status = new setPrinterStatus(function() { 137 | data = { 138 | status: 1, 139 | msg: this.getList() 140 | } 141 | res.setHeader('Content-Type', 'application/json'); 142 | res.end(JSON.stringify(data)); 143 | }); 144 | //执行批量打印 145 | for (var i = 0; i < print_data.length; i++) { 146 | var list_number = print_status.addList({ 147 | 'id': print_data[i].id, 148 | 'group_id': print_data[i].group_id 149 | }); 150 | //添加返回列表 151 | new printer(print_data[i].printer, function(err, msg) { 152 | //查找打印机出错 153 | if (err) { 154 | print_status.updateList(list_number, { 155 | status: 0, 156 | msg: msg + ':' + err.toString() 157 | }); 158 | //查找打印机成功 159 | } else { 160 | //发送执行命令 161 | this.compile(print_data[i].content).sendCmd(function(err, msg) { 162 | //打印失败 163 | if (err) { 164 | print_status.updateList(list_number, { 165 | status: 0, 166 | msg: msg + ':' + err.toString() 167 | }); 168 | //打印成功 169 | } else { 170 | print_status.updateList(list_number, { 171 | status: 1, 172 | msg: msg 173 | }); 174 | } 175 | }); 176 | } 177 | }); 178 | } 179 | print_status.callEnd(); 180 | break; 181 | default: 182 | data = { 183 | status: 0, 184 | msg: '错误的动作' 185 | } 186 | res.setHeader('Content-Type', 'application/json'); 187 | res.end(JSON.stringify(data)); 188 | } 189 | } else { 190 | data.msg = '没有数据'; 191 | res.setHeader('Content-Type', 'application/json'); 192 | res.end(JSON.stringify(data)); 193 | } 194 | }); 195 | }); 196 | app.listen(process.env.PORT || 2520, function() { 197 | console.log('打印测试程序已运行,请参考github说明测试打印'); 198 | }); -------------------------------------------------------------------------------- /lib/commands.js: -------------------------------------------------------------------------------- 1 | // 2 | // SEE: https://www.sparkfun.com/datasheets/Components/General/Driver%20board.pdf 3 | // 4 | 5 | module.exports = { 6 | INITIAL_PRINTER: '\x1B\x40', //Initial paper 7 | NEW_LINE: '\x0A', //Add new line 8 | PAPER_CUTTING: '\x1d\x56\x41', //Cut paper 9 | LINE_HEIGHT: '\x1b\x32', //Normal line height 10 | LINE_HEIGHT_B: '\x1b\x33\x6e', //Normal line height large 11 | CHN_TEXT: '\x1b\x52\x0f', //CHN text 12 | // text style 13 | TXT_NORMAL: '\x1d\x21\x00', // Normal text 14 | TXT_SIZE: '\x1d\x21', // Double height text 15 | 16 | TXT_UNDERL_OFF: '\x1b\x2d\x00', // Underline font OFF 17 | TXT_UNDERL_ON: '\x1b\x2d\x01', // Underline font 1-dot ON 18 | TXT_UNDERL2_ON: '\x1b\x2d\x02', // Underline font 2-dot ON 19 | TXT_BOLD_OFF: '\x1b\x45\x00', // Bold font OFF 20 | TXT_BOLD_ON: '\x1b\x45\x01', // Bold font ON 21 | 22 | TXT_ALIGN_L: '\x1b\x61\x00', // Left justification 23 | TXT_ALIGN_C: '\x1b\x61\x01', // Centering 24 | TXT_ALIGN_R: '\x1b\x61\x02', // Right justification 25 | 26 | //字体 27 | TXT_FONT_A: '\x1b\x4d\x00', // Font type A 28 | TXT_FONT_B: '\x1b\x4d\x01', // Font type B 29 | TXT_FONT_C: '\x1b\x4d\x02', // Font type C 30 | TXT_FONT_D: '\x1b\x4d\x48', // Font type D 31 | TXT_FONT_E: '\x1b\x4d\x31', // Font type E 32 | 33 | //barcode 34 | BARCODE_TXT_OFF: '\x1d\x48\x00', // HRI barcode chars OFF 35 | BARCODE_TXT_ABV: '\x1d\x48\x01', // HRI barcode chars above 36 | BARCODE_TXT_BLW: '\x1d\x48\x02', // HRI barcode chars below 37 | BARCODE_TXT_BTH: '\x1d\x48\x03', // HRI barcode chars both above and below 38 | 39 | BARCODE_FONT_A: '\x1d\x66\x00', // Font type A for HRI barcode chars 40 | BARCODE_FONT_B: '\x1d\x66\x01', // Font type B for HRI barcode chars 41 | 42 | BARCODE_HEIGHT: '\x1d\x68\x64', // Barcode Height [1-255] 43 | BARCODE_WIDTH: '\x1d\x77\x03', // Barcode Width [2-6] 44 | 45 | //一维码 46 | BARCODE_UPC_A: '\x1d\x6b\x00', // Barcode type UPC-A 47 | BARCODE_UPC_E: '\x1d\x6b\x01', // Barcode type UPC-E 48 | BARCODE_EAN13: '\x1d\x6b\x02', // Barcode type EAN13 49 | BARCODE_EAN8: '\x1d\x6b\x03', // Barcode type EAN8 50 | BARCODE_CODE39: '\x1d\x6b\x04', // Barcode type CODE39 51 | BARCODE_ITF: '\x1d\x6b\x05', // Barcode type ITF 52 | BARCODE_NW7: '\x1d\x6b\x06', // Barcode type NW7 53 | 54 | //二维码,from http://stackoverflow.com/questions/23577702/printing-qr-codes-through-an-esc-pos-thermal-printer 55 | QRCODE_SIZE_MODAL: '\x1D\x28\x6B\x03\x00\x31\x41\x32\x00', //Select the model,[49 x31, model 1] [50 x32, model 2] [51 x33, micro qr code] 56 | QRCODE_SIZE: '\x1D\x28\x6B\x03\x00\x31\x43', //Set the size of module 57 | QRCODE_ERROR: '\x1D\x28\x6B\x03\x00\x31\x45\x31', //Set n for error correction [48 x30 -> 7%] [49 x31-> 15%] [50 x32 -> 25%] [51 x33 -> 30%] 58 | QRCODE_AREA_LSB: '\x1D\x28\x6B', //Store the data in the symbol storage area LSB 59 | QRCODE_AREA_MSB: '\x31\x50\x30', //Store the data in the symbol storage area MSB 60 | QRCODE_PRINT:'\x1D\x28\x6B\x03\x00\x31\x51\x30', //Print the symbol data in the symbol storage area 61 | 62 | //钱箱 63 | CASHBOX_OPEN: '\x1B\x70\x00\xFF\xFF', //Open casebox 64 | 65 | //蜂鸣 66 | BEEP:'\x1b\x42' //beep 67 | } -------------------------------------------------------------------------------- /lib/printer.js: -------------------------------------------------------------------------------- 1 | var iconv = require('iconv-lite'), 2 | cmds = require('./commands'), 3 | node_printer = require("printer"), 4 | BufferHelper = require('bufferhelper'); 5 | 6 | /** 7 | * 打印任务 8 | * @param {string} printer_name 打印机名 9 | * @param {function} callback function(err,msg),当获取打印机后执行,如果不存在指定打印机,返回err信息 10 | */ 11 | var printer = function(printer_name, callback) { 12 | if (!printer_name) { 13 | printer_name = node_printer.getDefaultPrinterName(); 14 | } 15 | this.printer = printer_name; 16 | try { 17 | node_printer.getPrinter(this.printer); 18 | } catch (err) { 19 | if (callback) callback.call(this, err, 'Can\'t find the printer'); 20 | return false; 21 | } 22 | this._queue = new BufferHelper(); 23 | this._writeCmd('INITIAL_PRINTER'); 24 | this._writeCmd('CHN_TEXT'); 25 | if (callback) callback.call(this, null, 'Get printer success'); 26 | } 27 | 28 | printer.prototype = { 29 | /** 30 | * 打印文字 31 | * @param {string} text 文字内容 32 | * @param {boolen} inline 是否换行 33 | * @return {object} 当前对象 34 | */ 35 | text: function(text, inline) { 36 | if (text) { 37 | this._queue.concat(iconv.encode(text, 'GBK')); 38 | if (!inline) this._writeCmd('NEW_LINE'); 39 | } 40 | return this; 41 | }, 42 | /** 43 | * 打印空行 44 | * @param {number} number 行数 45 | * @return {object} 当前对象 46 | */ 47 | line: function(number) { 48 | number = number || 1; 49 | for (var i = 0; i < number; i++) { 50 | this._writeCmd('NEW_LINE'); 51 | } 52 | return this; 53 | }, 54 | /** 55 | * 设置对其 56 | * @param {string} align 居中类型,L/C/R 57 | * @return {object} 当前对象 58 | */ 59 | setAlign: function(align) { 60 | this._writeCmd('TXT_ALIGN_' + align.toUpperCase()); 61 | return this; 62 | }, 63 | /** 64 | * 设置字体 65 | * @param {string} family A/B/C/D/E 66 | * @return {object} 当前对象 67 | */ 68 | setFont: function(family) { 69 | this._writeCmd('TXT_FONT_' + family.toUpperCase()); 70 | return this; 71 | }, 72 | /** 73 | * 设置行距 74 | * @param {number} hex 16进制数据,如'\x05' 75 | */ 76 | setLineheight: function(hex) { 77 | this._writeCmd('LINE_HEIGHT'); 78 | if (hex) { 79 | //console.log('\x1b\x33'+hex); 80 | this._queue.concat(new Buffer('\x1b\x33' + hex)); 81 | //设置默认行间距 82 | } 83 | return this; 84 | }, 85 | /** 86 | * 设置格式(加粗,下拉) 87 | * @param {string} type B/U/U2/BU/BU2 88 | * @return {object} 当前对象 89 | */ 90 | setStyle: function(type) { 91 | switch (type.toUpperCase()) { 92 | case 'B': 93 | this._writeCmd('TXT_UNDERL_OFF'); 94 | this._writeCmd('TXT_BOLD_ON'); 95 | break; 96 | case 'U': 97 | this._writeCmd('TXT_BOLD_OFF'); 98 | this._writeCmd('TXT_UNDERL_ON'); 99 | break; 100 | case 'U2': 101 | this._writeCmd('TXT_BOLD_OFF'); 102 | this._writeCmd('TXT_UNDERL2_ON'); 103 | break; 104 | case 'BU': 105 | this._writeCmd('TXT_BOLD_ON'); 106 | this._writeCmd('TXT_UNDERL_ON'); 107 | break; 108 | case 'BU2': 109 | this._writeCmd('TXT_BOLD_ON'); 110 | this._writeCmd('TXT_UNDERL2_ON'); 111 | break; 112 | case 'NORMAL': 113 | default: 114 | this._writeCmd('TXT_BOLD_OFF'); 115 | this._writeCmd('TXT_UNDERL_OFF'); 116 | break; 117 | } 118 | return this; 119 | }, 120 | /** 121 | * 设定字体尺寸 122 | * @param {string} size 2/null 123 | * @return {object} 当前对象 124 | */ 125 | setSize: function(size) { 126 | this._writeCmd('TXT_NORMAL'); 127 | this._writeCmd('LINE_HEIGHT'); 128 | switch(parseInt(size)){ 129 | case 2: 130 | this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x10')); 131 | this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x01')); 132 | break; 133 | case 3: 134 | this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x32')); 135 | this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x02')); 136 | break; 137 | case 4: 138 | this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x48')); 139 | this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x03')); 140 | break; 141 | } 142 | return this; 143 | }, 144 | /** 145 | * 二维码 146 | * @param {string} code 打印内容 147 | * @param {string} type 打印类型,UPC-A(11-12)/UPC-E(11-12,不可用)/EAN13(默认,12-13)/EAN8(7-8)/CODE39(1-255,不可用)/ITF(1-255偶数,不可用)/NW7(1-255,不可用) 148 | * @param {number} width 宽度 149 | * @param {number} height 高度 150 | * @param {string} position OFF/ABV/BLW/BTH 151 | * @param {string} font 字体A/B 152 | * @return {object} 当前对象 153 | */ 154 | barcode: function(code, type, width, height, position, font) { 155 | if (width >= 1 || width <= 255) { 156 | this._writeCmd('BARCODE_WIDTH'); 157 | } 158 | if (height >= 2 || height <= 6) { 159 | this._writeCmd('BARCODE_HEIGHT'); 160 | } 161 | this._writeCmd('BARCODE_FONT_' + (font || 'A').toUpperCase()); 162 | this._writeCmd('BARCODE_TXT_' + (position || 'BLW').toUpperCase()); 163 | this._writeCmd('BARCODE_' + ((type || 'EAN13').replace('-', '_').toUpperCase())); 164 | this._queue.concat(new Buffer(code)); 165 | return this; 166 | }, 167 | /** 168 | * 打印二维码,需要打印机支持 169 | * @param {string} text 打印文字内容 170 | * @param {string} size 二维码大小,16进制字符串,如'\x01'.默认为'\x06' 171 | * @param {string} lsb (text长度+3)%256转16进制后的字符,如'\x01'; 172 | * @param {[type]} msb (text长度+3)/256取整转16进制后的字符,如'\x00'; 173 | * @return {object} 当前对象 174 | */ 175 | qrcode:function(text,size,lsb,msb){ 176 | size=size?size:'\x06'; 177 | if(!/^[\w\:\/\.\?\&\=]+$/.test(text)){ 178 | this.text('二维码请使用英文和数字打印'); 179 | return this; 180 | } 181 | this._writeCmd('QRCODE_SIZE_MODAL'); 182 | this._queue.concat(new Buffer(cmds['QRCODE_SIZE']+size)); 183 | this._writeCmd('QRCODE_ERROR'); 184 | this._queue.concat(new Buffer(cmds['QRCODE_AREA_LSB']+lsb+msb+cmds['QRCODE_AREA_MSB'])); 185 | this._queue.concat(new Buffer(text)); 186 | this._writeCmd('QRCODE_PRINT'); 187 | return this; 188 | }, 189 | /** 190 | * 蜂鸣警报 191 | * @param {string} times 蜂鸣次数,16进制,1-9.默认'\x09' 192 | * @param {string} interval 蜂鸣间隔,16进制,实际间隔时间为interval*50ms,默认'\x01' 193 | * @return {object} 当前对象 194 | */ 195 | beep:function(times,interval){ 196 | times=times?times:'\x09'; 197 | interval=interval?interval:'\x01'; 198 | this._queue.concat(new Buffer(cmds['BEEP']+times+interval)); 199 | return this; 200 | }, 201 | /** 202 | * 打开钱箱 203 | * @return {object} 当前对象 204 | */ 205 | openCashbox: function() { 206 | this._writeCmd('CASHBOX_OPEN'); 207 | return this; 208 | }, 209 | /** 210 | * 编译指定语法字符串为print方法 211 | * @param {string} string 语法字符串 212 | * @return {object} 当前对象 213 | */ 214 | compile: function(string) { 215 | if (typeof string != 'string') { 216 | console.log('必须为字符串'); 217 | return this; 218 | } 219 | var _this = this; 220 | //替换换行 221 | var tpl = string.replace(/[\n\r]+/g, '/n') 222 | //替换函数 223 | .replace(/<%([\s\S]+?)%>/g, function(match, code) { 224 | return '",true);\n' + _this._renderFunc(code) + '\nobj.text("'; 225 | }) 226 | //替换换行 227 | .replace(/\/n/g, '");\nobj.text("'); 228 | tpl = 'obj.text("' + tpl + '")'; 229 | new Function('obj', tpl).call(this, this); 230 | return this; 231 | }, 232 | /** 233 | * 执行命令 234 | * @param {string} cmd 命令名 235 | */ 236 | _writeCmd: function(cmd) { 237 | if (cmds[cmd]) { 238 | this._queue.concat(new Buffer(cmds[cmd])); 239 | } 240 | }, 241 | _renderFunc: function(string) { 242 | var _this = this, 243 | status = true; 244 | string = string.trim(); 245 | //函数名生命 246 | var func = string.replace(/^([\S]+?):/, function(match, code) { 247 | var func_name = _this._escape(code); 248 | if (!_this[func_name]) { 249 | //无效函数 250 | status = false; 251 | console.log('解析模板出错没有名为' + func_name + '的方法'); 252 | } 253 | return 'obj.' + func_name + ':'; 254 | //函数变量 255 | }).replace(/:([\S]+?)$/, function(match, code) { 256 | var func_var = _this._escape(code).split(','), 257 | tpl_var = ''; 258 | var length = func_var.length; 259 | for (var i = 0; i < length; i++) { 260 | //%u hack 261 | var cur_func_var = func_var[i]; 262 | if(/%u/.test(func_var[i])){ 263 | cur_func_var=cur_func_var.replace(/%u/g,'u'); 264 | } 265 | tpl_var += '"' + cur_func_var + '",'; 266 | } 267 | tpl_var = tpl_var.replace(/\,$/, ''); 268 | return '(' + tpl_var + ');'; 269 | }); 270 | if (status) return func 271 | else return ''; 272 | }, 273 | /** 274 | * 预留XSS防御 275 | * @param {string} string 目标内容 276 | * @return {string} 转换后 277 | */ 278 | _escape: function(string) { 279 | string = unescape(string.replace(/\u/g, "%u")); //转换unicode为正常符号 280 | string = string.replace(/[\<\>\"\'\{\}]/g, ''); 281 | return string; 282 | }, 283 | /** 284 | * 执行打印 285 | * @param {Function} callback function(err,msg),当执行打印后,回调该函数,打印错误返回err信息 286 | */ 287 | print: function(callback) { 288 | this._writeCmd('PAPER_CUTTING'); 289 | this._writeCmd('INITIAL_PRINTER'); 290 | this.sendCmd(callback); 291 | }, 292 | /** 293 | * 发送命令 294 | * @param {Function} callback function(err,msg),当执行打印后,回调该函数,打印错误返回err信息 295 | */ 296 | sendCmd:function(callback){ 297 | var _this = this; 298 | node_printer.printDirect({ 299 | data: _this._queue.toBuffer(), 300 | printer: _this.printer, 301 | type: "RAW", 302 | success: function() { 303 | callback.call(_this, null, 'Print successed'); 304 | _this._queue.empty(); 305 | }, 306 | error: function(err) { 307 | callback.call(_this, null, 'Print failed'); 308 | } 309 | }); 310 | }, 311 | /** 312 | * 清空打印内容 313 | * @return {object} 当前对象 314 | */ 315 | empty: function() { 316 | this._queue.empty(); 317 | return this; 318 | } 319 | } 320 | 321 | module.exports = printer; 322 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chn-escpos", 3 | "version": "1.1.4", 4 | "description": "Chinese ESC/POS Printer driver for nodejs", 5 | "main": "./lib/printer", 6 | "scripts": { 7 | "test": "node index.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/baka397/chn-escpos.git" 12 | }, 13 | "keywords": [ 14 | "print", 15 | "chn" 16 | ], 17 | "author": "bakajinsei", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/baka397/chn-escpos/issues" 21 | }, 22 | "homepage": "https://github.com/baka397/chn-escpos#readme", 23 | "dependencies": { 24 | "bufferhelper": "^0.2.1", 25 | "iconv-lite": "^0.4.13", 26 | "printer": "^0.2.1" 27 | } 28 | } --------------------------------------------------------------------------------