# Getting Started A step-by-step guide on how to integrate the STRICH SDK into your app and get started with barcode scanning. Tip: This guide covers integrating STRICH into an existing web app, and assumes you are familiar with web development topics like HTML, JavaScript, CSS and HTTP. ## Installation STRICH can be installed in multiple ways, depending on your environment or workflow. ### Installing via NPM and a bundler If you're using a bundler like [Vite](https://vite.dev/) or [esbuild](https://esbuild.github.io/), you can simply install STRICH via [NPM](https://www.npmjs.com/package/@pixelverse/strichjs-sdk). ```SHELL npm install @pixelverse/strichjs-sdk ``` STRICH uses [semantic versioning](https://semver.org/). By default, npm will add a version requirement of `^x.y.z`, which will automatically update the dependency to the latest minor version every time you run npm install. Note: If barcode scanning is a critical function in your app, we recommend a more conservative approach: either use `~x.y.z` which will only automatically apply patches or `x.y.z` to pin an exact version. ### Loading from a CDN Alternatively, you can choose to load STRICH from the [jsDeliver](https://www.jsdelivr.com/package/npm/@pixelverse/strichjs-sdk) CDN. Tip: While obtaining the library from a CDN is convenient, for PWAs we recommend bundling it with your app so your app is self-contained and offline-capable. #### ES6 Modules Use an [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) statement to load STRICH as an [ES6 module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). ```JAVASCRIPT import {StrichSDK, BarcodeReader} from "https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@latest"; ``` The same recommendations apply regarding versioning. To pin a version, change the URL to include the version: ```JAVASCRIPT import {StrichSDK, BarcodeReader} from "https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@1.11.0"; ``` #### Global object ( ``` ## Creating a license key To initialize the SDK, you need to obtain a license key first. License keys can be created in the [Customer Portal](https://portal.strich.io) after creating an account and starting the free 30-day trial. License keys are restricted to one or more application URLs (the scope of the license). So if your scanning app will be available at https://app.example.com and you have staging and development environments, you need to add those URLs to the license key when creating it. You can also create separate license keys if you already have a mechanism for injecting per-environment configurations. ![Creating a License Key](images/create_license_key.png) Please note that HTTPS is required. Web browsers only allow access to the camera feed for secure origins. For more information on how to serve from a secure origin during development, please refer to the [Deployment Guide](deployment-guide.html). Tip: Loopback and private IP address ranges such as localhost/127.0.0.1 and 192.168.x.y which are often used during development need not be specified. These are automatically included in the license key. ## Initializing the SDK The SDK needs to be initialized once with the license key created previously by calling [StrichSDK.initialize()](https://docs.strich.io/reference/classes/StrichSDK.html#initialize). When the Promise resolves successfully, the SDK is ready to use. ```JAVASCRIPT StrichSDK.initialize('eyJhbGciOiJIUzI1NiIsInR...') .then(() => console.log('STRICH SDK initialized')); ``` If initialization fails, the Promise will reject with an [SdkError](https://docs.strich.io/reference/classes/SdkError.html). The [message](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message) and [detailMessage](https://docs.strich.io/reference/classes/SdkError.html#detailMessage) fields should provide an enough information to diagnose the issue. A [list of common causes and their remedies](https://kb.strich.io/article/9-sdk-initialization-errors) is available in our Knowledge Base. In Single Page Apps (SPAs), we recommend doing the SDK initialization early, before the user expects to see the barcode scanning screen, for instance during the rest of your app initialization. SDK initialization is asynchronous and may require internet connectivity (successful license checks persist up to 48 hours), except for Enterprise licenses which support offline operation as an add-on. ## Choosing the Integration As of version 1.6.0 of the SDK, we now support two kinds of integrations: * BarcodeReader: An embeddable barcode reader that you control. You supply a container in your app UI and are in charge of managing the BarcodeReader. Allows full flexibility in designing your scanning experience. * PopupScanner: An out-of-the-box scanning experience presented in a modal dialog. Perfect for simple scanning use cases that do not require a lot of customization. No changes to your app UI are necessary. Scan barcodes with a single line of code. ![Integrations](images/integrations.png) The remainder of this guide will focus on the BarcodeReader integration. Please refer to the [PopupScanner](the-popup-scanner.html) documentation if you want to explore that integration, or if you want to quickly verify that the SDK is working. ## Integrating the BarcodeReader ### Defining the Host Element We need to provide the BarcodeReader with a place to live. We do so by defining a host element – a DOM element that will contain the UI, i.e. the camera feed, view finder, camera controls, etc. #### Example: Vertical Layout with BarcodeReader on Top A typical scanning app has a vertical layout with the scanner on top, results below and an action bar at the bottom. ```HTML
``` Here, the host element is a div with ID `scanner`, so we can refer to it via the CSS selector `#scanner`. Tip: The host element must use `relative` positioning and should not change its size during operation. The selector should only refer to a single element. ### Creating a Configuration Before we can create the BarcodeReader, we need to create a configuration – an object that specifies the functional and visual characteristics of the BarcodeReader. For a good scanning experience, it is important to restrict the [types of barcodes](https://docs.strich.io/supported-symbologies.html) to be detected by the BarcodeReader to only the ones you need. STRICH will automatically set the region of interest (the area where barcodes are detected) to something appropriate for selected barcode types. Tip: Fewer types of barcodes being searched in a smaller area lead to faster processing = quicker detection times. #### Example: Configuration for Code 128 Barcodes In the example below, we only look for [Code 128](https://docs.strich.io/code-128.html) barcodes. We also set a [duplicateInterval](https://docs.strich.io/reference/interfaces/EngineConfiguration.html#duplicateInterval) of 2.5 seconds, avoiding repeated scans of the same code over a short time frame. ```JAVASCRIPT let config = { // the CSS selector identifying the host element selector: '#scanner', engine: { // restrict to the required symbologies symbologies: ['code128'], // filter duplicate results duplicateInterval: 2500 } }; ``` ### Initializing the BarcodeReader Initializing the BarcodeReader is a two-step process: the configuration is passed to the [constructor](https://docs.strich.io/reference/classes/BarcodeReader.html#constructor), then the [initialize()](https://docs.strich.io/reference/classes/BarcodeReader.html#initialize) method is called, which will request access to the camera. Barcode recognition starts once you call the [start()](https://docs.strich.io/reference/classes/BarcodeReader.html#start) method. To receive barcode detections in your app code, provide a callback using the [detected](https://docs.strich.io/reference/classes/BarcodeReader.html#detected) property. The BarcodeReader will invoke this callback whenever barcodes are detected. #### Example: Initializing and Starting a BarcodeReader ```JAVASCRIPT new BarcodeReader(config).initialize() .then(result => { // initialization successful, store BarcodeReader for later this.barcodeReader = result; // register handler for code detections this.barcodeReader.detected = (detections) => { // .. do something with the scanned codes }; // start reading barcodes! return this.barcodeReader.start(); }) .catch(error => { // initialization failed (error message displayed in BarcodeReader) }); ``` #### Camera Access Initialization requires camera access, which is gated through a browser prompt. It is usually a good idea to make the user aware of this prior to initializing the BarcodeReader, for example by displaying a camera access will be required in the next step notice. The SDK provides utility functions for determining the [current camera permission state](https://docs.strich.io/reference/classes/StrichSDK.html#getCameraPermissionState) and whether the device actually [has a camera](https://docs.strich.io/reference/classes/StrichSDK.html#hasCameraDevice). ### Destroying the BarcodeReader When you are done with BarcodeReader, it needs to be released by calling the [destroy](https://docs.strich.io/reference/classes/BarcodeReader.html#destroy) method. Tip: Destroying the BarcodeReader is important as it releases the camera. Failure to do so will lead to camera access errors in subsequent initializations. ## Testing on a Smartphone STRICH is optimized for barcode scanning in web apps running on smartphones. It is very important to always test your app on actual smartphones. During development, you are probably running your app locally on your desktop or laptop. When accessing your app from a smartphone on the same network, you might have to do some extra work to set up SSL, as camera access only works if the app is hosted via HTTPS or is accessed via localhost, a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). Please refer to the [Deployment Guide](https://docs.strich.io/deployment-guide.html#serving-from-a-secure-origin) for more information about how to expose your app via HTTPS during development. Tip: Tools like [ngrok](https://ngrok.com/use-cases#development) or [DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) make developing scanning apps much simpler, as they provide a publicly reachable endpoint with automatic SSL. This is very useful for testing a locally hosted app on a smartphone. ## Next Steps You are now ready to scan barcodes in your web app. For further advice, check out these additional resources. * [API Reference](https://docs.strich.io/reference/index.html) – explore the SDK and its features with our reference documentation. * [Deployment Guide](deployment-guide.html) – What to know when taking your app live. * [Best Practices for Barcode Scanning Apps](best-practices-for-scanning-apps.html) – Observing these practices ensures a good scanning experience for your users. * [Sample code repositories on Github](sample-code.html) # The Popup Scanner ## Introduction [PopupScanner](https://docs.strich.io/reference/classes/PopupScanner.html) is a pre-built modal scanning dialog that you can integrate into your app with just a single line of code. The entire UI and management of the underlying [BarcodeReader](https://docs.strich.io/reference/classes/BarcodeReader.html) is taken care of for you. It is intended for use cases where tight integration with the host app UX and custom styling is less important. Example: Scanning a serial number barcode using PopupScanner [Video](images/popup_scanner_demo.mp4) ## Scanning Barcodes ### Scanning Single Barcodes To scan a barcode, call the [PopupScanner.scan()](https://docs.strich.io/reference/classes/PopupScanner.html#scan) method and supply a [PopupConfiguration](https://docs.strich.io/reference/interfaces/PopupConfiguration.html) as the argument. The SDK needs to be in an [initialized](getting-started.html#initializing-the-sdk) state. Scanning is an asynchronous operation that returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), and should be awaited using the [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) operator. The method returns as soon as a matching barcode is read, and returns `undefined` in case the user dismissed the dialog without scanning a barcode. Example: Scanning Code 128 barcodes using PopupScanner ```JAVASCRIPT // invoke PopupScanner, restrict to Code 128 symbology let barcodes = await PopupScanner.scan({ symbologies: ['code128'] }); if (barcodes) { // process the scanned barcode value handleBarcodeScan(barcodes[0].data); } else { // handle user cancellation... } ``` ### Scanning Multiple Barcodes Scanning a sequence of barcodes within the same popup can be achieved by setting a [detectionHandler](https://docs.strich.io/reference/interfaces/PopupConfiguration.html#detectionHandler) in the [Configuration](https://docs.strich.io/reference/interfaces/PopupConfiguration.html) passed to [PopupScanner.scan()](https://docs.strich.io/reference/classes/PopupScanner.html#scan). The detection handler is a piece of code that indicates to the PopupScanner if scanning should continue or finish after a barcode was detected. Our [JavaScript sample](https://github.com/pixelverse-llc/strich-javascript-sample/blob/57b12f40f419016763d4e77328a052db53d9a172/scan-popup.html#L129) on Github illustrates how a detection handler can be used to differentiate between different three different barcodes of the same symbology using regular expressions, and stop scanning once all three have been scanned. ## Popup Scanner Elements The Popup Scanner is a semi-translucent dialog that pops up over your app’s UI. There's a small margin on all sides so it is clear that the popup is shown in the context of your app. ![Anatomy of the Popup Scanner](images/popup_scanner_anatomy.png) ### Title Area The title area is used for user guidance — we recommend using something specific to the use case as the text, like Scan serial number or Scan document ID. The default text is Scan Barcode and is localized based on the browser’s locale. For further customization, a background color matching your app’s color palette can be set. ### Active Area The active area or region of interest is bordered by a rectangle. Its dimensions are set automatically based on the configured 1D and 2D symbologies. ### Cancel Button The cancel button dismisses the dialog, without scanning a barcode. This causes the [PopupScanner.scan()](https://docs.strich.io/reference/classes/PopupScanner.html#scan) to return `undefined`. The default text is Cancel and is localized based on the browser’s locale. The text and the button background color can be overridden from their defaults. ## Sample Configuration The following snippet illustrates the full extent of customization that can be achieved through [PopupConfiguration](https://docs.strich.io/reference/interfaces/PopupConfiguration.html). ```JAVASCRIPT // A fully customized popup scanner configuration, for illustration const popupCfg = { /** * The symbologies to recognize. Here we use Code 128 with a * length of 15...17 characters and QR codes */ */ symbologies: [{ name:'code128', 'minLen':15, 'maxLen':17}, 'qr'], /** * Overrides for textual elements */ labels: { title: 'Scan IMEI Number', cancel: 'Abort Scanning' }, /** * Overrides for background colors. */ style: { cancelButtonBackgroundColor: 'rgba(1,0,0,0.5)', cancelButtonColor: 'rgb(1,1,1)', titleBackgroundColor: 'rgb(0,0,0)', titleColor: 'rgb(1,1,1)', }, /** * Choose full-hd for dense 2D or long 1D codes, otherwise hd * is fine. */ resolution: 'hd', /** * Configure audible and haptic feedback. */ feedback: { audio: true, vibration: true }, /** * Custom code to further validate barcodes. * Here we check against a regular expression. */ detectionHandler: (detections) => { return detections.some(d => d.data.match('\d{15,17}')); } } ``` ## Sample Code A vanilla JavaScript sample is available on [Github](https://github.com/pixelverse-llc/strich-javascript-sample/blob/main/scan-popup.html) illustrating how to use PopupScanner for filling form fields. It also demonstrates a more complex [detectionHandler](https://docs.strich.io/reference/interfaces/PopupConfiguration.html#detectionHandler) that supports scanning multiple codes in succession. ## Requirements PopupScanner requires browser support for the [<dialog> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog), a modern Web API which has been widely available since early 2022. # Supported Symbologies STRICH supports linear (1D) and matrix (2D) symbologies. * [Linear (1D) symbologies](1d-linear-barcodes.html) * [Matrix (2D) symbologies](2d-matrix-codes.html) Note: In typical use cases, only one or two symbologies are required. For best performance, we recommend only enabling the symbologies needed using the [symbologies](https://docs.strich.io/reference/interfaces/EngineConfiguration.html#symbologies) configuration parameter. # 1D Linear Barcodes STRICH supports the following linear (1D) barcode symbologies: * [Code 128](code-128.html) * [EAN/UPC](ean-upc.html) * [ITF (Interleaved-2-of-5)](itf-interleaved-2-of-5.html) * [Code 39](code-39.html) * [Code 93](code-93.html) * [GS1 DataBar](gs1-databar.html) * [Codabar](codabar.html) * [MSI Plessey](msi-plessey.html) # Code 128 Code 128 is a compact, variable-length linear barcode symbology, developed in 1981 and standardized as ISO/IEC 15417. It is used extensively in logistics, healthcare, transportation, and the most widely used 1D symbology outside of retail. A sample Code 128 barcode is shown below. ![Sample Code 128 barcode](images/sample_code128.png) ## Symbology Characteristics | Configuration name |`code128` | | ISO specification |ISO/IEC 15417:2007 | | Encodable character set |All 128 ISO/IEC 646 (ASCII) characters, i.e. characters 0 to 127 inclusive, and characters with byte values 128-255 | | Integrity protection |Mandatory checksum character | | Quiet zone |Leading and trailing quiet zone of ten modules (10X) is required | ## Configuration Options | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code128", minLen: 8 } ] } } ``` |4 | | maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code128", maxLen: 16 } ] } } ``` |32 | | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code128", qz: 10 } ] } } ``` |5 | ## GS1-128 (formerly UCC/EAN-128) GS1-128 is a subset of a Code 128 which encodes structured information in Application Identifiers (AIs), such as a GTIN, a lot number, etc. For example, the GS1-128 barcode below encodes a GTIN `40614141006364`, a production date `220101` (YYMMDD) in [AI 11](https://ref.gs1.org/ai/11) of and a batch or lot number `A1B2C3D4` in [AI 10](https://ref.gs1.org/ai/10). ![Sample GS1-128 barcode](images/sample_gs1_128.png) # EAN/UPC EAN and UPC barcodes are the most commonly used barcodes in retail applications. While EAN barcodes is used on product packaging in Europe and worldwide, UPC barcodes are typically used in the United States and Canada. Although introduced as early as 1974 (it recently celebrated its 50th anniversary), the EAN/UPC barcodes and remain in widespread use. GS1 – the global standards organization – mandates their replacement as part of the [Sunrise 2027 initiative](https://www.gs1us.org/industries-and-insights/by-topic/sunrise-2027), but it is unlikely that these barcodes will disappear anytime soon. Both EAN and UPC codes exist in full (EAN-13, UPC-A) and shortened (EAN-8, UPC-E) variants. The table below shows samples for each of the variants. | Symbology |Sample barcode | ----------------------------- | EAN-13 (`ean13`) |![Sample EAN-13 barcode](images/sample_ean13.png) | | EAN-8 (`ean8`) |![Sample EAN-8 barcode](images/sample_ean8.png) | | UPC-A (`upca`) |![Sample UPC-A barcode](images/sample_upca.png) | | UPC-E (`upce`) |![Sample UPC-E barcode](images/sample_upce.png) | ## Symbology Characteristics | Configuration name | `ean13`, `ean8`, `upca`, `upce` | | ISO specification |ISO/IEC 15420 | | Encodable character set |Numeric (0-9) | | Integrity protection |One mandatory checksum character | | Quiet zone | The size of the quiet zone in modules (X) depends on the variant: EAN-13: left 11X, right 7X, EAN-8: 7X, UPC-A: 9X, UPC-E: left 9X, right 7X | ## Configuration Options EAN/UPC barcodes have a fixed length of 13 digits, so the `minLen` and `maxLen` options are not available. ### EAN-13 | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "ean13", qz: 10 } ] } } ``` |3 | ### EAN-8 EAN-8 barcodes have a fixed length of 8 digits, so the `minLen` and `maxLen` options are not available. | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "ean8", qz: 10 } ] } } ``` |3 | ### UPC-A UPC-A barcodes have a fixed length of 12 digits, so the `minLen` and `maxLen` options are not available. | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "upca", qz: 10 } ] } } ``` |4 | ### UPC-E UPC-E barcodes have a fixed length of 8 digits, so the `minLen` and `maxLen` options are not available. | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "upce", qz: 10 } ] } } ``` |3 | # ITF (Interleaved-2-of-5) Interleaved-2-of-5 is a variable-length linear symbology that is used primarily in logistics and retail and encodes an even number of numerical characters. A sample Interleaved-2-of-5 barcode is shown below. ![Sample Interleaved-2-of-5 barcode](images/sample_itf.png) ## Symbology Characteristics | Configuration name |`i25` | | ISO specification |ISO/IEC 16390 | | Encodable character set |Numeric (0-9) | | Integrity protection |One optional checksum character | | Quiet zone |Leading and trailing quiet zone of ten modules (10X) is required | ## Configuration Options | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "i25", minLen: 8 } ] } } ``` |6 | | maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "i25", maxLen: 16 } ] } } ``` |32 | | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "i25", qz: 10 } ] } } ``` |5 | ## ITF-14 ITF-14 is a GS1 subset of Interleaved-2-of-5 which encodes a GTIN-14. The sample barcode below encodes a 14-digit GTIN and shows the horizontal and vertical bearer bars surrounding the barcode that are required for ITF-14. ![Sample ITF-14 barcode](images/sample_itf_14.png) If you are scanning ITF-14 barcodes exclusively, set the minimum and maximum length to 14 to avoid short scans. ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "i25", minLen: 14, maxLen: 14 } ] } } ``` # Code 39 Code 39 is a variable-length linear barcode symbology, developed in 1974 and standardized as ISO/IEC 16388. The data density is comparatively low, so it requires considerably more space to encode the same amount of data and can not be used to label smaller items. Code 39 is used in inventory and item tracking applications as well as some postal services. A sample Code 39 barcode is shown below. ![Sample Code 39 barcode](images/sample_code39.png) ## Symbology Characteristics | Configuration name |`code39` | | ISO specification |ISO/IEC 16388 | | Encodable character set |Alphanumeric (A-Z) and numeric (0-9) as well as $ % + - . / and space characters | | Integrity protection |Optional mod 43 check digit | | Quiet zone |Leading and trailing quiet zone of at least ten modules (10X) is required | ## Optional Check Digit Code 39 supports an optional check digit for enhanced data security (ISO/IEC 16388:2007, A.1 Check Character). The check digit is placed immediately after the final data character and before the stop character. By default, check digit verification is disabled (`checksumMode` is set to `0`) and all characters, including the check digit, will be returned when the barcode is read. If the SDK is configured to validate the check digit, it can either be stripped (`1`) or transmitted (`2`). Tip: Please note that if you enable check digit verification, Code 39 barcodes that do not contain a valid check digit will no longer be readable. If you intend to read Code 39 barcodes with and without check digits, you have to implement the check digit validation yourself. Code 39 does not indicate the presence of a check digit in any way, so there is no reliable way to detect if a barcode has a check digit or not, unless you are using fixed-length barcodes. ### Example: Code 39 barcode with check digit In the barcode below, the R character is a check digit and the encoded data is CODE 39. The asterisk characters denote start and stop characters and are typically included in the human-readable interpretation but not transmitted by the decoder. ![Sample Code 39 barcode with check digit](images/sample_code39_check_digit.png) ## Configuration Options | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code39", minLen: 8 } ] } } ``` |4 | | maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code39", maxLen: 16 } ] } } ``` |32 | | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code39", qz: 10 } ] } } ``` |5 | | checksumMode | Check digit (mod 43) processing mode: 0: no check digit expected 1: validate and strip check digit 2: validate and transmit check digit | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code39", checksumMode: 1 } ] } } ``` |0 | # Code 93 Code 93 is a variable-length linear barcode symbology, developed in 1982 and standardized in the AIM-BC5-2000 specification. It was designed to provide higher density and better security than the [Code 39](code-39.html) symbology. The data density is comparatively low, so it requires considerably more space to encode the same amount of data and can not be used to label smaller items. A sample Code 93 barcode is shown below. ![Sample Code 93 barcode](images/sample_code93.png) ## Symbology Characteristics | Configuration name |`code93` | | AIM specification |AIM-BC5-2000 | | Encodable character set |All 128 ASCII characters | | Integrity protection |Two mandatory check characters | | Quiet zone |Leading and trailing quiet zone of at least ten modules (10X) is required | ## Configuration Options | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code93", minLen: 8 } ] } } ``` |4 | | maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code93", maxLen: 16 } ] } } ``` |32 | | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code93", qz: 10 } ] } } ``` |5 | # GS1 DataBar GS1 DataBar is a family of linear symbologies with both fixed-length and variable-length and stacked variants. DataBar is commonly used in retail settings, for product identification and coupon codes. Previously DataBar was also known as Reduced Spaced Symbology (RSS), but the name was eventually abandoned because of a name clash with another technology also named RSS: Really Simple Syndication. GS1 DataBar exists in a number of variants: * DataBar Omnidirectional encodes a GS1 GTIN * DataBar Expanded encode arbitrary GS1 AIs. * Stacked versions of both DataBar and DataBar Expanded exist to condense the information into a more square shape. The main use case of DataBar is adding additional product information apart from the GTIN, like weight or expiry date. DataBar barcodes are often found on fresh produce. | Symbology |Sample barcode | ----------------------------- | DataBar Omnidirectional (`databar`) |![Sample DataBar barcode](images/sample_databar.png) | | DataBar Omnidirectional Stacked (`databar`) |![Sample DataBar Stacked barcode](images/sample_databar_stacked.png) | | DataBar Expanded (`databar-exp`) |![Sample DataBar Expanded barcode](images/sample_databar_exp.png) | | DataBar Expanded Stacked (`databar-exp`) |![Sample DataBar Expanded Stacked barcode](images/sample_databar_exp_stacked.png) | ## Symbology Characteristics | Configuration name | `databar`, `databar-exp` Stacked variants are automatically enabled. | | ISO specification |ISO/IEC 24724 | | Encodable character set | DataBar: Numeric (0-9) DataBar Expanded: Numeric and alphanumeric characters, 20 punctuation characters. | | Integrity protection |One mandatory checksum character | | Quiet zone |No quiet zones are required | ## Configuration Options DataBar and DataBar Expanded symbologies do not have symbology-specific configuration options. ## Limitations * The DataBar Limited symbology is not supported. Please [contact us](mailto:hello@pixelverse.ch) if you require DataBar Limited support. * 2D component linkage is not supported # Codabar Codabar is a variable-length linear barcode symbology developed in 1972 and standardized in the AIM-BC3-2000 specification. STRICH implements Rationalized Codabar, which uses a fixed ratio of 3:1 for the wide and narrow elements, as recommended in the AIM-BC3-2000 specification. Previous specifications such as the original Pitney-Bowes specification used a fixed character width and varying wide-narrow ratios. Codabar is widely used in libraries. A sample Codabar barcode is shown below. ![Sample Codabar barcode](images/sample_codabar.png) ## Symbology Characteristics | Configuration name |`codabar` | | AIM specification |AIM-BC3-2000 | | Encodable character set |Numeric (0-9), six special characters (-, $, :, /, ., +) and four start/stop characters (A, B, C, D) that are transmitted as well as they are used to distinguish applications. | | Integrity protection |Optional mod 16 check digit | | Quiet zone |Leading and trailing quiet zone of at least ten modules (10X) is required | ## Optional Check Digit Codabar supports an optional check character for enhanced data security (AIM-BC3-2000, E.3 A Check Character Format). The check digit is placed immediately after the final data character and before the stop character. By default, check digit verification is disabled (`checksumMode` is set to `0`) and all characters, including the check digit, will be returned when the barcode is read. If the SDK is configured to validate the check digit, it can either be stripped (`1`) or transmitted (`2`). Tip: Please note that if you enable check digit verification, Codabar barcodes that do not contain a valid check digit will no longer be readable. If you intend to read Codabar barcodes with and without check digits, you have to implement the check digit validation yourself. Codabar does not indicate the presence of a check digit in any way, so there is no reliable way to detect if a barcode has a check digit or not, unless you are using fixed-length barcodes. ### Example: Codabar barcode with check digit The barcode belows encodes the value A1234567890A and includes a check digit that is not visible in the human-readable interpretation below the barcode. ![Sample Codabar barcode with check digit](images/sample_codabar_check_digit.png) If check digit verification is disabled, the barcode will read as A12345678903A with the final 3 being the check digit. ## Configuration Options | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "codabar", minLen: 8 } ] } } ``` |4 | | maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "codabar", maxLen: 16 } ] } } ``` |32 | | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "codabar", qz: 10 } ] } } ``` |5 | | checksumMode | Check digit (mod 10) processing mode: 0: no check digit expected 1: validate and strip check digit 2: validate and transmit check digit | ```JAVASCRIPT const config = { engine: { symbologies: [{name: "codabar", checksumMode: 1}] } } ``` |0 | ## Start/Stop Characters Codabar uses four distinct start/stop characters, `A`, `B`, `C` and `D`. Although the AIM specification says otherwise, the start/stop characters are often not included in the human-readable interpretation printed below the barcode. ![AIM specification note on start/stop characters being included in the human-readable interpretation](images/codabar_aim_hri_note.png) The SDK includes start/stop characters in its output, as they can be used for application-specific purposes. Start and stop characters also count towards the barcode length and need to be taken into consideration when using `minLen` and/or `maxLen` to restrict the acceptable barcode length. ### Example: Codabar library barcode The Codabar barcode below encodes the value `A34444913793584B`, with `A` used as start character and `B` as stop character. The total length of the barcode is `16`. ![Sample Codabar library barcode](images/sample_codabar_bpl.jpg) To restrict the SDK to only these types of barcodes, you could set `minLen` and `maxLen` to the `16`. ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "codabar", minLen: 16, maxLen: 16 } ] } } ``` # MSI Plessey MSI Plessey is a variable-length linear barcode symbology developed around 1970. STRICH implements Rationalized Codabar, which uses a fixed ratio of 3:1 for the wide and narrow elements, as recommended in the AIM-BC3-2000 specification. Previous specifications such as the original Pitney-Bowes specification used a fixed character width and varying wide-narrow ratios. MSI Plessey is not used widely anymore, but can still be found in inventory control applications and on shelf tags. A sample MSI Plessey barcode is shown below. ![Sample MSI Plessey barcode](images/sample_msi-plessey.svg) ## Symbology Characteristics | Configuration name |`msi-plessey` | | Encodable character set |Numeric (0-9) | | Integrity protection |Modulo 10 check digit (optional, but highly recommended) | | Quiet zone |Leading and trailing quiet zone of at least ten modules (10X) is recommended | ## Optional Check Digit MSI Plessey supports an optional check character for more reliable scanning. The check digit is placed immediately after the final data character and before the stop character. By default, check digit verification is enabled and the check digit is stripped from the returned data (`checksumMode` is set to `1`). Warning: The MSI Plessey symbology is not self-checking, it relies heavily on the check digit for reliable reading. Using a check digit is strongly recommended. ## Restricting Minimum and Maximum Length In addition to using a check digit, we also strongly recommend to configure `minLen` and `maxLen` to your use case. Most applications of MSI Plessey use fixed-length barcodes. For example, if all your barcodes encode eight digits, setting `minLen` and `maxLen` to `8` is recommended to prevent misreads and improve decoding efficiency. ## Configuration Options | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "msi-plessey", minLen: 8 } ] } } ``` |6 | | maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "msi-plessey", maxLen: 8 } ] } } ``` |12 | | qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "msi-plessey", qz: 10 } ] } } ``` |10 | | checksumMode | 0: no check digit expected 1: single Modulo 10 check digit validated and stripped 2: single Modulo 10 check digit validated and transmitted | ```JAVASCRIPT const config = { engine: { symbologies: [{name: "msi-plessey", checksumMode: 1}] } } ``` |0 | # 2D Matrix Codes STRICH supports the following matrix (2D) code symbologies: * [QR Code](qr-code.html) * [Data Matrix](data-matrix.html) * [Aztec Code](aztec-code.html) * [PDF417](pdf417.html) # QR Code QR Code is the most well-known 2D matrix symbology, originally developed in 1994 by Denso Wave, it is used in a wide variety of industries and applications. It is now standardized as ISO/IEC 18004. A sample QR Code is shown below: ![Sample QR Code](images/sample_qr.svg) A reduced variant called Micro QR Code is defined in the same ISO specification and also supported by SDK versions 1.12.0 and later. A sample Micro QR Code is shown below: ![Sample Micro QR Code](images/sample_microqr.svg) ## Symbology Characteristics | Configuration name | `qr` (regular QR Code), `microqr` (Micro QR Code) | | ISO specification |ISO/IEC 18004 | | Encodable character set |All 8-bit values can be encoded. | | Capacity |QR Code symbols can encode up to 7'089 numeric characters, 4'296 alphanumeric characters, 2'953 bytes of binary data or 1'817 kanji characters for a version 40 symbol at the lowest error correction level. Micro QR Codes are limited to 35 numeric, 21 alphanumeric, 15 bytes of binary data or 9 kanji characters for an M4 version symbol at the lowest error correction level. | | Integrity protection |Reed-Solomon error correction, selectable (four levels, three for Micro QR Code) | | Quiet zone | A quiet zone of four modules (4X) is required around the symbol. Micro QR Codes require a 2X quiet zone. | QR Code is a registered trademark of [DENSO WAVE INCORPORATED](http://www.denso-wave.com/en/). ## Configuration Options ### QR Code | Option |Description |Sample Configuration |Default | ------------------------------------------------------ | roundedFPs |Toggle recognition of QR codes with rounded finder patterns | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "qr", roundedFPs: true } ] } } ``` |false | | curved |Toggle recognition of QR codes on curved surfaces | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "qr", curved: true } ] } } ``` |false | Warning: Enabling recognition of QR codes with rounded finder patterns forces the library to use less efficient algorithms. We do not recommend using QR codes that are not compliant with the ISO specification. The edges of the square finder patterns are useful in determining the location of adjacent finder patterns, and help in mapping the QR Code grid. ### Micro QR Code Micro QR Code does not have any symbology-specific configuration options. ## Limitations The following elements of the ISO specification are not implemented: * ECI (Extended Channel Interpretation) * Model 1 QR Codes Tip: The term “Model 1” is distinct from “Version 1”. Somewhat confusingly, the version of a QR code denotes its size. A version 1 QR code has a size of 21x21 modules. ISO/IEC 18004:2006 stopped mentioning this first iteration of QR Code in 2006, and redefined QR Code to mean Model 2 QR Code, so we do not consider it relevant in today's landscape. ![Model 1 QR Code](images/qr_model1.png) # Data Matrix Data Matrix is a high-density 2D matrix symbology that can store large amounts of data in a small area. Data Matrix codes are used to label small items. A sample Data Matrix Code is shown below. ![Sample Data Matrix Code](images/sample_datamatrix.png) ## Symbology Characteristics | Configuration name |`datamatrix` | | ISO specification |ISO/IEC 16022 | | Encodable character set |All 8-bit values can be encoded. | | Capacity |The largest symbol can encode up to 3116 numeric characters, 2335 alphanumeric characters, or 1555 bytes of binary data. | | Integrity protection |Reed-Solomon error correction (ECC200). ECC levels 000-140 are not supported. | | Quiet zone |A quiet zone of one module (1X) is required around the symbol. | ## Configuration Options Data Matrix does not have any symbology-specific configuration options. ## Limitations The following elements of the ISO specification are not implemented: * Non-square Data Matrix codes * Structured Append * ECI (Extended Channel Interpretation) * ECC 000-140 convolution-based error correction (only ECC 200 is supported) # Aztec Code Aztec Code is a high-density 2D matrix symbology that can store large amounts of data in a small area and does not require a quiet zone. Aztec codes are commonly used in ticketing, for instance railway tickets or airline boarding passes. The name derives from the unique shape of the central finder pattern of concentric squares, which is reminiscent of an Aztec pyramid viewed from above. A sample Aztec Code is shown below. ![Sample Aztec Code](images/sample_aztec.png) Aztec codes are frequently seen on mobile airline boarding passes. On printed boarding passes, [PDF417](pdf417.html) is more commonly used. A sample boarding pass stored in Apple Wallet is shown below. ![Sample Boarding Pass](images/sample_aztec_boarding_pass.png) ## Symbology Characteristics | Configuration name |`aztec` | | ISO specification |ISO/IEC 24788:2008 | | Encodable character set |All 8-bit values can be encoded. | | Capacity |The largest symbol can encode up to 3832 numeric or 3067 alphanumeric characters, or 1914 bytes of binary data. | | Integrity protection |Aztec Code has strong Reed-Solomon error correction for both the inner part (bullseye) and the surrounding payload. | | Quiet zone |A quiet zone is not required. | ## Configuration Options Aztec Code does not have any symbology-specific configuration options. ## Limitations The following elements of the ISO specification are not implemented: * Structured Append * Reader Initialization Symbols * Aztec Runes * ECI (Extended Channel Interpretation) # PDF417 PDF417 is a stacked 2D barcode symbology. It was designed to be read by conventional 1D barcode scanners in multiple passes, and is composed of multiple 1D barcodes stacked over each other. PDF417 are often found on airline boarding passes and driving licenses. A sample PDF417 barcode is shown below. ![Sample PDF417 barcode](images/sample_pdf417.png) ## Symbology Characteristics | Configuration name |`pdf417` | | ISO specification |ISO/IEC 15438 | | Encodable character set |Three different compaction modes to efficiently encode numeric, alphanumeric and binary data. | | Capacity |The largest symbol can encode up to 3116 numeric characters, 2335 alphanumeric characters, or 1555 bytes of binary data. | | Integrity protection |Reed-Solomon error correction, with ten error correction levels (0-9) | | Quiet zone |A quiet zone of two modules (2X) is required around the symbol. | ## Configuration Options PDF417 does not have any symbology-specific configuration options. ## Limitations The following elements of the ISO specification are not implemented: * Macro PDF417 (distributing data across multiple codes) * ECI (Extended Channel Interpretation) ## Related Material Tip: Tutorial: [Scanning PDF417 Barcodes on US Driving Licenses](scanning-pdf417-barcodes-on-us-driving-licenses-for-age-verification.html) # Deployment Guide Advice on deploying an app that uses STRICH SDK to a hosting service. ## Serving From a Secure Origin STRICH accesses the smartphone camera using the web browser's [getUserMedia](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security) API. This API can only be used in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts), meaning your app needs to be served from an HTTPS, file:/// or localhost URL. Many developers of mobile web apps use tools like ngrok to expose a publicly accessible HTTPS endpoint with automatically generated TLS certificate. For local development, web frameworks such as Angular usually include a built-in web server, which will default to serving the app on localhost using HTTP, not HTTPS. There are usually options that can be supplied to control the interface the server binds and TLS support. Some examples for popular frameworks are included below. Tip: Tools like [ngrok](https://ngrok.com/use-cases#development) or [DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) make developing scanning apps much simpler, as they provide a publicly reachable endpoint with automatic SSL. This is very useful for testing a locally hosted app on a smartphone. ### Angular ```SHELL ng serve --host 0.0.0.0 --ssl ``` ### Parcel ```SHELL parcel src/index.html --https ``` ### React ```SHELL HTTPS=true npm start ``` ### Vite / Astro The [Vite documentation](https://vite.dev/config/server-options.html#server-https) suggests using the `@vitejs/plugin-basic-ssl` plugin. ```JAVASCRIPT import { defineConfig } from 'astro/config'; import basicSsl from '@vitejs/plugin-basic-ssl' // https://astro.build/config export default defineConfig({ vite: { plugins: [basicSsl()], server: { https: true }, }, }); ``` Binding to the public IP address of the network interface can be done via the `--host` CLI parameter: ```SHELL npm run dev -- --host ``` ### Vue [Add https: true field to the vue.config.js file](https://stackoverflow.com/questions/45807049/how-to-run-vue-js-dev-serve-with-https/50741529#50741529). ## License Key Management Even though it is impossible to fully secure a client-side license key, we recommend the following best practices: * If your app requires authentication, expose the license key only on authenticated routes. * If your app has a dynamic configuration mechanism, store your license key there instead of hard-coding it in your app. This will make it easier to exchange your license key if it expires (offline keys) or in case it's compromised. Note: For offline license keys, we highly recommend to manage the license key in a dynamic configuration mechanism, so it can be exchanged without redeploying the application when it expires. ## Firewall Configuration Note: This applies only to Basic and Professional plans. Enterprise licenses optionally allow fully offline operation. For Basic and Professional licenses, an online license check is performed via an HTTPS request. If you are running in a restricted environment, make sure that HTTPS connections to hosts *.strich.io are allowed, otherwise the SDK can not be initialized. ## Content Security Policy (CSP) Note: This applies only to Basic and Professional plans. Enterprise licenses optionally allow fully offline operation. When apps are being served with a Content Security Policy, a few entries are needed for STRICH license checking to work. | Directive |Values |Description | ---------------------------------- | connect-src | *.strich.io, data: | Required for license verification and analytics, as well as loading embedded Web Assembly from data URLs. | | script-src | wasm-unsafe-eval, unsafe-eval | Required for Web Assembly execution. Older browsers may not yet support wasm-unsafe-eval, and therefore require unsafe-eval. | | img-src | data: | Required for STRICH to load embedded image assets such as icons. | | media-src | data: | Required for STRICH to load embedded sound assets such as the beep sound played in [audio feedback](https://docs.strich.io/reference/interfaces/FeedbackConfiguration.html#audio). | ## User-Agent Client Hints (optional) Tip: This step is optional, but necessary if you would like to see detailed statistics about device models in the Customer Portal. Recent versions of Chrome (starting mid-2023) and browsers based on Chrome (i.e. Edge) include a privacy feature called User-Agent reduction which limits the amount of data a browser sends as part of its requests, especially the User-Agent HTTP header. Instead of the device model, e.g. `SM-G991` for a Samsung Galaxy S21, Chrome will now just send the value `K`, hiding the actual device model. Websites have the ability to ask the browser for additional information, so-called Client hints. This is commonly done by including an `Accept-CH` header in the response serving the website. Alternatively, and often easier to achieve for application developers, Client Hints can also be requested by including Accept-CH in a meta tag of the application's HTML code: ```HTML ``` To pass the Client Hints to STRICH for more detailed statistics, you need to delegate them. STRICH infrastructure is a third-party origin ― the first party being the origin that serves the app (you). To delegate the Client Hints to STRICH, also add the following snippet to the HEAD section of your HTML code: ```HTML ``` After you put both mechanisms in place (requesting Client Hints, and delegating them to STRICH), you should start seeing detailed device models in the usage stats. ## Optimizing Online License Check (optional) Tip: This applies only to Basic and Professional plans. Enterprise licenses optionally allow fully offline operation. During initialization, the SDK performs a check of the license key by contacting the license service. You can provide a hint to the web browser that will cause it to [preemptively perform the DNS lookup](https://developer.mozilla.org/en-US/docs/Web/Performance/dns-prefetch) for the license service, making the check run faster in some cases. To enable DNS-prefetching, add the following snippet to the HEAD section of your HTML code: ```HTML ``` If the license check was successful, the decision is stored locally for up to 48 hours, allowing for intermittent offline use. # Best Practices for Scanning Apps This guide contains a collection of advice for building great scanning apps. While no two apps are the same, we believe that the recommendations in this guide are pretty universal. ## Configure only the symbologies you need Typically scanning apps only scan certain types of barcodes. It is therefore important to enable only those types, and to avoid enabling types that are not needed. This avoids unnecessary image processing and computation dedicated to locating and decoding these types. ### Example: setting supported symbologies to Code 128 linear barcodes and QR codes: ```JAVASCRIPT const configuration = { // ... engine: { symbologies: [ "code128", "qr" ] } // ... }; ``` [EngineConfiguration API Reference](https://docs.strich.io/reference/interfaces/EngineConfiguration.html#symbologies) For variable-length symbologies like Code 128, Code 39 etc. it is also advisable to set `minLen` and `maxLen` according to your use case. For instance if you are scanning Code 128 barcodes with a length of between 8 and 10 characters, you could further restrict the configuration: ### Example: setting minLen/maxLen for variable-length symbologies ```JAVASCRIPT const configuration = { // ... engine: { symbologies: [ { name: "code128", minLen: 8, maxLen: 10 } ] } // ... }; ``` ## Enable audio and vibration feedback Audio and vibration user feedback is not a gimmick, it is an important part of the scanning user experience. There's nothing that can replace that satisfying beep after scan. ```JAVASCRIPT const configuration = { // ... feedback: { audio: true, vibration: true } // ... }; ``` [FeedbackConfiguration API Reference](https://docs.strich.io/reference/interfaces/FeedbackConfiguration.html) ## Choose an appropriate region of interest The region of interest specifies the extent of the video feed where STRICH should to locate and decode barcodes. The larger this region is, the more time is spent in computation, and the longer it may take for a barcode to be detected. Humans have a tendency to center the object of interest in their field of view. This principle (let's call it centrality) can also be applied to a camera feed. Make sure the region of interest is centered and its extent is appropriate for the barcode type. The region of interest is bounded by a viewfinder in the overlay, users will naturally try to center the barcode in the viewfinder. ### Example 1: narrow region of interest, suitable for 1D barcodes ```JAVASCRIPT const configuration = { // ... locator: { regionOfInterest: { // restrict active area to a horizontal bar in the center left: 0.05, right: 0.05, top: 0.4, bottom: 0.4 } } // ... }; ``` ![Horizontal region of interest](images/horizontal_region_of_interest.jpg) ### Example 2: square region of interest, suitable for 2D barcodes ```JAVASCRIPT const configuration = { // ... locator: { regionOfInterest: { // restrict active area to a square-shaped region in the center left: 0.2, right: 0.2, top: 0.3, bottom: 0.3 } } // ... }; ``` ![Square region of interest](images/square_region_of_interest.jpg) [LocatorConfiguration API Reference](https://docs.strich.io/reference/interfaces/LocatorConfiguration.html#) ## Adding manual entry capability There is always a possibility that a barcode type is supported by STRICH but can't be scanned because it is too damaged or otherwise illegible. Adding a fallback option to input a code manually using the smartphone's on-screen keyboard is therefore always recommended, especially for critical applications. A subtle keyboard icon that toggles visibility of an input field, and focuses the field so the keyboard pops up usually works fine. # Integration Guides # Angular Integration Guide This guide contains common patterns and advice on how to best integrate STRICH into an app built with the [Angular](https://angular.dev) framework. ## SDK Initialization SDK initialization should be done as early as possible, and only once. Angular apps usually contain a root component (named `AppComponent` by default). The AppComponent's `ngOnInit` is a convenient place to initialization the SDK, even better is to use a Service to encapsulate interaction with the SDK. ## Using a Service If you intend to read barcodes in multiple screens and not just in a single component, it makes sense to move common code into a [Service](https://angular.dev/guide/di/creating-injectable-service). Good candidates for putting in a Service are: Initialization : [Singleton services](https://angular.dev/guide/ngmodules/singleton-services) are initialized once, which makes them a good fit for initialization (which also happens only once). Scanning results : Using an `Observable` in the Service allows propagating scanned codes to components. Configuration : Sharing common parts of `BarcodeReader` configuration avoids duplication. Our Angular sample contains an example of such a Service: [ScannerService](https://github.com/pixelverse-llc/strich-angular-sample/blob/main/src/app/services/scanner.service.ts) ## Using route guards to require SDK initialization for routes that need scanning Using a [route guard](https://angular.dev/guide/routing/common-router-tasks#preventing-unauthorized-access) is an idiomatic way of requiring certain conditions to be met before accessing a part of your app. Screens requiring a `BarcodeReader` rely on the SDK to be initialized. This can be achieved by putting a guard on the route that checks the SDK initialization status. Our Angular sample contains an example of a guard that will only permit navigating to a route if the SDK is initialized: [sdkInitialized guard](https://github.com/pixelverse-llc/strich-angular-sample/blob/main/src/app/guards/strich-scanning-guards.ts) ## Initializing BarcodeReader in ngAfterViewInit The `BarcodeReader` requires a host element with known size to be present. An Angular component typically provides the host element in its template. Using the `ngAfterViewInit` lifecycle hook ensures that the component has finished initializing its view and child views, and is therefore an appropriate place to initialize the `BarcodeReader`. * [Angular component lifecycle](https://angular.dev/guide/components/lifecycle) Tip: The `ngOnInit` lifecycle hook can not be used to initialize a `BarcodeReader`, as the host element must be present and its size known, and that is only the case after `ngAfterViewInit` has run. ## Calling destroy() when BarcodeReader is no longer needed Components that include a `BarcodeReader` in their content need to destroy the `BarcodeReader` instance when they no longer intend to use it, otherwise the camera can remain in use, causing subsequent initializations to fail. The [ngOnDestroy](https://angular.dev/guide/components/lifecycle#ngondestroy) component lifecycle hook is an appropriate place for calling [BarcodeReader.destroy()](https://docs.strich.io/reference/classes/BarcodeReader.html#destroy), as it is invoked when the component is destroyed by Angular, for instance during a navigation event. ## Serving your app during development STRICH uses the smartphone camera to scan barcodes. Access to the smartphone camera is only available in secure origins, so your app needs to be served over HTTPS. The Angular development server (`ng serve`) supports serving via HTTPS using the `--ssl` command-line argument Binding to 0.0.0.0 instead of localhost (the default) allows to easily test on a smartphone connected to the same network as the developer machine. ```SHELL ng serve --host 0.0.0.0 --ssl ``` If you are using the popular [ngrok](https://ngrok.com) tool to serve your app, you might instead serve over plain HTTP, but then you need to disable checking of the `Host` header. ```SHELL ng serve --disable-host-check ngrok http 4200 ``` Tip: Tools like [ngrok](https://ngrok.com/use-cases#development) or [DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) make developing scanning apps much simpler, as they provide a publicly reachable endpoint with automatic SSL. This is very useful for testing a locally hosted app on a smartphone. ## Sample Project In addition to this guide, we also provide a [sample project](https://github.com/pixelverse-llc/strich-angular-sample) showing how to integrate of the SDK into an Angular application. Some of the aspects that are shown are: * Decoupling application logic from SDK and BarcodeReader by using a [Service](https://angular.dev/guide/di/creating-injectable-service) * Wrapping BarcodeReader UI and lifecycle management into a [Component](https://angular.dev/guide/components) * Using route guards to enforce preconditions (SDK is initialized) and postconditions (BarcodeReader is destroyed) when entering and leaving a route # SvelteKit Integration Guide We're working on it - hang in there! In the meantime, check out our SvelteKit sample code repository on [Github](https://github.com/pixelverse-llc/strich-sveltekit-sample). # Vanilla JS Integration Guide You don't need a framework to use STRICH! Check out our sample code repository on [Github](https://github.com/pixelverse-llc/strich-javascript-sample) for an example on how to use STRICH in plain JavaScript/HTML/CSS. # Vue Integration Guide We're working on it - hang in there! In the meantime, check out our [Vue 2](https://github.com/pixelverse-llc/strich-vue2-sample) and [Vue 3](https://github.com/pixelverse-llc/strich-vue3-sample) sample code repositories on [Github](https://github.com/topics/strich-sdk). # Salesforce Integration Guide This guide describes how to integrate STRICH into Salesforce [Lightning Web Components](https://developer.salesforce.com/developer-centers/lightning-web-components). The information in this guide is BETA, please contact us directly if you wish to integrate with Salesforce. ## Loading the SDK from Static Resources Due to restrictions on loading of third-party ES6 modules, a non-modular build of STRICH needs to be uploaded to [Static Resources](https://developer.salesforce.com/docs/platform/lwc/guide/create-resources.html). ### Uploading non-modular STRICH build to Static Resources At the time of writing, Lightning Web Components can not `import` ES6 modules from arbitrary URLs such as from a CDN. They can import from other components, but the file size is limited to 128 KB, making it unsuitable for STRICH, which currently bundles WebAssembly. Upload the file `strich_noesm.js` as a static resource, do not add it to a zip archive beforehand: * Name: `strich_noesm` * Description: `Non-modular build of STRICH SDK` * File: After uploading, the static resource should look like this: ![Static resources screenshot](images/sf_static_resource.png) ## Configuring Content-Security Policies (CSP) CSP are accessed by entering Setup, then navigating to Settings > Security > Trusted URLs in the sidebar. ### Data URLs STRICH embeds images and sounds, as well as WebAssembly code into its JavaScript bundle and loads these assets via [Data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs). To enable loading from Data URLs, add a Trusted URL: * URL: `data:` * Directives: `connect-src`, `img-src` and `media-src` ### License Service Note: This step is not required for Enterprise licenses with the offline license check add-on. To allow connections to the license service, add a Trusted URL: * URL: `https://license.strich.io` * Directives: `connect-src` After setting up the trusted URLs, the dialog should look like similar to the screenshot below: ![Trusted URLs screenshot](images/sf_trusted_urls.png) ## Strict Security If you run into CSP issues, please check that the Security Level setting for Content-Security Policies is set to Strict CSP (see screenshot below). ![Security and Privacy settings screenshot](images/sf_security_settings.png) ## Sample Code ### WebComponent Template ```HTML ``` ### WebComponent Logic For production code, we recommend removing the console logging statements, but they can be useful during integration. ```JAVASCRIPT import { LightningElement } from 'lwc'; import { loadScript } from 'lightning/platformResourceLoader'; import strich_noesm from '@salesforce/resourceUrl/strich_noesm'; export default class Scanner extends LightningElement { errorMessage = null; barcodeValue = ''; barcodeReader = null; sdkState = '-'; sdkVersion = null; constructor() { super(); } async connectedCallback() { try { // load STRICH SDK from static resources, requires non-modular build ('script tag') await loadScript(this, strich_noesm); this.sdkState = 'SDK loaded'; console.log(`Successfully loaded STRICH (noesm) from static resources`); // from here on, global variable 'strich' is defined await this.initializeSDK(); this.sdkState = 'SDK initialized'; const hostElem = this.template.querySelector('.strich-container'); this.barcodeReader = await this.initializeBarcodeReader(hostElem); } catch (error) { console.error('STRICH SDK initialization failed', error); this.errorMessage = error.message; } } async disconnectedCallback() { if (this.barcodeReader) { await this.barcodeReader.destroy(); this.barcodeReader = null; this.sdkState = 'BarcodeReader destroyed'; } } async initializeSDK() { const licenseKey = ''; await strich.StrichSDK.initialize(licenseKey); this.sdkVersion = strich.StrichSDK.version(); } async initializeBarcodeReader(hostElem) { const barcodeReader = new strich.BarcodeReader({ selector: hostElem, frameSource: { resolution: 'full-hd' }, engine: { symbologies: ['code128'] } }); barcodeReader.detected = (detections) => { this.barcodeValue = detections[0].data; }; await barcodeReader.initialize(); this.sdkState = 'BarcodeReader initialized'; await barcodeReader.start(); this.sdkState = 'BarcodeReader started'; return barcodeReader; } } ``` ### WebComponent Styling ```CSS .strich-container { min-height: 240px; height: 240px; background-color: black; position: relative; } .strich-footer { padding: 4px; display: flex; flex-direction: row; gap: 10px; justify-content: space-between; } .sdk-error-message { color: red; font-weight: bold; } ``` # OutSystems Integration Guide In collaboration with Stefan Nikolic, Senior Developer at Abeltech.eu. ## Forge Component The preferred way to integrate STRICH into OutSystems apps is by using our [Forge Component](https://www.outsystems.com/forge/component-overview/18265/strich-barcodereader-o11). ## Custom Integration If you are for some reason unable to use the Forge component, The rest of this guide focuses on custom JavaScript integrations. ### Adding STRICH SDK as a Script Add the file `strich-noesm.js` from the NPM distribution or from a CDN to the module's scripts as described in [Use JavaScript Code from an External Library](https://success.outsystems.com/documentation/11/extensibility_and_integration/javascript/extend_your_mobile_and_reactive_apps_using_javascript/use_javascript_code_from_an_external_library/). Note that OutSystems does not seem to support loading ES6 modules, so it is important to use the non-modular build instead of the regular `strich.js` distribution. ![Non-modular STRICH build added as a script in OutSystems](images/outsystems_script.png) ### Adding a BarcodeReader to a Screen #### Adding a Container host element The `BarcodeReader` instance needs a widget in which it will live. Add a Container element and give it a name, here we used `hostElem`. Add the `position-relative` style class to the widget, so it will have relative CSS positioning, which is required for `BarcodeReader`. ![Container widget](images/outsystems_host_element.png) #### Initializing SDK and BarcodeReader in onReady The [onReady](https://success.outsystems.com/documentation/11/developing_an_application/implement_application_logic/screen_and_block_lifecycle_events/#on-ready) client action can be used to initialize the [SDK](https://docs.strich.io/reference/classes/StrichSDK.html#initialize) and the [BarcodeReader](https://docs.strich.io/reference/classes/BarcodeReader.html#initialize). ![onReady client action calls JavaScript block](images/outsystems_onready.png) In the client action, we call a custom JavaScript block that receives the ID of the host widget as an input parameter. The ID is used to look up the DOM element, which is used to initialize the `BarcodeReader`. The script returns the initialization Promise as an output parameter, which is stored in a local variable by the client action. When a barcode is read, a client action is executed and passed the barcode data. ```JAVASCRIPT const hostElem = document.getElementById($parameters.hostElementId); const licenseKey = ''; $parameters.barcodeReader = strich.StrichSDK.initialize(licenseKey) .then(() => { const cfg = { selector: hostElem, engine: { symbologies: ['code128', 'qr'] } }; const barcodeReader = new strich.BarcodeReader(cfg); return barcodeReader.initialize(); }) .then((barcodeReader) => { barcodeReader.detected = (detections) => { $actions.onBarcodeDetected(detections[0].data); }; return barcodeReader.start().then(() => { return barcodeReader; }); }); ``` Note: The script currently returns the Promise that resolves into a BarcodeReader as an output parameter. We are investigating ways to make this more seamless. #### Tearing down BarcodeReader in onDestroy The [onDestroy](https://success.outsystems.com/documentation/11/developing_an_application/implement_application_logic/screen_and_block_lifecycle_events/#on-destroy) client action can be used to destroy the `BarcodeReader`, causing the camera to be released for subsequent use. ![outsystems_ondestroy.png](images/outsystems_ondestroy.png) The onDestroy client action receives the local variable containing the `BarcodeReader` Promise as an input parameter, and uses it to [destroy](https://docs.strich.io/reference/classes/BarcodeReader.html#destroy) the `BarcodeReader` when the screen is exited. ```JAVASCRIPT const barcodeReaderPromise = $parameters.barcodeReader; barcodeReaderPromise .then((barcodeReader) => { return barcodeReader.destroy() }); ``` Note: Destroying a `BarcodeReader` is an asynchronous action that should be awaited before the screen is left. Currently this is not the case, as during a navigation from one screen to the next, the previous screen onDestroy will only be called after the next screen's onReady is called. Also it is unclear if OutSystems supports asynchronous lifecycle hooks. This means that the approach described in this guide does not currently support two consecutive screens that both use a `BarcodeReader`. # Advanced Topics * [Reading GS1 Data in Barcodes](reading-gs1-data.html) * [Customizing the Barcode Scanning UI](customizing-the-scanner-ui.html) * [Non-Modular Build Flavor](non-modular-build-flavor.html) * [Automated Testing](automated-testing.html) * [Customizing the Scanner Sound](customizing-the-scanner-sound.html) # Automated Testing Automated testing of barcode scanning apps is not straightforward because of the camera requirement. The camera is a physical sensor exposed as a shared resource to the browser, with access gated by user-granted permissions. There are multiple approaches to test webs apps that scan barcodes from the camera: * Test the barcode scanning workflow end-to-end using a fake camera stream. This is the most consequent approach but may be difficult to set up depending on your environment. * Inject or simulate a successfully scanned barcode: this typically requires dedicated code in your app to support running under test. * Simulating manual entry of the barcode value by the user. This aligns with our recommendation to always provide [manual entry](best-practices-for-scanning-apps.html#adding-manual-entry-capability) as a fallback in case barcode scanning is not possible. ## Injecting a Fake Camera Stream using Playwright We recommend using [Playwright](https://playwright.dev) for automated end-to-end testing of web applications. Playwright can be configured to start Chrome using special launch arguments that provide a fake video stream to the browser. For more information on this approach, please refer to the article [Automated Testing of Barcode Scanning Apps with Playwright](https://strich.io/blog/posts/automated-testing-of-barcode-scanning-apps-with-playwright/) on our blog. Example: End-to-end testing of an ISBN Barcode Scan The snippet below contains a Playwright script that tests scanning an ISBN barcode with our [Demo App](https://demo.strich.io). ```TYPESCRIPT import {test, expect, chromium} from '@playwright/test'; test('Scans EAN/UPC Book Barcode', async () => { const browser = await chromium.launch({ args: [ "--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--use-file-for-fake-video-capture=./test-videos/book_barcode.mjpeg", ] }); const context = await browser.newContext({}); const page = await context.newPage(); // check start page is displayed await page.goto('https://demo.strich.io/'); // select 1D retail barcodes and start scanning await page.getByText("1D Retail").click(); await page.getByText("START SCANNING").click(); // fake video stream starts, barcode should be read await expect(page.getByText('9781292223049')).toBeVisible(); // clean up await context.close(); }); ``` As the SDK considers the resolution of the video, the size of the host element and the region of interest defined (if specified), you will get the best results if the video file resolution has the same or similar aspect ratio as the browser viewport. You can set a [device](https://playwright.dev/docs/emulation#devices) from a list of built-in devices that Playwright supports on the test, or set a specific [viewport](https://playwright.dev/docs/emulation#viewport) directly. In most cases, adding a mobile browser project to your `playwright.config.ts` file should be enough. Example: Mobile Browser Project in Playwright Configuration ```JSON export default defineConfig({ testDir: './tests', // ... other properties omitted... projects: [ { name: 'Mobile Chrome', // use mobile viewport use: {...devices['Pixel 7']}, } ] }); `` ``` # Reading GS1 Data ## Application Identifiers GS1 is a global organization that sets standards for efficient and accurate supply chain management, with its most notable contribution being the development of the barcode. Founded in 1974, GS1's mission is to enhance the flow of goods and information across various industries by providing a universal system for product identification and data sharing. A key element of GS1's standards is the use of [Application Identifiers](https://ref.gs1.org/ai/) (AIs) in barcodes. These identifiers allow specific data, such as batch numbers, expiration dates, and serial numbers, to be encoded within a barcode. This capability enables precise tracking and traceability of products throughout the supply chain, improving efficiency and ensuring product safety. Example: GS1 UDI encoded in a Data Matrix code The Data Matrix code below encodes a GS1 [Unique Device Identification](https://www.gs1.org/industries/healthcare/udi) (UDI) with multiple AIs. ![Data Matrix code encoding a GS1 UDI](images/sample_gs1_udi_data_matrix.jpg) The symbology identifier is `]d2`, identifying it as a GS1 Data Matrix code. The textual data contained in this code is `0108806388269617112302141728021310230214A3263-0121059240ARO4808C`. The ASCII group separator is a non-printable character used to delimit variable-length AIs and represented as `` here. The AIs encoded in this Data Matrix barcode are shown below. | Numeric AI |Description |Length |Data | ----------------------------------------- | 01 |GTIN |14 |08806388269617 | | 10 |Lot number |variable |10230214A3263-01 | | 11 |Production date |6 |230214 | | 17 |Expiration date |6 |280213 | | 21 |Serial number |variable |059 | | 240 |Manufacturer-specific ID |variable |ARO4808C | Note: The table is sorted by numeric AIs, with smaller ones on top. In the barcode payload, the AIs do not necessarily have to appear in this order. To learn more about the available GS1 Application Identifiers, visit the GS1 [Application Identifiers website](https://ref.gs1.org/ai/). ## Reading AIs from Scanned Barcodes Currently STRICH does not expose the AIs in a structured way. Instead, when a GS1 barcode is read, the appropriate symbology identifier is included in the detection, along with the barcode payload, to help distinguish it from a regular barcode. We might choose to provide this feature directly in our library, given enough interest from customers. In the meantime, we recommend you use freely available libraries such as [BarcodeParser](https://github.com/PeterBrockfeld/BarcodeParser) or the [GS1 Digital Link Toolkit](https://github.com/gs1/GS1DigitalLinkToolkit.js) to extract the AIs from the GS1 element string. In the following sections, we will show how to use both tools to achieve to parse the GS1 AIs into a human-readable interpretation. The full sample code is available in the [gs1-ai-parsing Github repository](https://github.com/pixelverse-llc/gs1-ai-parsing). ### Using the BarcodeParser Library to Extract GS1 AIs #### Loading the BarcodeParser Library Include the BarcodeParser library by loading it via a script tag from a CDN. the library is not available on NPM. Here we include the minified version via the jsDeliver CDN. ```HTML ``` ### Parsing the Barcode using parseBarcode The barcode data is parsed using the aptly named `parseBarcode` function. The function returns an object containing an array of parsed AIs. In the code snippet below, we assemble it into newline-separated human-readable interpretation, with each line containing the AI, the AI description, and the AI data (e.g. `10 (BATCH/LOT): 230214A3263-01`). ```JAVASCRIPT const answer = parseBarcode(data); let hri = ''; for (let item of answer.parsedCodeItems) { hri += `${item.ai} (${item.dataTitle}): ${item.data}`; hri += '\n'; } outputElem.innerText = hri; ``` ### Using the GS1 Digital Link Toolkit to Extract GS1 AIs #### Loading the GS1DigitalLinkToolkit.js Library Include the GS1DigitalLinkToolkit.js library by bundling it or by loading it via a script from a CDN. The library is not available on NPM. Here we load the latest version in the GitHub repository via the jsDeliver CDN. ```HTML ``` ### Parsing the Barcode using extractFromGS1elementStrings Instantiate the `GS1DigitalLinkToolkit` class and call the `extractFromGS1elementStrings` method, passing the barcode data. ```JAVASCRIPT const dlt = new GS1DigitalLinkToolkit(); const answer = dlt.extractFromGS1elementStrings(data); ``` The result of the method is a dictionary containing the AIs and the associated data. For the sample input `0108806388269617112302141728021310230214A3263-0121059240ARO4808C`, the output is the following dictionary: ```JSON { "10": "230214A3263-01", "11":"230214", "17":"280213", "21":"059", "240":"ARO4808C", "01":"08806388269617" } ``` # Customizing the Scanner UI ## Overview of the Barcode Scanning UI The screenshot below illustrates a full-screen barcode scanning UI. The area supplied by the host element is filled with the camera preview. Everything displayed on top of the camera preview is called the overlay. The region of interest, where barcodes are detected, is delimited by a rectangular frame, called the viewfinder. ![Overview of UI customization options](images/ui_customization_overview.png) ## Styling Options The viewfinder can be styled in the following ways: * Primary color: The color of the viewfinder rectangle and UI elements like the camera selector is set using the [primaryColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#primaryColor) property in the overlay configuration. * Corner radius: The corner radius of the viewfinder rectangle and the camera selector is set using the [cornerRadius](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#cornerRadius) property in the overlay configuration. * Targeting line visibility: visibility of the targeting line can be toggled using the [showTargetingLine](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#showTargetingLine) property. * Targeting line active color: when a barcode is detected, the targeting line flashes briefly to a bright red. The color can be changed with the [targetingLineActiveColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#targetingLineActiveColor) property. * Viewfinder border width: The border width of the viewfinder rectangle, using the [viewfinderBorderWidth](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#viewfinderBorderWidth) property. * Viewfinder corner radius: The corner radius of the viewfinder rectangle, using the [viewfinderCornerRadius](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#viewfinderCornerRadius) property. * Mask color: The color of the area outside of the viewfinder, using the [maskColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#maskColor) property. When a barcode is detected, and highlighting of detections is enabled (using the [showDetections](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#showDetections) property), a rectangle is drawn at its location. Barcode detections can be styled in the following ways: * Fill color: The color used to fill the rectangle can be changed using the [detectionFillColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#showDetections) property. * Border color: The color of the border surrounding the rectangle can be changed using the [detectionBorderColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#detectionBorderColor) property. * Border width: The width of the border surrounding the rectangle can be changed using the [detectionBorderWidth](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#detectionBorderWidth) property. Tip: Colors are set using the CSS [rgb notation](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb). Using hexadecimal notation (e.g. `#FF0000`) is not supported. ### Example 1: Yellow primary color with rounded corners The following configuration styles the UI using a yellow colors, applies rounded corners and sets the targeting line to be bright yellow in case of a detection. ![Example of yellow UI customization](images/ui_customization_yellow.png) Overlay configuration snippet ```JAVASCRIPT const config = { overlay: { primaryColor: 'rgb(239,253,95)', // Lemon detectionFillColor: 'rgb(249,166,2)', // Gold detectionBorderColor: 'rgb(255,255,0)', // Bright yellow detectionBorderWidth: 2, targetingLineActiveColor: 'rgb(255,255,0)', // Bright yellow cornerRadius: 4 // rounded } }; ``` ### Example 2: Using a mask to emphasize the region of interest The following configuration styles the UI using a near-white color and emphasizes the region of interest using a dark, semi-transparent mask color and a large border width for the viewfinder rectangle. ![Example of mask UI customization](images/ui_customization_mask.png) Overlay configuration snippet ```JAVASCRIPT const config = { overlay: { primaryColor: 'rgb(236,237,237)', // Decorator's White maskColor: 'rgba(0,0,0,0.5)', viewfinderBorderWidth: 5, viewfinderCornerRadius: 20 } }; ``` ## Hiding or Replacing the STRICH Logo Hiding or replacing the STRICH logo is an add-on capability available for Enterprise licenses. License keys that include the Custom Branding capability can be used to customize the logo appearance. Tip: Please note that hiding or replacing the STRICH logo by other means is prohibited and constitutes a violation of the license terms. * Hiding the logo: The built-in STRICH logo can be hidden by setting the [customLogoSrc](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#customLogoSrc) property to `null`. * Replacing the logo: A custom logo can be used by setting [customLogoSrc](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#customLogoSrc) property to a data URL of the logo image, or an absolute URL pointing to the logo image. Tip: By default, custom logos are rendered at their intrinsic size (image pixels = CSS pixels). This may result in a blurred image on high-resolution screens. To prevent this from happening, use a high-resolution image and set the [customLogoSize](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#customLogoSize) property to a smaller size (e.g. supply a 200x40px image and set the size to 100x20 CSS pixels). We recommend using a transparent SVG vector for the logo image. # Customizing the Scanner Sound Sometimes it is desirable to customize the sound played when a barcode is scanned, depending on business logic. Currently STRICH itself includes only a standard "cash register" beep sound, which can be toggled on or off via the [audio](https://docs.strich.io/reference/interfaces/FeedbackConfiguration.html#audio) property of the feedback configuration. ## Use Cases for Playing Custom Sounds Some examples of scanning scenarios where a custom sound is desirable: * Valid/expired tickets: a positive sound is played if the QR code contains a valid ticket, a negative one if it's expired or not in the expected format. * Format issues: a negative sound is played if the scanned barcode is in the wrong format, has an incorrect length, contains unexpected data. * Network errors: the scanned barcode could not be looked up, play a sound to indicate that the user should try again. As STRICH is only concerned with scanning barcodes, it cannot know if a barcode is valid in the context of its host app. An app-specific, asynchronous operation (e.g. an HTTP request) is often required to determine its validity, which introduces an unpredictable delay. ### Playing Custom Sounds with Howler.js The recommended approach to playing custom sounds is using a library like [Howler.js](https://howlerjs.com/) to play the sound in the [detected](https://docs.strich.io/reference/classes/BarcodeReader.html#detected) callback, and disabling the default beep by setting the [audio](https://docs.strich.io/reference/interfaces/FeedbackConfiguration.html#audio) to `false`. Tip: A benefit of this approach is that sound playback can be more reliable. Robust sound playback in the browser is unfortunately non-trivial, and we do not consider it a core competence of STRICH. Libraries like Howler.js are dedicated to the task of playing sound reliably across browsers. Sample Code In this sample, a positive sound is played if the scanned QR code contains a valid URL, and a negative sound is played otherwise. The sounds are preloaded before the scanner is started, so that they are ready to play immediately when a code is scanned. ```JAVASCRIPT // import Howler.js for playing sounds import { Howl } from 'https://cdn.jsdelivr.net/npm/howler@2.2.4/+esm'; // prepare Howler.js sounds const sounds = { positive: new Howl({ src: ['positive.mp3'], preload: true }), negative: new Howl({ src: ['negative.mp3'], preload: true }) } /** Play a positive sound if QR Code contains a valid URL, negative otherwise */ function processDetection(detection) { try { const parsedUrl = new URL(detection.data); console.log(`QR code contains URL: ${parsedUrl}`) sounds.positive.play(); } catch (e) { // TypeError thrown if URL is not OK sounds.negative.play(); } } try { await StrichSDK.initialize(``); const cfg = { selector: '.barcode-reader', engine: { symbologies: ['qr'], }, feedback: { audio: false // disable STRICH's default beep } }; let barcodeReader = new BarcodeReader(cfg); barcodeReader.detected = (detections) => processDetection(detections[0]); await barcodeReader.initialize(); await barcodeReader.start(); } catch (e) { window.alert(e.message); } ``` # Non-Modular Build Flavor STRICH is distributed as an ES6 module, to be imported into your app via an [import statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#importing_features_into_your_script). If you are running in an environment where you can not use ES6 modules, you can alternatively include the library via a ` ``` The non-modular builds registers the classes `StrichSDK`, `BarcodeReader` and `SdkError` under a global `strich` object, so instead of accessing importing `StrichSDK` and accessing it directly, you need to access it through the global: ```JAVASCRIPT // non-modular build, objects published under global 'strich' await strich.StrichSDK.initialize(...) ``` # API Reference The API Reference is located [here](https://docs.strich.io/reference/index.html). # Sample Code We aim to provide consistent, clean and idiomatic sample code for the most popular frontend frameworks. If you have feedback regarding our sample code (we are by no means expert in every framework!), feel free to send us your comments to [hello@pixelverse.ch](mailto:hello@pixelverse.ch) or open a pull request in the Github repository. ## Structure of Sample App Most of our code samples show the following three usage scenarios: Single Scan – Scan a single barcode and return to the previous screen. Illustrates basic SDK usage such as SDK and BarcodeReader initialization and lifecycle management. [Video](images/sample_single_scan.mp4) Repeated Scans – Scan barcodes repeatedly with an intermediate user action between scans until an application-specific criterion is met. Illustrates using `start()` and `stop()` to control the `BarcodeReader`, [Video](images/sample_repeated_scans.mp4) Continuous Scans - Scan barcodes continuously until an application-specific criterion is met (e.g. all N barcodes on a label are scanned). [Video](images/sample_continuous_scans.mp4) ## Code Samples by Framework Vanilla JS : An example of how to use STRICH without any web framework at all, using just plain ES6. [Github repository](https://github.com/pixelverse-llc/strich-javascript-sample) Angular : Sample integration into an Angular web app. Uses standalone components. [Github repository](https://github.com/pixelverse-llc/strich-angular-sample) SvelteKit : Sample integration into a SvelteKit web app. Uses SvelteKit 2.x. [Github repository](https://github.com/pixelverse-llc/strich-sveltekit-sample) React : Sample code showing how to integrate STRICH into a React project. Uses React functional components (React 16.8+). [Github repository](https://github.com/pixelverse-llc/strich-react-sample) Vue 3 : Sample code showing how to integrate STRICH into a Vue 3.x project. [Github repository](https://github.com/pixelverse-llc/strich-vue3-sample) iOS (Swift/Swift UI) : Sample code for integrating a web app that uses STRICH into a native iOS app using a WebView. [Github repository](https://github.com/pixelverse-llc/strich-ios-sample) Vaadin Flow (WIP) : Sample code showing how to integrate STRICH into a Vaadin Flow project. [Github repository](https://github.com/pixelverse-llc/strich-vaadin-sample) Vue 2 (deprecated) : Sample code showing how to integrate STRICH into a Vue 2.x project. This sample is no longer maintained. [Github repository](https://github.com/pixelverse-llc/strich-vue2-sample) ## Contributing Are you interested in contributing to our samples or even providing a sample of your own that we can link to? Drop us a line at [hello@pixelverse.ch](mailto:hello@pixelverse.ch). Here's what we value in submissions: Clean : Sample code should not contain extra files or dependencies that are not required to run the sample or dilute the sample's purpose. Minimal : Sample code should not contain business logic or styling and focus solely on idiomatic integration into the target environment. Self-contained : Sample code should not require extra dependencies apart from the target environment itself, e.g. an Angular sample should depend only on Angular, and not on additional libraries such as Angular Material. Documented : A short README containing a description of the sample and pointing out the salient bits is a must, as well as specifying a license for the code. ## Licensing Sample code is provided as-is under the [CC0 license](https://creativecommons.org/publicdomain/zero/1.0/deed.en). The code is for illustrative purposes only and may not be suitable for production. ## External Contributions Note: The following code samples were written by outside contributors who are not affiliated with Pixelverse GmbH. We are not responsible for their contents, and can not be held liable for any damages resulting in their use. Blazor : Sample code showing how to integrate STRICH into a Blazor project. [Github repository](https://github.com/skjwang/Blazor-Strich) : Contributed by: Shaokang Wang and Pierre Mertz, Infinera # Tutorials A collection of more in-depth, hands-on advice on building scanning apps with STRICH. # Creating a JavaScript Barcode Scanning App From Scratch In this tutorial, we will build an app that uses barcode scanning from scratch. The aim is not to build a production-ready, real-world app, but instead illustrate how common scanning workflows can be implemented using the SDK's capabilities and plain JavaScript/HTML/CSS. The app is deployed under [https://pixelverse-llc.github.io/inventory-tracking-app/](https://pixelverse-llc.github.io/inventory-tracking-app/) and full source is available on [GitHub](https://github.com/pixelverse-llc/inventory-tracking-app). ## Inventory Tracking The app that we'll build will do inventory tracking. The user signs into to the app and can borrow items (e.g. a laptop) from a storage locker, and return them later. Tracking of physical items is a task that is often done using barcode scanning. ## Tech Stack In frontend development, there are always a lot of different ways to achieve the same thing for any given task. For this tutorial, we have chosen a deliberately minimal approach that should be easily adaptable to your framework of choice. ### Vanilla JS/HTML/CSS (No Framework) We are going to implement the app without using any frontend frameworks or libraries, relying only on the baseline capabilities available in modern web browsers – JavaScript, HTML and CSS. This might seem like an odd choice, but STRICH is framework-agnostic and aims to work well in any web environment. We encourage you to try building an app with just the core Web primitives – it's a refreshing experience. ### No Styling We will not use any styling beyond basic layout and making the buttons touch-friendly. We will make it functional and have acceptable UX. The app will not look beautiful. Tip: In a real-world app, you would likely use a CSS framework like Tailwind CSS, Bootstrap or UIkit. Feel free to add some style to the app if you want! ### No Backend - localStorage Only To keep things simple, the app will not use a backend and instead use [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to store the currently logged-in user and borrowed inventory. Tip: In a real-world app, users would be authenticated using a scheme like OAuth and the state of the inventory would be stored in a database such as PostgreSQL. ### No Build Steps, No NPM No build steps like bundling and minification involved. The HTML, JS and CSS that we create can be opened as-is. Tip: In a real-world app you would use a dependency manager like NPM and these steps would be performed by tools like Vite, Webpack and others. ## How the App uses Barcode Scanning The app will use barcode scanning for two purposes: user identification and item scanning. ### Scanning Badge QR Code The app will require an user to scan a QR Code printed on a hypothetical badge before items can be borrowed. We'll assume the QR Code contains a string of digits, like the one below. ![Employee badge with QR Code](images/inventory_badge.webp) ### Scanning Inventory Items Items are borrowed and returned by scanning the 1D barcode printed on them. We assume that items are labelled using Code 128, Code 39 or EAN/UPC product barcodes. Here's a screen recording of a user scanning three PlayStation games in quick succession: [Video](images/inventory_scanning.mp4) ## Project Setup ### Git Repository We will store the app in a Git repository and use GitHub Pages to deploy it under a publicly reachable, HTTPS-enabled URL. We'll assume that you are already familiar with Git, the de-facto standard for version control. If you haven't used Git before, we recommend GitHub's [Getting Started](https://docs.github.com/en/get-started/learning-to-code/getting-started-with-git) documentation. Tip: We decided to use GitHub Pages because it's popular and free, and if you're reading this, you probably already have a GitHub account. There is no reason why you couldn't also use something else, e.g. Cloudflare Pages, Netlify, or self-hosting. GitHub Pages automatically provides your site with a valid SSL certificate, which is required for camera access in the browser outside of localhost, but other services do this as well. Create a new Git repository on GitHub. Then, in your repository settings, make sure GitHub Pages is enabled, and choose `main` as the branch and `/ (root)` as the repository path from where to deploy your web app. Your settings should look similar to the screenshot below. ![Screenshot of Github Pages configuration](images/inventory_gh_pages.png) Clone your new Git repository to your local machine using the command-line, or a Git client of your choice, using the URL shown in GitHub. If you want to work directly with the final repository, run the following command: ```SHELL git clone git@github.com:pixelverse-llc/inventory-tracking-app.git ``` ### Directory Structure Our app consists of the following HTML, CSS and JavaScript files. | Path |Contents | ------------------ | /index.html |Login and inventory screen | | /scan.html |Item scanning screen | | /css/styles.css |Global stylesheet (shared) | | /lib/authentication.js |User authentication module (shared) | | /lib/inventory.js |Inventory management module (shared) | | /img/badge_qr.webp |Badge image with QR Code | ### Hello, World on GitHub Pages Get started by adding an `index.html` file to the folder where you checked out your repository. Edit it and add a minimal Hello, world! document: ```HTML

