@nuintun/qrcode
Version:
A pure JavaScript QRCode encode and decode library.
105 lines (100 loc) • 3.43 kB
JavaScript
/**
* @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;