mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-08-01 20:07:22 +02:00
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:
parent
4a8fb4dde1
commit
54d8c5e7b4
@ -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() {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 };
|
||||||
|
|||||||
@ -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,
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user