Skip to content

Commit d86045e

Browse files
Merge pull request #19632 from Snuffleupagus/issue-15085
[api-minor] Attempt to support fetching the raw data of the PDF document from the `PDFDocumentLoadingTask`-instance (issue 15085)
2 parents f8c2a94 + 9e8d4e4 commit d86045e

3 files changed

Lines changed: 92 additions & 30 deletions

File tree

src/display/api.js

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -629,39 +629,48 @@ const isValidExplicitDest = _isValidExplicitDest.bind(
629629
class PDFDocumentLoadingTask {
630630
static #docId = 0;
631631

632-
constructor() {
633-
this._capability = Promise.withResolvers();
634-
this._transport = null;
635-
this._worker = null;
632+
/**
633+
* @private
634+
*/
635+
_capability = Promise.withResolvers();
636636

637-
/**
638-
* Unique identifier for the document loading task.
639-
* @type {string}
640-
*/
641-
this.docId = `d${PDFDocumentLoadingTask.#docId++}`;
637+
/**
638+
* @private
639+
*/
640+
_transport = null;
642641

643-
/**
644-
* Whether the loading task is destroyed or not.
645-
* @type {boolean}
646-
*/
647-
this.destroyed = false;
642+
/**
643+
* @private
644+
*/
645+
_worker = null;
648646

649-
/**
650-
* Callback to request a password if a wrong or no password was provided.
651-
* The callback receives two parameters: a function that should be called
652-
* with the new password, and a reason (see {@link PasswordResponses}).
653-
* @type {function}
654-
*/
655-
this.onPassword = null;
647+
/**
648+
* Unique identifier for the document loading task.
649+
* @type {string}
650+
*/
651+
docId = `d${PDFDocumentLoadingTask.#docId++}`;
656652

657-
/**
658-
* Callback to be able to monitor the loading progress of the PDF file
659-
* (necessary to implement e.g. a loading bar).
660-
* The callback receives an {@link OnProgressParameters} argument.
661-
* @type {function}
662-
*/
663-
this.onProgress = null;
664-
}
653+
/**
654+
* Whether the loading task is destroyed or not.
655+
* @type {boolean}
656+
*/
657+
destroyed = false;
658+
659+
/**
660+
* Callback to request a password if a wrong or no password was provided.
661+
* The callback receives two parameters: a function that should be called
662+
* with the new password, and a reason (see {@link PasswordResponses}).
663+
* @type {function}
664+
*/
665+
onPassword = null;
666+
667+
/**
668+
* Callback to be able to monitor the loading progress of the PDF file
669+
* (necessary to implement e.g. a loading bar).
670+
* The callback receives an {@link OnProgressParameters} argument.
671+
* @type {function}
672+
*/
673+
onProgress = null;
665674

666675
/**
667676
* Promise for document loading task completion.
@@ -699,6 +708,16 @@ class PDFDocumentLoadingTask {
699708
this._worker?.destroy();
700709
this._worker = null;
701710
}
711+
712+
/**
713+
* Attempt to fetch the raw data of the PDF document, when e.g.
714+
* - An exception was thrown during document initialization.
715+
* - An `onPassword` callback is delaying initialization.
716+
* @returns {Promise<Uint8Array>}
717+
*/
718+
async getData() {
719+
return this._transport.getData();
720+
}
702721
}
703722

704723
/**

test/unit/api_spec.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,47 @@ describe("api", function () {
828828

829829
await loadingTask.destroy();
830830
});
831+
832+
it("gets data, on failure, from `PDFDocumentLoadingTask`-instance", async function () {
833+
const typedArrayPdf = await DefaultFileReaderFactory.fetch({
834+
path: TEST_PDFS_PATH + "issue6010_1.pdf",
835+
});
836+
837+
// Sanity check to make sure that we fetched the entire PDF file.
838+
expect(typedArrayPdf instanceof Uint8Array).toEqual(true);
839+
expect(typedArrayPdf.length).toEqual(1116);
840+
841+
const loadingTask = getDocument(typedArrayPdf.slice());
842+
expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true);
843+
844+
let passwordData = null;
845+
// Attach the callback that is used to request a password;
846+
// similarly to how the default viewer handles passwords.
847+
loadingTask.onPassword = async (updatePassword, reason) => {
848+
passwordData = await loadingTask.getData();
849+
850+
updatePassword(new Error("Should reject the loadingTask."));
851+
};
852+
853+
try {
854+
await loadingTask.promise;
855+
856+
// Shouldn't get here.
857+
expect(false).toEqual(true);
858+
} catch (ex) {
859+
expect(ex instanceof PasswordException).toEqual(true);
860+
expect(ex.code).toEqual(PasswordResponses.NEED_PASSWORD);
861+
}
862+
863+
// Ensure that the raw PDF document can be fetched while
864+
// an `onPassword` callback is delaying initialization...
865+
expect(passwordData).toEqual(typedArrayPdf);
866+
// ... and once an exception has stopped initialization.
867+
const data = await loadingTask.getData();
868+
expect(data).toEqual(typedArrayPdf);
869+
870+
await loadingTask.destroy();
871+
});
831872
});
832873

833874
describe("PDFWorker", function () {

web/app.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,9 @@ const PDFViewerApplication = {
11531153
async download() {
11541154
let data;
11551155
try {
1156-
data = await this.pdfDocument.getData();
1156+
data = await (this.pdfDocument
1157+
? this.pdfDocument.getData()
1158+
: this.pdfLoadingTask.getData());
11571159
} catch {
11581160
// When the PDF document isn't ready, simply download using the URL.
11591161
}

0 commit comments

Comments
 (0)