Add an abstract BasePDFStreamReader class, that all the old IPDFStreamReader implementations inherit from

Given that there's no less than *five* different, but very similar, implementations this helps reduce code duplication and simplifies maintenance.

Also, remove the `rangeChunkSize` not defined checks in all the relevant stream-constructor implementations.
Note how the API, since some time, always validates *and* provides that parameter when creating a `BasePDFStreamReader`-instance.
This commit is contained in:
Jonas Jenwald 2026-01-30 08:01:55 +01:00
parent 4a8fb4dde1
commit 54d8c5e7b4
7 changed files with 184 additions and 269 deletions

View File

@ -13,7 +13,10 @@
* limitations under the License. * limitations under the License.
*/ */
import { BasePDFStream } from "../shared/base_pdf_stream.js"; import {
BasePDFStream,
BasePDFStreamReader,
} from "../shared/base_pdf_stream.js";
class PDFWorkerStream extends BasePDFStream { class PDFWorkerStream extends BasePDFStream {
constructor(source) { constructor(source) {
@ -21,42 +24,23 @@ class PDFWorkerStream extends BasePDFStream {
} }
} }
/** @implements {IPDFStreamReader} */ class PDFWorkerStreamReader extends BasePDFStreamReader {
class PDFWorkerStreamReader { _reader = null;
constructor(stream) {
const { msgHandler } = stream._source;
this.onProgress = null;
this._contentLength = null; constructor(stream) {
this._isRangeSupported = false; super(stream);
this._isStreamingSupported = false; const { msgHandler } = stream._source;
const readableStream = msgHandler.sendWithStream("GetReader"); const readableStream = msgHandler.sendWithStream("GetReader");
this._reader = readableStream.getReader(); this._reader = readableStream.getReader();
this._headersReady = msgHandler msgHandler.sendWithPromise("ReaderHeadersReady").then(data => {
.sendWithPromise("ReaderHeadersReady") this._contentLength = data.contentLength;
.then(data => { this._isStreamingSupported = data.isStreamingSupported;
this._isStreamingSupported = data.isStreamingSupported; this._isRangeSupported = data.isRangeSupported;
this._isRangeSupported = data.isRangeSupported;
this._contentLength = data.contentLength;
});
}
get headersReady() { this._headersCapability.resolve();
return this._headersReady; }, this._headersCapability.reject);
}
get contentLength() {
return this._contentLength;
}
get isStreamingSupported() {
return this._isStreamingSupported;
}
get isRangeSupported() {
return this._isRangeSupported;
} }
async read() { async read() {

View File

@ -14,6 +14,10 @@
*/ */
import { AbortException, warn } from "../shared/util.js"; import { AbortException, warn } from "../shared/util.js";
import {
BasePDFStream,
BasePDFStreamReader,
} from "../shared/base_pdf_stream.js";
import { import {
createHeaders, createHeaders,
createResponseError, createResponseError,
@ -22,7 +26,6 @@ import {
validateRangeRequestCapabilities, validateRangeRequestCapabilities,
validateResponseStatus, validateResponseStatus,
} from "./network_utils.js"; } from "./network_utils.js";
import { BasePDFStream } from "../shared/base_pdf_stream.js";
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
throw new Error( throw new Error(
@ -30,15 +33,15 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
); );
} }
function createFetchOptions(headers, withCredentials, abortController) { function fetchUrl(url, headers, withCredentials, abortController) {
return { return fetch(url, {
method: "GET", method: "GET",
headers, headers,
signal: abortController.signal, signal: abortController.signal,
mode: "cors", mode: "cors",
credentials: withCredentials ? "include" : "same-origin", credentials: withCredentials ? "include" : "same-origin",
redirect: "follow", redirect: "follow",
}; });
} }
function getArrayBuffer(val) { function getArrayBuffer(val) {
@ -62,34 +65,29 @@ class PDFFetchStream extends BasePDFStream {
} }
} }
/** @implements {IPDFStreamReader} */ class PDFFetchStreamReader extends BasePDFStreamReader {
class PDFFetchStreamReader { _abortController = new AbortController();
constructor(stream) {
this._stream = stream;
this._reader = null;
this._loaded = 0;
this._filename = null;
const source = stream._source;
this._withCredentials = source.withCredentials || false;
this._contentLength = source.length;
this._headersCapability = Promise.withResolvers();
this._disableRange = source.disableRange || false;
this._rangeChunkSize = source.rangeChunkSize;
if (!this._rangeChunkSize && !this._disableRange) {
this._disableRange = true;
}
this._abortController = new AbortController(); _reader = null;
this._isStreamingSupported = !source.disableStream;
this._isRangeSupported = !source.disableRange; constructor(stream) {
super(stream);
const {
disableRange,
disableStream,
length,
rangeChunkSize,
url,
withCredentials,
} = stream._source;
this._contentLength = length;
this._isStreamingSupported = !disableStream;
this._isRangeSupported = !disableRange;
// Always create a copy of the headers. // Always create a copy of the headers.
const headers = new Headers(stream.headers); const headers = new Headers(stream.headers);
const url = source.url; fetchUrl(url, headers, withCredentials, this._abortController)
fetch(
url,
createFetchOptions(headers, this._withCredentials, this._abortController)
)
.then(response => { .then(response => {
stream._responseOrigin = getResponseOrigin(response.url); stream._responseOrigin = getResponseOrigin(response.url);
@ -97,7 +95,6 @@ class PDFFetchStreamReader {
throw createResponseError(response.status, url); throw createResponseError(response.status, url);
} }
this._reader = response.body.getReader(); this._reader = response.body.getReader();
this._headersCapability.resolve();
const responseHeaders = response.headers; const responseHeaders = response.headers;
@ -105,8 +102,8 @@ class PDFFetchStreamReader {
validateRangeRequestCapabilities({ validateRangeRequestCapabilities({
responseHeaders, responseHeaders,
isHttp: stream.isHttp, isHttp: stream.isHttp,
rangeChunkSize: this._rangeChunkSize, rangeChunkSize,
disableRange: this._disableRange, disableRange,
}); });
this._isRangeSupported = allowRangeRequests; this._isRangeSupported = allowRangeRequests;
@ -120,30 +117,10 @@ class PDFFetchStreamReader {
if (!this._isStreamingSupported && this._isRangeSupported) { if (!this._isStreamingSupported && this._isRangeSupported) {
this.cancel(new AbortException("Streaming is disabled.")); this.cancel(new AbortException("Streaming is disabled."));
} }
this._headersCapability.resolve();
}) })
.catch(this._headersCapability.reject); .catch(this._headersCapability.reject);
this.onProgress = null;
}
get headersReady() {
return this._headersCapability.promise;
}
get filename() {
return this._filename;
}
get contentLength() {
return this._contentLength;
}
get isRangeSupported() {
return this._isRangeSupported;
}
get isStreamingSupported() {
return this._isStreamingSupported;
} }
async read() { async read() {
@ -182,10 +159,7 @@ class PDFFetchStreamRangeReader {
headers.append("Range", `bytes=${begin}-${end - 1}`); headers.append("Range", `bytes=${begin}-${end - 1}`);
const url = source.url; const url = source.url;
fetch( fetchUrl(url, headers, this._withCredentials, this._abortController)
url,
createFetchOptions(headers, this._withCredentials, this._abortController)
)
.then(response => { .then(response => {
const responseOrigin = getResponseOrigin(response.url); const responseOrigin = getResponseOrigin(response.url);

View File

@ -14,6 +14,10 @@
*/ */
import { assert, stringToBytes, warn } from "../shared/util.js"; import { assert, stringToBytes, warn } from "../shared/util.js";
import {
BasePDFStream,
BasePDFStreamReader,
} from "../shared/base_pdf_stream.js";
import { import {
createHeaders, createHeaders,
createResponseError, createResponseError,
@ -21,7 +25,6 @@ import {
getResponseOrigin, getResponseOrigin,
validateRangeRequestCapabilities, validateRangeRequestCapabilities,
} from "./network_utils.js"; } from "./network_utils.js";
import { BasePDFStream } from "../shared/base_pdf_stream.js";
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
throw new Error( throw new Error(
@ -42,7 +45,7 @@ class PDFNetworkStream extends BasePDFStream {
_responseOrigin = null; _responseOrigin = null;
constructor(source) { constructor(source) {
super(source, PDFNetworkStreamFullReader, PDFNetworkStreamRangeReader); super(source, PDFNetworkStreamReader, PDFNetworkStreamRangeReader);
this.url = source.url; this.url = source.url;
this.isHttp = /^https?:/i.test(this.url); this.isHttp = /^https?:/i.test(this.url);
this.headers = createHeaders(this.isHttp, source.httpHeaders); this.headers = createHeaders(this.isHttp, source.httpHeaders);
@ -166,40 +169,34 @@ class PDFNetworkStream extends BasePDFStream {
} }
} }
/** @implements {IPDFStreamReader} */ class PDFNetworkStreamReader extends BasePDFStreamReader {
class PDFNetworkStreamFullReader { _cachedChunks = [];
_done = false;
_requests = [];
_storedError = null;
constructor(stream) { constructor(stream) {
this._stream = stream; super(stream);
const { disableRange, length, rangeChunkSize } = stream._source; const { length } = stream._source;
this._contentLength = length;
// Note that `XMLHttpRequest` doesn't support streaming, and range requests
// will be enabled (if supported) in `this.#onHeadersReceived` below.
this._fullRequestXhr = stream._request({ this._fullRequestXhr = stream._request({
onHeadersReceived: this._onHeadersReceived.bind(this), onHeadersReceived: this.#onHeadersReceived.bind(this),
onDone: this._onDone.bind(this), onDone: this.#onDone.bind(this),
onError: this._onError.bind(this), onError: this.#onError.bind(this),
onProgress: this._onProgress.bind(this), onProgress: this.#onProgress.bind(this),
}); });
this._headersCapability = Promise.withResolvers();
this._disableRange = disableRange || false;
this._contentLength = length; // Optional
this._rangeChunkSize = rangeChunkSize;
if (!this._rangeChunkSize && !this._disableRange) {
this._disableRange = true;
}
this._isStreamingSupported = false;
this._isRangeSupported = false;
this._cachedChunks = [];
this._requests = [];
this._done = false;
this._storedError = undefined;
this._filename = null;
this.onProgress = null;
} }
_onHeadersReceived() { #onHeadersReceived() {
const stream = this._stream; const stream = this._stream;
const { disableRange, rangeChunkSize } = stream._source;
const fullRequestXhr = this._fullRequestXhr; const fullRequestXhr = this._fullRequestXhr;
stream._responseOrigin = getResponseOrigin(fullRequestXhr.responseURL); stream._responseOrigin = getResponseOrigin(fullRequestXhr.responseURL);
@ -222,8 +219,8 @@ class PDFNetworkStreamFullReader {
validateRangeRequestCapabilities({ validateRangeRequestCapabilities({
responseHeaders, responseHeaders,
isHttp: stream.isHttp, isHttp: stream.isHttp,
rangeChunkSize: this._rangeChunkSize, rangeChunkSize,
disableRange: this._disableRange, disableRange,
}); });
if (allowRangeRequests) { if (allowRangeRequests) {
@ -245,10 +242,10 @@ class PDFNetworkStreamFullReader {
this._headersCapability.resolve(); this._headersCapability.resolve();
} }
_onDone(chunk) { #onDone(chunk) {
if (this._requests.length > 0) { if (this._requests.length > 0) {
const requestCapability = this._requests.shift(); const capability = this._requests.shift();
requestCapability.resolve({ value: chunk, done: false }); capability.resolve({ value: chunk, done: false });
} else { } else {
this._cachedChunks.push(chunk); this._cachedChunks.push(chunk);
} }
@ -256,49 +253,29 @@ class PDFNetworkStreamFullReader {
if (this._cachedChunks.length > 0) { if (this._cachedChunks.length > 0) {
return; return;
} }
for (const requestCapability of this._requests) { for (const capability of this._requests) {
requestCapability.resolve({ value: undefined, done: true }); capability.resolve({ value: undefined, done: true });
} }
this._requests.length = 0; this._requests.length = 0;
} }
_onError(status) { #onError(status) {
this._storedError = createResponseError(status, this._stream.url); this._storedError = createResponseError(status, this._stream.url);
this._headersCapability.reject(this._storedError); this._headersCapability.reject(this._storedError);
for (const requestCapability of this._requests) { for (const capability of this._requests) {
requestCapability.reject(this._storedError); capability.reject(this._storedError);
} }
this._requests.length = 0; this._requests.length = 0;
this._cachedChunks.length = 0; this._cachedChunks.length = 0;
} }
_onProgress(evt) { #onProgress(evt) {
this.onProgress?.({ this.onProgress?.({
loaded: evt.loaded, loaded: evt.loaded,
total: evt.lengthComputable ? evt.total : this._contentLength, total: evt.lengthComputable ? evt.total : this._contentLength,
}); });
} }
get filename() {
return this._filename;
}
get isRangeSupported() {
return this._isRangeSupported;
}
get isStreamingSupported() {
return this._isStreamingSupported;
}
get contentLength() {
return this._contentLength;
}
get headersReady() {
return this._headersCapability.promise;
}
async read() { async read() {
await this._headersCapability.promise; await this._headersCapability.promise;
@ -312,16 +289,16 @@ class PDFNetworkStreamFullReader {
if (this._done) { if (this._done) {
return { value: undefined, done: true }; return { value: undefined, done: true };
} }
const requestCapability = Promise.withResolvers(); const capability = Promise.withResolvers();
this._requests.push(requestCapability); this._requests.push(capability);
return requestCapability.promise; return capability.promise;
} }
cancel(reason) { cancel(reason) {
this._done = true; this._done = true;
this._headersCapability.reject(reason); this._headersCapability.reject(reason);
for (const requestCapability of this._requests) { for (const capability of this._requests) {
requestCapability.resolve({ value: undefined, done: true }); capability.resolve({ value: undefined, done: true });
} }
this._requests.length = 0; this._requests.length = 0;

View File

@ -15,7 +15,10 @@
/* globals process */ /* globals process */
import { AbortException, assert, warn } from "../shared/util.js"; import { AbortException, assert, warn } from "../shared/util.js";
import { BasePDFStream } from "../shared/base_pdf_stream.js"; import {
BasePDFStream,
BasePDFStreamReader,
} from "../shared/base_pdf_stream.js";
import { createResponseError } from "./network_utils.js"; import { createResponseError } from "./network_utils.js";
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
@ -63,7 +66,7 @@ function getArrayBuffer(val) {
class PDFNodeStream extends BasePDFStream { class PDFNodeStream extends BasePDFStream {
constructor(source) { constructor(source) {
super(source, PDFNodeStreamFsFullReader, PDFNodeStreamFsRangeReader); super(source, PDFNodeStreamReader, PDFNodeStreamFsRangeReader);
this.url = parseUrlOrPath(source.url); this.url = parseUrlOrPath(source.url);
assert( assert(
this.url.protocol === "file:", this.url.protocol === "file:",
@ -72,26 +75,17 @@ class PDFNodeStream extends BasePDFStream {
} }
} }
class PDFNodeStreamFsFullReader { class PDFNodeStreamReader extends BasePDFStreamReader {
_headersCapability = Promise.withResolvers();
_reader = null; _reader = null;
constructor(stream) { constructor(stream) {
this.onProgress = null; super(stream);
const source = stream._source; const { disableRange, disableStream, length, rangeChunkSize } =
this._contentLength = source.length; // optional stream._source;
this._loaded = 0;
this._filename = null;
this._disableRange = source.disableRange || false; this._contentLength = length;
this._rangeChunkSize = source.rangeChunkSize; this._isStreamingSupported = !disableStream;
if (!this._rangeChunkSize && !this._disableRange) { this._isRangeSupported = !disableRange;
this._disableRange = true;
}
this._isStreamingSupported = !source.disableStream;
this._isRangeSupported = !source.disableRange;
const url = stream.url; const url = stream.url;
const fs = process.getBuiltinModule("fs"); const fs = process.getBuiltinModule("fs");
@ -104,7 +98,7 @@ class PDFNodeStreamFsFullReader {
this._reader = readableStream.getReader(); this._reader = readableStream.getReader();
const { size } = stat; const { size } = stat;
if (size <= 2 * this._rangeChunkSize) { if (size <= 2 * rangeChunkSize) {
// The file size is smaller than the size of two chunks, so it doesn't // The file size is smaller than the size of two chunks, so it doesn't
// make any sense to abort the request and retry with a range request. // make any sense to abort the request and retry with a range request.
this._isRangeSupported = false; this._isRangeSupported = false;
@ -128,26 +122,6 @@ class PDFNodeStreamFsFullReader {
}); });
} }
get headersReady() {
return this._headersCapability.promise;
}
get filename() {
return this._filename;
}
get contentLength() {
return this._contentLength;
}
get isRangeSupported() {
return this._isRangeSupported;
}
get isStreamingSupported() {
return this._isStreamingSupported;
}
async read() { async read() {
await this._headersCapability.promise; await this._headersCapability.promise;
const { value, done } = await this._reader.read(); const { value, done } = await this._reader.read();

View File

@ -13,12 +13,14 @@
* limitations under the License. * limitations under the License.
*/ */
/** @typedef {import("../interfaces").IPDFStreamReader} IPDFStreamReader */
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
/** @typedef {import("../interfaces").IPDFStreamRangeReader} IPDFStreamRangeReader */ /** @typedef {import("../interfaces").IPDFStreamRangeReader} IPDFStreamRangeReader */
import {
BasePDFStream,
BasePDFStreamReader,
} from "../shared/base_pdf_stream.js";
import { assert } from "../shared/util.js"; import { assert } from "../shared/util.js";
import { BasePDFStream } from "../shared/base_pdf_stream.js";
import { isPdfFile } from "./display_utils.js"; import { isPdfFile } from "./display_utils.js";
function getArrayBuffer(val) { function getArrayBuffer(val) {
@ -30,7 +32,7 @@ function getArrayBuffer(val) {
} }
class PDFDataTransportStream extends BasePDFStream { class PDFDataTransportStream extends BasePDFStream {
_pdfDataRangeTransport = null; _progressiveDone = false;
_queuedChunks = []; _queuedChunks = [];
@ -40,22 +42,14 @@ class PDFDataTransportStream extends BasePDFStream {
PDFDataTransportStreamReader, PDFDataTransportStreamReader,
PDFDataTransportStreamRangeReader PDFDataTransportStreamRangeReader
); );
const { pdfDataRangeTransport, disableRange, disableStream } = source; const { pdfDataRangeTransport } = source;
const { length, initialData, progressiveDone, contentDispositionFilename } = const { initialData, progressiveDone } = pdfDataRangeTransport;
pdfDataRangeTransport;
this._progressiveDone = progressiveDone;
this._contentDispositionFilename = contentDispositionFilename;
if (initialData?.length > 0) { if (initialData?.length > 0) {
const buffer = getArrayBuffer(initialData); const buffer = getArrayBuffer(initialData);
this._queuedChunks.push(buffer); this._queuedChunks.push(buffer);
} }
this._progressiveDone = progressiveDone;
this._pdfDataRangeTransport = pdfDataRangeTransport;
this._isStreamingSupported = !disableStream;
this._isRangeSupported = !disableRange;
this._contentLength = length;
pdfDataRangeTransport.addRangeListener((begin, chunk) => { pdfDataRangeTransport.addRangeListener((begin, chunk) => {
this.#onReceiveData(begin, chunk); this.#onReceiveData(begin, chunk);
@ -113,7 +107,7 @@ class PDFDataTransportStream extends BasePDFStream {
if (reader) { if (reader) {
reader.onDone = () => this._rangeReaders.delete(reader); reader.onDone = () => this._rangeReaders.delete(reader);
this._pdfDataRangeTransport.requestDataRange(begin, end); this._source.pdfDataRangeTransport.requestDataRange(begin, end);
} }
return reader; return reader;
} }
@ -121,27 +115,37 @@ class PDFDataTransportStream extends BasePDFStream {
cancelAllRequests(reason) { cancelAllRequests(reason) {
super.cancelAllRequests(reason); super.cancelAllRequests(reason);
this._pdfDataRangeTransport.abort(); this._source.pdfDataRangeTransport.abort();
} }
} }
/** @implements {IPDFStreamReader} */ class PDFDataTransportStreamReader extends BasePDFStreamReader {
class PDFDataTransportStreamReader { _done = false;
_queuedChunks = null;
_requests = [];
constructor(stream) { constructor(stream) {
this._stream = stream; super(stream);
this._done = stream._progressiveDone || false; const { pdfDataRangeTransport, disableRange, disableStream } =
this._filename = isPdfFile(stream._contentDispositionFilename) stream._source;
? stream._contentDispositionFilename const { length, contentDispositionFilename } = pdfDataRangeTransport;
: null;
this._queuedChunks = stream._queuedChunks || []; this._queuedChunks = stream._queuedChunks || [];
this._loaded = 0;
for (const chunk of this._queuedChunks) { for (const chunk of this._queuedChunks) {
this._loaded += chunk.byteLength; this._loaded += chunk.byteLength;
} }
this._requests = []; this._done = stream._progressiveDone;
this._headersReady = Promise.resolve();
this.onProgress = null; this._contentLength = length;
this._isStreamingSupported = !disableStream;
this._isRangeSupported = !disableRange;
if (isPdfFile(contentDispositionFilename)) {
this._filename = contentDispositionFilename;
}
this._headersCapability.resolve();
} }
_enqueue(chunk) { _enqueue(chunk) {
@ -149,34 +153,14 @@ class PDFDataTransportStreamReader {
return; // Ignore new data. return; // Ignore new data.
} }
if (this._requests.length > 0) { if (this._requests.length > 0) {
const requestCapability = this._requests.shift(); const capability = this._requests.shift();
requestCapability.resolve({ value: chunk, done: false }); capability.resolve({ value: chunk, done: false });
} else { } else {
this._queuedChunks.push(chunk); this._queuedChunks.push(chunk);
} }
this._loaded += chunk.byteLength; this._loaded += chunk.byteLength;
} }
get headersReady() {
return this._headersReady;
}
get filename() {
return this._filename;
}
get isRangeSupported() {
return this._stream._isRangeSupported;
}
get isStreamingSupported() {
return this._stream._isStreamingSupported;
}
get contentLength() {
return this._stream._contentLength;
}
async read() { async read() {
if (this._queuedChunks.length > 0) { if (this._queuedChunks.length > 0) {
const chunk = this._queuedChunks.shift(); const chunk = this._queuedChunks.shift();
@ -185,24 +169,21 @@ class PDFDataTransportStreamReader {
if (this._done) { if (this._done) {
return { value: undefined, done: true }; return { value: undefined, done: true };
} }
const requestCapability = Promise.withResolvers(); const capability = Promise.withResolvers();
this._requests.push(requestCapability); this._requests.push(capability);
return requestCapability.promise; return capability.promise;
} }
cancel(reason) { cancel(reason) {
this._done = true; this._done = true;
for (const requestCapability of this._requests) { for (const capability of this._requests) {
requestCapability.resolve({ value: undefined, done: true }); capability.resolve({ value: undefined, done: true });
} }
this._requests.length = 0; this._requests.length = 0;
} }
progressiveDone() { progressiveDone() {
if (this._done) { this._done ||= true;
return;
}
this._done = true;
} }
} }

View File

@ -94,18 +94,38 @@ class BasePDFStream {
/** /**
* Interface for a PDF binary data reader. * Interface for a PDF binary data reader.
*
* @interface
*/ */
class IPDFStreamReader { class BasePDFStreamReader {
constructor() { /**
/** * Sets or gets the progress callback. The callback can be useful when the
* Sets or gets the progress callback. The callback can be useful when the * isStreamingSupported property of the object is defined as false.
* isStreamingSupported property of the object is defined as false. * The callback is called with one parameter: an object with the loaded and
* The callback is called with one parameter: an object with the loaded and * total properties.
* total properties. */
*/ onProgress = null;
this.onProgress = null;
_contentLength = 0;
_filename = null;
_headersCapability = Promise.withResolvers();
_isRangeSupported = false;
_isStreamingSupported = false;
_loaded = 0;
_stream = null;
constructor(stream) {
if (
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
this.constructor === BasePDFStreamReader
) {
unreachable("Cannot initialize BasePDFStreamReader.");
}
this._stream = stream;
} }
/** /**
@ -114,7 +134,7 @@ class IPDFStreamReader {
* @type {Promise} * @type {Promise}
*/ */
get headersReady() { get headersReady() {
return Promise.resolve(); return this._headersCapability.promise;
} }
/** /**
@ -124,7 +144,7 @@ class IPDFStreamReader {
* header is missing/invalid. * header is missing/invalid.
*/ */
get filename() { get filename() {
return null; return this._filename;
} }
/** /**
@ -133,7 +153,7 @@ class IPDFStreamReader {
* @type {number} The data length (or 0 if unknown). * @type {number} The data length (or 0 if unknown).
*/ */
get contentLength() { get contentLength() {
return 0; return this._contentLength;
} }
/** /**
@ -143,7 +163,7 @@ class IPDFStreamReader {
* @type {boolean} * @type {boolean}
*/ */
get isRangeSupported() { get isRangeSupported() {
return false; return this._isRangeSupported;
} }
/** /**
@ -152,7 +172,7 @@ class IPDFStreamReader {
* @type {boolean} * @type {boolean}
*/ */
get isStreamingSupported() { get isStreamingSupported() {
return false; return this._isStreamingSupported;
} }
/** /**
@ -163,13 +183,17 @@ class IPDFStreamReader {
* set to true. * set to true.
* @returns {Promise} * @returns {Promise}
*/ */
async read() {} async read() {
unreachable("Abstract method `read` called");
}
/** /**
* Cancels all pending read requests and closes the stream. * Cancels all pending read requests and closes the stream.
* @param {Object} reason * @param {Object} reason
*/ */
cancel(reason) {} cancel(reason) {
unreachable("Abstract method `cancel` called");
}
} }
/** /**
@ -195,4 +219,4 @@ class IPDFStreamRangeReader {
cancel(reason) {} cancel(reason) {}
} }
export { BasePDFStream, IPDFStreamRangeReader, IPDFStreamReader }; export { BasePDFStream, BasePDFStreamReader, IPDFStreamRangeReader };

View File

@ -35,6 +35,7 @@ describe("fetch_stream", function () {
it("read with streaming", async function () { it("read with streaming", async function () {
const stream = new PDFFetchStream({ const stream = new PDFFetchStream({
url: getPdfUrl(), url: getPdfUrl(),
rangeChunkSize: 32768,
disableStream: false, disableStream: false,
disableRange: true, disableRange: true,
}); });