From 2cf9a8a479defdefc92c287b74063ca1101ae1c4 Mon Sep 17 00:00:00 2001 From: Romeo Fragomeli Date: Fri, 10 Feb 2023 10:35:16 +0000 Subject: [PATCH] [REF] web: extract ZXingBarcodeDetector into dedicated file Part-of: odoo/odoo#112855 --- .../webclient/barcode/ZXingBarcodeDetector.js | 119 ++++++++++++++++++ .../src/webclient/barcode/barcode_scanner.js | 118 +---------------- 2 files changed, 120 insertions(+), 117 deletions(-) create mode 100644 addons/web/static/src/webclient/barcode/ZXingBarcodeDetector.js diff --git a/addons/web/static/src/webclient/barcode/ZXingBarcodeDetector.js b/addons/web/static/src/webclient/barcode/ZXingBarcodeDetector.js new file mode 100644 index 00000000000..caf14df271c --- /dev/null +++ b/addons/web/static/src/webclient/barcode/ZXingBarcodeDetector.js @@ -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 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} + */ + 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); +} diff --git a/addons/web/static/src/webclient/barcode/barcode_scanner.js b/addons/web/static/src/webclient/barcode/barcode_scanner.js index 9c3c5a437b1..f4eb4e01d7c 100644 --- a/addons/web/static/src/webclient/barcode/barcode_scanner.js +++ b/addons/web/static/src/webclient/barcode/barcode_scanner.js @@ -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 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