UNPKG

26.5 kBPlain TextView Raw
1import { BarcodeWASMResult } from "../barcode";
2import { ImageSettings } from "../imageSettings";
3import { Parser } from "../parser";
4
5// WARNING
6// ==========
7// The "engine" function is extracted and executed in isolation as a WebWorker in the browser.
8// We currently cannot use too advanced language features here as the code will not get transformed/polyfilled correctly
9// by Rollup and Babel as it might refer to other externally defined variables/functions.
10// This means we also cannot import and use variables from the rest of the project.
11// The used language features should be compatible with (supported by) the browsers mentioned in the documentation.
12// See rollup.config.js and .browserslistrc.worker for more details.
13// TODO: This should be fixed...
14
15// tslint:disable:no-any
16
17/**
18 * @hidden
19 */
20declare const self: any;
21/**
22 * @hidden
23 */
24declare const importScripts: (...urls: string[]) => Promise<void> | undefined; // Promise is used only during testing
25/**
26 * @hidden
27 */
28declare const postMessage: (message: any, transfer?: any[]) => void;
29/**
30 * @hidden
31 *
32 * Defined here as we cannot use too recent typescript type definitions...
33 */
34declare namespace WebAssembly {
35 interface Instance {
36 readonly exports: any;
37 }
38
39 interface WebAssemblyInstantiatedSource {
40 instance: Instance;
41 // tslint:disable-next-line:no-reserved-keywords
42 module: {};
43 }
44}
45
46/**
47 * @hidden
48 */
49type FileSystemType = {};
50
51/**
52 * @hidden
53 */
54export declare type Module = {
55 HEAPU8: Uint8Array;
56 lengthBytesUTF8(str: string): number;
57 UTF8ToString(ptr: number): string;
58 stringToUTF8(str: string, outPtr: number, maxBytesToWrite: number): void;
59 _malloc(size: number): number;
60 _free(ptr: number): void;
61 _create_context(ptr: number, debug: boolean): void;
62 _scanner_settings_new_from_json(
63 ptr: number,
64 blurryDecodingEnabled: boolean,
65 matrixScanEnabled: boolean,
66 highQualitySingleFrameMode: boolean,
67 gpuEnabled: boolean
68 ): number;
69 _scanner_image_settings_new(width: number, height: number, channels: number): void;
70 _scanner_session_clear(): void;
71 _can_hide_logo(): number;
72 _scanner_scan(ptr: number): number;
73 _parser_parse_string(parserType: number, ptr: number, stringDataLength: number, ptr2: number): number;
74 canvas(): HTMLCanvasElement;
75 instantiateWasm(importObject: object, successCallback: (instance: WebAssembly.Instance) => void): void;
76 preRun(): void;
77 onRuntimeInitialized(): void;
78 callMain(): void;
79};
80
81/**
82 * @hidden
83 */
84declare let Module: Module;
85
86/**
87 * @hidden
88 */
89declare namespace FS {
90 function syncfs(populate: boolean, callback: (e: any) => any): void;
91 function mount(fsType: FileSystemType, opts: any, mountpoint: string): any;
92 function mkdir(path: string, mode?: number): any;
93}
94
95/**
96 * @hidden
97 */
98declare const IDBFS: FileSystemType;
99
100// tslint:enable:no-any
101
102/**
103 * @hidden
104 */
105declare type ScanWorkUnit = {
106 requestId: number;
107 data: Uint8ClampedArray;
108 highQualitySingleFrameMode: boolean;
109};
110
111/**
112 * @hidden
113 */
114declare type ParseWorkUnit = {
115 requestId: number;
116 dataFormat: Parser.DataFormat;
117 dataString: string;
118 options: string;
119};
120
121/**
122 * @hidden
123 */
124export declare type Engine = {
125 loadLibrary(
126 deviceId: string,
127 libraryLocation: string,
128 locationPath: string,
129 deviceModelName: string | undefined,
130 uaBrowserName: string | undefined
131 ): Promise<void>;
132 createContext(newLicenseKey: string): void;
133 setSettings(newSettings: string): void;
134 setImageSettings(newImageSettings: ImageSettings): void;
135 workOnScanQueue(): void;
136 workOnParseQueue(): void;
137 addScanWorkUnit(scanWorkUnit: ScanWorkUnit): void;
138 addParseWorkUnit(parseWorkUnit: ParseWorkUnit): void;
139 clearSession(): void;
140};
141
142/**
143 * @hidden
144 * @returns Engine
145 */
146// tslint:disable-next-line:max-func-body-length
147export function engine(): Engine {
148 const scanQueue: ScanWorkUnit[] = [];
149 const parseQueue: ParseWorkUnit[] = [];
150 const gpuAccelerationAvailable: boolean = typeof self.OffscreenCanvas === "function";
151
152 let browserName: string | undefined;
153 let imageBufferPointer: number | undefined;
154 let licenseKey: string;
155 let settings: string;
156 let imageSettings: ImageSettings;
157 let scanWorkSubmitted: boolean = false;
158 let fileSystemSynced: boolean = false;
159 let runtimeLoaded: boolean = false;
160 let wasmReady: boolean = false;
161 let scannerSettingsReady: boolean = false;
162 let scannerImageSettingsReady: boolean = false;
163 let contextAvailable: boolean = false;
164 let fsSyncInProgress: boolean | undefined;
165 let fsSyncScheduled: boolean = false;
166
167 // Public
168
169 // Promise is used only during testing
170 function loadLibrary(
171 deviceId: string,
172 libraryLocation: string,
173 locationPath: string,
174 deviceModelName: string | undefined,
175 uaBrowserName: string | undefined
176 ): Promise<void> {
177 function start(): void {
178 if (!wasmReady && fileSystemSynced && runtimeLoaded) {
179 wasmReady = true;
180 Module.callMain();
181 postMessage(["status", "ready"]);
182 workOnScanQueue();
183 workOnParseQueue();
184 }
185 }
186
187 const { jsURI, wasmURI } = getLibraryLocationURIs(libraryLocation);
188 Module = <Module>(<unknown>{
189 arguments: [deviceId],
190 canvas: gpuAccelerationAvailable ? new self.OffscreenCanvas(32, 32) : /* istanbul ignore next */ undefined,
191 instantiateWasm: (importObject: object, successCallback: (instance: WebAssembly.Instance) => void) => {
192 // wasmJSVersion is globally defined inside scandit-engine-sdk.min.js
193 let wasmJSVersion: string = self.wasmJSVersion;
194 // istanbul ignore if
195 if (wasmJSVersion == null) {
196 wasmJSVersion = "undefined";
197 }
198 // istanbul ignore if
199 if (wasmJSVersion !== "%VER%") {
200 console.error(
201 `The Scandit SDK Engine library JS file found at ${jsURI} seems invalid: ` +
202 `expected version doesn't match (received: ${wasmJSVersion}, expected: ${"%VER%"}). ` +
203 `Please ensure the correct Scandit SDK Engine file (with correct version) is retrieved.`
204 );
205 }
206
207 if (typeof self.WebAssembly.instantiateStreaming === "function") {
208 instantiateWebAssemblyStreaming(importObject, wasmURI, successCallback);
209 } else {
210 instantiateWebAssembly(importObject, wasmURI, successCallback);
211 }
212
213 return {};
214 },
215 noInitialRun: true,
216 preRun: () => {
217 try {
218 FS.mkdir("/scandit_sync_folder");
219 } catch (error) {
220 // istanbul ignore next
221 if (error.code !== "EEXIST") {
222 throw error;
223 }
224 }
225 FS.mount(IDBFS, {}, "/scandit_sync_folder");
226 FS.syncfs(true, () => {
227 fileSystemSynced = true;
228 start();
229 });
230 },
231 onRuntimeInitialized: () => {
232 runtimeLoaded = true;
233 start();
234 }
235 });
236 browserName = uaBrowserName;
237 self.window = self.document = self; // Fix some Emscripten quirks
238 self.path = locationPath; // Used by the Scandit SDK Engine library
239 self.deviceModelName = deviceModelName; // Used by the Scandit SDK Engine library
240
241 function tryImportScripts(): Promise<void> {
242 try {
243 const importScriptsResults: Promise<void> | undefined = importScripts(jsURI);
244 // istanbul ignore else
245 if (importScriptsResults != null) {
246 return importScriptsResults;
247 } else {
248 return Promise.resolve();
249 }
250 } catch (error) {
251 return Promise.reject(error);
252 }
253 }
254
255 return retryWithExponentialBackoff(tryImportScripts, 250, 4000, error => {
256 console.warn(error);
257 console.warn(`Couldn't retrieve Scandit SDK Engine library at ${jsURI}, retrying...`);
258 }).catch(error => {
259 console.error(error);
260 console.error(
261 `Couldn't retrieve Scandit SDK Engine library at ${jsURI}, did you configure the path for it correctly?`
262 );
263
264 return Promise.resolve(error); // Promise is used only during testing
265 });
266 }
267
268 function createContext(newLicenseKey: string): void {
269 licenseKey = newLicenseKey;
270 if (contextAvailable || licenseKey == null || !wasmReady) {
271 return;
272 }
273
274 const licenseKeyLength: number = Module.lengthBytesUTF8(licenseKey) + 1;
275 const licenseKeyPointer: number = Module._malloc(licenseKeyLength);
276 Module.stringToUTF8(licenseKey, licenseKeyPointer, licenseKeyLength);
277 Module._create_context(licenseKeyPointer, false);
278 Module._free(licenseKeyPointer);
279
280 contextAvailable = true;
281
282 postMessage([
283 "license-features",
284 {
285 hiddenScanditLogoAllowed: Module._can_hide_logo() === 1
286 }
287 ]);
288 }
289
290 function setSettings(newSettings: string): void {
291 settings = newSettings;
292 applySettings();
293 }
294
295 function setImageSettings(newImageSettings: ImageSettings): void {
296 imageSettings = newImageSettings;
297 applyImageSettings();
298 }
299
300 function augmentErrorInformation(error: { errorCode: number; errorMessage: string }): void {
301 if (error.errorCode === 260) {
302 let hostname: string;
303 // istanbul ignore if
304 if (location.href != null && location.href.indexOf("blob:null/") === 0) {
305 hostname = "localhost";
306 } else {
307 hostname = new URL(
308 location.pathname != null && location.pathname !== "" && !location.pathname.startsWith("/")
309 ? /* istanbul ignore next */ location.pathname
310 : location.origin
311 ).hostname;
312 }
313 // istanbul ignore next
314 if (hostname[0].startsWith("[") && hostname.endsWith("]")) {
315 hostname = hostname.slice(1, -1);
316 }
317 error.errorMessage = error.errorMessage.replace("domain name", `domain name (${hostname})`);
318 }
319 }
320
321 function processScanWorkUnit(currentScanWorkUnit: ScanWorkUnit): void {
322 if (currentScanWorkUnit.highQualitySingleFrameMode) {
323 applySettings(true);
324 }
325 const resultData: string = scanImage(currentScanWorkUnit.data);
326 if (currentScanWorkUnit.highQualitySingleFrameMode) {
327 applySettings(false);
328 }
329 const result: {
330 scanResult?: BarcodeWASMResult[];
331 error?: { errorCode: number; errorMessage: string };
332 } = JSON.parse(resultData);
333 // Important! We transfer data back even if we don't use it on the receiving end on Firefox.
334 // Not doing so can result in memory and stability issues.
335 // https://developer.mozilla.org/en-US/docs/Web/API/Transferable
336 // https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage
337 const postMessageTransfer: Transferable[] | undefined =
338 browserName === "Firefox" ? [currentScanWorkUnit.data.buffer] : undefined;
339 if (result.error != null) {
340 augmentErrorInformation(result.error);
341 postMessage(
342 [
343 "work-error",
344 {
345 requestId: currentScanWorkUnit.requestId,
346 error: result.error
347 }
348 ],
349 postMessageTransfer
350 );
351 } else {
352 // istanbul ignore else
353 if (result.scanResult != null) {
354 if (result.scanResult.length > 0 || fsSyncInProgress == null) {
355 syncFS();
356 }
357 postMessage(
358 [
359 "work-result",
360 {
361 requestId: currentScanWorkUnit.requestId,
362 result
363 }
364 ],
365 postMessageTransfer
366 );
367 } else {
368 console.error("Unrecognized Scandit Engine result:", result);
369 postMessage([""], postMessageTransfer);
370 }
371 }
372 }
373
374 function workOnScanQueue(): void {
375 if ((!scannerSettingsReady || !scannerImageSettingsReady) && scanQueue.length !== 0) {
376 // First submitted work unit
377 createContext(licenseKey);
378 applySettings();
379 applyImageSettings();
380 }
381
382 if (!scannerSettingsReady || !scannerImageSettingsReady || scanQueue.length === 0) {
383 return;
384 }
385
386 while (scanQueue.length !== 0) {
387 processScanWorkUnit(<ScanWorkUnit>scanQueue.shift());
388 }
389 }
390
391 function processParseWorkUnit(parseWorkUnit: ParseWorkUnit): void {
392 const resultData: string = parseString(parseWorkUnit.dataFormat, parseWorkUnit.dataString, parseWorkUnit.options);
393 const result: { result?: string; error?: { errorCode: number; errorMessage: string } } = JSON.parse(resultData);
394 if (result.error != null) {
395 augmentErrorInformation(result.error);
396 postMessage([
397 "parse-string-error",
398 {
399 requestId: parseWorkUnit.requestId,
400 error: result.error
401 }
402 ]);
403 } else {
404 // istanbul ignore else
405 if (result.result != null) {
406 postMessage([
407 "parse-string-result",
408 {
409 requestId: parseWorkUnit.requestId,
410 result: result.result
411 }
412 ]);
413 } else {
414 console.error("Unrecognized Scandit Parser result:", result);
415 postMessage([
416 "parse-string-error",
417 {
418 requestId: parseWorkUnit.requestId,
419 error: {
420 errorCode: -1,
421 errorMessage: "Unknown Scandit Parser error"
422 }
423 }
424 ]);
425 }
426 }
427 }
428
429 function workOnParseQueue(): void {
430 if (!contextAvailable && parseQueue.length !== 0) {
431 // First submitted work unit
432 createContext(licenseKey);
433 }
434
435 if (!contextAvailable || !wasmReady || parseQueue.length === 0) {
436 return;
437 }
438
439 while (parseQueue.length !== 0) {
440 processParseWorkUnit(<ParseWorkUnit>parseQueue.shift());
441 }
442
443 syncFS();
444 }
445
446 function addScanWorkUnit(scanWorkUnit: ScanWorkUnit): void {
447 scanWorkSubmitted = true;
448 scanQueue.push(scanWorkUnit);
449 workOnScanQueue();
450 }
451
452 function addParseWorkUnit(parseWorkUnit: ParseWorkUnit): void {
453 parseQueue.push(parseWorkUnit);
454 workOnParseQueue();
455 }
456
457 function clearSession(): void {
458 if (scannerSettingsReady) {
459 Module._scanner_session_clear();
460 }
461 }
462
463 // Private
464
465 function retryWithExponentialBackoff<T>(
466 handler: () => Promise<T>,
467 backoffMs: number,
468 maxBackoffMs: number,
469 singleTryRejectionCallback: (error: Error) => void
470 ): Promise<T> {
471 return new Promise((resolve, reject) => {
472 handler()
473 .then(resolve)
474 .catch(error => {
475 const newBackoffMs: number = backoffMs * 2;
476 if (newBackoffMs > maxBackoffMs) {
477 return reject(error);
478 }
479 singleTryRejectionCallback(error);
480 setTimeout(() => {
481 retryWithExponentialBackoff(handler, newBackoffMs, maxBackoffMs, singleTryRejectionCallback)
482 .then(resolve)
483 .catch(reject);
484 }, backoffMs);
485 });
486 });
487 }
488
489 function getLibraryLocationURIs(libraryLocation: string): { jsURI: string; wasmURI: string } {
490 let cdnURI: boolean = false;
491
492 if (/^https?:\/\/([^\/.]*\.)*cdn.jsdelivr.net\//.test(libraryLocation)) {
493 libraryLocation = "https://cdn.jsdelivr.net/npm/scandit-sdk@%VER%/build/";
494 cdnURI = true;
495 } else if (/^https?:\/\/([^\/.]*\.)*unpkg.com\//.test(libraryLocation)) {
496 libraryLocation = "https://unpkg.com/scandit-sdk@%VER%/build/";
497 cdnURI = true;
498 }
499
500 if (cdnURI) {
501 return {
502 jsURI: `${libraryLocation}scandit-engine-sdk.min.js`,
503 wasmURI: `${libraryLocation}scandit-engine-sdk.wasm`
504 };
505 }
506
507 return {
508 jsURI: `${libraryLocation}scandit-engine-sdk.min.js?v=%VER%`,
509 wasmURI: `${libraryLocation}scandit-engine-sdk.wasm?v=%VER%`
510 };
511 }
512
513 function arrayBufferToHexString(arrayBuffer: ArrayBuffer): string {
514 return Array.from(new Uint8Array(arrayBuffer))
515 .map(byteNumber => {
516 const byteHex: string = byteNumber.toString(16);
517
518 return byteHex.length === 1 ? /* istanbul ignore next */ `0${byteHex}` : byteHex;
519 })
520 .join("");
521 }
522
523 function applySettings(highQualitySingleFrameMode: boolean = false): void {
524 if (settings == null || !contextAvailable || !wasmReady || !scanWorkSubmitted) {
525 return;
526 }
527
528 scannerSettingsReady = false;
529
530 const parsedSettings: {
531 matrixScanEnabled: boolean;
532 gpuAcceleration: boolean;
533 blurryRecognition: boolean;
534 } = JSON.parse(settings);
535 const settingsLength: number = Module.lengthBytesUTF8(settings) + 1;
536 const settingsPointer: number = Module._malloc(settingsLength);
537 Module.stringToUTF8(settings, settingsPointer, settingsLength);
538 const resultPointer: number = Module._scanner_settings_new_from_json(
539 settingsPointer,
540 parsedSettings.blurryRecognition,
541 parsedSettings.matrixScanEnabled,
542 highQualitySingleFrameMode,
543 gpuAccelerationAvailable && parsedSettings.gpuAcceleration
544 );
545 Module._free(settingsPointer);
546
547 const result: string = Module.UTF8ToString(resultPointer);
548 if (result !== "") {
549 scannerSettingsReady = true;
550 console.debug(JSON.parse(result));
551 }
552 }
553
554 function applyImageSettings(): void {
555 if (imageSettings == null || !wasmReady || !scanWorkSubmitted) {
556 return;
557 }
558
559 scannerImageSettingsReady = false;
560
561 let channels: number;
562 // TODO: For now it's not possible to use imported variables as the worker doesn't have access at runtime
563 if (imageSettings.format.valueOf() === 1) {
564 // RGB_8U
565 channels = 3;
566 } else if (imageSettings.format.valueOf() === 2) {
567 // RGBA_8U
568 channels = 4;
569 } else {
570 // GRAY_8U
571 channels = 1;
572 }
573 Module._scanner_image_settings_new(imageSettings.width, imageSettings.height, channels);
574 if (imageBufferPointer != null) {
575 Module._free(imageBufferPointer);
576 imageBufferPointer = undefined;
577 }
578 imageBufferPointer = Module._malloc(imageSettings.width * imageSettings.height * channels);
579
580 scannerImageSettingsReady = true;
581 }
582
583 function scanImage(imageData: Uint8ClampedArray): string {
584 Module.HEAPU8.set(imageData, imageBufferPointer);
585
586 return Module.UTF8ToString(Module._scanner_scan(<number>imageBufferPointer));
587 }
588
589 function parseString(dataFormat: Parser.DataFormat, dataString: string, options: string): string {
590 const dataStringLength: number = Module.lengthBytesUTF8(dataString) + 1;
591 const dataStringPointer: number = Module._malloc(dataStringLength);
592 Module.stringToUTF8(dataString, dataStringPointer, dataStringLength);
593 const optionsLength: number = Module.lengthBytesUTF8(options) + 1;
594 const optionsPointer: number = Module._malloc(optionsLength);
595 Module.stringToUTF8(options, optionsPointer, optionsLength);
596 const resultPointer: number = Module._parser_parse_string(
597 dataFormat.valueOf(),
598 dataStringPointer,
599 dataStringLength - 1,
600 optionsPointer
601 );
602 Module._free(dataStringPointer);
603 Module._free(optionsPointer);
604
605 return Module.UTF8ToString(resultPointer);
606 }
607
608 function verifiedWasmFetch(wasmURI: string, awaitFullResponse: boolean): Promise<Response> {
609 function verifyResponseData(responseData: ArrayBuffer): void {
610 // istanbul ignore else
611 if (crypto.subtle != null && typeof crypto.subtle === "object" && typeof crypto.subtle.digest === "function") {
612 crypto.subtle.digest("SHA-256", responseData).then(hash => {
613 const hashString: string = arrayBufferToHexString(hash);
614 // istanbul ignore if
615 if (hashString !== "%________________________ENGINE_WASM_HASH________________________%") {
616 console.error(
617 `The Scandit SDK Engine library WASM file found at ${wasmURI} seems invalid: ` +
618 `expected file hash doesn't match (received: ${hashString}, ` +
619 `expected: ${"%________________________ENGINE_WASM_HASH________________________%"}). ` +
620 `Please ensure the correct Scandit SDK Engine file (with correct version) is retrieved.`
621 );
622 }
623 });
624 } else {
625 console.warn(
626 "Insecure origin (see https://goo.gl/Y0ZkNV): " +
627 `The hash of the Scandit SDK Engine library WASM file found at ${wasmURI} could not be verified`
628 );
629 }
630 }
631
632 function tryFetch(): Promise<Response> {
633 return new Promise((resolve, reject) => {
634 fetch(wasmURI)
635 .then(response => {
636 // istanbul ignore else
637 if (response.ok) {
638 response
639 .clone()
640 .arrayBuffer()
641 .then(responseData => {
642 if (awaitFullResponse) {
643 resolve(response);
644 }
645 verifyResponseData(responseData);
646 })
647 .catch(
648 // istanbul ignore next
649 error => {
650 if (awaitFullResponse) {
651 reject(error);
652 }
653 }
654 );
655
656 if (!awaitFullResponse) {
657 resolve(response);
658 }
659 } else {
660 reject(new Error("HTTP status code is not ok"));
661 }
662 })
663 .catch(error => {
664 reject(error);
665 });
666 });
667 }
668
669 return retryWithExponentialBackoff(tryFetch, 250, 4000, error => {
670 console.warn(error);
671 console.warn(`Couldn't retrieve Scandit SDK Engine library at ${wasmURI}, retrying...`);
672 }).catch(error => {
673 console.error(error);
674 console.error(
675 `Couldn't retrieve/instantiate Scandit SDK Engine library at ${wasmURI}, ` +
676 "did you configure the path for it correctly?"
677 );
678
679 return Promise.reject(error);
680 });
681 }
682 function instantiateWebAssembly(
683 importObject: object,
684 wasmURI: string,
685 successCallback: (instance: WebAssembly.Instance) => void
686 ): void {
687 verifiedWasmFetch(wasmURI, true)
688 .then(response => {
689 return response.arrayBuffer();
690 })
691 .then(bytes => {
692 return self.WebAssembly.instantiate(bytes, importObject)
693 .then((results: WebAssembly.WebAssemblyInstantiatedSource) => {
694 successCallback(results.instance);
695 })
696 .catch((error: Error) => {
697 console.error(error);
698 console.error(
699 `Couldn't instantiate Scandit SDK Engine library at ${wasmURI}, ` +
700 "did you configure the path for it correctly?"
701 );
702 });
703 })
704 .catch(
705 /* istanbul ignore next */ () => {
706 // Ignored
707 }
708 );
709 }
710
711 function instantiateWebAssemblyStreaming(
712 importObject: object,
713 wasmURI: string,
714 successCallback: (instance: WebAssembly.Instance) => void
715 ): void {
716 verifiedWasmFetch(wasmURI, false)
717 .then(response => {
718 self.WebAssembly.instantiateStreaming(response, importObject)
719 .then((results: WebAssembly.WebAssemblyInstantiatedSource) => {
720 successCallback(results.instance);
721 })
722 .catch((error: Error) => {
723 console.warn(error);
724 console.warn(
725 "WebAssembly streaming compile failed. " +
726 "Falling back to ArrayBuffer instantiation (this will make things slower)"
727 );
728 instantiateWebAssembly(importObject, wasmURI, successCallback);
729 });
730 })
731 .catch(
732 /* istanbul ignore next */ () => {
733 // Ignored
734 }
735 );
736 }
737
738 function syncFS(): void {
739 // istanbul ignore if
740 if (fsSyncInProgress === true) {
741 fsSyncScheduled = true;
742 } else {
743 fsSyncInProgress = true;
744 fsSyncScheduled = false;
745 FS.syncfs(false, () => {
746 fsSyncInProgress = false;
747 // istanbul ignore if
748 if (fsSyncScheduled) {
749 syncFS();
750 }
751 });
752 }
753 }
754
755 return {
756 loadLibrary,
757 createContext,
758 setSettings,
759 setImageSettings,
760 workOnScanQueue,
761 workOnParseQueue,
762 addScanWorkUnit,
763 addParseWorkUnit,
764 clearSession
765 };
766}
767
768/**
769 * @hidden
770 */
771// istanbul ignore next
772export function engineWorkerFunction(): void {
773 const engineInstance: Engine = engine();
774
775 onmessage = e => {
776 // Setting settings triggers license verification and activation: delay until first frame processed
777 // tslint:disable:no-reserved-keywords max-union-size
778 const data:
779 | {
780 type: "load-library";
781 deviceId: string;
782 libraryLocation: string;
783 path: string;
784 deviceModelName?: string;
785 uaBrowserName?: string;
786 }
787 | { type: "license-key"; licenseKey: string }
788 | { type: "settings"; settings: string }
789 | { type: "image-settings"; imageSettings: ImageSettings }
790 | { type: "work"; requestId: number; data: Uint8ClampedArray; highQualitySingleFrameMode: boolean }
791 | { type: "parse-string"; requestId: number; dataFormat: Parser.DataFormat; dataString: string; options: string }
792 | { type: "clear-session" } = e.data;
793 // tslint:enable:no-reserved-keywords max-union-size
794 switch (data.type) {
795 case "load-library":
796 // tslint:disable-next-line: no-floating-promises
797 engineInstance.loadLibrary(
798 data.deviceId,
799 data.libraryLocation,
800 data.path,
801 data.deviceModelName,
802 data.uaBrowserName
803 );
804 break;
805 case "license-key":
806 engineInstance.createContext(data.licenseKey);
807 engineInstance.workOnParseQueue();
808 break;
809 case "settings":
810 engineInstance.setSettings(data.settings);
811 engineInstance.workOnScanQueue();
812 break;
813 case "image-settings":
814 engineInstance.setImageSettings(data.imageSettings);
815 engineInstance.workOnScanQueue();
816 break;
817 case "work":
818 engineInstance.addScanWorkUnit({
819 requestId: data.requestId,
820 data: data.data,
821 highQualitySingleFrameMode: data.highQualitySingleFrameMode
822 });
823 break;
824 case "parse-string":
825 engineInstance.addParseWorkUnit({
826 requestId: data.requestId,
827 dataFormat: data.dataFormat,
828 dataString: data.dataString,
829 options: data.options
830 });
831 break;
832 case "clear-session":
833 engineInstance.clearSession();
834 break;
835 default:
836 break;
837 }
838 };
839}
840
841/**
842 * @hidden
843 */
844export const engineWorkerBlob: Blob = new Blob(
845 [`var Module;${engine.toString()}(${engineWorkerFunction.toString()})()`],
846 {
847 type: "text/javascript"
848 }
849);