@qr-platform/qr-code.js
Version:
QRCode.js is a professional JavaScript/TypeScript library for creating customized QR codes, offering a blend of simplicity and sophistication. With versatile styling options—dot shapes, colors, gradients, embedded images, borders, and text—it enables you
2 lines (1 loc) • 5.61 kB
JavaScript
import{Resvg as e}from"@resvg/resvg-js";import{scanImageData as t}from"@undecaf/zbar-wasm";import{Image as a}from"image-js";let r=!1;const s=e=>{r&&console.debug("[QR Validator]",e)},i=new class{constructor(e={}){this.maxRetries=e.maxRetries??5,this.retryInterval=e.retryInterval??100,this.debug=e.debug??!1}log(e){this.debug&&console.log(`[QRValidatorZbarNode] ${e}`)}async validate(e,a=!1){try{if(!t)throw new Error("zbar-wasm library not found. Please ensure @undecaf/zbar-wasm is installed and loaded correctly.");return this.validateWithRetry(e,a)}catch(e){return this.log(`Validation error: ${e instanceof Error?e.message:String(e)}`),{isValid:!1,message:e instanceof Error?e.message:String(e),errorCode:"VALIDATION_ERROR"}}}sleep(e){return new Promise((t=>setTimeout(t,e)))}async processImageResvg(t,a){try{const r=new e(t,{fitTo:{mode:"zoom",value:2}}).render(),s=r.width,i=r.height,o=r.pixels,d=Buffer.from(o);if(a)for(let e=0;e<d.length;e+=4)d[e]=255-d[e],d[e+1]=255-d[e+1],d[e+2]=255-d[e+2];return{imageData:{data:new Uint8ClampedArray(d),width:s,height:i},metadata:{width:s,height:i}}}catch(e){throw this.log(`Image processing error: ${e instanceof Error?e.message:String(e)}`),e}}async validateWithRetry(e,t){let a=0,r=null;const s=e;let i=!1;do{a++;try{const e=this.processImageResvg,{imageData:o}=await e(s,t);if(this.log(`Processed image ${o.width}x${o.height} pixels`),this.debug&&await this.saveDebugImage(o,`debug/qr-attempt-${a}-${i?"inverted":"normal"}`),r=await this.decodeQR(o),this.log(`Barcode scan attempt ${a}: ${r.success?"Success":"Failed"}`),r.success)return r.attempts=a,{isValid:!0,data:r.data,format:r.format,attempts:a,isInverted:t}}catch(e){this.log(`Attempt ${a} failed: ${e instanceof Error?e.message:String(e)}`),r={success:!1,error:e instanceof Error?e.message:String(e),errorCode:"PROCESSING_ERROR"}}!t||i?a<this.maxRetries&&await this.sleep(this.retryInterval):(i=!0,this.log("Switching to inverted image for next attempt"))}while(a<this.maxRetries);return{isValid:!1,attempts:a,message:`No barcode detected after ${a} attempts`,errorCode:"DECODER_ERROR",validator:"ZBar"}}async validateImageData(e){let t=0,a=null;if(!(e&&e.data&&e.width&&e.height))return this.log("Invalid imageData object provided"),{isValid:!1,isInverted:!1,message:"Invalid imageData object provided",validator:"ZBar",errorCode:"INVALID_INPUT"};do{t++;try{if(this.log(`Attempting to decode ImageData ${e.width}x${e.height} pixels (Attempt ${t})`),this.debug&&await this.saveDebugImage(e,`debug/qr-imagedata-attempt-${t}`),a=await this.decodeQR(e),this.log(`ImageData scan attempt ${t}: ${a.success?"Success":"Failed"}`),a.success)return a.attempts=t,{isValid:!0,data:a.data,format:a.format,attempts:t,isInverted:!1}}catch(e){this.log(`Attempt ${t} failed: ${e instanceof Error?e.message:String(e)}`),a={success:!1,error:e instanceof Error?e.message:String(e),errorCode:"PROCESSING_ERROR"}}t<this.maxRetries&&await this.sleep(this.retryInterval)}while(t<this.maxRetries);return{isValid:!1,attempts:t,message:`No barcode detected from ImageData after ${t} attempts`,errorCode:"DECODER_ERROR",validator:"ZBar"}}async saveDebugImage(e,t){try{const r=new a(e.width,e.height,e.data,{kind:"RGBA"}),s=`${t}-${Date.now()}`;await r.save(`${s}.png`,{format:"png"}),this.log(`Debug image saved to: ${s}`)}catch(e){this.log(`Failed to save debug image: ${e instanceof Error?e.message:String(e)}`)}}async decodeQR(e){try{let a;try{a=await t(e)}catch(e){this.log(`Error decoding QR code: ${e}`)}if(a&&a.length>0){const e=a[0],t=e.decode();return this.log(`Detected: ${e.typeName} - ${t}`),{success:!0,data:t,format:e.typeName||"UNKNOWN",confidence:.9,count:a.length,allSymbols:a.map((e=>({data:e.decode(),type:e.typeName,points:e.points})))}}return{success:!1,error:"No barcode detected",errorCode:"NO_BARCODE_DETECTED"}}catch(e){return this.log("Decoder error:"),console.error(e instanceof Error?e.message:String(e)),{success:!1,error:`Decoder error: ${e instanceof Error?e.message:String(e)}`,errorCode:"DECODER_ERROR"}}}}({maxRetries:1,debug:!1,retryInterval:200}),o={validateZbar:async(e,t=!1)=>{r=t||r;const a=[{name:"Normal SVG",execute:async()=>{if(!e)throw new Error("No svg source");const t=await i.validate(e,!1);if(!t.isValid||!t.data)throw new Error("No text found in normal QR code");return{isValid:!0,isInverted:!1,data:t.data,validator:"ZBar",message:"Decoded successfully.",attempts:t.attempts??1}}},{name:"Inverted SVG",execute:async()=>{if(!e)throw new Error("No svg source for inverted attempt");const t=await i.validate(e,!0);if(!t.isValid||!t.data)throw new Error("No text found in inverted QR code");return{isValid:!0,isInverted:!0,data:t.data,message:"Decoded successfully (inverted).",validator:"ZBar",attempts:t.attempts}}}];for(const e of a)try{s(`Trying ${e.name} decode...`);const t=await e.execute();return r&&console.timeEnd("scan-validator"),t}catch(t){s(`${e.name} decode failed:`),s(t)}return{isValid:!1,message:"Failed to decode QR code after all attempts.",errorCode:"DECODER_ERROR",validator:"ZBar"}},validateZbarImageData:async(e,t=!1)=>{r=t||r;try{const t=await i.validateImageData(e);return{...t,isInverted:t.isInverted??!1,message:t.message||(t.isValid?"Decoded successfully from ImageData.":"Validation failed."),attempts:t.attempts??1,validator:"ZBar"}}catch(e){return s(`Error during Node Zbar ImageData validation: ${e}`),r&&console.timeEnd("scan-validator-node-imagedata"),{isValid:!1,isInverted:!1,validator:"ZBar",message:e instanceof Error?e.message:"Unknown error during Node Zbar ImageData validation",errorCode:"VALIDATION_ERROR"}}}};export{o as qrValidatorZbar};