[JS] Handle correctly choice widgets where the display and the export values are different (issue #15815)

This commit is contained in:
Calixte Denizet 2022-12-13 00:07:45 +01:00
parent 64786b4c93
commit 0c1ec946aa
8 changed files with 155 additions and 30 deletions

View File

@ -1978,7 +1978,15 @@ class WidgetAnnotation extends Annotation {
assert(typeof value === "string", "Expected `value` to be a string."); assert(typeof value === "string", "Expected `value` to be a string.");
value = value.trim(); if (!this.data.combo) {
value = value.trim();
} else {
// The value is supposed to be one of the exportValue.
const option =
this.data.options.find(({ exportValue }) => value === exportValue) ||
this.data.options[0];
value = (option && option.displayValue) || "";
}
if (value === "") { if (value === "") {
// the field is empty: nothing to render // the field is empty: nothing to render

View File

@ -1612,10 +1612,10 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
selectElement.addEventListener("input", removeEmptyEntry); selectElement.addEventListener("input", removeEmptyEntry);
} }
const getValue = (event, isExport) => { const getValue = isExport => {
const name = isExport ? "value" : "textContent"; const name = isExport ? "value" : "textContent";
const options = event.target.options; const { options, multiple } = selectElement;
if (!event.target.multiple) { if (!multiple) {
return options.selectedIndex === -1 return options.selectedIndex === -1
? null ? null
: options[options.selectedIndex][name]; : options[options.selectedIndex][name];
@ -1625,6 +1625,8 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
.map(option => option[name]); .map(option => option[name]);
}; };
let selectedValues = getValue(/* isExport */ false);
const getItems = event => { const getItems = event => {
const options = event.target.options; const options = event.target.options;
return Array.prototype.map.call(options, option => { return Array.prototype.map.call(options, option => {
@ -1643,8 +1645,9 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
option.selected = values.has(option.value); option.selected = values.has(option.value);
} }
storage.setValue(id, { storage.setValue(id, {
value: getValue(event, /* isExport */ true), value: getValue(/* isExport */ true),
}); });
selectedValues = getValue(/* isExport */ false);
}, },
multipleSelection(event) { multipleSelection(event) {
selectElement.multiple = true; selectElement.multiple = true;
@ -1664,15 +1667,17 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
} }
} }
storage.setValue(id, { storage.setValue(id, {
value: getValue(event, /* isExport */ true), value: getValue(/* isExport */ true),
items: getItems(event), items: getItems(event),
}); });
selectedValues = getValue(/* isExport */ false);
}, },
clear(event) { clear(event) {
while (selectElement.length !== 0) { while (selectElement.length !== 0) {
selectElement.remove(0); selectElement.remove(0);
} }
storage.setValue(id, { value: null, items: [] }); storage.setValue(id, { value: null, items: [] });
selectedValues = getValue(/* isExport */ false);
}, },
insert(event) { insert(event) {
const { index, displayValue, exportValue } = event.detail.insert; const { index, displayValue, exportValue } = event.detail.insert;
@ -1687,9 +1692,10 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
selectElement.append(optionElement); selectElement.append(optionElement);
} }
storage.setValue(id, { storage.setValue(id, {
value: getValue(event, /* isExport */ true), value: getValue(/* isExport */ true),
items: getItems(event), items: getItems(event),
}); });
selectedValues = getValue(/* isExport */ false);
}, },
items(event) { items(event) {
const { items } = event.detail; const { items } = event.detail;
@ -1707,9 +1713,10 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
selectElement.options[0].selected = true; selectElement.options[0].selected = true;
} }
storage.setValue(id, { storage.setValue(id, {
value: getValue(event, /* isExport */ true), value: getValue(/* isExport */ true),
items: getItems(event), items: getItems(event),
}); });
selectedValues = getValue(/* isExport */ false);
}, },
indices(event) { indices(event) {
const indices = new Set(event.detail.indices); const indices = new Set(event.detail.indices);
@ -1717,8 +1724,9 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
option.selected = indices.has(option.index); option.selected = indices.has(option.index);
} }
storage.setValue(id, { storage.setValue(id, {
value: getValue(event, /* isExport */ true), value: getValue(/* isExport */ true),
}); });
selectedValues = getValue(/* isExport */ false);
}, },
editable(event) { editable(event) {
event.target.disabled = !event.detail.editable; event.target.disabled = !event.detail.editable;
@ -1728,18 +1736,19 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
}); });
selectElement.addEventListener("input", event => { selectElement.addEventListener("input", event => {
const exportValue = getValue(event, /* isExport */ true); const exportValue = getValue(/* isExport */ true);
const value = getValue(event, /* isExport */ false);
storage.setValue(id, { value: exportValue }); storage.setValue(id, { value: exportValue });
event.preventDefault();
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this, source: this,
detail: { detail: {
id, id,
name: "Keystroke", name: "Keystroke",
value, value: selectedValues,
changeEx: exportValue, changeEx: exportValue,
willCommit: true, willCommit: false,
commitKey: 1, commitKey: 1,
keyDown: false, keyDown: false,
}, },
@ -1761,7 +1770,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
); );
} else { } else {
selectElement.addEventListener("input", function (event) { selectElement.addEventListener("input", function (event) {
storage.setValue(id, { value: getValue(event, /* isExport */ true) }); storage.setValue(id, { value: getValue(/* isExport */ true) });
}); });
} }

