123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- var BufferUtil = require('../utils/buffer')
- var Utils = require('./utils')
- var ECLevel = require('./error-correction-level')
- 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')
- var Mode = require('./mode')
- var Segments = require('./segments')
- var isArray = require('isarray')
- /**
- * 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
- *
- //---------------------------------------------------------------------
- // 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
- //
- //---------------------------------------------------------------------
- */
- /**
- * 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
- */
- function setupVersionInfo (matrix, version) {
- 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 = ((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 {ErrorCorrectionLevel} errorCorrectionLevel Error correction level
- * @param {Number} maskPattern Mask pattern reference value
- */
- function setupFormatInfo (matrix, errorCorrectionLevel, maskPattern) {
- var size = matrix.size
- var bits = FormatInfo.getEncodedBits(errorCorrectionLevel, maskPattern)
- var i, mod
- for (i = 0; i < 15; i++) {
- mod = ((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, 1, 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 {ErrorCorrectionLevel} errorCorrectionLevel Error correction level
- * @param {ByteData} data Data input
- * @return {Buffer} Buffer containing encoded codewords
- */
- function createData (version, errorCorrectionLevel, segments) {
- // Prepare data buffer
- var buffer = new BitBuffer()
- segments.forEach(function (data) {
- // prefix data with mode indicator (4 bits)
- buffer.put(data.mode.bit, 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 Mode.getCharCountIndicator}.
- buffer.put(data.getLength(), Mode.getCharCountIndicator(data.mode, 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 {ErrorCorrectionLevel} 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 = BufferUtil.from(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 = BufferUtil.alloc(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++) {
- data[index++] = ecData[r][i]
- }
- }
- return data
- }
- /**
- * Build QR Code symbol
- *
- * @param {String} data Input string
- * @param {Number} version QR Code version
- * @param {ErrorCorretionLevel} errorCorrectionLevel Error level
- * @param {MaskPattern} maskPattern Mask pattern
- * @return {Object} Object containing symbol data
- */
- function createSymbol (data, version, errorCorrectionLevel, maskPattern) {
- var segments
- if (isArray(data)) {
- segments = Segments.fromArray(data)
- } else if (typeof data === 'string') {
- var estimatedVersion = version
- if (!estimatedVersion) {
- var rawSegments = Segments.rawSplit(data)
- // Estimate best version that can contain raw splitted segments
- estimatedVersion = Version.getBestVersionForData(rawSegments,
- errorCorrectionLevel)
- }
- // Build optimized segments
- // If estimated version is undefined, try with the highest version
- segments = Segments.fromString(data, estimatedVersion || 40)
- } else {
- throw new Error('Invalid data')
- }
- // Get the min version that can contain data
- var bestVersion = Version.getBestVersionForData(segments,
- 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 (!version) {
- version = bestVersion
- // Check if the specified version can contain the data
- } else if (version < bestVersion) {
- throw new Error('\n' +
- 'The chosen QR Code version cannot contain this amount of data.\n' +
- 'Minimum version required to store current data is: ' + bestVersion + '.\n'
- )
- }
- var dataBits = createData(version, errorCorrectionLevel, segments)
- // Allocate matrix buffer
- var moduleCount = Utils.getSymbolSize(version)
- var modules = new BitMatrix(moduleCount)
- // Add function modules
- setupFinderPattern(modules, version)
- setupTimingPattern(modules)
- setupAlignmentPattern(modules, version)
- // Add temporary dummy bits for format 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(modules, errorCorrectionLevel, 0)
- if (version >= 7) {
- setupVersionInfo(modules, version)
- }
- // Add data codewords
- setupData(modules, dataBits)
- if (isNaN(maskPattern)) {
- // Find best mask pattern
- maskPattern = MaskPattern.getBestMask(modules,
- setupFormatInfo.bind(null, modules, errorCorrectionLevel))
- }
- // Apply mask pattern
- MaskPattern.applyMask(maskPattern, modules)
- // Replace format info bits with correct values
- setupFormatInfo(modules, errorCorrectionLevel, maskPattern)
- return {
- modules: modules,
- version: version,
- errorCorrectionLevel: errorCorrectionLevel,
- maskPattern: maskPattern,
- segments: segments
- }
- }
- /**
- * QR Code
- *
- * @param {String | Array} data Input data
- * @param {Object} options Optional configurations
- * @param {Number} options.version QR Code version
- * @param {String} options.errorCorrectionLevel Error correction level
- * @param {Function} options.toSJISFunc Helper func to convert utf8 to sjis
- */
- exports.create = function create (data, options) {
- if (typeof data === 'undefined' || data === '') {
- throw new Error('No input text')
- }
- var errorCorrectionLevel = ECLevel.M
- var version
- var mask
- if (typeof options !== 'undefined') {
- // Use higher error correction level as default
- errorCorrectionLevel = ECLevel.from(options.errorCorrectionLevel, ECLevel.M)
- version = Version.from(options.version)
- mask = MaskPattern.from(options.maskPattern)
- if (options.toSJISFunc) {
- Utils.setToSJISFunction(options.toSJISFunc)
- }
- }
- return createSymbol(data, version, errorCorrectionLevel, mask)
- }
|