diff --git a/.github/skills/dbr-js-sample-creator/SKILL.md b/.github/skills/dbr-js-sample-creator/SKILL.md index 4d0bb6ec..2b57a085 100644 --- a/.github/skills/dbr-js-sample-creator/SKILL.md +++ b/.github/skills/dbr-js-sample-creator/SKILL.md @@ -139,6 +139,22 @@ await cvRouter.initSettings(jsonString); // JSON string await cvRouter.startCapturing("MyTemplateName"); ``` +## Restricting Barcode Formats (Best Practice) + +To limit decoding to specific formats (e.g., QR codes only), prefer mutating +`SimplifiedBarcodeReaderSettings` on a preset template over writing a custom JSON template: + +```js +const settings = await cvRouter.getSimplifiedSettings("ReadBarcodes_SpeedFirst"); +settings.barcodeSettings.barcodeFormatIds = Dynamsoft.DBR.EnumBarcodeFormat.BF_QR_CODE; +await cvRouter.updateSettings("ReadBarcodes_SpeedFirst", settings); +await cvRouter.startCapturing("ReadBarcodes_SpeedFirst"); +``` + +Only use a custom JSON template (`initSettings`) for scenarios this can't express — multiple +ROIs/tasks, DPM settings, or `ImageParameterOptions`. See `references/api-sdk.md` and +`references/sample-patterns.md` (Pattern 5) for full details. + ## Camera Scanning Pattern (UMD) ```js diff --git a/.github/skills/dbr-js-sample-creator/references/api-sdk.md b/.github/skills/dbr-js-sample-creator/references/api-sdk.md index 9a4d7950..93e7f643 100644 --- a/.github/skills/dbr-js-sample-creator/references/api-sdk.md +++ b/.github/skills/dbr-js-sample-creator/references/api-sdk.md @@ -73,6 +73,7 @@ CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; | `Dynamsoft.DCE.CameraView` | `CameraView` | | `Dynamsoft.DCE.CameraEnhancer` | `CameraEnhancer` | | `Dynamsoft.Utility.MultiFrameResultCrossFilter` | `MultiFrameResultCrossFilter` | +| `Dynamsoft.DBR.EnumBarcodeFormat` | `EnumBarcodeFormat` | --- @@ -137,6 +138,53 @@ Pass these strings to `cvRouter.startCapturing()` or `cvRouter.capture()`: --- +## Restricting Barcode Formats — SimplifiedBarcodeReaderSettings (Best Practice) + +**Preferred over a custom JSON template** when the only goal is to restrict which barcode +formats are decoded (e.g., "QR codes only"). Read the current simplified settings from a preset +template, mutate `barcodeFormatIds`, and write them back with `updateSettings`: + +```ts +cvRouter.getSimplifiedSettings(templateName: string): Promise +cvRouter.updateSettings(templateName: string, settings: SimplifiedCaptureVisionSettings): Promise +``` + +```js +// UMD +const settings = await cvRouter.getSimplifiedSettings("ReadBarcodes_SpeedFirst"); +settings.barcodeSettings.barcodeFormatIds = Dynamsoft.DBR.EnumBarcodeFormat.BF_QR_CODE; +await cvRouter.updateSettings("ReadBarcodes_SpeedFirst", settings); + +await cvRouter.startCapturing("ReadBarcodes_SpeedFirst"); // same template name, now QR-only +``` + +```ts +// ES module / npm +import { EnumBarcodeFormat } from "dynamsoft-barcode-reader-bundle"; + +const settings = await cvRouter.getSimplifiedSettings("ReadBarcodes_SpeedFirst"); +settings.barcodeSettings.barcodeFormatIds = EnumBarcodeFormat.BF_QR_CODE; +await cvRouter.updateSettings("ReadBarcodes_SpeedFirst", settings); +``` + +`barcodeFormatIds` accepts a bitwise-OR combination of `EnumBarcodeFormat` members for multiple +formats: + +```js +settings.barcodeSettings.barcodeFormatIds = + Dynamsoft.DBR.EnumBarcodeFormat.BF_QR_CODE | Dynamsoft.DBR.EnumBarcodeFormat.BF_CODE_128; +``` + +Other commonly adjusted `SimplifiedBarcodeReaderSettings` fields on `settings.barcodeSettings`: +`expectedBarcodesCount`, `minResultConfidence`, `scaleDownThreshold`, `barcodeTextRegExPattern`. + +> Use a **custom JSON template** (below) instead only for advanced scenarios a simplified settings +> object can't express — multiple ROIs/tasks, DPM/reader settings combinations, or +> `ImageParameterOptions`. For simple format filtering, `getSimplifiedSettings`/`updateSettings` is +> the recommended approach — no external `.json` file needed. + +--- + ## Custom JSON Template Structure ```json diff --git a/.github/skills/dbr-js-sample-creator/references/sample-patterns.md b/.github/skills/dbr-js-sample-creator/references/sample-patterns.md index 2a9dd233..f0e54512 100644 --- a/.github/skills/dbr-js-sample-creator/references/sample-patterns.md +++ b/.github/skills/dbr-js-sample-creator/references/sample-patterns.md @@ -220,11 +220,13 @@ Source: `frameworks/es6/es6.html` --- -## Pattern 5: Custom JSON Template (Scan QR Codes Only) +## Pattern 5: Restrict Barcode Formats (Scan QR Codes Only) — Best Practice Source: `scenarios/scan-qr-code/` -Load a custom template JSON and use it by name: +Use `getSimplifiedSettings` / `updateSettings` on a preset template to restrict decoding to a +specific format. **This is the recommended approach for simple format filtering** — prefer it over +a custom JSON template (see below), since it needs no external `.json` file and reads clearly. ```js Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); @@ -244,19 +246,31 @@ Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMD }; await cvRouter.addResultReceiver(resultReceiver); - // Load custom template from JSON file, then use the template name from the JSON - await cvRouter.initSettings("./ReadQR.json"); + // Restrict to QR codes only via SimplifiedBarcodeReaderSettings + const settings = await cvRouter.getSimplifiedSettings("ReadBarcodes_SpeedFirst"); + settings.barcodeSettings.barcodeFormatIds = Dynamsoft.DBR.EnumBarcodeFormat.BF_QR_CODE; + await cvRouter.updateSettings("ReadBarcodes_SpeedFirst", settings); document.querySelector(".barcode-scanner-view").append(cameraView.getUIElement()); await cameraEnhancer.open(); - await cvRouter.startCapturing("ReadQR"); // name matches the template in ReadQR.json + await cvRouter.startCapturing("ReadBarcodes_SpeedFirst"); } catch (ex) { alert(ex.message || ex); } })(); ``` -Minimal custom JSON template to read only QR codes: +### Alternative: Custom JSON Template (advanced scenarios only) + +Only reach for a custom JSON template when the scenario needs more than a format filter — +multiple ROIs/tasks, DPM reader settings, or `ImageParameterOptions`: + +```js +// Load custom template from JSON file, then use the template name from the JSON +await cvRouter.initSettings("./ReadQR.json"); +await cvRouter.startCapturing("ReadQR"); // name matches the template in ReadQR.json +``` + ```json { "CaptureVisionTemplates": [ @@ -328,3 +342,4 @@ See full code in `references/api-frameworks.md` → "Vue 3 → VideoCapture Comp | Not calling `cameraView.getUIElement()` and appending to DOM | Required before `open()` | | Calling `startCapturing` before `setInput` | Always `setInput` first | | Forgetting `CoreModule.engineResourcePaths.rootDirectory` in npm/ES module projects | Add to `dynamsoft.config.ts` | +| Writing a custom JSON template just to restrict barcode formats | Use `getSimplifiedSettings`/`updateSettings` + `barcodeFormatIds` instead (Pattern 5) | diff --git a/README.md b/README.md index b8d8aaed..33ebb6b2 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,8 @@ If you have any questions, feel free to [contact Dynamsoft support](https://www. - [scan-1D-Industrial/](./scenarios/scan-1D-Industrial/) — 1D industrial barcode tuning example. - [scan-from-distance/](./scenarios/scan-from-distance/) — Demo for scanning barcodes from a distance (zoom/ROI tuning). - [locate-an-item-with-barcode/](./scenarios/locate-an-item-with-barcode/) — UI to help locate items with barcodes in a list or layout. +- [grid-barcode-reading/](./scenarios/grid-barcode-reading/) — Scan a grid of DataMatrix / QR codes from an uploaded image with fast scan, layout analysis, and deep decode. +- [use-typescript-in-ui-definition/](./scenarios/use-typescript-in-ui-definition/) — Uses React as the framework for business logic, TypeScript for the UI definition script portion, and imports the UI definition JS in UMD format. - [debug/](./scenarios/debug/) — Debug utilities and a small server (frame collector) used for testing and troubleshooting. ### Official Online Demo diff --git a/frameworks/angular/package.json b/frameworks/angular/package.json index 6acbb9d0..dbc4c6d8 100644 --- a/frameworks/angular/package.json +++ b/frameworks/angular/package.json @@ -18,11 +18,14 @@ "@angular/platform-browser": "20.3.25", "@angular/platform-browser-dynamic": "20.3.25", "@angular/router": "20.3.25", - "dynamsoft-barcode-reader-bundle": "11.4.3000", + "dynamsoft-barcode-reader-bundle": "npm:@dynamsoft/dynamsoft-capture-vision-bundle@3.6.1000-dev-20260707150019", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" }, + "overrides": { + "dynamsoft-barcode-reader-bundle": "$dynamsoft-barcode-reader-bundle" + }, "devDependencies": { "@angular-devkit/build-angular": "20.3.25", "@angular/cli": "20.3.25", @@ -36,4 +39,4 @@ "karma-jasmine-html-reporter": "~2.1.0", "typescript": "~5.8" } -} +} \ No newline at end of file diff --git a/frameworks/angular/src/app/dynamsoft.config.ts b/frameworks/angular/src/app/dynamsoft.config.ts index e18769d8..bf9dd3df 100644 --- a/frameworks/angular/src/app/dynamsoft.config.ts +++ b/frameworks/angular/src/app/dynamsoft.config.ts @@ -1,7 +1,9 @@ import { CoreModule, LicenseManager } from 'dynamsoft-barcode-reader-bundle'; // Configures the paths where the .wasm files and other necessary resources for modules are located. -CoreModule.engineResourcePaths.rootDirectory = 'https://cdn.jsdelivr.net/npm/'; +// CoreModule.engineResourcePaths.rootDirectory = 'https://cdn.jsdelivr.net/npm/'; +CoreModule.engineResourcePaths.rootDirectory = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/"; +CoreModule.engineResourcePaths.dcvData = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/dynamsoft-capture-vision-data@1.2.2-dev-20260528161158/" /** LICENSE ALERT - README * To use the library, you need to first specify a license key using the API "initLicense()" as shown below. diff --git a/frameworks/angular/src/app/image-capture/image-capture.component.ts b/frameworks/angular/src/app/image-capture/image-capture.component.ts index 469ff61c..4db22cdb 100644 --- a/frameworks/angular/src/app/image-capture/image-capture.component.ts +++ b/frameworks/angular/src/app/image-capture/image-capture.component.ts @@ -1,7 +1,6 @@ import { Component } from '@angular/core'; -import '../dynamsoft.config'; -import { EnumCapturedResultItemType, CaptureVisionRouter } from 'dynamsoft-barcode-reader-bundle'; -import type { BarcodeResultItem } from 'dynamsoft-barcode-reader-bundle'; +import { EnumCapturedResultItemType, CaptureVisionRouter, type BarcodeResultItem } from 'dynamsoft-barcode-reader-bundle'; +import "../dynamsoft.config"; // import side effects (license, engineResourcePath) within a component is beneficial for lazy loading. @Component({ selector: 'app-image-capture', @@ -10,42 +9,43 @@ import type { BarcodeResultItem } from 'dynamsoft-barcode-reader-bundle'; standalone: true, }) export class ImageCaptureComponent { - resultText = ""; + constructor() { + this.pCvRouter = CaptureVisionRouter.createInstance(); + } - pCvRouter?: Promise; - isDestroyed = false; + resultText = ""; + pCvRouter: Promise; captureImage = async (e: Event) => { let files = [...((e.target! as HTMLInputElement).files as any as File[])]; (e.target! as HTMLInputElement).value = ''; // reset input - this.resultText = ''; + this.resultText = 'decoding...'; try { // ensure cvRouter is created only once - const cvRouter = await (this.pCvRouter = - this.pCvRouter || CaptureVisionRouter.createInstance()); - if (this.isDestroyed) return; + const cvRouter = await this.pCvRouter; + let _resultText = ''; for (let file of files) { // Decode selected image with 'ReadBarcodes_ReadRateFirst' template. const result = await cvRouter.capture(file, 'ReadBarcodes_ReadRateFirst'); console.log(result); - if (this.isDestroyed) return; // Print file name if there's multiple files if (files.length > 1) { - this.resultText += `\n${file.name}:\n`; + _resultText += `\n${file.name}:\n`; } for (let _item of result.items) { if (_item.type !== EnumCapturedResultItemType.CRIT_BARCODE) { continue; // check if captured result item is a barcode } let item = _item as BarcodeResultItem; - this.resultText += item.text + '\n'; // output the decoded barcode text + _resultText += item.text + '\n'; // output the decoded barcode text } // If no items are found, display that no barcode was detected - if (!result.items.length) - this.resultText += - 'No barcode found\n'; + if (!result.items.length) { + _resultText += 'No barcode found\n'; + } + this.resultText = _resultText; } } catch (ex: any) { let errMsg = ex.message || ex; @@ -55,12 +55,11 @@ export class ImageCaptureComponent { }; // dispose cvRouter when it's no longer needed - async ngOnDestroy() { - this.isDestroyed = true; - if (this.pCvRouter) { - try { - (await this.pCvRouter).dispose(); - } catch (_) {} - } + ngOnDestroy() { + console.log("image capture component disposed"); + // If the browser supports FinalizationRegistry, cvRouter can implement automatic resource recycling, so the manual resource cleanup code below does not need to be written. + this.pCvRouter?.then((cvRouter) => { + cvRouter.dispose(); + }); } } diff --git a/frameworks/angular/src/app/video-capture/video-capture.component.ts b/frameworks/angular/src/app/video-capture/video-capture.component.ts index 7ac4f50e..ced88496 100644 --- a/frameworks/angular/src/app/video-capture/video-capture.component.ts +++ b/frameworks/angular/src/app/video-capture/video-capture.component.ts @@ -1,8 +1,6 @@ -import { Component, ElementRef, ViewChild, NgZone } from '@angular/core'; -import '../dynamsoft.config'; +import { Component, ElementRef, ViewChild, NgZone } from '@angular/core'; import { CameraEnhancer, CameraView, MultiFrameResultCrossFilter, CaptureVisionRouter } from 'dynamsoft-barcode-reader-bundle'; - -const componentDestroyedErrorMsg = 'VideoCapture Component Destroyed'; +import "../dynamsoft.config"; // import side effects (license, engineResourcePath) within a component is beneficial for lazy loading. @Component({ selector: 'app-video-capture', @@ -14,96 +12,72 @@ export class VideoCaptureComponent { constructor(private ngZone: NgZone) { } @ViewChild('cameraViewContainer') cameraViewContainer?: ElementRef; + pInit: Promise | null = null; resultText = ""; - - resolveInit?: () => void; - pInit: Promise = new Promise((r) => { - this.resolveInit = r; - }); - isDestroyed = false; + isDisposed = false; cvRouter?: CaptureVisionRouter; cameraEnhancer?: CameraEnhancer; - async ngAfterViewInit(): Promise { - try { - // Create a `CameraEnhancer` instance for camera control and a `CameraView` instance for UI control. - const cameraView = await CameraView.createInstance(); - if (this.isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } // Check if component is destroyed after every async - this.cameraEnhancer = await CameraEnhancer.createInstance(cameraView); - if (this.isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } + ngAfterViewInit() { + this.pInit = (async () => { + try { + // Create a `CameraEnhancer` instance for camera control and a `CameraView` instance for UI control. + const cameraView = await CameraView.createInstance(); + this.cameraEnhancer = await CameraEnhancer.createInstance(cameraView); - // Get default UI and append it to DOM. - this.cameraViewContainer!.nativeElement.append(cameraView.getUIElement()); + if (this.isDisposed) return; - // Create a `CaptureVisionRouter` instance and set `CameraEnhancer` instance as its image source. - this.cvRouter = await CaptureVisionRouter.createInstance(); - if (this.isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } - this.cvRouter.setInput(this.cameraEnhancer); + // Get default UI and append it to DOM. + this.cameraViewContainer!.nativeElement.append(this.cameraEnhancer.getUIElement()); - // Define a callback for results. - await this.cvRouter.addResultReceiver({ - onDecodedBarcodesReceived: (result) => { - if (!result.barcodeResultItems.length) return; - console.log(result); - this.ngZone.run(() => { - this.resultText = ''; - for (let item of result.barcodeResultItems) { - this.resultText += `${item.formatString}: ${item.text}\n\n`; - } - }); - }, - }); + // Create a `CaptureVisionRouter` instance and set `CameraEnhancer` instance as its image source. + this.cvRouter = await CaptureVisionRouter.createInstance(); + this.cvRouter.setInput(this.cameraEnhancer); - // Filter out unchecked and duplicate results. - const filter = new MultiFrameResultCrossFilter(); - // Filter out unchecked barcodes. - filter.enableResultCrossVerification('barcode', true); - // Filter out duplicate barcodes within 3 seconds. - filter.enableResultDeduplication('barcode', true); - await this.cvRouter.addResultFilter(filter); - if (this.isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } + // Define a callback for results. + await this.cvRouter.addResultReceiver({ + onDecodedBarcodesReceived: (result) => { + if (!result.barcodeResultItems.length) return; + console.log(result); + this.ngZone.run(() => { + this.resultText = ''; + for (let item of result.barcodeResultItems) { + this.resultText += `${item.formatString}: ${item.text}\n\n`; + } + }); + }, + }); - // Open camera and start scanning barcode. - await this.cameraEnhancer.open(); - cameraView.setScanLaserVisible(true); - if (this.isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } - await this.cvRouter.startCapturing('ReadBarcodes_SpeedFirst'); - if (this.isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } - } catch (ex: any) { - if ((ex as Error)?.message === componentDestroyedErrorMsg) { - console.log(componentDestroyedErrorMsg); - } else { + // Filter out unchecked and duplicate results. + const filter = new MultiFrameResultCrossFilter(); + // Filter out unchecked barcodes. + filter.enableResultCrossVerification('barcode', true); + // Filter out duplicate barcodes within 3 seconds. + filter.enableResultDeduplication('barcode', true); + await this.cvRouter.addResultFilter(filter); + + // Open camera and start scanning barcode. + await this.cameraEnhancer.open(); + cameraView.setScanLaserVisible(true); + await this.cvRouter.startCapturing('ReadBarcodes_SpeedFirst'); + } catch (ex: any) { let errMsg = ex.message || ex; console.error(ex); alert(errMsg); } - } - - // Resolve pInit promise once initialization is complete. - this.resolveInit!(); + })(); } // dispose cvRouter when it's no longer needed - async ngOnDestroy() { - this.isDestroyed = true; - try { - // Wait for the pInit to complete before disposing resources. - await this.pInit; + ngOnDestroy() { + console.log("video capture component disposed"); + this.isDisposed = true; + this.cameraEnhancer?.getUIElement().remove(); + // If the browser supports FinalizationRegistry, cvRouter can implement automatic resource recycling, so the manual resource cleanup code below does not need to be written. + this.pInit?.then(() => { this.cvRouter?.dispose(); this.cameraEnhancer?.dispose(); - } catch (_) { } + }); } } diff --git a/frameworks/next/components/ImageCapture/ImageCapture.tsx b/frameworks/next/components/ImageCapture/ImageCapture.tsx index 51f491ad..1cec7c59 100644 --- a/frameworks/next/components/ImageCapture/ImageCapture.tsx +++ b/frameworks/next/components/ImageCapture/ImageCapture.tsx @@ -1,31 +1,37 @@ -import React, { useRef, useEffect, MutableRefObject, useState } from "react"; -import "../../dynamsoft.config"; // import side effects. The license, engineResourcePath, so on. +import React, { useRef, useEffect, useState } from "react"; import { EnumCapturedResultItemType, CaptureVisionRouter, BarcodeResultItem } from "dynamsoft-barcode-reader-bundle"; -import "./ImageCapture.css"; +import "./ImageCapture.css"; import "../../dynamsoft.config"; // import side effects (license, engineResourcePath) within a component is beneficial for lazy loading. function ImageCapture() { - const [resultText, setResultText] = useState(""); + let [resultText, setResultText] = useState(""); + let pCvRouter = useRef>(); - let pCvRouter: MutableRefObject | null> = useRef(null); - let isDestroyed = useRef(false); + useEffect(() => { + pCvRouter.current = CaptureVisionRouter.createInstance(); + return () => { + console.log("image capture component disposed"); + // If the browser supports FinalizationRegistry, cvRouter can implement automatic resource recycling, so the manual resource cleanup code below does not need to be written. + pCvRouter.current?.then((cvRouter) => { + cvRouter.dispose(); + }) + } + }, []) const captureImage = async (e: React.ChangeEvent) => { let files = [...(e.target.files as any as File[])]; e.target.value = ""; // reset input - let _resultText = ""; + setResultText("decoding..."); try { // ensure cvRouter is created only once - const cvRouter = await (pCvRouter.current = pCvRouter.current || CaptureVisionRouter.createInstance()); - if (isDestroyed.current) return; + const cvRouter = await pCvRouter.current!; + let _resultText = ""; for (let file of files) { // Decode selected image with 'ReadBarcodes_ReadRateFirst' template. const result = await cvRouter.capture(file, "ReadBarcodes_ReadRateFirst"); console.log(result); - if (isDestroyed.current) return; - let _resultText = ""; // Print file name if there's multiple files if (files.length > 1) { _resultText += `\n${file.name}:\n`; @@ -35,11 +41,11 @@ function ImageCapture() { continue; // check if captured result item is a barcode } let item = _item as BarcodeResultItem; - _resultText += item.text + "\n"; // output the decoded barcode text + _resultText += item.formatString + ": " + item.text + "\n" } - // If no items are found, display that no barcode was detected - if (!result.items.length) _resultText = "No barcode found"; setResultText(_resultText); + // If no items are found, display that no barcode was detected + if (!result.items.length) setResultText(_resultText + "No barcode found"); } } catch (ex: any) { let errMsg = ex.message || ex; @@ -48,21 +54,6 @@ function ImageCapture() { } }; - useEffect((): any => { - // In 'development', React runs setup and cleanup one extra time before the actual setup in Strict Mode. - isDestroyed.current = false; - - // componentWillUnmount. dispose cvRouter when it's no longer needed - return () => { - isDestroyed.current = true; - if (pCvRouter.current) { - pCvRouter.current.then((cvRouter) => { - cvRouter.dispose(); - }).catch((_) => { }) - } - }; - }, []); - return (
diff --git a/frameworks/next/components/VideoCapture/VideoCapture.tsx b/frameworks/next/components/VideoCapture/VideoCapture.tsx index 065cc4e9..3572cb19 100644 --- a/frameworks/next/components/VideoCapture/VideoCapture.tsx +++ b/frameworks/next/components/VideoCapture/VideoCapture.tsx @@ -1,47 +1,33 @@ import React, { useEffect, useRef, useState } from "react"; -import "../../dynamsoft.config"; // import side effects. The license, engineResourcePath, so on. import { CameraEnhancer, CameraView, CaptureVisionRouter, MultiFrameResultCrossFilter } from "dynamsoft-barcode-reader-bundle"; import "./VideoCapture.css"; - -const componentDestroyedErrorMsg = "VideoCapture Component Destroyed"; +import "../../dynamsoft.config"; // import side effects (license, engineResourcePath) within a component is beneficial for lazy loading. function VideoCapture() { + const [resultText, setResultText] = useState(""); const cameraViewContainer = useRef(null); - const [resultsText, setResultText] = useState(""); useEffect((): any => { - let resolveInit: () => void; - const pInit: Promise = new Promise((r) => { - resolveInit = r; - }); - let isDestroyed = false; - + let isDisposed = false; let cvRouter: CaptureVisionRouter; let cameraEnhancer: CameraEnhancer; - (async () => { + let pInit = (async () => { try { // Create a `CameraEnhancer` instance for camera control and a `CameraView` instance for UI control. const cameraView = await CameraView.createInstance(); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } // Check if component is destroyed after every async - cameraEnhancer = await CameraEnhancer.createInstance(cameraView); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } // Hide the "Powered by Message" overlay on the scanner view // cameraView.setPowerByMessageVisible(false); + cameraEnhancer = await CameraEnhancer.createInstance(cameraView); + if (isDisposed) return; + // Get default UI and append it to DOM. - cameraViewContainer.current!.append(cameraView.getUIElement()); + cameraViewContainer.current?.append(cameraEnhancer.getUIElement()); // Create a `CaptureVisionRouter` instance and set `CameraEnhancer` instance as its image source. cvRouter = await CaptureVisionRouter.createInstance(); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } cvRouter.setInput(cameraEnhancer); // Define a callback for results. @@ -50,6 +36,7 @@ function VideoCapture() { if (!result.barcodeResultItems.length) return; let _resultText = ""; + setResultText(_resultText); console.log(result); for (let item of result.barcodeResultItems) { _resultText += `${item.formatString}: ${item.text}\n\n`; @@ -65,59 +52,37 @@ function VideoCapture() { // Filter out duplicate barcodes within 3 seconds. filter.enableResultDeduplication("barcode", true); await cvRouter.addResultFilter(filter); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } // Open camera and start scanning barcode. await cameraEnhancer.open(); cameraView.setScanLaserVisible(true); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } await cvRouter.startCapturing("ReadBarcodes_SpeedFirst"); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } } catch (ex: any) { - if ((ex as Error)?.message === componentDestroyedErrorMsg) { - console.log(componentDestroyedErrorMsg); - } else { - let errMsg = ex.message || ex; - console.error(ex); - alert(errMsg); - } + let errMsg = ex.message || ex; + console.error(ex); + alert(errMsg); } })(); - // Resolve pInit promise once initialization is complete. - resolveInit!(); - // componentWillUnmount. dispose cvRouter when it's no longer needed - return async () => { - isDestroyed = true; - try { - // Wait for the pInit to complete before disposing resources. - await pInit; - cvRouter?.dispose(); + return () => { + console.log("video capture component disposed"); + isDisposed = true; + cameraEnhancer?.getUIElement().remove(); + // If the browser supports FinalizationRegistry, cvRouter can implement automatic resource recycling, so the manual resource cleanup code below does not need to be written. + pInit.then(() => { cameraEnhancer?.dispose(); - } catch (_) { } + cvRouter?.dispose(); + }) }; }, []); return (
-
+

