├── demos ├── barcode-generator │ ├── .gitignore │ ├── src │ │ ├── index.html │ │ ├── index.js │ │ ├── BarcodeRenderer.js │ │ ├── BarcodeInput.js │ │ ├── style.css │ │ ├── Barcode.js │ │ ├── app.js │ │ ├── FileSaver.js │ │ └── bardcode.js │ ├── package.json │ └── webpack.config.js ├── index.html ├── style.css └── demos.js ├── .gitignore ├── lib ├── index.js ├── .babelrc ├── FIM.js ├── ITF.js ├── validations.js ├── path.js ├── Codabar.js ├── svg.js ├── Code39.js ├── EAN.js ├── canvas.js ├── drawBarcode.js └── Code128.js ├── .eslintrc.json ├── CHANGELOG.md ├── LICENSE.txt ├── package.js ├── Rakefile ├── package.json ├── README.md ├── dist ├── bardcode.min.js └── bardcode.es.js └── test └── tests.js /demos/barcode-generator/.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules 3 | dist 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .npm 3 | /zbar 4 | /demos/bardcode.js 5 | /gh-pages 6 | 7 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | export { drawBarcode } from "./drawBarcode"; 2 | export { version } from "../package.json"; 3 | 4 | -------------------------------------------------------------------------------- /lib/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { modules: false }], 4 | ], 5 | "plugins": ["external-helpers"], 6 | } 7 | -------------------------------------------------------------------------------- /demos/barcode-generator/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Barcode Generator 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /demos/barcode-generator/src/index.js: -------------------------------------------------------------------------------- 1 | import "babel-polyfill"; 2 | import React from "react"; 3 | import { render } from "react-dom"; 4 | 5 | import "./index.html"; 6 | import "./style.css"; 7 | 8 | import App from "./app"; 9 | 10 | const app = document.getElementById("app"); 11 | render( 12 | , 13 | app, 14 | () => { 15 | 16 | } 17 | ); 18 | 19 | -------------------------------------------------------------------------------- /lib/FIM.js: -------------------------------------------------------------------------------- 1 | export default function encodeFIM(text) { 2 | if (!/^[ABCD]$/.test(text)) { 3 | throw new Error("FIM can only encode 'A', 'B', 'C', or 'D'"); 4 | } 5 | 6 | let bits; 7 | switch (text) { 8 | case "A": bits = "110010011"; break; 9 | case "B": bits = "101101101"; break; 10 | case "C": bits = "110101011"; break; 11 | case "D": bits = "111010111"; break; 12 | } 13 | 14 | return { 15 | type: "bits", 16 | data: [{ char: text, humanReadable: false, bits }], 17 | }; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | } 7 | }, 8 | "globals": { 9 | }, 10 | "rules": { 11 | "indent": ["error", 2], 12 | "semi": "error", 13 | "comma-dangle": ["error", "always-multiline"], 14 | "quotes": ["error", "double"], 15 | "no-console": "error", 16 | "no-var": "error", 17 | "prefer-const": "error", 18 | "no-useless-concat": "error", 19 | "prefer-template": "error" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.2.0 2020-08-31 2 | * Add $/+% to Code 39 lookup table. Thanks I P Susila! 3 | 4 | # 2.1.0 2019-05-31 5 | * Add GS1 128 type. Thanks Tom S! 6 | 7 | ## 2.0.1 - 2018-08-14 8 | * Fix EAN checksum check 9 | 10 | ## 2.0.0 - 2017-11-14 11 | * Add Path output 12 | * Make ES6 and rollup builds 13 | 14 | ## 1.2.0 - 2015-09-01 15 | * Add `options.hasChecksum`. Thank you wolfgang42! 16 | 17 | ## 1.1.3 - 2015-08-29 18 | * Fix EAN checksum bug. Thank you wolfgang42! 19 | 20 | ## 1.1.2 - 2015-03-23 21 | * Added demos 22 | 23 | ## 1.1.1 - 2015-03-21 24 | * Automated tests via zbar 25 | * Fixed Code 39 rendering 26 | 27 | ## 1.1.0 - 2015-03-10 28 | * Experimental SVG support 29 | 30 | ## 1.0.0 - 2015-02-01 31 | * Canvas only 32 | * Code 128 33 | * Codabar 34 | * Code 39 35 | * EAN-8 36 | * EAN-13 37 | * FIM 38 | * ITF (interleaved 2 of 5) 39 | * UPC-A 40 | 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015 froatsnook 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /demos/barcode-generator/src/BarcodeRenderer.js: -------------------------------------------------------------------------------- 1 | import Radium from "radium"; 2 | import React, { Component, PropTypes } from "react"; 3 | 4 | import Barcode from "./Barcode"; 5 | 6 | @Radium 7 | export default class BarcodeRenderer extends Component { 8 | static propTypes = { 9 | barcodes: PropTypes.arrayOf(PropTypes.string).isRequired, 10 | style: PropTypes.object, 11 | }; 12 | 13 | render() { 14 | const { barcodes, style } = this.props; 15 | 16 | const nonEmpty = barcodes.filter((x) => !!x); 17 | 18 | const elements = nonEmpty.map((barcode, n) => { 19 | return ( 20 | 21 | ); 22 | }); 23 | 24 | return ( 25 |
26 | {elements} 27 |
28 | ); 29 | } 30 | } 31 | 32 | const styles = { 33 | container: { 34 | display: "flex", 35 | flexDirection: "row", 36 | flexWrap: "wrap", 37 | justifyContent: "space-around", 38 | }, 39 | 40 | barcode: { 41 | margin: "8px", 42 | }, 43 | }; 44 | 45 | -------------------------------------------------------------------------------- /demos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 |
18 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: "froatsnook:bardcode", 3 | version: "1.2.0", 4 | summary: "Draw 1-D barcodes (client and server); supports code 128, 3 of 9, 2 of 5, EAN, and more", 5 | git: "https://github.com/froatsnook/bardcode" 6 | }); 7 | 8 | Package.onUse(function(api) { 9 | api.versionsFrom("1.0.3.1"); 10 | api.addFiles("lib/01-bardcode.js"); 11 | api.addFiles("lib/canvas.js"); 12 | api.addFiles("lib/Codabar.js"); 13 | api.addFiles("lib/Code128.js"); 14 | api.addFiles("lib/Code39.js"); 15 | api.addFiles("lib/drawBarcode.js"); 16 | api.addFiles("lib/EAN.js"); 17 | api.addFiles("lib/FIM.js"); 18 | api.addFiles("lib/ITF.js"); 19 | api.addFiles("lib/svg.js"); 20 | api.addFiles("lib/validations.js"); 21 | 22 | api.export("drawBarcode"); 23 | api.export("bardcode"); 24 | }); 25 | 26 | Package.onTest(function(api) { 27 | Npm.depends({ 28 | "canvas": "1.2.1" 29 | }); 30 | 31 | api.use("tinytest"); 32 | api.use("froatsnook:stream-to-buffer@1.0.1"); 33 | api.use("froatsnook:bardcode"); 34 | 35 | api.addFiles("test/tests.js"); 36 | }); 37 | 38 | -------------------------------------------------------------------------------- /lib/ITF.js: -------------------------------------------------------------------------------- 1 | const ITFData = [ 2 | [0, 0, 1, 1, 0], 3 | [1, 0, 0, 0, 1], 4 | [0, 1, 0, 0, 1], 5 | [1, 1, 0, 0, 0], 6 | [0, 0, 1, 0, 1], 7 | [1, 0, 1, 0, 0], 8 | [0, 1, 1, 0, 0], 9 | [0, 0, 0, 1, 1], 10 | [1, 0, 0, 1, 0], 11 | [0, 1, 0, 1, 0], 12 | ]; 13 | 14 | export default function encodeITF(text) { 15 | if (text.length % 2 === 1) { 16 | text = `0${text}`; 17 | } 18 | 19 | for (let n = 0, len = text.length; n < len; n++) { 20 | const ch = text[n]; 21 | if (ch < "0" || ch > "9") { 22 | throw new Error("ITF can only encode numbers."); 23 | } 24 | } 25 | 26 | const outlist = []; 27 | 28 | outlist.push({ 29 | char: "START", 30 | bits: "1010", 31 | humanReadable: false, 32 | }); 33 | 34 | for (let i = 0; i < text.length; i += 2) { 35 | const c1 = text[i]; 36 | const c2 = text[i + 1]; 37 | 38 | const n1 = c1 - "0"; 39 | const n2 = c2 - "0"; 40 | 41 | let bits = ""; 42 | for (let j = 0; j < 5; j++) { 43 | bits += ITFData[n1][j] === 0 ? "1" : "11"; 44 | bits += ITFData[n2][j] === 0 ? "0" : "00"; 45 | } 46 | 47 | outlist.push({ 48 | char: c1 + c2, 49 | bits: bits, 50 | humanReadable: true, 51 | }); 52 | } 53 | 54 | outlist.push({ 55 | char: "STOP", 56 | bits: "1101", 57 | humanReadable: false, 58 | }); 59 | 60 | return { 61 | type: "bits", 62 | data: outlist, 63 | }; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /demos/barcode-generator/src/BarcodeInput.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Barcode input, 1 per line. 3 | */ 4 | 5 | import Radium from "radium"; 6 | import React, { Component, PropTypes } from "react"; 7 | 8 | @Radium 9 | export default class BarcodeInput extends Component { 10 | static propTypes = { 11 | value: PropTypes.arrayOf(PropTypes.string).isRequired, 12 | 13 | // onChange(barcodes) 14 | // Called when the barcodes change. 15 | onChange: PropTypes.func.isRequired, 16 | 17 | // Additional styles. 18 | style: PropTypes.object, 19 | }; 20 | 21 | change = (e) => { 22 | const { onChange } = this.props; 23 | const text = e.target.value; 24 | 25 | const value = text.split("\n"); 26 | onChange(value); 27 | }; 28 | 29 | render() { 30 | const { value, style } = this.props; 31 | const text = value.join("\n"); 32 | 33 | return ( 34 |
35 | 76 | 77 | 82 |
83 | 84 | ); 85 | } 86 | }); 87 | 88 | React.render( 89 |
90 | 91 |
92 |

Introduction

93 | 94 |

By default, bardcode renders in the top left corner of the canvas.

95 | 96 | 97 | {` 98 | var w = 420, h = 200; 99 | var canvas = document.getElementById("demo-1"); 100 | var g = canvas.getContext("2d"); 101 | g.fillStyle = "white"; 102 | g.fillRect(0, 0, w, h); 103 | bardcode.drawBarcode(g, "test", { }); 104 | `} 105 | 106 | 107 |

So why is it not at the very top left? bardcode, by default, includes a quiet zone to the left and right of the barcode. Since it's left aligned, there is some space on the left. This can be configured:

108 | 109 | 110 | {` 111 | var w = 420, h = 200; 112 | var canvas = document.getElementById("demo-2"); 113 | var g = canvas.getContext("2d"); 114 | g.fillStyle = "white"; 115 | g.fillRect(0, 0, w, h); 116 | bardcode.drawBarcode(g, "test", { 117 | quietZoneSize: 0 118 | }); 119 | `} 120 | 121 | 122 |

You can align and position the barcode within the canvas however you want:

123 | 124 | 125 | {` 126 | var w = 420, h = 230; 127 | var canvas = document.getElementById("demo-3"); 128 | var g = canvas.getContext("2d"); 129 | g.fillStyle = "white"; 130 | g.fillRect(0, 0, w, h); 131 | bardcode.drawBarcode(g, "test", { 132 | x: w/2, 133 | y: h/2, 134 | horizontalAlign: "center", 135 | verticalAlign: "middle" 136 | }); 137 | `} 138 | 139 | 140 |

You might also find it useful to rotate the barcode. Use options.angle (degrees clockwise).

141 | 142 | 143 | {` 144 | var w = 420, h = 250; 145 | var canvas = document.getElementById("demo-4"); 146 | var g = canvas.getContext("2d"); 147 | g.fillStyle = "white"; 148 | g.fillRect(0, 0, w, h); 149 | bardcode.drawBarcode(g, "test", { 150 | x: w/2, 151 | y: h/2, 152 | horizontalAlign: "center", 153 | verticalAlign: "middle", 154 | angle: 90 155 | }); 156 | `} 157 | 158 | 159 |
160 | 161 |
162 |

Width and height

163 | 164 |

There are several options for controlling the width and height of rendered barcodes.

165 | 166 |

Setting the moduleWidth sets the width of the thinnest bar. The default value is 2.892.

167 | 168 |

That is, of course, unless you specify the maxWidth which will decrease the moduleWidth (if necessary) so that the barcode will fit within it (including the quiet zone unless quietZoneSize is set to 0).

169 | 170 |

However, both are ignored if the width is specfied. The moduleWidth will be set to whatever value is necessary to make the barcode (plus quiet zone unless quietZoneSize is set to 0) have the given width.

171 | 172 | 173 | {` 174 | var w = 420, h = 290; 175 | var canvas = document.getElementById("demo-5"); 176 | var g = canvas.getContext("2d"); 177 | g.fillStyle = "white"; 178 | g.fillRect(0, 0, w, h); 179 | 180 | drawGrid(g, w, h, 20); 181 | 182 | bardcode.drawBarcode(g, "test", { 183 | x: w/2, 184 | y: 0, 185 | moduleWidth: 2, 186 | quietZoneSize: 0, 187 | horizontalAlign: "center", 188 | verticalAlign: "top", 189 | height: 50 190 | }); 191 | bardcode.drawBarcode(g, "test", { 192 | x: w/2, 193 | y: 60, 194 | maxWidth: 2*w/3, 195 | horizontalAlign: "center", 196 | verticalAlign: "top", 197 | height: 50 198 | }); 199 | bardcode.drawBarcode(g, "test", { 200 | x: w/2, 201 | y: 120, 202 | maxWidth: 2*w/3, 203 | quietZoneSize: 0, 204 | horizontalAlign: "center", 205 | verticalAlign: "top", 206 | height: 50 207 | }); 208 | bardcode.drawBarcode(g, "test", { 209 | x: w/2, 210 | y: 180, 211 | width: w, 212 | horizontalAlign: "center", 213 | verticalAlign: "top", 214 | height: 50 215 | }); 216 | bardcode.drawBarcode(g, "test", { 217 | x: w/2, 218 | y: 240, 219 | width: w, 220 | quietZoneSize: 0, 221 | horizontalAlign: "center", 222 | verticalAlign: "top", 223 | height: 50 224 | }); 225 | `} 226 | 227 | 228 |
229 | 230 |
231 |

Symbologies

232 | 233 |

So far all of the barcodes have been Code-128 encoded "test". Use the second parameter to `bardcode.drawBarcode` to change the barcode text, and set options.type to use a different symbology.

234 | 235 |

The supported symbologies are:

236 |
    237 |
  • Codabar
  • 238 |
  • Code 128
  • 239 |
  • Code 39
  • 240 |
  • EAN-8
  • 241 |
  • EAN-13
  • 242 |
  • FIM
  • 243 |
  • ITF (interleaved 2 of 5)
  • 244 |
  • UPC-A
  • 245 |
246 | 247 | 248 | {` 249 | var w = 420, h = 370; 250 | var canvas = document.getElementById("demo-6"); 251 | var g = canvas.getContext("2d"); 252 | g.fillStyle = "white"; 253 | g.fillRect(0, 0, w, h); 254 | 255 | var barcodes = [ 256 | { type: "Codabar", val: "31117013206375" }, 257 | { type: "Code 128", val: "BarCode 1" }, 258 | { type: "Code 39", val: "0123456789" }, 259 | { type: "EAN-8", val: "9638507" }, 260 | { type: "EAN-13", val: "590123412345" }, 261 | { type: "FIM", val: "C" }, 262 | { type: "ITF", val: "04004" }, 263 | { type: "UPC-A", val: "90123412345" } 264 | ]; 265 | 266 | var targetHeight = (h + 5)/barcodes.length; 267 | 268 | for (var i = 0; i < barcodes.length; i++) { 269 | var barcode = barcodes[i]; 270 | bardcode.drawBarcode(g, barcode.val, { 271 | type: barcode.type, 272 | x: w/2, 273 | y: i*targetHeight, 274 | maxWidth: w, 275 | horizontalAlign: "center", 276 | verticalAlign: "top", 277 | height: targetHeight - 5 278 | }); 279 | } 280 | `} 281 | 282 | 283 |
284 |
, 285 | document.getElementById("content") 286 | ); 287 | 288 | })(); 289 | 290 | -------------------------------------------------------------------------------- /lib/Code128.js: -------------------------------------------------------------------------------- 1 | // At the moment Code 128 supports 128-B well, with basic support for 128-A 2 | // (shifting for each character which lives in A but not B). Most of the 3 | // building blocks for 128-C are included, but it's not implemented yet. 4 | // 5 | // There is also no ISO-8859-1 support (which would use FNC4). 6 | 7 | const CODE_128_VAL = 0; 8 | const CODE_128_CHAR_A = 1; 9 | const CODE_128_CHAR_B = 2; 10 | const CODE_128_CHAR_C = 3; 11 | const CODE_128_BITS = 4; 12 | 13 | // VALUE, CODE A CHAR, CODE B CHAR, CODE C CHARS, BITS 14 | const code128 = [ 15 | [0, " ", " ", "00", "11011001100"], 16 | [1, "!", "!", "01", "11001101100"], 17 | [2, "\"", "\"", "02", "11001100110"], 18 | [3, "#", "#", "03", "10010011000"], 19 | [4, "$", "$", "04", "10010001100"], 20 | [5, " %", " %", "05", "10001001100"], 21 | [6, "&", "&", "06", "10011001000"], 22 | [7, "'", "'", "07", "10011000100"], 23 | [8, "(", "(", "08", "10001100100"], 24 | [9, ")", ")", "09", "11001001000"], 25 | [10, "*", "*", "10", "11001000100"], 26 | [11, "+", "+", "11", "11000100100"], 27 | [12, ", ", ", ", "12", "10110011100"], 28 | [13, "-", "-", "13", "10011011100"], 29 | [14, ".", ".", "14", "10011001110"], 30 | [15, "/", "/", "15", "10111001100"], 31 | [16, "0", "0", "16", "10011101100"], 32 | [17, "1", "1", "17", "10011100110"], 33 | [18, "2", "2", "18", "11001110010"], 34 | [19, "3", "3", "19", "11001011100"], 35 | [20, "4", "4", "20", "11001001110"], 36 | [21, "5", "5", "21", "11011100100"], 37 | [22, "6", "6", "22", "11001110100"], 38 | [23, "7", "7", "23", "11101101110"], 39 | [24, "8", "8", "24", "11101001100"], 40 | [25, "9", "9", "25", "11100101100"], 41 | [26, ":", ":", "26", "11100100110"], 42 | [27, ";", ";", "27", "11101100100"], 43 | [28, "<", "<", "28", "11100110100"], 44 | [29, "=", "=", "29", "11100110010"], 45 | [30, ">", ">", "30", "11011011000"], 46 | [31, "?", "?", "31", "11011000110"], 47 | [32, "@", "@", "32", "11000110110"], 48 | [33, "A", "A", "33", "10100011000"], 49 | [34, "B", "B", "34", "10001011000"], 50 | [35, "C", "C", "35", "10001000110"], 51 | [36, "D", "D", "36", "10110001000"], 52 | [37, "E", "E", "37", "10001101000"], 53 | [38, "F", "F", "38", "10001100010"], 54 | [39, "G", "G", "39", "11010001000"], 55 | [40, "H", "H", "40", "11000101000"], 56 | [41, "I", "I", "41", "11000100010"], 57 | [42, "J", "J", "42", "10110111000"], 58 | [43, "K", "K", "43", "10110001110"], 59 | [44, "L", "L", "44", "10001101110"], 60 | [45, "M", "M", "45", "10111011000"], 61 | [46, "N", "N", "46", "10111000110"], 62 | [47, "O", "O", "47", "10001110110"], 63 | [48, "P", "P", "48", "11101110110"], 64 | [49, "Q", "Q", "49", "11010001110"], 65 | [50, "R", "R", "50", "11000101110"], 66 | [51, "S", "S", "51", "11011101000"], 67 | [52, "T", "T", "52", "11011100010"], 68 | [53, "U", "U", "53", "11011101110"], 69 | [54, "V", "V", "54", "11101011000"], 70 | [55, "W", "W", "55", "11101000110"], 71 | [56, "X", "X", "56", "11100010110"], 72 | [57, "Y", "Y", "57", "11101101000"], 73 | [58, "Z", "Z", "58", "11101100010"], 74 | [59, "[", "[", "59", "11100011010"], 75 | [60, "\\", "\\", "60", "11101111010"], 76 | [61, "]", "]", "61", "11001000010"], 77 | [62, "^", "^", "62", "11110001010"], 78 | [63, "_", "_", "63", "10100110000"], 79 | [64, "\0", "`", "64", "10100001100"], 80 | [65, "\x01", "a", "65", "10010110000"], 81 | [66, "\x02", "b", "66", "10010000110"], 82 | [67, "\x03", "c", "67", "10000101100"], 83 | [68, "\x04", "d", "68", "10000100110"], 84 | [69, "\x05", "e", "69", "10110010000"], 85 | [70, "\x06", "f", "70", "10110000100"], 86 | [71, "\x07", "g", "71", "10011010000"], 87 | [72, "\b", "h", "72", "10011000010"], 88 | [73, "\x01", "i", "73", "10000110100"], 89 | [74, "\n", "j", "74", "10000110010"], 90 | [75, "\v", "k", "75", "11000010010"], 91 | [76, "\f", "l", "76", "11001010000"], 92 | [77, "\r", "m", "77", "11110111010"], 93 | [78, "\x08", "n", "78", "11000010100"], 94 | [79, "\x09", "o", "79", "10001111010"], 95 | [80, "\x10", "p", "80", "10100111100"], 96 | [81, "\x11", "q", "81", "10010111100"], 97 | [82, "\x12", "r", "82", "10010011110"], 98 | [83, "\x13", "s", "83", "10111100100"], 99 | [84, "\x14", "t", "84", "10011110100"], 100 | [85, "\x15", "u", "85", "10011110010"], 101 | [86, "\x16", "v", "86", "11110100100"], 102 | [87, "\x17", "w", "87", "11110010100"], 103 | [88, "\x18", "x", "88", "11110010010"], 104 | [89, "\x19", "y", "89", "11011011110"], 105 | [90, "\x1a", "z", "90", "11011110110"], 106 | [91, "\x1b", "{", "91", "11110110110"], 107 | [92, "\x1c", "|", "92", "10101111000"], 108 | [93, "\x1d", "}", "93", "10100011110"], 109 | [94, "\x1e", "~", "94", "10001011110"], 110 | [95, "\x1f", "\x7f", "95", "10111101000"], 111 | [96, "FNC 3", "FNC 3", "96", "10111100010"], 112 | [97, "FNC 2", "FNC 2", "97", "11110101000"], 113 | [98, "SHIFT B", "SHIFT A", "98", "11110100010"], 114 | [99, "CODE C", "CODE C", "99", "10111011110"], 115 | [100, "CODE B", "FNC 4", "CODE B", "10111101110"], 116 | [101, "FNC 4", "CODE A", "CODE A", "11101011110"], 117 | [102, "FNC 1", "FNC 1", "FNC 1", "11110101110"], 118 | [103, "A0", "A0", "A0", "11010000100"], 119 | [104, "B0", "B0", "B0", "11010010000"], 120 | [105, "C0", "C0", "C0", "11010011100"], 121 | [106, "STOP", "STOP", "STOP", "1100011101011"], 122 | ]; 123 | 124 | // Reverse lookups from first 4 columns. Created on first use. 125 | let code128ValLookup = null; 126 | let code128ALookup = null; 127 | let code128BLookup = null; 128 | let code128CLookup = null; 129 | 130 | const makeCode128Lookups = function() { 131 | if (code128ValLookup) { 132 | return; 133 | } 134 | 135 | code128ValLookup = { }; 136 | code128ALookup = { }; 137 | code128BLookup = { }; 138 | code128CLookup = { }; 139 | 140 | for (let i = 0; i < code128.length; i++) { 141 | const data = code128[i]; 142 | 143 | const val = data[CODE_128_VAL]; 144 | const charA = data[CODE_128_CHAR_A]; 145 | const charB = data[CODE_128_CHAR_B]; 146 | const charC = data[CODE_128_CHAR_C]; 147 | 148 | code128ValLookup[val] = data; 149 | code128ALookup[charA] = data; 150 | code128BLookup[charB] = data; 151 | code128CLookup[charC] = data; 152 | } 153 | }; 154 | 155 | export default function encodeCode128(text) { 156 | makeCode128Lookups(); 157 | 158 | const chars = new Array(1 + text.length); 159 | chars[0] = "B0"; 160 | for (let i = 0, len = text.length; i < len; i++) { 161 | chars[1 + i] = text[i]; 162 | } 163 | 164 | // Basic support for Code 128-A: do shift A before characters which live in 165 | // A but not in B. 166 | for (let i = 0; i < chars.length; i++) { 167 | const ch = chars[i]; 168 | if (code128ALookup[ch] && !code128BLookup[ch]) { 169 | chars.splice(i, 0, "SHIFT A"); 170 | i++; 171 | } 172 | } 173 | 174 | // Main thing we return is a list of characters. 175 | // [{ bits: "1011011", mode: "A", value: 44, char: "L", humanReadable: true }, ...] 176 | const outlist = []; 177 | 178 | let mode; 179 | switch (chars[0]) { 180 | case "A0": mode = "A"; break; 181 | case "B0": mode = "B"; break; 182 | case "C0": mode = "C"; break; 183 | default: throw new Error("Expected a starting character"); 184 | } 185 | 186 | // If "SHIFT A" is a character in chars, then shift to mode A for one 187 | // character, and then switch back to returnMode. 188 | let returnMode; 189 | 190 | let sum = 0; 191 | for (let i = 0; i < chars.length; i++) { 192 | const ch = chars[i]; 193 | 194 | // The weight value depends on what mode we're in. 195 | let data; 196 | switch (mode) { 197 | case "A": 198 | data = code128ALookup[ch]; 199 | break; 200 | case "B": 201 | data = code128BLookup[ch]; 202 | break; 203 | case "C": 204 | data = code128CLookup[ch]; 205 | break; 206 | } 207 | 208 | // Throw an error if the character does not exist in this mode. 209 | if (!data) { 210 | throw new Error(`Invalid input (no such char '${ch}' in mode ${mode})`); 211 | } 212 | 213 | const val = data[CODE_128_VAL]; 214 | const bits = data[CODE_128_BITS]; 215 | 216 | // Contribute to sum. 217 | const n = i || 1; // both start code and first text char have position 1. 218 | sum += n * val; 219 | 220 | outlist.push({ 221 | bits: bits, 222 | char: ch, 223 | humanReadable: null, 224 | _mode: mode, 225 | _val: val, 226 | }); 227 | 228 | // Return to previous mode after a shift. 229 | if (returnMode) { 230 | mode = returnMode; 231 | returnMode = null; 232 | } 233 | 234 | // Handle mode switches. 235 | switch (ch) { 236 | case "CODE A": mode = "A"; break; 237 | case "CODE B": mode = "B"; break; 238 | case "CODE C": mode = "C"; break; 239 | case "SHIFT A": returnMode = mode; mode = "A"; break; 240 | case "SHIFT B": returnMode = mode; mode = "B"; break; 241 | 242 | default: 243 | // Do nothing for non-mode switching characters. 244 | break; 245 | } 246 | } 247 | 248 | const checksum = sum % 103; 249 | 250 | // Append the checksum. 251 | const checksumData = code128ValLookup[checksum]; 252 | outlist.push({ 253 | bits: checksumData[CODE_128_BITS], 254 | char: "CHECKSUM", 255 | humanReadable: false, 256 | _val: checksum, 257 | }); 258 | 259 | // Append the stop char. 260 | const stopData = code128ALookup.STOP; 261 | outlist.push({ 262 | bits: stopData[CODE_128_BITS], 263 | char: "STOP", 264 | humanReadable: false, 265 | _val: stopData[CODE_128_VAL], 266 | }); 267 | 268 | return { 269 | type: "bits", 270 | checksum: checksum, 271 | data: outlist, 272 | }; 273 | } 274 | 275 | -------------------------------------------------------------------------------- /demos/barcode-generator/src/FileSaver.js: -------------------------------------------------------------------------------- 1 | /* FileSaver.js 2 | * A saveAs() FileSaver implementation. 3 | * 1.1.20160328 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * License: MIT 7 | * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 8 | */ 9 | 10 | /*global self */ 11 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 12 | 13 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 14 | 15 | var saveAs = saveAs || (function(view) { 16 | "use strict"; 17 | // IE <10 is explicitly unsupported 18 | if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { 19 | return; 20 | } 21 | var 22 | doc = view.document 23 | // only get URL when necessary in case Blob.js hasn't overridden it yet 24 | , get_URL = function() { 25 | return view.URL || view.webkitURL || view; 26 | } 27 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 28 | , can_use_save_link = "download" in save_link 29 | , click = function(node) { 30 | var event = new MouseEvent("click"); 31 | node.dispatchEvent(event); 32 | } 33 | , is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent) 34 | , webkit_req_fs = view.webkitRequestFileSystem 35 | , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem 36 | , throw_outside = function(ex) { 37 | (view.setImmediate || view.setTimeout)(function() { 38 | throw ex; 39 | }, 0); 40 | } 41 | , force_saveable_type = "application/octet-stream" 42 | , fs_min_size = 0 43 | // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to 44 | , arbitrary_revoke_timeout = 1000 * 40 // in ms 45 | , revoke = function(file) { 46 | var revoker = function() { 47 | if (typeof file === "string") { // file is an object URL 48 | get_URL().revokeObjectURL(file); 49 | } else { // file is a File 50 | file.remove(); 51 | } 52 | }; 53 | /* // Take note W3C: 54 | var 55 | uri = typeof file === "string" ? file : file.toURL() 56 | , revoker = function(evt) { 57 | // idealy DownloadFinishedEvent.data would be the URL requested 58 | if (evt.data === uri) { 59 | if (typeof file === "string") { // file is an object URL 60 | get_URL().revokeObjectURL(file); 61 | } else { // file is a File 62 | file.remove(); 63 | } 64 | } 65 | } 66 | ; 67 | view.addEventListener("downloadfinished", revoker); 68 | */ 69 | setTimeout(revoker, arbitrary_revoke_timeout); 70 | } 71 | , dispatch = function(filesaver, event_types, event) { 72 | event_types = [].concat(event_types); 73 | var i = event_types.length; 74 | while (i--) { 75 | var listener = filesaver["on" + event_types[i]]; 76 | if (typeof listener === "function") { 77 | try { 78 | listener.call(filesaver, event || filesaver); 79 | } catch (ex) { 80 | throw_outside(ex); 81 | } 82 | } 83 | } 84 | } 85 | , auto_bom = function(blob) { 86 | // prepend BOM for UTF-8 XML and text/* types (including HTML) 87 | if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 88 | return new Blob(["\ufeff", blob], {type: blob.type}); 89 | } 90 | return blob; 91 | } 92 | , FileSaver = function(blob, name, no_auto_bom) { 93 | if (!no_auto_bom) { 94 | blob = auto_bom(blob); 95 | } 96 | // First try a.download, then web filesystem, then object URLs 97 | var 98 | filesaver = this 99 | , type = blob.type 100 | , blob_changed = false 101 | , object_url 102 | , target_view 103 | , dispatch_all = function() { 104 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 105 | } 106 | // on any filesys errors revert to saving with object URLs 107 | , fs_error = function() { 108 | if (target_view && is_safari && typeof FileReader !== "undefined") { 109 | // Safari doesn't allow downloading of blob urls 110 | var reader = new FileReader(); 111 | reader.onloadend = function() { 112 | var base64Data = reader.result; 113 | target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/)); 114 | filesaver.readyState = filesaver.DONE; 115 | dispatch_all(); 116 | }; 117 | reader.readAsDataURL(blob); 118 | filesaver.readyState = filesaver.INIT; 119 | return; 120 | } 121 | // don't create more object URLs than needed 122 | if (blob_changed || !object_url) { 123 | object_url = get_URL().createObjectURL(blob); 124 | } 125 | if (target_view) { 126 | target_view.location.href = object_url; 127 | } else { 128 | var new_tab = view.open(object_url, "_blank"); 129 | if (new_tab === undefined && is_safari) { 130 | //Apple do not allow window.open, see http://bit.ly/1kZffRI 131 | view.location.href = object_url 132 | } 133 | } 134 | filesaver.readyState = filesaver.DONE; 135 | dispatch_all(); 136 | revoke(object_url); 137 | } 138 | , abortable = function(func) { 139 | return function() { 140 | if (filesaver.readyState !== filesaver.DONE) { 141 | return func.apply(this, arguments); 142 | } 143 | }; 144 | } 145 | , create_if_not_found = {create: true, exclusive: false} 146 | , slice 147 | ; 148 | filesaver.readyState = filesaver.INIT; 149 | if (!name) { 150 | name = "download"; 151 | } 152 | if (can_use_save_link) { 153 | object_url = get_URL().createObjectURL(blob); 154 | setTimeout(function() { 155 | save_link.href = object_url; 156 | save_link.download = name; 157 | click(save_link); 158 | dispatch_all(); 159 | revoke(object_url); 160 | filesaver.readyState = filesaver.DONE; 161 | }); 162 | return; 163 | } 164 | // Object and web filesystem URLs have a problem saving in Google Chrome when 165 | // viewed in a tab, so I force save with application/octet-stream 166 | // http://code.google.com/p/chromium/issues/detail?id=91158 167 | // Update: Google errantly closed 91158, I submitted it again: 168 | // https://code.google.com/p/chromium/issues/detail?id=389642 169 | if (view.chrome && type && type !== force_saveable_type) { 170 | slice = blob.slice || blob.webkitSlice; 171 | blob = slice.call(blob, 0, blob.size, force_saveable_type); 172 | blob_changed = true; 173 | } 174 | // Since I can't be sure that the guessed media type will trigger a download 175 | // in WebKit, I append .download to the filename. 176 | // https://bugs.webkit.org/show_bug.cgi?id=65440 177 | if (webkit_req_fs && name !== "download") { 178 | name += ".download"; 179 | } 180 | if (type === force_saveable_type || webkit_req_fs) { 181 | target_view = view; 182 | } 183 | if (!req_fs) { 184 | fs_error(); 185 | return; 186 | } 187 | fs_min_size += blob.size; 188 | req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { 189 | fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { 190 | var save = function() { 191 | dir.getFile(name, create_if_not_found, abortable(function(file) { 192 | file.createWriter(abortable(function(writer) { 193 | writer.onwriteend = function(event) { 194 | target_view.location.href = file.toURL(); 195 | filesaver.readyState = filesaver.DONE; 196 | dispatch(filesaver, "writeend", event); 197 | revoke(file); 198 | }; 199 | writer.onerror = function() { 200 | var error = writer.error; 201 | if (error.code !== error.ABORT_ERR) { 202 | fs_error(); 203 | } 204 | }; 205 | "writestart progress write abort".split(" ").forEach(function(event) { 206 | writer["on" + event] = filesaver["on" + event]; 207 | }); 208 | writer.write(blob); 209 | filesaver.abort = function() { 210 | writer.abort(); 211 | filesaver.readyState = filesaver.DONE; 212 | }; 213 | filesaver.readyState = filesaver.WRITING; 214 | }), fs_error); 215 | }), fs_error); 216 | }; 217 | dir.getFile(name, {create: false}, abortable(function(file) { 218 | // delete file if it already exists 219 | file.remove(); 220 | save(); 221 | }), abortable(function(ex) { 222 | if (ex.code === ex.NOT_FOUND_ERR) { 223 | save(); 224 | } else { 225 | fs_error(); 226 | } 227 | })); 228 | }), fs_error); 229 | }), fs_error); 230 | } 231 | , FS_proto = FileSaver.prototype 232 | , saveAs = function(blob, name, no_auto_bom) { 233 | return new FileSaver(blob, name, no_auto_bom); 234 | } 235 | ; 236 | // IE 10+ (native saveAs) 237 | if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { 238 | return function(blob, name, no_auto_bom) { 239 | if (!no_auto_bom) { 240 | blob = auto_bom(blob); 241 | } 242 | return navigator.msSaveOrOpenBlob(blob, name || "download"); 243 | }; 244 | } 245 | 246 | FS_proto.abort = function() { 247 | var filesaver = this; 248 | filesaver.readyState = filesaver.DONE; 249 | dispatch(filesaver, "abort"); 250 | }; 251 | FS_proto.readyState = FS_proto.INIT = 0; 252 | FS_proto.WRITING = 1; 253 | FS_proto.DONE = 2; 254 | 255 | FS_proto.error = 256 | FS_proto.onwritestart = 257 | FS_proto.onprogress = 258 | FS_proto.onwrite = 259 | FS_proto.onabort = 260 | FS_proto.onerror = 261 | FS_proto.onwriteend = 262 | null; 263 | 264 | return saveAs; 265 | }( 266 | typeof self !== "undefined" && self 267 | || typeof window !== "undefined" && window 268 | || this.content 269 | )); 270 | // `self` is undefined in Firefox for Android content script context 271 | // while `this` is nsIContentFrameMessageManager 272 | // with an attribute `content` that corresponds to the window 273 | 274 | if (typeof module !== "undefined" && module.exports) { 275 | module.exports.saveAs = saveAs; 276 | } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) { 277 | define([], function() { 278 | return saveAs; 279 | }); 280 | } -------------------------------------------------------------------------------- /dist/bardcode.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.bardcode={})}(this,function(e){"use strict";function t(e,t){if("number"!=typeof e)throw new Error("Expected "+t+" to be a number, got "+e)}function r(e,t){if("number"!=typeof e)throw new Error("Expected "+t+" to be a number, got "+e);if(e<=0)throw new Error("Expected "+t+" to be positive, got "+e)}function a(e){b();var t=new Array(1+e.length);t[0]="B0";for(var r=0,a=e.length;r",">","30","11011011000"],[31,"?","?","31","11011000110"],[32,"@","@","32","11000110110"],[33,"A","A","33","10100011000"],[34,"B","B","34","10001011000"],[35,"C","C","35","10001000110"],[36,"D","D","36","10110001000"],[37,"E","E","37","10001101000"],[38,"F","F","38","10001100010"],[39,"G","G","39","11010001000"],[40,"H","H","40","11000101000"],[41,"I","I","41","11000100010"],[42,"J","J","42","10110111000"],[43,"K","K","43","10110001110"],[44,"L","L","44","10001101110"],[45,"M","M","45","10111011000"],[46,"N","N","46","10111000110"],[47,"O","O","47","10001110110"],[48,"P","P","48","11101110110"],[49,"Q","Q","49","11010001110"],[50,"R","R","50","11000101110"],[51,"S","S","51","11011101000"],[52,"T","T","52","11011100010"],[53,"U","U","53","11011101110"],[54,"V","V","54","11101011000"],[55,"W","W","55","11101000110"],[56,"X","X","56","11100010110"],[57,"Y","Y","57","11101101000"],[58,"Z","Z","58","11101100010"],[59,"[","[","59","11100011010"],[60,"\\","\\","60","11101111010"],[61,"]","]","61","11001000010"],[62,"^","^","62","11110001010"],[63,"_","_","63","10100110000"],[64,"\0","`","64","10100001100"],[65,"","a","65","10010110000"],[66,"","b","66","10010000110"],[67,"","c","67","10000101100"],[68,"","d","68","10000100110"],[69,"","e","69","10110010000"],[70,"","f","70","10110000100"],[71,"","g","71","10011010000"],[72,"\b","h","72","10011000010"],[73,"","i","73","10000110100"],[74,"\n","j","74","10000110010"],[75,"\v","k","75","11000010010"],[76,"\f","l","76","11001010000"],[77,"\r","m","77","11110111010"],[78,"\b","n","78","11000010100"],[79,"\t","o","79","10001111010"],[80,"","p","80","10100111100"],[81,"","q","81","10010111100"],[82,"","r","82","10010011110"],[83,"","s","83","10111100100"],[84,"","t","84","10011110100"],[85,"","u","85","10011110010"],[86,"","v","86","11110100100"],[87,"","w","87","11110010100"],[88,"","x","88","11110010010"],[89,"","y","89","11011011110"],[90,"","z","90","11011110110"],[91,"","{","91","11110110110"],[92,"","|","92","10101111000"],[93,"","}","93","10100011110"],[94,"","~","94","10001011110"],[95,"","","95","10111101000"],[96,"FNC 3","FNC 3","96","10111100010"],[97,"FNC 2","FNC 2","97","11110101000"],[98,"SHIFT B","SHIFT A","98","11110100010"],[99,"CODE C","CODE C","99","10111011110"],[100,"CODE B","FNC 4","CODE B","10111101110"],[101,"FNC 4","CODE A","CODE A","11101011110"],[102,"FNC 1","FNC 1","FNC 1","11110101110"],[103,"A0","A0","A0","11010000100"],[104,"B0","B0","B0","11010010000"],[105,"C0","C0","C0","11010011100"],[106,"STOP","STOP","STOP","1100011101011"]],c=null,u=null,l=null,d=null,b=function(){if(!c){c={},u={},l={},d={};for(var e=0;e"9")throw new Error("ITF can only encode numbers.")}var n=[];n.push({char:"START",bits:"1010",humanReadable:!1});for(var o=0;oe.maxWidth&&(n=e.maxWidth/a):n=e.width/a;for(var o=e.height,i=[],h=e.quietZoneSize*n,s=0;se.maxWidth&&(n=(o=e.maxWidth)/a):n=(o=e.width)/a;var i=e.height,h=[],s=["xmlns='http://www.w3.org/2000/svg'","width='"+o.toFixed(3)+"'","height='"+i+"'","fill='black'"].join(" ");h.push("");for(var c=e.quietZoneSize*n,u=0;u"),c+=d}else{for(var f=1;u"),h.join("\n")}(t,r):function(e,t,r){var a=r.data.map(function(e){return e.bits}).join("");e.save();var n=a.length+2*t.quietZoneSize,o=void 0,i=void 0;isNaN(t.width)?(i=n*(o=t.moduleWidth))>t.maxWidth&&(o=(i=t.maxWidth)/n):o=(i=t.width)/n;var h=t.height;e.translate(t.x,t.y);var s=t.angle*Math.PI/180,c=Math.cos(s),u=Math.sin(s),l=[0,i*c,i*c-h*u,-h*u],d=[0,i*u,i*u+h*c,h*c],b=Math.min.apply(this,l),f=Math.min.apply(this,d),p=Math.max.apply(this,l),v=Math.max.apply(this,d);switch(t.horizontalAlign){case"left":e.translate(-b,0);break;case"center":e.translate(-(i/2*c-h/2*u),0);break;case"right":e.translate(-p,0)}switch(t.verticalAlign){case"top":e.translate(0,-f);break;case"middle":e.translate(0,-(i/2*u+h/2*c));break;case"bottom":e.translate(0,-v)}e.rotate(s),e.translate(t.quietZoneSize*o,0),e.fillStyle="black";for(var R=0;R= 0); 166 | test.isTrue(lines.indexOf("CODE-128:b") >= 0); 167 | test.isTrue(lines.indexOf("CODE-128:c") >= 0); 168 | test.isTrue(lines.indexOf("CODE-128:d") >= 0); 169 | test.isTrue(lines.indexOf("CODE-128:e") >= 0); 170 | test.isTrue(lines.indexOf("CODE-128:test") >= 0); 171 | }); 172 | 173 | Tinytest.add("Code 128 - test SHIFT A", function(test) { 174 | var canvas = new Canvas(300, 100); 175 | var g = canvas.getContext("2d"); 176 | g.fillStyle = "white"; 177 | g.fillRect(0, 0, 300, 100); 178 | drawBarcode(g, "Test\nThree", { 179 | maxWidth: 300, 180 | height: 100, 181 | quietZoneSize: 10 182 | }); 183 | 184 | var zbarOutput = getBarcodes(canvas.toBuffer()); 185 | test.equal(zbarOutput.code, 0); 186 | var output = zbarOutput.stdout.trim(); 187 | var lines = output.split("\n"); 188 | test.equal(lines.length, 2); 189 | test.equal(lines[0], "CODE-128:Test"); 190 | test.equal(lines[1], "Three"); 191 | }); 192 | 193 | Tinytest.add("ITF - Odd Char Count", function(test) { 194 | var canvas = new Canvas(300, 100); 195 | var g = canvas.getContext("2d"); 196 | g.fillStyle = "white"; 197 | g.fillRect(0, 0, 300, 100); 198 | drawBarcode(g, "04004", { 199 | type: "ITF", 200 | maxWidth: 300, 201 | height: 100, 202 | quietZoneSize: 10 203 | }); 204 | 205 | var zbarOutput = getBarcodes(canvas.toBuffer()); 206 | test.equal(zbarOutput.code, 0); 207 | var output = zbarOutput.stdout.trim(); 208 | var lines = output.split("\n"); 209 | test.equal(lines.length, 1); 210 | test.equal(lines[0], "I2/5:004004"); 211 | }); 212 | 213 | Tinytest.add("ITF - Even Char Count", function(test) { 214 | var canvas = new Canvas(300, 100); 215 | var g = canvas.getContext("2d"); 216 | g.fillStyle = "white"; 217 | g.fillRect(0, 0, 300, 100); 218 | drawBarcode(g, "11223344", { 219 | type: "ITF", 220 | maxWidth: 300, 221 | height: 100, 222 | quietZoneSize: 10 223 | }); 224 | 225 | var zbarOutput = getBarcodes(canvas.toBuffer()); 226 | test.equal(zbarOutput.code, 0); 227 | var output = zbarOutput.stdout.trim(); 228 | var lines = output.split("\n"); 229 | test.equal(lines.length, 1); 230 | test.equal(lines[0], "I2/5:11223344"); 231 | }); 232 | 233 | Tinytest.add("Code 39 - Characters", function(test) { 234 | var canvas = new Canvas(1000, 1000); 235 | var g = canvas.getContext("2d"); 236 | 237 | g.fillStyle = "white"; 238 | g.fillRect(0, 0, 400, 550); 239 | 240 | drawBarcode(g, "0123456789", { 241 | type: "Code 39", 242 | x: 0, 243 | y: 0, 244 | maxWidth: 400, 245 | height: 100, 246 | quietZoneSize: 10 247 | }); 248 | 249 | drawBarcode(g, "ABCDEFGHIJ", { 250 | type: "Code 39", 251 | x: 0, 252 | y: 150, 253 | maxWidth: 400, 254 | height: 100, 255 | quietZoneSize: 10 256 | }); 257 | 258 | drawBarcode(g, "KLMNOPQRST", { 259 | type: "Code 39", 260 | x: 0, 261 | y: 300, 262 | maxWidth: 400, 263 | height: 100, 264 | quietZoneSize: 10 265 | }); 266 | 267 | drawBarcode(g, "UVW -.XYZ", { 268 | type: "Code 39", 269 | x: 0, 270 | y: 450, 271 | maxWidth: 400, 272 | height: 100, 273 | quietZoneSize: 10 274 | }); 275 | 276 | var zbarOutput = getBarcodes(canvas.toBuffer()); 277 | test.equal(zbarOutput.code, 0); 278 | var output = zbarOutput.stdout.trim(); 279 | var lines = output.split("\n"); 280 | test.equal(lines.length, 4); 281 | 282 | test.isTrue(lines.indexOf("CODE-39:0123456789") >= 0); 283 | test.isTrue(lines.indexOf("CODE-39:ABCDEFGHIJ") >= 0); 284 | test.isTrue(lines.indexOf("CODE-39:KLMNOPQRST") >= 0); 285 | test.isTrue(lines.indexOf("CODE-39:UVW -.XYZ") >= 0); 286 | }); 287 | 288 | Tinytest.add("Code 39 - WIKIPEDIA", function(test) { 289 | var canvas = new Canvas(1000, 1000); 290 | var g = canvas.getContext("2d"); 291 | 292 | g.fillStyle = "white"; 293 | g.fillRect(0, 0, 800, 300); 294 | 295 | drawBarcode(g, "WIKIPEDIA", { 296 | type: "Code 39", 297 | x: 0, 298 | y: 0, 299 | width: 800, 300 | height: 300, 301 | quietZoneSize: 10 302 | }); 303 | 304 | var zbarOutput = getBarcodes(canvas.toBuffer()); 305 | test.equal(zbarOutput.code, 0); 306 | var output = zbarOutput.stdout.trim(); 307 | var lines = output.split("\n"); 308 | test.equal(lines.length, 1); 309 | 310 | test.isTrue(lines.indexOf("CODE-39:WIKIPEDIA") >= 0); 311 | }); 312 | 313 | Tinytest.add("EAN - checksum(\"400638133393\") == 1", function(test) { 314 | var encodeData = bardcode.encodeEAN("400638133393"); 315 | test.equal(encodeData.checksum, 1); 316 | }); 317 | 318 | Tinytest.add("EAN - checksum(\"846823000342\") == 0", function(test) { 319 | var encodeData = bardcode.encodeEAN("846823000342"); 320 | test.equal(encodeData.checksum, 0); 321 | }); 322 | 323 | Tinytest.add("EAN - valid checksum(\"846823000342\")", function(test) { 324 | var encodeData = bardcode.encodeEAN("846823000342", true); 325 | test.equal(encodeData.checksum, 2); 326 | }); 327 | 328 | Tinytest.add("EAN - invalid checksum(\"846823000344\")", function(test) { 329 | test.throws( 330 | function(){ bardcode.encodeEAN("846823000344", true); }, 331 | "Invalid checksum." 332 | ); 333 | }); 334 | 335 | Tinytest.add("EAN - checksum(\"9638507\") == 4", function(test) { 336 | var encodeData = bardcode.encodeEAN("9638507"); 337 | test.equal(encodeData.checksum, 4); 338 | }); 339 | 340 | Tinytest.add("EAN - UPC-A, precalculated checksum", function(test) { 341 | var canvas = new Canvas(400, 100); 342 | var g = canvas.getContext("2d"); 343 | g.fillStyle = "white"; 344 | g.fillRect(0, 0, 400, 100); 345 | drawBarcode(g, "846823000342", { 346 | type: "UPC-A", 347 | hasChecksum: true, 348 | maxWidth: 400, 349 | height: 100, 350 | quietZoneSize: 10 351 | }); 352 | 353 | var zbarOutput = getBarcodes(canvas.toBuffer()); 354 | test.equal(zbarOutput.code, 0); 355 | var output = zbarOutput.stdout.trim(); 356 | var lines = output.split("\n"); 357 | test.equal(lines.length, 1); 358 | // UPC-A is EAN-13 with a leading zero 359 | test.equal(lines[0], "EAN-13:0846823000342"); 360 | }); 361 | 362 | Tinytest.add("EAN - UPC-A", function(test) { 363 | var canvas = new Canvas(400, 100); 364 | var g = canvas.getContext("2d"); 365 | g.fillStyle = "white"; 366 | g.fillRect(0, 0, 400, 100); 367 | drawBarcode(g, "84682300034", { 368 | type: "UPC-A", 369 | maxWidth: 400, 370 | height: 100, 371 | quietZoneSize: 10 372 | }); 373 | 374 | var zbarOutput = getBarcodes(canvas.toBuffer()); 375 | test.equal(zbarOutput.code, 0); 376 | var output = zbarOutput.stdout.trim(); 377 | var lines = output.split("\n"); 378 | test.equal(lines.length, 1); 379 | // UPC-A is EAN-13 with a leading zero 380 | test.equal(lines[0], "EAN-13:0846823000342"); 381 | }); 382 | 383 | Tinytest.add("EAN - EAN-13", function(test) { 384 | var canvas = new Canvas(400, 100); 385 | var g = canvas.getContext("2d"); 386 | g.fillStyle = "white"; 387 | g.fillRect(0, 0, 400, 100); 388 | drawBarcode(g, "590123412345", { 389 | type: "EAN-13", 390 | maxWidth: 400, 391 | height: 100, 392 | quietZoneSize: 10 393 | }); 394 | 395 | var zbarOutput = getBarcodes(canvas.toBuffer()); 396 | test.equal(zbarOutput.code, 0); 397 | var output = zbarOutput.stdout.trim(); 398 | var lines = output.split("\n"); 399 | test.equal(lines.length, 1); 400 | test.equal(lines[0], "EAN-13:5901234123457"); 401 | }); 402 | 403 | Tinytest.add("EAN - EAN-8", function(test) { 404 | var canvas = new Canvas(400, 100); 405 | var g = canvas.getContext("2d"); 406 | g.fillStyle = "white"; 407 | g.fillRect(0, 0, 400, 100); 408 | drawBarcode(g, "9638507", { 409 | type: "EAN-8", 410 | maxWidth: 400, 411 | height: 100, 412 | quietZoneSize: 10 413 | }); 414 | 415 | var zbarOutput = getBarcodes(canvas.toBuffer()); 416 | test.equal(zbarOutput.code, 0); 417 | var output = zbarOutput.stdout.trim(); 418 | var lines = output.split("\n"); 419 | test.equal(lines.length, 1); 420 | test.equal(lines[0], "EAN-8:96385074"); 421 | }); 422 | 423 | Tinytest.add("FIM - test no crash", function(test) { 424 | var canvas = new Canvas(400, 100); 425 | var g = canvas.getContext("2d"); 426 | g.fillStyle = "white"; 427 | g.fillRect(0, 0, 400, 100); 428 | drawBarcode(g, "C", { 429 | type: "FIM", 430 | maxWidth: 400, 431 | height: 100, 432 | moduleWidth: 2, 433 | quietZoneSize: 10 434 | }); 435 | }); 436 | 437 | Tinytest.add("Codabar - test no crash", function(test) { 438 | var canvas = new Canvas(400, 100); 439 | var g = canvas.getContext("2d"); 440 | g.fillStyle = "white"; 441 | g.fillRect(0, 0, 400, 100); 442 | drawBarcode(g, "31117013206375", { 443 | type: "Codabar", 444 | maxWidth: 400, 445 | height: 100, 446 | quietZoneSize: 10 447 | }); 448 | }); 449 | 450 | Tinytest.add("SVG - test", function(test) { 451 | var svg = drawBarcode("svg", "3117820", { 452 | maxWidth: 400, 453 | height: 100, 454 | quietZoneSize: 8 455 | }); 456 | 457 | test.equal(svg.indexOf("", ">", "30", "11011011000"], 140 | [31, "?", "?", "31", "11011000110"], 141 | [32, "@", "@", "32", "11000110110"], 142 | [33, "A", "A", "33", "10100011000"], 143 | [34, "B", "B", "34", "10001011000"], 144 | [35, "C", "C", "35", "10001000110"], 145 | [36, "D", "D", "36", "10110001000"], 146 | [37, "E", "E", "37", "10001101000"], 147 | [38, "F", "F", "38", "10001100010"], 148 | [39, "G", "G", "39", "11010001000"], 149 | [40, "H", "H", "40", "11000101000"], 150 | [41, "I", "I", "41", "11000100010"], 151 | [42, "J", "J", "42", "10110111000"], 152 | [43, "K", "K", "43", "10110001110"], 153 | [44, "L", "L", "44", "10001101110"], 154 | [45, "M", "M", "45", "10111011000"], 155 | [46, "N", "N", "46", "10111000110"], 156 | [47, "O", "O", "47", "10001110110"], 157 | [48, "P", "P", "48", "11101110110"], 158 | [49, "Q", "Q", "49", "11010001110"], 159 | [50, "R", "R", "50", "11000101110"], 160 | [51, "S", "S", "51", "11011101000"], 161 | [52, "T", "T", "52", "11011100010"], 162 | [53, "U", "U", "53", "11011101110"], 163 | [54, "V", "V", "54", "11101011000"], 164 | [55, "W", "W", "55", "11101000110"], 165 | [56, "X", "X", "56", "11100010110"], 166 | [57, "Y", "Y", "57", "11101101000"], 167 | [58, "Z", "Z", "58", "11101100010"], 168 | [59, "[", "[", "59", "11100011010"], 169 | [60, "\\", "\\", "60", "11101111010"], 170 | [61, "]", "]", "61", "11001000010"], 171 | [62, "^", "^", "62", "11110001010"], 172 | [63, "_", "_", "63", "10100110000"], 173 | [64, "\0", "`", "64", "10100001100"], 174 | [65, "\x01", "a", "65", "10010110000"], 175 | [66, "\x02", "b", "66", "10010000110"], 176 | [67, "\x03", "c", "67", "10000101100"], 177 | [68, "\x04", "d", "68", "10000100110"], 178 | [69, "\x05", "e", "69", "10110010000"], 179 | [70, "\x06", "f", "70", "10110000100"], 180 | [71, "\x07", "g", "71", "10011010000"], 181 | [72, "\b", "h", "72", "10011000010"], 182 | [73, "\x01", "i", "73", "10000110100"], 183 | [74, "\n", "j", "74", "10000110010"], 184 | [75, "\v", "k", "75", "11000010010"], 185 | [76, "\f", "l", "76", "11001010000"], 186 | [77, "\r", "m", "77", "11110111010"], 187 | [78, "\x08", "n", "78", "11000010100"], 188 | [79, "\x09", "o", "79", "10001111010"], 189 | [80, "\x10", "p", "80", "10100111100"], 190 | [81, "\x11", "q", "81", "10010111100"], 191 | [82, "\x12", "r", "82", "10010011110"], 192 | [83, "\x13", "s", "83", "10111100100"], 193 | [84, "\x14", "t", "84", "10011110100"], 194 | [85, "\x15", "u", "85", "10011110010"], 195 | [86, "\x16", "v", "86", "11110100100"], 196 | [87, "\x17", "w", "87", "11110010100"], 197 | [88, "\x18", "x", "88", "11110010010"], 198 | [89, "\x19", "y", "89", "11011011110"], 199 | [90, "\x1a", "z", "90", "11011110110"], 200 | [91, "\x1b", "{", "91", "11110110110"], 201 | [92, "\x1c", "|", "92", "10101111000"], 202 | [93, "\x1d", "}", "93", "10100011110"], 203 | [94, "\x1e", "~", "94", "10001011110"], 204 | [95, "\x1f", "\x7f", "95", "10111101000"], 205 | [96, "FNC 3", "FNC 3", "96", "10111100010"], 206 | [97, "FNC 2", "FNC 2", "97", "11110101000"], 207 | [98, "SHIFT B", "SHIFT A", "98", "11110100010"], 208 | [99, "CODE C", "CODE C", "99", "10111011110"], 209 | [100, "CODE B", "FNC 4", "CODE B", "10111101110"], 210 | [101, "FNC 4", "CODE A", "CODE A", "11101011110"], 211 | [102, "FNC 1", "FNC 1", "FNC 1", "11110101110"], 212 | [103, "A0", "A0", "A0", "11010000100"], 213 | [104, "B0", "B0", "B0", "11010010000"], 214 | [105, "C0", "C0", "C0", "11010011100"], 215 | [106, "STOP", "STOP", "STOP", "1100011101011"] 216 | ]; 217 | 218 | // Reverse lookups from first 4 columns. Created on first use. 219 | var code128ValLookup = null; 220 | var code128ALookup = null; 221 | var code128BLookup = null; 222 | var code128CLookup = null; 223 | 224 | var makeCode128Lookups = function() { 225 | if (code128ValLookup) { 226 | return; 227 | } 228 | 229 | code128ValLookup = { }; 230 | code128ALookup = { }; 231 | code128BLookup = { }; 232 | code128CLookup = { }; 233 | 234 | for (var i = 0; i < code128.length; i++) { 235 | var data = code128[i]; 236 | 237 | var val = data[CODE_128_VAL]; 238 | var charA = data[CODE_128_CHAR_A]; 239 | var charB = data[CODE_128_CHAR_B]; 240 | var charC = data[CODE_128_CHAR_C]; 241 | 242 | code128ValLookup[val] = data; 243 | code128ALookup[charA] = data; 244 | code128BLookup[charB] = data; 245 | code128CLookup[charC] = data; 246 | } 247 | }; 248 | 249 | bardcode.encodeCode128 = function(text) { 250 | makeCode128Lookups(); 251 | 252 | var chars = new Array(1 + text.length); 253 | chars[0] = "B0"; 254 | for (var i = 0, len = text.length; i < len; i++) { 255 | chars[1 + i] = text[i]; 256 | } 257 | 258 | // Basic support for Code 128-A: do shift A before characters which live in 259 | // A but not in B. 260 | for (var i = 0; i < chars.length; i++) { 261 | var ch = chars[i]; 262 | if (code128ALookup[ch] && !code128BLookup[ch]) { 263 | chars.splice(i, 0, "SHIFT A"); 264 | i++; 265 | } 266 | } 267 | 268 | // Main thing we return is a list of characters. 269 | // [{ bits: "1011011", mode: "A", value: 44, char: "L", humanReadable: true }, ...] 270 | var outlist = []; 271 | 272 | var mode; 273 | switch (chars[0]) { 274 | case "A0": mode = "A"; break; 275 | case "B0": mode = "B"; break; 276 | case "C0": mode = "C"; break; 277 | default: throw new Error("Expected a starting character"); 278 | } 279 | 280 | // If "SHIFT A" is a character in chars, then shift to mode A for one 281 | // character, and then switch back to returnMode. 282 | var returnMode; 283 | 284 | var sum = 0; 285 | for (var i = 0; i < chars.length; i++) { 286 | var ch = chars[i]; 287 | 288 | // The weight value depends on what mode we're in. 289 | var data; 290 | switch (mode) { 291 | case "A": 292 | data = code128ALookup[ch]; 293 | break; 294 | case "B": 295 | data = code128BLookup[ch]; 296 | break; 297 | case "C": 298 | data = code128CLookup[ch]; 299 | break; 300 | } 301 | 302 | // Throw an error if the character does not exist in this mode. 303 | if (!data) { 304 | throw new Error("Invalid input (no such char '" + ch + "' in mode " + mode); 305 | } 306 | 307 | var val = data[CODE_128_VAL]; 308 | var bits = data[CODE_128_BITS]; 309 | 310 | // Contribute to sum. 311 | var n = i || 1; // both start code and first text char have position 1. 312 | sum += n * val; 313 | 314 | outlist.push({ 315 | bits: bits, 316 | char: ch, 317 | humanReadable: null, 318 | _mode: mode, 319 | _val: val 320 | }); 321 | 322 | // Return to previous mode after a shift. 323 | if (returnMode) { 324 | mode = returnMode; 325 | returnMode = null; 326 | } 327 | 328 | // Handle mode switches. 329 | switch (ch) { 330 | case "CODE A": mode = "A"; break; 331 | case "CODE B": mode = "B"; break; 332 | case "CODE C": mode = "C"; break; 333 | case "SHIFT A": returnMode = mode; mode = "A"; break; 334 | case "SHIFT B": returnMode = mode; mode = "B"; break; 335 | 336 | default: 337 | // Do nothing for non-mode switching characters. 338 | break; 339 | } 340 | } 341 | 342 | var checksum = sum % 103; 343 | 344 | // Append the checksum. 345 | var checksumData = code128ValLookup[checksum]; 346 | outlist.push({ 347 | bits: checksumData[CODE_128_BITS], 348 | char: "CHECKSUM", 349 | humanReadable: false, 350 | _val: checksum 351 | }); 352 | 353 | // Append the stop char. 354 | var stopData = code128ALookup.STOP; 355 | outlist.push({ 356 | bits: stopData[CODE_128_BITS], 357 | char: "STOP", 358 | humanReadable: false, 359 | _val: stopData[CODE_128_VAL] 360 | }); 361 | 362 | return { 363 | type: "bits", 364 | checksum: checksum, 365 | data: outlist 366 | }; 367 | }; 368 | 369 | var CODE_39_CHAR = 0; 370 | var CODE_39_CHECKSUM_VAL = 1; 371 | var CODE_39_BITS = 2; 372 | 373 | var code39Data = [ 374 | ["1", 1, "110100101011"], 375 | ["2", 2, "101100101011"], 376 | ["3", 3, "110110010101"], 377 | ["4", 4, "101001101011"], 378 | ["5", 5, "110100110101"], 379 | ["6", 6, "101100110101"], 380 | ["7", 7, "101001011011"], 381 | ["8", 8, "110100101101"], 382 | ["9", 9, "101100101101"], 383 | ["0", 0, "101001101101"], 384 | ["A", 10, "110101001011"], 385 | ["B", 11, "101101001011"], 386 | ["C", 12, "110110100101"], 387 | ["D", 13, "101011001011"], 388 | ["E", 14, "110101100101"], 389 | ["F", 15, "101101100101"], 390 | ["G", 16, "101010011011"], 391 | ["H", 17, "110101001101"], 392 | ["I", 18, "101101001101"], 393 | ["J", 19, "101011001101"], 394 | ["K", 20, "110101010011"], 395 | ["L", 21, "101101010011"], 396 | ["M", 22, "110110101001"], 397 | ["N", 23, "101011010011"], 398 | ["O", 24, "110101101001"], 399 | ["P", 25, "101101101001"], 400 | ["Q", 26, "101010110011"], 401 | ["R", 27, "110101011001"], 402 | ["S", 28, "101101011001"], 403 | ["T", 29, "101011011001"], 404 | ["U", 30, "110010101011"], 405 | ["V", 31, "100110101011"], 406 | ["W", 32, "110011010101"], 407 | ["X", 33, "100101101011"], 408 | ["Y", 34, "110010110101"], 409 | ["Z", 35, "100110110101"], 410 | ["-", 36, "100101011011"], 411 | [".", 37, "110010101101"], 412 | [" ", 38, "100110101101"], 413 | ["*", NaN, "100101101101"] 414 | ]; 415 | 416 | var code39Lookup = null; 417 | 418 | var makeCode39Lookups = function() { 419 | if (code39Lookup) { 420 | return; 421 | } 422 | 423 | code39Lookup = { }; 424 | 425 | code39Data.forEach(function(row) { 426 | var ch = row[CODE_39_CHAR]; 427 | code39Lookup[ch] = row; 428 | }); 429 | }; 430 | 431 | // @param [withChecksum] {Boolean} If true, then add the mod 43 checksum. Defaults to false. 432 | bardcode.encodeCode39 = function(text, withChecksum) { 433 | makeCode39Lookups(); 434 | 435 | // @todo implement 436 | withChecksum = withChecksum || false; 437 | 438 | if (!text) { 439 | text = ""; 440 | } 441 | 442 | text = "*" + text + "*"; 443 | 444 | var outlist = []; 445 | 446 | for (var i = 0; i < text.length; i++) { 447 | if (i !== 0) { 448 | outlist.push({ 449 | char: "", 450 | bits: "0", 451 | humanReadable: false 452 | }); 453 | } 454 | 455 | var ch = text[i]; 456 | var row = code39Lookup[ch]; 457 | if (!row) { 458 | throw new Error("Cannot encode code 39 barcode: invalid char: " + ch); 459 | } 460 | 461 | var bits = row[CODE_39_BITS]; 462 | outlist.push({ 463 | char: ch, 464 | bits: bits, 465 | humanReadable: true 466 | }); 467 | } 468 | 469 | return { 470 | type: "bits", 471 | data: outlist 472 | }; 473 | }; 474 | 475 | var EAN_L = 0; 476 | var EAN_G = 1; 477 | var EAN_R = 2; 478 | 479 | var eanData = [ 480 | ["0001101", "0100111", "1110010"], 481 | ["0011001", "0110011", "1100110"], 482 | ["0010011", "0011011", "1101100"], 483 | ["0111101", "0100001", "1000010"], 484 | ["0100011", "0011101", "1011100"], 485 | ["0110001", "0111001", "1001110"], 486 | ["0101111", "0000101", "1010000"], 487 | ["0111011", "0010001", "1000100"], 488 | ["0110111", "0001001", "1001000"], 489 | ["0001011", "0010111", "1110100"] 490 | ]; 491 | 492 | bardcode.encodeEAN = function(text) { 493 | if (!/^\d+$/.test(text)) { 494 | throw new Error("EAN can only encode numbers."); 495 | } 496 | 497 | var len = text.length; 498 | var sum = 0; 499 | for (var i = 0; i < len; i++) { 500 | var ch = text[i]; 501 | var n = ch - "0"; 502 | var weight = (len - i) % 2 === 1 ? 3 : 1; 503 | sum += weight * n; 504 | } 505 | 506 | var checksum = 10 - sum % 10; 507 | text += checksum; 508 | 509 | var outlist = []; 510 | 511 | var encoding; 512 | switch (text.length) { 513 | case 8: 514 | encoding = "LLLLRRRR"; 515 | break; 516 | case 12: 517 | // UPC-A is just like EAN-13 with first digit 0. 518 | encoding = "LLLLLLRRRRRR"; 519 | break; 520 | case 13: 521 | switch (text[0]) { 522 | case "0": encoding = "LLLLLLRRRRRR"; break; 523 | case "1": encoding = "LLGLGGRRRRRR"; break; 524 | case "2": encoding = "LLGGLGRRRRRR"; break; 525 | case "3": encoding = "LLGGGLRRRRRR"; break; 526 | case "4": encoding = "LGLLGGRRRRRR"; break; 527 | case "5": encoding = "LGGLLGRRRRRR"; break; 528 | case "6": encoding = "LGGGLLRRRRRR"; break; 529 | case "7": encoding = "LGLGLGRRRRRR"; break; 530 | case "8": encoding = "LGLGGLRRRRRR"; break; 531 | case "9": encoding = "LGGLGLRRRRRR"; break; 532 | } 533 | 534 | outlist.push({ 535 | char: text[0], 536 | humanReadable: true, 537 | bits: "" 538 | }); 539 | 540 | text = text.slice(1); 541 | break; 542 | default: 543 | throw new Error("Don't know how to make EAN with that length."); 544 | } 545 | 546 | outlist.push({ 547 | char: "START", 548 | humanReadable: false, 549 | bits: "101" 550 | }); 551 | 552 | for (var i = 0; i < text.length; i++) { 553 | if (i === text.length / 2) { 554 | outlist.push({ 555 | char: "CENTER", 556 | humanReadable: false, 557 | bits: "01010" 558 | }); 559 | } 560 | 561 | var digit = text[i] - "0"; 562 | var type = encoding[i]; 563 | 564 | var index = type === "L" ? EAN_L : type === "G" ? EAN_G : EAN_R; 565 | var bitpattern = eanData[digit][index]; 566 | outlist.push({ 567 | char: text[i], 568 | humanReadable: true, 569 | bits: bitpattern 570 | }); 571 | } 572 | 573 | outlist.push({ 574 | char: "END", 575 | humanReadable: false, 576 | bits: "101" 577 | }); 578 | 579 | return { 580 | type: "bits", 581 | checksum: checksum, 582 | data: outlist 583 | }; 584 | }; 585 | 586 | bardcode.encodeFIM = function(text) { 587 | if (!/^[ABCD]$/.test(text)) { 588 | throw new Error("FIM can only encode 'A', 'B', 'C', or 'D'"); 589 | } 590 | 591 | var bits; 592 | 593 | switch (text) { 594 | case "A": bits = "110010011"; break; 595 | case "B": bits = "101101101"; break; 596 | case "C": bits = "110101011"; break; 597 | case "D": bits = "111010111"; break; 598 | } 599 | 600 | return { 601 | type: "bits", 602 | data: [{ char: text, humanReadable: false, bits: bits }] 603 | }; 604 | }; 605 | 606 | var ITFData = [ 607 | [0, 0, 1, 1, 0], 608 | [1, 0, 0, 0, 1], 609 | [0, 1, 0, 0, 1], 610 | [1, 1, 0, 0, 0], 611 | [0, 0, 1, 0, 1], 612 | [1, 0, 1, 0, 0], 613 | [0, 1, 1, 0, 0], 614 | [0, 0, 0, 1, 1], 615 | [1, 0, 0, 1, 0], 616 | [0, 1, 0, 1, 0] 617 | ]; 618 | 619 | bardcode.encodeITF = function(text) { 620 | if (text.length % 2 === 1) { 621 | text = "0" + text; 622 | } 623 | 624 | for (var n = 0, len = text.length; n < len; n++) { 625 | var ch = text[n]; 626 | if (ch < "0" || ch > "9") { 627 | throw new Error("ITF can only encode numbers."); 628 | } 629 | } 630 | 631 | var outlist = []; 632 | 633 | outlist.push({ 634 | char: "START", 635 | bits: "1010", 636 | humanReadable: false 637 | }); 638 | 639 | for (var i = 0; i < text.length; i += 2) { 640 | var c1 = text[i]; 641 | var c2 = text[i + 1]; 642 | 643 | var n1 = c1 - "0"; 644 | var n2 = c2 - "0"; 645 | 646 | var bits = ""; 647 | 648 | for (var j = 0; j < 5; j++) { 649 | bits += ITFData[n1][j] === 0 ? "1" : "11"; 650 | bits += ITFData[n2][j] === 0 ? "0" : "00"; 651 | } 652 | 653 | outlist.push({ 654 | char: c1 + c2, 655 | bits: bits, 656 | humanReadable: true 657 | }); 658 | } 659 | 660 | outlist.push({ 661 | char: "STOP", 662 | bits: "1101", 663 | humanReadable: false 664 | }); 665 | 666 | return { 667 | type: "bits", 668 | data: outlist 669 | }; 670 | }; 671 | 672 | bardcode.drawBitsBarcodeToCanvas = function(g, options, encodeData) { 673 | var bits = encodeData.data.map(function(d) { 674 | return d.bits; 675 | }).join(""); 676 | 677 | g.save(); 678 | 679 | // First transform so that no matter the x, y, horizontalAlign and 680 | // verticalAlign, we draw from the left at 0,0. 681 | 682 | var bw; 683 | var width; 684 | 685 | var multiplier = (bits.length + 2 * options.quietZoneSize); 686 | 687 | if (!isNaN(options.width)) { 688 | // options.width takes precedence... if given, then it overrides 689 | // moduleWidth and maxWidth 690 | width = options.width; 691 | bw = width / multiplier; 692 | } else { 693 | // Try to use the given moduleWidth 694 | bw = options.moduleWidth; 695 | width = multiplier * bw; 696 | 697 | // But adjust if it doesn't fit in maxWidth (if given) 698 | if (width > options.maxWidth) { 699 | width = options.maxWidth; 700 | bw = width / multiplier; 701 | } 702 | } 703 | 704 | var height = options.height; 705 | 706 | // Translate to barcode start. 707 | g.translate(options.x, options.y); 708 | 709 | var rad = options.angle * Math.PI / 180; 710 | var cos = Math.cos(rad); 711 | var sin = Math.sin(rad); 712 | 713 | // Compute all positions relative to the point (x, y) in the unrotated 714 | // coordinate system. Using min and max values, we can figure out how much 715 | // we need to translate to make the desired alignment. 716 | // 717 | // 0,0 __________ w,0 718 | // | | 719 | // |__________| 720 | // 0,h w,h 721 | // 722 | // To compute the new positions, multiply by the 2d multiplication matrix: 723 | // 724 | // [cos(a) -sin(a)] * [0] = [0] 725 | // [sin(a) cos(a)] [0] [0] 726 | // 727 | // [cos(a) -sin(a)] * [w] = [w*cos(a)] 728 | // [sin(a) cos(a)] [0] [w*sin(a)] 729 | // 730 | // [cos(a) -sin(a)] * [w] = [w*cos(a)-h*sin(a)] 731 | // [sin(a) cos(a)] [h] [w*sin(a)+h*cos(a)] 732 | // 733 | // [cos(a) -sin(a)] * [0] = [-h*sin(a)] 734 | // [sin(a) cos(a)] [h] [ h*cos(a)] 735 | // 736 | // For centering, compute the rectangle's center's position in the same 737 | // way: 738 | // 739 | // [cos(a) -sin(a)] * [w/2] = [w/2*cos(a)-h/2*sin(a)] 740 | // [sin(a) cos(a)] [h/2] [w/2*sin(a)+h/2*cos(a)] 741 | var xs = [0, width * cos, width * cos - height * sin, -height * sin]; 742 | var ys = [0, width * sin, width * sin + height * cos, height * cos]; 743 | 744 | var xmin = Math.min.apply(this, xs); 745 | var ymin = Math.min.apply(this, ys); 746 | var xmax = Math.max.apply(this, xs); 747 | var ymax = Math.max.apply(this, ys); 748 | 749 | switch (options.horizontalAlign) { 750 | case "left": 751 | g.translate(-xmin, 0); 752 | break; 753 | case "center": 754 | g.translate(-(width / 2 * cos - height / 2 * sin), 0); 755 | break; 756 | case "right": 757 | g.translate(-xmax, 0); 758 | break; 759 | } 760 | 761 | switch (options.verticalAlign) { 762 | case "top": 763 | g.translate(0, -ymin); 764 | break; 765 | case "middle": 766 | g.translate(0, -(width / 2 * sin + height / 2 * cos)); 767 | break; 768 | case "bottom": 769 | g.translate(0, -ymax); 770 | break; 771 | } 772 | 773 | // Rotate. 774 | g.rotate(rad); 775 | 776 | // Skip quiet zone... 777 | g.translate(options.quietZoneSize * bw, 0); 778 | 779 | g.fillStyle = "black"; 780 | 781 | var n = 0; 782 | while (n < bits.length) { 783 | // We are at the start of a bar or a space. 784 | var bit = bits[n]; 785 | if (bit === "1") { 786 | // We are at a bar. 787 | var barCount = 1; 788 | while (n < bits.length && bits[++n] === "1") { 789 | barCount++; 790 | } 791 | 792 | var barWidth = barCount * bw; 793 | g.fillRect(0, 0, barWidth, height); 794 | g.translate(barWidth, 0); 795 | } else { 796 | // We are at a space. 797 | var spaceCount = 1; 798 | while (n < bits.length && bits[++n] === "0") { 799 | spaceCount++; 800 | } 801 | 802 | var spaceWidth = spaceCount * bw; 803 | g.translate(spaceWidth, 0); 804 | } 805 | } 806 | 807 | g.restore(); 808 | 809 | return { 810 | barcodeWidth: width, 811 | barcodeHeight: height, 812 | bbox: { 813 | x: xmin, 814 | y: ymin, 815 | width: xmax - xmin, 816 | height: ymax - ymin 817 | } 818 | }; 819 | }; 820 | 821 | var optionDefaults = { 822 | type: "Code 128", 823 | x: 0, 824 | y: 0, 825 | moduleWidth: 2.892, 826 | height: 90.72, 827 | horizontalAlign: "left", 828 | verticalAlign: "top", 829 | quietZoneSize: 10, 830 | angle: 0, 831 | maxWidth: Infinity, 832 | width: NaN 833 | }; 834 | 835 | // Copy default values from source to target if they don't exist. 836 | var copyDefaults = function(target, source) { 837 | for (var key in source) { 838 | if (typeof target[key] === "undefined") { 839 | target[key] = source[key]; 840 | } 841 | } 842 | }; 843 | 844 | /** 845 | * @summary Draw a barcode to a canvas graphics context. 846 | * @todo IMB, Pharmacode, PostBar, POSTNET, Telepen 847 | * @param {Context2D|String} g An HTML5 or node-canvas graphics context or the output format. The only supported non-canvas output format is "svg". 848 | * @param {String} text Barcode text (without start, end, or check characters). 849 | * @param {Object} options Controls what barcode is drawn, where, and how. 850 | * @param {String} options.type Barcode type. Defaults to Code 128. Other valid options are "Codabar", "Code 39", "EAN-8", "EAN-13", "FIM", "ITF" (interleaved 2 of 5), and "UPC-A". 851 | * @param {Number} options.x Where to draw barcode. Defaults to 0. 852 | * @param {Number} options.y Where to draw the barcode. Defaults to 0. 853 | * @param {String} options.horizontalAlign How to align the barcode. Defaults to "left". Other options are "center" and "right". 854 | * @param {String} options.verticalAlign How to align the barcode. Defaults to "top". Other options are "middle" and "bottom". 855 | * @param {Number} options.height Barcode height. Defaults to 90.72. 856 | * @param {Number} options.moduleWidth Width of thinnest bar. Defaults to 2.892. 857 | * @param {Number} options.quietZoneSize Number of moduleWidths in quiet zone on either side. Defaults to 10. 858 | * @param {Number} options.angle Rotate barcode this many degrees clockwise. Defaults to 0. 859 | * @param {Number} options.maxWidth Maximum barcode width (including quiet zones). If specified, then the moduleWidth will be adjusted if necessary to make the entire barcode fit in the given width. 860 | * @param {Number} options.width If given, then ignore moduleWidth and maxWidth and set the moduleWidth so that the barcode will have the given width. 861 | */ 862 | drawBarcode = bardcode.drawBarcode = function(g, text, options) { 863 | // Validate input. 864 | if (typeof g !== "object" && g !== "svg") { 865 | throw new Error("drawBarcode: expected `g' to be an object or 'svg'."); 866 | } 867 | 868 | if (!text || typeof text !== "string") { 869 | throw new Error("drawBarcode: expected `text' to be a non-empty string."); 870 | } 871 | 872 | if (typeof options !== "object") { 873 | options = { }; 874 | } 875 | 876 | copyDefaults(options, optionDefaults); 877 | bardcode.validateDrawBarcodeOptions(options); 878 | 879 | var encodeData; 880 | switch (options.type) { 881 | case "Codabar": 882 | encodeData = bardcode.encodeCodabar(text); 883 | break; 884 | case "Code 128": 885 | encodeData = bardcode.encodeCode128(text); 886 | break; 887 | case "Code 39": 888 | encodeData = bardcode.encodeCode39(text); 889 | break; 890 | case "ITF": 891 | encodeData = bardcode.encodeITF(text); 892 | break; 893 | case "EAN-8": 894 | encodeData = bardcode.encodeEAN(text); 895 | break; 896 | case "EAN-13": 897 | encodeData = bardcode.encodeEAN(text); 898 | break; 899 | case "FIM": 900 | encodeData = bardcode.encodeFIM(text); 901 | break; 902 | case "UPC-A": 903 | encodeData = bardcode.encodeEAN(text); 904 | break; 905 | } 906 | 907 | switch (encodeData.type) { 908 | case "bits": 909 | return bardcode.drawBitsBarcode(g, options, encodeData); 910 | default: 911 | throw new Error("Unrecognized encoded barcode type: " + encodeData.type); 912 | } 913 | }; 914 | 915 | bardcode.validateDrawBarcodeOptions = function validateDrawBarcodeOptions(options) { 916 | bardcode.assertIsNumber(options.x, "options.x"); 917 | bardcode.assertIsNumber(options.y, "options.y"); 918 | bardcode.assertIsValidHorizontalAlign(options.horizontalAlign); 919 | bardcode.assertIsValidVerticalAlign(options.verticalAlign); 920 | bardcode.assertIsPositiveNumber(options.height, "options.height"); 921 | bardcode.assertIsPositiveNumber(options.moduleWidth, "options.moduleWidth"); 922 | bardcode.assertIsNonNegativeNumber(options.quietZoneSize, "options.quietZoneSize"); 923 | bardcode.assertIsNumber(options.angle, "options.angle"); 924 | bardcode.assertIsPositiveNumber(options.maxWidth, "options.maxWidth"); 925 | 926 | // width can either be NaN or a positive number. 927 | if (!isNaN(options.width)) { 928 | bardcode.assertIsPositiveNumber(options.width, "options.width"); 929 | } 930 | }; 931 | 932 | bardcode.drawBitsBarcode = function(g, options, encodeData) { 933 | if (g === "svg") { 934 | return bardcode.drawBitsBarcodeToSVG(options, encodeData); 935 | } else { 936 | return bardcode.drawBitsBarcodeToCanvas(g, options, encodeData); 937 | } 938 | }; 939 | 940 | bardcode.drawBitsBarcodeToSVG = function(options, encodeData) { 941 | var bits = encodeData.data.map(function(d) { 942 | return d.bits; 943 | }).join(""); 944 | 945 | var bw; 946 | var width; 947 | 948 | var multiplier = (bits.length + 2 * options.quietZoneSize); 949 | 950 | if (!isNaN(options.width)) { 951 | // options.width takes precedence... if given, then it overrides 952 | // moduleWidth and maxWidth 953 | width = options.width; 954 | bw = width / multiplier; 955 | } else { 956 | // Try to use the given moduleWidth 957 | bw = options.moduleWidth; 958 | width = multiplier * bw; 959 | 960 | // But adjust if it doesn't fit in maxWidth (if given) 961 | if (width > options.maxWidth) { 962 | width = options.maxWidth; 963 | bw = width / multiplier; 964 | } 965 | } 966 | 967 | var height = options.height; 968 | 969 | var svgLines = []; 970 | svgLines.push(""); 976 | 977 | // Walk xpos from left to right side. 978 | var xpos = options.quietZoneSize * bw; 979 | 980 | var n = 0; 981 | while (n < bits.length) { 982 | // We are at the start of a bar or a space. 983 | var bit = bits[n]; 984 | if (bit === "1") { 985 | // We are at a bar. 986 | var barCount = 1; 987 | while (n < bits.length && bits[++n] === "1") { 988 | barCount++; 989 | } 990 | 991 | var barWidth = barCount * bw; 992 | svgLines.push(""); 998 | xpos += barWidth; 999 | } else { 1000 | // We are at a space. 1001 | var spaceCount = 1; 1002 | while (n < bits.length && bits[++n] === "0") { 1003 | spaceCount++; 1004 | } 1005 | 1006 | var spaceWidth = spaceCount * bw; 1007 | xpos += spaceWidth; 1008 | } 1009 | } 1010 | 1011 | svgLines.push(""); 1012 | return svgLines.join("\n"); 1013 | }; 1014 | 1015 | 1016 | bardcode.assertIsString = function(x, name) { 1017 | if (typeof x !== "string") { 1018 | throw new Error("Expected " + name + " to be a string, got " + x); 1019 | } 1020 | }; 1021 | 1022 | bardcode.assertIsNonEmptyString = function(x) { 1023 | if (typeof x !== "string" || x.length === 0) { 1024 | throw new Error("Expected a non-empty string, got " + x); 1025 | } 1026 | }; 1027 | 1028 | bardcode.assertIsValidHorizontalAlign = function(x) { 1029 | switch (x) { 1030 | case "left": return; 1031 | case "center": return; 1032 | case "right": return; 1033 | default: throw new Error("Unexpected horizontalAlign (acceptable values are \"left\", \"center\", and \"right\"); got " + x); 1034 | } 1035 | }; 1036 | 1037 | bardcode.assertIsValidVerticalAlign = function(x) { 1038 | switch (x) { 1039 | case "top": return; 1040 | case "middle": return; 1041 | case "bottom": return; 1042 | default: throw new Error("Unexpected verticalAlign (acceptable values are \"top\", \"middle\", and \"bottom\"); got " + x); 1043 | } 1044 | }; 1045 | 1046 | bardcode.assertIsNumber = function(x, name) { 1047 | if (typeof x !== "number") { 1048 | throw new Error("Expected " + name + " to be a number, got " + x); 1049 | } 1050 | }; 1051 | 1052 | bardcode.assertIsPositiveNumber = function(x, name) { 1053 | if (typeof x !== "number") { 1054 | throw new Error("Expected " + name + " to be a number, got " + x); 1055 | } 1056 | 1057 | if (x <= 0) { 1058 | throw new Error("Expected " + name + " to be positive, got " + x); 1059 | } 1060 | }; 1061 | 1062 | bardcode.assertIsNonNegativeNumber = function(x, name) { 1063 | if (typeof x !== "number") { 1064 | throw new Error("Expected " + name + " to be a number, got " + x); 1065 | } 1066 | 1067 | if (x < 0) { 1068 | throw new Error("Expected " + name + " to be positive, got " + x); 1069 | } 1070 | }; 1071 | 1072 | export default drawBarcode; 1073 | 1074 | -------------------------------------------------------------------------------- /dist/bardcode.es.js: -------------------------------------------------------------------------------- 1 | /* 2 | * bardcode (c) 2016-2017 froatsnook 3 | */ 4 | function assertIsValidHorizontalAlign(x) { 5 | switch (x) { 6 | case "left": 7 | return; 8 | case "center": 9 | return; 10 | case "right": 11 | return; 12 | default: 13 | throw new Error("Unexpected horizontalAlign (acceptable values are \"left\", \"center\", and \"right\"); got " + x); 14 | } 15 | } 16 | 17 | function assertIsValidVerticalAlign(x) { 18 | switch (x) { 19 | case "top": 20 | return; 21 | case "middle": 22 | return; 23 | case "bottom": 24 | return; 25 | default: 26 | throw new Error("Unexpected verticalAlign (acceptable values are \"top\", \"middle\", and \"bottom\"); got " + x); 27 | } 28 | } 29 | 30 | function assertIsNumber(x, name) { 31 | if (typeof x !== "number") { 32 | throw new Error("Expected " + name + " to be a number, got " + x); 33 | } 34 | } 35 | 36 | function assertIsBoolean(x, name) { 37 | if (typeof x !== "boolean") { 38 | throw new Error("Expected " + name + " to be a boolean, got " + x); 39 | } 40 | } 41 | 42 | function assertIsPositiveNumber(x, name) { 43 | if (typeof x !== "number") { 44 | throw new Error("Expected " + name + " to be a number, got " + x); 45 | } 46 | 47 | if (x <= 0) { 48 | throw new Error("Expected " + name + " to be positive, got " + x); 49 | } 50 | } 51 | 52 | function assertIsNonNegativeNumber(x, name) { 53 | if (typeof x !== "number") { 54 | throw new Error("Expected " + name + " to be a number, got " + x); 55 | } 56 | 57 | if (x < 0) { 58 | throw new Error("Expected " + name + " to be non-negative, got " + x); 59 | } 60 | } 61 | 62 | var codabarData = { 63 | "0": "1010100110", 64 | "1": "1010110010", 65 | "2": "1010010110", 66 | "3": "1100101010", 67 | "4": "1011010010", 68 | "5": "1101010010", 69 | "6": "1001010110", 70 | "7": "1001011010", 71 | "8": "1001101010", 72 | "9": "1101001010", 73 | "-": "1010011010", 74 | "$": "1011001010", 75 | ".": "11011011010", 76 | "/": "11011010110", 77 | ":": "11010110110", 78 | "+": "10110110110" 79 | }; 80 | 81 | var codabarStartsAndStops = { 82 | "C": "10100100110", 83 | "*": "10100100110", 84 | "B": "10010010110", 85 | "N": "10010010110", 86 | "D": "10100110010", 87 | "E": "10100110010", 88 | "A": "10110010010", 89 | "T": "10110010010" 90 | }; 91 | 92 | function encodeCodabar(text) { 93 | if (!/^[C\*BNDEAT]?[-:0-9\$\.\/\+]*[C\*BNDEAT]?$/.test(text)) { 94 | throw new Error("Cannot encode \"" + text + "\" in codabar."); 95 | } 96 | 97 | if (text.length === 0) { 98 | text = "AA"; 99 | } 100 | 101 | var firstIsStart = !!codabarStartsAndStops[text[0]]; 102 | var lastIsStop = !!codabarStartsAndStops[text[text.length - 1]]; 103 | 104 | if (text.length === 1 && firstIsStart) { 105 | throw new Error("Cannot encode \"" + text + "\" as codabar: it's just a start/stop character"); 106 | } 107 | 108 | if (firstIsStart ^ lastIsStop) { 109 | throw new Error("Cannot encode \"" + text + "\" as codabar: must give both start and stop characters or neither start nor stop"); 110 | } 111 | 112 | var outlist = []; 113 | 114 | // If it doesn't have start and stop symbols, just use "A". 115 | if (!firstIsStart) { 116 | text = "A" + text + "A"; 117 | } 118 | 119 | for (var i = 0; i < text.length; i++) { 120 | var ch = text[i]; 121 | 122 | if (i === 0 || i === text.length - 1) { 123 | outlist.push({ 124 | char: ch, 125 | humanReadable: false, 126 | bits: codabarStartsAndStops[ch] 127 | }); 128 | continue; 129 | } 130 | 131 | outlist.push({ 132 | char: ch, 133 | humanReadable: true, 134 | bits: codabarData[ch] 135 | }); 136 | } 137 | 138 | return { 139 | type: "bits", 140 | data: outlist 141 | }; 142 | } 143 | 144 | // At the moment Code 128 supports 128-B well, with basic support for 128-A 145 | // (shifting for each character which lives in A but not B). Most of the 146 | // building blocks for 128-C are included, but it's not implemented yet. 147 | // 148 | // There is also no ISO-8859-1 support (which would use FNC4). 149 | 150 | var CODE_128_VAL = 0; 151 | var CODE_128_CHAR_A = 1; 152 | var CODE_128_CHAR_B = 2; 153 | var CODE_128_CHAR_C = 3; 154 | var CODE_128_BITS = 4; 155 | 156 | // VALUE, CODE A CHAR, CODE B CHAR, CODE C CHARS, BITS 157 | var code128 = [[0, " ", " ", "00", "11011001100"], [1, "!", "!", "01", "11001101100"], [2, "\"", "\"", "02", "11001100110"], [3, "#", "#", "03", "10010011000"], [4, "$", "$", "04", "10010001100"], [5, " %", " %", "05", "10001001100"], [6, "&", "&", "06", "10011001000"], [7, "'", "'", "07", "10011000100"], [8, "(", "(", "08", "10001100100"], [9, ")", ")", "09", "11001001000"], [10, "*", "*", "10", "11001000100"], [11, "+", "+", "11", "11000100100"], [12, ", ", ", ", "12", "10110011100"], [13, "-", "-", "13", "10011011100"], [14, ".", ".", "14", "10011001110"], [15, "/", "/", "15", "10111001100"], [16, "0", "0", "16", "10011101100"], [17, "1", "1", "17", "10011100110"], [18, "2", "2", "18", "11001110010"], [19, "3", "3", "19", "11001011100"], [20, "4", "4", "20", "11001001110"], [21, "5", "5", "21", "11011100100"], [22, "6", "6", "22", "11001110100"], [23, "7", "7", "23", "11101101110"], [24, "8", "8", "24", "11101001100"], [25, "9", "9", "25", "11100101100"], [26, ":", ":", "26", "11100100110"], [27, ";", ";", "27", "11101100100"], [28, "<", "<", "28", "11100110100"], [29, "=", "=", "29", "11100110010"], [30, ">", ">", "30", "11011011000"], [31, "?", "?", "31", "11011000110"], [32, "@", "@", "32", "11000110110"], [33, "A", "A", "33", "10100011000"], [34, "B", "B", "34", "10001011000"], [35, "C", "C", "35", "10001000110"], [36, "D", "D", "36", "10110001000"], [37, "E", "E", "37", "10001101000"], [38, "F", "F", "38", "10001100010"], [39, "G", "G", "39", "11010001000"], [40, "H", "H", "40", "11000101000"], [41, "I", "I", "41", "11000100010"], [42, "J", "J", "42", "10110111000"], [43, "K", "K", "43", "10110001110"], [44, "L", "L", "44", "10001101110"], [45, "M", "M", "45", "10111011000"], [46, "N", "N", "46", "10111000110"], [47, "O", "O", "47", "10001110110"], [48, "P", "P", "48", "11101110110"], [49, "Q", "Q", "49", "11010001110"], [50, "R", "R", "50", "11000101110"], [51, "S", "S", "51", "11011101000"], [52, "T", "T", "52", "11011100010"], [53, "U", "U", "53", "11011101110"], [54, "V", "V", "54", "11101011000"], [55, "W", "W", "55", "11101000110"], [56, "X", "X", "56", "11100010110"], [57, "Y", "Y", "57", "11101101000"], [58, "Z", "Z", "58", "11101100010"], [59, "[", "[", "59", "11100011010"], [60, "\\", "\\", "60", "11101111010"], [61, "]", "]", "61", "11001000010"], [62, "^", "^", "62", "11110001010"], [63, "_", "_", "63", "10100110000"], [64, "\0", "`", "64", "10100001100"], [65, "\x01", "a", "65", "10010110000"], [66, "\x02", "b", "66", "10010000110"], [67, "\x03", "c", "67", "10000101100"], [68, "\x04", "d", "68", "10000100110"], [69, "\x05", "e", "69", "10110010000"], [70, "\x06", "f", "70", "10110000100"], [71, "\x07", "g", "71", "10011010000"], [72, "\b", "h", "72", "10011000010"], [73, "\x01", "i", "73", "10000110100"], [74, "\n", "j", "74", "10000110010"], [75, "\v", "k", "75", "11000010010"], [76, "\f", "l", "76", "11001010000"], [77, "\r", "m", "77", "11110111010"], [78, "\x08", "n", "78", "11000010100"], [79, "\x09", "o", "79", "10001111010"], [80, "\x10", "p", "80", "10100111100"], [81, "\x11", "q", "81", "10010111100"], [82, "\x12", "r", "82", "10010011110"], [83, "\x13", "s", "83", "10111100100"], [84, "\x14", "t", "84", "10011110100"], [85, "\x15", "u", "85", "10011110010"], [86, "\x16", "v", "86", "11110100100"], [87, "\x17", "w", "87", "11110010100"], [88, "\x18", "x", "88", "11110010010"], [89, "\x19", "y", "89", "11011011110"], [90, "\x1a", "z", "90", "11011110110"], [91, "\x1b", "{", "91", "11110110110"], [92, "\x1c", "|", "92", "10101111000"], [93, "\x1d", "}", "93", "10100011110"], [94, "\x1e", "~", "94", "10001011110"], [95, "\x1f", "\x7f", "95", "10111101000"], [96, "FNC 3", "FNC 3", "96", "10111100010"], [97, "FNC 2", "FNC 2", "97", "11110101000"], [98, "SHIFT B", "SHIFT A", "98", "11110100010"], [99, "CODE C", "CODE C", "99", "10111011110"], [100, "CODE B", "FNC 4", "CODE B", "10111101110"], [101, "FNC 4", "CODE A", "CODE A", "11101011110"], [102, "FNC 1", "FNC 1", "FNC 1", "11110101110"], [103, "A0", "A0", "A0", "11010000100"], [104, "B0", "B0", "B0", "11010010000"], [105, "C0", "C0", "C0", "11010011100"], [106, "STOP", "STOP", "STOP", "1100011101011"]]; 158 | 159 | // Reverse lookups from first 4 columns. Created on first use. 160 | var code128ValLookup = null; 161 | var code128ALookup = null; 162 | var code128BLookup = null; 163 | var code128CLookup = null; 164 | 165 | var makeCode128Lookups = function makeCode128Lookups() { 166 | if (code128ValLookup) { 167 | return; 168 | } 169 | 170 | code128ValLookup = {}; 171 | code128ALookup = {}; 172 | code128BLookup = {}; 173 | code128CLookup = {}; 174 | 175 | for (var i = 0; i < code128.length; i++) { 176 | var data = code128[i]; 177 | 178 | var val = data[CODE_128_VAL]; 179 | var charA = data[CODE_128_CHAR_A]; 180 | var charB = data[CODE_128_CHAR_B]; 181 | var charC = data[CODE_128_CHAR_C]; 182 | 183 | code128ValLookup[val] = data; 184 | code128ALookup[charA] = data; 185 | code128BLookup[charB] = data; 186 | code128CLookup[charC] = data; 187 | } 188 | }; 189 | 190 | function encodeCode128(text) { 191 | makeCode128Lookups(); 192 | 193 | var chars = new Array(1 + text.length); 194 | chars[0] = "B0"; 195 | for (var i = 0, len = text.length; i < len; i++) { 196 | chars[1 + i] = text[i]; 197 | } 198 | 199 | // Basic support for Code 128-A: do shift A before characters which live in 200 | // A but not in B. 201 | for (var _i = 0; _i < chars.length; _i++) { 202 | var ch = chars[_i]; 203 | if (code128ALookup[ch] && !code128BLookup[ch]) { 204 | chars.splice(_i, 0, "SHIFT A"); 205 | _i++; 206 | } 207 | } 208 | 209 | // Main thing we return is a list of characters. 210 | // [{ bits: "1011011", mode: "A", value: 44, char: "L", humanReadable: true }, ...] 211 | var outlist = []; 212 | 213 | var mode = void 0; 214 | switch (chars[0]) { 215 | case "A0": 216 | mode = "A";break; 217 | case "B0": 218 | mode = "B";break; 219 | case "C0": 220 | mode = "C";break; 221 | default: 222 | throw new Error("Expected a starting character"); 223 | } 224 | 225 | // If "SHIFT A" is a character in chars, then shift to mode A for one 226 | // character, and then switch back to returnMode. 227 | var returnMode = void 0; 228 | 229 | var sum = 0; 230 | for (var _i2 = 0; _i2 < chars.length; _i2++) { 231 | var _ch = chars[_i2]; 232 | 233 | // The weight value depends on what mode we're in. 234 | var data = void 0; 235 | switch (mode) { 236 | case "A": 237 | data = code128ALookup[_ch]; 238 | break; 239 | case "B": 240 | data = code128BLookup[_ch]; 241 | break; 242 | case "C": 243 | data = code128CLookup[_ch]; 244 | break; 245 | } 246 | 247 | // Throw an error if the character does not exist in this mode. 248 | if (!data) { 249 | throw new Error("Invalid input (no such char '" + _ch + "' in mode " + mode + ")"); 250 | } 251 | 252 | var val = data[CODE_128_VAL]; 253 | var bits = data[CODE_128_BITS]; 254 | 255 | // Contribute to sum. 256 | var n = _i2 || 1; // both start code and first text char have position 1. 257 | sum += n * val; 258 | 259 | outlist.push({ 260 | bits: bits, 261 | char: _ch, 262 | humanReadable: null, 263 | _mode: mode, 264 | _val: val 265 | }); 266 | 267 | // Return to previous mode after a shift. 268 | if (returnMode) { 269 | mode = returnMode; 270 | returnMode = null; 271 | } 272 | 273 | // Handle mode switches. 274 | switch (_ch) { 275 | case "CODE A": 276 | mode = "A";break; 277 | case "CODE B": 278 | mode = "B";break; 279 | case "CODE C": 280 | mode = "C";break; 281 | case "SHIFT A": 282 | returnMode = mode;mode = "A";break; 283 | case "SHIFT B": 284 | returnMode = mode;mode = "B";break; 285 | 286 | default: 287 | // Do nothing for non-mode switching characters. 288 | break; 289 | } 290 | } 291 | 292 | var checksum = sum % 103; 293 | 294 | // Append the checksum. 295 | var checksumData = code128ValLookup[checksum]; 296 | outlist.push({ 297 | bits: checksumData[CODE_128_BITS], 298 | char: "CHECKSUM", 299 | humanReadable: false, 300 | _val: checksum 301 | }); 302 | 303 | // Append the stop char. 304 | var stopData = code128ALookup.STOP; 305 | outlist.push({ 306 | bits: stopData[CODE_128_BITS], 307 | char: "STOP", 308 | humanReadable: false, 309 | _val: stopData[CODE_128_VAL] 310 | }); 311 | 312 | return { 313 | type: "bits", 314 | checksum: checksum, 315 | data: outlist 316 | }; 317 | } 318 | 319 | var CODE_39_CHAR = 0; 320 | var CODE_39_BITS = 2; 321 | 322 | var code39Data = [["1", 1, "110100101011"], ["2", 2, "101100101011"], ["3", 3, "110110010101"], ["4", 4, "101001101011"], ["5", 5, "110100110101"], ["6", 6, "101100110101"], ["7", 7, "101001011011"], ["8", 8, "110100101101"], ["9", 9, "101100101101"], ["0", 0, "101001101101"], ["A", 10, "110101001011"], ["B", 11, "101101001011"], ["C", 12, "110110100101"], ["D", 13, "101011001011"], ["E", 14, "110101100101"], ["F", 15, "101101100101"], ["G", 16, "101010011011"], ["H", 17, "110101001101"], ["I", 18, "101101001101"], ["J", 19, "101011001101"], ["K", 20, "110101010011"], ["L", 21, "101101010011"], ["M", 22, "110110101001"], ["N", 23, "101011010011"], ["O", 24, "110101101001"], ["P", 25, "101101101001"], ["Q", 26, "101010110011"], ["R", 27, "110101011001"], ["S", 28, "101101011001"], ["T", 29, "101011011001"], ["U", 30, "110010101011"], ["V", 31, "100110101011"], ["W", 32, "110011010101"], ["X", 33, "100101101011"], ["Y", 34, "110010110101"], ["Z", 35, "100110110101"], ["-", 36, "100101011011"], [".", 37, "110010101101"], [" ", 38, "100110101101"], ["$", 39, "100100100101"], ["/", 40, "100100101001"], ["+", 41, "100101001001"], ["%", 42, "101001001001"], ["*", NaN, "100101101101"]]; 323 | 324 | var code39Lookup = null; 325 | 326 | var makeCode39Lookups = function makeCode39Lookups() { 327 | if (code39Lookup) { 328 | return; 329 | } 330 | 331 | code39Lookup = {}; 332 | 333 | code39Data.forEach(function (row) { 334 | var ch = row[CODE_39_CHAR]; 335 | code39Lookup[ch] = row; 336 | }); 337 | }; 338 | 339 | // @param [withChecksum] {Boolean} If true, then add the mod 43 checksum. Defaults to false. 340 | function encodeCode39(text, withChecksum) { 341 | makeCode39Lookups(); 342 | 343 | // @todo implement 344 | withChecksum = withChecksum || false; 345 | 346 | if (!text) { 347 | text = ""; 348 | } 349 | 350 | text = "*" + text + "*"; 351 | 352 | var outlist = []; 353 | 354 | for (var i = 0; i < text.length; i++) { 355 | if (i !== 0) { 356 | outlist.push({ 357 | char: "", 358 | bits: "0", 359 | humanReadable: false 360 | }); 361 | } 362 | 363 | var ch = text[i]; 364 | var row = code39Lookup[ch]; 365 | if (!row) { 366 | throw new Error("Cannot encode code 39 barcode: invalid char: " + ch); 367 | } 368 | 369 | var bits = row[CODE_39_BITS]; 370 | outlist.push({ 371 | char: ch, 372 | bits: bits, 373 | humanReadable: true 374 | }); 375 | } 376 | 377 | return { 378 | type: "bits", 379 | data: outlist 380 | }; 381 | } 382 | 383 | var EAN_L = 0; 384 | var EAN_G = 1; 385 | var EAN_R = 2; 386 | 387 | var eanData = [["0001101", "0100111", "1110010"], ["0011001", "0110011", "1100110"], ["0010011", "0011011", "1101100"], ["0111101", "0100001", "1000010"], ["0100011", "0011101", "1011100"], ["0110001", "0111001", "1001110"], ["0101111", "0000101", "1010000"], ["0111011", "0010001", "1000100"], ["0110111", "0001001", "1001000"], ["0001011", "0010111", "1110100"]]; 388 | 389 | function encodeEAN(text, hasChecksum) { 390 | if (!/^\d+$/.test(text)) { 391 | throw new Error("EAN can only encode numbers."); 392 | } 393 | 394 | var origChecksum = void 0; 395 | if (hasChecksum) { 396 | origChecksum = parseInt(text.substr(text.length - 1, 1), 10); 397 | text = text.substr(0, text.length - 1); 398 | } 399 | 400 | var len = text.length; 401 | var sum = 0; 402 | for (var i = 0; i < len; i++) { 403 | var ch = text[i]; 404 | var n = ch - "0"; 405 | var weight = (len - i) % 2 === 1 ? 3 : 1; 406 | sum += weight * n; 407 | } 408 | 409 | // This could probably be achieved with a modulo and a check for 10 to wrap. 410 | // However this implementation was lifted from the GS1 website: 411 | // http://www.gs1.org/check-digit-calculator and is therefore guaranteed correct. 412 | var closest = Math.round(sum / 10) * 10; 413 | var checksum = closest - sum; 414 | if (checksum < 0) { 415 | checksum = closest + 10 - sum; 416 | } 417 | if (hasChecksum && checksum !== origChecksum) { 418 | throw new Error("Invalid checksum."); 419 | } 420 | text += checksum; 421 | 422 | var outlist = []; 423 | 424 | var encoding = void 0; 425 | switch (text.length) { 426 | case 8: 427 | encoding = "LLLLRRRR"; 428 | break; 429 | case 12: 430 | // UPC-A is just like EAN-13 with first digit 0. 431 | encoding = "LLLLLLRRRRRR"; 432 | break; 433 | case 13: 434 | switch (text[0]) { 435 | case "0": 436 | encoding = "LLLLLLRRRRRR";break; 437 | case "1": 438 | encoding = "LLGLGGRRRRRR";break; 439 | case "2": 440 | encoding = "LLGGLGRRRRRR";break; 441 | case "3": 442 | encoding = "LLGGGLRRRRRR";break; 443 | case "4": 444 | encoding = "LGLLGGRRRRRR";break; 445 | case "5": 446 | encoding = "LGGLLGRRRRRR";break; 447 | case "6": 448 | encoding = "LGGGLLRRRRRR";break; 449 | case "7": 450 | encoding = "LGLGLGRRRRRR";break; 451 | case "8": 452 | encoding = "LGLGGLRRRRRR";break; 453 | case "9": 454 | encoding = "LGGLGLRRRRRR";break; 455 | } 456 | 457 | outlist.push({ 458 | char: text[0], 459 | humanReadable: true, 460 | bits: "" 461 | }); 462 | 463 | text = text.slice(1); 464 | break; 465 | default: 466 | throw new Error("Don't know how to make EAN with that length."); 467 | } 468 | 469 | outlist.push({ 470 | char: "START", 471 | humanReadable: false, 472 | bits: "101" 473 | }); 474 | 475 | for (var _i = 0; _i < text.length; _i++) { 476 | if (_i === text.length / 2) { 477 | outlist.push({ 478 | char: "CENTER", 479 | humanReadable: false, 480 | bits: "01010" 481 | }); 482 | } 483 | 484 | var digit = text[_i] - "0"; 485 | var type = encoding[_i]; 486 | 487 | var index = type === "L" ? EAN_L : type === "G" ? EAN_G : EAN_R; 488 | var bitpattern = eanData[digit][index]; 489 | outlist.push({ 490 | char: text[_i], 491 | humanReadable: true, 492 | bits: bitpattern 493 | }); 494 | } 495 | 496 | outlist.push({ 497 | char: "END", 498 | humanReadable: false, 499 | bits: "101" 500 | }); 501 | 502 | return { 503 | type: "bits", 504 | checksum: checksum, 505 | data: outlist 506 | }; 507 | } 508 | 509 | function encodeFIM(text) { 510 | if (!/^[ABCD]$/.test(text)) { 511 | throw new Error("FIM can only encode 'A', 'B', 'C', or 'D'"); 512 | } 513 | 514 | var bits = void 0; 515 | switch (text) { 516 | case "A": 517 | bits = "110010011";break; 518 | case "B": 519 | bits = "101101101";break; 520 | case "C": 521 | bits = "110101011";break; 522 | case "D": 523 | bits = "111010111";break; 524 | } 525 | 526 | return { 527 | type: "bits", 528 | data: [{ char: text, humanReadable: false, bits: bits }] 529 | }; 530 | } 531 | 532 | var ITFData = [[0, 0, 1, 1, 0], [1, 0, 0, 0, 1], [0, 1, 0, 0, 1], [1, 1, 0, 0, 0], [0, 0, 1, 0, 1], [1, 0, 1, 0, 0], [0, 1, 1, 0, 0], [0, 0, 0, 1, 1], [1, 0, 0, 1, 0], [0, 1, 0, 1, 0]]; 533 | 534 | function encodeITF(text) { 535 | if (text.length % 2 === 1) { 536 | text = "0" + text; 537 | } 538 | 539 | for (var n = 0, len = text.length; n < len; n++) { 540 | var ch = text[n]; 541 | if (ch < "0" || ch > "9") { 542 | throw new Error("ITF can only encode numbers."); 543 | } 544 | } 545 | 546 | var outlist = []; 547 | 548 | outlist.push({ 549 | char: "START", 550 | bits: "1010", 551 | humanReadable: false 552 | }); 553 | 554 | for (var i = 0; i < text.length; i += 2) { 555 | var c1 = text[i]; 556 | var c2 = text[i + 1]; 557 | 558 | var n1 = c1 - "0"; 559 | var n2 = c2 - "0"; 560 | 561 | var bits = ""; 562 | for (var j = 0; j < 5; j++) { 563 | bits += ITFData[n1][j] === 0 ? "1" : "11"; 564 | bits += ITFData[n2][j] === 0 ? "0" : "00"; 565 | } 566 | 567 | outlist.push({ 568 | char: c1 + c2, 569 | bits: bits, 570 | humanReadable: true 571 | }); 572 | } 573 | 574 | outlist.push({ 575 | char: "STOP", 576 | bits: "1101", 577 | humanReadable: false 578 | }); 579 | 580 | return { 581 | type: "bits", 582 | data: outlist 583 | }; 584 | } 585 | 586 | function drawBitsBarcodeToCanvas(g, options, encodeData) { 587 | var bits = encodeData.data.map(function (d) { 588 | return d.bits; 589 | }).join(""); 590 | 591 | g.save(); 592 | 593 | // First transform so that no matter the x, y, horizontalAlign and 594 | // verticalAlign, we draw from the left at 0,0. 595 | 596 | var multiplier = bits.length + 2 * options.quietZoneSize; 597 | 598 | var bw = void 0; 599 | var width = void 0; 600 | if (!isNaN(options.width)) { 601 | // options.width takes precedence... if given, then it overrides 602 | // moduleWidth and maxWidth 603 | width = options.width; 604 | bw = width / multiplier; 605 | } else { 606 | // Try to use the given moduleWidth 607 | bw = options.moduleWidth; 608 | width = multiplier * bw; 609 | 610 | // But adjust if it doesn't fit in maxWidth (if given) 611 | if (width > options.maxWidth) { 612 | width = options.maxWidth; 613 | bw = width / multiplier; 614 | } 615 | } 616 | 617 | var height = options.height; 618 | 619 | // Translate to barcode start. 620 | g.translate(options.x, options.y); 621 | 622 | var rad = options.angle * Math.PI / 180; 623 | var cos = Math.cos(rad); 624 | var sin = Math.sin(rad); 625 | 626 | // Compute all positions relative to the point (x, y) in the unrotated 627 | // coordinate system. Using min and max values, we can figure out how much 628 | // we need to translate to make the desired alignment. 629 | // 630 | // 0,0 __________ w,0 631 | // | | 632 | // |__________| 633 | // 0,h w,h 634 | // 635 | // To compute the new positions, multiply by the 2d multiplication matrix: 636 | // 637 | // [cos(a) -sin(a)] * [0] = [0] 638 | // [sin(a) cos(a)] [0] [0] 639 | // 640 | // [cos(a) -sin(a)] * [w] = [w*cos(a)] 641 | // [sin(a) cos(a)] [0] [w*sin(a)] 642 | // 643 | // [cos(a) -sin(a)] * [w] = [w*cos(a)-h*sin(a)] 644 | // [sin(a) cos(a)] [h] [w*sin(a)+h*cos(a)] 645 | // 646 | // [cos(a) -sin(a)] * [0] = [-h*sin(a)] 647 | // [sin(a) cos(a)] [h] [ h*cos(a)] 648 | // 649 | // For centering, compute the rectangle's center's position in the same 650 | // way: 651 | // 652 | // [cos(a) -sin(a)] * [w/2] = [w/2*cos(a)-h/2*sin(a)] 653 | // [sin(a) cos(a)] [h/2] [w/2*sin(a)+h/2*cos(a)] 654 | var xs = [0, width * cos, width * cos - height * sin, -height * sin]; 655 | var ys = [0, width * sin, width * sin + height * cos, height * cos]; 656 | 657 | var xmin = Math.min.apply(this, xs); 658 | var ymin = Math.min.apply(this, ys); 659 | var xmax = Math.max.apply(this, xs); 660 | var ymax = Math.max.apply(this, ys); 661 | 662 | switch (options.horizontalAlign) { 663 | case "left": 664 | g.translate(-xmin, 0); 665 | break; 666 | case "center": 667 | g.translate(-(width / 2 * cos - height / 2 * sin), 0); 668 | break; 669 | case "right": 670 | g.translate(-xmax, 0); 671 | break; 672 | } 673 | 674 | switch (options.verticalAlign) { 675 | case "top": 676 | g.translate(0, -ymin); 677 | break; 678 | case "middle": 679 | g.translate(0, -(width / 2 * sin + height / 2 * cos)); 680 | break; 681 | case "bottom": 682 | g.translate(0, -ymax); 683 | break; 684 | } 685 | 686 | // Rotate. 687 | g.rotate(rad); 688 | 689 | // Skip quiet zone... 690 | g.translate(options.quietZoneSize * bw, 0); 691 | 692 | g.fillStyle = "black"; 693 | 694 | var n = 0; 695 | while (n < bits.length) { 696 | // We are at the start of a bar or a space. 697 | var bit = bits[n]; 698 | if (bit === "1") { 699 | // We are at a bar. 700 | var barCount = 1; 701 | while (n < bits.length && bits[++n] === "1") { 702 | barCount++; 703 | } 704 | 705 | var barWidth = barCount * bw; 706 | g.fillRect(0, 0, barWidth, height); 707 | g.translate(barWidth, 0); 708 | } else { 709 | // We are at a space. 710 | var spaceCount = 1; 711 | while (n < bits.length && bits[++n] === "0") { 712 | spaceCount++; 713 | } 714 | 715 | var spaceWidth = spaceCount * bw; 716 | g.translate(spaceWidth, 0); 717 | } 718 | } 719 | 720 | g.restore(); 721 | 722 | return { 723 | barcodeWidth: width, 724 | barcodeHeight: height, 725 | bbox: { 726 | x: xmin, 727 | y: ymin, 728 | width: xmax - xmin, 729 | height: ymax - ymin 730 | } 731 | }; 732 | } 733 | 734 | function drawBitsBarcodeToPath(options, encodeData) { 735 | var bits = encodeData.data.map(function (d) { 736 | return d.bits; 737 | }).join(""); 738 | 739 | var multiplier = bits.length + 2 * options.quietZoneSize; 740 | 741 | var bw = void 0; 742 | var width = void 0; 743 | if (!isNaN(options.width)) { 744 | // options.width takes precedence... if given, then it overrides 745 | // moduleWidth and maxWidth 746 | width = options.width; 747 | bw = width / multiplier; 748 | } else { 749 | // Try to use the given moduleWidth 750 | bw = options.moduleWidth; 751 | width = multiplier * bw; 752 | 753 | // But adjust if it doesn't fit in maxWidth (if given) 754 | if (width > options.maxWidth) { 755 | width = options.maxWidth; 756 | bw = width / multiplier; 757 | } 758 | } 759 | 760 | var height = options.height; 761 | 762 | var rects = []; 763 | 764 | // Walk xpos from left to right side. 765 | var xpos = options.quietZoneSize * bw; 766 | 767 | var n = 0; 768 | while (n < bits.length) { 769 | // We are at the start of a bar or a space. 770 | var bit = bits[n]; 771 | if (bit === "1") { 772 | // We are at a bar. 773 | var barCount = 1; 774 | while (n < bits.length && bits[++n] === "1") { 775 | barCount++; 776 | } 777 | 778 | var barWidth = barCount * bw; 779 | 780 | rects.push("M " + xpos.toFixed(3) + ",0"); 781 | rects.push("l " + barWidth + ",0"); 782 | rects.push("l 0," + height); 783 | rects.push("l " + (-barWidth).toFixed(3) + ",0"); 784 | rects.push("Z"); 785 | 786 | xpos += barWidth; 787 | } else { 788 | // We are at a space. 789 | var spaceCount = 1; 790 | while (n < bits.length && bits[++n] === "0") { 791 | spaceCount++; 792 | } 793 | 794 | var spaceWidth = spaceCount * bw; 795 | xpos += spaceWidth; 796 | } 797 | } 798 | 799 | return rects.join(" "); 800 | } 801 | 802 | function drawBitsBarcodeToSVG(options, encodeData) { 803 | var bits = encodeData.data.map(function (d) { 804 | return d.bits; 805 | }).join(""); 806 | 807 | var multiplier = bits.length + 2 * options.quietZoneSize; 808 | 809 | var bw = void 0; 810 | var width = void 0; 811 | if (!isNaN(options.width)) { 812 | // options.width takes precedence... if given, then it overrides 813 | // moduleWidth and maxWidth 814 | width = options.width; 815 | bw = width / multiplier; 816 | } else { 817 | // Try to use the given moduleWidth 818 | bw = options.moduleWidth; 819 | width = multiplier * bw; 820 | 821 | // But adjust if it doesn't fit in maxWidth (if given) 822 | if (width > options.maxWidth) { 823 | width = options.maxWidth; 824 | bw = width / multiplier; 825 | } 826 | } 827 | 828 | var height = options.height; 829 | 830 | var svgLines = []; 831 | var attr = ["xmlns='http://www.w3.org/2000/svg'", "width='" + width.toFixed(3) + "'", "height='" + height + "'", "fill='black'"].join(" "); 832 | 833 | svgLines.push(""); 834 | 835 | // Walk xpos from left to right side. 836 | var xpos = options.quietZoneSize * bw; 837 | 838 | var n = 0; 839 | while (n < bits.length) { 840 | // We are at the start of a bar or a space. 841 | var bit = bits[n]; 842 | if (bit === "1") { 843 | // We are at a bar. 844 | var barCount = 1; 845 | while (n < bits.length && bits[++n] === "1") { 846 | barCount++; 847 | } 848 | 849 | var barWidth = barCount * bw; 850 | var props = ["width='" + barWidth + "'", "height='" + height + "'", "x='" + xpos.toFixed(3) + "'", "y='0'"].join(" "); 851 | svgLines.push(""); 852 | xpos += barWidth; 853 | } else { 854 | // We are at a space. 855 | var spaceCount = 1; 856 | while (n < bits.length && bits[++n] === "0") { 857 | spaceCount++; 858 | } 859 | 860 | var spaceWidth = spaceCount * bw; 861 | xpos += spaceWidth; 862 | } 863 | } 864 | 865 | svgLines.push(""); 866 | return svgLines.join("\n"); 867 | } 868 | 869 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 870 | return typeof obj; 871 | } : function (obj) { 872 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 873 | }; 874 | 875 | 876 | 877 | 878 | 879 | var asyncGenerator = function () { 880 | function AwaitValue(value) { 881 | this.value = value; 882 | } 883 | 884 | function AsyncGenerator(gen) { 885 | var front, back; 886 | 887 | function send(key, arg) { 888 | return new Promise(function (resolve, reject) { 889 | var request = { 890 | key: key, 891 | arg: arg, 892 | resolve: resolve, 893 | reject: reject, 894 | next: null 895 | }; 896 | 897 | if (back) { 898 | back = back.next = request; 899 | } else { 900 | front = back = request; 901 | resume(key, arg); 902 | } 903 | }); 904 | } 905 | 906 | function resume(key, arg) { 907 | try { 908 | var result = gen[key](arg); 909 | var value = result.value; 910 | 911 | if (value instanceof AwaitValue) { 912 | Promise.resolve(value.value).then(function (arg) { 913 | resume("next", arg); 914 | }, function (arg) { 915 | resume("throw", arg); 916 | }); 917 | } else { 918 | settle(result.done ? "return" : "normal", result.value); 919 | } 920 | } catch (err) { 921 | settle("throw", err); 922 | } 923 | } 924 | 925 | function settle(type, value) { 926 | switch (type) { 927 | case "return": 928 | front.resolve({ 929 | value: value, 930 | done: true 931 | }); 932 | break; 933 | 934 | case "throw": 935 | front.reject(value); 936 | break; 937 | 938 | default: 939 | front.resolve({ 940 | value: value, 941 | done: false 942 | }); 943 | break; 944 | } 945 | 946 | front = front.next; 947 | 948 | if (front) { 949 | resume(front.key, front.arg); 950 | } else { 951 | back = null; 952 | } 953 | } 954 | 955 | this._invoke = send; 956 | 957 | if (typeof gen.return !== "function") { 958 | this.return = undefined; 959 | } 960 | } 961 | 962 | if (typeof Symbol === "function" && Symbol.asyncIterator) { 963 | AsyncGenerator.prototype[Symbol.asyncIterator] = function () { 964 | return this; 965 | }; 966 | } 967 | 968 | AsyncGenerator.prototype.next = function (arg) { 969 | return this._invoke("next", arg); 970 | }; 971 | 972 | AsyncGenerator.prototype.throw = function (arg) { 973 | return this._invoke("throw", arg); 974 | }; 975 | 976 | AsyncGenerator.prototype.return = function (arg) { 977 | return this._invoke("return", arg); 978 | }; 979 | 980 | return { 981 | wrap: function (fn) { 982 | return function () { 983 | return new AsyncGenerator(fn.apply(this, arguments)); 984 | }; 985 | }, 986 | await: function (value) { 987 | return new AwaitValue(value); 988 | } 989 | }; 990 | }(); 991 | 992 | var optionDefaults = { 993 | type: "Code 128", 994 | hasChecksum: false, 995 | x: 0, 996 | y: 0, 997 | moduleWidth: 2.892, 998 | height: 90.72, 999 | horizontalAlign: "left", 1000 | verticalAlign: "top", 1001 | quietZoneSize: 10, 1002 | angle: 0, 1003 | maxWidth: Infinity, 1004 | width: NaN 1005 | }; 1006 | 1007 | // Copy default values from source to target if they don't exist. 1008 | var copyDefaults = function copyDefaults(target, source) { 1009 | for (var key in source) { 1010 | if (typeof target[key] === "undefined") { 1011 | target[key] = source[key]; 1012 | } 1013 | } 1014 | }; 1015 | 1016 | /** 1017 | * @summary Draw a barcode to a canvas graphics context. 1018 | * @todo IMB, Pharmacode, PostBar, POSTNET, Telepen 1019 | * @param {Context2D|String} g An HTML5 or node-canvas graphics context or the output format. The only supported non-canvas output formats are "path" and "svg". 1020 | * @param {String|String[]} text Barcode text (without start, end, or check characters). It can also be an array of characters, in case you want to include a command character, like "FNC 1". 1021 | * @param {Object} options Controls what barcode is drawn, where, and how. 1022 | * @param {String} options.type Barcode type. Defaults to Code 128. Other valid options are "GS1 128", "Codabar", "Code 39", "EAN-8", "EAN-13", "FIM", "ITF" (interleaved 2 of 5), and "UPC-A". 1023 | * @param {Boolean} options.hasChecksum If true, the barcode already has a checksum (which will be validated); if false, calculate and add a checksum. Defaults to false. **Currently works only for EAN-type barcodes (EAN-8, EAN-13, UPC-A).** 1024 | * @param {Number} options.x Where to draw barcode. Defaults to 0. 1025 | * @param {Number} options.y Where to draw the barcode. Defaults to 0. 1026 | * @param {String} options.horizontalAlign How to align the barcode. Defaults to "left". Other options are "center" and "right". 1027 | * @param {String} options.verticalAlign How to align the barcode. Defaults to "top". Other options are "middle" and "bottom". 1028 | * @param {Number} options.height Barcode height. Defaults to 90.72. 1029 | * @param {Number} options.moduleWidth Width of thinnest bar. Defaults to 2.892. 1030 | * @param {Number} options.quietZoneSize Number of moduleWidths in quiet zone on either side. Defaults to 10. 1031 | * @param {Number} options.angle Rotate barcode this many degrees clockwise. Defaults to 0. 1032 | * @param {Number} options.maxWidth Maximum barcode width (including quiet zones). If specified, then the moduleWidth will be adjusted if necessary to make the entire barcode fit in the given width. 1033 | * @param {Number} options.width If given, then ignore moduleWidth and maxWidth and set the moduleWidth so that the barcode will have the given width. 1034 | */ 1035 | function drawBarcode(g, text, options) { 1036 | // Validate input. 1037 | if ((typeof g === "undefined" ? "undefined" : _typeof(g)) !== "object" && g !== "path" && g !== "svg") { 1038 | throw new Error("drawBarcode: expected `g' to be an object or 'path' or 'svg'."); 1039 | } 1040 | 1041 | if (!text) { 1042 | throw new Error("drawBarcode: missing required parameter `text'."); 1043 | } 1044 | 1045 | if ((typeof options === "undefined" ? "undefined" : _typeof(options)) !== "object") { 1046 | options = {}; 1047 | } 1048 | 1049 | copyDefaults(options, optionDefaults); 1050 | validateDrawBarcodeOptions(options); 1051 | 1052 | var encodeData = void 0; 1053 | switch (options.type) { 1054 | case "Codabar": 1055 | encodeData = encodeCodabar(text); 1056 | break; 1057 | case "Code 128": 1058 | encodeData = encodeCode128(text); 1059 | break; 1060 | case "GS1 128": 1061 | // GS1 128 is a Code 128 barcode with an FNC 1 at the begining. 1062 | var textArray = []; 1063 | textArray.push("FNC 1"); 1064 | for (var n = 0; n < text.length; n++) { 1065 | textArray.push(text[n]); 1066 | } 1067 | encodeData = encodeCode128(textArray); 1068 | break; 1069 | case "Code 39": 1070 | encodeData = encodeCode39(text); 1071 | break; 1072 | case "ITF": 1073 | encodeData = encodeITF(text); 1074 | break; 1075 | case "FIM": 1076 | encodeData = encodeFIM(text); 1077 | break; 1078 | case "EAN-8": 1079 | case "EAN-13": 1080 | case "UPC-A": 1081 | var expectedLength = void 0; 1082 | if (options.type === "EAN-8") { 1083 | expectedLength = 7; 1084 | } 1085 | if (options.type === "EAN-13") { 1086 | expectedLength = 12; 1087 | } 1088 | if (options.type === "UPC-A") { 1089 | expectedLength = 11; 1090 | } 1091 | if (options.hasChecksum) { 1092 | expectedLength += 1; 1093 | } 1094 | if (expectedLength !== text.length) { 1095 | throw new Error(options.type + " must be of length " + expectedLength); 1096 | } 1097 | encodeData = encodeEAN(text, options.hasChecksum); 1098 | break; 1099 | default: 1100 | throw new Error("Unrecognized barcode type: " + options.type); 1101 | } 1102 | 1103 | switch (encodeData.type) { 1104 | case "bits": 1105 | return drawBitsBarcode(g, options, encodeData); 1106 | default: 1107 | throw new Error("Unrecognized encoded barcode type: " + encodeData.type); 1108 | } 1109 | } 1110 | 1111 | function validateDrawBarcodeOptions(options) { 1112 | assertIsBoolean(options.hasChecksum, "options.hasChecksum"); 1113 | assertIsNumber(options.x, "options.x"); 1114 | assertIsNumber(options.y, "options.y"); 1115 | assertIsValidHorizontalAlign(options.horizontalAlign); 1116 | assertIsValidVerticalAlign(options.verticalAlign); 1117 | assertIsPositiveNumber(options.height, "options.height"); 1118 | assertIsPositiveNumber(options.moduleWidth, "options.moduleWidth"); 1119 | assertIsNonNegativeNumber(options.quietZoneSize, "options.quietZoneSize"); 1120 | assertIsNumber(options.angle, "options.angle"); 1121 | assertIsPositiveNumber(options.maxWidth, "options.maxWidth"); 1122 | 1123 | // width can either be NaN or a positive number. 1124 | if (!isNaN(options.width)) { 1125 | assertIsPositiveNumber(options.width, "options.width"); 1126 | } 1127 | } 1128 | 1129 | function drawBitsBarcode(g, options, encodeData) { 1130 | if (g === "path") { 1131 | return drawBitsBarcodeToPath(options, encodeData); 1132 | } 1133 | 1134 | if (g === "svg") { 1135 | return drawBitsBarcodeToSVG(options, encodeData); 1136 | } 1137 | 1138 | return drawBitsBarcodeToCanvas(g, options, encodeData); 1139 | } 1140 | 1141 | var version = "2.2.0"; 1142 | 1143 | export { drawBarcode, version }; 1144 | --------------------------------------------------------------------------------