forked from mebjas/html5-qrcode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
native-bar-code-detector.ts
198 lines (178 loc) · 6.72 KB
/
native-bar-code-detector.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
* @fileoverview
* {@interface QrcodeDecoder} wrapper around experimental BarcodeDetector API.
*
* @author mebjas <[email protected]>
*
* Read more about the experimental feature here:
* https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import {
QrcodeResult,
QrcodeResultFormat,
Html5QrcodeSupportedFormats,
QrcodeDecoderAsync,
Logger
} from "./core";
declare const BarcodeDetector: any;
/** Config for BarcodeDetector API. */
interface BarcodeDetectorConfig {
formats: Array<string>;
}
/**
* Interface for BarcodeDetector result.
*
* Forked from
* https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector#methods
*/
interface BarcodeDetectorResult {
/**
* A DOMRectReadOnly, which returns the dimensions of a rectangle
* representing the extent of a detected barcode, aligned with the image.
*/
boundingBox: DOMRectReadOnly;
/**
* The x and y co-ordinates of the four corner points of the detected
* barcode relative to the image, starting with the top left and working
* clockwise. This may not be square due to perspective distortions within
* the image.
*/
cornerPoints: any;
/**
* The detected barcode format.
*/
format: string;
/**
* A String decoded from the barcode data.
*/
rawValue: string;
}
/**
* ZXing based Code decoder.
*/
export class BarcodeDetectorDelegate implements QrcodeDecoderAsync {
// All formats defined here
// https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API#supported_barcode_formats
private readonly formatMap: Map<Html5QrcodeSupportedFormats, string>
= new Map([
[ Html5QrcodeSupportedFormats.QR_CODE, "qr_code" ],
[ Html5QrcodeSupportedFormats.AZTEC, "aztec" ],
[ Html5QrcodeSupportedFormats.CODABAR, "codabar" ],
[ Html5QrcodeSupportedFormats.CODE_39, "code_39" ],
[ Html5QrcodeSupportedFormats.CODE_93, "code_93" ],
[ Html5QrcodeSupportedFormats.CODE_128, "code_128" ],
[ Html5QrcodeSupportedFormats.DATA_MATRIX, "data_matrix" ],
[ Html5QrcodeSupportedFormats.ITF, "itf" ],
[ Html5QrcodeSupportedFormats.EAN_13, "ean_13" ],
[ Html5QrcodeSupportedFormats.EAN_8, "ean_8" ],
[ Html5QrcodeSupportedFormats.PDF_417, "pdf417" ],
[ Html5QrcodeSupportedFormats.UPC_A, "upc_a" ],
[ Html5QrcodeSupportedFormats.UPC_E, "upc_e" ]
]);
private readonly reverseFormatMap: Map<string, Html5QrcodeSupportedFormats>
= this.createReverseFormatMap();
private verbose: boolean;
private logger: Logger;
private detector: any;
/**
* Returns true if this API is supported by the browser.
*
* TODO(mebjas): Add checks like this
* https://web.dev/shape-detection/#featuredetection
* TODO(mebjas): Check for format supported by the BarcodeDetector using
* getSupportedFormats() API.
* @returns
*/
public static isSupported(): boolean {
if (!("BarcodeDetector" in window)) {
return false;
}
const dummyDetector = new BarcodeDetector({formats: [ "qr_code" ]});
return typeof dummyDetector !== "undefined";
}
public constructor(
requestedFormats: Array<Html5QrcodeSupportedFormats>,
verbose: boolean,
logger: Logger) {
if (!BarcodeDetectorDelegate.isSupported()) {
throw "Use html5qrcode.min.js without edit, Use "
+ "BarcodeDetectorDelegate only if it isSupported();";
}
this.verbose = verbose;
this.logger = logger;
// create new detector
const formats = this.createBarcodeDetectorFormats(requestedFormats);
this.detector = new BarcodeDetector(formats);
// check compatibility
if (!this.detector) {
throw "BarcodeDetector detector not supported";
}
}
async decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult> {
const barcodes: Array<BarcodeDetectorResult>
= await this.detector.detect(canvas);
if (!barcodes || barcodes.length === 0) {
throw "No barcode or QR code detected.";
}
// TODO(mebjas): Today BarcodeDetector library seems to be returning
// mutliple barcodes if supported. But the documentation around it is
// not the best. As of now, we are returning just the largest code
// found. In future it'd be desriable to return mutli codes if supported
// and found.
let largestBarcode = this.selectLargestBarcode(barcodes);
return {
text: largestBarcode.rawValue,
format: QrcodeResultFormat.create(
this.toHtml5QrcodeSupportedFormats(largestBarcode.format))
};
}
private selectLargestBarcode(barcodes: Array<BarcodeDetectorResult>)
: BarcodeDetectorResult {
let largestBarcode: BarcodeDetectorResult | null = null;
let maxArea = 0;
for (let barcode of barcodes) {
let area = barcode.boundingBox.width * barcode.boundingBox.height;
if (area > maxArea) {
maxArea = area;
largestBarcode = barcode;
}
}
if (!largestBarcode) {
throw "No largest barcode found";
}
return largestBarcode!;
}
private createBarcodeDetectorFormats(
requestedFormats: Array<Html5QrcodeSupportedFormats>):
BarcodeDetectorConfig {
let formats: Array<string> = [];
for (const requestedFormat of requestedFormats) {
if (this.formatMap.has(requestedFormat)) {
formats.push(
this.formatMap.get(requestedFormat)!);
} else {
this.logger.warn(`${requestedFormat} is not supported by`
+ "BarcodeDetectorDelegate");
}
}
return { formats: formats };
}
private toHtml5QrcodeSupportedFormats(barcodeDetectorFormat: string)
: Html5QrcodeSupportedFormats {
if (!this.reverseFormatMap.has(barcodeDetectorFormat)) {
throw `reverseFormatMap doesn't have ${barcodeDetectorFormat}`;
}
return this.reverseFormatMap.get(barcodeDetectorFormat)!;
}
private createReverseFormatMap(): Map<string, Html5QrcodeSupportedFormats> {
let result = new Map();
this.formatMap.forEach(
(value: string, key: Html5QrcodeSupportedFormats, _) => {
result.set(value, key);
});
return result;
}
}