UNPKG

@nuintun/qrcode

Version:

A pure JavaScript QRCode encode and decode library.

116 lines (112 loc) 3.48 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 */ import { getSegmentLength, appendECI, appendFNC1Info, appendModeInfo, isHanziMode, chooseRecommendVersion, calculateBitsNeeded, willFit, appendLengthInfo, appendTerminator, injectECCodewords, chooseBestMaskAndMatrix } from './utils/encoder.js'; import { Encoded } from './Encoded.js'; import { Charset } from '../common/Charset.js'; import { ECLevel } from '../common/ECLevel.js'; import { BitArray } from '../common/BitArray.js'; import { VERSIONS } from '../common/Version.js'; import { encode } from '../common/encoding/index.js'; import { assertHints, assertLevel, assertVersion } from './utils/asserts.js'; /** * @module Encoder */ class Encoder { #hints; #level; #encode; #version; /** * @constructor * @param options The options of encoder. */ constructor({ hints = {}, level = 'L', version = 'Auto', encode: encode$1 = encode } = {}) { assertHints(hints); assertLevel(level); assertVersion(version); this.#hints = hints; this.#encode = encode$1; this.#version = version; this.#level = ECLevel[level]; } /** * @method encode * @description Encode the segments. * @param segments The segments. */ encode(...segments) { const ecLevel = this.#level; const encode = this.#encode; const { fnc1 } = this.#hints; const versionNumber = this.#version; const segmentBlocks = []; // Only append FNC1 once. let isFNC1Appended = false; // Current ECI value. let [currentECIValue] = Charset.ISO_8859_1.values; // Init segments. for (const segment of segments) { const { mode } = segment; const head = new BitArray(); const body = segment.encode(encode); const length = getSegmentLength(segment, body); // Append ECI segment if applicable. currentECIValue = appendECI(head, segment, currentECIValue); // Append FNC1 if applicable. if (fnc1 != null && !isFNC1Appended) { isFNC1Appended = true; appendFNC1Info(head, fnc1); } // With ECI in place, Write the mode marker. appendModeInfo(head, mode); // If is Hanzi mode append GB2312 subset. if (isHanziMode(segment)) { head.append(1, 4); } // Push segment block. segmentBlocks.push({ mode, head, body, length }); } let version; if (versionNumber === 'Auto') { version = chooseRecommendVersion(segmentBlocks, ecLevel); } else { version = VERSIONS[versionNumber - 1]; const bitsNeeded = calculateBitsNeeded(segmentBlocks, version); if (!willFit(bitsNeeded, version, ecLevel)) { throw new Error('data too big for requested version'); } } const buffer = new BitArray(); for (const { mode, head, body, length } of segmentBlocks) { buffer.append(head); appendLengthInfo(buffer, mode, version, length); buffer.append(body); } const ecBlocks = version.getECBlocks(ecLevel); appendTerminator(buffer, ecBlocks.numTotalDataCodewords); const codewords = injectECCodewords(buffer, ecBlocks); const [mask, matrix] = chooseBestMaskAndMatrix(codewords, version, ecLevel); return new Encoded(matrix, version, ecLevel, mask); } } export { Encoder };