barcode.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /* eslint-disable */
  2. const CHAR_TILDE = 126;
  3. const CODE_FNC1 = 102;
  4. const SET_STARTA = 103;
  5. const SET_STARTB = 104;
  6. const SET_STARTC = 105;
  7. const SET_SHIFT = 98;
  8. const SET_CODEA = 101;
  9. const SET_CODEB = 100;
  10. const SET_STOP = 106;
  11. const REPLACE_CODES = {
  12. CHAR_TILDE: CODE_FNC1 // ~ corresponds to FNC1 in GS1-128 standard
  13. };
  14. const CODESET = {
  15. ANY: 1,
  16. AB: 2,
  17. A: 3,
  18. B: 4,
  19. C: 5
  20. };
  21. function getBytes(str) {
  22. const bytes = [];
  23. for (let i = 0; i < str.length; i++) {
  24. bytes.push(str.charCodeAt(i));
  25. }
  26. return bytes;
  27. }
  28. exports.code128 = function (ctx, text, width, height) {
  29. width = parseInt(width);
  30. height = parseInt(height);
  31. const codes = stringToCode128(text);
  32. const g = new Graphics(ctx, width, height);
  33. const barWeight = g.area.width / ((codes.length - 3) * 11 + 35);
  34. let x = g.area.left;
  35. const y = g.area.top;
  36. for (let i = 0; i < codes.length; i++) {
  37. const c = codes[i]; // two bars at a time: 1 black and 1 white
  38. for (let bar = 0; bar < 8; bar += 2) {
  39. const barW = PATTERNS[c][bar] * barWeight; // var barH = height - y - this.border;
  40. const barH = height - y;
  41. const spcW = PATTERNS[c][bar + 1] * barWeight; // no need to draw if 0 width
  42. if (barW > 0) {
  43. g.fillFgRect(x, y, barW, barH);
  44. }
  45. x += barW + spcW;
  46. }
  47. }
  48. ctx.draw();
  49. };
  50. function stringToCode128(text) {
  51. const barc = {
  52. currcs: CODESET.C
  53. };
  54. const bytes = getBytes(text); // decide starting codeset
  55. let index = bytes[0] == CHAR_TILDE ? 1 : 0;
  56. const csa1 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
  57. const csa2 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
  58. barc.currcs = getBestStartSet(csa1, csa2);
  59. barc.currcs = perhapsCodeC(bytes, barc.currcs); // if no codeset changes this will end up with bytes.length+3
  60. // start, checksum and stop
  61. let codes = new Array();
  62. switch (barc.currcs) {
  63. case CODESET.A:
  64. codes.push(SET_STARTA);
  65. break;
  66. case CODESET.B:
  67. codes.push(SET_STARTB);
  68. break;
  69. default:
  70. codes.push(SET_STARTC);
  71. break;
  72. }
  73. for (let i = 0; i < bytes.length; i++) {
  74. let b1 = bytes[i]; // get the first of a pair
  75. // should we translate/replace
  76. if (b1 in REPLACE_CODES) {
  77. codes.push(REPLACE_CODES[b1]);
  78. i++; // jump to next
  79. b1 = bytes[i];
  80. } // get the next in the pair if possible
  81. const b2 = bytes.length > i + 1 ? bytes[i + 1] : -1;
  82. codes = codes.concat(codesForChar(b1, b2, barc.currcs)); // code C takes 2 chars each time
  83. if (barc.currcs == CODESET.C) i++;
  84. } // calculate checksum according to Code 128 standards
  85. let checksum = codes[0];
  86. for (let weight = 1; weight < codes.length; weight++) {
  87. checksum += weight * codes[weight];
  88. }
  89. codes.push(checksum % 103);
  90. codes.push(SET_STOP); // encoding should now be complete
  91. return codes;
  92. function getBestStartSet(csa1, csa2) {
  93. // tries to figure out the best codeset
  94. // to start with to get the most compact code
  95. let vote = 0;
  96. vote += csa1 == CODESET.A ? 1 : 0;
  97. vote += csa1 == CODESET.B ? -1 : 0;
  98. vote += csa2 == CODESET.A ? 1 : 0;
  99. vote += csa2 == CODESET.B ? -1 : 0; // tie goes to B due to my own predudices
  100. return vote > 0 ? CODESET.A : CODESET.B;
  101. }
  102. function perhapsCodeC(bytes, codeset) {
  103. for (let i = 0; i < bytes.length; i++) {
  104. const b = bytes[i];
  105. if ((b < 48 || b > 57) && b != CHAR_TILDE) return codeset;
  106. }
  107. return CODESET.C;
  108. } // chr1 is current byte
  109. // chr2 is the next byte to process. looks ahead.
  110. function codesForChar(chr1, chr2, currcs) {
  111. const result = [];
  112. let shifter = -1;
  113. if (charCompatible(chr1, currcs)) {
  114. if (currcs == CODESET.C) {
  115. if (chr2 == -1) {
  116. shifter = SET_CODEB;
  117. currcs = CODESET.B;
  118. } else if (chr2 != -1 && !charCompatible(chr2, currcs)) {
  119. // need to check ahead as well
  120. if (charCompatible(chr2, CODESET.A)) {
  121. shifter = SET_CODEA;
  122. currcs = CODESET.A;
  123. } else {
  124. shifter = SET_CODEB;
  125. currcs = CODESET.B;
  126. }
  127. }
  128. }
  129. } else {
  130. // if there is a next char AND that next char is also not compatible
  131. if (chr2 != -1 && !charCompatible(chr2, currcs)) {
  132. // need to switch code sets
  133. switch (currcs) {
  134. case CODESET.A:
  135. shifter = SET_CODEB;
  136. currcs = CODESET.B;
  137. break;
  138. case CODESET.B:
  139. shifter = SET_CODEA;
  140. currcs = CODESET.A;
  141. break;
  142. }
  143. } else {
  144. // no need to shift code sets, a temporary SHIFT will suffice
  145. shifter = SET_SHIFT;
  146. }
  147. } // ok some type of shift is nessecary
  148. if (shifter != -1) {
  149. result.push(shifter);
  150. result.push(codeValue(chr2));
  151. } else if (currcs == CODESET.C) {
  152. // include next as well
  153. result.push(codeValue(chr1, chr2));
  154. } else {
  155. result.push(codeValue(chr1));
  156. }
  157. barc.currcs = currcs;
  158. return result;
  159. }
  160. } // reduce the ascii code to fit into the Code128 char table
  161. function codeValue(chr1, chr2) {
  162. if (typeof chr2 === 'undefined') {
  163. return chr1 >= 32 ? chr1 - 32 : chr1 + 64;
  164. } else {
  165. return parseInt(String.fromCharCode(chr1) + String.fromCharCode(chr2));
  166. }
  167. }
  168. function charCompatible(chr, codeset) {
  169. const csa = codeSetAllowedFor(chr);
  170. if (csa == CODESET.ANY) return true; // if we need to change from current
  171. if (csa == CODESET.AB) return true;
  172. if (csa == CODESET.A && codeset == CODESET.A) return true;
  173. if (csa == CODESET.B && codeset == CODESET.B) return true;
  174. return false;
  175. }
  176. function codeSetAllowedFor(chr) {
  177. if (chr >= 48 && chr <= 57) {
  178. // 0-9
  179. return CODESET.ANY;
  180. } else if (chr >= 32 && chr <= 95) {
  181. // 0-9 A-Z
  182. return CODESET.AB;
  183. } else {
  184. // if non printable
  185. return chr < 32 ? CODESET.A : CODESET.B;
  186. }
  187. }
  188. var Graphics = function (ctx, width, height) {
  189. this.width = width;
  190. this.height = height;
  191. this.quiet = Math.round(this.width / 40);
  192. this.border_size = 0;
  193. this.padding_width = 0;
  194. this.area = {
  195. width: width - this.padding_width * 2 - this.quiet * 2,
  196. height: height - this.border_size * 2,
  197. top: this.border_size - 4,
  198. left: this.padding_width + this.quiet
  199. };
  200. this.ctx = ctx;
  201. this.fg = '#000000';
  202. this.bg = '#ffffff'; // fill background
  203. this.fillBgRect(0, 0, width, height); // fill center to create border
  204. this.fillBgRect(0, this.border_size, width, height - this.border_size * 2);
  205. }; // use native color
  206. Graphics.prototype._fillRect = function (x, y, width, height, color) {
  207. this.ctx.setFillStyle(color);
  208. this.ctx.fillRect(x, y, width, height);
  209. };
  210. Graphics.prototype.fillFgRect = function (x, y, width, height) {
  211. this._fillRect(x, y, width, height, this.fg);
  212. };
  213. Graphics.prototype.fillBgRect = function (x, y, width, height) {
  214. this._fillRect(x, y, width, height, this.bg);
  215. };
  216. var PATTERNS = [[2, 1, 2, 2, 2, 2, 0, 0], // 0
  217. [2, 2, 2, 1, 2, 2, 0, 0], // 1
  218. [2, 2, 2, 2, 2, 1, 0, 0], // 2
  219. [1, 2, 1, 2, 2, 3, 0, 0], // 3
  220. [1, 2, 1, 3, 2, 2, 0, 0], // 4
  221. [1, 3, 1, 2, 2, 2, 0, 0], // 5
  222. [1, 2, 2, 2, 1, 3, 0, 0], // 6
  223. [1, 2, 2, 3, 1, 2, 0, 0], // 7
  224. [1, 3, 2, 2, 1, 2, 0, 0], // 8
  225. [2, 2, 1, 2, 1, 3, 0, 0], // 9
  226. [2, 2, 1, 3, 1, 2, 0, 0], // 10
  227. [2, 3, 1, 2, 1, 2, 0, 0], // 11
  228. [1, 1, 2, 2, 3, 2, 0, 0], // 12
  229. [1, 2, 2, 1, 3, 2, 0, 0], // 13
  230. [1, 2, 2, 2, 3, 1, 0, 0], // 14
  231. [1, 1, 3, 2, 2, 2, 0, 0], // 15
  232. [1, 2, 3, 1, 2, 2, 0, 0], // 16
  233. [1, 2, 3, 2, 2, 1, 0, 0], // 17
  234. [2, 2, 3, 2, 1, 1, 0, 0], // 18
  235. [2, 2, 1, 1, 3, 2, 0, 0], // 19
  236. [2, 2, 1, 2, 3, 1, 0, 0], // 20
  237. [2, 1, 3, 2, 1, 2, 0, 0], // 21
  238. [2, 2, 3, 1, 1, 2, 0, 0], // 22
  239. [3, 1, 2, 1, 3, 1, 0, 0], // 23
  240. [3, 1, 1, 2, 2, 2, 0, 0], // 24
  241. [3, 2, 1, 1, 2, 2, 0, 0], // 25
  242. [3, 2, 1, 2, 2, 1, 0, 0], // 26
  243. [3, 1, 2, 2, 1, 2, 0, 0], // 27
  244. [3, 2, 2, 1, 1, 2, 0, 0], // 28
  245. [3, 2, 2, 2, 1, 1, 0, 0], // 29
  246. [2, 1, 2, 1, 2, 3, 0, 0], // 30
  247. [2, 1, 2, 3, 2, 1, 0, 0], // 31
  248. [2, 3, 2, 1, 2, 1, 0, 0], // 32
  249. [1, 1, 1, 3, 2, 3, 0, 0], // 33
  250. [1, 3, 1, 1, 2, 3, 0, 0], // 34
  251. [1, 3, 1, 3, 2, 1, 0, 0], // 35
  252. [1, 1, 2, 3, 1, 3, 0, 0], // 36
  253. [1, 3, 2, 1, 1, 3, 0, 0], // 37
  254. [1, 3, 2, 3, 1, 1, 0, 0], // 38
  255. [2, 1, 1, 3, 1, 3, 0, 0], // 39
  256. [2, 3, 1, 1, 1, 3, 0, 0], // 40
  257. [2, 3, 1, 3, 1, 1, 0, 0], // 41
  258. [1, 1, 2, 1, 3, 3, 0, 0], // 42
  259. [1, 1, 2, 3, 3, 1, 0, 0], // 43
  260. [1, 3, 2, 1, 3, 1, 0, 0], // 44
  261. [1, 1, 3, 1, 2, 3, 0, 0], // 45
  262. [1, 1, 3, 3, 2, 1, 0, 0], // 46
  263. [1, 3, 3, 1, 2, 1, 0, 0], // 47
  264. [3, 1, 3, 1, 2, 1, 0, 0], // 48
  265. [2, 1, 1, 3, 3, 1, 0, 0], // 49
  266. [2, 3, 1, 1, 3, 1, 0, 0], // 50
  267. [2, 1, 3, 1, 1, 3, 0, 0], // 51
  268. [2, 1, 3, 3, 1, 1, 0, 0], // 52
  269. [2, 1, 3, 1, 3, 1, 0, 0], // 53
  270. [3, 1, 1, 1, 2, 3, 0, 0], // 54
  271. [3, 1, 1, 3, 2, 1, 0, 0], // 55
  272. [3, 3, 1, 1, 2, 1, 0, 0], // 56
  273. [3, 1, 2, 1, 1, 3, 0, 0], // 57
  274. [3, 1, 2, 3, 1, 1, 0, 0], // 58
  275. [3, 3, 2, 1, 1, 1, 0, 0], // 59
  276. [3, 1, 4, 1, 1, 1, 0, 0], // 60
  277. [2, 2, 1, 4, 1, 1, 0, 0], // 61
  278. [4, 3, 1, 1, 1, 1, 0, 0], // 62
  279. [1, 1, 1, 2, 2, 4, 0, 0], // 63
  280. [1, 1, 1, 4, 2, 2, 0, 0], // 64
  281. [1, 2, 1, 1, 2, 4, 0, 0], // 65
  282. [1, 2, 1, 4, 2, 1, 0, 0], // 66
  283. [1, 4, 1, 1, 2, 2, 0, 0], // 67
  284. [1, 4, 1, 2, 2, 1, 0, 0], // 68
  285. [1, 1, 2, 2, 1, 4, 0, 0], // 69
  286. [1, 1, 2, 4, 1, 2, 0, 0], // 70
  287. [1, 2, 2, 1, 1, 4, 0, 0], // 71
  288. [1, 2, 2, 4, 1, 1, 0, 0], // 72
  289. [1, 4, 2, 1, 1, 2, 0, 0], // 73
  290. [1, 4, 2, 2, 1, 1, 0, 0], // 74
  291. [2, 4, 1, 2, 1, 1, 0, 0], // 75
  292. [2, 2, 1, 1, 1, 4, 0, 0], // 76
  293. [4, 1, 3, 1, 1, 1, 0, 0], // 77
  294. [2, 4, 1, 1, 1, 2, 0, 0], // 78
  295. [1, 3, 4, 1, 1, 1, 0, 0], // 79
  296. [1, 1, 1, 2, 4, 2, 0, 0], // 80
  297. [1, 2, 1, 1, 4, 2, 0, 0], // 81
  298. [1, 2, 1, 2, 4, 1, 0, 0], // 82
  299. [1, 1, 4, 2, 1, 2, 0, 0], // 83
  300. [1, 2, 4, 1, 1, 2, 0, 0], // 84
  301. [1, 2, 4, 2, 1, 1, 0, 0], // 85
  302. [4, 1, 1, 2, 1, 2, 0, 0], // 86
  303. [4, 2, 1, 1, 1, 2, 0, 0], // 87
  304. [4, 2, 1, 2, 1, 1, 0, 0], // 88
  305. [2, 1, 2, 1, 4, 1, 0, 0], // 89
  306. [2, 1, 4, 1, 2, 1, 0, 0], // 90
  307. [4, 1, 2, 1, 2, 1, 0, 0], // 91
  308. [1, 1, 1, 1, 4, 3, 0, 0], // 92
  309. [1, 1, 1, 3, 4, 1, 0, 0], // 93
  310. [1, 3, 1, 1, 4, 1, 0, 0], // 94
  311. [1, 1, 4, 1, 1, 3, 0, 0], // 95
  312. [1, 1, 4, 3, 1, 1, 0, 0], // 96
  313. [4, 1, 1, 1, 1, 3, 0, 0], // 97
  314. [4, 1, 1, 3, 1, 1, 0, 0], // 98
  315. [1, 1, 3, 1, 4, 1, 0, 0], // 99
  316. [1, 1, 4, 1, 3, 1, 0, 0], // 100
  317. [3, 1, 1, 1, 4, 1, 0, 0], // 101
  318. [4, 1, 1, 1, 3, 1, 0, 0], // 102
  319. [2, 1, 1, 4, 1, 2, 0, 0], // 103
  320. [2, 1, 1, 2, 1, 4, 0, 0], // 104
  321. [2, 1, 1, 2, 3, 2, 0, 0], // 105
  322. [2, 3, 3, 1, 1, 1, 2, 0] // 106
  323. ];