[REF] web: extract ZXingBarcodeDetector into dedicated file

Part-of: odoo/odoo#112855
This commit is contained in:
Romeo Fragomeli
2023-02-17 12:07:20 +01:00
parent 79a87e854c
commit 2cf9a8a479
2 changed files with 120 additions and 117 deletions
@@ -0,0 +1,119 @@
/** @odoo-module **/
/**
* Builder for BarcodeDetector-like polyfill class using ZXing library.
*
* @param {ZXing} ZXing Zxing library
* @returns {class} ZxingBarcodeDetector class
*/
export function buildZXingBarcodeDetector(ZXing) {
const ZXingFormats = new Map([
["aztec", ZXing.BarcodeFormat.AZTEC],
["code_39", ZXing.BarcodeFormat.CODE_39],
["code_128", ZXing.BarcodeFormat.CODE_128],
["data_matrix", ZXing.BarcodeFormat.DATA_MATRIX],
["ean_8", ZXing.BarcodeFormat.EAN_8],
["ean_13", ZXing.BarcodeFormat.EAN_13],
["itf", ZXing.BarcodeFormat.ITF],
["pdf417", ZXing.BarcodeFormat.PDF_417],
["qr_code", ZXing.BarcodeFormat.QR_CODE],
["upc_a", ZXing.BarcodeFormat.UPC_A],
["upc_e", ZXing.BarcodeFormat.UPC_E],
]);
const allSupportedFormats = Array.from(ZXingFormats.keys());
/**
* ZXingBarcodeDetector class
*
* BarcodeDetector-like polyfill class using ZXing library.
* API follows the Shape Detection Web API (specifically Barcode Detection).
*/
class ZXingBarcodeDetector {
/**
* @param {object} opts
* @param {Array} opts.formats list of codes' formats to detect
*/
constructor(opts = {}) {
const formats = opts.formats || allSupportedFormats;
const hints = new Map([
[
ZXing.DecodeHintType.POSSIBLE_FORMATS,
formats.map((format) => ZXingFormats.get(format)),
],
// Enable Scanning at 90 degrees rotation
// https://github.com/zxing-js/library/issues/291
[ZXing.DecodeHintType.TRY_HARDER, true],
]);
this.reader = new ZXing.MultiFormatReader();
this.reader.setHints(hints);
}
/**
* Detect codes in image.
*
* @param {HTMLVideoElement} video source video element
* @returns {Promise<Array>} array of detected codes
*/
async detect(video) {
if (!(video instanceof HTMLVideoElement)) {
throw new DOMException(
"imageDataFrom() requires an HTMLVideoElement",
"InvalidArgumentError"
);
}
if (!isVideoElementReady(video)) {
throw new DOMException("HTMLVideoElement is not ready", "InvalidStateError");
}
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
const luminanceSource = new ZXing.HTMLCanvasElementLuminanceSource(canvas);
const binaryBitmap = new ZXing.BinaryBitmap(new ZXing.HybridBinarizer(luminanceSource));
try {
const result = this.reader.decode(binaryBitmap);
const format = Array.from(ZXingFormats).find(
([k, val]) => val === result.getBarcodeFormat()
);
const rawValue = result.getText();
return [
{
format,
rawValue,
},
];
} catch (err) {
if (err.name === "NotFoundException") {
return [];
}
throw err;
}
}
}
/**
* Supported codes formats
*
* @static
* @returns {Promise<string[]>}
*/
ZXingBarcodeDetector.getSupportedFormats = async () => allSupportedFormats;
return ZXingBarcodeDetector;
}
/**
* Check for HTMLVideoElement readiness.
*
* See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
*/
const HAVE_NOTHING = 0;
const HAVE_METADATA = 1;
export function isVideoElementReady(video) {
return ![HAVE_NOTHING, HAVE_METADATA].includes(video.readyState);
}
@@ -5,6 +5,7 @@ import { browser } from "@web/core/browser/browser";
import Dialog from "web.OwlDialog";
import { delay } from "web.concurrency";
import { loadJS, templates } from "@web/core/assets";
import { isVideoElementReady, buildZXingBarcodeDetector } from "./ZXingBarcodeDetector";
import { App, Component, EventBus, onMounted, onWillStart, onWillUnmount, useRef } from "@odoo/owl";
import { _t } from "web.core";
@@ -12,123 +13,6 @@ const bus = new EventBus();
const busOk = "BarcodeDialog-Ok";
const busError = "BarcodeDialog-Error";
/**
* Check for HTMLVideoElement readiness.
*
* See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
*/
const HAVE_NOTHING = 0;
const HAVE_METADATA = 1;
function isVideoElementReady(video) {
return ![HAVE_NOTHING, HAVE_METADATA].includes(video.readyState);
}
/**
* Builder for BarcodeDetector-like polyfill class using ZXing library.
*
* @param {ZXing} ZXing Zxing library
* @returns {class} ZxingBarcodeDetector class
*/
function buildZXingBarcodeDetector(ZXing) {
const ZXingFormats = new Map([
["aztec", ZXing.BarcodeFormat.AZTEC],
["code_39", ZXing.BarcodeFormat.CODE_39],
["code_128", ZXing.BarcodeFormat.CODE_128],
["data_matrix", ZXing.BarcodeFormat.DATA_MATRIX],
["ean_8", ZXing.BarcodeFormat.EAN_8],
["ean_13", ZXing.BarcodeFormat.EAN_13],
["itf", ZXing.BarcodeFormat.ITF],
["pdf417", ZXing.BarcodeFormat.PDF_417],
["qr_code", ZXing.BarcodeFormat.QR_CODE],
["upc_a", ZXing.BarcodeFormat.UPC_A],
["upc_e", ZXing.BarcodeFormat.UPC_E],
]);
const allSupportedFormats = Array.from(ZXingFormats.keys());
/**
* ZXingBarcodeDetector class
*
* BarcodeDetector-like polyfill class using ZXing library.
* API follows the Shape Detection Web API (specifically Barcode Detection).
*/
class ZXingBarcodeDetector {
/**
* @param {object} opts
* @param {Array} opts.formats list of codes' formats to detect
*/
constructor(opts = {}) {
const formats = opts.formats || allSupportedFormats;
const hints = new Map([
[
ZXing.DecodeHintType.POSSIBLE_FORMATS,
formats.map((format) => ZXingFormats.get(format)),
],
// Enable Scanning at 90 degrees rotation
// https://github.com/zxing-js/library/issues/291
[ZXing.DecodeHintType.TRY_HARDER, true],
]);
this.reader = new ZXing.MultiFormatReader();
this.reader.setHints(hints);
}
/**
* Detect codes in image.
*
* @param {HTMLVideoElement} video source video element
* @returns {Promise<Array>} array of detected codes
*/
async detect(video) {
if (!(video instanceof HTMLVideoElement)) {
throw new DOMException(
"imageDataFrom() requires an HTMLVideoElement",
"InvalidArgumentError"
);
}
if (!isVideoElementReady(video)) {
throw new DOMException("HTMLVideoElement is not ready", "InvalidStateError");
}
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
const luminanceSource = new ZXing.HTMLCanvasElementLuminanceSource(canvas);
const binaryBitmap = new ZXing.BinaryBitmap(new ZXing.HybridBinarizer(luminanceSource));
try {
const result = this.reader.decode(binaryBitmap);
const format = Array.from(ZXingFormats).find(
([k, val]) => val === result.getBarcodeFormat()
);
const rawValue = result.getText();
return [
{
format,
rawValue,
},
];
} catch (err) {
if (err.name === "NotFoundException") {
return [];
}
throw err;
}
}
}
/**
* Supported codes formats
*
* @static
* @returns {Array}
*/
ZXingBarcodeDetector.getSupportedFormats = async () => allSupportedFormats;
return ZXingBarcodeDetector;
}
class BarcodeDialog extends Component {
/**
* @override