This isn't directly part of the official API, and having this class in its own file could help avoid future changes (e.g. issue 18148) affecting the size of the `src/display/api.js` file unnecessarily.
126 lines
3.4 KiB
JavaScript
126 lines
3.4 KiB
JavaScript
/* Copyright 2012 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
const INITIAL_DATA = Symbol("INITIAL_DATA");
|
|
|
|
/**
|
|
* A PDF document and page is built of many objects. E.g. there are objects for
|
|
* fonts, images, rendering code, etc. These objects may get processed inside of
|
|
* a worker. This class implements some basic methods to manage these objects.
|
|
*/
|
|
class PDFObjects {
|
|
#objs = Object.create(null);
|
|
|
|
/**
|
|
* Ensures there is an object defined for `objId`.
|
|
*
|
|
* @param {string} objId
|
|
* @returns {Object}
|
|
*/
|
|
#ensureObj(objId) {
|
|
return (this.#objs[objId] ||= {
|
|
...Promise.withResolvers(),
|
|
data: INITIAL_DATA,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* If called *without* callback, this returns the data of `objId` but the
|
|
* object needs to be resolved. If it isn't, this method throws.
|
|
*
|
|
* If called *with* a callback, the callback is called with the data of the
|
|
* object once the object is resolved. That means, if you call this method
|
|
* and the object is already resolved, the callback gets called right away.
|
|
*
|
|
* @param {string} objId
|
|
* @param {function} [callback]
|
|
* @returns {any}
|
|
*/
|
|
get(objId, callback = null) {
|
|
// If there is a callback, then the get can be async and the object is
|
|
// not required to be resolved right now.
|
|
if (callback) {
|
|
const obj = this.#ensureObj(objId);
|
|
obj.promise.then(() => callback(obj.data));
|
|
return null;
|
|
}
|
|
// If there isn't a callback, the user expects to get the resolved data
|
|
// directly.
|
|
const obj = this.#objs[objId];
|
|
// If there isn't an object yet or the object isn't resolved, then the
|
|
// data isn't ready yet!
|
|
if (!obj || obj.data === INITIAL_DATA) {
|
|
throw new Error(`Requesting object that isn't resolved yet ${objId}.`);
|
|
}
|
|
return obj.data;
|
|
}
|
|
|
|
/**
|
|
* @param {string} objId
|
|
* @returns {boolean}
|
|
*/
|
|
has(objId) {
|
|
const obj = this.#objs[objId];
|
|
return !!obj && obj.data !== INITIAL_DATA;
|
|
}
|
|
|
|
/**
|
|
* @param {string} objId
|
|
* @returns {boolean}
|
|
*/
|
|
delete(objId) {
|
|
const obj = this.#objs[objId];
|
|
if (!obj || obj.data === INITIAL_DATA) {
|
|
// Only allow removing the object *after* it's been resolved.
|
|
return false;
|
|
}
|
|
delete this.#objs[objId];
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Resolves the object `objId` with optional `data`.
|
|
*
|
|
* @param {string} objId
|
|
* @param {any} [data]
|
|
*/
|
|
resolve(objId, data = null) {
|
|
const obj = this.#ensureObj(objId);
|
|
obj.data = data;
|
|
obj.resolve();
|
|
}
|
|
|
|
clear() {
|
|
for (const objId in this.#objs) {
|
|
const { data } = this.#objs[objId];
|
|
data?.bitmap?.close(); // Release any `ImageBitmap` data.
|
|
}
|
|
this.#objs = Object.create(null);
|
|
}
|
|
|
|
*[Symbol.iterator]() {
|
|
for (const objId in this.#objs) {
|
|
const { data } = this.#objs[objId];
|
|
|
|
if (data === INITIAL_DATA) {
|
|
continue;
|
|
}
|
|
yield [objId, data];
|
|
}
|
|
}
|
|
}
|
|
|
|
export { PDFObjects };
|