diff --git a/src/core/obj.js b/src/core/obj.js index d8721ba8a..931723f1c 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -592,9 +592,12 @@ class Catalog { return shadow(this, "viewerPreferences", prefs); } - get openActionDestination() { + /** + * NOTE: "JavaScript" actions are, for now, handled by `get javaScript` below. + */ + get openAction() { const obj = this.catDict.get("OpenAction"); - let openActionDest = null; + let openAction = null; if (isDict(obj)) { // Convert the OpenAction dictionary into a format that works with @@ -602,16 +605,27 @@ class Catalog { const destDict = new Dict(this.xref); destDict.set("A", obj); - const resultObj = { url: null, dest: null }; + const resultObj = { url: null, dest: null, action: null }; Catalog.parseDestDictionary({ destDict, resultObj }); if (Array.isArray(resultObj.dest)) { - openActionDest = resultObj.dest; + if (!openAction) { + openAction = Object.create(null); + } + openAction.dest = resultObj.dest; + } else if (resultObj.action) { + if (!openAction) { + openAction = Object.create(null); + } + openAction.action = resultObj.action; } } else if (Array.isArray(obj)) { - openActionDest = obj; + if (!openAction) { + openAction = Object.create(null); + } + openAction.dest = obj; } - return shadow(this, "openActionDestination", openActionDest); + return shadow(this, "openAction", openAction); } get attachments() { @@ -668,27 +682,10 @@ class Catalog { } } - // Append OpenAction actions to the JavaScript array. - const openActionDict = this.catDict.get("OpenAction"); - if ( - isDict(openActionDict) && - (isName(openActionDict.get("Type"), "Action") || - !openActionDict.has("Type")) - ) { - const actionType = openActionDict.get("S"); - if (isName(actionType, "Named")) { - // The named Print action is not a part of the PDF 1.7 specification, - // but is supported by many PDF readers/writers (including Adobe's). - const action = openActionDict.get("N"); - if (isName(action, "Print")) { - if (!javaScript) { - javaScript = []; - } - javaScript.push("print({});"); - } - } else { - appendIfJavaScriptDict(openActionDict); - } + // Append OpenAction "JavaScript" actions to the JavaScript array. + const openAction = this.catDict.get("OpenAction"); + if (isDict(openAction) && isName(openAction.get("S"), "JavaScript")) { + appendIfJavaScriptDict(openAction); } return shadow(this, "javaScript", javaScript); diff --git a/src/core/worker.js b/src/core/worker.js index 0b03785ba..1fee538f7 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -458,8 +458,8 @@ var WorkerMessageHandler = { return pdfManager.ensureCatalog("viewerPreferences"); }); - handler.on("GetOpenActionDestination", function(data) { - return pdfManager.ensureCatalog("openActionDestination"); + handler.on("GetOpenAction", function(data) { + return pdfManager.ensureCatalog("openAction"); }); handler.on("GetAttachments", function wphSetupGetAttachments(data) { diff --git a/src/display/api.js b/src/display/api.js index 008fb7743..51d596390 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -668,11 +668,18 @@ class PDFDocumentProxy { } /** - * @returns {Promise} A promise that is resolved with an {Array} containing - * the destination, or `null` when no open action is present in the PDF. + * @returns {Promise} A promise that is resolved with an {Object} containing + * the currently supported actions, or `null` when no OpenAction exists. */ + getOpenAction() { + return this._transport.getOpenAction(); + } + getOpenActionDestination() { - return this._transport.getOpenActionDestination(); + deprecated("getOpenActionDestination, use getOpenAction instead."); + return this.getOpenAction().then(function(openAction) { + return openAction && openAction.dest ? openAction.dest : null; + }); } /** @@ -2518,11 +2525,8 @@ class WorkerTransport { return this.messageHandler.sendWithPromise("GetViewerPreferences", null); } - getOpenActionDestination() { - return this.messageHandler.sendWithPromise( - "GetOpenActionDestination", - null - ); + getOpenAction() { + return this.messageHandler.sendWithPromise("GetOpenAction", null); } getAttachments() { diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index 30a6d30a3..400cb725a 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -863,29 +863,69 @@ describe("api", function() { .catch(done.fail); }); - it("gets default open action destination", function(done) { + it("gets default open action", function(done) { var loadingTask = getDocument(buildGetDocumentParams("tracemonkey.pdf")); loadingTask.promise .then(function(pdfDocument) { - return pdfDocument.getOpenActionDestination(); + return pdfDocument.getOpenAction(); }) - .then(function(dest) { - expect(dest).toEqual(null); + .then(function(openAction) { + expect(openAction).toEqual(null); loadingTask.destroy().then(done); }) .catch(done.fail); }); - it("gets non-default open action destination", function(done) { + it("gets non-default open action (with destination)", function(done) { doc - .getOpenActionDestination() - .then(function(dest) { - expect(dest).toEqual([{ num: 15, gen: 0 }, { name: "FitH" }, null]); + .getOpenAction() + .then(function(openAction) { + expect(openAction.dest).toEqual([ + { num: 15, gen: 0 }, + { name: "FitH" }, + null, + ]); + expect(openAction.action).toBeUndefined(); + done(); }) .catch(done.fail); }); + it("gets non-default open action (with Print action)", function(done) { + // PDF document with "Print" Named action in the OpenAction dictionary. + const loadingTask1 = getDocument( + buildGetDocumentParams("bug1001080.pdf") + ); + // PDF document with "Print" Named action in the OpenAction dictionary, + // but the OpenAction dictionary is missing the `Type` entry. + const loadingTask2 = getDocument( + buildGetDocumentParams("issue11442_reduced.pdf") + ); + + const promise1 = loadingTask1.promise + .then(function(pdfDocument) { + return pdfDocument.getOpenAction(); + }) + .then(function(openAction) { + expect(openAction.dest).toBeUndefined(); + expect(openAction.action).toEqual("Print"); + + return loadingTask1.destroy(); + }); + const promise2 = loadingTask2.promise + .then(function(pdfDocument) { + return pdfDocument.getOpenAction(); + }) + .then(function(openAction) { + expect(openAction.dest).toBeUndefined(); + expect(openAction.action).toEqual("Print"); + + return loadingTask2.destroy(); + }); + + Promise.all([promise1, promise2]).then(done, done.fail); + }); it("gets non-existent attachments", function(done) { var promise = doc.getAttachments(); @@ -923,37 +963,6 @@ describe("api", function() { }) .catch(done.fail); }); - it("gets javascript with printing instructions (Print action)", function(done) { - // PDF document with "Print" Named action in the OpenAction dictionary. - var loadingTask = getDocument(buildGetDocumentParams("bug1001080.pdf")); - var promise = loadingTask.promise.then(function(doc) { - return doc.getJavaScript(); - }); - promise - .then(function(data) { - expect(data).toEqual(["print({});"]); - expect(data[0]).toMatch(AutoPrintRegExp); - loadingTask.destroy().then(done); - }) - .catch(done.fail); - }); - it("gets javascript with printing instructions (Print action without type)", function(done) { - // PDF document with "Print" Named action in the OpenAction dictionary, - // but the OpenAction dictionary is missing the `Type` entry. - var loadingTask = getDocument( - buildGetDocumentParams("issue11442_reduced.pdf") - ); - var promise = loadingTask.promise.then(function(doc) { - return doc.getJavaScript(); - }); - promise - .then(function(data) { - expect(data).toEqual(["print({});"]); - expect(data[0]).toMatch(AutoPrintRegExp); - loadingTask.destroy().then(done); - }) - .catch(done.fail); - }); it("gets javascript with printing instructions (JS action)", function(done) { // PDF document with "JavaScript" action in the OpenAction dictionary. var loadingTask = getDocument(buildGetDocumentParams("issue6106.pdf")); diff --git a/web/app.js b/web/app.js index 72569d163..fe557a77f 100644 --- a/web/app.js +++ b/web/app.js @@ -1033,11 +1033,9 @@ const PDFViewerApplication = { const pageModePromise = pdfDocument.getPageMode().catch(function() { /* Avoid breaking initial rendering; ignoring errors. */ }); - const openActionDestPromise = pdfDocument - .getOpenActionDestination() - .catch(function() { - /* Avoid breaking initial rendering; ignoring errors. */ - }); + const openActionPromise = pdfDocument.getOpenAction().catch(function() { + /* Avoid breaking initial rendering; ignoring errors. */ + }); this.toolbar.setPagesCount(pdfDocument.numPages, false); this.secondaryToolbar.setPagesCount(pdfDocument.numPages); @@ -1085,7 +1083,7 @@ const PDFViewerApplication = { storePromise, pageLayoutPromise, pageModePromise, - openActionDestPromise, + openActionPromise, ]) .then( async ([ @@ -1093,14 +1091,14 @@ const PDFViewerApplication = { values = {}, pageLayout, pageMode, - openActionDest, + openAction, ]) => { const viewOnLoad = AppOptions.get("viewOnLoad"); this._initializePdfHistory({ fingerprint: pdfDocument.fingerprint, viewOnLoad, - initialDest: openActionDest, + initialDest: openAction && openAction.dest, }); const initialBookmark = this.initialBookmark; @@ -1226,14 +1224,20 @@ const PDFViewerApplication = { ); }); - pagesPromise.then(() => { + pagesPromise.then(async () => { if (!this.supportsPrinting) { return; } - pdfDocument.getJavaScript().then(javaScript => { - if (!javaScript) { - return; - } + const [openAction, javaScript] = await Promise.all([ + openActionPromise, + pdfDocument.getJavaScript(), + ]); + let triggerAutoPrint = false; + + if (openAction && openAction.action === "Print") { + triggerAutoPrint = true; + } + if (javaScript) { javaScript.some(js => { if (!js) { // Don't warn/fallback for empty JavaScript actions. @@ -1244,16 +1248,22 @@ const PDFViewerApplication = { return true; }); - // Hack to support auto printing. - for (const js of javaScript) { - if (js && AutoPrintRegExp.test(js)) { - setTimeout(function() { - window.print(); - }); - return; + if (!triggerAutoPrint) { + // Hack to support auto printing. + for (const js of javaScript) { + if (js && AutoPrintRegExp.test(js)) { + triggerAutoPrint = true; + break; + } } } - }); + } + + if (triggerAutoPrint) { + setTimeout(function() { + window.print(); + }); + } }); onePageRendered.then(() => {