version.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. var Utils = require('./utils')
  2. var ECCode = require('./error-correction-code')
  3. var ECLevel = require('./error-correction-level')
  4. var Mode = require('./mode')
  5. var VersionCheck = require('./version-check')
  6. var isArray = require('isarray')
  7. // Generator polynomial used to encode version information
  8. var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0)
  9. var G18_BCH = Utils.getBCHDigit(G18)
  10. function getBestVersionForDataLength (mode, length, errorCorrectionLevel) {
  11. for (var currentVersion = 1; currentVersion <= 40; currentVersion++) {
  12. if (length <= exports.getCapacity(currentVersion, errorCorrectionLevel, mode)) {
  13. return currentVersion
  14. }
  15. }
  16. return undefined
  17. }
  18. function getReservedBitsCount (mode, version) {
  19. // Character count indicator + mode indicator bits
  20. return Mode.getCharCountIndicator(mode, version) + 4
  21. }
  22. function getTotalBitsFromDataArray (segments, version) {
  23. var totalBits = 0
  24. segments.forEach(function (data) {
  25. var reservedBits = getReservedBitsCount(data.mode, version)
  26. totalBits += reservedBits + data.getBitsLength()
  27. })
  28. return totalBits
  29. }
  30. function getBestVersionForMixedData (segments, errorCorrectionLevel) {
  31. for (var currentVersion = 1; currentVersion <= 40; currentVersion++) {
  32. var length = getTotalBitsFromDataArray(segments, currentVersion)
  33. if (length <= exports.getCapacity(currentVersion, errorCorrectionLevel, Mode.MIXED)) {
  34. return currentVersion
  35. }
  36. }
  37. return undefined
  38. }
  39. /**
  40. * Returns version number from a value.
  41. * If value is not a valid version, returns defaultValue
  42. *
  43. * @param {Number|String} value QR Code version
  44. * @param {Number} defaultValue Fallback value
  45. * @return {Number} QR Code version number
  46. */
  47. exports.from = function from (value, defaultValue) {
  48. if (VersionCheck.isValid(value)) {
  49. return parseInt(value, 10)
  50. }
  51. return defaultValue
  52. }
  53. /**
  54. * Returns how much data can be stored with the specified QR code version
  55. * and error correction level
  56. *
  57. * @param {Number} version QR Code version (1-40)
  58. * @param {Number} errorCorrectionLevel Error correction level
  59. * @param {Mode} mode Data mode
  60. * @return {Number} Quantity of storable data
  61. */
  62. exports.getCapacity = function getCapacity (version, errorCorrectionLevel, mode) {
  63. if (!VersionCheck.isValid(version)) {
  64. throw new Error('Invalid QR Code version')
  65. }
  66. // Use Byte mode as default
  67. if (typeof mode === 'undefined') mode = Mode.BYTE
  68. // Total codewords for this QR code version (Data + Error correction)
  69. var totalCodewords = Utils.getSymbolTotalCodewords(version)
  70. // Total number of error correction codewords
  71. var ecTotalCodewords = ECCode.getTotalCodewordsCount(version, errorCorrectionLevel)
  72. // Total number of data codewords
  73. var dataTotalCodewordsBits = (totalCodewords - ecTotalCodewords) * 8
  74. if (mode === Mode.MIXED) return dataTotalCodewordsBits
  75. var usableBits = dataTotalCodewordsBits - getReservedBitsCount(mode, version)
  76. // Return max number of storable codewords
  77. switch (mode) {
  78. case Mode.NUMERIC:
  79. return Math.floor((usableBits / 10) * 3)
  80. case Mode.ALPHANUMERIC:
  81. return Math.floor((usableBits / 11) * 2)
  82. case Mode.KANJI:
  83. return Math.floor(usableBits / 13)
  84. case Mode.BYTE:
  85. default:
  86. return Math.floor(usableBits / 8)
  87. }
  88. }
  89. /**
  90. * Returns the minimum version needed to contain the amount of data
  91. *
  92. * @param {Segment} data Segment of data
  93. * @param {Number} [errorCorrectionLevel=H] Error correction level
  94. * @param {Mode} mode Data mode
  95. * @return {Number} QR Code version
  96. */
  97. exports.getBestVersionForData = function getBestVersionForData (data, errorCorrectionLevel) {
  98. var seg
  99. var ecl = ECLevel.from(errorCorrectionLevel, ECLevel.M)
  100. if (isArray(data)) {
  101. if (data.length > 1) {
  102. return getBestVersionForMixedData(data, ecl)
  103. }
  104. if (data.length === 0) {
  105. return 1
  106. }
  107. seg = data[0]
  108. } else {
  109. seg = data
  110. }
  111. return getBestVersionForDataLength(seg.mode, seg.getLength(), ecl)
  112. }
  113. /**
  114. * Returns version information with relative error correction bits
  115. *
  116. * The version information is included in QR Code symbols of version 7 or larger.
  117. * It consists of an 18-bit sequence containing 6 data bits,
  118. * with 12 error correction bits calculated using the (18, 6) Golay code.
  119. *
  120. * @param {Number} version QR Code version
  121. * @return {Number} Encoded version info bits
  122. */
  123. exports.getEncodedBits = function getEncodedBits (version) {
  124. if (!VersionCheck.isValid(version) || version < 7) {
  125. throw new Error('Invalid QR Code version')
  126. }
  127. var d = version << 12
  128. while (Utils.getBCHDigit(d) - G18_BCH >= 0) {
  129. d ^= (G18 << (Utils.getBCHDigit(d) - G18_BCH))
  130. }
  131. return (version << 12) | d
  132. }