Move a few helper functions/classes out of the src/display/api.js file
Given that this file represents the official API, it's difficult to avoid it becoming fairly large as we add new functionality. However, it also contains a couple of smaller (and internal) helpers that we can move into a new utils-file. Also, we inline the `DEFAULT_RANGE_CHUNK_SIZE` constant since it's only used *once* and its value has never been changed in over a decade.
This commit is contained in:
parent
e921533577
commit
0105237af6
@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
_isValidExplicitDest,
|
||||
AbortException,
|
||||
AnnotationMode,
|
||||
assert,
|
||||
@ -29,7 +28,6 @@ import {
|
||||
RenderingIntentFlag,
|
||||
setVerbosityLevel,
|
||||
shadow,
|
||||
stringToBytes,
|
||||
unreachable,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
@ -47,6 +45,13 @@ import {
|
||||
StatTimer,
|
||||
} from "./display_utils.js";
|
||||
import { FontFaceObject, FontLoader } from "./font_loader.js";
|
||||
import {
|
||||
getDataProp,
|
||||
getFactoryUrlProp,
|
||||
getUrlProp,
|
||||
isRefProxy,
|
||||
LoopbackPort,
|
||||
} from "./api_utils.js";
|
||||
import { MessageHandler, wrapReason } from "../shared/message_handler.js";
|
||||
import {
|
||||
NodeCanvasFactory,
|
||||
@ -71,7 +76,6 @@ import { PDFNodeStream } from "display-node_stream";
|
||||
import { TextLayer } from "./text_layer.js";
|
||||
import { XfaText } from "./xfa_text.js";
|
||||
|
||||
const DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536
|
||||
const RENDERING_CANCELLED_TIMEOUT = 100; // ms
|
||||
|
||||
/**
|
||||
@ -111,7 +115,7 @@ const RENDERING_CANCELLED_TIMEOUT = 100; // ms
|
||||
* @property {PDFDataRangeTransport} [range] - Allows for using a custom range
|
||||
* transport implementation.
|
||||
* @property {number} [rangeChunkSize] - Specify maximum number of bytes fetched
|
||||
* per range request. The default value is {@link DEFAULT_RANGE_CHUNK_SIZE}.
|
||||
* per range request. The default value is 65536 (= 2^16).
|
||||
* @property {PDFWorker} [worker] - The worker that will be used for loading and
|
||||
* parsing the PDF data.
|
||||
* @property {number} [verbosity] - Controls the logging level; the constants
|
||||
@ -255,7 +259,7 @@ function getDocument(src = {}) {
|
||||
const rangeChunkSize =
|
||||
Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0
|
||||
? src.rangeChunkSize
|
||||
: DEFAULT_RANGE_CHUNK_SIZE;
|
||||
: 2 ** 16;
|
||||
let worker = src.worker instanceof PDFWorker ? src.worker : null;
|
||||
const verbosity = src.verbosity;
|
||||
// Ignore "data:"-URLs, since they can't be used to recover valid absolute
|
||||
@ -507,94 +511,6 @@ function getDocument(src = {}) {
|
||||
return task;
|
||||
}
|
||||
|
||||
function getUrlProp(val) {
|
||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
||||
return null; // The 'url' is unused with `PDFDataRangeTransport`.
|
||||
}
|
||||
if (val instanceof URL) {
|
||||
return val.href;
|
||||
}
|
||||
if (typeof val === "string") {
|
||||
if (
|
||||
typeof PDFJSDev !== "undefined" &&
|
||||
PDFJSDev.test("GENERIC") &&
|
||||
isNodeJS
|
||||
) {
|
||||
return val; // Use the url as-is in Node.js environments.
|
||||
}
|
||||
|
||||
// The full path is required in the 'url' field.
|
||||
const url = URL.parse(val, window.location);
|
||||
if (url) {
|
||||
return url.href;
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
"Invalid PDF url data: " +
|
||||
"either string or URL-object is expected in the url property."
|
||||
);
|
||||
}
|
||||
|
||||
function getDataProp(val) {
|
||||
// Converting string or array-like data to Uint8Array.
|
||||
if (
|
||||
typeof PDFJSDev !== "undefined" &&
|
||||
PDFJSDev.test("GENERIC") &&
|
||||
isNodeJS &&
|
||||
typeof Buffer !== "undefined" && // eslint-disable-line no-undef
|
||||
val instanceof Buffer // eslint-disable-line no-undef
|
||||
) {
|
||||
throw new Error(
|
||||
"Please provide binary data as `Uint8Array`, rather than `Buffer`."
|
||||
);
|
||||
}
|
||||
if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
|
||||
// Use the data as-is when it's already a Uint8Array that completely
|
||||
// "utilizes" its underlying ArrayBuffer, to prevent any possible
|
||||
// issues when transferring it to the worker-thread.
|
||||
return val;
|
||||
}
|
||||
if (typeof val === "string") {
|
||||
return stringToBytes(val);
|
||||
}
|
||||
if (
|
||||
val instanceof ArrayBuffer ||
|
||||
ArrayBuffer.isView(val) ||
|
||||
(typeof val === "object" && !isNaN(val?.length))
|
||||
) {
|
||||
return new Uint8Array(val);
|
||||
}
|
||||
throw new Error(
|
||||
"Invalid PDF binary data: either TypedArray, " +
|
||||
"string, or array-like object is expected in the data property."
|
||||
);
|
||||
}
|
||||
|
||||
function getFactoryUrlProp(val) {
|
||||
if (typeof val !== "string") {
|
||||
return null;
|
||||
}
|
||||
if (val.endsWith("/")) {
|
||||
return val;
|
||||
}
|
||||
throw new Error(`Invalid factory url: "${val}" must include trailing slash.`);
|
||||
}
|
||||
|
||||
const isRefProxy = v =>
|
||||
typeof v === "object" &&
|
||||
Number.isInteger(v?.num) &&
|
||||
v.num >= 0 &&
|
||||
Number.isInteger(v?.gen) &&
|
||||
v.gen >= 0;
|
||||
|
||||
const isNameProxy = v => typeof v === "object" && typeof v?.name === "string";
|
||||
|
||||
const isValidExplicitDest = _isValidExplicitDest.bind(
|
||||
null,
|
||||
/* validRef = */ isRefProxy,
|
||||
/* validName = */ isNameProxy
|
||||
);
|
||||
|
||||
/**
|
||||
* @typedef {Object} OnProgressParameters
|
||||
* @property {number} loaded - Currently loaded number of bytes.
|
||||
@ -2012,54 +1928,6 @@ class PDFPageProxy {
|
||||
}
|
||||
}
|
||||
|
||||
class LoopbackPort {
|
||||
#listeners = new Map();
|
||||
|
||||
#deferred = Promise.resolve();
|
||||
|
||||
postMessage(obj, transfer) {
|
||||
const event = {
|
||||
data: structuredClone(obj, transfer ? { transfer } : null),
|
||||
};
|
||||
|
||||
this.#deferred.then(() => {
|
||||
for (const [listener] of this.#listeners) {
|
||||
listener.call(this, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener(name, listener, options = null) {
|
||||
let rmAbort = null;
|
||||
if (options?.signal instanceof AbortSignal) {
|
||||
const { signal } = options;
|
||||
if (signal.aborted) {
|
||||
warn("LoopbackPort - cannot use an `aborted` signal.");
|
||||
return;
|
||||
}
|
||||
const onAbort = () => this.removeEventListener(name, listener);
|
||||
rmAbort = () => signal.removeEventListener("abort", onAbort);
|
||||
|
||||
signal.addEventListener("abort", onAbort);
|
||||
}
|
||||
this.#listeners.set(listener, rmAbort);
|
||||
}
|
||||
|
||||
removeEventListener(name, listener) {
|
||||
const rmAbort = this.#listeners.get(listener);
|
||||
rmAbort?.();
|
||||
|
||||
this.#listeners.delete(listener);
|
||||
}
|
||||
|
||||
terminate() {
|
||||
for (const [, rmAbort] of this.#listeners) {
|
||||
rmAbort?.();
|
||||
}
|
||||
this.#listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} PDFWorkerParameters
|
||||
* @property {string} [name] - The name of the worker.
|
||||
@ -3511,8 +3379,6 @@ const build =
|
||||
export {
|
||||
build,
|
||||
getDocument,
|
||||
isValidExplicitDest,
|
||||
LoopbackPort,
|
||||
PDFDataRangeTransport,
|
||||
PDFDocumentLoadingTask,
|
||||
PDFDocumentProxy,
|
||||
|
||||
167
src/display/api_utils.js
Normal file
167
src/display/api_utils.js
Normal file
@ -0,0 +1,167 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
import {
|
||||
_isValidExplicitDest,
|
||||
isNodeJS,
|
||||
stringToBytes,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
|
||||
function getUrlProp(val) {
|
||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
||||
return null; // The 'url' is unused with `PDFDataRangeTransport`.
|
||||
}
|
||||
if (val instanceof URL) {
|
||||
return val.href;
|
||||
}
|
||||
if (typeof val === "string") {
|
||||
if (
|
||||
typeof PDFJSDev !== "undefined" &&
|
||||
PDFJSDev.test("GENERIC") &&
|
||||
isNodeJS
|
||||
) {
|
||||
return val; // Use the url as-is in Node.js environments.
|
||||
}
|
||||
|
||||
// The full path is required in the 'url' field.
|
||||
const url = URL.parse(val, window.location);
|
||||
if (url) {
|
||||
return url.href;
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
"Invalid PDF url data: " +
|
||||
"either string or URL-object is expected in the url property."
|
||||
);
|
||||
}
|
||||
|
||||
function getDataProp(val) {
|
||||
// Converting string or array-like data to Uint8Array.
|
||||
if (
|
||||
typeof PDFJSDev !== "undefined" &&
|
||||
PDFJSDev.test("GENERIC") &&
|
||||
isNodeJS &&
|
||||
typeof Buffer !== "undefined" && // eslint-disable-line no-undef
|
||||
val instanceof Buffer // eslint-disable-line no-undef
|
||||
) {
|
||||
throw new Error(
|
||||
"Please provide binary data as `Uint8Array`, rather than `Buffer`."
|
||||
);
|
||||
}
|
||||
if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
|
||||
// Use the data as-is when it's already a Uint8Array that completely
|
||||
// "utilizes" its underlying ArrayBuffer, to prevent any possible
|
||||
// issues when transferring it to the worker-thread.
|
||||
return val;
|
||||
}
|
||||
if (typeof val === "string") {
|
||||
return stringToBytes(val);
|
||||
}
|
||||
if (
|
||||
val instanceof ArrayBuffer ||
|
||||
ArrayBuffer.isView(val) ||
|
||||
(typeof val === "object" && !isNaN(val?.length))
|
||||
) {
|
||||
return new Uint8Array(val);
|
||||
}
|
||||
throw new Error(
|
||||
"Invalid PDF binary data: either TypedArray, " +
|
||||
"string, or array-like object is expected in the data property."
|
||||
);
|
||||
}
|
||||
|
||||
function getFactoryUrlProp(val) {
|
||||
if (typeof val !== "string") {
|
||||
return null;
|
||||
}
|
||||
if (val.endsWith("/")) {
|
||||
return val;
|
||||
}
|
||||
throw new Error(`Invalid factory url: "${val}" must include trailing slash.`);
|
||||
}
|
||||
|
||||
const isRefProxy = v =>
|
||||
typeof v === "object" &&
|
||||
Number.isInteger(v?.num) &&
|
||||
v.num >= 0 &&
|
||||
Number.isInteger(v?.gen) &&
|
||||
v.gen >= 0;
|
||||
|
||||
const isNameProxy = v => typeof v === "object" && typeof v?.name === "string";
|
||||
|
||||
const isValidExplicitDest = _isValidExplicitDest.bind(
|
||||
null,
|
||||
/* validRef = */ isRefProxy,
|
||||
/* validName = */ isNameProxy
|
||||
);
|
||||
|
||||
class LoopbackPort {
|
||||
#listeners = new Map();
|
||||
|
||||
#deferred = Promise.resolve();
|
||||
|
||||
postMessage(obj, transfer) {
|
||||
const event = {
|
||||
data: structuredClone(obj, transfer ? { transfer } : null),
|
||||
};
|
||||
|
||||
this.#deferred.then(() => {
|
||||
for (const [listener] of this.#listeners) {
|
||||
listener.call(this, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener(name, listener, options = null) {
|
||||
let rmAbort = null;
|
||||
if (options?.signal instanceof AbortSignal) {
|
||||
const { signal } = options;
|
||||
if (signal.aborted) {
|
||||
warn("LoopbackPort - cannot use an `aborted` signal.");
|
||||
return;
|
||||
}
|
||||
const onAbort = () => this.removeEventListener(name, listener);
|
||||
rmAbort = () => signal.removeEventListener("abort", onAbort);
|
||||
|
||||
signal.addEventListener("abort", onAbort);
|
||||
}
|
||||
this.#listeners.set(listener, rmAbort);
|
||||
}
|
||||
|
||||
removeEventListener(name, listener) {
|
||||
const rmAbort = this.#listeners.get(listener);
|
||||
rmAbort?.();
|
||||
|
||||
this.#listeners.delete(listener);
|
||||
}
|
||||
|
||||
terminate() {
|
||||
for (const [, rmAbort] of this.#listeners) {
|
||||
rmAbort?.();
|
||||
}
|
||||
this.#listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getDataProp,
|
||||
getFactoryUrlProp,
|
||||
getUrlProp,
|
||||
isNameProxy,
|
||||
isRefProxy,
|
||||
isValidExplicitDest,
|
||||
LoopbackPort,
|
||||
};
|
||||
@ -47,7 +47,6 @@ import {
|
||||
import {
|
||||
build,
|
||||
getDocument,
|
||||
isValidExplicitDest,
|
||||
PDFDataRangeTransport,
|
||||
PDFWorker,
|
||||
version,
|
||||
@ -76,6 +75,7 @@ import { DOMSVGFactory } from "./display/svg_factory.js";
|
||||
import { DrawLayer } from "./display/draw_layer.js";
|
||||
import { GlobalWorkerOptions } from "./display/worker_options.js";
|
||||
import { HighlightOutliner } from "./display/editor/drawers/highlight.js";
|
||||
import { isValidExplicitDest } from "./display/api_utils.js";
|
||||
import { SignatureExtractor } from "./display/editor/drawers/signaturedraw.js";
|
||||
import { TextLayer } from "./display/text_layer.js";
|
||||
import { TouchManager } from "./display/touch_manager.js";
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
AbortException,
|
||||
UnknownErrorException,
|
||||
} from "../../src/shared/util.js";
|
||||
import { LoopbackPort } from "../../src/display/api.js";
|
||||
import { LoopbackPort } from "../../src/display/api_utils.js";
|
||||
import { MessageHandler } from "../../src/shared/message_handler.js";
|
||||
|
||||
describe("message_handler", function () {
|
||||
|
||||
@ -38,7 +38,6 @@ import {
|
||||
import {
|
||||
build,
|
||||
getDocument,
|
||||
isValidExplicitDest,
|
||||
PDFDataRangeTransport,
|
||||
PDFWorker,
|
||||
version,
|
||||
@ -66,6 +65,7 @@ import { ColorPicker } from "../../src/display/editor/color_picker.js";
|
||||
import { DOMSVGFactory } from "../../src/display/svg_factory.js";
|
||||
import { DrawLayer } from "../../src/display/draw_layer.js";
|
||||
import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
|
||||
import { isValidExplicitDest } from "../../src/display/api_utils.js";
|
||||
import { SignatureExtractor } from "../../src/display/editor/drawers/signaturedraw.js";
|
||||
import { TextLayer } from "../../src/display/text_layer.js";
|
||||
import { TouchManager } from "../../src/display/touch_manager.js";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user