UNPKG

@nuintun/qrcode

Version:

A pure JavaScript QRCode encode and decode library.

127 lines (122 loc) 5.3 kB
/** * @module QRCode * @package @nuintun/qrcode * @license MIT * @version 5.0.2 * @author nuintun <nuintun@qq.com> * @description A pure JavaScript QRCode encode and decode library. * @see https://github.com/nuintun/qrcode#readme */ 'use strict'; const Pattern = require('./Pattern.cjs'); const Detected = require('./Detected.cjs'); const utils = require('../common/utils.cjs'); const transform = require('./utils/transform.cjs'); const timing = require('./utils/timing.cjs'); const FinderPatternGroup = require('./FinderPatternGroup.cjs'); const PatternRatios = require('./PatternRatios.cjs'); const FinderPatternFinder = require('./FinderPatternFinder.cjs'); const AlignmentPatternFinder = require('./AlignmentPatternFinder.cjs'); const Version = require('../common/Version.cjs'); /** * @module Detector */ function getExpectAlignment(finderPatternGroup) { const { x, y } = finderPatternGroup.topLeft; const size = FinderPatternGroup.FinderPatternGroup.size(finderPatternGroup); const expectAlignmentCorrectionToTopLeftRatio = 1 - 3 / (size - 7); const bottomRight = FinderPatternGroup.FinderPatternGroup.bottomRight(finderPatternGroup); const [xModuleSize, yModuleSize] = FinderPatternGroup.FinderPatternGroup.moduleSizes(finderPatternGroup); const expectAlignmentX = x + (bottomRight.x - x) * expectAlignmentCorrectionToTopLeftRatio; const expectAlignmentY = y + (bottomRight.y - y) * expectAlignmentCorrectionToTopLeftRatio; return new Pattern.Pattern( PatternRatios.ALIGNMENT_PATTERN_RATIOS, expectAlignmentX, expectAlignmentY, xModuleSize * 5, yModuleSize * 5, 0 ); } function findAlignmentInRegion(matrix, finderPatternGroup, strict) { const size = FinderPatternGroup.FinderPatternGroup.size(finderPatternGroup); const scanAllowanceRatio = Math.min(20, utils.toInt32(size / 4)); const expectAlignment = getExpectAlignment(finderPatternGroup); const alignmentFinder = new AlignmentPatternFinder.AlignmentPatternFinder(matrix, strict); const moduleSize = FinderPatternGroup.FinderPatternGroup.moduleSize(finderPatternGroup); const { x: expectAlignmentX, y: expectAlignmentY } = expectAlignment; const alignmentAreaAllowanceSize = Math.ceil(moduleSize * scanAllowanceRatio); const alignmentAreaTop = utils.toInt32(Math.max(0, expectAlignmentY - alignmentAreaAllowanceSize)); const alignmentAreaLeft = utils.toInt32(Math.max(0, expectAlignmentX - alignmentAreaAllowanceSize)); const alignmentAreaRight = utils.toInt32(Math.min(matrix.width - 1, expectAlignmentX + alignmentAreaAllowanceSize)); const alignmentAreaBottom = utils.toInt32(Math.min(matrix.height - 1, expectAlignmentY + alignmentAreaAllowanceSize)); alignmentFinder.find( alignmentAreaLeft, alignmentAreaTop, alignmentAreaRight - alignmentAreaLeft, alignmentAreaBottom - alignmentAreaTop ); return alignmentFinder.filter(expectAlignment, moduleSize); } class Detector { #options; /** * @constructor * @param options The options of detector. */ constructor(options = {}) { this.#options = options; } /** * @method detect Detect the binarized image matrix. * @param matrix The binarized image matrix. */ *detect(matrix) { const { strict } = this.#options; const { width, height } = matrix; const finderFinder = new FinderPatternFinder.FinderPatternFinder(matrix, strict); finderFinder.find(0, 0, width, height); const finderPatternGroups = finderFinder.groups(); let current = finderPatternGroups.next(); while (!current.done) { let succeed = false; const finderPatternGroup = current.value; const size = FinderPatternGroup.FinderPatternGroup.size(finderPatternGroup); // Find alignment. if (size >= Version.MIN_VERSION_SIZE_WITH_ALIGNMENTS) { // Kind of arbitrary -- expand search radius before giving up // If we didn't find alignment pattern... well try anyway without it. const alignmentPatterns = findAlignmentInRegion(matrix, finderPatternGroup, strict); // Founded alignment. for (const alignmentPattern of alignmentPatterns) { const transform$1 = transform.createTransform(finderPatternGroup, alignmentPattern); if ( // Top left to top right. timing.checkMappingTimingLine(matrix, transform$1, size) && // Top left to bottom left. timing.checkMappingTimingLine(matrix, transform$1, size, true) ) { succeed = yield new Detected.Detected(matrix, transform$1, finderPatternGroup, alignmentPattern); // Succeed, skip next alignment pattern. if (succeed) { break; } } } } else { const transform$1 = transform.createTransform(finderPatternGroup); if ( // Top left to top right. timing.checkMappingTimingLine(matrix, transform$1, size) && // Top left to bottom left. timing.checkMappingTimingLine(matrix, transform$1, size, true) ) { // No alignment pattern version. succeed = yield new Detected.Detected(matrix, transform$1, finderPatternGroup); } } current = finderPatternGroups.next(succeed); } } } exports.Detector = Detector;