// export * from '../qrcode' export * from '@/uni_modules/lime-qrcodegen/utssdk/interface'; import { type QRCodePropsTypes , type ImageSettings, type QRCodeCallback } from '@/uni_modules/lime-qrcodegen/utssdk/interface' import { qrcodegen } from './qrcodegen' const Ecc = qrcodegen.QrCode.Ecc const QrCode = qrcodegen.QrCode type Modules = boolean[][]; type Excavation = { x : number; y : number; w : number; h : number }; type ImageSettingExcavation = { x: number y: number h: number w: number excavation: Excavation } const ERROR_LEVEL_MAP = { L: Ecc.LOW, M: Ecc.MEDIUM, Q: Ecc.QUARTILE, H: Ecc.HIGH, }; const DEFAULT_SIZE:number = 128; const DEFAULT_LEVEL:string = 'L'; // const DEFAULT_BGCOLOR:string = '#FFFFFF'; const DEFAULT_FGCOLOR:string = '#000000'; const DEFAULT_INCLUDEMARGIN:boolean = false; const SPEC_MARGIN_SIZE : number = 4; const DEFAULT_MARGIN_SIZE : number = 0; // This is *very* rough estimate of max amount of QRCode allowed to be covered. // It is "wrong" in a lot of ways (area is a terrible way to estimate, it // really should be number of modules covered), but if for some reason we don't // get an explicit height or width, I'd rather default to something than throw. const DEFAULT_IMG_SCALE = 0.1; // We could just do this in generatePath, except that we want to support // non-Path2D canvas, so we need to keep it an explicit step. function excavateModules(modules : Modules, excavation : Excavation) : Modules { const ox = excavation.x const oy = excavation.y const oh = excavation.h const ow = excavation.w return modules.slice().map((row, y):boolean[] => { if (y < oy || y >= oy + oh) { return row; } return row.map((cell, x):boolean => { if (x < ox || x >= ox + ow) { return cell; } return false; }); }); } function getImageSettings( cells : Modules, size : number, margin : number, imageSettings: ImageSettings | null ) : ImageSettingExcavation | null { if (imageSettings == null) { return null; } // const result : UTSJSONObject = {} const numCells = cells.length + margin * 2; const defaultSize = Math.floor(size * DEFAULT_IMG_SCALE); const scale = numCells / size; const width = imageSettings.width const height = imageSettings.height const ox = imageSettings.x const oy = imageSettings.y const excavate = imageSettings.excavate const w = (width > 0 ? width : defaultSize) * scale; const h = (height > 0 ? height: defaultSize) * scale; const x = ox == null ? cells.length / 2 - w / 2 : ox * scale; const y = oy == null ? cells.length / 2 - h / 2 : oy * scale; let excavation: Excavation = { x: 0, y: 0, w: 0, h: 0, }; if (excavate) { let floorX = Math.floor(x); let floorY = Math.floor(y); let ceilW = Math.ceil(w + x - floorX); let ceilH = Math.ceil(h + y - floorY); // excavation = { x: floorX, y: floorY, w: ceilW, h: ceilH }; excavation.x = floorX excavation.y = floorY excavation.w = ceilW excavation.h = ceilH } return { x, y, h, w, excavation } as ImageSettingExcavation; } function getMarginSize(includeMargin : boolean, marginSize: number|null) : number { if (marginSize != null) { return Math.floor(marginSize); } return includeMargin ? SPEC_MARGIN_SIZE : DEFAULT_MARGIN_SIZE; } export class QRCodeCanvas { ctx : DrawableContext constructor(ctx : DrawableContext) { this.ctx = ctx } render(props : QRCodePropsTypes, cb : QRCodeCallback | null=null) { const ctx = this.ctx const value = props.value const size = props.size ?? DEFAULT_SIZE const level = props.level ?? DEFAULT_LEVEL const fgColor = props.fgColor ?? DEFAULT_FGCOLOR const includeMargin = props.includeMargin || DEFAULT_INCLUDEMARGIN const marginSize = props.marginSize const imageSettings = props.imageSettings if (value == null || value == '') { return; } let cells = QrCode.encodeText(value, ERROR_LEVEL_MAP[level] as Ecc).getModules(); const margin = getMarginSize(includeMargin, marginSize); const numCells = cells.length + margin * 2; const scale = (size / numCells); const calculatedImageSettings = getImageSettings( cells, size, margin, imageSettings ) const haveImageToRender = calculatedImageSettings != null const excavation: Excavation | null = haveImageToRender ? calculatedImageSettings?.excavation : null if(haveImageToRender && excavation != null) { cells = excavateModules(cells, excavation); } ctx.reset() // ctx.clearRect(0, 0, size, size) // ctx.fillStyle = bgColor; // ctx.fillRect(0, 0, numCells, numCells); ctx.fillStyle = fgColor; cells.forEach(function (row, rdx) { row.forEach(function (cell, cdx) { if (cell) { ctx.fillRect((cdx + margin) * scale, (rdx + margin) * scale, scale, scale); } }); }); ctx.update() if(cb != null){ cb(cells) } } }