├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── benchmark.js ├── lib └── encoder.js ├── package.json ├── qr.js └── test ├── encoder.js └── qr.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" 5 | - "0.8" 6 | before_install: 7 | - sudo apt-get update -qq 8 | install: 9 | - sudo apt-get --reinstall install -qq qrencode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010 Brian Celenza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node QR # 2 | 3 | node-qr is a simple NodeJS binding to the libqrencode C library written by Kentaro Fukuchi (http://fukuchi.org/works/qrencode/index.en.html). 4 | 5 | ## Installation ## 6 | 7 | To use node-qr, you will first need to install the libqrencode C library 8 | 9 | For Mac OS X (assumed MacPorts installed) 10 | 11 | port install qrencode 12 | 13 | For Ubuntu Linux 14 | 15 | apt-get install qrencode 16 | 17 | Others (see instructions on site) 18 | 19 | http://fukuchi.org/works/qrencode/index.en.html 20 | 21 | 22 | Node-qr is available in the Node Package Manager (NPM) 23 | 24 | npm install qr 25 | 26 | ## Usage ## 27 | 28 | Currently node-qr only comes with an encoder. The usage is simple 29 | 30 | encoder.encode(value, path = null, options = {}); 31 | 32 | First, require the module 33 | 34 | var Encoder = require('qr').Encoder; 35 | var encoder = new Encoder; 36 | 37 | The following example will encode a given value and emit an 'end' event with PNG data upon completion 38 | 39 | // add an event listener for the 'end' event 40 | // which fires upon encoding completion 41 | encoder.on('end', function(png_data){ 42 | // png_data is an instance of Buffer 43 | // do something 44 | }); 45 | encoder.encode('some value'); 46 | 47 | Alternatively, you can pass a file path for the PNG data to be saved to 48 | 49 | encoder.on('end', function(){ 50 | // if you specify a file path, nothing will be passed to the end listener 51 | // do something 52 | }); 53 | encoder.encode('some value', '/tmp/my_qr_file.png'); 54 | 55 | If at any time an error occurs, an 'error' event will be emitted 56 | 57 | encoder.on('error', function(err){ 58 | // err is an instance of Error 59 | // do something 60 | }); 61 | 62 | See the tests in test/qr.js for more ways to use the encoder. 63 | 64 | While node-qr and libqrencode can encode images up to 4000 characters, this library makes no attempt to automatically set error correction and version options based on the size of the value (yet, anyway). You are responsible for determining what length your values will be and adjusting the options accordingly. 65 | 66 | See [2D QR Code Barcode Generation Guide](http://www.idautomation.com/barcode/qr-code.html#Data_Encoded) for more information on limitations of QR Codes and the devices that read them. 67 | 68 | ## Encoder Options ## 69 | 70 | The following options can be passed via the third argument of the encode method, which should be an object 71 | 72 | * background_color: self explanatory 73 | * foreground_color: self explanatory 74 | * dot_size: specify the size of dot (pixel). (default=3) 75 | * margin: specify the width of margin. (default=4) 76 | * level: specify error correction level from L (lowest) to H (highest). (default=L) 77 | * case_sensitive: ignore case distinctions and use only upper-case characters. (default=true) 78 | * version: specify the version of the symbol. (default=1) 79 | * type: type of image to export ('PNG','EPS','SVG','ANSI','ANSI256','ASCII','ASCIIi','UTF8','ANSIUTF8') 80 | 81 | For more information around options, see the docs provided for the libqrencoder library [here](http://fukuchi.org/works/qrencode/index.en.html "libqrencoder") 82 | 83 | ## Feedback/Pull Requests ## 84 | 85 | Feedback and pull requests are always welcomed. This is a work-in-progress. Any help is greatly appreciated. 86 | 87 | ## Who's Using node-qr? ## 88 | 89 | * Rax.io (Rackspace Short URL Service) - http://rax.io 90 | 91 | ## Are you using node-qr? ## 92 | 93 | If you are using Node QR in a production environment, I'd love to know! Send me a PM or email if you don't mind being listed in this readme. -------------------------------------------------------------------------------- /benchmark.js: -------------------------------------------------------------------------------- 1 | var Encoder = require('./qr').Encoder, 2 | Benchmark = require('benchmark'); 3 | var encoder = new Encoder; 4 | var suite = new Benchmark.Suite 5 | 6 | suite.add('Encoder#encode()', function(){ 7 | encoder.encode('node-qr'); 8 | }) 9 | .on('cycle', function(event, bench){ 10 | console.log(String(bench)); 11 | }) 12 | .on('complete', function(){ 13 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 14 | }) 15 | .run({ 'async': true }); -------------------------------------------------------------------------------- /lib/encoder.js: -------------------------------------------------------------------------------- 1 | var util = require('util'), 2 | events = require('events'), 3 | child_process = require('child_process'); 4 | 5 | var Encoder = exports.Encoder = function(){ 6 | events.EventEmitter.call(this); 7 | 8 | 9 | } 10 | util.inherits(Encoder, events.EventEmitter); 11 | 12 | Encoder.prototype.default_options = { 13 | foreground_color: '#000000', // change foreground color in qrencode v3.4.0+ 14 | background_color: '#FFFFFF', // change background color in qrencode v3.4.0+ 15 | dot_size: 3, // default 3x3px per dot 16 | margin: 4, // default 4 dots for the margin 17 | level: 'L', // valid args (lowest to highest): L, M, Q, H 18 | case_sensitive: true, // case sensitive, 19 | version: 1, // default version 1, 20 | type : null // Default to null type, removing this flag qrencode < v3.4.0 21 | }; 22 | 23 | Encoder.prototype.types = ['PNG', 'EPS', 'SVG', 'ANSI', 'ANSI256', 'ASCII', 24 | 'ASCIIi', 'UTF8', 'ANSIUTF8', null]; 25 | 26 | /** 27 | * Converts a string value to QR Code PNG data, and optionally saves to a file 28 | * 29 | * @param String value The value to be encoded 30 | * @param String path Where to save the PNG file (optional) 31 | * @param Object options A hash of options (optional) 32 | * @return void 33 | */ 34 | Encoder.prototype.encode = function(value, path, options) 35 | { 36 | // preserve scope in callbacks with self 37 | var self = this, 38 | cmd_options, qrencode_args, stdout, stderr, qrencode, exitcode; 39 | 40 | try { 41 | // check for undefined value 42 | if(value == undefined) { 43 | throw new Error('No value specified for encode method'); 44 | } 45 | 46 | // create new buffer for value 47 | value = new Buffer(value); 48 | 49 | // if options are given, override defaults 50 | cmd_options = {}; 51 | if(options == null) options = {}; 52 | for(var key in this.default_options) { 53 | cmd_options[key] = (options[key] == undefined) ? 54 | this.default_options[key] 55 | : options[key]; 56 | } 57 | 58 | // start with base set of args that we'll always pass 59 | qrencode_args = [ 60 | '-s', cmd_options.dot_size, 61 | '-m', cmd_options.margin, 62 | '-l', cmd_options.level, 63 | '-v', cmd_options.version 64 | ]; 65 | 66 | // only set foreground and background colors if they differ from 67 | // defaults to maintain compatibility with qrencode pre-v3.4.0 68 | if(cmd_options.foreground_color !== self.default_options.foreground_color 69 | || cmd_options.background_color !== self.default_options.background_color) { 70 | 71 | // remove # symbol from color codes because qrencoder does not like it 72 | cmd_options.foreground_color = cmd_options.foreground_color.replace('#', ''); 73 | cmd_options.background_color = cmd_options.background_color.replace('#', ''); 74 | 75 | qrencode_args.push( 76 | '--foreground=' + cmd_options.foreground_color, 77 | '--background=' + cmd_options.background_color 78 | ); 79 | } 80 | 81 | // Only include the type flag if it has been passed to 82 | // maintain compatibility with qrencode pre-v3.4.0 83 | if(cmd_options.type !== null){ 84 | if(self.types.indexOf(cmd_options.type.toUpperCase()) === -1){ 85 | throw new Error('type must be one of ', self.types.toString()); 86 | } 87 | qrencode_args.push('-t' + cmd_options.type); 88 | } 89 | 90 | // if case-sensitivity is disabled, add flag 91 | if(!cmd_options.case_sensitive) qrencode_args.push('-i'); 92 | 93 | // if we have a path, write to the path 94 | // otherwise, it will write to stdout 95 | qrencode_args.push('-o'); 96 | if(path != null) { 97 | qrencode_args.push(path); 98 | } else { 99 | qrencode_args.push('-'); 100 | } 101 | 102 | // add the value to be encoded 103 | qrencode_args.push(value); 104 | 105 | // spawn the child process 106 | qrencode = child_process.spawn( 107 | 'qrencode', 108 | qrencode_args 109 | ); 110 | 111 | // add event listener for stdout data and populate stdout var 112 | // in the event no path was given 113 | qrencode.stdout.on('data', function(data) { 114 | stdout = data; 115 | }); 116 | 117 | // add event listener for stderr 118 | qrencode.stderr.on('data', function(data) { 119 | stderr = data; 120 | }); 121 | 122 | // add listener for process exit and save exit code 123 | qrencode.on('exit', function(code) { 124 | exitcode = code; 125 | }); 126 | 127 | // add listener for filehandle close and emit end or error event 128 | // depending on exit code 129 | qrencode.on('close', function() { 130 | if(exitcode !== 0) { 131 | self.emit('error', new Error(stderr)); 132 | } else { 133 | self.emit('end', stdout); 134 | } 135 | }); 136 | 137 | } catch(err) { 138 | this.emit('error', err); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qr", 3 | "description": "A small library to generate QR codes with libqrencode.", 4 | "version": "0.2.4", 5 | "author": { 6 | "name": "Brian Celenza", 7 | "email": "bcelenza@gmail.com" 8 | }, 9 | "homepage": "https://github.com/heavyco/node-qr", 10 | "keywords": ["qr", "qrcode", "qrencode", "qrencoder"], 11 | "main": "./qr.js", 12 | "dependencies": { 13 | "vows": ">= 0.5.8", 14 | "benchmark": "" 15 | }, 16 | "engines": { 17 | "node": ">= 0.8.0" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/heavyco/node-qr.git" 22 | }, 23 | "license": { 24 | "type": "MIT", 25 | "url": "http://www.opensource.org/licenses/mit-license.php" 26 | }, 27 | "scripts": { 28 | "test": "vows --spec test/*.js" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /qr.js: -------------------------------------------------------------------------------- 1 | exports.Encoder = require('./lib/encoder').Encoder; -------------------------------------------------------------------------------- /test/encoder.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | events = require('events'), 3 | assert = require('assert'), 4 | fs = require('fs'), 5 | util = require('util'), 6 | child_process = require('child_process'), 7 | qr = require('../qr'); 8 | 9 | var Encoder = qr.Encoder; 10 | var test_file_name = './test.png'; 11 | 12 | vows.describe('Encoder').addBatch({ 13 | 'The libqrencode library': { 14 | topic: function() { 15 | var which = child_process.spawn('which', ['qrencode']); 16 | which.on('exit', this.callback); 17 | }, 18 | 'is installed and available via $PATH': function(exit_code, process) { 19 | assert.equal(exit_code, null, 'libqrencode does not appear to have been installed properly'); 20 | } 21 | } 22 | }).addBatch({ 23 | 'The encoder': { 24 | topic: new(Encoder), 25 | 'is an event emitter': function(encoder) { 26 | assert.ok((encoder instanceof events.EventEmitter)); 27 | }, 28 | 'has some default options': function(encoder) { 29 | assert.ok(encoder.default_options); 30 | }, 31 | 'has an encode method': function(encoder) { 32 | assert.ok((typeof encoder.encode == "function")); 33 | } 34 | } 35 | }).addBatch({ 36 | 'The encode method': { 37 | 'with a provided value': { 38 | 'and a path': { 39 | topic: function() { 40 | var encoder = new Encoder; 41 | encoder.on('end', this.callback); 42 | encoder.encode('test', test_file_name); 43 | }, 44 | 'emits an \'end\' event': function(result, encoder) { 45 | return true; 46 | }, 47 | 'creates a PNG file at provided path': function(result, encoder) { 48 | var stats = fs.statSync(test_file_name); 49 | assert.notEqual(stats.size, 0, 'Zero file size'); 50 | fs.unlinkSync(test_file_name); 51 | } 52 | }, 53 | 'and no path': { 54 | topic: function() { 55 | var encoder = new Encoder; 56 | encoder.on('end', this.callback); 57 | encoder.encode('test'); 58 | }, 59 | 'emits an \'end\' event': function(result, encoder) { 60 | return true; 61 | }, 62 | 'provides PNG data with event': function(result, encoder) { 63 | assert.ok((result instanceof Buffer)); 64 | assert.notEqual(result.length, 0, 'Zero buffer length'); 65 | } 66 | }, 67 | 'and a custom size option': { 68 | topic: function() { 69 | var encoder = new Encoder; 70 | encoder.on('end', this.callback); 71 | // set custom dot size of 10px 72 | encoder.encode('test', null, { dot_size: 10 }); 73 | }, 74 | 'provides PNG data with a custom size': function(result, encoder) { 75 | assert.ok((result instanceof Buffer)); 76 | assert.notEqual(result.length, 0, 'Zero buffer length for custom size'); 77 | } 78 | }, 79 | 'and a custom margin option': { 80 | topic: function() { 81 | var encoder = new Encoder; 82 | encoder.on('end', this.callback); 83 | // set custom margin size of 5 dots 84 | encoder.encode('test', null, { margin: 5 }); 85 | }, 86 | 'provides PNG data with a custom margin': function(result, encoder) { 87 | assert.ok((result instanceof Buffer)); 88 | assert.notEqual(result.length, 0, 'Zero buffer length for custom margin'); 89 | } 90 | }, 91 | 'and a custom level option': { 92 | topic: function() { 93 | var encoder = new Encoder; 94 | encoder.on('end', this.callback); 95 | // set a custom level option of 'H' 96 | encoder.encode('test', null, { level: 'H' }); 97 | }, 98 | 'provides PNG data with a custom level': function(result, encoder) { 99 | assert.ok((result instanceof Buffer)); 100 | assert.notEqual(result.length, 0, 'Zero buffer length for custom level'); 101 | } 102 | }, 103 | 'and a custom version option': { 104 | topic: function() { 105 | var encoder = new Encoder; 106 | encoder.on('end', this.callback); 107 | // set a custom version 108 | encoder.encode('test', null, { version: 10 }); 109 | }, 110 | 'provides PNG data with a different version': function(result, encoder) { 111 | assert.ok((result instanceof Buffer)); 112 | assert.notEqual(result.length, 0, 'Zero buffer length for custom version'); 113 | } 114 | }, 115 | 'and a custom type option': { 116 | topic: function() { 117 | var encoder = new Encoder; 118 | encoder.on('end', this.callback); 119 | // set a custom output 120 | encoder.encode('test', null, { type: 'SVG' }); 121 | }, 122 | 'provides SVG data' : function(result, encoder) { 123 | assert.ok((result instanceof Buffer)); 124 | assert.notEqual(result.length, 0, 'Zero buffer length for custom type'); 125 | } 126 | }, 127 | 'and an invalid type option': { 128 | topic: function() { 129 | var encoder = new Encoder; 130 | encoder.on('error', this.callback); 131 | // set an incorrect type option 132 | encoder.encode('test', null, { type: 'SPVG' }); 133 | }, 134 | 'emits an \'error\' event with Error object' : function(result, encoder) { 135 | assert.ok((result instanceof Error), 'Unexpected result emitted with error event'); 136 | assert.notEqual(result.message, ""); 137 | } 138 | }, 139 | 'and an invalid option': { 140 | topic: function() { 141 | var encoder = new Encoder; 142 | encoder.on('error', this.callback); 143 | // set bad option 144 | encoder.encode('test', null, { version: 1000 }); 145 | }, 146 | 'emits an \'error\' event with Error object': function(result, encoder) { 147 | assert.ok((result instanceof Error), 'Unexpcted result emitted with error event'); 148 | assert.notEqual(result.message, ""); 149 | } 150 | }, 151 | 'and a bad path': { 152 | topic: function() { 153 | var encoder = new Encoder; 154 | encoder.on('error', this.callback); 155 | // set a bad path 156 | encoder.encode('test', 'C:/notwindows/'); 157 | }, 158 | 'emits an \'error\' event with Error object': function(result, encoder) { 159 | assert.ok((result instanceof Error), 'Unexpcted result emitted with error event'); 160 | assert.notEqual(result.message, ""); 161 | } 162 | } 163 | }, 164 | 'without a provided value': { 165 | topic: function() { 166 | var encoder = new Encoder; 167 | encoder.on('error', this.callback); 168 | encoder.encode(); 169 | }, 170 | 'emits an \'error\' event with an Error object': function(result, encoder) { 171 | assert.ok((result instanceof Error), 'Unexpcted result emitted with error event'); 172 | assert.equal(result.message, "No value specified for encode method"); 173 | } 174 | } 175 | } 176 | }).export(module); -------------------------------------------------------------------------------- /test/qr.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | util = require('util'), 4 | qr = require('../qr'); 5 | 6 | vows.describe('QR').addBatch({ 7 | 'The QR module': { 8 | topic: qr, 9 | 'exists': function(qr) { 10 | assert.notEqual(qr, undefined, 'QR module is undefined'); 11 | }, 12 | 'has an encoder object': function(qr) { 13 | assert.notEqual(qr.Encoder, undefined, 'QR Encoder undefined'); 14 | } 15 | } 16 | }).export(module); --------------------------------------------------------------------------------