Search code examples
javascriptencryptionqr-codergba

Why I cannot decode QR codes in Javascript?


I am developing angular application and trying to decode image with QR code on client side only and facing with next errors.

I have next flow:

  1. User uploads image.
  2. I decode qr code from image.

<input type="file" name="file" id="file" accept="image/*"(change)="qrCodeUploaded($event.target.files)"/>

I have tried next libraries:

  1. https://github.com/zxing-js/library
  qrCodeUploaded(files: FileList): void {
    const fileReader = new FileReader();
    const codeReader = new BrowserQRCodeReader();
    fileReader.readAsDataURL(files[0]);

    fileReader.onload = (e: any) => {
        var image = document.createElement("img");
        image.src = e.target.result;
        setTimeout(() => codeReader.decodeFromImage(image,  e.target.result).then(res => console.log(res)), 100);
    };
  }

Works for some of qr codes, but issues on mobile. If you will take a photo of QR code with your phone, it will be not decoded. So for mobile you will need to implement video.

  1. https://github.com/cozmo/jsQR
  qrCodeUploaded(files: FileList): void {
    const fileReader = new FileReader();
    fileReader.readAsArrayBuffer(files[0]);
    fileReader.onload = (e: any) => {
      console.log(new Uint8ClampedArray(e.target.result));
      console.log(jsQR(new Uint8ClampedArray(e.target.result), 256, 256));
    };
  }

I get next error for any QR images I upload:

core.js:15714 ERROR Error: Malformed data passed to binarizer.
    at Object.binarize (jsQR.js:408)
    at jsQR (jsQR.js:368)

gist link: https://gist.github.com/er-ant/b5c75c822eb085e70035cf333bb0fb55

Please, tell me what I am doing wrong and propose some solution for QR codes decoding. Open for any thoughts :)


Solution

  • For second library I missed that it expects ImageData and I pass Binary Data.

    Thus, we have 3 solutions how to convert Binary Data to ImageData:

    1. Use createImageBitmap Kaiido solution with some updates, as proposed in comments doesn't work.
    qrCodeUploadedHandler(files: FileList): void {
      const file: File = files[0];
    
      createImageBitmap(files[0])
        .then(bmp  => {
          const canvas = document.createElement('canvas');
    
          const width: number = bmp.width;
          const height: number = bmp.height;
          canvas.width = bmp.width;
          canvas.height = bmp.height;
    
          const ctx = canvas.getContext('2d');
    
          ctx.drawImage(bmp, 0, 0);
          const qrCodeImageFormat = ctx.getImageData(0,0,bmp.width,bmp.height);
          const qrDecoded = jsQR(qrCodeImageFormat.data, qrCodeImageFormat.width, qrCodeImageFormat.height);
        });
    }
    
    1. Get ImageData from canvas.
    qrCodeUploadedHandler(files: FileList): void {
      const file: File = files[0];
      const fileReader: FileReader = new FileReader();
      fileReader.readAsDataURL(files[0]);
    
      fileReader.onload = (event: ProgressEvent) => {
        const img: HTMLImageElement = new Image();
        img.onload = () => {
          const canvas: HTMLCanvasElement = document.createElement('canvas');
          const width: number = img.width;
          const height: number = img.height;
    
          canvas.width = width;
          canvas.height = height;
    
          const canvasRenderingContext: CanvasRenderingContext2D = canvas.getContext('2d');
          console.log(canvasRenderingContext);
    
          canvasRenderingContext.drawImage(img, 0, 0);
    
          const qrCodeImageFormat: ImageData = canvasRenderingContext.getImageData(0, 0, width, height);
    
          const qrDecoded = jsQR(qrCodeImageFormat.data, qrCodeImageFormat.width, qrCodeImageFormat.height);
          canvas.remove();
        };
        img.onerror = () => console.error('Upload file of image format please.');
        img.src = (<any>event.target).result;
    }
    
    1. You can parse images with png.js and jpeg-js libraries for ImageData.