1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-26 10:08:06 +02:00

Merge pull request #12702 from calixteman/doc_actions

JS - Collect and execute actions at doc level
This commit is contained in:
calixteman 2020-12-18 21:33:32 +01:00 committed by GitHub
commit e6e2809825
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 829 additions and 125 deletions

View file

@ -21,11 +21,9 @@ import {
AnnotationReplyType,
AnnotationType,
assert,
bytesToString,
escapeString,
getModificationDate,
isString,
objectSize,
OPS,
shadow,
stringToPDFString,
@ -34,17 +32,9 @@ import {
warn,
} from "../shared/util.js";
import { Catalog, FileSpec, ObjectLoader } from "./obj.js";
import {
Dict,
isDict,
isName,
isRef,
isStream,
Name,
RefSet,
} from "./primitives.js";
import { collectActions, getInheritableProperty } from "./core_utils.js";
import { Dict, isDict, isName, isRef, isStream, Name } from "./primitives.js";
import { ColorSpace } from "./colorspace.js";
import { getInheritableProperty } from "./core_utils.js";
import { OperatorList } from "./operator_list.js";
import { StringStream } from "./stream.js";
import { writeDict } from "./writer.js";
@ -977,7 +967,7 @@ class WidgetAnnotation extends Annotation {
data.annotationType = AnnotationType.WIDGET;
data.fieldName = this._constructFieldName(dict);
data.actions = this._collectActions(params.xref, dict);
data.actions = collectActions(params.xref, dict, AnnotationActionEventType);
const fieldValue = getInheritableProperty({
dict,
@ -1459,78 +1449,6 @@ class WidgetAnnotation extends Annotation {
return localResources || Dict.empty;
}
_collectJS(entry, xref, list, parents) {
if (!entry) {
return;
}
let parent = null;
if (isRef(entry)) {
if (parents.has(entry)) {
// If we've already found entry then we've a cycle.
return;
}
parent = entry;
parents.put(parent);
entry = xref.fetch(entry);
}
if (Array.isArray(entry)) {
for (const element of entry) {
this._collectJS(element, xref, list, parents);
}
} else if (entry instanceof Dict) {
if (isName(entry.get("S"), "JavaScript") && entry.has("JS")) {
const js = entry.get("JS");
let code;
if (isStream(js)) {
code = bytesToString(js.getBytes());
} else {
code = js;
}
code = stringToPDFString(code);
if (code) {
list.push(code);
}
}
this._collectJS(entry.getRaw("Next"), xref, list, parents);
}
if (parent) {
parents.remove(parent);
}
}
_collectActions(xref, dict) {
const actions = Object.create(null);
if (dict.has("AA")) {
const additionalActions = dict.get("AA");
for (const key of additionalActions.getKeys()) {
const action = AnnotationActionEventType[key];
if (!action) {
continue;
}
const actionDict = additionalActions.getRaw(key);
const parents = new RefSet();
const list = [];
this._collectJS(actionDict, xref, list, parents);
if (list.length > 0) {
actions[action] = list;
}
}
}
// Collect the Action if any (we may have one on pushbutton).
if (dict.has("A")) {
const actionDict = dict.get("A");
const parents = new RefSet();
const list = [];
this._collectJS(actionDict, xref, list, parents);
if (list.length > 0) {
actions.Action = list;
}
}
return objectSize(actions) > 0 ? actions : null;
}
getFieldObject() {
if (this.data.fieldType === "Sig") {
return {

View file

@ -13,7 +13,15 @@
* limitations under the License.
*/
import { assert, BaseException, warn } from "../shared/util.js";
import {
assert,
BaseException,
bytesToString,
objectSize,
stringToPDFString,
warn,
} from "../shared/util.js";
import { Dict, isName, isRef, isStream, RefSet } from "./primitives.js";
function getLookupTableFactory(initializer) {
let lookup;
@ -240,7 +248,80 @@ function escapePDFName(str) {
return buffer.join("");
}
function _collectJS(entry, xref, list, parents) {
if (!entry) {
return;
}
let parent = null;
if (isRef(entry)) {
if (parents.has(entry)) {
// If we've already found entry then we've a cycle.
return;
}
parent = entry;
parents.put(parent);
entry = xref.fetch(entry);
}
if (Array.isArray(entry)) {
for (const element of entry) {
_collectJS(element, xref, list, parents);
}
} else if (entry instanceof Dict) {
if (isName(entry.get("S"), "JavaScript") && entry.has("JS")) {
const js = entry.get("JS");
let code;
if (isStream(js)) {
code = bytesToString(js.getBytes());
} else {
code = js;
}
code = stringToPDFString(code);
if (code) {
list.push(code);
}
}
_collectJS(entry.getRaw("Next"), xref, list, parents);
}
if (parent) {
parents.remove(parent);
}
}
function collectActions(xref, dict, eventType) {
const actions = Object.create(null);
if (dict.has("AA")) {
const additionalActions = dict.get("AA");
for (const key of additionalActions.getKeys()) {
const action = eventType[key];
if (!action) {
continue;
}
const actionDict = additionalActions.getRaw(key);
const parents = new RefSet();
const list = [];
_collectJS(actionDict, xref, list, parents);
if (list.length > 0) {
actions[action] = list;
}
}
}
// Collect the Action if any (we may have one on pushbutton).
if (dict.has("A")) {
const actionDict = dict.get("A");
const parents = new RefSet();
const list = [];
_collectJS(actionDict, xref, list, parents);
if (list.length > 0) {
actions.Action = list;
}
}
return objectSize(actions) > 0 ? actions : null;
}
export {
collectActions,
escapePDFName,
getLookupTableFactory,
getArrayLookupTableFactory,

View file

@ -24,6 +24,7 @@ import {
isNum,
isString,
OPS,
PageActionEventType,
shadow,
stringToBytes,
stringToPDFString,
@ -42,6 +43,7 @@ import {
Ref,
} from "./primitives.js";
import {
collectActions,
getInheritableProperty,
isWhiteSpace,
MissingDataException,
@ -467,6 +469,16 @@ class Page {
return shadow(this, "_parsedAnnotations", parsedAnnotations);
}
get jsActions() {
const actions = collectActions(
this.xref,
this.pageDict,
PageActionEventType
);
return shadow(this, "jsActions", actions);
}
}
const PDF_HEADER_SIGNATURE = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d]);

View file

@ -19,6 +19,7 @@ import {
bytesToString,
createPromiseCapability,
createValidAbsoluteUrl,
DocumentActionEventType,
FormatError,
info,
InvalidPDFException,
@ -47,13 +48,14 @@ import {
RefSet,
RefSetCache,
} from "./primitives.js";
import { Lexer, Parser } from "./parser.js";
import {
collectActions,
MissingDataException,
toRomanNumerals,
XRefEntryException,
XRefParseException,
} from "./core_utils.js";
import { Lexer, Parser } from "./parser.js";
import { CipherTransformFactory } from "./crypto.js";
import { ColorSpace } from "./colorspace.js";
import { GlobalImageCache } from "./image_utils.js";
@ -873,11 +875,11 @@ class Catalog {
return shadow(this, "attachments", attachments);
}
get javaScript() {
_collectJavaScript() {
const obj = this._catDict.get("Names");
let javaScript = null;
function appendIfJavaScriptDict(jsDict) {
function appendIfJavaScriptDict(name, jsDict) {
const type = jsDict.get("S");
if (!isName(type, "JavaScript")) {
return;
@ -890,10 +892,10 @@ class Catalog {
return;
}
if (!javaScript) {
javaScript = [];
if (javaScript === null) {
javaScript = Object.create(null);
}
javaScript.push(stringToPDFString(js));
javaScript[name] = stringToPDFString(js);
}
if (obj && obj.has("JavaScript")) {
@ -904,7 +906,7 @@ class Catalog {
// defensive so we don't cause errors on document load.
const jsDict = names[name];
if (isDict(jsDict)) {
appendIfJavaScriptDict(jsDict);
appendIfJavaScriptDict(name, jsDict);
}
}
}
@ -912,10 +914,43 @@ class Catalog {
// Append OpenAction "JavaScript" actions to the JavaScript array.
const openAction = this._catDict.get("OpenAction");
if (isDict(openAction) && isName(openAction.get("S"), "JavaScript")) {
appendIfJavaScriptDict(openAction);
appendIfJavaScriptDict("OpenAction", openAction);
}
return shadow(this, "javaScript", javaScript);
return javaScript;
}
get javaScript() {
const javaScript = this._collectJavaScript();
return shadow(
this,
"javaScript",
javaScript ? Object.values(javaScript) : null
);
}
get jsActions() {
const js = this._collectJavaScript();
let actions = collectActions(
this.xref,
this._catDict,
DocumentActionEventType
);
if (!actions && js) {
actions = Object.create(null);
}
if (actions && js) {
for (const [key, val] of Object.entries(js)) {
if (key in actions) {
actions[key].push(val);
} else {
actions[key] = [val];
}
}
}
return shadow(this, "jsActions", actions);
}
fontFallback(id, handler) {

View file

@ -481,6 +481,16 @@ class WorkerMessageHandler {
return pdfManager.ensureCatalog("javaScript");
});
handler.on("GetDocJSActions", function wphSetupGetDocJSActions(data) {
return pdfManager.ensureCatalog("jsActions");
});
handler.on("GetPageJSActions", function ({ pageIndex }) {
return pdfManager.getPage(pageIndex).then(function (page) {
return page.jsActions;
});
});
handler.on("GetOutline", function wphSetupGetOutline(data) {
return pdfManager.ensureCatalog("documentOutline");
});