UNPKG

@pdftron/webviewer-react-toolkit

Version:

A React component library for integrating with PDFTron WebViewer API.

220 lines (219 loc) 8.69 kB
import { blobToDocument, documentToBlob, getExtension, getRotatedDocument, getStringId, getThumbnail, globalLicense, } from '../utils'; import { MemoizedPromise } from './memoizedPromise'; /** A representation of the data within a file. */ export class File { /** * Initialize the `File`. * @param fileDetails The file details object or file-like class to initialize * this `File` with. */ constructor(fileDetails) { /* --- Private helpers. --- */ /** Generate a thumbnail from document object. */ this._generateThumbnail = async () => { try { const result = await getThumbnail(this.fullDocumentObj || this.documentObj.get(), { extension: this.extension, pageNumber: this.pageNumber, }); return result; } catch (e) { console.warn(`Unable to get thumbnail for extension ${this.extension}`); return ''; } }; /** Generate a file object from document object. */ this._generateFileObj = () => { return documentToBlob(this.documentObj.get()); }; /** Generate a document object from file object and extension. */ this._generateDocumentObj = () => { return blobToDocument(this.fileObj.get(), this.extension, this._license); }; const { name, id, originalName, extension, fileObj, documentObj, thumbnail, license, fullDocumentObj, pageNumber } = fileDetails; if (!fileObj && !documentObj) { throw new Error('One of `fileObj` or `documentObj` is required to initialize File.'); } if (fullDocumentObj) { if (typeof pageNumber !== 'number') { throw new Error('"pageNumber" must be passed if using "fullDocumentObj"'); } this._fullDocumentObj = fullDocumentObj; this._pageNumber = pageNumber; } this._id = id || getStringId('file'); this._name = name; this._originalName = originalName || name; this._extension = extension || getExtension(name); this._documentObj = new MemoizedPromise(documentObj ?? this._generateDocumentObj); this._fileObj = new MemoizedPromise(fileObj ?? this._generateFileObj); this._thumbnail = new MemoizedPromise(thumbnail ?? this._generateThumbnail); this._freezeThumbnail = false; this._subscribers = {}; this._license = license; } /** * Set the license key in order to use full WebViewer features. This only * needs to be done once, and will be used globally by all files. * @param license The license key for WebViewer. */ static setGlobalLicense(license) { globalLicense.set(license); } /** A unique ID generated for the file. */ get id() { return this._id; } /** The original name of the file. */ get originalName() { return this._originalName; } /** The extension of the file (for example `'pdf'`). */ get extension() { return this._extension; } /** A memoized promise for the thumbnail. */ get thumbnail() { return this._thumbnail; } /** A memoized promise for the fileObj. */ get fileObj() { return this._fileObj; } /** A memoized promise for the documentObj. */ get documentObj() { return this._documentObj; } /** Gets the full document object if provided during initialization. */ get fullDocumentObj() { return this._fullDocumentObj; } /** Gets the page index if provided during initialization. */ get pageNumber() { return this._pageNumber; } /** The name of the file. */ get name() { return this._name; } set name(name) { this._name = name; this._extension = getExtension(name); this.dispatchEvent('onnamechange'); } /** Freeze to prevent thumbnail from changing. */ get freezeThumbnail() { return this._freezeThumbnail; } set freezeThumbnail(freezeThumbnail) { this._freezeThumbnail = freezeThumbnail; if (freezeThumbnail) this.dispatchEvent('onfreezethumbnail'); else this.dispatchEvent('onunfreezethumbnail'); } /* --- Memoized promise value setters. --- */ /** * Set the thumbnail or give a futurable or getter. * @param thumbnail The thumbnail, promise, or getter for the thumbnail. */ setThumbnail(thumbnail) { if (this.freezeThumbnail) return; this._thumbnail = new MemoizedPromise(thumbnail ?? this._generateThumbnail); this.dispatchEvent('onthumbnailchange'); } /** * Set the fileObj or give a futurable or getter. * @param fileObj The fileObj, promise, or getter for the fileObj. */ setFileObj(fileObj) { this._fileObj = new MemoizedPromise(fileObj ?? this._generateFileObj); // Only update documentObj if fileObj was given, not generated. if (fileObj) this.setDocumentObj(); this.dispatchEvent('onfileobjchange'); } /** * Set the documentObj or give a futurable or getter. * @param documentObj The documentObj, promise, or getter for the documentObj. */ setDocumentObj(documentObj) { this._documentObj = new MemoizedPromise(documentObj ?? this._generateDocumentObj); // Only update fileObj if documentObj was given, not generated. if (documentObj) this.setFileObj(); this.setThumbnail(); this.dispatchEvent('ondocumentobjchange'); } /** * Use this file to make updates to `documentObj` that you want reflected in * `fileObj` and `thumbnail`. Since mutations directly to `documentObj` will * not be detected, using this function tells `File` to trigger an update. */ async updateDocumentObj(updater) { const documentObj = await this.documentObj.get(); await updater(documentObj); this.setDocumentObj(documentObj); } /* --- File utility functions. --- */ /** * Rotate 90 degrees clockwise unless `counterclockwise` is true. * @param counterclockwise Rotate 90 degrees counterclockwise. */ async rotate(counterclockwise) { const rotated = await getRotatedDocument(this.documentObj.get(), counterclockwise); this.setDocumentObj(rotated); this.dispatchEvent('onrotate'); } /** * Creates a clone of the file with a new `documentObj`. This is the * recommended way of duplicating files, as it will prevent them both * referencing the same documentObj. * @param overrides Override any of the `FileDetails` that initialize `File`. * Note that `fileObj` will be overridden by the `documentObj` in overrides, * or cloned from this file. */ clone(overrides) { const { documentObj, ...rest } = overrides || {}; return new File({ name: this.name, originalName: this.originalName, extension: this.extension, ...rest, documentObj: documentObj || blobToDocument(documentToBlob(this.documentObj.get()), this.extension, this._license), }); } /* --- Events. --- */ /** * Appends an subscriber for events whose type attribute value is type. * The callback argument sets the callback that will be invoked when the event * is dispatched. * @param type The event type that will invoke the listener. * @param subscriber The listener function that will be invoked. */ subscribe(type, subscriber) { (this._subscribers[type] ?? (this._subscribers[type] = [])).push(subscriber); return this._unsubscribe(type, subscriber); } /** * Dispatch an event for this file. Once all subscribers have been called for * the dispatched type, it will complete by calling all onchange subscribers. * @param type The file event type to dispatch. */ dispatchEvent(type) { this._subscribers[type]?.forEach((subscriber) => subscriber()); if (type !== 'onchange') this.dispatchEvent('onchange'); } /** * Removes the event listener in target's event listener list with the same * type and callback. * @param type The event type that the listener is registered for. * @param subscriber The listener to remove. */ _unsubscribe(type, subscriber) { return () => (this._subscribers[type] = this._subscribers[type]?.filter((l) => l !== subscriber)); } }