View File

@ -137,6 +137,7 @@ class EventDispatcher {
case "Keystroke": case "Keystroke":
savedChange = { savedChange = {
value: event.value, value: event.value,
changeEx: event.changeEx,
change: event.change, change: event.change,
selStart: event.selStart, selStart: event.selStart,
selEnd: event.selEnd, selEnd: event.selEnd,
@ -170,6 +171,16 @@ class EventDispatcher {
if (event.willCommit) { if (event.willCommit) {
this.runValidation(source, event); this.runValidation(source, event);
} else { } else {
if (source.obj._isChoice) {
source.obj.value = savedChange.changeEx;
source.obj._send({
id: source.obj._id,
siblings: source.obj._siblings,
value: source.obj.value,
});
return;
}
const value = (source.obj.value = this.mergeChange(event)); const value = (source.obj.value = this.mergeChange(event));
let selStart, selEnd; let selStart, selEnd;
if ( if (

View File

@ -242,6 +242,11 @@ class Field extends PDFObject {
} }
set value(value) { set value(value) {
if (this._isChoice) {
this._setChoiceValue(value);
return;
}
if (value === "") { if (value === "") {
this._value = ""; this._value = "";
} else if (typeof value === "string") { } else if (typeof value === "string") {
@ -260,23 +265,37 @@ class Field extends PDFObject {
} else { } else {
this._value = value; this._value = value;
} }
if (this._isChoice) { }
if (this.multipleSelection) {
const values = new Set(value); _setChoiceValue(value) {
if (Array.isArray(this._currentValueIndices)) { if (this.multipleSelection) {
this._currentValueIndices.length = 0; if (!Array.isArray(value)) {
} else { value = [value];
this._currentValueIndices = []; }
} const values = new Set(value);
this._items.forEach(({ displayValue }, i) => { if (Array.isArray(this._currentValueIndices)) {
if (values.has(displayValue)) { this._currentValueIndices.length = 0;
this._currentValueIndices.push(i); this._value.length = 0;
}
});
} else { } else {
this._currentValueIndices = this._items.findIndex( this._currentValueIndices = [];
({ displayValue }) => value === displayValue this._value = [];
); }
this._items.forEach((item, i) => {
if (values.has(item.exportValue)) {
this._currentValueIndices.push(i);
this._value.push(item.exportValue);
}
});
} else {
if (Array.isArray(value)) {
value = value[0];
}
const index = this._items.findIndex(
({ exportValue }) => value === exportValue
);
if (index !== -1) {
this._currentValueIndices = index;
this._value = this._items[index].exportValue;
} }
} }
} }

View File

@ -1651,4 +1651,54 @@ describe("Interaction", () => {
); );
}); });
}); });
describe("in issue15815.pdf", () => {
let pages;
beforeAll(async () => {
pages = await loadAndWait("issue15815.pdf", getSelector("24R"));
});
afterAll(async () => {
await closePages(pages);
});
it("must check field value is correctly updated when committed with ENTER key", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await page.waitForFunction(
"window.PDFViewerApplication.scriptingReady === true"
);
let value = "A";
for (const [displayValue, exportValue] of [
["B", "x2"],
["C", "x3"],
["A", "x1"],
]) {
await clearInput(page, getSelector("27R"));
await page.select(getSelector("24R"), exportValue);
await page.waitForFunction(
`${getQuerySelector("27R")}.value !== ""`
);
const text = await page.$eval(getSelector("27R"), el => el.value);
expect(text)
.withContext(`In ${browserName}`)
.toEqual(`value=${value}, changeEx=${exportValue}`);
value = displayValue;
}
for (const exportValue of ["x3", "x2", "x1"]) {
await clearInput(page, getSelector("27R"));
await page.type(getSelector("27R"), exportValue);
await page.click("[data-annotation-id='28R']");
await page.waitForTimeout(10);
value = await page.$eval(getSelector("24R"), el => el.value);
expect(value).withContext(`In ${browserName}`).toEqual(exportValue);
}
})
);
});
});
}); });

View File

@ -561,3 +561,4 @@
!issue15753.pdf !issue15753.pdf
!issue15789.pdf !issue15789.pdf
!fields_order.pdf !fields_order.pdf
!issue15815.pdf

BIN
test/pdfs/issue15815.pdf Executable file

Binary file not shown.

View File

@ -7240,5 +7240,32 @@
"value": "مرحبا بالعالم" "value": "مرحبا بالعالم"
} }
} }
},
{
"id": "issue15815-print",
"file": "pdfs/issue15815.pdf",
"md5": "48b8b057954d5b773421ac03b7fcd738",
"rounds": 1,
"type": "eq",
"print": true,
"annotationStorage": {
"24R": {
"value": "x3"
}
}
},
{
"id": "issue15815-save-print",
"file": "pdfs/issue15815.pdf",
"md5": "48b8b057954d5b773421ac03b7fcd738",
"rounds": 1,
"type": "eq",
"save": true,
"print": true,
"annotationStorage": {
"24R": {
"value": "x2"
}
}
} }
] ]