var Buffer = require('../utils/buffer') var Utils = require('./utils') var ECLevel = require('./error-correction-level') var ByteData = require('./byte-data') var BitBuffer = require('./bit-buffer') var BitMatrix = require('./bit-matrix') var AlignmentPattern = require('./alignment-pattern') var FinderPattern = require('./finder-pattern') var MaskPattern = require('./mask-pattern') var ECCode = require('./error-correction-code') var ReedSolomonEncoder = require('./reed-solomon-encoder') var Version = require('./version') var FormatInfo = require('./format-info') /** * QRCode for JavaScript * * modified by Ryan Day for nodejs support * Copyright (c) 2011 Ryan Day * * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * EXPORTS: * { * QRCode:QRCode * QRErrorCorrectLevel:QRErrorCorrectLevel * } //--------------------------------------------------------------------- // QRCode for JavaScript // // Copyright (c) 2009 Kazuhiko Arase // // URL: http://www.d-project.com/ // // Licensed under the MIT license: // http://www.opensource.org/licenses/mit-license.php // // The word "QR Code" is registered trademark of // DENSO WAVE INCORPORATED // http://www.denso-wave.com/qrcode/faqpatent-e.html // //--------------------------------------------------------------------- */ module.exports = QRCode /** * Add finder patterns bits to matrix * * @param {BitMatrix} matrix Modules matrix * @param {Number} version QR Code version */ function setupFinderPattern (matrix, version) { var size = matrix.size var pos = FinderPattern.getPositions(version) for (var i = 0; i < pos.length; i++) { var row = pos[i][0] var col = pos[i][1] for (var r = -1; r <= 7; r++) { if (row + r <= -1 || size <= row + r) continue for (var c = -1; c <= 7; c++) { if (col + c <= -1 || size <= col + c) continue if ((r >= 0 && r <= 6 && (c === 0 || c === 6)) || (c >= 0 && c <= 6 && (r === 0 || r === 6)) || (r >= 2 && r <= 4 && c >= 2 && c <= 4)) { matrix.set(row + r, col + c, true, true) } else { matrix.set(row + r, col + c, false, true) } } } } } /** * Add timing pattern bits to matrix * * Note: this function must be called before {@link setupAlignmentPattern} * * @param {BitMatrix} matrix Modules matrix */ function setupTimingPattern (matrix) { var size = matrix.size for (var r = 8; r < size - 8; r++) { var value = r % 2 === 0 matrix.set(r, 6, value, true) matrix.set(6, r, value, true) } } /** * Add alignment patterns bits to matrix * * Note: this function must be called after {@link setupTimingPattern} * * @param {BitMatrix} matrix Modules matrix * @param {Number} version QR Code version */ function setupAlignmentPattern (matrix, version) { var pos = AlignmentPattern.getPositions(version) for (var i = 0; i < pos.length; i++) { var row = pos[i][0] var col = pos[i][1] for (var r = -2; r <= 2; r++) { for (var c = -2; c <= 2; c++) { if (r === -2 || r === 2 || c === -2 || c === 2 || (r === 0 && c === 0)) { matrix.set(row + r, col + c, true, true) } else { matrix.set(row + r, col + c, false, true) } } } } } /** * Add version info bits to matrix * * @param {BitMatrix} matrix Modules matrix * @param {Number} version QR Code version * @param {Boolean} reserve If true, marks bits as reserved and set their values to 0 */ function setupVersionInfo (matrix, version, reserve) { var size = matrix.size var bits = Version.getEncodedBits(version) var row, col, mod for (var i = 0; i < 18; i++) { row = Math.floor(i / 3) col = i % 3 + size - 8 - 3 mod = (!reserve && ((bits >> i) & 1) === 1) matrix.set(row, col, mod, true) matrix.set(col, row, mod, true) } } /** * Add format info bits to matrix * * @param {BitMatrix} matrix Modules matrix * @param {Number} errorCorrectionLevel Error correction level * @param {Number} maskPattern Mask pattern reference value * @param {Boolean} reserve If true, marks bits as reserved and set their values to 0 */ function setupFormatInfo (matrix, errorCorrectionLevel, maskPattern, reserve) { var size = matrix.size var bits = FormatInfo.getEncodedBits(errorCorrectionLevel, maskPattern) var i, mod for (i = 0; i < 15; i++) { mod = (!reserve && ((bits >> i) & 1) === 1) // vertical if (i < 6) { matrix.set(i, 8, mod, true) } else if (i < 8) { matrix.set(i + 1, 8, mod, true) } else { matrix.set(size - 15 + i, 8, mod, true) } // horizontal if (i < 8) { matrix.set(8, size - i - 1, mod, true) } else if (i < 9) { matrix.set(8, 15 - i - 1 + 1, mod, true) } else { matrix.set(8, 15 - i - 1, mod, true) } } // fixed module matrix.set(size - 8, 8, !reserve, true) } /** * Add encoded data bits to matrix * * @param {BitMatrix} matrix Modules matrix * @param {Buffer} data Data codewords */ function setupData (matrix, data) { var size = matrix.size var inc = -1 var row = size - 1 var bitIndex = 7 var byteIndex = 0 for (var col = size - 1; col > 0; col -= 2) { if (col === 6) col-- while (true) { for (var c = 0; c < 2; c++) { if (!matrix.isReserved(row, col - c)) { var dark = false if (byteIndex < data.length) { dark = (((data[byteIndex] >>> bitIndex) & 1) === 1) } matrix.set(row, col - c, dark) bitIndex-- if (bitIndex === -1) { byteIndex++ bitIndex = 7 } } } row += inc if (row < 0 || size <= row) { row -= inc inc = -inc break } } } } /** * Create encoded codewords from data input * * @param {Number} version QR Code version * @param {Number} errorCorrectionLevel Error correction level * @param {ByteData} data Data input * @return {Buffer} Buffer containing encoded codewords */ function createData (version, errorCorrectionLevel, data) { // Prepare data buffer var buffer = new BitBuffer() // prefix data with mode indicator (4 bits in byte mode) buffer.put(data.mode, 4) // Prefix data with character count indicator. // The character count indicator is a string of bits that represents the number of characters // that are being encoded. The character count indicator must be placed after the mode indicator // and must be a certain number of bits long, depending on the QR version and data mode // @see {@link ByteData.getCharCountIndicator}. buffer.put(data.getLength(), data.getCharCountIndicator(version)) // add binary data sequence to buffer data.write(buffer) // Calculate required number of bits var totalCodewords = Utils.getSymbolTotalCodewords(version) var ecTotalCodewords = ECCode.getTotalCodewordsCount(version, errorCorrectionLevel) var dataTotalCodewordsBits = (totalCodewords - ecTotalCodewords) * 8 // Add a terminator. // If the bit string is shorter than the total number of required bits, // a terminator of up to four 0s must be added to the right side of the string. // If the bit string is more than four bits shorter than the required number of bits, // add four 0s to the end. if (buffer.getLengthInBits() + 4 <= dataTotalCodewordsBits) { buffer.put(0, 4) } // If the bit string is fewer than four bits shorter, add only the number of 0s that // are needed to reach the required number of bits. // After adding the terminator, if the number of bits in the string is not a multiple of 8, // pad the string on the right with 0s to make the string's length a multiple of 8. while (buffer.getLengthInBits() % 8 !== 0) { buffer.putBit(0) } // Add pad bytes if the string is still shorter than the total number of required bits. // Extend the buffer to fill the data capacity of the symbol corresponding to // the Version and Error Correction Level by adding the Pad Codewords 11101100 (0xEC) // and 00010001 (0x11) alternately. var remainingByte = (dataTotalCodewordsBits - buffer.getLengthInBits()) / 8 for (var i = 0; i < remainingByte; i++) { buffer.put(i % 2 ? 0x11 : 0xEC, 8) } return createCodewords(buffer, version, errorCorrectionLevel) } /** * Encode input data with Reed-Solomon and return codewords with * relative error correction bits * * @param {BitBuffer} bitBuffer Data to encode * @param {Number} version QR Code version * @param {Number} errorCorrectionLevel Error correction level * @return {Buffer} Buffer containing encoded codewords */ function createCodewords (bitBuffer, version, errorCorrectionLevel) { // Total codewords for this QR code version (Data + Error correction) var totalCodewords = Utils.getSymbolTotalCodewords(version) // Total number of error correction codewords var ecTotalCodewords = ECCode.getTotalCodewordsCount(version, errorCorrectionLevel) // Total number of data codewords var dataTotalCodewords = totalCodewords - ecTotalCodewords // Total number of blocks var ecTotalBlocks = ECCode.getBlocksCount(version, errorCorrectionLevel) // Calculate how many blocks each group should contain var blocksInGroup2 = totalCodewords % ecTotalBlocks var blocksInGroup1 = ecTotalBlocks - blocksInGroup2 var totalCodewordsInGroup1 = Math.floor(totalCodewords / ecTotalBlocks) var dataCodewordsInGroup1 = Math.floor(dataTotalCodewords / ecTotalBlocks) var dataCodewordsInGroup2 = dataCodewordsInGroup1 + 1 // Number of EC codewords is the same for both groups var ecCount = totalCodewordsInGroup1 - dataCodewordsInGroup1 // Initialize a Reed-Solomon encoder with a generator polynomial of degree ecCount var rs = new ReedSolomonEncoder(ecCount) var offset = 0 var dcData = new Array(ecTotalBlocks) var ecData = new Array(ecTotalBlocks) var maxDataSize = 0 var buffer = new Buffer(bitBuffer.buffer) // Divide the buffer into the required number of blocks for (var b = 0; b < ecTotalBlocks; b++) { var dataSize = b < blocksInGroup1 ? dataCodewordsInGroup1 : dataCodewordsInGroup2 // extract a block of data from buffer dcData[b] = buffer.slice(offset, offset + dataSize) // Calculate EC codewords for this data block ecData[b] = rs.encode(dcData[b]) offset += dataSize maxDataSize = Math.max(maxDataSize, dataSize) } // Create final data // Interleave the data and error correction codewords from each block var data = new Buffer(totalCodewords) var index = 0 var i, r // Add data codewords for (i = 0; i < maxDataSize; i++) { for (r = 0; r < ecTotalBlocks; r++) { if (i < dcData[r].length) { data[index++] = dcData[r][i] } } } // Apped EC codewords for (i = 0; i < ecCount; i++) { for (r = 0; r < ecTotalBlocks; r++) { if (i < ecData[r].length) { data[index++] = ecData[r][i] } } } return data } /** * QR Code * * @param {Number} version QR Code version * @param {Number} errorCorrectionLevel Error correction level */ function QRCode (version, errorCorrectionLevel) { this.version = version this.errorCorrectionLevel = errorCorrectionLevel this.modules = null this.moduleCount = 0 this.dataCache = null this.data = null } /** * Add datas to store * * @param {String, Number, Array, Buffer} data */ QRCode.prototype.addData = function addData (data) { if (this.data) this.data.append(data) else this.data = new ByteData(data) this.dataCache = null } /** * Return value of module at position * * @param {Number} row Row * @param {Number} col Column * @return {Boolean} Module value (black/white) */ QRCode.prototype.isDark = function isDark (row, col) { if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) { throw new Error(row + ',' + col) } return this.modules.get(row, col) } /** * Return QR Code size (number of modules in row/col) * * @return {Number} size */ QRCode.prototype.getModuleCount = function getModuleCount () { return this.moduleCount } /** * Build QR Code symbol */ QRCode.prototype.make = function make () { if (this.dataCache === null) { // Use higher error correction level as default if (typeof this.errorCorrectionLevel === 'undefined') this.errorCorrectionLevel = ECLevel.H // Get the min version that can contain data var bestVersion = Version.getBestVersionForData(this.data, this.errorCorrectionLevel) // If no version is found, data cannot be stored if (!bestVersion) { throw new Error('The amount of data is too big to be stored in a QR Code') } // If not specified, use min version as default if (!this.version) { this.version = bestVersion // Check if the specified version can contain the data } else if (this.version < bestVersion) { throw new Error('\n' + 'The chosen QR Code version cannot contain this amount of data.\n' + 'Max characters allowed with current config: ' + Version.getCapacity(this.version, this.errorCorrectionLevel) + '\n' + 'Minimum version required to store current data: ' + bestVersion + '\n' ) } this.dataCache = createData(this.version, this.errorCorrectionLevel, this.data) } // Allocate matrix buffer this.moduleCount = Utils.getSymbolSize(this.version) this.modules = new BitMatrix(this.moduleCount) // Add function modules setupFinderPattern(this.modules, this.version) setupTimingPattern(this.modules) setupAlignmentPattern(this.modules, this.version) // Add temporary blank bits for format info and version info just to set them as reserved. // This is needed to prevent these bits from being masked by {@link MaskPattern.applyMask} // since the masking operation must be performed only on the encoding region. // These blocks will be replaced with correct values later in code. setupFormatInfo(this.modules, this.errorCorrectionLevel, 0, true) if (this.version >= 7) { setupVersionInfo(this.modules, this.version, true) } // Add data codewords setupData(this.modules, this.dataCache) // Find best mask pattern var maskPattern = MaskPattern.getBestMask(this.modules) // Apply mask pattern MaskPattern.applyMask(maskPattern, this.modules) // Replace format info bits with correct values setupFormatInfo(this.modules, this.errorCorrectionLevel, maskPattern) // Replace version info bits with correct values if (this.version >= 7) { setupVersionInfo(this.modules, this.version) } }