Skip to content

Commit 21ef454

Browse files
Merge pull request #19962 from Snuffleupagus/api_utils
Move a few helper functions/classes out of the `src/display/api.js` file
2 parents d90ec29 + e91b480 commit 21ef454

File tree

6 files changed

+305
-255
lines changed

6 files changed

+305
-255
lines changed

src/display/api.js

Lines changed: 10 additions & 252 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
*/
1919

2020
import {
21-
_isValidExplicitDest,
2221
AbortException,
2322
AnnotationMode,
2423
assert,
@@ -29,7 +28,6 @@ import {
2928
RenderingIntentFlag,
3029
setVerbosityLevel,
3130
shadow,
32-
stringToBytes,
3331
unreachable,
3432
warn,
3533
} from "../shared/util.js";
@@ -47,6 +45,13 @@ import {
4745
StatTimer,
4846
} from "./display_utils.js";
4947
import { FontFaceObject, FontLoader } from "./font_loader.js";
48+
import {
49+
getDataProp,
50+
getFactoryUrlProp,
51+
getUrlProp,
52+
isRefProxy,
53+
LoopbackPort,
54+
} from "./api_utils.js";
5055
import { MessageHandler, wrapReason } from "../shared/message_handler.js";
5156
import {
5257
NodeCanvasFactory,
@@ -68,10 +73,10 @@ import { PDFDataTransportStream } from "./transport_stream.js";
6873
import { PDFFetchStream } from "display-fetch_stream";
6974
import { PDFNetworkStream } from "display-network";
7075
import { PDFNodeStream } from "display-node_stream";
76+
import { PDFObjects } from "./pdf_objects.js";
7177
import { TextLayer } from "./text_layer.js";
7278
import { XfaText } from "./xfa_text.js";
7379

74-
const DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536
7580
const RENDERING_CANCELLED_TIMEOUT = 100; // ms
7681

7782
/**
@@ -111,7 +116,7 @@ const RENDERING_CANCELLED_TIMEOUT = 100; // ms
111116
* @property {PDFDataRangeTransport} [range] - Allows for using a custom range
112117
* transport implementation.
113118
* @property {number} [rangeChunkSize] - Specify maximum number of bytes fetched
114-
* per range request. The default value is {@link DEFAULT_RANGE_CHUNK_SIZE}.
119+
* per range request. The default value is 65536 (= 2^16).
115120
* @property {PDFWorker} [worker] - The worker that will be used for loading and
116121
* parsing the PDF data.
117122
* @property {number} [verbosity] - Controls the logging level; the constants
@@ -255,7 +260,7 @@ function getDocument(src = {}) {
255260
const rangeChunkSize =
256261
Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0
257262
? src.rangeChunkSize
258-
: DEFAULT_RANGE_CHUNK_SIZE;
263+
: 2 ** 16;
259264
let worker = src.worker instanceof PDFWorker ? src.worker : null;
260265
const verbosity = src.verbosity;
261266
// Ignore "data:"-URLs, since they can't be used to recover valid absolute
@@ -507,94 +512,6 @@ function getDocument(src = {}) {
507512
return task;
508513
}
509514

510-
function getUrlProp(val) {
511-
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
512-
return null; // The 'url' is unused with `PDFDataRangeTransport`.
513-
}
514-
if (val instanceof URL) {
515-
return val.href;
516-
}
517-
if (typeof val === "string") {
518-
if (
519-
typeof PDFJSDev !== "undefined" &&
520-
PDFJSDev.test("GENERIC") &&
521-
isNodeJS
522-
) {
523-
return val; // Use the url as-is in Node.js environments.
524-
}
525-
526-
// The full path is required in the 'url' field.
527-
const url = URL.parse(val, window.location);
528-
if (url) {
529-
return url.href;
530-
}
531-
}
532-
throw new Error(
533-
"Invalid PDF url data: " +
534-
"either string or URL-object is expected in the url property."
535-
);
536-
}
537-
538-
function getDataProp(val) {
539-
// Converting string or array-like data to Uint8Array.
540-
if (
541-
typeof PDFJSDev !== "undefined" &&
542-
PDFJSDev.test("GENERIC") &&
543-
isNodeJS &&
544-
typeof Buffer !== "undefined" && // eslint-disable-line no-undef
545-
val instanceof Buffer // eslint-disable-line no-undef
546-
) {
547-
throw new Error(
548-
"Please provide binary data as `Uint8Array`, rather than `Buffer`."
549-
);
550-
}
551-
if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
552-
// Use the data as-is when it's already a Uint8Array that completely
553-
// "utilizes" its underlying ArrayBuffer, to prevent any possible
554-
// issues when transferring it to the worker-thread.
555-
return val;
556-
}
557-
if (typeof val === "string") {
558-
return stringToBytes(val);
559-
}
560-
if (
561-
val instanceof ArrayBuffer ||
562-
ArrayBuffer.isView(val) ||
563-
(typeof val === "object" && !isNaN(val?.length))
564-
) {
565-
return new Uint8Array(val);
566-
}
567-
throw new Error(
568-
"Invalid PDF binary data: either TypedArray, " +
569-
"string, or array-like object is expected in the data property."
570-
);
571-
}
572-
573-
function getFactoryUrlProp(val) {
574-
if (typeof val !== "string") {
575-
return null;
576-
}
577-
if (val.endsWith("/")) {
578-
return val;
579-
}
580-
throw new Error(`Invalid factory url: "${val}" must include trailing slash.`);
581-
}
582-
583-
const isRefProxy = v =>
584-
typeof v === "object" &&
585-
Number.isInteger(v?.num) &&
586-
v.num >= 0 &&
587-
Number.isInteger(v?.gen) &&
588-
v.gen >= 0;
589-
590-
const isNameProxy = v => typeof v === "object" && typeof v?.name === "string";
591-
592-
const isValidExplicitDest = _isValidExplicitDest.bind(
593-
null,
594-
/* validRef = */ isRefProxy,
595-
/* validName = */ isNameProxy
596-
);
597-
598515
/**
599516
* @typedef {Object} OnProgressParameters
600517
* @property {number} loaded - Currently loaded number of bytes.
@@ -2012,54 +1929,6 @@ class PDFPageProxy {
20121929
}
20131930
}
20141931

2015-
class LoopbackPort {
2016-
#listeners = new Map();
2017-
2018-
#deferred = Promise.resolve();
2019-
2020-
postMessage(obj, transfer) {
2021-
const event = {
2022-
data: structuredClone(obj, transfer ? { transfer } : null),
2023-
};
2024-
2025-
this.#deferred.then(() => {
2026-
for (const [listener] of this.#listeners) {
2027-
listener.call(this, event);
2028-
}
2029-
});
2030-
}
2031-
2032-
addEventListener(name, listener, options = null) {
2033-
let rmAbort = null;
2034-
if (options?.signal instanceof AbortSignal) {
2035-
const { signal } = options;
2036-
if (signal.aborted) {
2037-
warn("LoopbackPort - cannot use an `aborted` signal.");
2038-
return;
2039-
}
2040-
const onAbort = () => this.removeEventListener(name, listener);
2041-
rmAbort = () => signal.removeEventListener("abort", onAbort);
2042-
2043-
signal.addEventListener("abort", onAbort);
2044-
}
2045-
this.#listeners.set(listener, rmAbort);
2046-
}
2047-
2048-
removeEventListener(name, listener) {
2049-
const rmAbort = this.#listeners.get(listener);
2050-
rmAbort?.();
2051-
2052-
this.#listeners.delete(listener);
2053-
}
2054-
2055-
terminate() {
2056-
for (const [, rmAbort] of this.#listeners) {
2057-
rmAbort?.();
2058-
}
2059-
this.#listeners.clear();
2060-
}
2061-
}
2062-
20631932
/**
20641933
* @typedef {Object} PDFWorkerParameters
20651934
* @property {string} [name] - The name of the worker.
@@ -3142,115 +3011,6 @@ class WorkerTransport {
31423011
}
31433012
}
31443013

3145-
const INITIAL_DATA = Symbol("INITIAL_DATA");
3146-
3147-
/**
3148-
* A PDF document and page is built of many objects. E.g. there are objects for
3149-
* fonts, images, rendering code, etc. These objects may get processed inside of
3150-
* a worker. This class implements some basic methods to manage these objects.
3151-
*/
3152-
class PDFObjects {
3153-
#objs = Object.create(null);
3154-
3155-
/**
3156-
* Ensures there is an object defined for `objId`.
3157-
*
3158-
* @param {string} objId
3159-
* @returns {Object}
3160-
*/
3161-
#ensureObj(objId) {
3162-
return (this.#objs[objId] ||= {
3163-
...Promise.withResolvers(),
3164-
data: INITIAL_DATA,
3165-
});
3166-
}
3167-
3168-
/**
3169-
* If called *without* callback, this returns the data of `objId` but the
3170-
* object needs to be resolved. If it isn't, this method throws.
3171-
*
3172-
* If called *with* a callback, the callback is called with the data of the
3173-
* object once the object is resolved. That means, if you call this method
3174-
* and the object is already resolved, the callback gets called right away.
3175-
*
3176-
* @param {string} objId
3177-
* @param {function} [callback]
3178-
* @returns {any}
3179-
*/
3180-
get(objId, callback = null) {
3181-
// If there is a callback, then the get can be async and the object is
3182-
// not required to be resolved right now.
3183-
if (callback) {
3184-
const obj = this.#ensureObj(objId);
3185-
obj.promise.then(() => callback(obj.data));
3186-
return null;
3187-
}
3188-
// If there isn't a callback, the user expects to get the resolved data
3189-
// directly.
3190-
const obj = this.#objs[objId];
3191-
// If there isn't an object yet or the object isn't resolved, then the
3192-
// data isn't ready yet!
3193-
if (!obj || obj.data === INITIAL_DATA) {
3194-
throw new Error(`Requesting object that isn't resolved yet ${objId}.`);
3195-
}
3196-
return obj.data;
3197-
}
3198-
3199-
/**
3200-
* @param {string} objId
3201-
* @returns {boolean}
3202-
*/
3203-
has(objId) {
3204-
const obj = this.#objs[objId];
3205-
return !!obj && obj.data !== INITIAL_DATA;
3206-
}
3207-
3208-
/**
3209-
* @param {string} objId
3210-
* @returns {boolean}
3211-
*/
3212-
delete(objId) {
3213-
const obj = this.#objs[objId];
3214-
if (!obj || obj.data === INITIAL_DATA) {
3215-
// Only allow removing the object *after* it's been resolved.
3216-
return false;
3217-
}
3218-
delete this.#objs[objId];
3219-
return true;
3220-
}
3221-
3222-
/**
3223-
* Resolves the object `objId` with optional `data`.
3224-
*
3225-
* @param {string} objId
3226-
* @param {any} [data]
3227-
*/
3228-
resolve(objId, data = null) {
3229-
const obj = this.#ensureObj(objId);
3230-
obj.data = data;
3231-
obj.resolve();
3232-
}
3233-
3234-
clear() {
3235-
for (const objId in this.#objs) {
3236-
const { data } = this.#objs[objId];
3237-
data?.bitmap?.close(); // Release any `ImageBitmap` data.
3238-
}
3239-
this.#objs = Object.create(null);
3240-
}
3241-
3242-
*[Symbol.iterator]() {
3243-
for (const objId in this.#objs) {
3244-
const { data } = this.#objs[objId];
3245-
3246-
if (data === INITIAL_DATA) {
3247-
continue;
3248-
}
3249-
yield [objId, data];
3250-
}
3251-
}
3252-
}
3253-
32543014
/**
32553015
* Allows controlling of the rendering tasks.
32563016
*/
@@ -3511,8 +3271,6 @@ const build =
35113271
export {
35123272
build,
35133273
getDocument,
3514-
isValidExplicitDest,
3515-
LoopbackPort,
35163274
PDFDataRangeTransport,
35173275
PDFDocumentLoadingTask,
35183276
PDFDocumentProxy,

0 commit comments

Comments
 (0)