Steps to Develop an Angular Passport MRZ Reader & Scanner

The Dynamsoft Label Recognizer JavaScript SDK offers a robust OCR engine capable of extracting MRZ (Machine Readable Zone) characters from passports, visas, and other travel documents. It is a key part of Dynamsoft’s Capture Vision Suite, which also includes the Barcode Reader, Camera Enhancer, and Document Normalizer. This article will guide you through integrating the SDK into an Angular web application, enabling MRZ scanning from both image files and live camera streams.

Angular MRZ Reader & Scanner

Prerequisites

Step 1: Install Dynamsoft Label Recognizer and Dependencies

  1. Install the SDK: Use npm to download and install the necessary packages dynamsoft-label-recognizer, dynamsoft-code-parser, dynamsoft-capture-vision-dnn, and dynamsoft-label-recognizer-data.

     npm i dynamsoft-label-recognizer dynamsoft-code-parser dynamsoft-capture-vision-dnn dynamsoft-label-recognizer-data
    

    Explanation

    • The dynamsoft-label-recognizer-data package contains pre-trained models for MRZ detection.
    • The dynamsoft-capture-vision-dnn package includes a neural network engine used by the dynamsoft-label-recognizer package.
    • The dynamsoft-code-parser package is responsible for parsing and extracting MRZ data.
  2. Configure Asset Path: Update angular.json to include the paths to the new packages.

     "assets": [
       "src/favicon.ico",
       "src/assets",
       ...
       {
         "glob": "**/*",
         "input": "./node_modules/dynamsoft-label-recognizer/dist",
         "output": "assets/dynamsoft-label-recognizer"
       },
       {
         "glob": "**/*",
         "input": "./node_modules/dynamsoft-code-parser/dist",
         "output": "assets/dynamsoft-code-parser"
       },
       {
         "glob": "**/*",
         "input": "./node_modules/dynamsoft-capture-vision-dnn/dist",
         "output": "assets/dynamsoft-capture-vision-dnn"
       },
       {
         "glob": "**/*",
         "input": "./node_modules/dynamsoft-label-recognizer-data/dist",
         "output": "assets/dynamsoft-label-recognizer-data"
       } 
     ],
    
  3. Update Resource Paths in TypeScript Code: Define the full resource paths in the CoreModule.engineResourcePaths object within the product-list.component.ts file.

     export function getFullUrl(endpoint: string): string {
         let baseUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}`;
         return `${baseUrl}${endpoint}`;
     }
    
     CoreModule.engineResourcePaths = {
       std: getFullUrl('assets/dynamsoft-capture-vision-std/'),
       dip: getFullUrl('assets/dynamsoft-image-processing/'),
       core: getFullUrl('assets/dynamsoft-core/'),
       license: getFullUrl('assets/dynamsoft-license/'),
       cvr: getFullUrl('assets/dynamsoft-capture-vision-router/'),
       dbr: getFullUrl('assets/dynamsoft-barcode-reader/'),
       dce: getFullUrl('assets/dynamsoft-camera-enhancer/'),
       ddn: getFullUrl('assets/dynamsoft-document-normalizer/'),
       dlr: getFullUrl('assets/dynamsoft-label-recognizer/'),
       dcp: getFullUrl('assets/dynamsoft-code-parser/'),
       dnn: getFullUrl('assets/dynamsoft-capture-vision-dnn/'),
       dlrData: getFullUrl('assets/dynamsoft-label-recognizer-data/'),
     };
    

Step 2: Add Angular MRZ Reader and MRZ Scanner Components

  1. Generate Components: Use the Angular CLI to create two new components: MRZ Reader and MRZ Scanner.

     ng generate component mrz-reader
     ng generate component mrz-scanner
    
  2. Configure Routing: Update the app-routing.module.ts file to include routes for the new components.

     import { NgModule } from '@angular/core';
     import { RouterModule, Routes } from '@angular/router';
     import { ProductListComponent } from './product-list/product-list.component';
     import { BarcodeReaderComponent } from './barcode-reader/barcode-reader.component';
     import { BarcodeScannerComponent } from './barcode-scanner/barcode-scanner.component';
     import { FileDetectionComponent } from './file-detection/file-detection.component';
     import { CameraDetectionComponent } from './camera-detection/camera-detection.component';
     import { MrzReaderComponent } from './mrz-reader/mrz-reader.component';
     import { MrzScannerComponent } from './mrz-scanner/mrz-scanner.component';
        
     const routes: Routes = [
       { path: '', component: ProductListComponent },
       { path: 'barcode-reader', component: BarcodeReaderComponent },
       { path: 'barcode-scanner', component: BarcodeScannerComponent },
       { path: 'file-detection', component: FileDetectionComponent },
       { path: 'camera-detection', component: CameraDetectionComponent },
       { path: 'mrz-reader', component: MrzReaderComponent },
       { path: 'mrz-scanner', component: MrzScannerComponent }
     ];
    
  3. Update Product List Template: Add navigation links to the new components in the product-list.component.html file.

     <h3>
       <div>
         <ng-template [ngIf]="product.id === 'reader'">
           <a [title]="product.name + ' details'" [routerLink]="['/barcode-reader']">>
                
           </a>
         </ng-template>
       </div>
    
       <div>
         <ng-template [ngIf]="product.id === 'scanner'">
           <a [title]="product.name + ' details'" [routerLink]="['/barcode-scanner']">>
                
           </a>
         </ng-template>
       </div>
    
       <div>
         <ng-template [ngIf]="product.id === 'file-detection'">
           <a [title]="product.name + ' details'" [routerLink]="['/file-detection']">>
                
           </a>
         </ng-template>
       </div>
    
       <div>
         <ng-template [ngIf]="product.id === 'camera-detection'">
           <a [title]="product.name + ' details'" [routerLink]="['/camera-detection']">>
                
           </a>
         </ng-template>
       </div>
    
       <div>
         <ng-template [ngIf]="product.id === 'mrz-reader'">
           <a [title]="product.name + ' details'" [routerLink]="['/mrz-reader']">>
                
           </a>
         </ng-template>
       </div>
    
       <div>
         <ng-template [ngIf]="product.id === 'mrz-scanner'">
           <a [title]="product.name + ' details'" [routerLink]="['/mrz-scanner']">>
                
           </a>
         </ng-template>
       </div>
    
     </h3>
    

Step 3: Configure the MRZ Recognition Template

The Dynamsoft Label Recognizer allows you to customize templates for various scenarios. For detecting MRZ characters in passports and other travel documents, we will create a template named ReadMRZ and save it to assets/template.json.

{
  "CaptureVisionTemplates": [
    {
      "Name": "ReadMRZ",
      "OutputOriginalImage": 0,
      "ImageROIProcessingNameArray": [
        "roi-mrz"
      ],
      "Timeout": 2000
    }
  ],
  "TargetROIDefOptions": [
    {
      "Name": "roi-mrz",
      "TaskSettingNameArray": [
        "task-mrz"
      ]
    }
  ],
  "TextLineSpecificationOptions": [
    {
      "Name": "tls-mrz-passport",
      "BaseTextLineSpecificationName": "tls-base",
      "StringLengthRange": [
        44,
        44
      ],
      "OutputResults": 1,
      "ExpectedGroupsCount": 1,
      "ConcatResults": 1,
      "ConcatSeparator": "\n",
      "SubGroups": [
        {
          "StringRegExPattern": "(P[A-Z<][A-Z<]{3}[A-Z<]{39}){(44)}",
          "StringLengthRange": [
            44,
            44
          ],
          "BaseTextLineSpecificationName": "tls-base"
        },
        {
          "StringRegExPattern": "([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{14}[0-9<][0-9]){(44)}",
          "StringLengthRange": [
            44,
            44
          ],
          "BaseTextLineSpecificationName": "tls-base"
        }
      ]
    },
    {
      "Name": "tls-mrz-visa-td3",
      "BaseTextLineSpecificationName": "tls-base",
      "StringLengthRange": [
        44,
        44
      ],
      "OutputResults": 1,
      "ExpectedGroupsCount": 1,
      "ConcatResults": 1,
      "ConcatSeparator": "\n",
      "SubGroups": [
        {
          "StringRegExPattern": "(V[A-Z<][A-Z<]{3}[A-Z<]{39}){(44)}",
          "StringLengthRange": [
            44,
            44
          ],
          "BaseTextLineSpecificationName": "tls-base"
        },
        {
          "StringRegExPattern": "([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{14}[A-Z0-9<]{2}){(44)}",
          "StringLengthRange": [
            44,
            44
          ],
          "BaseTextLineSpecificationName": "tls-base"
        }
      ]
    },
    {
      "Name": "tls-mrz-visa-td2",
      "BaseTextLineSpecificationName": "tls-base",
      "StringLengthRange": [
        36,
        36
      ],
      "OutputResults": 1,
      "ExpectedGroupsCount": 1,
      "ConcatResults": 1,
      "ConcatSeparator": "\n",
      "SubGroups": [
        {
          "StringRegExPattern": "(V[A-Z<][A-Z<]{3}[A-Z<]{31}){(36)}",
          "StringLengthRange": [
            36,
            36
          ],
          "BaseTextLineSpecificationName": "tls-base"
        },
        {
          "StringRegExPattern": "([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{8}){(36)}",
          "StringLengthRange": [
            36,
            36
          ],
          "BaseTextLineSpecificationName": "tls-base"
        }
      ]
    },
    {
      "Name": "tls-mrz-id-td2",
      "BaseTextLineSpecificationName": "tls-base",
      "StringLengthRange": [
        36,
        36
      ],
      "OutputResults": 1,
      "ExpectedGroupsCount": 1,
      "ConcatResults": 1,
      "ConcatSeparator": "\n",
      "SubGroups": [
        {
          "StringRegExPattern": "([ACI][A-Z<][A-Z<]{3}[A-Z<]{31}){(36)}",
          "StringLengthRange": [
            36,
            36
          ],
          "BaseTextLineSpecificationName": "tls-base"
        },
        {
          "StringRegExPattern": "([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{8}){(36)}",
          "StringLengthRange": [
            36,
            36
          ],
          "BaseTextLineSpecificationName": "tls-base"
        }
      ]
    },
    {
      "Name": "tls-mrz-id-td1",
      "BaseTextLineSpecificationName": "tls-base",
      "StringLengthRange": [
        30,
        30
      ],
      "OutputResults": 1,
      "ExpectedGroupsCount": 1,
      "ConcatResults": 1,
      "ConcatSeparator": "\n",
      "SubGroups": [
        {
          "StringRegExPattern": "([ACI][A-Z<][A-Z<]{3}[A-Z0-9<]{9}[0-9][A-Z0-9<]{15}){(30)}",
          "StringLengthRange": [
            30,
            30
          ],
          "BaseTextLineSpecificationName": "tls-base"
        },
        {
          "StringRegExPattern": "([0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z<]{3}[A-Z0-9<]{11}[0-9]){(30)}",
          "StringLengthRange": [
            30,
            30
          ],
          "BaseTextLineSpecificationName": "tls-base"
        },
        {
          "StringRegExPattern": "([A-Z<]{30}){(30)}",
          "StringLengthRange": [
            30,
            30
          ],
          "BaseTextLineSpecificationName": "tls-base"
        }
      ]
    },
    {
      "Name": "tls-base",
      "CharacterModelName": "MRZ",
      "CharHeightRange": [
        5,
        1000,
        1
      ],
      "BinarizationModes": [
        {
          "BlockSizeX": 30,
          "BlockSizeY": 30,
          "Mode": "BM_LOCAL_BLOCK",
          "EnableFillBinaryVacancy": 0,
          "ThresholdCompensation": 15
        }
      ],
      "ConfusableCharactersCorrection": {
        "ConfusableCharacters": [
          [
            "0",
            "O"
          ],
          [
            "1",
            "I"
          ],
          [
            "5",
            "S"
          ]
        ],
        "FontNameArray": [
          "OCR_B"
        ]
      }
    }
  ],
  "LabelRecognizerTaskSettingOptions": [
    {
      "Name": "task-mrz",
      "ConfusableCharactersPath": "ConfusableChars.data",
      "TextLineSpecificationNameArray": [
        "tls-mrz-passport",
        "tls-mrz-visa-td3",
        "tls-mrz-id-td1",
        "tls-mrz-id-td2",
        "tls-mrz-visa-td2"
      ],
      "SectionImageParameterArray": [
        {
          "Section": "ST_REGION_PREDETECTION",
          "ImageParameterName": "ip-mrz"
        },
        {
          "Section": "ST_TEXT_LINE_LOCALIZATION",
          "ImageParameterName": "ip-mrz"
        },
        {
          "Section": "ST_TEXT_LINE_RECOGNITION",
          "ImageParameterName": "ip-mrz"
        }
      ]
    }
  ],
  "CharacterModelOptions": [
    {
      "DirectoryPath": "",
      "Name": "MRZ"
    }
  ],
  "ImageParameterOptions": [
    {
      "Name": "ip-mrz",
      "TextureDetectionModes": [
        {
          "Mode": "TDM_GENERAL_WIDTH_CONCENTRATION",
          "Sensitivity": 8
        }
      ],
      "BinarizationModes": [
        {
          "EnableFillBinaryVacancy": 0,
          "ThresholdCompensation": 21,
          "Mode": "BM_LOCAL_BLOCK"
        }
      ],
      "TextDetectionMode": {
        "Mode": "TTDM_LINE",
        "CharHeightRange": [
          5,
          1000,
          1
        ],
        "Direction": "HORIZONTAL",
        "Sensitivity": 7
      }
    }
  ]
}

Step 4: Recognize and Extract MRZ Characters from Image Files

Basic Steps

  1. Upload an image file.
  2. Recognize the MRZ characters with the Dynamsoft Capture Vision SDK.
  3. Extract the MRZ data from the recognized text using the Dynamsoft Code Parser SDK.
  4. Display the results in a text area.

UI Design

Add the following HTML code to the mrz-reader.component.html file:

<div id="loading-indicator" class="loading-indicator" *ngIf="!isLoaded">
    <div class="spinner"></div>
</div>

<div>
    <input type="file" title="file" id="file" accept="image/*" (change)="onChange($event)" />
</div>

<div class="container" id="file_container">


    <div class="row">
        <div id="imageview" class="imageview">
            <img id="image" alt="" />
            <canvas id="overlay" class="overlay"></canvas>

        </div>
    </div>



</div>

<div>
    <textarea id="detection_result"></textarea>
</div>

Explanation

  • The file input element allows users to upload an image file.
  • The image element displays the uploaded image.
  • The overlay canvas draws the text area boundaries.
  • The text area displays the MRZ data extracted from the image.

Component Implementation

  1. Initialize the SDK: Create an instance of CaptureVisionRouter and load the MRZ template.

     ngOnInit(): void {
       this.overlayManager.initOverlay(document.getElementById('overlay') as HTMLCanvasElement);
    
       (async () => {
         try {
           await CodeParserModule.loadSpec("MRTD_TD1_ID");
           await CodeParserModule.loadSpec("MRTD_TD2_FRENCH_ID");
           await CodeParserModule.loadSpec("MRTD_TD2_ID");
           await CodeParserModule.loadSpec("MRTD_TD2_VISA");
           await CodeParserModule.loadSpec("MRTD_TD3_PASSPORT");
           await CodeParserModule.loadSpec("MRTD_TD3_VISA");
    
           await LabelRecognizerModule.loadRecognitionData("MRZ");
           this.cvr = await CaptureVisionRouter.createInstance();
           this.parser = await CodeParser.createInstance();
           let ret = await this.cvr.initSettings('assets/template.json');
           console.log(ret);
         }
         catch (ex) {
           alert(ex);
         }
         this.isLoaded = true;
       })();
     }
    
    
  2. Load an image file:

     onChange(event: Event) {
       let parser = this.parser;
       const element = event.currentTarget as HTMLInputElement;
       let textarea = document.getElementById('detection_result') as HTMLTextAreaElement;
       let fileList: FileList | null = element.files;
       if (fileList) {
         let file = fileList.item(0) as any;
         if (file) {
           let fr = new FileReader();
           fr.onload = (event: any) => {
             let image = document.getElementById('image') as HTMLImageElement;
             if (image) {
               image.src = event.target.result;
               const img = new Image();
               textarea.value = '';
               img.onload = (event: any) => {
                 this.overlayManager.updateOverlay(img.width, img.height);
                 // TODO
               };
               img.src = event.target.result;
             }
           };
           fr.readAsDataURL(file);
         }
       }
     }
    
  3. Recognize MRZ: Call the capture method with the ReadMRZ template to recognize MRZ from the image file.

     if (this.cvr) {
       this.cvr.capture(file, "ReadMRZ").then(async (result: CapturedResult) => {
         let txts: any = [];
         try {
           let items = result.items;
           if (items.length > 0) {
    
             for (var i = 0; i < items.length; ++i) {
               if (items[i].type !== EnumCapturedResultItemType.CRIT_TEXT_LINE) {
                 continue; // check if captured result item is a barcode
               }
    
               // TODO
    
               break;
             }
    
           }
         } catch (e) {
           alert(e);
         }
    
         if (txts.length === 0) {
           textarea.value = 'No MRZ detected.';
         }
       });
     }
    
  4. Display the Results: Cast the CapturedResult to TextLineResultItem and get the MRZ characters using the CodeParser instance.

     let item = items[i] as TextLineResultItem;
     let localization = item.location;
     this.overlayManager.drawOverlay(
       localization,
       ''
     );
    
     txts.push(item.text);
    
     textarea.value = txts.join('') + '\n';
    
     let parseResults = await parser?.parse(item.text);
     textarea.value += JSON.stringify(handleMrzParseResult(parseResults!)) + '\n';
    
     export function handleMrzParseResult(result: ParsedResultItem): any {
         const parseResultInfo: any = {};
         let type = result.getFieldValue("documentCode");
         parseResultInfo['Document Type'] = JSON.parse(result.jsonString).CodeType;
         let nation = result.getFieldValue("issuingState");
         parseResultInfo['Issuing State'] = nation;
         let surName = result.getFieldValue("primaryIdentifier");
         parseResultInfo['Surname'] = surName;
         let givenName = result.getFieldValue("secondaryIdentifier");
         parseResultInfo['Given Name'] = givenName;
         let passportNumber = type === "P" ? result.getFieldValue("passportNumber") : result.getFieldValue("documentNumber");
         parseResultInfo['Passport Number'] = passportNumber;
         let nationality = result.getFieldValue("nationality");
         parseResultInfo['Nationality'] = nationality;
         let gender = result.getFieldValue("sex");
         parseResultInfo["Gender"] = gender;
         let birthYear = result.getFieldValue("birthYear");
         let birthMonth = result.getFieldValue("birthMonth");
         let birthDay = result.getFieldValue("birthDay");
         if (parseInt(birthYear) > (new Date().getFullYear() % 100)) {
             birthYear = "19" + birthYear;
         } else {
             birthYear = "20" + birthYear;
         }
         parseResultInfo['Date of Birth (YYYY-MM-DD)'] = birthYear + "-" + birthMonth + "-" + birthDay
         let expiryYear = result.getFieldValue("expiryYear");
         let expiryMonth = result.getFieldValue("expiryMonth");
         let expiryDay = result.getFieldValue("expiryDay");
         if (parseInt(expiryYear) >= 60) {
             expiryYear = "19" + expiryYear;
         } else {
             expiryYear = "20" + expiryYear;
         }
         parseResultInfo["Date of Expiry (YYYY-MM-DD)"] = expiryYear + "-" + expiryMonth + "-" + expiryDay;
         return parseResultInfo;
     }
    

    Angular MRZ Detection from image file

Step 5: Recognize and Extract MRZ Characters from Camera Stream

Basic Steps

  1. Register an event listener with CaptureVisionRouter to receive detected text results.
  2. Bind a CameraEnhancer instance to the CaptureVisionRouter instance.
  3. Start the camera stream and MRZ recognition.
  4. Parse and extract the MRZ data in the callback function.
  5. Display the results in a text area.

UI Design

Add the following HTML code to the mrz-scanner.component.html file:

<div id="loading-indicator" class="loading-indicator" *ngIf="!isLoaded">
    <div class="spinner"></div>
</div>

<div class="select">
    <label for="videoSource">Video source: </label>
    <select id="videoSource" (change)="openCamera()"></select>
</div>

<div class="container">
    <div id="videoview">
        <div class="dce-video-container" id="videoContainer"></div>
        <canvas id="overlay"></canvas>
    </div>

</div>


<div>
    <textarea id="result"></textarea>
</div>

Explanation

  • The select element allows users to choose a video source.
  • The video container displays the camera stream.
  • The overlay canvas draws the text area boundaries in real-time.
  • The text area displays the MRZ data extracted from the camera stream.

Component Implementation

  1. Initialize the SDK and Register Callback: Create an instance of CaptureVisionRouter , load the MRZ template, and register a callback function to receive detected document edges. Bind a camera view to the CaptureVisionRouter instance.

     ngOnInit(): void {
       this.videoSelect = document.querySelector('select#videoSource') as HTMLSelectElement;
       this.overlayManager.initOverlay(document.getElementById('overlay') as HTMLCanvasElement);
       (async () => {
         await CodeParserModule.loadSpec("MRTD_TD1_ID");
         await CodeParserModule.loadSpec("MRTD_TD2_FRENCH_ID");
         await CodeParserModule.loadSpec("MRTD_TD2_ID");
         await CodeParserModule.loadSpec("MRTD_TD2_VISA");
         await CodeParserModule.loadSpec("MRTD_TD3_PASSPORT");
         await CodeParserModule.loadSpec("MRTD_TD3_VISA");
    
         await LabelRecognizerModule.loadRecognitionData("MRZ");
         this.cvr = await CaptureVisionRouter.createInstance();
         this.parser = await CodeParser.createInstance();
         let ret = await this.cvr.initSettings('assets/template.json');
         console.log(ret);
         await this.initBarcodeScanner();
       })();
     }
    
     async initBarcodeScanner(): Promise<void> {
       const cameraView: CameraView = await CameraView.createInstance();
    
       this.cameraEnhancer = await CameraEnhancer.createInstance(cameraView);
    
       let uiElement = document.getElementById('videoContainer');
       if (uiElement) {
         uiElement.append(cameraView.getUIElement());
    
         cameraView.getUIElement().shadowRoot?.querySelector('.dce-sel-camera')?.setAttribute('style', 'display: none');
         cameraView.getUIElement().shadowRoot?.querySelector('.dce-sel-resolution')?.setAttribute('style', 'display: none');
    
         let cameras = await this.cameraEnhancer.getAllCameras();
         this.listCameras(cameras);
    
         if (this.isDestroyed) {
           throw Error(componentDestroyedErrorMsg);
         }
         this.cvr?.setInput(this.cameraEnhancer);
    
         this.cvr?.addResultReceiver({
           onCapturedResultReceived: async (result: CapturedResult) => {
             // TODO
           },
         });
    
         this.cameraEnhancer.on('played', () => {
           this.updateResolution();
         });
         await this.openCamera();
         if (this.isDestroyed) {
           throw Error(componentDestroyedErrorMsg);
         }
         await this.cvr?.startCapturing('ReadMRZ');
         if (this.isDestroyed) {
           throw Error(componentDestroyedErrorMsg);
         }
    
         this.isLoaded = true;
       }
     }
    
  2. Process the MRZ Recognition Results: Implement the callback function to handle MRZ recognition results.

     onCapturedResultReceived: async (result: CapturedResult) => {
       this.overlayManager.clearOverlay();
       let txts: any = [];
       let resultElement = document.getElementById('result');
       try {
         let localization;
         let items = result.items
         if (items.length > 0) {
           for (var i = 0; i < items.length; ++i) {
    
             if (items[i].type !== EnumCapturedResultItemType.CRIT_TEXT_LINE) {
               continue;
             }
    
             let item = items[i] as TextLineResultItem;
    
             txts.push(item.text);
             localization = item.location;
             this.overlayManager.drawOverlay(
               localization,
               ''
             );
    
             let parseResults = await this.parser?.parse(item.text);
             if (resultElement) {
               resultElement.innerHTML = JSON.stringify(handleMrzParseResult(parseResults!)) + '\n';
             }
    
             break;
           }
    
         }
       } catch (e) {
         alert(e);
       }
     },
    

    Angular MRZ Detection from camera stream

Source Code

https://github.com/yushulx/angular-barcode-mrz-document-scanner