├── .eslintignore ├── .gitignore ├── screenshot.png ├── .babelrc ├── modules ├── encodings │ ├── upc.js │ ├── index.js │ ├── code128b.js │ ├── itf14.js │ ├── code128c.js │ ├── pharmacode.js │ ├── itf.js │ ├── code39.js │ ├── ean.js │ └── code128.js └── index.js ├── .eslintrc ├── webpack.config.js ├── CHANGELOG.md ├── LICENSE ├── package.json ├── test ├── server-test.js └── index.html └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | test/*.html -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | test/server-test.html 4 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavded/io-barcode/HEAD/screenshot.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /modules/encodings/upc.js: -------------------------------------------------------------------------------- 1 | import EAN from './ean' 2 | 3 | class UPC extends EAN { 4 | constructor(code) { 5 | super(`0${code}`) 6 | } 7 | } 8 | 9 | export default UPC -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "ecmaFeatures": { 8 | "modules": true 9 | }, 10 | "rules": { 11 | "no-var": 1, 12 | "semi": [1, "never"], 13 | "quotes": [1, "single"], 14 | "new-cap": 0, 15 | "eol-last": 0, 16 | "no-underscore-dangle": 0, 17 | "curly": [1, "multi-line"] 18 | } 19 | } -------------------------------------------------------------------------------- /modules/encodings/index.js: -------------------------------------------------------------------------------- 1 | import EAN from './ean' 2 | import UPC from './upc' 3 | import ITF from './itf' 4 | import ITF14 from './itf14' 5 | import CODE39 from './code39' 6 | import CODE128B from './code128b' 7 | import CODE128C from './code128c' 8 | import Pharmacode from './pharmacode' 9 | 10 | export default { 11 | EAN, 12 | UPC, 13 | ITF, 14 | ITF14, 15 | CODE39, 16 | CODE128B, 17 | CODE128C, 18 | Pharmacode 19 | } -------------------------------------------------------------------------------- /modules/encodings/code128b.js: -------------------------------------------------------------------------------- 1 | import CODE128 from './code128' 2 | 3 | class CODE128B extends CODE128 { 4 | constructor (code) { 5 | super(code) 6 | this.startCode = 104 7 | } 8 | 9 | encodeClass () { 10 | let result = '' 11 | for (let i = 0; i < this.code.length; i++) { 12 | result += super.encodingByChar(this.code[i]) 13 | } 14 | return result 15 | } 16 | 17 | checksum() { 18 | let sum = 0 19 | for (let i = 0; i < this.code.length; i++) { 20 | sum += super.weightByCharacter(this.code[i]) * (i + 1) 21 | } 22 | return (sum + this.startCode) % 103 23 | } 24 | } 25 | 26 | export default CODE128B -------------------------------------------------------------------------------- /modules/encodings/itf14.js: -------------------------------------------------------------------------------- 1 | import ITF from './itf' 2 | 3 | const validRe = /^[0-9]{13,14}$/ 4 | 5 | class ITF14 extends ITF { 6 | constructor(code) { 7 | super(code) 8 | 9 | if (code.length === 13) { 10 | this.code += this.checksum() 11 | } 12 | } 13 | 14 | isValid() { 15 | return super.isValid() && validRe.test(this.code) && 16 | Number(this.code[13]) === this.checksum() 17 | } 18 | 19 | checksum() { 20 | let result = 0 21 | 22 | for (let i = 0; i < 13; i++) { 23 | result += Number(this.code[i]) * (3 - (i % 2) * 2) 24 | } 25 | 26 | return Math.ceil(result / 10) * 10 - result; 27 | } 28 | } 29 | 30 | export default ITF14 31 | -------------------------------------------------------------------------------- /modules/encodings/code128c.js: -------------------------------------------------------------------------------- 1 | import CODE128 from './code128' 2 | 3 | class CODE128C extends CODE128 { 4 | constructor (code) { 5 | super(code) 6 | this.code = this.code.replace(/ /g, '') 7 | this.startCode = 105 8 | } 9 | 10 | encodeClass () { 11 | let result = '' 12 | for(let i = 0; i < this.code.length; i += 2) { 13 | result += super.encodingById(Number(this.code.substr(i, 2))) 14 | } 15 | return result 16 | } 17 | 18 | checksum() { 19 | let sum = 0 20 | let w = 1 21 | for (let i = 0; i < this.code.length; i += 2) { 22 | sum += Number(this.code.substr(i, 2)) * (w) 23 | w++ 24 | } 25 | return (sum + this.startCode) % 103 26 | } 27 | } 28 | 29 | export default CODE128C -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | let webpack = require('webpack') 3 | const env = process.env.NODE_ENV || 'development' 4 | 5 | let plugins = [ 6 | new webpack.DefinePlugin({ 7 | 'process.env.NODE_ENV': env 8 | }) 9 | ] 10 | 11 | if (env === 'production') { 12 | plugins.push( 13 | new webpack.optimize.UglifyJsPlugin({ 14 | compressor: { 15 | warnings: false 16 | } 17 | }) 18 | ) 19 | } 20 | 21 | module.exports = { 22 | devtool: env === 'development' && 'inline-source-map', 23 | output: { 24 | library: 'ioBarcode', 25 | libraryTarget: 'umd' 26 | }, 27 | module: { 28 | loaders: [{ 29 | test: /\.js$/, 30 | exclude: /node_modules/, 31 | loader: 'babel-loader' 32 | }] 33 | }, 34 | plugins: plugins, 35 | devServer: { 36 | contentBase: './test', 37 | port: 3000 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 1.3.0 / 2016-02-03 3 | ================== 4 | 5 | * Updated to Babel 6 6 | * Use es6 object literal extensions [toddw/isomorphic] 7 | * Optimize code39 and code128 8 | 9 | 1.2.0 / 2015-07-10 10 | ================== 11 | 12 | * Add font weight customization [orzarchi] 13 | 14 | 1.1.0 / 2015-07-06 15 | ================== 16 | 17 | * Add custom label support [orzarchi] 18 | * Switched to babel-runtime 19 | 20 | 1.0.4 / 2015-04-24 21 | ================== 22 | 23 | * missing commits from release 1.0.3 24 | * fix; opts formatting in readme 25 | 26 | 1.0.3 / 2015-04-24 27 | ================== 28 | 29 | * add; badges 30 | * mod; note about testing 31 | 32 | 1.0.2 / 2015-04-24 33 | ================== 34 | 35 | * fix; use node 'main' for webpack/browserify 36 | * mod; update package.json links 37 | 38 | 1.0.1 / 2015-04-24 39 | ================== 40 | 41 | * fix; switch to core-js for smaller footprint 42 | 43 | 1.0.0 / 2015-04-24 44 | ================== 45 | 46 | * initial release 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Marc Harter (wavded@gmail.com) 2 | Copyright (c) 2012 Johan Lindell (johan@lindell.me) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /modules/encodings/pharmacode.js: -------------------------------------------------------------------------------- 1 | import repeat from 'core-js/library/fn/string/repeat' 2 | 3 | class Pharmacode { 4 | constructor (code) { 5 | this.code = Number(code) 6 | } 7 | 8 | isValid() { 9 | return this.code >= 3 && this.code <= 131070 10 | } 11 | 12 | // A helper function to calculate the zeros at the end of a string 13 | _calcZeros (code) { 14 | let i = code.length - 1 15 | let zeros = 0 16 | while (code[i] === '0' || i < 0){ 17 | zeros++ 18 | i-- 19 | } 20 | return zeros 21 | } 22 | 23 | encodeBinary (code, state) { 24 | if (code.length === 0) return '' 25 | 26 | let generated 27 | let nextState = false 28 | let nZeros = this._calcZeros(code) 29 | 30 | if (nZeros === 0) { 31 | generated = state ? '001' : '00111' 32 | nextState = state 33 | } 34 | else { 35 | generated = repeat('001', nZeros - (state ? 1 : 0)) 36 | generated += '00111' 37 | } 38 | return this.encodeBinary(code.substr(0, code.length - nZeros - 1), nextState) + generated 39 | } 40 | 41 | encode() { 42 | return this.encodeBinary(this.code.toString(2), true).substr(2) 43 | } 44 | } 45 | 46 | export default Pharmacode -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "io-barcode", 3 | "version": "1.3.0", 4 | "description": "Isomorphic barcode generator for Node and browsers", 5 | "keywords": [ 6 | "barcode", 7 | "isomorphic", 8 | "universal" 9 | ], 10 | "main": "./build/node", 11 | "author": "Marc Harter ", 12 | "homepage": "https://github.com/wavded/io-barcode#readme", 13 | "scripts": { 14 | "prepublish": "npm run build-node", 15 | "test": "babel-node test/server-test.js && webpack-dev-server modules/index.js", 16 | "build-browser": "rm -rf build/browser && webpack modules/index.js build/browser/io-barcode.js && NODE_ENV=production webpack modules/index.js build/browser/io-barcode.min.js && echo \"gzipped, the global build is `gzip -c build/browser/io-barcode.min.js | wc -c` bytes\"", 17 | "build-node": "rm -rf build/node && babel -d build/node ./modules" 18 | }, 19 | "dependencies": { 20 | "babel-runtime": "^6.0.0", 21 | "canvas-browserify": "^1.0.0", 22 | "core-js": "^2.0.0" 23 | }, 24 | "devDependencies": { 25 | "babel-cli": "^6.0.0", 26 | "babel-loader": "^6.0.0", 27 | "babel-plugin-transform-runtime": "^6.0.0", 28 | "babel-preset-es2015": "^6.0.0", 29 | "webpack": "^1.0.0", 30 | "webpack-dev-server": "^1.0.0" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/wavded/io-barcode.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/wavded/io-barcode/issues" 38 | }, 39 | "license": "MIT" 40 | } 41 | -------------------------------------------------------------------------------- /modules/encodings/itf.js: -------------------------------------------------------------------------------- 1 | //The structure for the all digits, 1 is wide and 0 is narrow 2 | const digitStructure = { 3 | 0: '00110', 4 | 1: '10001', 5 | 2: '01001', 6 | 3: '11000', 7 | 4: '00101', 8 | 5: '10100', 9 | 6: '01100', 10 | 7: '00011', 11 | 8: '10010', 12 | 9: '01010' 13 | } 14 | 15 | // The start bits 16 | const startBin = '1010' 17 | // The end bits 18 | const endBin = '11101' 19 | 20 | // Regexp for a valid Inter25 code 21 | const validRe = /^([0-9][0-9])+$/ 22 | 23 | class ITF { 24 | constructor (code) { 25 | this.code = String(code) 26 | } 27 | 28 | isValid() { 29 | return validRe.test(this.code) 30 | } 31 | 32 | encode() { 33 | // Create the variable that should be returned at the end of the function 34 | let result = '' 35 | 36 | // Always add the same start bits 37 | result += startBin 38 | 39 | // Calculate all the digit pairs 40 | for (let i = 0; i < this.code.length; i += 2) { 41 | result += this.calculatePair(this.code.substr(i, 2)) 42 | } 43 | 44 | // Always add the same end bits 45 | result += endBin 46 | 47 | return result 48 | } 49 | 50 | calculatePair (twoNumbers) { 51 | let result = '' 52 | 53 | let number1Struct = digitStructure[twoNumbers[0]] 54 | let number2Struct = digitStructure[twoNumbers[1]] 55 | 56 | // Take every second bit and add to the result 57 | for (let i = 0; i < 5; i++) { 58 | result += (number1Struct[i] === '1') ? '111' : '1' 59 | result += (number2Struct[i] === '1') ? '000' : '0' 60 | } 61 | 62 | return result 63 | } 64 | } 65 | 66 | export default ITF -------------------------------------------------------------------------------- /modules/encodings/code39.js: -------------------------------------------------------------------------------- 1 | const code39 = { 2 | '0': '101000111011101', 3 | '1': '111010001010111', 4 | '2': '101110001010111', 5 | '3': '111011100010101', 6 | '4': '101000111010111', 7 | '5': '111010001110101', 8 | '6': '101110001110101', 9 | '7': '101000101110111', 10 | '8': '111010001011101', 11 | '9': '101110001011101', 12 | 'A': '111010100010111', 13 | 'B': '101110100010111', 14 | 'C': '111011101000101', 15 | 'D': '101011100010111', 16 | 'E': '111010111000101', 17 | 'F': '101110111000101', 18 | 'G': '101010001110111', 19 | 'H': '111010100011101', 20 | 'I': '101110100011101', 21 | 'J': '101011100011101', 22 | 'K': '111010101000111', 23 | 'L': '101110101000111', 24 | 'M': '111011101010001', 25 | 'N': '101011101000111', 26 | 'O': '111010111010001', 27 | 'P': '101110111010001', 28 | 'Q': '101010111000111', 29 | 'R': '111010101110001', 30 | 'S': '101110101110001', 31 | 'T': '101011101110001', 32 | 'U': '111000101010111', 33 | 'V': '100011101010111', 34 | 'W': '111000111010101', 35 | 'X': '100010111010111', 36 | 'Y': '111000101110101', 37 | 'Z': '100011101110101', 38 | '-': '100010101110111', 39 | '.': '111000101011101', 40 | ' ': '100011101011101', 41 | '$': '100010001000101', 42 | '/': '100010001010001', 43 | '+': '100010100010001', 44 | '%': '101000100010001' 45 | } 46 | 47 | const validRe = /^[0-9a-zA-Z\-\.\ \$\/\+\%]+$/ 48 | 49 | class CODE39 { 50 | constructor (code) { 51 | this.code = String(code) 52 | } 53 | 54 | isValid() { 55 | return validRe.test(this.code) 56 | } 57 | 58 | encode() { 59 | let string = this.code.toUpperCase() 60 | 61 | let result = '' 62 | result += '1000101110111010' 63 | for (let i = 0; i < string.length; i++) { 64 | result += this.encodingByChar(string[i]) + '0' 65 | } 66 | result += '1000101110111010' 67 | return result 68 | } 69 | 70 | encodingByChar (char) { 71 | return code39[char] || '' 72 | } 73 | } 74 | 75 | export default CODE39 76 | 77 | -------------------------------------------------------------------------------- /test/server-test.js: -------------------------------------------------------------------------------- 1 | import ioBarcode from '../modules' 2 | import { join } from 'path' 3 | import fs from 'fs' 4 | 5 | const ioBarcode2 = require('../modules') 6 | 7 | let date = new Date() 8 | let time = `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}` 9 | let opts = { 10 | displayValue: true 11 | } 12 | 13 | /** 14 | * Currently exists an issue in node-canvas on OSX for fonts 15 | * This test works good on Ubuntu 14.04 16 | * https://github.com/Automattic/node-canvas/issues/548 17 | */ 18 | fs.writeFile(join(__dirname, 'server-test.html'), ` 19 | 20 | 21 | 22 | 23 | 24 | JsBarcode - Server Render 25 | 26 | 27 | Client Render Test 28 | 29 |

Example: Barcode Clock

30 | 31 | 32 |

Example: Options

33 | 47 | 48 |

Encodings

49 | 50 |

UPC

51 | 52 | 53 |

EAN

54 | 55 | 56 |

ITF

57 | 58 | 59 |

ITF14

60 | 61 | 62 |

CODE39

63 | 64 | 65 |

CODE128B

66 | 67 | 68 |

CODE128C

69 | 70 | 71 |

Pharmacode

72 | 73 | 74 | 75 | 76 | 77 | `) 78 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ioBarcode 7 | 8 | 9 | 10 | 11 |
12 | Server Render Test 13 | 14 |

