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

Add the possibility to collect Javascript actions

This commit is contained in:
Calixte Denizet 2020-09-30 20:58:45 +02:00
parent fd1d9cc85f
commit 71ecc3129b
7 changed files with 383 additions and 3 deletions

View file

@ -14,12 +14,14 @@
*/
import {
AnnotationActionEventType,
AnnotationBorderStyleType,
AnnotationFieldFlag,
AnnotationFlag,
AnnotationReplyType,
AnnotationType,
assert,
bytesToString,
escapeString,
getModificationDate,
isString,
@ -30,7 +32,15 @@ import {
warn,
} from "../shared/util.js";
import { Catalog, FileSpec, ObjectLoader } from "./obj.js";
import { Dict, isDict, isName, isRef, isStream, Name } from "./primitives.js";
import {
Dict,
isDict,
isName,
isRef,
isStream,
Name,
RefSet,
} from "./primitives.js";
import { ColorSpace } from "./colorspace.js";
import { getInheritableProperty } from "./core_utils.js";
import { OperatorList } from "./operator_list.js";
@ -569,6 +579,20 @@ class Annotation {
return null;
}
/**
* Get field data for usage in JS sandbox.
*
* Field object is defined here:
* https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/js_api_reference.pdf#page=16
*
* @public
* @memberof Annotation
* @returns {Object | null}
*/
getFieldObject() {
return null;
}
/**
* Reset the annotation.
*
@ -903,6 +927,7 @@ class WidgetAnnotation extends Annotation {
data.annotationType = AnnotationType.WIDGET;
data.fieldName = this._constructFieldName(dict);
data.actions = this._collectActions(params.xref, dict);
const fieldValue = getInheritableProperty({
dict,
@ -937,6 +962,7 @@ class WidgetAnnotation extends Annotation {
}
data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
data.hidden = this.hasFieldFlag(AnnotationFieldFlag.HIDDEN);
// Hide signatures because we cannot validate them, and unset the fieldValue
// since it's (most likely) a `Dict` which is non-serializable and will thus
@ -944,6 +970,7 @@ class WidgetAnnotation extends Annotation {
if (data.fieldType === "Sig") {
data.fieldValue = null;
this.setFlags(AnnotationFlag.HIDDEN);
data.hidden = true;
}
}
@ -1366,6 +1393,87 @@ 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()) {
if (key in AnnotationActionEventType) {
const actionDict = additionalActions.getRaw(key);
const parents = new RefSet();
const list = [];
this._collectJS(actionDict, xref, list, parents);
if (list.length > 0) {
actions[AnnotationActionEventType[key]] = 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 actions;
}
getFieldObject() {
if (this.data.fieldType === "Sig") {
return {
id: this.data.id,
value: null,
type: "signature",
};
}
return null;
}
}
class TextWidgetAnnotation extends WidgetAnnotation {
@ -1516,6 +1624,23 @@ class TextWidgetAnnotation extends WidgetAnnotation {
return chunks;
}
getFieldObject() {
return {
id: this.data.id,
value: this.data.fieldValue,
multiline: this.data.multiLine,
password: this.hasFieldFlag(AnnotationFieldFlag.PASSWORD),
charLimit: this.data.maxLen,
comb: this.data.comb,
editable: !this.data.readOnly,
hidden: this.data.hidden,
name: this.data.fieldName,
rect: this.data.rect,
actions: this.data.actions,
type: "text",
};
}
}
class ButtonWidgetAnnotation extends WidgetAnnotation {
@ -1793,6 +1918,28 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
docBaseUrl: params.pdfManager.docBaseUrl,
});
}
getFieldObject() {
let type = "button";
let value = null;
if (this.data.checkBox) {
type = "checkbox";
value = this.data.fieldValue && this.data.fieldValue !== "Off";
} else if (this.data.radioButton) {
type = "radiobutton";
value = this.data.fieldValue === this.data.buttonValue;
}
return {
id: this.data.id,
value,
editable: !this.data.readOnly,
name: this.data.fieldName,
rect: this.data.rect,
hidden: this.data.hidden,
actions: this.data.actions,
type,
};
}
}
class ChoiceWidgetAnnotation extends WidgetAnnotation {
@ -1843,6 +1990,23 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT);
this._hasText = true;
}
getFieldObject() {
const type = this.data.combo ? "combobox" : "listbox";
const value =
this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : null;
return {
id: this.data.id,
value,
editable: !this.data.readOnly,
name: this.data.fieldName,
rect: this.data.rect,
multipleSelection: this.data.multiSelect,
hidden: this.data.hidden,
actions: this.data.actions,
type,
};
}
}
class TextAnnotation extends MarkupAnnotation {