Skip to content

Commit 0105237

Browse files
committed
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.
1 parent e921533 commit 0105237

File tree

5 files changed

+179
-146
lines changed

5 files changed

+179
-146
lines changed

src/display/api.js

Lines changed: 9 additions & 143 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,
@@ -71,7 +76,6 @@ import { PDFNodeStream } from "display-node_stream";
7176
import { TextLayer } from "./text_layer.js";
7277
import { XfaText } from "./xfa_text.js";
7378

74-
const DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536
7579
const RENDERING_CANCELLED_TIMEOUT = 100; // ms
7680

7781
/**
@@ -111,7 +115,7 @@ const RENDERING_CANCELLED_TIMEOUT = 100; // ms
111115
* @property {PDFDataRangeTransport} [range] - Allows for using a custom range
112116
* transport implementation.
113117
* @property {number} [rangeChunkSize] - Specify maximum number of bytes fetched
114-
* per range request. The default value is {@link DEFAULT_RANGE_CHUNK_SIZE}.
118+
* per range request. The default value is 65536 (= 2^16).
115119
* @property {PDFWorker} [worker] - The worker that will be used for loading and
116120
* parsing the PDF data.
117121
* @property {number} [verbosity] - Controls the logging level; the constants
@@ -255,7 +259,7 @@ function getDocument(src = {}) {
255259
const rangeChunkSize =
256260
Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0
257261
? src.rangeChunkSize
258-
: DEFAULT_RANGE_CHUNK_SIZE;
262+
: 2 ** 16;
259263
let worker = src.worker instanceof PDFWorker ? src.worker : null;
260264
const verbosity = src.verbosity;
261265
// Ignore "data:"-URLs, since they can't be used to recover valid absolute
@@ -507,94 +511,6 @@ function getDocument(src = {}) {
507511
return task;
508512
}
509513

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-
598514
/**
599515
* @typedef {Object} OnProgressParameters
600516
* @property {number} loaded - Currently loaded number of bytes.
@@ -2012,54 +1928,6 @@ class PDFPageProxy {
20121928
}
20131929
}
20141930

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-
20631931
/**
20641932
* @typedef {Object} PDFWorkerParameters
20651933
* @property {string} [name] - The name of the worker.
@@ -3511,8 +3379,6 @@ const build =
35113379
export {
35123380
build,
35133381
getDocument,
3514-
isValidExplicitDest,
3515-
LoopbackPort,
35163382
PDFDataRangeTransport,
35173383
PDFDocumentLoadingTask,
35183384
PDFDocumentProxy,

src/display/api_utils.js

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/* Copyright 2012 Mozilla Foundation
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
import {
17+
_isValidExplicitDest,
18+
isNodeJS,
19+
stringToBytes,
20+
warn,
21+
} from "../shared/util.js";
22+
23+
function getUrlProp(val) {
24+
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
25+
return null; // The 'url' is unused with `PDFDataRangeTransport`.
26+
}
27+
if (val instanceof URL) {
28+
return val.href;
29+
}
30+
if (typeof val === "string") {
31+
if (
32+
typeof PDFJSDev !== "undefined" &&
33+
PDFJSDev.test("GENERIC") &&
34+
isNodeJS
35+
) {
36+
return val; // Use the url as-is in Node.js environments.
37+
}
38+
39+
// The full path is required in the 'url' field.
40+
const url = URL.parse(val, window.location);
41+
if (url) {
42+
return url.href;
43+
}
44+
}
45+
throw new Error(
46+
"Invalid PDF url data: " +
47+
"either string or URL-object is expected in the url property."
48+
);
49+
}
50+
51+
function getDataProp(val) {
52+
// Converting string or array-like data to Uint8Array.
53+
if (
54+
typeof PDFJSDev !== "undefined" &&
55+
PDFJSDev.test("GENERIC") &&
56+
isNodeJS &&
57+
typeof Buffer !== "undefined" && // eslint-disable-line no-undef
58+
val instanceof Buffer // eslint-disable-line no-undef
59+
) {
60+
throw new Error(
61+
"Please provide binary data as `Uint8Array`, rather than `Buffer`."
62+
);
63+
}
64+
if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
65+
// Use the data as-is when it's already a Uint8Array that completely
66+
// "utilizes" its underlying ArrayBuffer, to prevent any possible
67+
// issues when transferring it to the worker-thread.
68+
return val;
69+
}
70+
if (typeof val === "string") {
71+
return stringToBytes(val);
72+
}
73+
if (
74+
val instanceof ArrayBuffer ||
75+
ArrayBuffer.isView(val) ||
76+
(typeof val === "object" && !isNaN(val?.length))
77+
) {
78+
return new Uint8Array(val);
79+
}
80+
throw new Error(
81+
"Invalid PDF binary data: either TypedArray, " +
82+
"string, or array-like object is expected in the data property."
83+
);
84+
}
85+
86+
function getFactoryUrlProp(val) {
87+
if (typeof val !== "string") {
88+
return null;
89+
}
90+
if (val.endsWith("/")) {
91+
return val;
92+
}
93+
throw new Error(`Invalid factory url: "${val}" must include trailing slash.`);
94+
}
95+
96+
const isRefProxy = v =>
97+
typeof v === "object" &&
98+
Number.isInteger(v?.num) &&
99+
v.num >= 0 &&
100+
Number.isInteger(v?.gen) &&
101+
v.gen >= 0;
102+
103+
const isNameProxy = v => typeof v === "object" && typeof v?.name === "string";
104+
105+
const isValidExplicitDest = _isValidExplicitDest.bind(
106+
null,
107+
/* validRef = */ isRefProxy,
108+
/* validName = */ isNameProxy
109+
);
110+
111+
class LoopbackPort {
112+
#listeners = new Map();
113+
114+
#deferred = Promise.resolve();
115+
116+
postMessage(obj, transfer) {
117+
const event = {
118+
data: structuredClone(obj, transfer ? { transfer } : null),
119+
};
120+
121+
this.#deferred.then(() => {
122+
for (const [listener] of this.#listeners) {
123+
listener.call(this, event);
124+
}
125+
});
126+
}
127+
128+
addEventListener(name, listener, options = null) {
129+
let rmAbort = null;
130+
if (options?.signal instanceof AbortSignal) {
131+
const { signal } = options;
132+
if (signal.aborted) {
133+
warn("LoopbackPort - cannot use an `aborted` signal.");
134+
return;
135+
}
136+
const onAbort = () => this.removeEventListener(name, listener);
137+
rmAbort = () => signal.removeEventListener("abort", onAbort);
138+
139+
signal.addEventListener("abort", onAbort);
140+
}
141+
this.#listeners.set(listener, rmAbort);
142+
}
143+
144+
removeEventListener(name, listener) {
145+
const rmAbort = this.#listeners.get(listener);
146+
rmAbort?.();
147+
148+
this.#listeners.delete(listener);
149+
}
150+
151+
terminate() {
152+
for (const [, rmAbort] of this.#listeners) {
153+
rmAbort?.();
154+
}
155+
this.#listeners.clear();
156+
}
157+
}
158+
159+
export {
160+
getDataProp,
161+
getFactoryUrlProp,
162+
getUrlProp,
163+
isNameProxy,
164+
isRefProxy,
165+
isValidExplicitDest,
166+
LoopbackPort,
167+
};

src/pdf.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ import {
4747
import {
4848
build,
4949
getDocument,
50-
isValidExplicitDest,
5150
PDFDataRangeTransport,
5251
PDFWorker,
5352
version,
@@ -76,6 +75,7 @@ import { DOMSVGFactory } from "./display/svg_factory.js";
7675
import { DrawLayer } from "./display/draw_layer.js";
7776
import { GlobalWorkerOptions } from "./display/worker_options.js";
7877
import { HighlightOutliner } from "./display/editor/drawers/highlight.js";
78+
import { isValidExplicitDest } from "./display/api_utils.js";
7979
import { SignatureExtractor } from "./display/editor/drawers/signaturedraw.js";
8080
import { TextLayer } from "./display/text_layer.js";
8181
import { TouchManager } from "./display/touch_manager.js";

0 commit comments

Comments
 (0)