UNPKG

@nuintun/qrcode

Version:

A pure JavaScript QRCode encode and decode library.

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