UNPKG

react-native-scanbot-sdk

Version:

Scanbot Document and Barcode Scanner SDK React Native Plugin for Android and iOS

425 lines (357 loc) 11.2 kB
import { NativeModules, Platform } from 'react-native'; import { ImageSerializationMode } from '../utils'; import { DeepPartial, PartiallyConstructible } from '../utils/utils'; import { BufferImageLoadOptions, EncodeImageOptions, ImageInfo, ImageRefPoolSnapshot, PathImageLoadOptions, SaveImageOptions, } from './ImageRefTypes'; const LINKING_ERROR = `The package 'react-native-scanbot-barcode-scanner-sdk' doesn't seem to be linked. Make sure: \n\n` + Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n'; const ScanbotSDKImpl = NativeModules.RNScanbotSDK ? NativeModules.RNScanbotSDK : new Proxy( {}, { get() { throw new Error(LINKING_ERROR); }, } ); class AutoReleasePool { static globalPull: AutoReleasePool | null = null; static globalPullReferences: number = 0; private poolObjects: AutoReleasable[] = []; releaseAll() { for (const obj of this.poolObjects) { if (!obj.isRetained()) { obj.release(); } } } addObject(obj: AutoReleasable) { this.poolObjects.push(obj); } } export async function autorelease<TReturn>( computation: () => TReturn | Promise<TReturn> ): Promise<TReturn> { if (AutoReleasePool.globalPull === null) { AutoReleasePool.globalPull = new AutoReleasePool(); } AutoReleasePool.globalPullReferences++; const releasePoolRef = () => { AutoReleasePool.globalPullReferences--; if (AutoReleasePool.globalPullReferences === 0) { AutoReleasePool.globalPull!.releaseAll(); AutoReleasePool.globalPull = null; } }; try { return await computation(); } finally { releasePoolRef(); } } export abstract class AutoReleasable extends PartiallyConstructible { private retained: boolean = false; protected constructor(uniqueId?: string) { super(); if (uniqueId) { if (AutoReleasePool.globalPull === null) { const errorMessage = 'Initializing an object that contains a ScanbotImage instance as REFERENCE must be wrapped inside an autorelease pool. For example:' + '\n autorelease(()=>{' + '\n const barcodeItem = new BarcodeItem(source);' + '\n });'; throw new Error(errorMessage); } AutoReleasePool.globalPull.addObject(this); } } public abstract release(): void; public isRetained() { return this.retained; } public retain() { this.retained = true; } } export class ImageRef extends AutoReleasable { public readonly uniqueId?: string; private _buffer?: string; private released: boolean = false; public get buffer() { return this._buffer; } private constructor(uniqueId?: string, buffer?: string) { super(uniqueId); this.uniqueId = uniqueId; this._buffer = buffer; } public static From(source: DeepPartial<ImageRef>): ImageRef { if (source.buffer) { return new ImageRef(undefined, source.buffer); } else { return ImageRef.deserialize(source); } } public static deserialize(serializedRef: DeepPartial<ImageRef>): ImageRef { if (!serializedRef.uniqueId) { throw new Error('uniqueId must be present in serializedRef argument'); } // The promise is intentionally not awaited here ScanbotSDKImpl.imageRefDeserialize(serializedRef.uniqueId) .catch((error: any) => { console.error( `Error while deserializing ImageRef with uniqueId ${serializedRef.uniqueId}: ${error}` ); }) .then((success: boolean) => { if (!success) { console.error( `Unsuccessful deserialization of ImageRef with uniqueId ${serializedRef.uniqueId}` ); } }); return new ImageRef(serializedRef.uniqueId, undefined); } /** * Converts the Image Ref to Json representation */ public async serialize( imageSerializationMode: ImageSerializationMode ): Promise<DeepPartial<ImageRef | null>> { if (imageSerializationMode === 'BUFFER') { const encodedImage = await this.encodeImage(); if (encodedImage) { return { buffer: encodedImage }; } else { return null; } } else { this.throwErrorIfUniqueIdIsMissing(); this.throwErrorIfReleased(); // The promise is intentionally not awaited here ScanbotSDKImpl.imageRefSerialize(this.uniqueId) .catch((error: any) => { console.error( `Error while serializing ImageRef with uniqueId ${this.uniqueId}: ${error}` ); }) .then((success: boolean) => { if (!success) { console.error(`Unsuccessful serialization of ImageRef with uniqueId ${this.uniqueId}`); } }); return { uniqueId: this.uniqueId }; } } /** * Creates ImageRef with uniqueId from the path to an image. */ public static async fromImageFileUri( uri: string, options: PathImageLoadOptions = new PathImageLoadOptions() ): Promise<ImageRef | null> { try { const serializedImageRefUniqueId: string = await ScanbotSDKImpl.imageRefFromImageFileUri( uri, options ); return ImageRef.deserialize({ uniqueId: serializedImageRefUniqueId }); } catch (error: any) { console.error(error); return null; } } /** * Creates ImageRef with uniqueId from base64 encoded buffer, e.g. from jpeg. */ public static async fromEncodedBuffer( buffer: string, options: BufferImageLoadOptions = new BufferImageLoadOptions() ): Promise<ImageRef | null> { try { const serializedImageRefUniqueId: string = await ScanbotSDKImpl.imageRefFromEncodedBuffer( buffer, options ); return ImageRef.deserialize({ uniqueId: serializedImageRefUniqueId }); } catch (error: any) { console.error(error); return null; } } /** * Creates a deep copy of the image. * If uniqueId is not set or the image is already released, an exception will be thrown. */ public async clone(): Promise<ImageRef | null> { this.throwErrorIfUniqueIdIsMissing(); this.throwErrorIfReleased(); try { const serializedImageRefUniqueId: string = await ScanbotSDKImpl.imageRefClone(this.uniqueId); return ImageRef.deserialize({ uniqueId: serializedImageRefUniqueId }); } catch (error: any) { console.error(error); return null; } } /** * Compresses ImageRef and stores it either on disk or in memory according to global settings. * If uniqueId is not set or the image is already released, an exception will be thrown. */ public async hibernate() { this.throwErrorIfUniqueIdIsMissing(); this.throwErrorIfReleased(); try { await ScanbotSDKImpl.imageRefHibernate(this.uniqueId); } catch (error: any) { console.error(error); } } /** * Releases native resources stored by the ref. * If two different ImageRef objects have the same uniqueId both of them become cleared. * If uniqueId is not set or the image is already released, an exception will be thrown. */ public async clear() { this.throwErrorIfUniqueIdIsMissing(); this.throwErrorIfReleased(); try { await ScanbotSDKImpl.imageRefClear(this.uniqueId); } catch (error: any) { console.error(error); } } /** * Information about stored image as reference. * If uniqueId is not set or the image is already released, an exception will be thrown. */ public async info(): Promise<ImageInfo | null> { this.throwErrorIfUniqueIdIsMissing(); this.throwErrorIfReleased(); try { const imageInfo = await ScanbotSDKImpl.imageRefInfo(this.uniqueId); return new ImageInfo(imageInfo); } catch (error: any) { console.error(error); return null; } } /** * Saves the stored image with the given options. * If uniqueId is not set or the image is already released, an exception will be thrown. */ public async saveImage( path: string, options: SaveImageOptions = new SaveImageOptions() ): Promise<boolean> { this.throwErrorIfUniqueIdIsMissing(); this.throwErrorIfReleased(); try { return await ScanbotSDKImpl.imageRefSaveImage(this.uniqueId, path, options); } catch (error: any) { console.error(error); return false; } } /** * Encode image. */ public async encodeInPlace(): Promise<void> { if (this._buffer) { // NO-OP, ImageRef is already converted. } else { this.throwErrorIfUniqueIdIsMissing(); this.throwErrorIfReleased(); try { const imageAsBuffer = await ScanbotSDKImpl.imageRefEncodeImage( this.uniqueId, new EncodeImageOptions() ); if (imageAsBuffer) { this._buffer = imageAsBuffer; } } catch (error: any) { console.error(error); } } } /** * Returns the stored image as base64. */ public async encodeImage(options?: EncodeImageOptions): Promise<string | null> { if (this._buffer) { if (options) { throw new Error( 'EncodeImageOptions are not available when image is already encoded to base64' ); } return this._buffer; } else { this.throwErrorIfUniqueIdIsMissing(); this.throwErrorIfReleased(); try { return await ScanbotSDKImpl.imageRefEncodeImage( this.uniqueId, options ?? new EncodeImageOptions() ); } catch (error: any) { console.error(error); return null; } } } /** * Releases strong reference to the image. * If uniqueId to the image is not set, an exception will be thrown. */ public release() { this.throwErrorIfUniqueIdIsMissing(); if (!this.released) { // The promise is intentionally not awaited here ScanbotSDKImpl.imageRefRelease(this.uniqueId).catch((error: any) => { console.error(`Error while releasing ImageRef with uniqueId ${this.uniqueId}: ${error}`); }); this.released = true; } } /** * Returns a snapshot of all alive ImageRefs. * The snapshot contains a list of ImageRefs with information such as in-memory size. */ public static async makeSnapshot(): Promise<ImageRefPoolSnapshot | null> { try { const snapshot = await ScanbotSDKImpl.makeSnapshot(); return new ImageRefPoolSnapshot(snapshot); } catch (error: any) { console.error(error); return null; } } /** * Releases all alive images despite any existing references. */ public static releaseAll() { // The promise is intentionally not awaited here ScanbotSDKImpl.imageRefReleaseAll().catch(() => {}); } private throwErrorIfUniqueIdIsMissing(): void { if (!this.uniqueId) { throw new Error('uniqueId is missing'); } } private throwErrorIfReleased(): void { if (this.released) { throw new Error(`ImageRef with uniqueId ${this.uniqueId} has been released`); } } }