upc-a.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /*
  2. * barcode generator - UPC-A
  3. */
  4. var gm = require('gm'),
  5. getCheckDigit = require('./utils/checksums').weight3odd10,
  6. Barcode1D = require('./utils/draw1D');
  7. const START = [1,1,1];
  8. const MIDDLE = [1,1,1,1,1];
  9. const END = [1,1,1];
  10. const BARMAP = [
  11. [3,2,1,1], [2,2,2,1], [2,1,2,2], [1,4,1,1], [1,1,3,2],
  12. [1,2,3,1], [1,1,1,4], [1,3,1,2], [1,2,1,3], [3,1,1,2]
  13. ];
  14. /*
  15. This is because UPC always has the same formula:
  16. Start - 3 wide
  17. Data (x6) - 7 wide
  18. Middle - 5 wide)
  19. Data (x5) - 7 wide
  20. Check - 7 wide
  21. End - 3 wide
  22. */
  23. const MODULE_WIDTH = 95;
  24. const FULL_DATA_LEN = 12
  25. const REG_DATA_LEN = 11;
  26. module.exports.createCode = function (options, callback) {
  27. if (!options.data) {
  28. return callback(new Error('No data given'), {});
  29. }
  30. var userCheckDigit = false;
  31. options.data = ('' + options.data).replace(/[^0-9]/g, ''); // force to string
  32. if (options.data.length === FULL_DATA_LEN) {
  33. userCheckDigit = parseInt(options.data.substr(-1));
  34. options.data = options.data.substr(0, REG_DATA_LEN)
  35. }
  36. else if (options.data.length !== REG_DATA_LEN) {
  37. return callback(new Error('Data must be 11 digits (0-9) long for UPC-A'), {});
  38. }
  39. if (!options.w || !options.h) {
  40. return callback(new Error('Width and Height must be non-zero'), {});
  41. }
  42. var barWidth = options.w / MODULE_WIDTH >> 0;
  43. if (!barWidth) {
  44. return callback(new Error('Width will result in a degenerate barcode'), {});
  45. }
  46. // @todo externalize this to a function that generates the sequence
  47. var digit, encoding = [START];
  48. for (var i = 0; i < options.data.length; i++) {
  49. // Between position 6 and 7
  50. if (i == 6) {
  51. encoding.push(MIDDLE);
  52. }
  53. digit = parseInt(options.data[i]);
  54. encoding.push( BARMAP[ digit ] );
  55. }
  56. var checkDigit = getCheckDigit(options.data);
  57. // we're doing them a service by verifying it for them, the UPC wont work
  58. // otherwise, and they might have bad data.
  59. if (userCheckDigit !== false && checkDigit !== userCheckDigit) {
  60. return callback(new Error('Supplied check digit (digit 12: ' +
  61. userCheckDigit + ') is incorrect. Correct: ' + checkDigit), {} );
  62. }
  63. encoding.push(BARMAP[checkDigit]);
  64. encoding.push(END);
  65. // @todo make pattern generation function that ouputs a flat array instead
  66. // of a 2d array with each digit separate
  67. var flatPattern = [];
  68. for(var i=0; i<encoding.length; ++i) {
  69. flatPattern = flatPattern.concat(encoding[i]);
  70. }
  71. new Barcode1D().setWidth(options.w)
  72. .setHeight(options.h)
  73. .setModuleWidth(MODULE_WIDTH)
  74. .draw(flatPattern, callback);
  75. }