Results: -
{resultsText}
+
{resultText}
); } diff --git a/frameworks/next/dynamsoft.config.ts b/frameworks/next/dynamsoft.config.ts index 612a1ae7..a0203c5e 100644 --- a/frameworks/next/dynamsoft.config.ts +++ b/frameworks/next/dynamsoft.config.ts @@ -1,7 +1,9 @@ import { CoreModule, LicenseManager } from "dynamsoft-barcode-reader-bundle"; // Configures the paths where the .wasm files and other necessary resources for modules are located. -CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; +// CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; +CoreModule.engineResourcePaths.rootDirectory = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/"; +CoreModule.engineResourcePaths.dcvData = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/dynamsoft-capture-vision-data@1.2.2-dev-20260528161158/" /** LICENSE ALERT - README * To use the library, you need to first specify a license key using the API "initLicense()" as shown below. diff --git a/frameworks/next/package.json b/frameworks/next/package.json index 716ee947..14f1dffb 100644 --- a/frameworks/next/package.json +++ b/frameworks/next/package.json @@ -11,8 +11,11 @@ "dependencies": { "react": "^18", "react-dom": "^18", - "next": "16.2.5", - "dynamsoft-barcode-reader-bundle": "11.4.3000" + "next": "16.2.11", + "dynamsoft-barcode-reader-bundle": "npm:@dynamsoft/dynamsoft-capture-vision-bundle@3.6.1000-dev-20260707150019" + }, + "overrides": { + "dynamsoft-barcode-reader-bundle": "$dynamsoft-barcode-reader-bundle" }, "devDependencies": { "typescript": "^5", diff --git a/frameworks/nuxt/components/ImageCapture.client.vue b/frameworks/nuxt/components/ImageCapture.client.vue index 68c076ac..d612b3b3 100644 --- a/frameworks/nuxt/components/ImageCapture.client.vue +++ b/frameworks/nuxt/components/ImageCapture.client.vue @@ -1,42 +1,44 @@ diff --git a/frameworks/nuxt/components/VideoCapture.client.vue b/frameworks/nuxt/components/VideoCapture.client.vue index 9242fa10..230efc67 100644 --- a/frameworks/nuxt/components/VideoCapture.client.vue +++ b/frameworks/nuxt/components/VideoCapture.client.vue @@ -1,93 +1,78 @@ diff --git a/frameworks/nuxt/dynamsoft.config.ts b/frameworks/nuxt/dynamsoft.config.ts index 612a1ae7..a0203c5e 100644 --- a/frameworks/nuxt/dynamsoft.config.ts +++ b/frameworks/nuxt/dynamsoft.config.ts @@ -1,7 +1,9 @@ import { CoreModule, LicenseManager } from "dynamsoft-barcode-reader-bundle"; // Configures the paths where the .wasm files and other necessary resources for modules are located. -CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; +// CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; +CoreModule.engineResourcePaths.rootDirectory = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/"; +CoreModule.engineResourcePaths.dcvData = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/dynamsoft-capture-vision-data@1.2.2-dev-20260528161158/" /** LICENSE ALERT - README * To use the library, you need to first specify a license key using the API "initLicense()" as shown below. diff --git a/frameworks/nuxt/package.json b/frameworks/nuxt/package.json index 5316245b..35ba36d5 100644 --- a/frameworks/nuxt/package.json +++ b/frameworks/nuxt/package.json @@ -13,6 +13,9 @@ "nuxt": "3.2.3" }, "dependencies": { - "dynamsoft-barcode-reader-bundle": "11.4.3000" + "dynamsoft-barcode-reader-bundle": "npm:@dynamsoft/dynamsoft-capture-vision-bundle@3.6.1000-dev-20260707150019" + }, + "overrides": { + "dynamsoft-barcode-reader-bundle": "$dynamsoft-barcode-reader-bundle" } } \ No newline at end of file diff --git a/frameworks/react/package.json b/frameworks/react/package.json index edcbd5e2..b0dde95a 100644 --- a/frameworks/react/package.json +++ b/frameworks/react/package.json @@ -11,13 +11,16 @@ "@types/node": "^16.18.99", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "dynamsoft-barcode-reader-bundle": "11.4.3000", + "dynamsoft-barcode-reader-bundle": "npm:@dynamsoft/dynamsoft-capture-vision-bundle@3.6.1000-dev-20260707150019", "react": "^18.3.1", "react-dom": "^18.3.1", "react-scripts": "5.0.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, + "overrides": { + "dynamsoft-barcode-reader-bundle": "$dynamsoft-barcode-reader-bundle" + }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", diff --git a/frameworks/react/src/App.tsx b/frameworks/react/src/App.tsx index 51c1029d..c1e013a1 100644 --- a/frameworks/react/src/App.tsx +++ b/frameworks/react/src/App.tsx @@ -11,9 +11,7 @@ enum Modes { function App() { const [mode, setMode] = useState(Modes.VIDEO_CAPTURE); - const showVideoCapture = () => setMode(Modes.VIDEO_CAPTURE); - const showImageCapture = () => setMode(Modes.IMAGE_CAPTURE); return ( diff --git a/frameworks/react/src/components/ImageCapture/ImageCapture.tsx b/frameworks/react/src/components/ImageCapture/ImageCapture.tsx index 83fe29d0..8f2476e8 100644 --- a/frameworks/react/src/components/ImageCapture/ImageCapture.tsx +++ b/frameworks/react/src/components/ImageCapture/ImageCapture.tsx @@ -1,30 +1,37 @@ -import React, { useRef, useEffect, MutableRefObject, useState } from "react"; -import "../../dynamsoft.config"; // import side effects. The license, engineResourcePath, so on. +import React, { useEffect, useRef, useState } from "react"; import { EnumCapturedResultItemType, CaptureVisionRouter, BarcodeResultItem } from "dynamsoft-barcode-reader-bundle"; import "./ImageCapture.css"; +import "../../dynamsoft.config"; // import side effects (license, engineResourcePath) within a component is beneficial for lazy loading. function ImageCapture() { - const [resultText, setResultText] = useState(""); + let [resultText, setResultText] = useState(""); + let pCvRouter = useRef>(); - let pCvRouter: MutableRefObject | null> = useRef(null); - let isDestroyed = useRef(false); + useEffect(() => { + pCvRouter.current = CaptureVisionRouter.createInstance(); + return () => { + console.log("image capture component disposed"); + // If the browser supports FinalizationRegistry, cvRouter can implement automatic resource recycling, so the manual resource cleanup code below does not need to be written. + pCvRouter.current?.then((cvRouter) => { + cvRouter.dispose(); + }) + } + }, []) const captureImage = async (e: React.ChangeEvent) => { let files = [...(e.target.files as any as File[])]; e.target.value = ""; // reset input - setResultText(""); + setResultText("decoding..."); try { // ensure cvRouter is created only once - const cvRouter = await (pCvRouter.current = pCvRouter.current || CaptureVisionRouter.createInstance()); - if (isDestroyed.current) return; + const cvRouter = await pCvRouter.current!; let _resultText = ""; for (let file of files) { // Decode selected image with 'ReadBarcodes_ReadRateFirst' template. const result = await cvRouter.capture(file, "ReadBarcodes_ReadRateFirst"); console.log(result); - if (isDestroyed.current) return; // Print file name if there's multiple files if (files.length > 1) { @@ -48,21 +55,6 @@ function ImageCapture() { } }; - useEffect((): any => { - // In 'development', React runs setup and cleanup one extra time before the actual setup in Strict Mode. - isDestroyed.current = false; - - // componentWillUnmount. dispose cvRouter when it's no longer needed - return () => { - isDestroyed.current = true; - if (pCvRouter.current) { - pCvRouter.current.then((cvRouter) => { - cvRouter.dispose(); - }).catch((_) => { }) - } - }; - }, []); - return (
diff --git a/frameworks/react/src/components/VideoCapture/VideoCapture.tsx b/frameworks/react/src/components/VideoCapture/VideoCapture.tsx index df243ee7..ab43fcb0 100644 --- a/frameworks/react/src/components/VideoCapture/VideoCapture.tsx +++ b/frameworks/react/src/components/VideoCapture/VideoCapture.tsx @@ -1,25 +1,18 @@ import { useEffect, useRef, useState } from "react"; -import "../../dynamsoft.config"; // import side effects. The license, engineResourcePath, so on. import { CameraEnhancer, CameraView, CaptureVisionRouter, MultiFrameResultCrossFilter } from "dynamsoft-barcode-reader-bundle"; import "./VideoCapture.css"; - -const componentDestroyedErrorMsg = "VideoCapture Component Destroyed"; +import "../../dynamsoft.config" // import side effects (license, engineResourcePath) within a component is beneficial for lazy loading. function VideoCapture() { const [resultText, setResultText] = useState(""); const cameraViewContainer = useRef(null); useEffect((): any => { - let resolveInit: () => void; - const pInit: Promise = new Promise((r) => { - resolveInit = r; - }); - let isDestroyed = false; - + let isDisposed = false; let cvRouter: CaptureVisionRouter; let cameraEnhancer: CameraEnhancer; - (async () => { + let pInit = (async () => { try { // Create a `CameraEnhancer` instance for camera control and a `CameraView` instance for UI control. const cameraView = await CameraView.createInstance(); @@ -27,22 +20,14 @@ function VideoCapture() { // Hide the "Powered by Message" overlay on the scanner view // cameraView.setPowerByMessageVisible(false); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } // Check if component is destroyed after every async cameraEnhancer = await CameraEnhancer.createInstance(cameraView); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } + if (isDisposed) return; // Get default UI and append it to DOM. - cameraViewContainer.current!.append(cameraView.getUIElement()); + cameraViewContainer.current?.append(cameraEnhancer.getUIElement()); // Create a `CaptureVisionRouter` instance and set `CameraEnhancer` instance as its image source. cvRouter = await CaptureVisionRouter.createInstance(); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } cvRouter.setInput(cameraEnhancer); // Define a callback for results. @@ -67,41 +52,28 @@ function VideoCapture() { // Filter out duplicate barcodes within 3 seconds. filter.enableResultDeduplication("barcode", true); await cvRouter.addResultFilter(filter); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } // Open camera and start scanning barcode. await cameraEnhancer.open(); cameraView.setScanLaserVisible(true); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } await cvRouter.startCapturing("ReadBarcodes_SpeedFirst"); - if (isDestroyed) { - throw Error(componentDestroyedErrorMsg); - } } catch (ex: any) { - if ((ex as Error)?.message === componentDestroyedErrorMsg) { - console.log(componentDestroyedErrorMsg); - } else { - let errMsg = ex.message || ex; - console.error(ex); - alert(errMsg); - } + let errMsg = ex.message || ex; + console.error(ex); + alert(errMsg); } - // Resolve pInit promise once initialization is complete. - resolveInit!(); })(); // componentWillUnmount. dispose cvRouter when it's no longer needed return () => { - isDestroyed = true; - // Wait for the pInit to complete before disposing resources. + console.log("video capture component disposed"); + isDisposed = true; + cameraEnhancer?.getUIElement().remove(); + // If the browser supports FinalizationRegistry, cvRouter can implement automatic resource recycling, so the manual resource cleanup code below does not need to be written. pInit.then(() => { - cvRouter?.dispose(); cameraEnhancer?.dispose(); - }).catch((_) => { }) + cvRouter?.dispose(); + }) }; }, []); diff --git a/frameworks/react/src/dynamsoft.config.ts b/frameworks/react/src/dynamsoft.config.ts index 612a1ae7..a0203c5e 100644 --- a/frameworks/react/src/dynamsoft.config.ts +++ b/frameworks/react/src/dynamsoft.config.ts @@ -1,7 +1,9 @@ import { CoreModule, LicenseManager } from "dynamsoft-barcode-reader-bundle"; // Configures the paths where the .wasm files and other necessary resources for modules are located. -CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; +// CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; +CoreModule.engineResourcePaths.rootDirectory = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/"; +CoreModule.engineResourcePaths.dcvData = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/dynamsoft-capture-vision-data@1.2.2-dev-20260528161158/" /** LICENSE ALERT - README * To use the library, you need to first specify a license key using the API "initLicense()" as shown below. diff --git a/frameworks/svelte/package.json b/frameworks/svelte/package.json index f74feba4..c4c91324 100644 --- a/frameworks/svelte/package.json +++ b/frameworks/svelte/package.json @@ -10,7 +10,10 @@ "check": "svelte-check --tsconfig ./tsconfig.json" }, "dependencies": { - "dynamsoft-barcode-reader-bundle": "11.4.3000" + "dynamsoft-barcode-reader-bundle": "npm:@dynamsoft/dynamsoft-capture-vision-bundle@3.6.1000-dev-20260707150019" + }, + "overrides": { + "dynamsoft-barcode-reader-bundle": "$dynamsoft-barcode-reader-bundle" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.2", diff --git a/frameworks/svelte/src/components/ImageCapture.svelte b/frameworks/svelte/src/components/ImageCapture.svelte index cad8fa8c..57b1dc66 100644 --- a/frameworks/svelte/src/components/ImageCapture.svelte +++ b/frameworks/svelte/src/components/ImageCapture.svelte @@ -1,41 +1,46 @@
- +
{resultText}
diff --git a/frameworks/svelte/src/components/VideoCapture.svelte b/frameworks/svelte/src/components/VideoCapture.svelte index 435c85c6..9d7c6a3e 100644 --- a/frameworks/svelte/src/components/VideoCapture.svelte +++ b/frameworks/svelte/src/components/VideoCapture.svelte @@ -1,52 +1,38 @@ diff --git a/frameworks/svelte/src/dynamsoft.config.ts b/frameworks/svelte/src/dynamsoft.config.ts index 63df9b48..71518dd6 100644 --- a/frameworks/svelte/src/dynamsoft.config.ts +++ b/frameworks/svelte/src/dynamsoft.config.ts @@ -2,7 +2,9 @@ import { CoreModule, LicenseManager } from "dynamsoft-barcode-reader-bundle"; // Configures the paths where the .wasm files and other necessary resources for modules are located. -CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; +// CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; +CoreModule.engineResourcePaths.rootDirectory = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/"; +CoreModule.engineResourcePaths.dcvData = "https://npm.scannerproxy.com:802/cdn/@dynamsoft/dynamsoft-capture-vision-data@1.2.2-dev-20260528161158/" /** LICENSE ALERT - README * To use the library, you need to first specify a license key using the API "initLicense()" as shown below. diff --git a/frameworks/vue/package.json b/frameworks/vue/package.json index 306fa673..5545ce5e 100644 --- a/frameworks/vue/package.json +++ b/frameworks/vue/package.json @@ -11,9 +11,12 @@ "type-check": "vue-tsc --noEmit" }, "dependencies": { - "dynamsoft-barcode-reader-bundle": "11.4.3000", + "dynamsoft-barcode-reader-bundle": "npm:@dynamsoft/dynamsoft-capture-vision-bundle@3.6.1000-dev-20260707150019", "vue": "^3.2.45" }, + "overrides": { + "dynamsoft-barcode-reader-bundle": "$dynamsoft-barcode-reader-bundle" + }, "devDependencies": { "@types/node": "^18.11.12", "@vitejs/plugin-vue": "^4.0.0", diff --git a/frameworks/vue/src/App.vue b/frameworks/vue/src/App.vue index 752b1e44..9e24718d 100644 --- a/frameworks/vue/src/App.vue +++ b/frameworks/vue/src/App.vue @@ -5,7 +5,6 @@ import VideoCapture from "./components/VideoCapture.vue"; import ImageCapture from "./components/ImageCapture.vue"; const mode: Ref = ref("video"); -