Merge pull request #11644 from Snuffleupagus/openAction

[api-minor] Add more general OpenAction support (PR 10334 follow-up, issue 11642)
This commit is contained in:
Tim van der Meij 2020-03-15 13:16:37 +01:00 committed by GitHub
commit aa3e5a2b8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 97 deletions

View File

@ -592,9 +592,12 @@ class Catalog {
return shadow(this, "viewerPreferences", prefs); 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"); const obj = this.catDict.get("OpenAction");
let openActionDest = null; let openAction = null;
if (isDict(obj)) { if (isDict(obj)) {
// Convert the OpenAction dictionary into a format that works with // Convert the OpenAction dictionary into a format that works with
@ -602,16 +605,27 @@ class Catalog {
const destDict = new Dict(this.xref); const destDict = new Dict(this.xref);
destDict.set("A", obj); destDict.set("A", obj);
const resultObj = { url: null, dest: null }; const resultObj = { url: null, dest: null, action: null };
Catalog.parseDestDictionary({ destDict, resultObj }); Catalog.parseDestDictionary({ destDict, resultObj });
if (Array.isArray(resultObj.dest)) { 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)) { } 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() { get attachments() {
@ -668,27 +682,10 @@ class Catalog {
} }
} }
// Append OpenAction actions to the JavaScript array. // Append OpenAction "JavaScript" actions to the JavaScript array.
const openActionDict = this.catDict.get("OpenAction"); const openAction = this.catDict.get("OpenAction");
if ( if (isDict(openAction) && isName(openAction.get("S"), "JavaScript")) {
isDict(openActionDict) && appendIfJavaScriptDict(openAction);
(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);
}
} }
return shadow(this, "javaScript", javaScript); return shadow(this, "javaScript", javaScript);

View File

@ -458,8 +458,8 @@ var WorkerMessageHandler = {
return pdfManager.ensureCatalog("viewerPreferences"); return pdfManager.ensureCatalog("viewerPreferences");
}); });
handler.on("GetOpenActionDestination", function(data) { handler.on("GetOpenAction", function(data) {
return pdfManager.ensureCatalog("openActionDestination"); return pdfManager.ensureCatalog("openAction");
}); });
handler.on("GetAttachments", function wphSetupGetAttachments(data) { handler.on("GetAttachments", function wphSetupGetAttachments(data) {

View File

@ -668,11 +668,18 @@ class PDFDocumentProxy {
} }
/** /**
* @returns {Promise} A promise that is resolved with an {Array} containing * @returns {Promise} A promise that is resolved with an {Object} containing
* the destination, or `null` when no open action is present in the PDF. * the currently supported actions, or `null` when no OpenAction exists.
*/ */
getOpenAction() {
return this._transport.getOpenAction();
}
getOpenActionDestination() { 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); return this.messageHandler.sendWithPromise("GetViewerPreferences", null);
} }
getOpenActionDestination() { getOpenAction() {
return this.messageHandler.sendWithPromise( return this.messageHandler.sendWithPromise("GetOpenAction", null);
"GetOpenActionDestination",
null
);
} }
getAttachments() { getAttachments() {

View File

@ -863,29 +863,69 @@ describe("api", function() {
.catch(done.fail); .catch(done.fail);
}); });
it("gets default open action destination", function(done) { it("gets default open action", function(done) {
var loadingTask = getDocument(buildGetDocumentParams("tracemonkey.pdf")); var loadingTask = getDocument(buildGetDocumentParams("tracemonkey.pdf"));
loadingTask.promise loadingTask.promise
.then(function(pdfDocument) { .then(function(pdfDocument) {
return pdfDocument.getOpenActionDestination(); return pdfDocument.getOpenAction();
}) })
.then(function(dest) { .then(function(openAction) {
expect(dest).toEqual(null); expect(openAction).toEqual(null);
loadingTask.destroy().then(done); loadingTask.destroy().then(done);
}) })
.catch(done.fail); .catch(done.fail);
}); });
it("gets non-default open action destination", function(done) { it("gets non-default open action (with destination)", function(done) {
doc doc
.getOpenActionDestination() .getOpenAction()
.then(function(dest) { .then(function(openAction) {
expect(dest).toEqual([{ num: 15, gen: 0 }, { name: "FitH" }, null]); expect(openAction.dest).toEqual([
{ num: 15, gen: 0 },
{ name: "FitH" },
null,
]);
expect(openAction.action).toBeUndefined();
done(); done();
}) })
.catch(done.fail); .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) { it("gets non-existent attachments", function(done) {
var promise = doc.getAttachments(); var promise = doc.getAttachments();
@ -923,37 +963,6 @@ describe("api", function() {
}) })
.catch(done.fail); .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) { it("gets javascript with printing instructions (JS action)", function(done) {
// PDF document with "JavaScript" action in the OpenAction dictionary. // PDF document with "JavaScript" action in the OpenAction dictionary.
var loadingTask = getDocument(buildGetDocumentParams("issue6106.pdf")); var loadingTask = getDocument(buildGetDocumentParams("issue6106.pdf"));

View File

@ -1033,11 +1033,9 @@ const PDFViewerApplication = {
const pageModePromise = pdfDocument.getPageMode().catch(function() { const pageModePromise = pdfDocument.getPageMode().catch(function() {
/* Avoid breaking initial rendering; ignoring errors. */ /* Avoid breaking initial rendering; ignoring errors. */
}); });
const openActionDestPromise = pdfDocument const openActionPromise = pdfDocument.getOpenAction().catch(function() {
.getOpenActionDestination() /* Avoid breaking initial rendering; ignoring errors. */
.catch(function() { });
/* Avoid breaking initial rendering; ignoring errors. */
});
this.toolbar.setPagesCount(pdfDocument.numPages, false); this.toolbar.setPagesCount(pdfDocument.numPages, false);
this.secondaryToolbar.setPagesCount(pdfDocument.numPages); this.secondaryToolbar.setPagesCount(pdfDocument.numPages);
@ -1085,7 +1083,7 @@ const PDFViewerApplication = {
storePromise, storePromise,
pageLayoutPromise, pageLayoutPromise,
pageModePromise, pageModePromise,
openActionDestPromise, openActionPromise,
]) ])
.then( .then(
async ([ async ([
@ -1093,14 +1091,14 @@ const PDFViewerApplication = {
values = {}, values = {},
pageLayout, pageLayout,
pageMode, pageMode,
openActionDest, openAction,
]) => { ]) => {
const viewOnLoad = AppOptions.get("viewOnLoad"); const viewOnLoad = AppOptions.get("viewOnLoad");
this._initializePdfHistory({ this._initializePdfHistory({
fingerprint: pdfDocument.fingerprint, fingerprint: pdfDocument.fingerprint,
viewOnLoad, viewOnLoad,
initialDest: openActionDest, initialDest: openAction && openAction.dest,
}); });
const initialBookmark = this.initialBookmark; const initialBookmark = this.initialBookmark;
@ -1226,14 +1224,20 @@ const PDFViewerApplication = {
); );
}); });
pagesPromise.then(() => { pagesPromise.then(async () => {
if (!this.supportsPrinting) { if (!this.supportsPrinting) {
return; return;
} }
pdfDocument.getJavaScript().then(javaScript => { const [openAction, javaScript] = await Promise.all([
if (!javaScript) { openActionPromise,
return; pdfDocument.getJavaScript(),
} ]);
let triggerAutoPrint = false;
if (openAction && openAction.action === "Print") {
triggerAutoPrint = true;
}
if (javaScript) {
javaScript.some(js => { javaScript.some(js => {
if (!js) { if (!js) {
// Don't warn/fallback for empty JavaScript actions. // Don't warn/fallback for empty JavaScript actions.
@ -1244,16 +1248,22 @@ const PDFViewerApplication = {
return true; return true;
}); });
// Hack to support auto printing. if (!triggerAutoPrint) {
for (const js of javaScript) { // Hack to support auto printing.
if (js && AutoPrintRegExp.test(js)) { for (const js of javaScript) {
setTimeout(function() { if (js && AutoPrintRegExp.test(js)) {
window.print(); triggerAutoPrint = true;
}); break;
return; }
} }
} }
}); }
if (triggerAutoPrint) {
setTimeout(function() {
window.print();
});
}
}); });
onePageRendered.then(() => { onePageRendered.then(() => {