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
text/typescript
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`);
}
}
}