barcode.js 12 KB

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