version.js 4.8 KB

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