code39.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /*
  2. * barcode generator - code39
  3. */
  4. var gm = require('gm');
  5. const bmap = {
  6. ' ': [0,1,1,0,0,0,1,0,0],
  7. '$': [0,1,0,1,0,1,0,0,0],
  8. '%': [0,0,0,1,0,1,0,1,0],
  9. '*': [0,1,0,0,1,0,1,0,0], // Start/stop marker
  10. '+': [0,1,0,0,0,1,0,1,0],
  11. '|': [0,1,0,0,0,0,1,0,1],
  12. '.': [1,1,0,0,0,0,1,0,0],
  13. '/': [0,1,0,1,0,0,0,1,0],
  14. '-': [0,1,0,0,0,0,1,0,1],
  15. '0': [0,0,0,1,1,0,1,0,0],
  16. '1': [1,0,0,1,0,0,0,0,1],
  17. '2': [0,0,1,1,0,0,0,0,1],
  18. '3': [1,0,1,1,0,0,0,0,0],
  19. '4': [0,0,0,1,1,0,0,0,1],
  20. '5': [1,0,0,1,1,0,0,0,0],
  21. '6': [0,0,1,1,1,0,0,0,0],
  22. '7': [0,0,0,1,0,0,1,0,1],
  23. '8': [1,0,0,1,0,0,1,0,0],
  24. '9': [0,0,1,1,0,0,1,0,0],
  25. 'A': [1,0,0,0,0,1,0,0,1],
  26. 'B': [0,0,1,0,0,1,0,0,1],
  27. 'C': [1,0,1,0,0,1,0,0,0],
  28. 'D': [0,0,0,0,1,1,0,0,1],
  29. 'E': [1,0,0,0,1,1,0,0,0],
  30. 'F': [0,0,1,0,1,1,0,0,0],
  31. 'G': [0,0,0,0,0,1,1,0,1],
  32. 'H': [1,0,0,0,0,1,1,0,0],
  33. 'I': [0,0,1,0,0,1,1,0,0],
  34. 'J': [0,0,0,0,1,1,1,0,0],
  35. 'K': [1,0,0,0,0,0,0,1,1],
  36. 'L': [0,0,1,0,0,0,0,1,1],
  37. 'M': [1,0,1,0,0,0,0,1,0],
  38. 'N': [0,0,0,0,1,0,0,1,1],
  39. 'O': [1,0,0,0,1,0,0,1,0],
  40. 'P': [0,0,1,0,1,0,0,1,0],
  41. 'Q': [0,0,0,0,0,0,1,1,1],
  42. 'R': [1,0,0,0,0,0,1,1,0],
  43. 'S': [0,0,1,0,0,0,1,1,0],
  44. 'T': [0,0,0,0,1,0,1,1,0],
  45. 'U': [1,1,0,0,0,0,0,0,1],
  46. 'V': [0,1,1,0,0,0,0,0,1],
  47. 'W': [1,1,1,0,0,0,0,0,0],
  48. 'X': [0,1,0,0,1,0,0,0,1],
  49. 'Y': [1,1,0,0,1,0,0,0,0],
  50. 'Z': [0,1,1,0,1,0,0,0,0]
  51. };
  52. const NARROW_BAR = 20;
  53. const WIDE_BAR = 55;
  54. const QUIET_BAR = 35;
  55. const SYM_LEN = 6 * NARROW_BAR + 3 * WIDE_BAR + QUIET_BAR;
  56. module.exports.createCode = function (options, callback) {
  57. if (!options.w || !options.h) {
  58. return callback(new Error('Width and Height must be non-zero'), {});
  59. }
  60. options.data = options.data.trim();
  61. if (!options.data) {
  62. return callback(new Error('No data given'), {});
  63. }
  64. options.data = ('*' + options.data + '*').toUpperCase();
  65. var wQuotient = options.w / (options.data.length * SYM_LEN - QUIET_BAR);
  66. var nBarWidth = NARROW_BAR * wQuotient >> 0;
  67. var wBarWidth = WIDE_BAR * wQuotient >> 0;
  68. var qBarWidth = QUIET_BAR * wQuotient >> 0;
  69. if (nBarWidth < 1 || qBarWidth == nBarWidth) {
  70. return callback(new Error('Current settings will result in a ' +
  71. 'degenerate barcode'), {});
  72. }
  73. var img = gm(options.w, options.h, options.bgcolor),
  74. count = 0,
  75. pos = 0,
  76. ch,
  77. w;
  78. img.stroke(options.barcolor, 1);
  79. for(var k in options.data) {
  80. if (count++) {
  81. pos += qBarWidth;
  82. }
  83. ch = bmap[options.data[k]] || bmap[' '];
  84. for(var j in ch) {
  85. w = ch[j] ? wBarWidth : nBarWidth;
  86. if ( !(j % 2) ) {
  87. // workaround for GM - cannot set gravity on stroke
  88. for (var m = 0; m < w; m++) {
  89. img.drawLine(pos + m, 0, pos + m, options.h);
  90. }
  91. }
  92. pos += w;
  93. }
  94. }
  95. img.stream(options.type, function (err, stdout, stderr) {
  96. callback(err, stdout);
  97. });
  98. };