Added scanning barcodes with a camera

This commit is contained in:
2026-03-08 16:59:33 +00:00
parent b4f8489834
commit 5a37e5dd5f
404 changed files with 224181 additions and 0 deletions

View File

@@ -0,0 +1,758 @@
# API Documentation {#api-documentation}
Complete reference for all Quagga2 methods, callbacks, and events.
## Core Methods {#core-methods}
### `Quagga.init(config, callback)` {#quagga-init}
Initializes the library with the given configuration and requests camera access if using live stream mode.
**Parameters**:
- `config` (Object) - Configuration object. See [Configuration Reference](configuration.md) for complete details.
- `callback` (Function) - Called when initialization completes: `callback(err)`
- `err` (Error | null) - Error object if initialization failed, `null` on success
**Returns**: `void`
**Example - Live Camera**:
```javascript
Quagga.init({
inputStream: {
type: "LiveStream",
target: document.querySelector('#scanner') // Or '#scanner'
},
decoder: {
readers: ["code_128_reader"]
}
}, function(err) {
if (err) {
console.error("Initialization failed:", err);
return;
}
console.log("Initialization successful, ready to start");
Quagga.start();
});
```
**Example - Static Images**:
```javascript
Quagga.init({
inputStream: {
type: "ImageStream",
src: "/path/to/images/*.jpg",
target: document.querySelector('#scanner')
},
decoder: {
readers: ["ean_reader", "code_128_reader"]
}
}, function(err) {
if (err) {
console.error(err);
return;
}
Quagga.start();
});
```
**Error Handling**:
The callback receives an `err` parameter if initialization fails. Common causes:
- User denies camera permission
- No camera device found
- Browser doesn't support required APIs
- Invalid configuration parameters
Always check for errors before calling `start()`:
**Target Element**:
If no `target` is specified, Quagga looks for an element matching the CSS selector `#interactive.viewport` (for backwards compatibility).
### `Quagga.start()` {#quagga-start}
Starts the video stream and begins locating and decoding barcodes.
**Parameters**: None
**Returns**: `void`
**Example**:
```javascript
Quagga.init(config, function(err) {
if (!err) {
Quagga.start();
}
});
```
**Prerequisites**:
- `Quagga.init()` must have completed successfully
- For live stream: Camera permission must be granted
**Note**: Call this in the `init()` callback after checking for errors.
### `Quagga.stop()` {#quagga-stop}
Stops the decoder from processing images and disconnects the camera if one was requested.
**Parameters**: None
**Returns**: `Promise<void>` - Resolves when cleanup is complete
**Example**:
```javascript
// Stop scanning
await Quagga.stop();
console.log("Scanner stopped");
// Or with .then()
Quagga.stop().then(() => {
console.log("Scanner stopped");
});
```
**Behavior**:
- Stops processing new frames
- If using live camera: disconnects and releases camera
- Does not remove event listeners (use `offDetected()` / `offProcessed()` for that)
- Returns a Promise that resolves when camera release is complete
### `Quagga.pause()` {#quagga-pause}
Pauses frame processing without stopping the camera or releasing resources.
**Parameters**: None
**Returns**: `void`
**Example**:
```javascript
// Pause scanning temporarily
Quagga.pause();
// Later, resume scanning
Quagga.start();
```
**Behavior**:
- Stops processing new frames (no more `onProcessed` or `onDetected` callbacks)
- **Does not stop the camera** - the video stream continues running
- **Does not release resources** - the camera remains connected
- Can be resumed by calling `Quagga.start()`
**Use Cases**:
- Temporarily pause scanning while showing a modal or dialog
- Reduce CPU usage when the scanner is not visible
- Pause after detecting a barcode to allow user confirmation before resuming
**Difference from `stop()`**:
| Aspect | `pause()` | `stop()` |
|--------|-----------|----------|
| Frame processing | Stops | Stops |
| Camera stream | **Continues** | Disconnects |
| Resources | **Retained** | Released |
| Resume with | `start()` | `init()` + `start()` |
> **Note**: Since `pause()` keeps the camera running, the user's camera indicator light will remain on. If you want to fully release the camera, use `stop()` instead.
### `Quagga.onDetected(callback)` {#quagga-ondetected}
Registers a callback that is triggered when a barcode is successfully located and decoded.
**Parameters**:
- `callback` (Function) - Handler function: `callback(data)`
- `data` (Object) - Result object containing decoded barcode information
**Returns**: `void`
**Example**:
```javascript
Quagga.onDetected(function(result) {
const code = result.codeResult.code;
const format = result.codeResult.format;
console.log(`Detected ${format} barcode: ${code}`);
// Process the barcode
processBarcode(code);
});
```
**Multiple Handlers**:
You can register multiple handlers - all will be called:
```javascript
Quagga.onDetected(handler1);
Quagga.onDetected(handler2); // Both execute on detection
```
### `Quagga.onProcessed(callback)` {#quagga-onprocessed}
Registers a callback that is called for each processed frame, regardless of detection success.
**Parameters**:
- `callback` (Function) - Handler function: `callback(data)`
- `data` (Object) - Processing result with detailed information
**Returns**: `void`
**Example**:
```javascript
Quagga.onProcessed(function(result) {
const drawingCtx = Quagga.canvas.ctx.overlay;
const drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
// Draw boxes and lines for visualization
if (result.boxes) {
drawingCtx.clearRect(0, 0,
drawingCanvas.width, drawingCanvas.height);
result.boxes.forEach(function(box) {
Quagga.ImageDebug.drawPath(box, {x: 0, y: 1},
drawingCtx, {color: "blue", lineWidth: 2});
});
}
if (result.box) {
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1},
drawingCtx, {color: "green", lineWidth: 2});
}
if (result.codeResult && result.codeResult.code) {
// Successfully decoded
console.log("Code detected:", result.codeResult.code);
}
}
});
```
**Use Cases**:
- Custom visualization of detection process
- Counting processed frames
- Performance monitoring
- Drawing custom overlays
### `Quagga.drawScannerArea()` {#quagga-drawscannerarea}
Manually draws the scanner area overlay on the overlay canvas using the configured `inputStream.area`.
**Parameters**: None (uses the area configuration from `Quagga.init()`)
**Returns**: `void`
**Behavior**:
- Only draws when `locate` is `false` and `inputStream.area` is configured with styling
- Uses the actual adjusted scanning area (after patch alignment) to accurately match where barcodes will be detected
- Automatically accounts for the area offset and dimensions
- Skips drawing if no styling is provided (no `borderColor`, `borderWidth`, or `backgroundColor`)
- Requires `canvas.createOverlay: true` so the overlay canvas exists
- Drawing occurs automatically every processed frame when conditions are met
**Example**:
```javascript
// Configure area during init
Quagga.init({
inputStream: {
area: {
top: "30%",
right: "10%",
bottom: "30%",
left: "10%",
borderColor: "#0F0",
borderWidth: 2,
backgroundColor: "rgba(255,0,0,0.15)"
}
},
locate: false
});
// Later, manually redraw if you've cleared the overlay
Quagga.drawScannerArea();
```
### `Quagga.offDetected(handler)` {#quagga-offdetected}
Removes a previously registered `onDetected` handler.
**Parameters**:
- `handler` (Function, optional) - Specific handler to remove. If omitted, **all** handlers are removed.
**Returns**: `void`
**Example**:
```javascript
function myHandler(result) {
console.log(result.codeResult.code);
}
Quagga.onDetected(myHandler);
// Later: remove specific handler
Quagga.offDetected(myHandler);
// Or remove all handlers
Quagga.offDetected();
```
### `Quagga.offProcessed(handler)` {#quagga-offprocessed}
Removes a previously registered `onProcessed` handler.
**Parameters**:
- `handler` (Function, optional) - Specific handler to remove. If omitted, **all** handlers are removed.
**Returns**: `void`
**Example**:
```javascript
function processHandler(result) {
// Process frame
}
Quagga.onProcessed(processHandler);
// Remove specific handler
Quagga.offProcessed(processHandler);
// Or remove all handlers
Quagga.offProcessed();
```
### `Quagga.decodeSingle(config, callback)` {#quagga-decodesingle}
Decodes a single image without using `getUserMedia`. Useful for processing uploaded images or static images.
**Parameters**:
- `config` (Object) - Configuration object (subset of full config)
- `callback` (Function) - Result handler: `callback(result)`
- `result` (Object) - Same format as `onDetected` callback
**Returns**: `void`
**Important - Default Scaling**: `decodeSingle` has a built-in default of `inputStream.size: 800`. This means images are **automatically scaled to 800px** on their longest side (both larger images scaled down AND smaller images scaled up). The result's `box`, `boxes`, and `line` coordinates are returned in this scaled coordinate space, not the original image dimensions. To use the original image dimensions without scaling, set `inputStream.size` to `0`.
**Example**:
```javascript
Quagga.decodeSingle({
src: '/images/barcode.jpg', // Or data URL
decoder: {
readers: ["code_128_reader", "ean_reader"]
},
locate: true // Try to locate barcode in image
// Note: inputStream.size defaults to 800; images are scaled to 800px (up or down)
}, function(result) {
if (result && result.codeResult) {
console.log("Detected:", result.codeResult.code);
console.log("Format:", result.codeResult.format);
} else {
console.log("No barcode detected");
}
});
```
**Using Data URLs**:
```javascript
// From file input
document.querySelector('#file-input').addEventListener('change', function(e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = function(event) {
Quagga.decodeSingle({
src: event.target.result, // Data URL
decoder: {
readers: ["code_128_reader"]
}
// Default size: 800 applies - image scaled if larger
}, function(result) {
if (result && result.codeResult) {
alert("Barcode: " + result.codeResult.code);
}
});
};
reader.readAsDataURL(file);
});
```
**Node.js Usage**:
```javascript
const Quagga = require('@ericblade/quagga2').default;
Quagga.decodeSingle({
src: "./barcode.jpg",
inputStream: {
size: 800 // This is actually the default; shown explicitly here
},
decoder: {
readers: ["code_128_reader"]
}
}, function(result) {
if (result && result.codeResult) {
console.log("Code:", result.codeResult.code);
}
});
```
## Result Object {#result-object}
The result object passed to `onDetected`, `onProcessed`, and `decodeSingle` callbacks contains detailed information about the detection and decoding process.
### Complete Result Structure {#result-structure}
```javascript
{
codeResult: {
code: "FANAVF1461710", // The decoded barcode string
format: "code_128", // Barcode format
start: 355, // Start position
end: 26, // End position
codeset: 100, // Code 128 specific
startInfo: {
error: 1.0,
code: 104,
start: 21,
end: 41
},
decodedCodes: [ // Individual code segments
{ code: 104, start: 21, end: 41 },
// ... more segments
{ error: 0.88, code: 106, start: 328, end: 350 }
],
endInfo: {
error: 0.88,
code: 106,
start: 328,
end: 350
},
direction: -1 // Scan direction
},
line: [ // Scan line coordinates
{ x: 25.97, y: 360.56 },
{ x: 401.92, y: 70.88 }
],
angle: -0.657, // Rotation angle in radians
pattern: [0, 0, 1, 1, ...], // Bar pattern (0=space, 1=bar)
box: [ // Primary bounding box (4 corners)
[77.41, 410.93], // Top-left
[0.05, 310.54], // Top-right
[360.16, 33.06], // Bottom-right
[437.51, 133.45] // Bottom-left
],
boxes: [ // All detected boxes
[/* box 1 */],
[/* box 2 */],
// ...
]
}
```
### Result Properties {#result-properties}
| Property | Type | Description |
|----------|------|-------------|
| `codeResult` | Object | Decoded barcode information (may be `undefined` if detection failed) |
| `codeResult.code` | String | The decoded barcode value |
| `codeResult.format` | String | Barcode format (e.g., "code_128", "ean_13") |
| `codeResult.start` | Number | Start position in pattern |
| `codeResult.end` | Number | End position in pattern |
| `codeResult.direction` | Number | Scan direction (1 or -1) |
| `codeResult.supplement` | Object | (Optional) Supplement barcode data for EAN-13/UPC-A with EAN-2 or EAN-5 extensions |
| `codeResult.supplement.code` | String | The decoded supplement value (2 or 5 digits) |
| `codeResult.supplement.format` | String | Supplement format: "ean_2" or "ean_5" |
| `line` | Array | Two points defining the scan line |
| `angle` | Number | Barcode rotation angle (radians) |
| `pattern` | Array | Binary pattern (0=space, 1=bar) |
| `box` | Array | Bounding box coordinates (4 corner points) |
| `boxes` | Array | All candidate boxes found during localization. When `locate` is `false`, this contains a single box representing the actual adjusted scanning area (after patch alignment) |
> **Note: `boxes` with `locate: false`**
>
> When `locate` is `false` and an `inputStream.area` is configured, `result.boxes` contains a single box representing the actual scanning area dimensions. This box reflects the adjusted dimensions after patch alignment (which can differ slightly from the percentage-based area due to rounding to patch size multiples). Use these coordinates if you need to know the exact scanning rectangle:
>
> ```javascript
> Quagga.onProcessed(function(result) {
> if (result.boxes && result.boxes.length > 0) {
> // When locate=false, boxes[0] is the actual scanning area
> const scanArea = result.boxes[0];
> console.log("Scanning area corners:", scanArea);
> }
> });
> ```
> **Important: Coordinate System**
>
> The `box`, `boxes`, and `line` coordinates are returned in **processed canvas coordinates**, not original image/video coordinates. If you're using `inputStream.size` to scale the processing resolution (e.g., for performance), you'll need to scale these coordinates to match your original video/image dimensions.
>
> ```javascript
> // Scale coordinates to original video size
> const scaleX = video.videoWidth / Quagga.canvas.dom.image.width;
> const scaleY = video.videoHeight / Quagga.canvas.dom.image.height;
> const scaledBox = result.box.map(p => [p[0] * scaleX, p[1] * scaleY]);
> ```
>
> See [Working with Box Coordinates](../how-to-guides/working-with-coordinates.md) for complete examples.
### Checking for Successful Detection {#checking-detection}
```javascript
Quagga.onDetected(function(result) {
// Always check if codeResult exists
if (result && result.codeResult && result.codeResult.code) {
console.log("Detected:", result.codeResult.code);
}
});
```
### Using Multiple Barcode Detection {#multiple-barcode-detection}
When `decoder.multiple` is `true`, results are returned as an array:
```javascript
Quagga.init({
decoder: {
readers: ["code_128_reader"],
multiple: true
}
});
Quagga.onDetected(function(result) {
// result is an array of result objects
result.forEach(function(item) {
if (item.codeResult) {
console.log("Code:", item.codeResult.code);
console.log("Box:", item.box);
}
});
});
```
## Canvas Access {#canvas-access}
Quagga automatically creates and manages two canvas elements for visualization. These are positioned over the video/image stream and sized to match the processing dimensions.
### Canvas Structure {#canvas-structure}
```javascript
Quagga.canvas = {
dom: {
image: HTMLCanvasElement, // Canvas for processed image data
overlay: HTMLCanvasElement | null // Transparent canvas for drawing overlays
},
ctx: {
image: CanvasRenderingContext2D, // Context for image canvas
overlay: CanvasRenderingContext2D | null // Context for overlay canvas
}
};
```
> **Note**: The overlay canvas can be `null` if `canvas.createOverlay` is set to `false` in the configuration. See [Canvas Configuration](configuration.md#canvas-configuration) for details.
### Overlay Canvas {#overlay-canvas}
The **overlay canvas** (`Quagga.canvas.dom.overlay`) is a transparent canvas element positioned over the video stream. It's automatically created when Quagga initializes (unless `canvas.createOverlay` is `false`) and is designed for drawing bounding boxes, scan lines, and other visual feedback.
**Key characteristics:**
- Has CSS class `drawingBuffer`
- Sized to match the processed image dimensions (`inputStream.size`)
- Positioned absolutely over the video/image element
- Automatically appended to the viewport container
- Coordinates match the processed image space (no scaling needed)
- Can be disabled via `canvas.createOverlay: false` for performance
**Accessing the overlay:**
```javascript
const overlay = Quagga.canvas.dom.overlay;
const overlayCtx = Quagga.canvas.ctx.overlay;
// Always check if overlay exists before using
if (overlayCtx && overlay) {
// Clear overlay
overlayCtx.clearRect(0, 0, overlay.width, overlay.height);
// Draw custom shapes
overlayCtx.strokeStyle = "red";
overlayCtx.lineWidth = 3;
overlayCtx.strokeRect(10, 10, 100, 100);
}
```
### Image Canvas {#image-canvas}
The **image canvas** (`Quagga.canvas.dom.image`) contains the processed grayscale image data used for barcode detection. This is primarily for internal use and debugging.
**Key characteristics:**
- Has CSS class `imgBuffer`
- Contains the grayscale/processed image data
- Useful for debugging locator issues
### When to Use Each Canvas {#when-to-use-canvas}
| Use Case | Canvas to Use |
|----------|---------------|
| Drawing bounding boxes | `overlay` |
| Highlighting detected barcodes | `overlay` |
| Custom scan line visualization | `overlay` |
| Debugging image processing | `image` |
| Checking processed resolution | Either (they have same dimensions) |
### Important: Coordinate System {#canvas-coordinate-system}
When drawing on the overlay canvas, use `result.box` and `result.boxes` coordinates directly - **no scaling is needed**. These coordinates are already in the overlay canvas's coordinate space.
```javascript
Quagga.onProcessed(function(result) {
const ctx = Quagga.canvas.ctx.overlay;
const canvas = Quagga.canvas.dom.overlay;
// Clear previous drawings
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw box directly - coordinates already match the overlay canvas
if (result && result.box) {
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, ctx, {
color: "green", lineWidth: 2
});
}
});
```
> **Note**: Scaling is only needed when drawing on a **different** canvas (like a custom overlay on the original video element). See [Working with Box Coordinates](../how-to-guides/working-with-coordinates.md) for details.
### CSS Styling {#canvas-css-styling}
The overlay canvas can be styled with CSS for positioning:
```css
/* Default positioning (handled automatically by Quagga) */
canvas.drawingBuffer {
position: absolute;
top: 0;
left: 0;
}
/* Ensure proper stacking */
#scanner-container {
position: relative;
}
```
## ImageDebug Helper {#imagedebug-helper}
Quagga provides a helper for drawing debug visualizations:
```javascript
// Draw a path (array of points)
Quagga.ImageDebug.drawPath(points, offset, ctx, options);
// Example
Quagga.ImageDebug.drawPath(
result.box,
{ x: 0, y: 1 },
overlayCtx,
{ color: "green", lineWidth: 2 }
);
```
## Complete Example {#complete-example}
```javascript
// Initialize
Quagga.init({
inputStream: {
type: "LiveStream",
target: document.querySelector('#scanner'),
constraints: {
width: 640,
height: 480,
facingMode: "environment"
}
},
decoder: {
readers: ["code_128_reader", "ean_reader"]
}
}, function(err) {
if (err) {
console.error(err);
return;
}
// Start scanning
Quagga.start();
});
// Handle detections
Quagga.onDetected(function(result) {
console.log("Barcode detected:", result.codeResult.code);
// Stop after first detection
Quagga.stop();
// Cleanup
Quagga.offDetected();
Quagga.offProcessed();
});
// Visualize processing
Quagga.onProcessed(function(result) {
const ctx = Quagga.canvas.ctx.overlay;
const canvas = Quagga.canvas.dom.overlay;
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (result && result.box) {
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, ctx, {
color: "green",
lineWidth: 2
});
}
});
// Stop button
document.querySelector('#stop').addEventListener('click', function() {
Quagga.stop();
Quagga.offDetected();
Quagga.offProcessed();
});
```
## Related {#related}
- [Configuration Reference](configuration.md) - Complete configuration options
- [CameraAccess API](camera-access.md) - Camera control methods
- [Supported Barcode Types](readers.md) - Available barcode readers
- [Getting Started](../getting-started.md) - Basic usage examples
---
[← Back to Reference](index.md)

View File

@@ -0,0 +1,109 @@
# Browser Support {#browser-support}
Quagga2 makes use of many modern Web APIs which are not implemented by all browsers yet. This page details browser compatibility and required APIs.
## Operating Modes {#operating-modes}
Quagga2 operates in two modes:
1. **Analyzing static images** - Process existing image files
2. **Using a camera** - Decode images from a live video stream
The latter requires the MediaDevices API for camera access.
## Browser Compatibility {#browser-compatibility}
You can track the compatibility of the used Web APIs for each mode:
- [Static Images](http://caniuse.com/#feat=canvas,typedarrays,bloburls,blobbuilder)
- [Live Stream](http://caniuse.com/#feat=canvas,typedarrays,bloburls,blobbuilder,stream)
### Static Image Mode {#static-image-mode}
The following APIs must be supported by your browser:
- [Canvas](http://caniuse.com/#feat=canvas)
- [Typed Arrays](http://caniuse.com/#feat=typedarrays)
- [Blob URLs](http://caniuse.com/#feat=bloburls)
- [Blob Builder](http://caniuse.com/#feat=blobbuilder)
### Live Stream Mode {#live-stream-mode}
In addition to the APIs required for static images:
- [MediaDevices API](http://caniuse.com/#feat=stream) - Required for camera access
## Secure Origins Required {#secure-origins}
**Important**: Accessing `getUserMedia` requires a secure origin in most browsers:
- `http://` can **only** be used on `localhost`
- All other hostnames **must** be served via `https://`
This is a browser security requirement. Read more in the [Chrome M47 WebRTC Release Notes](https://groups.google.com/forum/#!topic/discuss-webrtc/sq5CVmY69sc).
## Feature Detection {#feature-detection}
### Detecting getUserMedia Support {#detecting-getusermedia}
Every browser implements the `mediaDevices.getUserMedia` API differently. It's highly recommended to include [webrtc-adapter](https://github.com/webrtc/adapter) in your project for cross-browser compatibility.
**How to test browser capabilities:**
```javascript
if (navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function') {
// Safe to use getUserMedia
console.log('Camera access is supported');
} else {
// Camera access not available
console.log('Camera access is NOT supported');
}
```
### Browser Support Table {#browser-support-table}
The above condition evaluates as follows:
| Browser | Result | Notes |
|---------------|---------|-------|
| Chrome | `true` | Full support |
| Firefox | `true` | Full support |
| Edge | `true` | Full support |
| Safari iOS | `true` | Requires HTTPS |
| IE 11 | `false` | Not supported |
| Safari Desktop| `true` | macOS 11+ |
## Known Issues {#known-issues}
### iOS Torch/Flash {#ios-torch-flash}
Torch (flash) control via `CameraAccess.enableTorch()` and `CameraAccess.disableTorch()` does **not work** on iOS devices running version 16.4 and earlier. Support on later versions may vary.
### Safari Limitations {#safari-limitations}
- Older Safari versions may require user interaction before camera access
- Some older iOS versions have limited WebRTC support
### Internet Explorer {#internet-explorer}
Internet Explorer 11 and below do not support the MediaDevices API and cannot use live camera features. Static image decoding may work with polyfills, but this is not officially supported.
## Recommendations {#recommendations}
For best compatibility:
1. **Use HTTPS** - Required for camera access on all non-localhost domains
2. **Include webrtc-adapter** - Normalizes browser differences
3. **Feature detect** - Check for API support before attempting to use camera
4. **Provide fallbacks** - Offer file upload as alternative to camera access
5. **Test thoroughly** - Browser behavior varies, especially on mobile
## Related {#related}
- [Configuration Reference](configuration.md) - How to configure Quagga2
- [Camera Access API](camera-access.md) - Camera control methods
- [Getting Started](../getting-started.md) - Installation and setup
---
[← Back to Reference](index.md)

View File

@@ -0,0 +1,376 @@
# CameraAccess API {#cameraaccess-api}
Quagga2 exposes a `CameraAccess` API for direct control of camera functionality. This API provides shortcuts for commonly used camera operations.
**Access**: `Quagga.CameraAccess`
## Overview {#overview}
The CameraAccess API allows you to:
- Request and release camera access
- Enumerate available video devices
- Control camera torch (flash)
- Get information about active video streams and tracks
All methods return Promises for async operation handling.
## Methods {#methods}
### `CameraAccess.request(videoElement, constraints)` {#cameraaccess-request}
Initializes the camera and starts playback.
**Parameters**:
- `videoElement` (HTMLVideoElement | null) - Video element to display camera stream. If `null`, camera initializes but remains invisible.
- `constraints` (MediaTrackConstraints, optional) - Camera selection and configuration constraints.
**Returns**: `Promise<void>` - Resolves when camera is ready, rejects on error.
**Example**:
```javascript
const video = document.querySelector('#camera-video');
// Request camera with default constraints
await Quagga.CameraAccess.request(video);
// Request specific camera
await Quagga.CameraAccess.request(video, {
facingMode: 'environment', // Back camera on mobile
width: { ideal: 1280 },
height: { ideal: 720 }
});
// Request camera by device ID
const deviceId = 'abc123...';
await Quagga.CameraAccess.request(video, { deviceId });
// Initialize camera without displaying (for probing)
await Quagga.CameraAccess.request(null);
```
**Use cases**:
- Start camera before Quagga initialization
- Probe camera availability and permissions
- Initialize camera without displaying video
### `CameraAccess.release()` {#cameraaccess-release}
Stops the video stream and releases all camera resources.
**Returns**: `Promise<void>` - Resolves when all tracks are stopped and resources released.
**Example**:
```javascript
// Stop camera
await Quagga.CameraAccess.release();
console.log('Camera released');
```
**Behavior**:
1. Pauses the video element
2. Stops all tracks in the media stream
3. Releases camera for use by other applications
**Note**: Always call `release()` when finished with the camera to free system resources.
### `CameraAccess.enumerateVideoDevices(constraints?)` {#cameraaccess-enumeratevideodevices}
Lists all available video input devices (cameras), optionally filtered by constraints.
**Parameters**:
- `constraints` (MediaTrackConstraints, optional) - Constraints to filter devices. When provided, only devices that can satisfy the given constraints will be returned.
**Returns**: `Promise<MediaDeviceInfo[]>` - Array of video device information.
**Example**:
```javascript
// Get all video devices
const devices = await Quagga.CameraAccess.enumerateVideoDevices();
devices.forEach(device => {
console.log('Device:', device.label);
console.log('Device ID:', device.deviceId);
console.log('Group ID:', device.groupId);
});
// Example output:
// Device: Front Camera
// Device ID: abc123...
// Device: Back Camera
// Device ID: def456...
```
**Filtering devices with constraints**:
```javascript
// Get only devices that support a minimum resolution
const hdDevices = await Quagga.CameraAccess.enumerateVideoDevices({
width: { min: 1280 },
height: { min: 720 }
});
// Get only back-facing cameras
const backCameras = await Quagga.CameraAccess.enumerateVideoDevices({
facingMode: 'environment'
});
// Eliminate wide-angle only cameras by specifying aspect ratio
const standardCameras = await Quagga.CameraAccess.enumerateVideoDevices({
aspectRatio: { ideal: 1.777 } // 16:9
});
```
**Use cases**:
- Build camera selector UI
- Detect available cameras before initialization
- Check for front/back camera availability on mobile
- Filter out cameras that don't meet quality requirements
- Eliminate wide-angle cameras that may not be suitable for barcode scanning
**Note**: Device labels may be empty strings until camera permission is granted. When using constraints, the method will request temporary access to each device to test if it satisfies the constraints.
### `CameraAccess.getActiveStreamLabel()` {#cameraaccess-getactivestreamlabel}
Gets the label of the currently active video track.
**Returns**: `string` - Label of active video track (e.g., "Back Camera", "USB Camera").
**Example**:
```javascript
const label = Quagga.CameraAccess.getActiveStreamLabel();
console.log('Using camera:', label);
// Output: "Using camera: Back Camera"
```
**Use cases**:
- Display which camera is currently active
- Verify correct camera is being used
- Logging and debugging
### `CameraAccess.getActiveStream()` {#cameraaccess-getactivestream}
Gets the complete MediaStream object for the currently active video.
**Returns**: `MediaStream | null` - The active MediaStream object, or `null` if no camera is active.
**Example**:
```javascript
const stream = Quagga.CameraAccess.getActiveStream();
if (stream) {
console.log('Stream ID:', stream.id);
console.log('Stream active:', stream.active);
console.log('Video tracks:', stream.getVideoTracks().length);
console.log('Audio tracks:', stream.getAudioTracks().length);
// Clone the stream
const clonedStream = stream.clone();
}
// Pass stream to WebRTC peer connection
if (stream?.active) {
peerConnection.addStream(stream);
}
```
**Use cases**:
- Pass the stream to WebRTC peer connections
- Clone the stream for multiple consumers
- Check if the stream is still active via `stream.active`
- Access the stream ID
- Work with all tracks (video and audio) in the stream
**Note**: For accessing just the video track, use `getActiveTrack()` instead.
### `CameraAccess.getActiveTrack()` {#cameraaccess-getactivetrack}
Gets the MediaStreamTrack for the currently active video.
**Returns**: `MediaStreamTrack | null` - Active video track object, or `null` if no camera is active.
**Example**:
```javascript
const track = Quagga.CameraAccess.getActiveTrack();
console.log('Track state:', track.readyState);
console.log('Track settings:', track.getSettings());
console.log('Track capabilities:', track.getCapabilities());
// Get current resolution
const settings = track.getSettings();
console.log(`Resolution: ${settings.width}x${settings.height}`);
```
**Use cases**:
- Access advanced track capabilities
- Monitor track state
- Apply additional constraints
- Access camera capabilities (zoom, focus, etc.)
### `CameraAccess.enableTorch()` {#cameraaccess-enabletorch}
Turns on the camera torch (flash).
**Returns**: `Promise<void>` - Resolves when torch is enabled, rejects on error.
**Example**:
```javascript
try {
await Quagga.CameraAccess.enableTorch();
console.log('Torch enabled');
} catch (error) {
console.error('Failed to enable torch:', error);
}
```
**Browser Support**:
- ✅ Chrome (Android)
- ✅ Chrome (Desktop with supported cameras)
- ❌ Safari iOS 16.4 and earlier
- ⚠️ Safari iOS later versions - may or may not work
**Requirements**:
- Camera must support torch capability
- Camera must be actively streaming
- Browser must support torch constraint
**Note**: Always wrap in try-catch as not all devices support torch.
### `CameraAccess.disableTorch()` {#cameraaccess-disabletorch}
Turns off the camera torch (flash).
**Returns**: `Promise<void>` - Resolves when torch is disabled, rejects on error.
**Example**:
```javascript
try {
await Quagga.CameraAccess.disableTorch();
console.log('Torch disabled');
} catch (error) {
console.error('Failed to disable torch:', error);
}
```
**Browser Support**: Same as `enableTorch()`.
## Complete Example {#complete-example}
```javascript
// Enumerate cameras and let user choose
const devices = await Quagga.CameraAccess.enumerateVideoDevices();
const backCamera = devices.find(d => d.label.includes('back'));
// Initialize camera
const video = document.querySelector('#video');
await Quagga.CameraAccess.request(video, {
deviceId: backCamera.deviceId
});
console.log('Active camera:', Quagga.CameraAccess.getActiveStreamLabel());
// Enable torch for better scanning in dark environments
try {
await Quagga.CameraAccess.enableTorch();
} catch (error) {
console.log('Torch not available');
}
// ... use camera for scanning ...
// Cleanup
await Quagga.CameraAccess.disableTorch();
await Quagga.CameraAccess.release();
```
## Torch Control in Live Scanning {#torch-control}
For torch control during live scanning, you may want to provide a toggle button:
```javascript
let torchEnabled = false;
document.querySelector('#torch-toggle').addEventListener('click', async () => {
try {
if (torchEnabled) {
await Quagga.CameraAccess.disableTorch();
torchEnabled = false;
} else {
await Quagga.CameraAccess.enableTorch();
torchEnabled = true;
}
} catch (error) {
console.error('Torch control failed:', error);
alert('Torch not available on this device');
}
});
```
## Advanced Camera Control {#advanced-camera-control}
For advanced camera control (zoom, focus, etc.), use the MediaStreamTrack API:
```javascript
const track = Quagga.CameraAccess.getActiveTrack();
const capabilities = track.getCapabilities();
// Check if zoom is supported
if (capabilities.zoom) {
console.log('Zoom range:', capabilities.zoom.min, '-', capabilities.zoom.max);
// Apply zoom
await track.applyConstraints({
advanced: [{ zoom: 2.0 }]
});
}
```
Read more: [MediaStreamTrack Capabilities](https://www.oberhofer.co/mediastreamtrack-and-its-capabilities)
## Error Handling {#error-handling}
Always handle errors when using CameraAccess methods:
```javascript
try {
await Quagga.CameraAccess.request(video);
} catch (error) {
if (error.name === 'NotAllowedError') {
console.error('Camera permission denied');
} else if (error.name === 'NotFoundError') {
console.error('No camera found');
} else {
console.error('Camera error:', error);
}
}
```
## Related {#related}
- [Browser Support](browser-support.md) - Camera compatibility information
- [Configuration Reference](configuration.md) - Camera configuration in Quagga.init()
- [API Documentation](api.md) - Main Quagga API methods
- [Tips & Tricks](../how-to-guides/tips-and-tricks.md) - Camera optimization tips
---
[← Back to Reference](index.md)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,295 @@
# Quagga2 Dependencies
This document explains the dependency structure of Quagga2 and clarifies which packages are runtime code dependencies versus build/test tools.
## Background
Quagga2 bundles all its code with Webpack, producing standalone browser and Node.js builds. As a result, **all packages are listed as `devDependencies`** in `package.json` because consumers never directly install them - they only use the pre-built bundles in `dist/` and `lib/`.
However, this makes it unclear which packages are actual code dependencies (bundled into the final output) versus which are just build/test tools. This document clarifies that distinction.
---
## Runtime Code Dependencies
These packages contain code that is **imported by the source code** and **bundled into the final output**:
### Core Libraries
- **`gl-matrix`** (^3.4.4)
- **Purpose**: High-performance vector and matrix math operations
- **Usage**: Used throughout the codebase for geometric calculations
- **Files**:
- `src/quagga/quagga.ts` - vec2 operations for bounding boxes
- `src/quagga/initBuffers.ts` - vec2 for buffer initialization
- `src/locator/barcode_locator.js` - vec2, mat2 for barcode location
- `src/common/image_wrapper.ts` - vec2 for image transforms
- `src/common/cvutils/ImageRef.ts` - vec2, vec3 for computer vision
- `src/common/cluster.js` - vec2 for clustering algorithms
- **`lodash`** (^4.17.21)
- **Purpose**: Utility functions for object manipulation
- **Usage**: Primarily `merge()` for config merging, `pick()` for object selection
- **Files**:
- `src/quagga.js` - merge() for configuration
- `src/QuaggaStatic.ts` - merge() for configuration
- `src/reader/ean_reader.ts` - merge() for config defaults
- `src/reader/i2of5_reader.ts` - merge() for config defaults
- `src/input/camera_access.ts` - pick() for MediaTrackConstraints
- `src/locator/test/barcode_locator.spec.ts` - merge() in tests
### Image Processing
- **`ndarray`** (^1.0.19)
- **Purpose**: N-dimensional array manipulation
- **Usage**: Core data structure for image data processing
- **Files**:
- `src/input/input_stream/input_stream_base.ts` - NdArray type definitions
- `src/input/input_stream/input_stream.ts` - NdArray for frame data
- `src/input/frame_grabber.ts` - Ndarray for frame manipulation
- `src/vendor.d.ts` - Type definitions
- **`ndarray-linear-interpolate`** (^1.0.0)
- **Purpose**: Bilinear interpolation for ndarray data
- **Usage**: Image scaling and transformations
- **Files**:
- `src/input/frame_grabber.ts` - `d2()` method for 2D interpolation
- **`ndarray-pixels`** (^5.0.1)
- **Purpose**: Convert between image formats and ndarray
- **Usage**: Loading image data from various sources
- **Files**:
- `src/input/input_stream/input_stream.ts` - `getPixels()` for image loading
### Polyfills (Deprecated)
- **`@babel/polyfill`** (^7.12.1)
- **Status**: ⚠️ **DEPRECATED** by Babel team
- **Purpose**: Legacy polyfill for ES6+ features
- **Current Usage**: Not directly imported in source code
- **Recommendation**: Should be removed in favor of `core-js` + `regenerator-runtime` or Babel's automatic polyfill injection
- **Migration Path**: Use `@babel/preset-env` with `useBuiltIns: 'usage'` and explicit `core-js@3`
---
## Build & Development Tools
These packages are **only used during build/development** and are **not bundled into the final output**:
### TypeScript Toolchain
- **`typescript`** (^5.9.3) - TypeScript compiler
- **`@types/*`** packages - Type definitions for TypeScript
- `@types/chai`, `@types/gl-vec2`, `@types/lodash`, `@types/mocha`, `@types/node`, `@types/sinon`, `@types/sinon-chai`
### Webpack & Bundling
- **`webpack`** (^4.44.2) - Module bundler (used to create `dist/` and `lib/` outputs)
- **`webpack-cli`** (^3.3.12) - Webpack command-line interface
- **`babel-loader`** (^8.2.5) - Webpack loader for Babel transpilation
- **`source-map-loader`** (^1.1.1) - Webpack loader for source maps
### Babel Transpilation
- **`@babel/core`** (^7.28.5) - Babel compiler core
- **`@babel/preset-env`** (^7.28.5) - Smart transpilation based on target environments
- **`@babel/preset-typescript`** (^7.28.5) - TypeScript support in Babel
- **`@babel/plugin-*`** - Various syntax plugins:
- `@babel/plugin-proposal-class-properties`
- `@babel/plugin-proposal-nullish-coalescing-operator`
- `@babel/plugin-proposal-object-rest-spread`
- `@babel/plugin-proposal-optional-chaining`
- `@babel/plugin-transform-runtime`
- **`@babel/runtime`** (^7.28.4) - Babel runtime helpers
- **`babel-plugin-add-module-exports`** (^1.0.4) - CommonJS module.exports handling
- **`babel-plugin-istanbul`** (^7.0.1) - Code coverage instrumentation
### Testing
- **`mocha`** (^5.2.0) - Test framework
- **`chai`** (^4.3.10) - Assertion library
- **`sinon`** (^21.0.0) - Test spies, stubs, and mocks
- **`sinon-chai`** (^3.7.0) - Sinon assertions for Chai
- **`ts-mocha`** (^11.1.0) - TypeScript support for Mocha
- **`ts-node`** (^10.9.2) - TypeScript execution for Node.js
- **`cypress`** (^13.1.0) - End-to-end browser testing
- **`@cypress/webpack-preprocessor`** (6.0.0) - Webpack integration for Cypress
- **`@cypress/code-coverage`** (^3.12.4) - Code coverage for Cypress tests
- **`nyc`** (^17.1.0) - Code coverage tool (Istanbul wrapper)
### Linting & Code Quality
- **`eslint`** (^8.57.1) - JavaScript/TypeScript linter
- **`@typescript-eslint/eslint-plugin`** (^7.18.0) - TypeScript-specific ESLint rules
- **`@typescript-eslint/parser`** (^7.18.0) - TypeScript parser for ESLint
- **`eslint-config-airbnb-base`** (^15.0.0) - Airbnb JavaScript style guide
- **`eslint-config-airbnb-typescript`** (^18.0.0) - Airbnb style for TypeScript
- **`eslint-config-airbnb-typescript-base`** (^6.0.1) - Base Airbnb TypeScript config
- **`eslint-plugin-import`** (^2.32.0) - Import/export validation
- **`eslint-plugin-jsx-a11y`** (^6.10.2) - Accessibility linting
- **`eslint-plugin-typescript-sort-keys`** (^3.3.0) - Enforce sorted object keys
### Utilities
- **`core-js`** (^3.46.0) - Modern JavaScript polyfills (used by Babel)
- **`cross-env`** (^10.1.0) - Cross-platform environment variable setting
---
## Optional Dependencies
- **`fsevents`** (2.3.3)
- **Platform**: macOS only
- **Purpose**: Native file watching for better performance
- **Usage**: Automatically used by Webpack/build tools on macOS
---
## Overrides
The `overrides` field forces specific versions of transitive dependencies:
```json
"overrides": {
"@cypress/request": "^3.0.9"
}
```
- **Purpose**: Security fix for `form-data` vulnerability (CVE in versions < 2.5.4)
- **Details**: Cypress 13.1.0 bundles `@cypress/request@3.0.0` which depends on vulnerable `form-data@2.3.3`. This override forces `@cypress/request@^3.0.9` which uses safe `form-data@~4.0.4`.
---
## Bundle Size Impact
When evaluating dependencies, consider their impact on bundle size:
| Package | Approximate Size | Bundled? |
|---------|-----------------|----------|
| `gl-matrix` | ~50 KB (minified) | ✅ Yes |
| `lodash` | ~4 KB (only `merge` + `pick`) | ✅ Yes (tree-shaken) |
| `ndarray` | ~5 KB | ✅ Yes |
| `ndarray-linear-interpolate` | ~2 KB | ✅ Yes |
| `ndarray-pixels` | ~10 KB | ✅ Yes (browser) |
| `webpack` | ~1.5 MB | ❌ No (dev only) |
| `typescript` | ~50 MB | ❌ No (dev only) |
---
## Adding New Dependencies
When adding a new dependency, consider:
1. **Is it a runtime dependency?**
- Will the code be `import`ed in `src/` files?
- Will it be bundled into `dist/` or `lib/` output?
- → Add to `devDependencies` (all deps go here due to bundling)
- → Document it in the "Runtime Code Dependencies" section above
2. **Is it a build/test tool?**
- Is it only used by Webpack, Babel, ESLint, Mocha, etc.?
- → Add to `devDependencies`
- → Document it in the "Build & Development Tools" section above
3. **Bundle size impact?**
- Run `npm run build` and check the size change in `dist/quagga.min.js`
- Consider tree-shaking (does the library support ES modules?)
- Look for lighter alternatives if the package is large
4. **Browser compatibility?**
- Does the package work in browsers?
- Does it require Node.js-specific APIs (`fs`, `path`, etc.)?
- → Check if it's already shimmed in `configs/webpack.config.js` (e.g., `node: { fs: 'empty' }`)
---
## Version Constraints
### Pinned Versions
Some packages are pinned to specific versions due to compatibility issues:
- **`mocha@^5.2.0`** - Pinned to v5 because newer versions have breaking changes
- **`chai@^4.3.10`** - Pinned to v4 because v5+ and v6+ are ESM-only, incompatible with CommonJS tests
- **`sinon-chai@^3.7.0`** - Pinned to match `chai@4.x` compatibility
- **`webpack@^4.44.2`** - Pinned to v4 because v5 requires significant config migration
- **`cypress@^13.1.0`** - Pinned to v13 for stability
These are configured in `.ncurc.json` to prevent accidental upgrades via `npm-check-updates`.
### Upgrade Policy
- **TypeScript ecosystem** (`typescript`, `@typescript-eslint/*`, `ts-*`): Keep up-to-date
- **Babel ecosystem** (`@babel/*`): Keep up-to-date for security and features
- **Testing tools** (`mocha`, `chai`, `sinon`): Upgrade cautiously, test thoroughly
- **Webpack & bundlers**: Major version upgrades require careful migration planning
- **Runtime dependencies** (`gl-matrix`, `lodash`, `ndarray*`): Keep up-to-date unless breaking changes occur
---
## Security Considerations
### Known Issues
1. **`@babel/polyfill` is deprecated** - Should migrate to `core-js@3` + `regenerator-runtime`
2. **Old `mocha` version** - v5.2.0 is from 2018, may have unpatched vulnerabilities
3. **Webpack 4** - No longer receives updates, consider upgrading to Webpack 5
### Monitoring
- Run `npm audit` regularly to check for vulnerabilities
- Use `npm run check-updates` to see available updates
- Check GitHub Dependabot alerts
---
## FAQ
**Q: Why are runtime dependencies in `devDependencies` instead of `dependencies`?**
A: Quagga2 is a **bundled library**. Consumers install the package and use the pre-built files (`dist/quagga.min.js` or `lib/quagga.js`), not the source code. They never run `npm install` on Quagga2's dependencies. Therefore, from npm's perspective, all packages are development dependencies (used during build), not runtime dependencies (used after install).
**Q: How can I tell if a package is actually used in the code?**
A: Search the `src/` directory:
```bash
# Search for imports
grep -r "from 'package-name'" src/
grep -r 'from "package-name"' src/
grep -r "require('package-name')" src/
```
**Q: What's the difference between `optionalDependencies` and `devDependencies`?**
A: `optionalDependencies` are packages that enhance functionality if available but aren't required (like `fsevents` for macOS file watching). `devDependencies` are required for development but not for using the published package.
**Q: Can I remove `@babel/polyfill`?**
A: Yes, but carefully. It's deprecated and not directly imported anymore. Remove it from `package.json` and verify that `@babel/preset-env` is configured to polyfill features automatically via `core-js@3`. Test thoroughly in older browsers (IE11, older Safari) after removal.
**Q: Why can't I upgrade `chai` to version 5 or 6?**
A: `chai@5+` and `chai@6+` are ESM-only (ES modules). Quagga2's tests use CommonJS (`require()`), and `mocha@5` doesn't support ESM. Upgrading `chai` requires also upgrading `mocha` to v9.1.0+ and migrating all test files to ESM syntax.
---
## Related Files
- **`package.json`** - Dependency declarations
- **`.ncurc.json`** - npm-check-updates configuration (blocks unsafe auto-upgrades)
- **`configs/webpack.config.js`** - Build configuration showing which dependencies are bundled
- **`configs/webpack.node.config.js`** - Node.js-specific build configuration
- **`CHANGELOG.md`** - Version history and dependency changes
---
## Maintenance Notes
This document was created in November 2025 following the TypeScript 5.9.3 upgrade. It should be updated whenever:
- A new dependency is added or removed
- A major version upgrade changes behavior
- Security vulnerabilities are discovered and patched
- Build tooling changes significantly
Last updated: 2025-11-16

View File

@@ -0,0 +1,74 @@
# Reference Documentation
Precise technical descriptions of Quagga2's API, configuration, and capabilities. Consult these when you need exact details about how something works.
## Core API
### [API Documentation](api.md)
Complete reference for all Quagga2 methods, callbacks, and events.
### [Configuration Options](configuration.md)
Detailed documentation of every configuration parameter and its effects.
### [Camera Access API](camera-access.md)
Methods for controlling camera access, torch/flash, and device enumeration.
## Barcode Support
### [Supported Barcode Types](readers.md)
List of all supported barcode formats with characteristics and use cases.
## Compatibility
### [Browser Support](browser-support.md)
Browser compatibility matrix and required Web APIs.
## Development
### [Dependencies](dependencies.md)
Explanation of all package dependencies - which are bundled vs. dev-only.
## Quick Lookup
| Need | See |
|------|-----|
| Method signatures | [API Documentation](api.md) |
| Config parameter details | [Configuration Options](configuration.md) |
| Which browsers work | [Browser Support](browser-support.md) |
| Which barcodes supported | [Supported Barcode Types](readers.md) |
| Camera control | [Camera Access API](camera-access.md) |
| Package versions | [Dependencies](dependencies.md) |
## Reading Reference Docs
Reference documentation is:
- **Information-oriented** - Focuses on describing *what exists*
- **Accurate** - Every detail matters, kept up-to-date with code
- **Complete** - Covers all features, even obscure ones
- **Structured** - Organized for lookup, not linear reading
Don't read reference docs cover-to-cover. Instead:
1. **Look up** what you need when you need it
2. **Verify** assumptions about how things work
3. **Discover** features you didn't know existed
4. **Confirm** exact method signatures or parameter types
## Need Context?
Reference docs tell you *what* and *how*, but not *why*:
- For **why** something works a certain way → See [Explanation](../explanation/)
- For **how to use** something in practice → See [How-To Guides](../how-to-guides/)
- For **learning** from scratch → See [Tutorials](../tutorials/)
---
[← Back to Documentation Home](../index.md)

View File

@@ -0,0 +1,326 @@
# Supported Barcode Types {#supported-barcode-types}
Quagga2 supports a wide variety of 1D barcode formats. This page lists all available barcode readers and how to configure them.
## Available Readers {#available-readers}
Quagga2 includes built-in readers for the following barcode formats:
| Reader Name | Barcode Format | Common Uses |
|-------------|----------------|-------------|
| `code_128_reader` | [Code 128](https://en.wikipedia.org/wiki/Code_128), [GS1-128](https://en.wikipedia.org/wiki/GS1-128) | General purpose, shipping, packaging, supply chain |
| `ean_reader` | [EAN-13](https://en.wikipedia.org/wiki/International_Article_Number) | Retail products worldwide |
| `ean_8_reader` | [EAN-8](https://en.wikipedia.org/wiki/EAN-8) | Small retail products |
| `code_39_reader` | [Code 39](https://en.wikipedia.org/wiki/Code_39) | Automotive, defense, healthcare |
| `code_39_vin_reader` | Code 39 VIN | Vehicle Identification Numbers |
| `codabar_reader` | [Codabar](https://en.wikipedia.org/wiki/Codabar) | Libraries, blood banks, logistics |
| `upc_reader` | [UPC-A](https://en.wikipedia.org/wiki/Universal_Product_Code) | Retail products (North America) |
| `upc_e_reader` | [UPC-E](https://en.wikipedia.org/wiki/Universal_Product_Code#UPC-E) | Small retail products |
| `i2of5_reader` | [Interleaved 2 of 5](https://en.wikipedia.org/wiki/Interleaved_2_of_5) | Warehouse, distribution |
| `2of5_reader` | [Standard 2 of 5](https://en.wikipedia.org/wiki/Two-out-of-five_code) | Industrial, airline tickets |
| `code_93_reader` | [Code 93](https://en.wikipedia.org/wiki/Code_93) | Logistics, retail |
| `code_32_reader` | [Code 32](https://en.wikipedia.org/wiki/Pharmacode#Code_32) | Italian pharmaceuticals |
| `pharmacode_reader` | [Pharmacode](https://en.wikipedia.org/wiki/Pharmacode) | Pharmaceutical packaging |
## Basic Configuration {#basic-configuration}
Specify which barcode types to detect in the `decoder.readers` array:
```javascript
Quagga.init({
decoder: {
readers: ["code_128_reader"] // Only detect Code 128
}
}, function(err) {
if (err) {
console.error(err);
return;
}
Quagga.start();
});
```
### Multiple Readers {#multiple-readers}
You can enable multiple readers to detect different barcode types:
```javascript
Quagga.init({
decoder: {
readers: [
"code_128_reader",
"ean_reader",
"upc_reader"
]
}
});
```
## Important Considerations {#important-considerations}
### Reader Priority and Order {#reader-priority-and-order}
**Readers are processed in the exact order they appear in the `readers` array.** The first reader to successfully decode the barcode wins - subsequent readers are not tried.
This allows you to prioritize certain formats over others when multiple formats might match the same barcode pattern:
```javascript
decoder: {
// EAN-13 checked first, then UPC formats
readers: ['ean_reader', 'upc_reader', 'upc_e_reader']
}
```
**Why order matters:**
- Readers are processed sequentially, not in parallel
- Some readers may return false positives for other formats
- Example: EAN-13 and UPC-A/UPC-E share similar patterns and can clash
- The first successful decode is returned immediately
**Best practice**: List your most commonly expected barcode types first for best accuracy and performance.
### Don't Enable All Readers {#dont-enable-all-readers}
**Why not enable all readers by default?**
- More readers = more processing time
- Increased chance of false positives
- Some formats overlap and can interfere
**Best practice**: Only enable the barcode formats you actually need to scan.
### Format Conflicts {#format-conflicts}
Some barcode formats are subsets or extensions of others:
- **UPC-A** is a subset of **EAN-13**
- **EAN-8** is shorter version of **EAN-13**
- **Code 39** and **Code 39 VIN** share similar patterns
Be careful when enabling multiple related formats together.
## GS1-128 Barcodes {#gs1-128-barcodes}
[GS1-128](https://en.wikipedia.org/wiki/GS1-128) (formerly known as EAN-128 or UCC-128) is a subset of Code 128 used extensively in supply chain and logistics. It uses special FNC1 (Function Code 1) characters to separate variable-length data fields called Application Identifiers (AIs).
### How GS1-128 Works {#gs1-128-how-it-works}
GS1-128 barcodes encode structured data using standardized Application Identifiers. For example:
- **AI 01** = GTIN (Global Trade Item Number)
- **AI 10** = Batch/Lot Number
- **AI 17** = Expiration Date
- **AI 21** = Serial Number
The FNC1 character acts as a field separator between variable-length AIs, allowing decoders to know where one field ends and another begins.
### FNC1 Character Handling {#fnc1-character-handling}
When the `code_128_reader` decodes a GS1-128 barcode, FNC1 characters are represented as ASCII 29 (Group Separator, `\x1D` or `\u001d`). This follows the GS1 standard for representing FNC1 in decoded data.
```javascript
Quagga.decodeSingle({
src: 'gs1-128-barcode.jpg',
decoder: {
readers: ['code_128_reader']
}
}, function(result) {
if (result && result.codeResult) {
const code = result.codeResult.code;
// FNC1 characters appear as ASCII 29 (Group Separator)
const GS = String.fromCharCode(29); // '\x1D'
// Split on Group Separator to get individual AI fields
const fields = code.split(GS);
console.log('Fields:', fields);
// Example output: ["", "01034531200000111719050810ABCD1234", ...]
// Or check for GS1-128 format (starts with FNC1)
if (code.startsWith(GS)) {
console.log('This is a GS1-128 barcode');
}
}
});
```
### Parsing GS1-128 Data {#parsing-gs1-128-data}
Once decoded, you can parse the GS1-128 data using the Application Identifier structure:
```javascript
function parseGS1(code) {
const GS = String.fromCharCode(29);
// Remove leading FNC1 if present
const data = code.startsWith(GS) ? code.substring(1) : code;
// Split by FNC1 separator
const segments = data.split(GS);
// Parse each segment for its AI
// (A full implementation would use a complete AI table)
return segments;
}
```
For full GS1 parsing, consider using a dedicated library like [gs1-barcode-parser](https://www.npmjs.com/package/gs1-barcode-parser) after decoding with Quagga2.
## EAN Extensions {#ean-extensions}
### EAN-2 and EAN-5 Supplements {#ean-supplements}
The EAN and UPC barcode formats support a supplement format, adding an additional 2 or 5 digits beyond the main barcode, EAN-2 and EAN-5, respectively. They are typically used for:
- **Magazines and periodicals**: The main barcode identifies the publication, while the supplement denotes issue numbers or publication dates
- **Books with ISBN**: The 5-digit supplement often encodes the suggested retail price
By default, `ean_reader` does not read and decode these extensions, you must explicitly enable support for them, if you are looking for them. Since UPC-A is a subset of EAN-13 -- UPC-A codes are EAN-13 codes that begin with a 0 -- supplement support configured on `ean_reader` also works for UPC-A codes.
To enable supplement decoding:
```javascript
Quagga.init({
decoder: {
readers: [{
format: "ean_reader",
config: {
supplements: [
'ean_5_reader', // 5-digit supplement
'ean_2_reader' // 2-digit supplement
]
}
}]
}
});
```
#### Supplement Result Structure {#supplement-result-structure}
When a barcode with a supplement is decoded, the result includes a `supplement` property:
```javascript
{
codeResult: {
code: "419871600890101", // Combined: main barcode + supplement
format: "ean_13", // Main barcode format
supplement: {
code: "01", // Supplement digits only
format: "ean_2" // "ean_2" or "ean_5"
}
}
}
```
The main `codeResult.code` contains the full combined value, while `codeResult.supplement` provides the supplement details separately.
### Important Notes About Supplements {#supplements-notes}
**Supplement order matters**: The reader stops when it finds the first matching supplement. List `ean_5_reader` before `ean_2_reader` if you want to prioritize 5-digit extensions.
**Cannot read regular EAN-13 with supplements enabled**: If you configure supplements, that reader instance can **only** decode EAN codes **with** supplements. To read both:
```javascript
Quagga.init({
decoder: {
readers: [
"ean_reader", // Regular EAN-13 without supplements
{
format: "ean_reader",
config: {
supplements: ['ean_5_reader', 'ean_2_reader']
}
} // EAN-13 with supplements
]
}
});
```
This configuration creates two separate reader instances.
## External Readers {#external-readers}
Quagga2 supports external reader modules for additional barcode formats not built into the core library.
### QR Code Reader {#qr-code-reader}
For QR code support, see [quagga2-reader-qr](https://github.com/ericblade/quagga2-reader-qr).
External readers extend Quagga2's capabilities beyond 1D barcodes:
```javascript
import Quagga from '@ericblade/quagga2';
import QRReader from 'quagga2-reader-qr';
// Register external reader
Quagga.registerReader('qr', QRReader);
Quagga.init({
decoder: {
readers: ['qr'] // Use external QR reader
}
});
```
### External Reader Priority {#external-reader-priority}
External readers follow the **same priority rules** as built-in readers. Once registered with `Quagga.registerReader()`, an external reader can be placed anywhere in the `readers` array, and its position determines when it attempts to decode relative to other readers:
```javascript
// Register external reader first
Quagga.registerReader('my_custom_reader', MyCustomReader);
// Use in config - position determines priority
Quagga.init({
decoder: {
// External reader tried first, then built-in readers
readers: ['my_custom_reader', 'ean_reader', 'code_128_reader']
}
});
```
**Key points:**
- External readers must be registered via `registerReader()` before use
- Their position in `readers` array determines decode priority
- There is no automatic "internal first, external second" ordering
- External readers interleave freely with built-in readers
### Creating Custom Readers {#creating-custom-readers}
You can create your own barcode reader implementations by extending the `BarcodeReader` prototype exported by Quagga2. See [How-To: Create External Readers](../how-to-guides/external-readers.md) for details.
## Reader Performance {#reader-performance}
Different readers have different performance characteristics:
**Fastest readers**:
- `code_128_reader` - Optimized, widely used
- `ean_reader` - Fast and reliable
**Slower readers**:
- `code_39_reader` - More complex pattern
- `i2of5_reader` - Requires more validation
**Resource intensive**:
- Multiple readers enabled simultaneously
- Readers with supplements configured
## Validation {#validation}
Some barcode formats include check digits for validation:
- **EAN-13/EAN-8**: Built-in check digit
- **Code 128**: Built-in check digit
- **UPC-A/UPC-E**: Built-in check digit
For additional validation in your application, consider using [barcode-validator](https://github.com/ericblade/barcode-validator).
## Related {#related}
- [Configuration Reference](configuration.md) - Complete config options
- [API Documentation](api.md) - How to use Quagga2 methods
- [Tips & Tricks](../how-to-guides/tips-and-tricks.md) - Handling false positives
---
[← Back to Reference](index.md)