Example: Barcode Clock

15 | 16 | 17 |

Example: Options

18 | 19 | 20 |

Encodings

21 | 22 |

UPC

23 | 24 |

EAN

25 | 26 |

ITF

27 | 28 |

ITF14

29 | 30 |

CODE39

31 | 32 |

CODE128B

33 | 34 |

CODE128C

35 | 36 |

Pharmacode

37 | 38 | 39 | 86 |
87 | 88 | -------------------------------------------------------------------------------- /modules/index.js: -------------------------------------------------------------------------------- 1 | import encodings from './encodings' 2 | import Canvas from 'canvas-browserify' 3 | 4 | let api = {} 5 | 6 | const defaults = { 7 | width: 2, 8 | height: 100, 9 | quite: 10, 10 | displayValue: false, 11 | font: 'monospace', 12 | textAlign: 'center', 13 | fontSize: 12, 14 | fontWeight: 'normal', 15 | backgroundColor: '', 16 | lineColor: '#000' 17 | } 18 | 19 | function _drawBarcodeText(text, canvas, opts) { 20 | let ctx = canvas.getContext('2d') 21 | let x, y 22 | 23 | y = opts.height 24 | 25 | ctx.font = `${opts.fontWeight} ${opts.fontSize}px ${opts.font}` 26 | ctx.textBaseline = 'bottom' 27 | ctx.textBaseline = 'top' 28 | 29 | if (opts.textAlign === 'left') { 30 | x = opts.quite 31 | ctx.textAlign = 'left' 32 | } else if (opts.textAlign === 'right') { 33 | x = canvas.width - opts.quite 34 | ctx.textAlign = 'right' 35 | } else { 36 | x = canvas.width / 2 37 | ctx.textAlign = 'center' 38 | } 39 | 40 | ctx.fillText(text, x, y) 41 | } 42 | 43 | 44 | function generateBarcodeDataUri(Encoding, code, opts) { 45 | /* eslint complexity:0 */ 46 | opts = Object.assign({}, defaults, opts) 47 | 48 | let canvas = new Canvas() 49 | let encoder = new Encoding(code) 50 | 51 | // Abort if the barcode format does not support the content 52 | if (!encoder.isValid()) { 53 | throw new Error('Content is not supported by the encoding') 54 | } 55 | 56 | // Encode the content 57 | let binaryString = encoder.encode() 58 | 59 | // Get the canvas context 60 | let ctx = canvas.getContext('2d') 61 | 62 | // Set the width and height of the barcode 63 | canvas.width = binaryString.length * opts.width + 2 * opts.quite 64 | 65 | // Set extra height if the value is displayed under the barcode. 66 | canvas.height = opts.height + (opts.displayValue ? opts.fontSize * 1.3 : 0) 67 | 68 | // Paint the canvas 69 | ctx.clearRect(0, 0, canvas.width, canvas.height) 70 | 71 | if (opts.backgroundColor) { 72 | ctx.fillStyle = opts.backgroundColor 73 | ctx.fillRect(0, 0, canvas.width, canvas.height) 74 | } 75 | 76 | // Change to lineColor to paint the lines 77 | ctx.fillStyle = opts.lineColor 78 | 79 | // Creates the barcode out of the binary string 80 | for (let i = 0; i < binaryString.length; i++) { 81 | let x = i * opts.width + opts.quite 82 | if (binaryString[i] === '1') { 83 | ctx.fillRect(x, 0, opts.width, opts.height) 84 | } 85 | } 86 | 87 | // Add value below if enabled 88 | if (opts.displayValue) { 89 | _drawBarcodeText(opts.customLabel || code, canvas, opts) 90 | } 91 | 92 | return canvas 93 | } 94 | 95 | /* eslint no-loop-func:0 */ 96 | for (let name in encodings) { 97 | api[name] = (...args) => generateBarcodeDataUri(encodings[name], ...args) 98 | } 99 | 100 | module.exports = api 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm package](https://img.shields.io/npm/v/io-barcode.svg?style=flat-square)](https://www.npmjs.org/package/io-barcode) 2 | [![dependency status](https://img.shields.io/david/wavded/io-barcode.svg?style=flat-square)](https://david-dm.org/wavded/io-barcode) 3 | 4 | **io-barcode** is a simple way to create different types of barcodes on server or client. 5 | 6 | This started as a fork of the [Johan Lindell's JsBarcode][1] project. It adds the following functionality: 7 | 8 | 1. Isomorphic barcode generation on client and server. 9 | 2. Node support through `node-canvas`. 10 | 3. Packaged with UMD support on client side. 11 | 4. Modular design. 12 | 5. Returns a canvas element. 13 | 6. Removed direct jQuery integration. 14 | 7. Custom label support (Instead of the encoded data string). 15 | 16 | ## Demo and examples 17 | [Barcode Generator](http://lindell.github.io/JsBarcode/) 18 | 19 | ![Samples](screenshot.png) 20 | 21 | #### Supported barcodes 22 | * CODE128 (B or C) 23 | * EAN (13) 24 | * UPC-A 25 | * CODE39 26 | * ITF (Interleaved 2 of 5) 27 | * ITF14 28 | * Pharmacode 29 | 30 | ## Installation 31 | 32 | With npm: 33 | 34 | ``` 35 | npm install io-barcode 36 | ``` 37 | 38 | If you are not using Node, browserify, webpack or similar npm-based systems, download the [minified UMD bundle](build/browser/io-barcode.min.js) for browsers only. 39 | 40 | ## Usage 41 | 42 | #### ioBarcode.TYPE(code, opts) 43 | Create a new barcode. Returns a canvas element. 44 | 45 | * `TYPE` - the type of barcode, can be: 46 | * CODE128B 47 | * CODE128C 48 | * EAN 49 | * UPC 50 | * CODE39 51 | * ITF 52 | * ITF14 53 | * Pharmacode 54 | * `code` - the string to encode 55 | * `opts` - additional formatting, default options are: 56 | 57 | ```js 58 | { 59 | width: 2, 60 | height: 100, 61 | quite: 10, 62 | displayValue: false, // Will display the encoded data as a label, or 'customLabel' if not null 63 | font: 'monospace', 64 | textAlign: 'center', 65 | fontSize: 12, 66 | fontWeight: 'bold', 67 | backgroundColor: '', 68 | lineColor: "#000", 69 | customLabel:null, // Will be displayed if displayValue is set to true 70 | } 71 | ``` 72 | 73 | Example on server side: 74 | 75 | ```js 76 | var fs = require('fs') 77 | var ioBarcode = require("io-barcode") 78 | var canvas = ioBarcode.CODE128B('Javascript is fun!', { 79 | width: 1, 80 | height: 25 81 | }) 82 | var stream = canvas.pngStream() 83 | stream.pipe(fs.createWriteStream('./barcode.png')) 84 | ``` 85 | 86 | Example on the client side: 87 | 88 | ```js 89 | // If using a require system like browserify or webpack just require it 90 | var ioBarcode = require("io-barcode") 91 | // If using UMD bundle via a