Hello, world!

``` Add the `index.html` file to Git, commit it and push it to the `main` branch. ```SHELL git add index.html git commit -m "initial commit" index.html git push ``` If everything is set up properly, you should be able to access your new GitHub Pages site via the URL https://USERNAME.github.io/REPOSITORY/ (URL will also be shown in the settings) after a short wait, and be greeted with your shiny new app: ![Screenshot of dummy Hello, World website](images/inventory_hello_world.png) Not terribly impressive, but you have a working GitHub Pages setup now! Feel free to play around with it until you're ready for the next step. ### Optional: Run a Web Server Locally For development purposes, it is useful to run a web server that serves the contents of the repository on your local machine. If you have Python installed (it comes pre-installed on macOS), you can launch a simple HTTP server with the following command: ```SHELL python3 -m http.server ``` You can then open your web app by opening the URL http://localhost:8000 in your web browser. Tip: Running a local web server is easy, but setting up a valid SSL certificate is usually not straightforward. Outside of localhost, browsers require an HTTPS connection for accessing the smartphone's camera. Tools like [ngrok](https://ngrok.com) and [alternatives](https://github.com/anderspitman/awesome-tunneling) make this easy, and we recommend using them for development and testing on smartphones. ### Obtaining a STRICH SDK License Key Next, you will need to get a license key for the STRICH SDK. Create an account in the [Customer Portal](https://portal.strich.io/register/) and start the free trial. A credit card or PayPal will be required, but it will not be charged during the trial. Then create a license key, and when asked for a URL, use your GitHub Pages URL. ![Screenshot of Customer Portal license key](images/inventory_license_key.png) ## Building the Login Screen Now that we have an SDK license key, we can start scanning some codes! The first thing our users will see is a login screen. It will consist of a header, an image of a badge with a QR Code printed on it, and a button to scan the QR Code: ![Screenshot of login screen](images/inventory_login.jpg) Adding a button to start scanning instead of starting it directly when the page has loaded is recommended, because it allows you to inform the user that access to the camera will be required, and for what purpose. Initially the user will have to grant permission to access the camera to the app, and if the reason is not clear and the prompt appears seemingly out of the blue, it will likely be denied. ### Login Screen HTML In the HTML for the login screen, we set a [mobile viewport](https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Viewport_meta_element#viewport_basics), include a CSS stylesheet (we'll create it later) and display the screen contents in a centered Flex container which will contain the header, image and button. Update your `index.html` to match the snippet below. ```HTML Inventory Tracking App
``` ### Login Screen Stylesheet (CSS) To style our app, we will use a CSS stylesheet and store it under `css/styles.css`. The CSS does a subset of what most [CSS Resets](https://en.wikipedia.org/wiki/Reset_style_sheet) do: use the more intuitive `border-box` box-sizing model, remove margins, and make images responsive. We also make buttons more touch-friendly by making them full-width and increasing their height to 48px, the recommended size for mobile-friendly buttons. The content of our screens will live inside a `main` element, which we will center in the screen using Flex layout. ```CSS /* see: https://css-tricks.com/box-sizing/#aa-universal-box-sizing */ *, *:before, *:after { box-sizing: border-box; } /* remove body margin */ body { margin: 0; } /* responsive images */ .img-responsive { max-width: 100%; height: auto; } /* make buttons wide and touch-friendly */ button { min-height: 48px; width: 100%; } /* centered content container */ main { max-width: 640px; padding: 20px; display: flex; flex-direction: column; height: 100dvh; justify-content: center; margin: auto; } ``` For the full stylesheet, please check out the [styles.css](https://github.com/pixelverse-llc/inventory-tracking-app/blob/main/css/styles.css) in the GitHub repository. ### Login Screen Logic (JavaScript) To fill the screen with some life, we need to add some JavaScript that: * [Imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) the latest version of the STRICH SDK as an ES6 module from the jsDeliver CDN. * Checks if the device has a camera using the [hasCameraDevice](https://docs.strich.io/reference/classes/StrichSDK.html#hasCameraDevice) method, and if not, displays an error message to the user. * Initializes the STRICH SDK using the license key we obtained previously. * Enables the SCAN QR CODE button, and makes it open a [PopupScanner](https://docs.strich.io/the-popup-scanner.html) instance configured to read QR Codes. We don't do anything with scanned code yet. Tip: For simplicity, the JavaScript is included directly in the `index.html` file. In a real-world app, you might prefer keeping it in a separate file (e.g. `index.js`). Populate the empty script tag in `index.html` with the following JavaScript, and substitute the placeholder with your license key. ```JAVASCRIPT import { StrichSDK, PopupScanner } from "https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@latest"; /** * Attempt to scan a QR Code using the PopupScanner. * * See: https://docs.strich.io/the-popup-scanner.html */ async function scanQRCode() { const qrCodes = await PopupScanner.scan({ symbologies: ['qr'], labels: { title: 'Scan Badge' } }); // if a QR Code was scanned, use its encoded value as the user, and update the UI if (qrCodes) { // log in... } } async function updateUI() { // set up the QR Code login screen, but check if the browser has access to a camera const hasCamera = await StrichSDK.hasCameraDevice(); if (hasCamera) { const scanQrButton = document.getElementById('scan-qr-button'); try { const licenseKey = ''; await StrichSDK.initialize(licenseKey); scanQrButton.disabled = false; scanQrButton.onclick = scanQRCode; } catch (err) { // handle SDK error: https://docs.strich.io/reference/classes/SdkError.html alert(`Failed to initialize STRICH SDK, your license key may have expired: ${err.message}`); } } else { alert(`Sorry, you need a camera to scan barcodes`); } } // initial UI update updateUI(); ``` Committing and pushing the updated `index.html` file will cause a new version of your GitHub Pages site to be deployed. ![Screenshot of successful login page deployment on GitHub Pages](images/inventory_page_deployed.png) You should now be able to see your new login page and trigger a QR Code scan. Try it out! ### The Authentication Module Our app uses the data in the badge QR Code to log the user into the app. Since we do not have a real backend against which we could authenticate the user, we will simply treat the data encoded in the QR Code as the username, and store it in [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage). We will encapsulate the login functionality in an [ES6 module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) that can be shared between all pages that require access to the login information. lib/authentication.js: Utility functions for storing the currently logged-in user The `lib/authentication.js` module provides access to the name of the logged-in user, and functions for setting the logged-in user, and logging out. ```JAVASCRIPT // the key to use to store a logged-in user in localStorage const usernameKey = 'username'; // the currently logged-in username, or null, if the user is logged out export function getUsername() { return localStorage.getItem(usernameKey); } export function logOut() { localStorage.removeItem(usernameKey); } export function logIn(username) { localStorage.setItem(usernameKey, username); } ``` To load the module in the login screen, import it along-side the SDK. ```JAVASCRIPT import { StrichSDK, PopupScanner } from "https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@latest"; import { logIn } from './lib/authentication.js'; async function scanQRCode() { // ... PopupScanner ... // if a QR Code was scanned, use its value as the user, and update the UI if (qrCodes) { logIn(qrCodes[0].data); await updateUI(); } } ``` ## Building the Inventory Screen After the user has logged in, we want to display the main screen: the list of borrowed inventory, and buttons for the primary actions. The screen consists of a title, a list of items (barcode and count) and buttons to borrow or lend items. At the bottom, the username of the logged-in user is displayed, along with a button for logging out. ![Screenshot of inventory screen](images/inventory_items.jpg) ### Inventory Screen Layout (HTML) Add the following snippet as a child of the `
` element. The view is initially hidden, and will be shown only if the user is logged in. ```HTML ``` The list items will be generated dynamically. We use an HTML [template](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/template) element for the list item template. ### Inventory Screen Logic (JavaScript) To switch between the logged-out and logged-in views, we have to extend the previous `updateUI()` function. Depending on the authentication state, it will either make the `logged-in` view or the `logged-out` view visible. If the user is logged in, the borrowed inventory is loaded from a module that exposes a `loadItems()` function. The returned items are used to populate the list using the dynamically instantiated item template. If the borrowed inventory is empty, a placeholder is made visible instead. We hook up the Borrow Items and Return Items buttons and make them navigate to a scan screen, passing a query parameter containing the mode (`borrow` or `return`). We disable the Return Items button if the inventory is empty. At the bottom, we display the logged-in username and a button that logs the user out, clearing the inventory. ```JAVASCRIPT import { loadItems, clearItems } from './lib/inventory.js'; /** * Update the UI depending on the current application state (authenticated: yes/no, inventory items) */ async function updateUI() { const isLoggedIn = getUsername() !== null; // show logged-in or logged-out view depending on authentication state document.getElementById('logged-out').style.display = isLoggedIn ? 'none' : 'block'; document.getElementById('logged-in').style.display = isLoggedIn ? 'block' : 'none'; if (isLoggedIn) { // display stored items in a list const items = loadItems(); // show placeholder if list is empty document.getElementById('no-items-placeholder').hidden = items.length > 0; // populate list from item template const listElement = document.getElementById('item-list'); document.getElementById('item-list').hidden = items.length === 0; for (const item of items) { const itemElement = document.getElementById('item-template').content.cloneNode(true); itemElement.querySelector('.item-barcode').innerHTML = item.barcode; itemElement.querySelector('.item-count').innerHTML = `${item.count}x`; listElement.appendChild(itemElement); } // if we don't have any items, disable the return items button if (items.length === 0) { document.getElementById('return-items').disabled = true; } // borrow items/return items buttons navigate to scan page document.getElementById('borrow-items').onclick = () => document.location.href = 'scan.html?mode=borrow'; document.getElementById('return-items').onclick = () => document.location.href = 'scan.html?mode=return'; // show logged-in user and logout button, clears inventory document.getElementById('username').innerHTML = getUsername(); document.getElementById('log-out').onclick = () => { logOut(); clearItems(); updateUI(); }; } else { // logged out logic (see previous sections) } } ``` Tip: The DOM manipulation might feel convoluted if you're used to frontend frameworks that allow you to bind elements directly to data and control logic. Check out the [index.html](https://github.com/pixelverse-llc/inventory-tracking-app/blob/main/index.html) file in the GitHub repository for the full code. ### The Inventory Module Where is `loadItems()` coming from? Similar to the authentication module, we create a module that encapsulates all interactions with the inventory: * Getting the list of borrowed items * Adding/removing an item * Clearing the list items Since we are not using a backend, we will again use localStorage to store the borrowed items locally. For every item, a barcode and count are stored. Adding an item that already exists increments the existing item's count. Removing an item decrements the count. If the count reaches 0, the item is removed from the list. We use JSON to convert the array of items to a string and vice versa, suitable for storing it in localStorage. ```JAVASCRIPT // Expose a list of items from localStorage const itemsKey = 'items'; export function clearItems() { localStorage.removeItem(itemsKey); } export function loadItems() { const itemsStr = localStorage.getItem(itemsKey); return itemsStr === null ? [] : JSON.parse(itemsStr); } export function storeItems(items) { localStorage.setItem(itemsKey, JSON.stringify(items)); } export function addItem(item) { const myItems = loadItems(); const existingItemIdx = myItems.findIndex(it => it.barcode === item.barcode); if (existingItemIdx === -1) { myItems.push(item); } else { myItems[existingItemIdx].count += item.count; } storeItems(myItems); } export function removeItem(item) { const myItems = loadItems(); const existingItemIdx = myItems.findIndex(it => it.barcode === item.barcode); if (existingItemIdx === -1) { myItems.splice(existingItemIdx, 1); } else { myItems[existingItemIdx].count -= item.count; if (myItems[existingItemIdx].count <= 0) { myItems.splice(existingItemIdx, 1); } } storeItems(myItems); } ``` Save the module's code in a file called `lib/inventory.js`. ## Building the Scanning Screen We've built the main screen, but our inventory is empty. The scanning screen is where we finally scan items and add them to the inventory. The scanning screen will have two modes: borrowing items and returning items. Depending on the mode, the scanning screen will display a different title, and interact with the inventory module differently. The barcode reader will be displayed on top, a label showing the number of items scanned and a Finish Scanning button below. ![Screenshot of the scanning screen](images/inventory_scan.jpg) The screen will allow scanning multiple items in one go, making the [Popup Scanner](https://docs.strich.io/the-popup-scanner.html) less suitable. Instead, we will use [BarcodeReader](https://docs.strich.io/getting-started.html#choosing-the-integration), a more customizable integration. Check out the [Getting Started Guide](https://docs.strich.io/getting-started.html#choosing-the-integration) for more information about the different ways to integrate STRICH into your app. ### Scanning Screen Layout (HTML) Copy the `index.html` file and rename it to `scan.html`. Replace the `
` element with the snippet below. ```HTML

No items scanned yet

``` ### Scanning Screen Logic (JavaScript) The mode is passed to the screen via a query parameter, e.g. `scan.html?mode=borrow`. Scanned items are accumulated in the `onItemScanned()` function and stored in the `scannedItems` variable. Compared to scanning the badge QR Code, setting up the barcode scanning code here is a bit more complex. A [BarcodeReader](https://docs.strich.io/reference/classes/BarcodeReader.html) is created by invoking its constructor with a [Configuration](https://docs.strich.io/reference/interfaces/Configuration.html) object. The configuration contains the list of barcode types (symbologies) to detect, as well as a parameter that suppresses duplicate scans, and most importantly, the [host element](https://docs.strich.io/reference/interfaces/Configuration.html#selector). The host element is the HTML element that will contain the visible elements of the BarcodeReader. It needs to have a fixed size and use [relative positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/position#relative). When a barcode is detected, the BarcodeReader is instructed will invoke the `onItemScanned()` callback, which accumulates the item in the `scannedItems` variable. After we've configured the BarcodeReader, we initialize and start barcode detection. When scanning is finished, we [destroy](https://docs.strich.io/reference/classes/BarcodeReader.html#destroy) the BarcodeReader and navigate back to the main screen. Add the following JavaScript snippet to the ` ``` We’ll keep the styling to a minimum. The linked CSS makes the success dialog have a green background, the failure dialog have a red one, and makes the OK button “fat finger compatible” (full-width with added padding). We’re not going to win beauty prizes and that's ok. ## Building the code In a nutshell, the JavaScript code does the following: SDK and BarcodeReader initialization — initialize the [SDK](https://docs.strich.io/reference/classes/StrichSDK.html) for use and provide it with a valid license key. Initialize a [BarcodeReader](https://docs.strich.io/reference/classes/BarcodeReader.html) instance and configure it to look for PDF417 barcodes and use the container as its host element. Barcode data processing — provide the [detected](https://docs.strich.io/reference/classes/BarcodeReader.html#detected) hook that parses the PDF417 data, executes the age check, and displays the success or failure dialog. AAMVA data extraction — regular expression-based parsing of date of birth, last name and first name fields from raw PDF417 data. For details, see [aamva.js](https://github.com/pixelverse-llc/strich-pdf417-sample/blob/main/aamva.js). In a real application, you would be doing a lot more validation and supporting of older AAMVA specification versions. Here’s the code: ```JAVASCRIPT // import STRICH SDK as an ES6 module directly from a CDN import {StrichSDK, BarcodeReader} from 'https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@1.7.1 /dist/strich.js'; // AAMVA helper routines import {parseAAMVALicenseData} from "./aamva.js"; function processPDF417Barcode(codeDetection) { // attempt to parse barcode data as AAMVA driver's license const parsed = parseAAMVALicenseData(codeDetection.data); if (!parsed) { console.error('PDF417 data could not be parsed according to AAMVA spec'); return; } // calculate age from system time const age = new Date().getFullYear() - parsed.dateOfBirth.getFullYear(); // depending on age, show success or reject popup const dialog = document.getElementById(age < 21 ? 'failure' : 'success'); dialog.getElementsByClassName('popup-name')[0].innerText = parsed.firstName + ' ' + parsed.lastName; dialog.getElementsByClassName('popup-age')[0].innerText = age + ' yrs old'; dialog.showModal(); } // Initialize BarcodeReader with appropriate settings for PDF417 function initializeBarcodeReader() { let configuration = { selector: '.container', engine: { symbologies: ['pdf417'] }, locator: { regionOfInterest: { // PDF417 is typically a wide rectangle, size the ROI appropriately left: 0.05, right: 0.05, top: 0.3, bottom: 0.3 } }, frameSource: { // high resolution recommended for PDF417 resolution: 'full-hd' }, }; return new BarcodeReader(configuration).initialize() .then(barcodeReader => { // store the BarcodeReader in a global, to be able to access it later (e.g. to destroy it) window['barcodeReader'] = barcodeReader; barcodeReader.detected = (detections) => { processPDF417Barcode(detections[0]); }; return barcodeReader.start(); }); } // initialize STRICH SDK with a valid license key const licenseKey = ''; StrichSDK.initialize(licenseKey) .then(() => { console.log('SDK initialized successfully'); return initializeBarcodeReader(); }) .catch(err => { // See: https://docs.strich.io/reference/classes/SdkError.html window.alert(err.localizedMessage); }); ``` Typically STRICH SDK is installed via NPM: here we include the strich.js file as an ES6 module and obtain it from [jsDelivr](https://www.jsdelivr.com/), a popular CDN. ## Demo To run the app, all you have to do is serve the files in the sample directory using an HTTP server. There is no build step involved — it’s just plain HTML and JavaScript after all. If your system has Python installed, which is likely if you are a developer, you already have a [barebones](https://docs.python.org/3/library/http.server.html) HTTP server at your disposal: ```SHELL $ python -m http.server Serving HTTP on :: port 8000 (http://[::]:8000/) ... ::1 - - [07/Nov/2023 09:39:43] "GET / HTTP/1.1" 200 - ::1 - - [07/Nov/2023 09:39:44] "GET /index.js HTTP/1.1" 304 - ``` Unfortunately, web apps that access the camera need to be served from secure origins — a fancy word for “served over HTTPS or localhost”. We recommend using tools like [ngrok](https://ngrok.com/) or [devtunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) to easily expose a local HTTP service through a publicly resolvable hostname and valid TLS certificate. More details on that topic are available in the [Deployment Guide](deployment-guide.html). If you want to peak at the app in action but don’t want to set it up yourself, here’s a short clip: [Video](images/pdf417_dl_demo.mp4) ## Wrapping Up In this article we showed how the PDF417 barcode on a US driver's license can be used to build a simple age verification web app. Full sample code is available on [Github](https://github.com/pixelverse-llc/strich-pdf417-sample). # Release Notes ## 1.13.0 - 2026-01-14 Added * MSI Plessey is now a supported barcode symbology, enabled by adding `msi-plessey` symbology. Developed in the 1970s and rarely used these days, we consider it a legacy symbology – it has to be explicitly enabled. As it is not self-checking, we highly recommend configuring a restrictive `minLen` and `maxLen`. By default, a single Modulo 10 check digit is expected. Changed * Optimized WebGL processing by removing redundancies and unnecessary resource allocation, resulting in a slight decrease in runtime. * Updated internal development dependency updates. ## 1.12.2 - 2025-12-15 Added * The SDK's camera selection logic can now be customized by providing a camera selector callback through `BarcodeReader.cameraSelector`. The callback receives the list of available cameras and returns the camera that should be used, or `undefined`, in which case the default logic is used. Fixed * Slightly improved Data Matrix detection rate by using a more precise grid mapping. ## 1.12.1 - 2025-12-05 Changed * Rolled back binary-encoded WebAssembly to use Base64 encoding instead of UTF-8 due to Uncaught SyntaxError: octal escape sequences can't be used in untagged template literals or in strict mode code errors in some customer installations. ## 1.12.0 - 2025-12-04 Added * The Micro QR Code symbology (ISO/IEC 18004:2015) is now supported, it is enabled by passing the symbology name `microqr`. * Added a method to `BarcodeReader.setZoom()` to programmatically toggle the zoom, if the current device supports it. * Added a method to `BarcodeReader.setFlashlight()` to programmatically toggle the flashlight, if the current device supports it. Changed * The app-supplied detection handler `BarcodeReader.detected` can now return a boolean or array of booleans to indicate if the passed detections were handled or not. Unhandled detections are not drawn on the overlay, and no audio or vibration feedback is emitted. Existing detection handlers do not need to be changed, this change is backward compatible. * The encoding of the JavaScript artifacts is now UTF-8 instead of ASCII, allowing for a smaller distribution size due to more efficient encoding of the bundled WASM module. * Updated Emscripten compiler to 4.0.20. Fixed * An incorrect default camera was being selected on iPhones with three cameras when using a French locale. ## 1.11.0 - 2025-10-27 This package is now published using Trusted Publishing on NPM. Added * Added an overlay to zoom the camera stream. The control is shown if the browser and the selected camera support it, and the `showZoom` config option to `true` (the default value). * Zoom can alternatively be toggled by double-tapping the camera feed. This behavior can be controlled with the new `zoomOnDoubleTap` config option. * Added `customLogoSize` overlay configuration parameter to set the size in CSS pixels at which the logo image should be rendered. If you provide a custom logo in a raster format such as PNG, JPEG or WEBP, you can use this parameter to make sure it is rendered at the correct size. For example, you can provide your logo at 210x60px resolution (3X), and set `customLogoSize` to `{ width: 70, height: 20 }` to make sure it is rendered crisply on high-density screens. * The overlay controls (camera selector, zoom and flashlight) are now animated, unless the browser indicates that it prefers reduced motion. Changed * Refreshed the camera selector to look and feel more intuitive while reducing UI clutter by no longer displaying the currently selected camera. * Custom logos in the overlay are no longer tinted with the color given through `primaryColor`, they are now rendered as-is, allowing use of colors different from the primary color. * Custom logos are now rendered at their intrinsic size, unless overridden by specifying a width/height in `customLogoSize`. Previously, the provided image would be scaled down by a factor that depended on the device display density, resulting in inconsistent sizes. Fixed * Flashlight icon no longer appears blurry on devices with high DPI displays. * Improved the display of the built-in logo in the overlay and reduced its size. ## 1.10.1 - 2025-10-06 Fixed * QR Codes with 'Structured Append' mode no longer lead to detections with empty data. The QR Code payload is now passed through. * Fixed an issue in the PDF417 decoder that prevented many codes with less than 6 rows from being read. Changed * Updated Emscripten WASM compiler to 4.15.0. ## 1.10.0 - 2025-08-28 Fixed * Detections drawn on overlay are now cleared after calling `BarcodeReader.stop()`. Changed * Significantly improved Aztec Code recognition, especially when reading them from an angle. * Updated Emscripten WASM compiler to 4.13.0. ## 1.9.2 - 2025-08-13 Fixed * Fixed an issue in the Aztec Code decoder that prevented codes of dimension 31x31 to be read. ## 1.9.1 - 2025-08-11 Added * Extended `PopupConfiguration` to allow passing in a `FrameSourceConfiguration`, allowing more advanced configuration of the frame source in `PopupScanner`. * `rememberCameraDeviceId` now falls back to the device label, if the ID of the remembered device changed. This makes remembering the camera more robust on browsers that rotate the device IDs frequently, like Samsung Internet. Fixed * A remembered device no longer being available no longer clears the remembered device. * Switching cameras on Firefox Android no longer causes an error. ## 1.9.0 - 2025-07-09 Changed * Successful online license checks now persist for up to 48 hours, allowing for instant initialization in most cases, and intermittent offline use by PWAs. Fixed * Calling `PopupScanner.scan()` before the SDK is initialized no longer results in a frozen popup with a spinner. * Properly marked accidentally exposed utility methods of `SdkError` as internal. * Opting out of usage data for Enterprise licenses only worked for offline keys, now the capability is honored also for online keys. ## 1.8.2 - 2025-06-16 Fixed * Added a missing bounds check to the Code 93 decoder that caused memory access errors that manifested frequently on DuckDuckGo Android and potentially other browsers. ## 1.8.1 - 2025-06-04 Fixed * Corrected a build system issue that caused the type definition for `EngineConfiguration.duplicateInterval` to be excluded. Special thanks to Johnervan Lee for reporting this issue. ## 1.8.0 - 2025-05-27 Added * Preliminary support for scanning static images with new `ImageScanner` API. Changed * Bumped Emscripten WASM compiler to 4.0.9. Fixed * Restored audio feedback in desktop/non-touch environments, we were waiting for touch activation only to unlock the Web Audio context. ## 1.7.5 - 2025-05-08 Fixed * Fixed an out-of-bounds memory read in the Aztec decoder which could cause occasional crashes. If Aztec symbology is not used, this fix has no effect. * Fixed an unhandled Promise rejection when audible feedback could not be played. ## 1.7.4 - 2025-04-28 Added * Added `StrichSDK.setAudioImpl()` function to explicitly set the audio implementation to be used for audible feedback. If not set (the default), the SDK will automatically choose an appropriate one depending on the runtime context and capabilities. Fixed * Added a workaround for an intermittent issue with iOS PWAs, where a Web Audio context would remain silent after the PWA returned to foreground. We now automatically fall back to an HTML5 Audio element for PWAs on iOS. * Explicitly set flashlight icon size to avoid application styles bleeding in. ## 1.7.3 - 2025-04-10 Added * It is now possible to provide an object instead of a boolean for the audio feedback configuration. * The new audio feedback configuration object has an `enabled` property with the same semantics as the naked boolean option. * The type of the AudioSession to use for emitting the beep can now be set using the `audioSessionType` property of the audio feedback configuration object. Setting the value to `null` will opt out of setting the AudioSession type. The value `transient` is still used as the default. ## 1.7.2 - 2025-03-27 Fixed * Clarified documentation around BarcodeReader lifecycle methods and their relation to camera access. * Added missing ARIA attributes for flashlight toggle control. * Added missing TypeDoc annotations that resulted in broken or nonexistent links in reference documentation. ## 1.7.1 - 2025-02-17 Added * PopupScanner can now be more extensively styled by supplying an overlay configuration in the `overlay` option of the PopupScanner configuration. This also enables Enterprise license holders to use the custom branding add-on within PopupScanner. ## 1.7.0 - 2025-01-28 Added * New QR Code decoder now supports rounded finder patterns via the `roundedFPs` symbology config option. * New QR Code decoder now supports codes printed on curved surfaces via the `curved` symbology config option. * If audio feedback is enabled and the [Audio Session API](https://www.w3.org/TR/audio-session/) is available, the audio session type is set to `transient`. * Improved performance in situations where lighting is uneven. Changed * Downgraded target from ES2020 to ES2019 to avoid issues with the optional chaining operator and Webpack 4.x builds. Special thanks to Gaetan from GCK Technology Consulting for providing a minimum reproducible sample. ## 1.6.1 - 2024-12-16 Added * Added `maskColor` configuration option to de-emphasize the area around the viewfinder rectangle. Using dark, semi-transparent color like `rgba(0,0,0,0.5)` works best to keep the user focused on region of interest. * Added configuration options `viewfinderBorderWidth` and `viewfinderCornerRadius` for styling the viewfinder rectangle. ## 1.6.0 - 2024-11-25 Added * Added `PopupScanner`, an out-of-the-box, modal scanning dialog with minimal customization options, drastically simplifying SDK usage for simple barcode scanning use cases. You can now scan barcodes with a single call to `PopupScanner.scan()` – building a scanner UI and managing the `BarcodeReader` is done for you. * Added localized error messages for Japanese, Chinese, Hindi, Vietnamese, Indonesian, Thai, Korean, Portuguese, Dutch, Belgian, Danish, Swedish, Norwegian and Finnish. Changed * We now use the widely available Web Audio API for audible feedback. For now, we are using it to play the existing beep sound. Going forward, the Web Audio API will allow flexible customization of the sound played on scans. ## 1.5.5 - 2024-11-04 Fixed * QR Codes that use “Mirror Imaging” feature (ISO/IEC 18004:2015, section 6.2) and have their rows/columns transposed are now supported. * QR Codes were erroneously reported as having a symbology identifier of `]Q0` (Model 1 QR Codes) instead of `]Q1`. ## 1.5.4 - 2024-10-09 Fixed * Fixed an issue in Safari on iOS 18 that prevents the flashlight from being turned off after it has been turned on. ## 1.5.3 - 2024-10-07 Added * Support optional Modulo 43 checksum for the Code 39 symbology. Setting the new symbology parameter `checksumMode` to `1` (validate) or `2` (validate and transmit) will expect a check digit. Please note that setting the parameter to `1` or `2` will lead to Code 39 barcodes that lack a valid check digit to be no longer readable. * Support optional Modulo 16 checksum for the Codabar symbology. Please refer to the new `checksumMode` symbology parameter for details. Fixed * Removed overly aggressive pruning of candidates introduced in 1.5.2 that caused performance regressions in certain scenarios. ## 1.5.2 - 2024-09-18 Added * Add a utility method `StrichSDK.getCameraPermissionState()` that returns the current state of the camera permission: `granted`, `denied`, `prompt` or `unknown`. It relies on the Permissions API which is supported by all major browsers except Firefox. Invoking the method on a browser that does not support the Permissions API will return `unknown`. Changed * If all symbologies are enabled by omitting or specifying an empty `symbologies` array in the configuration, an error message is now logged to the browser console using WARN log level. Previously this was logged by the WebAssembly engine and logged using the normal log level. This change has no impact on customers, and we maintain that logging this state as a warning makes sense, as it indicates a misconfiguration of the SDK. Fixed * Further decreased EAN/UPC barcode misread rates by performing additional checks on the barcode structure. * Fixed lower recognition rates due to too aggressive quiet zone checks. * Some error messages that were created due to malformed JSON configuration were not propagated from the WebAssembly engine to the `detailMessage` of the `SdkError`. * Fixed a rare issue where initializing a `BarcodeReader` with a bad JSON configuration prevented further attempts at initializing it with a correct one. ## 1.5.1 - 2024-08-26 Added * It is now possible to set the minimum size of the quiet zone for 1D symbologies, using the `qz` symbology parameter. There should rarely be a need to change this parameter, but especially for symbologies with weak checksums, it can make sense to use a more strict value. Fixed * Fixed frequent short scans for Codabar symbology because of insufficient quiet zone check. ## 1.5.0 - 2024-08-22 This is a major release with extensive under-the-hood improvements. No changes to existing configurations are necessary. Added * Improved read rates for the most popular 1D symbologies EAN/UPC and Code 128 across several benchmarks. * Added ink spread compensation to EAN/UPC decoder, allowing the decoder to better decode barcodes where the ink has degraded to the point where bar widths are too narrow, making the barcode non-conformant with regard to the ISO specifications. No configuration change is necessary to take advantage of this feature. * Added ISO/IEC 15424 symbology identifier to barcode detections. This is primarily useful for identifying GS1 payloads. * Improved detection rectangle for 1D barcode detections. Changed * Completely reworked core barcode segmentation and 1D barcode decoding logic, laying the groundwork for future features like multiple code detection and Augmented Reality (AR) mode. No changes to configuration are necessary. Fixed * Decreased misread ratio for EAN/UPC barcodes. Versions prior to 1.5 sometimes misread EAN/UPC barcodes. * Fixed misplaced detection rectangle for PDF417 barcodes read in reverse orientation (visual-only). * Increased read rates for QR codes at high resolutions. Removed * Removed support for 2-digit and 5-digit EAN/UPC supplements. While refactoring the 1D core, we decided to remove this seldomly-used feature. Please contact us if you require this capability. * Removed support for WebGL1 in favor of WebGL2 which has been widely available for some time now (https://www.khronos.org/blog/webgl-2-achieves-pervasive-support-from-all-major-web-browsers). Setting the `impl` property in the locator configuration to `webgl1` no longer has an effect. Devices that do not support WebGL 2 will continue working, but will skip a preprocessing step which will have a negative impact on code recognition. * Removed an unnecessary check for WebAssembly SIMD support. SIMD support is planned for the next major release. * Removed `numScanlines` property from engine configuration. The engine now automatically determines an appropriate number of scanlines depending on the context. ## 1.4.9 - 2024-07-26 Deprecated * This is the last release to retain compatibility with WebGL 1. Future releases will require WebGL 2, which has been widely available for several years now. Fixed * Decreased WASM size by using more space-efficient lookup tables in the PDF417 decoder. * Decreased bundle size by not including some code that was only used for internal debugging purposes. * Clarified documentation regarding availability of the `customLogoSrc` overlay configuration property. ## 1.4.8 - 2024-06-19 Fixed * Fixed a memory leak in the WebAssembly engine when running in debug mode. Changed * Request a smaller initial memory allocation for the WebAssembly engine (32MB instead of 64MB) to ease memory pressure on iOS. Safari currently has an open issue with WASM memory management that leads to 'Out of Memory' error in `StrichSDK.initialize()` and persists across reloads so can only be recovered from by closing and reopening the browser tab. The associated WebKit issue is: https://bugs.webkit.org/show_bug.cgi?id=222097 ## 1.4.7 - 2024-06-03 Added * Slightly improved PDF417 recognition rate for dense codes. Fixed * Fixed an issue where the number of unique devices reported to the Customer Portal was incorrect. Existing devices were being counted multiple times. The number of scans was not affected. * Gracefully handle the edge case where the device ID returned in the video track can not be found in the enumerated devices instead of throwing an internal error with detail message `Video track device not found in enumerated devices`. This state shouldn't be possible, but has been observed on Safari iOS in PWA mode. ## 1.4.6 - 2024-04-04 Added * Flashlight capability is now available on iOS, as newer versions of Safari apparently support it. Fixed * Improved camera reliability on iOS, Safari 17.4 spuriously loses the camera stream, and we were unable to recover from this event in all situations. * Fixed symbologies sometimes being enabled even though they were not configured due to uninitialized memory. ## 1.4.5 - 2024-03-26 Fixed * Fixed the `BarcodeReader` host element growing in vertical size in certain layout situations. A ResizeObserver was registered on the host element and caused an endless loop. The element hosting the camera stream will now no longer adapt automatically to size changes of the host element. Changed * If an error occurs in the constructor of `BarcodeReader`, the thrown error will now include the `detailMessage` in its `message` property, making it easier to identify problems during integration. Previously `message` contained only an opaque error message (The supplied configuration is not valid) which was not helpful in debugging issues such as the host element not using relative positioning. ## 1.4.4 - 2024-03-04 Added * Added a non-modular build flavor `strich-noesm.js` that uses an IIFE and exposes the SDK through a global `strich` object. This flavor is only recommended for scenarios where ES6 modules (import/export) are not supported. Changed * Switched our underlying build infrastructure to use esbuild instead of Parcel. If you experience issues in your build environment when upgrading to this release, please let us know. Fixed * Handle camera track muting: when the browser decides to mute the camera video track, display an error and offer to re-acquire the camera. This addresses in an issue in iOS 17.4 Beta versions where client-side navigation in SPAs triggers causes Safari to mute the track (https://bugs.webkit.org/show_bug.cgi?id=269846) and allows to recover from this state manually. * Calling `BarcodeReader.start()` on an already started `BarcodeReader` instance no longer causes performance warnings in the browser console on Chrome, and is now simply ignored. * Absolute image URLs are now supported for overlay logo customization (Enterprise-only). The hosting server must be configured to support cross-origin resource sharing (CORS). ## 1.4.3 - 2024-02-12 Changed * The default camera on iPhones with multiple back-facing cameras (e.g. iPhone 15 Pro) is now the Back Dual Wide Camera. Previously, the Back Camera was selected, which resulted in bad scanning performance due to the camera being out of focus. ## 1.4.2 - 2024-02-05 Added * Allow overriding camera selection by providing a `constraints` object in the frame source configuration. This is an advanced option that should be used with care. Fixed * Significantly improved Data Matrix read rates where codes suffer from perspective warp (not viewed from above). * GS1 QR codes with FNC1 in first position now no longer results in empty data. ## 1.4.1 - 2024-01-26 Fixed * Improved Aztec Code detection rate for compact codes and improved accuracy of detection quadrilateral. Changed * Removed all `console.error()` invocations to avoid issues with automated testing systems checking for console error messages. Non-critical errors are reported via `console.warn()`, critical errors are propagated via `SdkError` and the displayed on the overlay. ## 1.4.0 - 2023-12-29 Fixed * Fixed a performance issue that manifested itself on less powerful devices where the majority of processing time was spent on GPU->CPU buffer transfers, leading to up to 60% performance increase in some situations. Changed * `BarcodeReader.destroy()` now returns a Promise that resolves when the `BarcodeReader` and its resources are destroyed. The returned Promise is typically already resolved, unless `destroy()` is called while the BarcodeReader is being initialized, in which case the destruction happens after initialization completes. Previously, calling `destroy()` while `initialize()` was still pending caused a crash. Existing application code that calls `destroy()` does not need to be adapted. Added * Added basic overlay styling capabilities to overlay. The color of the viewfinder, camera selector control and flashlight icons can now be set via overlay configuration property `primaryColor`. The color of highlighted barcode detections can be set using `highlightColor`, and the `cornerRadius` property can be used to apply rounded corners to the viewfinder and camera selector control. The color of the targeting line when a linear barcode is detected can be set using `targetingLineActiveColor`. The defaults for all these properties match the previous hard-coded values, so if you are happy with the look & feel, you don't have to do anything. * Added overlay configuration property `filterCameras` to control filtering of camera devices shown in camera selector. If set to true `true` (default), only back-facing camera devices are listed. If set to `false`, all camera devices available in the browser are listed. * If a `BarcodeReader` could not be initialized due to missing browser capabilities like WebAssembly, WebGL or getUserMedia, mention the missing capability in the `detailMessage` property of the thrown `SdkError`. * Report the detected code's location as a quadrilateral instead of an axis-aligned bounding box in the new `quadrilateral` property of the `CodeDetection`. The overlay will also use this more precise location for drawing the detections if `drawDetections` is enabled. ## 1.3.4 - 2023-11-28 Fixed * Significantly improved Aztec Code recognition rates for full-range codes. * Removed transmission of unused URL parts in license verification and usage tracking. ## 1.3.3 - 2023-11-07 Fixed * Fixed an issue on iOS where camera access failed with a camera device is not compatible error message `rememberCameraDeviceId` was set to `true` and the user's device changed, leading to the remembered camera device id no longer being valid. Changed * Expose detail message when license check fails, providing potentially useful information on how to resolve the issue (e.g. URL not in scope). Added * Throw a dedicated error if the SDK is initialized in an insecure context (non-HTTPS, non-localhost). Previously this was reported in a confusing fashion (invalid license). ## 1.3.2 - 2023-10-26 Fixed * Fixed an issue that led to offline-capable license keys being reported as expired. ## 1.3.1 - 2023-10-14 Added * Added configuration option `focusOnTap` (default: `true`) to overlay configuration: this toggles the autofocus that occurs when tapping on the overlay. Previously focusing on tap was always enabled. Changed * Adapted license service calls in order to re-enable detailed usage stats in Customer Portal using Client Hints. No changes to existing Content Security Policies are necessary. ## 1.3.0 - 2023-09-20 Added * New configuration option `rememberCameraDeviceId` in frame source configuration: if set to `true`, the SDK will remember the selected camera device after a successful scan, and attempt to re-acquire it when re-initialized. Default value is `false`. * Support for PDF417 and Compact PDF417 symbologies is now available, the new symbology can be activated by specifying `pdf417` in the `symbologies` array of the engine configuration. PDF417 barcodes can only be read in horizontal direction at the moment. Changed * Under the hood improvements and removal of legacy code. ## 1.2.2 - 2023-08-16 Added * Extended support for inverted codes to 1D barcodes. Setting `invertedCodes` to `true` will now also cause inverted 1D barcodes to be read. * New helper method `StrichSDK.hasCameraDevice()` to check for the existence and availability of a camera device. ## 1.2.1 - 2023-07-28 Added * Raw code data is now exposed through the `rawData` property of the `CodeDetection`. ## 1.2.0 - 2023-07-12 Added * The GPU-accelerated barcode locator is now based on WebGL 2, which is widely supported by now. If you are for some reason required to use WebGL 1, set the new `impl` field on the locator configuration to `webgl1`. WebGL 2 supports asynchronous reads which we use to decrease the load on the main thread. Some older Safari versions have reported issues using the asynchronous reads, which is why for the moment we have disabled asynchronous reads on iOS. This will likely change in the future. Fixed * Fixed a rare engine crash that resulted from incorrect error handling. Changed * Changed the default values for `hysteresisMinCount` to `2` and `hysteresisInterval` to `350`. The previous defaults were causing EAN/UPC codes to be unreadable on low-end Android devices. ## 1.1.4 - 2023-06-26 Added * User-facing error messages (e.g. "Access to camera not permitted") are now localized. English, Spanish, German, French and Italian are the currently supported languages, with more to come. * Prepared support for iOS 17 Safari OffscreenCanvas+WebGL support. STRICH will utilize a GPU-accelerated OffscreenCanvas if the browser supports it. Fixed * Improved performance when scanning EAN/UPC codes with hysteresis enabled. ## 1.1.3 - 2023-06-07 Added * Configurable min/max length for variable-length symbologies. Instead of just the name, specify an object with `name`, `minLen` and/or `maxLen` in the `symbologies` array. Example: `symbologies: 'ean13', { name: 'code128', minLen: 8, maxLen: 12 }` Fixed * Removed a stray console output statement that was meant for debugging purposes only. ## 1.1.2 - 2023-06-06 Fixed * Improved error handling when encountering `OverconstrainedError`, especially on Firefox desktop. Special thanks to Jukka Aittokallio for reporting the issue. ## 1.1.1 - 2023-05-22 Added * Added hysteresis for 1D barcodes with weak checksums (EAN/UPC symbologies) to decrease misreads. This feature improves reliability for EAN/UPC barcodes at the cost of slightly slower detection, and can be deactivated by setting the new `hysteresisMinCount` engine parameter to `0`. Fixed * Fixed a memory violation in the Databar decoder, which would occasionally crash the engine. ## 1.1.0 - 2023-05-17 Added * Added experimental support for EAN-2/EAN-5 supplements (new `ean2` and `ean5` symbology options). Note that this feature is experimental: supplemental symbologies will not be enabled if you enable all symbologies (which we do not recommend). * Added an optional `onError` callback to the BarcodeReader instance. ## 1.0.9 - 2023-05-15 Fixed * Fixed an issue where an error in the WebAssembly engine was misinterpreted, and wrong results where generated. * Do not draw the red 'laser line' when a 2D symbology is scanned ## 1.0.8 - 2023-05-11 Added * Not calling `BarcodeReader.destroy()` after no longer needing a BarcodeReader is a common cause of camera access errors. A warning is now logged to the browser console if a BarcodeReader is initialized when another instance remains that was not destroyed yet. * If no `regionOfInterest` is specified in the configuration, an appropriately sized region will now be automatically selected based on the configured symbologies. Fixed * The horizontal targeting line in the scanning overlay was not being shown if all symbologies were enabled. ## 1.0.7 - 2023-05-06 Fixed * When a BarcodeReader was used in standalone display mode on iOS (PWA, added to home screen), camera access was lost when the user unfocused the PWA. An error is now displayed in this case, along with a button to resume scanning. Changed * Clarified acceptable use of `StrichSDK.setCustomId()`, personally identifying data must not be sent. ## 1.0.6 - 2023-04-19 Fixed * The combination of Enterprise offline license with analytics opt-in did not work, no usage data was sent. ## 1.0.5 - 2023-04-18 Fixed * Deactivating audio feedback (beep) was not possible. Added * Log a warning to the browser console if an invalid resolution (e.g. 'qhd') is specified in the frame source configuration. ## 1.0.4 - 2023-03-30 Fixed * Internal code change that addresses a "ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor" build error in older optimized Angular versions. ## 1.0.3 - 2023-03-30 Changed * Not setting a `duplicateInterval` in the engine configuration will now lead to a default 750ms interval to be used. Fixed * Ensured compatibility with iOS 16.4, do not attempt to instantiate a WebGL context on an OffscreenCanvas. ## 1.0.2 - 2023-03-23 Added * Activated custom logo on overlay feature (Enterprise-only). Fixed * Added missing fallback to medium precision on devices which do not support WebGL high precision floats. ## 1.0.1 - 2023-03-20 Fixed * Gracefully handle the situation where MediaStream does not implement getCapabilities() (e.g. Firefox Android). * Avoid spurious exception after `BarcodeReader.destroy()` from an asynchronous overlay update in destroyed state * Aztec code detection rectangle was not displayed on overlay for some non-compact Aztec codes. ## 1.0.0 - 2023-03-15 This is the first public release. A special thank you to all pilot users for getting us here! Added * Log engine version on initialization. ## 0.9.11 - 2023-03-13 Fixed * Fixed memory access violations which manifested quickly when all symbologies are activated ## 0.9.10 - 2023-03-10 Changed * Force WebGL context loss when destroying WebGL resources, avoiding warning messages when too many WebGL contexts are allocated. Fixed * BarcodeReader.destroy() did not destroy all engine resources, leading to intermittent crashes on iOS Safari ## 0.9.9 - 2023-03-10 Fixed * BarcodeReader.destroy() now properly removes all elements added to the host element * Respect analyticsOptOut: false for offline-capable licenses that still want usage data ## 0.9.8 - 2023-02-28 Fixed * Fixed bug where online license verification was not retried on network errors, only on non-200 HTTP status codes. ## 0.9.7 - 2023-02-23 Fixed * Fixed accidental inclusion of dependency. ## 0.9.6 - 2023-02-22 Fixed * Fixed bug that prevented offline licenses from activating the SDK. ## 0.9.5 - 2023-02-22 Added * Activated support for Offline licenses. ## 0.9.4 - 2023-02-21 Fixed * Fixed a regression that caused problems reading rotated Aztec codes. ## 0.9.3 - 2023-02-14 Added * Added this CHANGELOG.md file to the project. ## 0.9.2 - 2023-02-10 Changed * Started using `main` instead of `browser` target in `package.json` to avoid problems with SSR frameworks such as Next.js. ## 0.9.1 - 2023-02-07 This is the first public release, distributed via NPMJS.