"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { AbstractQRCodeWithImage: () => AbstractQRCodeWithImage, QRCodeCanvas: () => QRCodeCanvas, QRCodeRaw: () => QRCodeRaw, QRCodeSVG: () => QRCodeSVG, QRCodeText: () => QRCodeText }); module.exports = __toCommonJS(src_exports); // src/utils/invariant.ts function invariant(condition, message) { if (condition) return; throw new Error(message); } // src/utils/ColorUtils.ts var ColorUtils = class { static convertHexColorToBytes(hexColor) { invariant( typeof hexColor === "string", `Expected hexColor param to be a string instead got ${typeof hexColor}` ); let hex = hexColor.replace("#", ""); const isHexColor = /^([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$/i.test(hex); invariant( isHexColor, `Expected hexColor to be of length 3, 4, 6 or 8 with 0-9 A-F characters, instead got ${hex} with length ${hex.length}` ); const bytes = []; if (hex.length === 3) { hex += "F"; } else if (hex.length === 6) { hex += "FF"; } if (hex.length === 4) { bytes.push(...hex.split("").map((h) => parseInt(h.repeat(2), 16))); } else if (hex.length === 8) { bytes.push(parseInt(hex.substring(0, 2), 16)); bytes.push(parseInt(hex.substring(2, 4), 16)); bytes.push(parseInt(hex.substring(4, 6), 16)); bytes.push(parseInt(hex.substring(6, 8), 16)); } return bytes; } }; // src/QRCodeRaw.ts var import_qr = require("@akamfoad/qr"); var ERROR_CORRECTION_LEVEL_LOW = "L"; var DEFAULT_CONSTRUCTOR_PARAMS = { level: ERROR_CORRECTION_LEVEL_LOW, padding: 1, invert: false, typeNumber: 0, errorsEnabled: false }; var QRCodeRaw = class { value; level; typeNumber; padding; errorsEnabled; invert; qrCodeData; constructor(value, options = {}) { const params = { ...DEFAULT_CONSTRUCTOR_PARAMS, ...options }; this.value = value; this.level = params.level; this.typeNumber = params.typeNumber; this.padding = params.padding; this.invert = params.invert; this.errorsEnabled = params.errorsEnabled; } setValue(value) { this.value = value; this._clearCache(); } getDataSize() { const data = this.getData(); return data ? data.length : 0; } _clearCache() { this.qrCodeData = null; } _getQrCodeData(modules) { const qrCodeData = []; const padding = this.padding; const invert = this.invert; const rowPadding = Array(padding * 2 + modules.length).fill( invert ); const rowsPadding = Array(padding).fill(rowPadding); const columnPadding = Array(padding).fill(invert); if (padding) { qrCodeData.push(...rowsPadding); } modules.forEach((row) => { const qrCodeRow = []; qrCodeRow.push( ...columnPadding, ...row.map((isBlack) => invert ? !isBlack : isBlack), ...columnPadding ); qrCodeData.push(qrCodeRow); }); if (padding) { qrCodeData.push(...rowsPadding); } return qrCodeData; } getData() { if (!this.qrCodeData) { try { const qrcode = new import_qr.QRCode( this.typeNumber, import_qr.ErrorCorrectLevel[this.level] ); qrcode.addData(this.value); qrcode.make(); if (!qrcode.modules) { return null; } this.qrCodeData = this._getQrCodeData(qrcode.modules); Object.freeze(this.qrCodeData); } catch (error) { if (this.errorsEnabled) { throw error; } return null; } } return this.qrCodeData; } }; // src/utils/DimensionUtils.ts var DimensionUtils = class { static calculateDimension(value, canvasSize) { const isNumber = typeof value === "number"; const isString = typeof value === "string"; invariant( isNumber || isString, `value must be either string or number, instead got ${typeof value}` ); if (isNumber) { return value; } if (value.indexOf("%") > 0) { return Math.round(parseFloat(value) / 100 * canvasSize) || 0; } return parseFloat(value) || 0; } static calculatePosition(value, size, canvasSize) { const isNumber = typeof value === "number"; const isString = typeof value === "string"; invariant( isNumber || isString, `value must be either string or number, instead got ${typeof value}` ); if (isNumber) return value; if (value === "left" || value === "top") { return 0; } if (value === "right" || value === "bottom") { return canvasSize - size; } if (value === "center") { return Math.round((canvasSize - size) / 2); } const match = value.match( /^(?:(right|bottom|left|top)\s+)?(-?[0-9.]+)(%)?$/ ); invariant(!!match, `Expected position with number, instead got ${value}`); const isRight = match[1] === "right" || match[1] === "bottom"; const isPercent = !!match[3]; let val = parseFloat(match[2]) || 0; if (isPercent) { val = Math.round(val / 100 * canvasSize); } if (isRight) { val = canvasSize - val - size; } return Math.round(val); } }; // src/AbstractQRCodeWithImage.ts var DEFAULT_OPTIONS = { image: null }; var DEFAULT_IMAGE_BORDER = 1; var AbstractQRCodeWithImage = class extends QRCodeRaw { image = null; imageConfig = null; constructor(value, options = {}) { super(value, options); const params = { ...DEFAULT_OPTIONS, ...options }; this.image = params.image; } _clearCache() { super._clearCache(); this.imageConfig = null; } _getImageSource(imageConfig) { const source = imageConfig.source; if (typeof source === "string") { return source; } if (source instanceof Image) { return source.src; } if (source instanceof HTMLCanvasElement) { return source.toDataURL(); } return null; } _getImageConfig() { if (this.imageConfig) { return this.imageConfig; } if (!this.image || !this.image.source || !this.image.width || !this.image.height) { return null; } const dataSize = this.getDataSize(); if (!dataSize) { return null; } const source = this._getImageSource(this.image); if (!source) { return null; } const dataSizeWithoutPadding = dataSize - this.padding * 2; const width = DimensionUtils.calculateDimension( this.image.width, dataSizeWithoutPadding ); const height = DimensionUtils.calculateDimension( this.image.height, dataSizeWithoutPadding ); const x = DimensionUtils.calculatePosition( // @ts-expect-error make types stronger this.image.x, width, dataSizeWithoutPadding ) + this.padding; const y = DimensionUtils.calculatePosition( // @ts-expect-error make types stronger this.image.y, height, dataSizeWithoutPadding ) + this.padding; let border = DEFAULT_IMAGE_BORDER; if (typeof this.image.border === "number" || this.image.border === null) { border = this.image.border; } this.imageConfig = { source, border, x, y, width, height }; return this.imageConfig; } getData() { if (this.qrCodeData) { return this.qrCodeData; } const data = super.getData(); if (!data) { return data; } const imageConfig = this._getImageConfig(); if (imageConfig !== null && imageConfig.width && imageConfig.height) { if (typeof imageConfig.border === "number") { const begX = Math.max(imageConfig.x - imageConfig.border, 0); const begY = Math.max(imageConfig.y - imageConfig.border, 0); const endX = Math.min( // @ts-expect-error make types stronger begX + imageConfig.width + imageConfig.border * 2, data.length ); const endY = Math.min( // @ts-expect-error make types stronger begY + imageConfig.height + imageConfig.border * 2, data.length ); for (let y = begY; y < endY; y += 1) { for (let x = begX; x < endX; x += 1) { data[y][x] = this.invert ? true : false; } } } } return data; } }; // src/loader/ImageLoader.ts var loadImage = (url) => { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = () => reject(img); img.src = url; }); }; // src/QRCodeCanvas.ts var DEFAULT_OPTIONS2 = { fgColor: "#000", bgColor: "#FFF", scale: 10, size: null }; var QRCodeCanvas = class extends AbstractQRCodeWithImage { fgColor; bgColor; scale; size; canvas; canvasContext; constructor(value, options = {}) { super(value, options); const params = { ...DEFAULT_OPTIONS2, ...options }; this.fgColor = params.fgColor; this.bgColor = params.bgColor; this.scale = params.scale; this.size = params.size; this.canvas = document.createElement("canvas"); const canvasContext = this.canvas.getContext("2d"); if (canvasContext === null) { throw new Error("canvas context is null"); } this.canvasContext = canvasContext; } _clearCache() { super._clearCache(); this.canvas.width = 0; } _getCanvasSize() { const dataSize = this.getDataSize(); if (!dataSize) { return null; } if (this.size) { return this.size; } if (this.scale) { return this.scale * dataSize; } return dataSize; } draw(canvas = null) { const dataSize = this.getDataSize(); if (!dataSize) { return null; } const data = this.getData(); if (!data) { return null; } const fgColor = ColorUtils.convertHexColorToBytes(this.fgColor); const bgColor = ColorUtils.convertHexColorToBytes(this.bgColor); let index = 0; const bytes = new Uint8ClampedArray(dataSize ** 2 * 4); data.forEach((row) => { row.forEach((isBlack) => { if (isBlack) { bytes.set(fgColor, index); } else { bytes.set(bgColor, index); } index += 4; }); }); const imageData = new ImageData(bytes, dataSize, dataSize); this.canvas.width = dataSize; this.canvas.height = dataSize; this.canvasContext.putImageData(imageData, 0, 0); const canvasSize = this._getCanvasSize(); const qrCodeCanvas = canvas || document.createElement("canvas"); qrCodeCanvas.width = canvasSize; qrCodeCanvas.height = canvasSize; const qrCodeCanvasContext = qrCodeCanvas.getContext("2d"); qrCodeCanvasContext.imageSmoothingEnabled = false; qrCodeCanvasContext.drawImage(this.canvas, 0, 0, canvasSize, canvasSize); const drawImageResult = this._drawImage( // @ts-expect-error make types stronger qrCodeCanvasContext, // @ts-expect-error make types stronger canvasSize / dataSize ); if (drawImageResult instanceof Promise) { return drawImageResult.then(() => { return qrCodeCanvas; }); } return qrCodeCanvas; } // @ts-expect-error make types stronger _getImageSource(imageConfig) { const source = imageConfig.source; if (typeof source === "string") { return loadImage(source).then((image) => { this.image.source = image; imageConfig.source = image; return image; }); } if (source instanceof Image) { return source; } if (source instanceof HTMLCanvasElement) { return source; } return null; } _drawImage(qrCodeCanvasContext, pixelSize) { const imageConfig = this._getImageConfig(); if (!imageConfig) { return null; } if (imageConfig.source instanceof Promise) { return imageConfig.source.then((image) => { qrCodeCanvasContext.drawImage( image, // @ts-expect-error make types stronger imageConfig.x * pixelSize, // @ts-expect-error make types stronger imageConfig.y * pixelSize, // @ts-expect-error make types stronger imageConfig.width * pixelSize, // @ts-expect-error make types stronger imageConfig.height * pixelSize ); }); } qrCodeCanvasContext.drawImage( imageConfig.source, // @ts-expect-error make types stronger imageConfig.x * pixelSize, // @ts-expect-error make types stronger imageConfig.y * pixelSize, // @ts-expect-error make types stronger imageConfig.width * pixelSize, // @ts-expect-error make types stronger imageConfig.height * pixelSize ); return true; } getCanvas() { return this.draw(); } toDataUrl(type = "image/png", encoderOptions = 0.92) { const canvasOrPromise = this.draw(); if (!canvasOrPromise) { return null; } if (canvasOrPromise instanceof Promise) { return canvasOrPromise.then((qrCodeCanvas) => { return qrCodeCanvas.toDataURL(type, encoderOptions); }); } return canvasOrPromise.toDataURL(type, encoderOptions); } }; // src/QRCodeSVG.ts var TYPE_INT_WHITE = 0; var TYPE_INT_BLACK = 1; var TYPE_INT_PROCESSED = 2; var DEFAULT_OPTIONS3 = { fgColor: "#000", bgColor: "#FFF" }; var QRCodeSVG = class extends AbstractQRCodeWithImage { fgColor; bgColor; qrCodeSVG = null; height; width; qrCodeDataUrl = null; constructor(value, options = {}) { super(value, options); const params = { ...DEFAULT_OPTIONS3, ...options }; this.fgColor = params.fgColor; this.bgColor = params.bgColor; this.width = params.width; this.height = params.height; } _clearCache() { super._clearCache(); this.qrCodeSVG = null; this.qrCodeDataUrl = null; } _getDataInt() { const data = this.getData(); if (!data) { return null; } return data.map((row) => { return row.map((isBlack) => { return isBlack ? TYPE_INT_BLACK : TYPE_INT_WHITE; }); }); } _getRects() { const dataInt = this._getDataInt(); if (!dataInt) { return null; } const rects = []; const count = dataInt.length - 1; for (let y = 0; y <= count; y += 1) { let begX = -1; for (let x = 0; x <= count; x += 1) { const intType = dataInt[y][x]; const isLast = x === count; const isBlack = intType === TYPE_INT_BLACK; if (isBlack && begX === -1) { begX = x; } if (begX !== -1 && (isLast || !isBlack)) { const endX = x - (isBlack ? 0 : 1); const rect = this._processRect(dataInt, begX, endX, y); if (rect) { rects.push(rect); } begX = -1; } } } return rects; } _processRect(dataInt, begX, endX, begY) { const count = dataInt.length - 1; let isNewRect = false; let isStopped = false; let height = 0; for (let y = begY; y <= count; y += 1) { for (let x = begX; x <= endX; x += 1) { const intType = dataInt[y][x]; if (intType === TYPE_INT_WHITE) { isStopped = true; break; } } if (isStopped) { break; } for (let x = begX; x <= endX; x += 1) { if (dataInt[y][x] === TYPE_INT_BLACK) { isNewRect = true; dataInt[y][x] = TYPE_INT_PROCESSED; } } height += 1; } if (!isNewRect) { return null; } return { x: begX, y: begY, width: endX - begX + 1, height }; } _getRelativeRects() { const rects = this._getRects(); if (!rects) { return null; } const relativeRects = []; const rectsMap = {}; let seqRectId = 0; rects.forEach((rect) => { const key = `${rect.width}:${rect.height}`; if (rectsMap[key]) { rectsMap[key].count += 1; if (!rectsMap[key].id) { rectsMap[key].id = `i${seqRectId.toString(32)}`; seqRectId += 1; } } else { rectsMap[key] = { count: 1, rect, relative: false, id: null }; } }); rects.forEach((rect) => { const key = `${rect.width}:${rect.height}`; const rectsMapItem = rectsMap[key]; if (rectsMapItem.relative) { relativeRects.push({ id: rectsMapItem.id, x: rect.x - rectsMapItem.rect.x, y: rect.y - rectsMapItem.rect.y }); } else { if (rectsMapItem.id) { rect.id = rectsMapItem.id; rectsMapItem.relative = true; } relativeRects.push(rect); } }); return relativeRects; } _buildSVG(rects) { const size = this.getDataSize(); const tags = [ `` ]; if (this.bgColor) { tags.push( `` ); } rects.forEach((rect) => { if (rect.width && rect.height) { const rectId = rect.id ? `id="${rect.id}" ` : ""; tags.push( `` ); } else { tags.push( `` ); } }); const imageConfig = this._getImageConfig(); if (imageConfig && imageConfig.width && imageConfig.height) { tags.push( `` ); } tags.push(""); return tags.join(""); } toString() { if (!this.qrCodeSVG) { const dataSize = this.getDataSize(); if (!dataSize) { return null; } const rects = this._getRects(); if (!rects) { return null; } this.qrCodeSVG = this._buildSVG(rects); } return this.qrCodeSVG; } toDataUrl() { if (!this.qrCodeDataUrl) { const dataSize = this.getDataSize(); if (!dataSize) { return null; } const relativeRects = this._getRelativeRects(); if (!relativeRects) { return null; } const svg = this._buildSVG(relativeRects); this.qrCodeDataUrl = `data:image/svg+xml;base64,${btoa(svg)}`; } return this.qrCodeDataUrl; } }; // src/QRCodeText.ts var DEFAULT_OPTIONS4 = { blackSymbol: "\u2593\u2593", whiteSymbol: " " }; var QRCodeText = class extends QRCodeRaw { blackSymbol; whiteSymbol; qrCodeText; constructor(value, options = {}) { super(value, options); const params = { ...DEFAULT_OPTIONS4, ...options }; this.blackSymbol = params.blackSymbol; this.whiteSymbol = params.whiteSymbol; } _clearCache() { super._clearCache(); this.qrCodeText = null; } toString() { if (this.qrCodeText) { return this.qrCodeText; } const dataSize = this.getDataSize(); if (!dataSize) { return null; } const data = this.getData(); if (data === null) { return null; } const symbols = []; for (let y = 0; y < dataSize; y += 1) { for (let x = 0; x < dataSize; x += 1) { const isBlack = data[y][x]; symbols.push(isBlack ? this.blackSymbol : this.whiteSymbol); } symbols.push("\n"); } this.qrCodeText = symbols.join(""); return this.qrCodeText; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AbstractQRCodeWithImage, QRCodeCanvas, QRCodeRaw, QRCodeSVG, QRCodeText });