mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-28 23:28:16 +02:00
XFA - Save filled data in the pdf when downloading the file (Bug 1716288)
- when binding (after parsing) we get a map between some template nodes and some data nodes; - so set user data in input handlers in using data node uids in the annotation storage; - to save the form, just put the value we have in the storage in the correct data nodes, serialize the xml as a string and then write the string at the end of the pdf using src/core/writer.js; - fix few bugs around data bindings: - the "Off" issue in Bug 1716980.
This commit is contained in:
parent
d7fdb72a3f
commit
429ffdcd2f
17 changed files with 71564 additions and 113 deletions
|
@ -21,6 +21,7 @@ import {
|
|||
$clean,
|
||||
$cleanPage,
|
||||
$content,
|
||||
$data,
|
||||
$extra,
|
||||
$finalize,
|
||||
$flushHTML,
|
||||
|
@ -32,9 +33,9 @@ import {
|
|||
$getSubformParent,
|
||||
$getTemplateRoot,
|
||||
$globalData,
|
||||
$hasItem,
|
||||
$hasSettableValue,
|
||||
$ids,
|
||||
$isBindable,
|
||||
$isCDATAXml,
|
||||
$isSplittable,
|
||||
$isTransparent,
|
||||
|
@ -572,7 +573,7 @@ class BooleanElement extends Option01 {
|
|||
}
|
||||
|
||||
[$toHTML](availableSpace) {
|
||||
return valueToHtml(this[$content] === 1);
|
||||
return valueToHtml(this[$content] === 1 ? "1" : "0");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -950,17 +951,31 @@ class CheckButton extends XFAObject {
|
|||
let type;
|
||||
let className;
|
||||
let groupId;
|
||||
let id;
|
||||
const fieldId = this[$getParent]()[$getParent]()[$uid];
|
||||
const container = this[$getParent]()[$getParent]()[$getParent]();
|
||||
const field = this[$getParent]()[$getParent]();
|
||||
const items =
|
||||
(field.items.children.length &&
|
||||
field.items.children[0][$toHTML]().html) ||
|
||||
[];
|
||||
const exportedValue = {
|
||||
on: (items[0] || "on").toString(),
|
||||
off: (items[1] || "off").toString(),
|
||||
};
|
||||
|
||||
const value = (field.value && field.value[$text]()) || "off";
|
||||
const checked = value === exportedValue.on || undefined;
|
||||
const container = field[$getParent]();
|
||||
const fieldId = field[$uid];
|
||||
let dataId;
|
||||
|
||||
if (container instanceof ExclGroup) {
|
||||
groupId = container[$uid];
|
||||
type = "radio";
|
||||
className = "xfaRadio";
|
||||
id = `${fieldId}-radio`;
|
||||
dataId = container[$data] && container[$data][$uid];
|
||||
} else {
|
||||
type = "checkbox";
|
||||
className = "xfaCheckbox";
|
||||
dataId = field[$data] && field[$data][$uid];
|
||||
}
|
||||
|
||||
const input = {
|
||||
|
@ -969,14 +984,13 @@ class CheckButton extends XFAObject {
|
|||
class: [className],
|
||||
style,
|
||||
fieldId,
|
||||
dataId,
|
||||
type,
|
||||
checked,
|
||||
xfaOn: exportedValue.on,
|
||||
},
|
||||
};
|
||||
|
||||
if (id) {
|
||||
input.attributes.id = id;
|
||||
}
|
||||
|
||||
if (groupId) {
|
||||
input.attributes.name = groupId;
|
||||
}
|
||||
|
@ -1022,25 +1036,36 @@ class ChoiceList extends XFAObject {
|
|||
const children = [];
|
||||
|
||||
if (field.items.children.length > 0) {
|
||||
const displayed = field.items.children[0][$toHTML]().html;
|
||||
const values = field.items.children[1]
|
||||
? field.items.children[1][$toHTML]().html
|
||||
: [];
|
||||
const items = field.items;
|
||||
let displayedIndex = 0;
|
||||
let saveIndex = 0;
|
||||
if (items.children.length === 2) {
|
||||
displayedIndex = items.children[0].save;
|
||||
saveIndex = 1 - displayedIndex;
|
||||
}
|
||||
const displayed = items.children[displayedIndex][$toHTML]().html;
|
||||
const values = items.children[saveIndex][$toHTML]().html;
|
||||
|
||||
const value = (field.value && field.value[$text]()) || "";
|
||||
for (let i = 0, ii = displayed.length; i < ii; i++) {
|
||||
children.push({
|
||||
const option = {
|
||||
name: "option",
|
||||
attributes: {
|
||||
value: values[i] || displayed[i],
|
||||
},
|
||||
value: displayed[i],
|
||||
});
|
||||
};
|
||||
if (values[i] === value) {
|
||||
option.attributes.selected = true;
|
||||
}
|
||||
children.push(option);
|
||||
}
|
||||
}
|
||||
|
||||
const selectAttributes = {
|
||||
class: ["xfaSelect"],
|
||||
fieldId: this[$getParent]()[$getParent]()[$uid],
|
||||
fieldId: field[$uid],
|
||||
dataId: field[$data] && field[$data][$uid],
|
||||
style,
|
||||
};
|
||||
|
||||
|
@ -1272,11 +1297,13 @@ class DateTimeEdit extends XFAObject {
|
|||
// When the picker is host we should use type=date for the input
|
||||
// but we need to put the buttons outside the text-field.
|
||||
const style = toStyle(this, "border", "font", "margin");
|
||||
const field = this[$getParent]()[$getParent]();
|
||||
const html = {
|
||||
name: "input",
|
||||
attributes: {
|
||||
type: "text",
|
||||
fieldId: this[$getParent]()[$getParent]()[$uid],
|
||||
fieldId: field[$uid],
|
||||
dataId: field[$data] && field[$data][$uid],
|
||||
class: ["xfaTextfield"],
|
||||
style,
|
||||
},
|
||||
|
@ -1976,6 +2003,10 @@ class ExclGroup extends XFAObject {
|
|||
this.setProperty = new XFAObjectArray();
|
||||
}
|
||||
|
||||
[$isBindable]() {
|
||||
return true;
|
||||
}
|
||||
|
||||
[$hasSettableValue]() {
|
||||
return true;
|
||||
}
|
||||
|
@ -1988,17 +2019,7 @@ class ExclGroup extends XFAObject {
|
|||
field.value = nodeValue;
|
||||
}
|
||||
|
||||
const nodeBoolean = new BooleanElement({});
|
||||
nodeBoolean[$content] = 0;
|
||||
|
||||
for (const item of field.items.children) {
|
||||
if (item[$hasItem](value)) {
|
||||
nodeBoolean[$content] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
field.value[$setValue](nodeBoolean);
|
||||
field.value[$setValue](value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2312,6 +2333,10 @@ class Field extends XFAObject {
|
|||
this.setProperty = new XFAObjectArray();
|
||||
}
|
||||
|
||||
[$isBindable]() {
|
||||
return true;
|
||||
}
|
||||
|
||||
[$setValue](value) {
|
||||
_setValue(this, value);
|
||||
}
|
||||
|
@ -2906,15 +2931,6 @@ class Items extends XFAObject {
|
|||
this.time = new XFAObjectArray();
|
||||
}
|
||||
|
||||
[$hasItem](value) {
|
||||
return (
|
||||
this.hasOwnProperty(value[$nodeName]) &&
|
||||
this[value[$nodeName]].children.some(
|
||||
node => node[$content] === value[$content]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[$toHTML]() {
|
||||
const output = [];
|
||||
for (const child of this[$getChildren]()) {
|
||||
|
@ -3182,11 +3198,13 @@ class NumericEdit extends XFAObject {
|
|||
[$toHTML](availableSpace) {
|
||||
// TODO: incomplete.
|
||||
const style = toStyle(this, "border", "font", "margin");
|
||||
const field = this[$getParent]()[$getParent]();
|
||||
const html = {
|
||||
name: "input",
|
||||
attributes: {
|
||||
type: "text",
|
||||
fieldId: this[$getParent]()[$getParent]()[$uid],
|
||||
fieldId: field[$uid],
|
||||
dataId: field[$data] && field[$data][$uid],
|
||||
class: ["xfaTextfield"],
|
||||
style,
|
||||
},
|
||||
|
@ -4151,6 +4169,10 @@ class Subform extends XFAObject {
|
|||
this.subformSet = new XFAObjectArray();
|
||||
}
|
||||
|
||||
[$isBindable]() {
|
||||
return true;
|
||||
}
|
||||
|
||||
*[$getContainedChildren]() {
|
||||
// This function is overriden in order to fake that subforms under
|
||||
// this set are in fact under parent subform.
|
||||
|
@ -4924,11 +4946,13 @@ class TextEdit extends XFAObject {
|
|||
// TODO: incomplete.
|
||||
const style = toStyle(this, "border", "font", "margin");
|
||||
let html;
|
||||
const field = this[$getParent]()[$getParent]();
|
||||
if (this.multiLine === 1) {
|
||||
html = {
|
||||
name: "textarea",
|
||||
attributes: {
|
||||
fieldId: this[$getParent]()[$getParent]()[$uid],
|
||||
dataId: field[$data] && field[$data][$uid],
|
||||
fieldId: field[$uid],
|
||||
class: ["xfaTextfield"],
|
||||
style,
|
||||
},
|
||||
|
@ -4938,7 +4962,8 @@ class TextEdit extends XFAObject {
|
|||
name: "input",
|
||||
attributes: {
|
||||
type: "text",
|
||||
fieldId: this[$getParent]()[$getParent]()[$uid],
|
||||
dataId: field[$data] && field[$data][$uid],
|
||||
fieldId: field[$uid],
|
||||
class: ["xfaTextfield"],
|
||||
style,
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue