UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

580 lines (469 loc) 18.2 kB
import { toBitsString, toDecimal } from '../utils'; import { FreeCellVisitor } from './free-cell-visitor'; import { IsoEncoder } from './encoders/iso-encoder'; import { Utf8Encoder } from './encoders/utf8-encoder'; import { VersionsCodewordsInformation } from './version-codewords'; let terminator = "0000", powersOfTwo = { "1": 0 }, powersOfTwoResult = { "0": 1 }, irregularAlignmentPatternsStartDistance = { 15: 20, 16: 20, 18: 24, 19: 24, 22: 20, 24: 22, 26: 24, 28: 20, 30: 20, 31: 24, 32: 28, 33: 24, 36: 18, 37: 22, 39: 20, 40: 24 }, finderPattern = [1, 0, 1, 1, 1], alignmentPattern = [1, 0, 1], errorCorrectionPatterns = { L: "01", M: "00", Q: "11", H: "10" }, formatMaskPattern = "101010000010010", formatGeneratorPolynomial = "10100110111", versionGeneratorPolynomial = "1111100100101", paddingCodewords = ["11101100", "00010001"], finderPatternValue = 93, /* eslint-disable arrow-body-style */ /* eslint-disable no-unused-vars */ maskPatternConditions = [ (row, column) => { return (row + column) % 2 === 0; }, (row, column) => { return row % 2 === 0; }, (row, column) => { return column % 3 === 0; }, (row, column) => { return (row + column) % 3 === 0; }, (row, column) => { return (Math.floor(row / 2) + Math.floor(column / 3)) % 2 === 0; }, (row, column) => { return ((row * column) % 2) + ((row * column) % 3) === 0; }, (row, column) => { return (((row * column) % 2) + ((row * column) % 3)) % 2 === 0; }, (row, column) => { return (((row + column) % 2) + ((row * column) % 3)) % 2 === 0; } ]; /* eslint-enable no-unused-vars */ /* eslint-enable arrow-body-style */ export const generatorPolynomials = [[1, 0], [1, 25, 0]]; export function fillFunctionCell(matrices, bit, x, y) { for (let i = 0; i < matrices.length; i++) { matrices[i][x][y] = bit; } } export function fillDataCell(matrices, bit, x, y) { for (let i = 0; i < maskPatternConditions.length; i++) { matrices[i][x][y] = maskPatternConditions[i](x, y) ? bit ^ 1 : parseInt(bit, 10); } } export function fillData(matrices, blocks) { let cellVisitor = new FreeCellVisitor(matrices[0]), block, codewordIdx, cell; for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) { block = blocks[blockIdx]; codewordIdx = 0; while (block.length > 0) { for (let i = 0; i < block.length; i++) { for (let j = 0; j < 8; j++) { cell = cellVisitor.getNextCell(); fillDataCell(matrices, block[i][codewordIdx].charAt(j), cell.row, cell.column); } } codewordIdx++; while (block[0] && codewordIdx === block[0].length) { block.splice(0, 1); } } } while ((cell = cellVisitor.getNextRemainderCell())) { fillDataCell(matrices, 0, cell.row, cell.column); } } export function padDataString(initialDataString, totalDataCodewords) { let dataBitsCount = totalDataCodewords * 8, terminatorIndex = 0, paddingCodewordIndex = 0; let dataString = initialDataString; while (dataString.length < dataBitsCount && terminatorIndex < terminator.length) { dataString += terminator.charAt(terminatorIndex++); } if (dataString.length % 8 !== 0) { dataString += new Array(9 - dataString.length % 8).join("0"); } while (dataString.length < dataBitsCount) { dataString += paddingCodewords[paddingCodewordIndex]; paddingCodewordIndex ^= 1; } return dataString; } export function generatePowersOfTwo() { let result; let power; for (power = 1; power < 255; power++) { result = powersOfTwoResult[power - 1] * 2; if (result > 255) { result = result ^ 285; } powersOfTwoResult[power] = result; powersOfTwo[result] = power; } result = (powersOfTwoResult[power - 1] * 2) ^ 285; powersOfTwoResult[power] = result; powersOfTwoResult[-1] = 0; } export function xorPolynomials(x, y) { let result = [], idx = x.length - 2; for (let i = idx; i >= 0; i--) { result[i] = x[i] ^ y[i]; } return result; } export function multiplyPolynomials(x, y) { let result = []; for (let i = 0; i < x.length; i++) { for (let j = 0; j < y.length; j++) { if (result[i + j] === undefined) { result[i + j] = (x[i] + (y[j] >= 0 ? y[j] : 0)) % 255; } else { result[i + j] = powersOfTwo[powersOfTwoResult[result[i + j]] ^ powersOfTwoResult[(x[i] + y[j]) % 255]]; } } } return result; } export function generateGeneratorPolynomials() { let maxErrorCorrectionCodeWordsCount = 68; for (let idx = 2; idx <= maxErrorCorrectionCodeWordsCount; idx++) { let firstPolynomial = generatorPolynomials[idx - 1], secondPolynomial = [idx, 0]; generatorPolynomials[idx] = multiplyPolynomials(firstPolynomial, secondPolynomial); } } //possibly generate on demand generatePowersOfTwo(); generateGeneratorPolynomials(); export function multiplyByConstant(polynomial, power) { let result = [], idx = polynomial.length - 1; do { result[idx] = powersOfTwoResult[(polynomial[idx] + power) % 255]; idx--; } while (polynomial[idx] !== undefined); return result; } export function generateErrorCodewords(data, errorCodewordsCount) { let generator = generatorPolynomials[errorCodewordsCount - 1], result = new Array(errorCodewordsCount).concat(data), generatorPolynomial = new Array(result.length - generator.length).concat(generator), steps = data.length, errorCodewords = [], divisor, idx; for (idx = 0; idx < steps; idx++) { divisor = multiplyByConstant(generatorPolynomial, powersOfTwo[result[result.length - 1]]); generatorPolynomial.splice(0, 1); result = xorPolynomials(divisor, result); } for (idx = result.length - 1; idx >= 0; idx--) { errorCodewords[errorCodewordsCount - 1 - idx] = toBitsString(result[idx], 8); } return errorCodewords; } export function getBlocks(dataStream, versionCodewordsInformation) { let codewordStart = 0, dataBlocks = [], errorBlocks = [], dataBlock, versionGroups = versionCodewordsInformation.groups, blockCodewordsCount, groupBlocksCount, messagePolynomial, codeword; for (let groupIdx = 0; groupIdx < versionGroups.length; groupIdx++) { groupBlocksCount = versionGroups[groupIdx][0]; for (let blockIdx = 0; blockIdx < groupBlocksCount; blockIdx++) { blockCodewordsCount = versionGroups[groupIdx][1]; dataBlock = []; messagePolynomial = []; for (let codewordIdx = 1; codewordIdx <= blockCodewordsCount; codewordIdx++) { codeword = dataStream.substring(codewordStart, codewordStart + 8); dataBlock.push(codeword); messagePolynomial[blockCodewordsCount - codewordIdx] = toDecimal(codeword); codewordStart += 8; } dataBlocks.push(dataBlock); errorBlocks.push(generateErrorCodewords(messagePolynomial, versionCodewordsInformation.errorCodewordsPerBlock)); } } return [dataBlocks, errorBlocks]; } //fix case all zeros export function encodeFormatInformation(format) { let formatNumber = toDecimal(format), encodedString, result = ""; if (formatNumber === 0) { return "101010000010010"; } encodedString = encodeBCH(toDecimal(format), formatGeneratorPolynomial, 15); for (let i = 0; i < encodedString.length; i++) { result += encodedString.charAt(i) ^ formatMaskPattern.charAt(i); } return result; } export function encodeBCH(value, generatorPolynomial, codeLength) { let generatorNumber = toDecimal(generatorPolynomial), polynomialLength = generatorPolynomial.length - 1, valueNumber = value << polynomialLength, length = codeLength - polynomialLength, valueString = toBitsString(value, length), result = dividePolynomials(valueNumber, generatorNumber); result = valueString + toBitsString(result, polynomialLength); return result; } export function dividePolynomials(numberX, numberY) { let yLength = numberY.toString(2).length, xLength = numberX.toString(2).length; let x = numberX; do { x ^= numberY << xLength - yLength; xLength = x.toString(2).length; } while (xLength >= yLength); return x; } export function getNumberAt(str, idx) { return parseInt(str.charAt(idx), 10); } export function initMatrices(version) { let matrices = [], modules = 17 + 4 * version; for (let i = 0; i < maskPatternConditions.length; i++) { matrices[i] = new Array(modules); for (let j = 0; j < modules; j++) { matrices[i][j] = new Array(modules); } } return matrices; } export function addFormatInformation(matrices, formatString) { let matrix = matrices[0], x, y, idx = 0, length = formatString.length; for (x = 0, y = 8; x <= 8; x++) { if (x !== 6) { fillFunctionCell(matrices, getNumberAt(formatString, length - 1 - idx++), x, y); } } for (x = 8, y = 7; y >= 0; y--) { if (y !== 6) { fillFunctionCell(matrices, getNumberAt(formatString, length - 1 - idx++), x, y); } } idx = 0; for (y = matrix.length - 1, x = 8; y >= matrix.length - 8; y--) { fillFunctionCell(matrices, getNumberAt(formatString, length - 1 - idx++), x, y); } fillFunctionCell(matrices, 1, matrix.length - 8, 8); for (x = matrix.length - 7, y = 8; x < matrix.length; x++) { fillFunctionCell(matrices, getNumberAt(formatString, length - 1 - idx++), x, y); } } export function encodeVersionInformation(version) { return encodeBCH(version, versionGeneratorPolynomial, 18); } export function addVersionInformation(matrices, dataString) { let matrix = matrices[0], modules = matrix.length, x1 = 0, y1 = modules - 11, x2 = modules - 11, y2 = 0, quotient, mod, value; for (let idx = 0; idx < dataString.length; idx++) { quotient = Math.floor(idx / 3); mod = idx % 3; value = getNumberAt(dataString, dataString.length - idx - 1); fillFunctionCell(matrices, value, x1 + quotient, y1 + mod); fillFunctionCell(matrices, value, x2 + mod, y2 + quotient); } } export function addCentricPattern(matrices, pattern, x, y) { let size = pattern.length + 2, length = pattern.length + 1, value; for (let i = 0; i < pattern.length; i++) { for (let j = i; j < size - i; j++) { value = pattern[i]; fillFunctionCell(matrices, value, x + j, y + i); fillFunctionCell(matrices, value, x + i, y + j); fillFunctionCell(matrices, value, x + length - j, y + length - i); fillFunctionCell(matrices, value, x + length - i, y + length - j); } } } export function addFinderSeparator(matrices, direction, x, y) { let nextX = x, nextY = y, matrix = matrices[0]; do { fillFunctionCell(matrices, 0, nextX, y); fillFunctionCell(matrices, 0, x, nextY); nextX += direction[0]; nextY += direction[1]; } while (nextX >= 0 && nextX < matrix.length); } export function addFinderPatterns(matrices) { let modules = matrices[0].length; addCentricPattern(matrices, finderPattern, 0, 0); addFinderSeparator(matrices, [-1, -1], 7, 7); addCentricPattern(matrices, finderPattern, modules - 7, 0); addFinderSeparator(matrices, [1, -1], modules - 8, 7); addCentricPattern(matrices, finderPattern, 0, modules - 7); addFinderSeparator(matrices, [-1, 1], 7, modules - 8); } export function addAlignmentPatterns(matrices, version) { if (version < 2) { return; } let matrix = matrices[0], modules = matrix.length, pointsCount = Math.floor(version / 7), points = [6], startDistance, distance, idx = 0; if ((startDistance = irregularAlignmentPatternsStartDistance[version])) { distance = (modules - 13 - startDistance) / pointsCount; } else { startDistance = distance = (modules - 13) / (pointsCount + 1); } points.push(points[idx++] + startDistance); while ((points[idx] + distance) < modules) { points.push(points[idx++] + distance); } for (let i = 0; i < points.length; i++) { for (let j = 0; j < points.length; j++) { if (matrix[points[i]][points[j]] === undefined) { addCentricPattern(matrices, alignmentPattern, points[i] - 2, points[j] - 2); } } } } export function addTimingFunctions(matrices) { let row = 6, column = 6, value = 1, modules = matrices[0].length; for (let i = 8; i < modules - 8; i++) { fillFunctionCell(matrices, value, row, i); fillFunctionCell(matrices, value, i, column); value ^= 1; } } export function scoreMaskMatrixes(matrices) { let scores = [], previousBits = [], darkModules = [], patterns = [], adjacentSameBits = [], matrix, i, row = 0, column = 1, modulesLength = matrices[0].length; for (i = 0; i < matrices.length; i++) { scores[i] = 0; darkModules[i] = 0; adjacentSameBits[i] = [0, 0]; patterns[i] = [0, 0]; previousBits[i] = []; } for (let rowIndex = 0; rowIndex < modulesLength; rowIndex++) { for (let columnIndex = 0; columnIndex < modulesLength; columnIndex++) { for (let matrixIndex = 0; matrixIndex < matrices.length; matrixIndex++) { matrix = matrices[matrixIndex]; darkModules[matrixIndex] += parseInt(matrix[rowIndex][columnIndex], 10); if (previousBits[matrixIndex][row] === matrix[rowIndex][columnIndex] && rowIndex + 1 < modulesLength && columnIndex - 1 >= 0 && matrix[rowIndex + 1][columnIndex] === previousBits[matrixIndex][row] && matrix[rowIndex + 1][columnIndex - 1] === previousBits[matrixIndex][row]) { scores[matrixIndex] += 3; } scoreFinderPatternOccurance(matrixIndex, patterns, scores, row, matrix[rowIndex][columnIndex]); scoreFinderPatternOccurance(matrixIndex, patterns, scores, column, matrix[columnIndex][rowIndex]); scoreAdjacentSameBits(matrixIndex, scores, previousBits, matrix[rowIndex][columnIndex], adjacentSameBits, row); scoreAdjacentSameBits(matrixIndex, scores, previousBits, matrix[columnIndex][rowIndex], adjacentSameBits, column); } } } let total = modulesLength * modulesLength, minIdx, min = Number.MAX_VALUE; for (i = 0; i < scores.length; i++) { scores[i] += calculateDarkModulesRatioScore(darkModules[i], total); if (scores[i] < min) { min = scores[i]; minIdx = i; } } return minIdx; } export function scoreFinderPatternOccurance(idx, patterns, scores, rowColumn, bit) { patterns[idx][rowColumn] = ((patterns[idx][rowColumn] << 1) ^ bit) % 128; if (patterns[idx][rowColumn] === finderPatternValue) { scores[idx] += 40; } } export function scoreAdjacentSameBits(idx, scores, previousBits, bit, adjacentBits, rowColumn) { if (previousBits[idx][rowColumn] === bit) { adjacentBits[idx][rowColumn]++; } else { previousBits[idx][rowColumn] = bit; if (adjacentBits[idx][rowColumn] >= 5) { scores[idx] += 3 + adjacentBits[idx][rowColumn] - 5; } adjacentBits[idx][rowColumn] = 1; } } export function calculateDarkModulesRatioScore(darkModules, total) { let percent = Math.floor((darkModules / total) * 100), mod5 = percent % 5, previous = Math.abs(percent - mod5 - 50), next = Math.abs(percent + 5 - mod5 - 50), score = 10 * Math.min(previous / 5, next / 5); return score; } export function createQRCodeDataEncoder(encoding) { if (encoding && encoding.toLowerCase().indexOf("utf_8") >= 0) { return new Utf8Encoder(); } return new IsoEncoder(); } export function encodeData(inputString, errorCorrectionLevel, encoding) { let encoder = createQRCodeDataEncoder(encoding), encodingResult = encoder.getEncodingResult(inputString, errorCorrectionLevel), version = encodingResult.version, versionInformation = VersionsCodewordsInformation[version - 1][errorCorrectionLevel], dataString = padDataString(encodingResult.dataString, versionInformation.totalDataCodewords), blocks = getBlocks(dataString, versionInformation), matrices = initMatrices(version); addFinderPatterns(matrices); addAlignmentPatterns(matrices, version); addTimingFunctions(matrices); if (version >= 7) { addVersionInformation(matrices, toBitsString(0, 18)); } addFormatInformation(matrices, toBitsString(0, 15)); fillData(matrices, blocks); let minIdx = scoreMaskMatrixes(matrices), optimalMatrix = matrices[minIdx]; if (version >= 7) { addVersionInformation([optimalMatrix], encodeVersionInformation(version)); } let formatString = errorCorrectionPatterns[errorCorrectionLevel] + toBitsString(minIdx, 3); addFormatInformation([optimalMatrix], encodeFormatInformation(formatString)); return optimalMatrix; }