Compare commits

...

6 Commits

Author SHA1 Message Date
Tim van der Meij
e738566900
Merge pull request #19669 from timvandermeij/icc-colorspace-404
Don't get ICC color space files if required API options are missing
2025-03-16 18:45:21 +01:00
Tim van der Meij
f44cba86d5
Don't get ICC color space files if required API options are missing
This commit improves validation of the API options for the ICC color
space logic. If `useWasm` is `true` but the corresponding `wasmUrl`
or `iccUrl` API options are not provided we can avoid requesting
files with `null` URLs which always results in a 404 response.
2025-03-16 16:29:42 +01:00
Jonas Jenwald
4c5ad92584
Merge pull request #19667 from Snuffleupagus/version-5.1
Bump library version to `5.1`
2025-03-16 13:51:35 +01:00
Jonas Jenwald
e3e2396230 Bump library version to 5.1 2025-03-16 13:47:18 +01:00
Tim van der Meij
d86045eacb
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)
2025-03-16 12:32:20 +01:00
Jonas Jenwald
9e8d4e4d46 [api-minor] Attempt to support fetching the raw data of the PDF document from the PDFDocumentLoadingTask-instance (issue 15085)
The new API-functionality will allow a PDF document to be downloaded in the viewer e.g. while the PasswordPrompt is open, or in cases when document initialization failed.
Normally the raw data of the PDF document would be accessed via the `PDFDocumentProxy.prototype.getData` method, however in these cases the `PDFDocumentProxy`-instance isn't available.
2025-03-16 10:09:44 +01:00
6 changed files with 117 additions and 38 deletions

View File

@ -1,5 +1,5 @@
{
"stableVersion": "5.0.375",
"baseVersion": "7a57af12e13a47927c460e6b739a6ca132e7603d",
"versionPrefix": "5.0."
"baseVersion": "d86045eacbd6f8cc9cbe8fd616800ca3edcbd0dc",
"versionPrefix": "5.1."
}

View File

@ -289,7 +289,7 @@ class ColorSpaceUtils {
}
static get cmyk() {
if (IccColorSpace.isUsable) {
if (CmykICCBasedCS.isUsable) {
try {
return shadow(this, "cmyk", new CmykICCBasedCS());
} catch {

View File

@ -130,11 +130,15 @@ class IccColorSpace extends ColorSpace {
static get isUsable() {
let isUsable = false;
if (this.#useWasm) {
try {
this._module = QCMS._module = this.#load();
isUsable = !!this._module;
} catch (e) {
warn(`ICCBased color space: "${e}".`);
if (this.#wasmUrl) {
try {
this._module = QCMS._module = this.#load();
isUsable = !!this._module;
} catch (e) {
warn(`ICCBased color space: "${e}".`);
}
} else {
warn("No ICC color space support due to missing `wasmUrl` API option");
}
}
@ -169,6 +173,19 @@ class CmykICCBasedCS extends IccColorSpace {
static setOptions({ iccUrl }) {
this.#iccUrl = iccUrl;
}
static get isUsable() {
let isUsable = false;
if (IccColorSpace.isUsable) {
if (this.#iccUrl) {
isUsable = true;
} else {
warn("No CMYK ICC profile support due to missing `iccUrl` API option");
}
}
return shadow(this, "isUsable", isUsable);
}
}
export { CmykICCBasedCS, IccColorSpace };

View File

@ -629,39 +629,48 @@ const isValidExplicitDest = _isValidExplicitDest.bind(
class PDFDocumentLoadingTask {
static #docId = 0;
constructor() {
this._capability = Promise.withResolvers();
this._transport = null;
this._worker = null;
/**
* @private
*/
_capability = Promise.withResolvers();
/**
* Unique identifier for the document loading task.
* @type {string}
*/
this.docId = `d${PDFDocumentLoadingTask.#docId++}`;
/**
* @private
*/
_transport = null;
/**
* Whether the loading task is destroyed or not.
* @type {boolean}
*/
this.destroyed = false;
/**
* @private
*/
_worker = null;
/**
* Callback to request a password if a wrong or no password was provided.
* The callback receives two parameters: a function that should be called
* with the new password, and a reason (see {@link PasswordResponses}).
* @type {function}
*/
this.onPassword = null;
/**
* Unique identifier for the document loading task.
* @type {string}
*/
docId = `d${PDFDocumentLoadingTask.#docId++}`;
/**
* Callback to be able to monitor the loading progress of the PDF file
* (necessary to implement e.g. a loading bar).
* The callback receives an {@link OnProgressParameters} argument.
* @type {function}
*/
this.onProgress = null;
}
/**
* Whether the loading task is destroyed or not.
* @type {boolean}
*/
destroyed = false;
/**
* Callback to request a password if a wrong or no password was provided.
* The callback receives two parameters: a function that should be called
* with the new password, and a reason (see {@link PasswordResponses}).
* @type {function}
*/
onPassword = null;
/**
* Callback to be able to monitor the loading progress of the PDF file
* (necessary to implement e.g. a loading bar).
* The callback receives an {@link OnProgressParameters} argument.
* @type {function}
*/
onProgress = null;
/**
* Promise for document loading task completion.
@ -699,6 +708,16 @@ class PDFDocumentLoadingTask {
this._worker?.destroy();
this._worker = null;
}
/**
* Attempt to fetch the raw data of the PDF document, when e.g.
* - An exception was thrown during document initialization.
* - An `onPassword` callback is delaying initialization.
* @returns {Promise<Uint8Array>}
*/
async getData() {
return this._transport.getData();
}
}
/**

View File

@ -828,6 +828,47 @@ describe("api", function () {
await loadingTask.destroy();
});
it("gets data, on failure, from `PDFDocumentLoadingTask`-instance", async function () {
const typedArrayPdf = await DefaultFileReaderFactory.fetch({
path: TEST_PDFS_PATH + "issue6010_1.pdf",
});
// Sanity check to make sure that we fetched the entire PDF file.
expect(typedArrayPdf instanceof Uint8Array).toEqual(true);
expect(typedArrayPdf.length).toEqual(1116);
const loadingTask = getDocument(typedArrayPdf.slice());
expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true);
let passwordData = null;
// Attach the callback that is used to request a password;
// similarly to how the default viewer handles passwords.
loadingTask.onPassword = async (updatePassword, reason) => {
passwordData = await loadingTask.getData();
updatePassword(new Error("Should reject the loadingTask."));
};
try {
await loadingTask.promise;
// Shouldn't get here.
expect(false).toEqual(true);
} catch (ex) {
expect(ex instanceof PasswordException).toEqual(true);
expect(ex.code).toEqual(PasswordResponses.NEED_PASSWORD);
}
// Ensure that the raw PDF document can be fetched while
// an `onPassword` callback is delaying initialization...
expect(passwordData).toEqual(typedArrayPdf);
// ... and once an exception has stopped initialization.
const data = await loadingTask.getData();
expect(data).toEqual(typedArrayPdf);
await loadingTask.destroy();
});
});
describe("PDFWorker", function () {

View File

@ -1153,7 +1153,9 @@ const PDFViewerApplication = {
async download() {
let data;
try {
data = await this.pdfDocument.getData();
data = await (this.pdfDocument
? this.pdfDocument.getData()
: this.pdfLoadingTask.getData());
} catch {
// When the PDF document isn't ready, simply download using the URL.
}