diff --git a/src/core/annotation.js b/src/core/annotation.js index 50cd6ba096c2b..1355f08b44d06 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -1978,7 +1978,15 @@ class WidgetAnnotation extends Annotation { 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 === "") { // the field is empty: nothing to render diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 1eb5f9c5d1f2c..dce12bf6ffa2e 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -1612,10 +1612,10 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { selectElement.addEventListener("input", removeEmptyEntry); } - const getValue = (event, isExport) => { + const getValue = isExport => { const name = isExport ? "value" : "textContent"; - const options = event.target.options; - if (!event.target.multiple) { + const { options, multiple } = selectElement; + if (!multiple) { return options.selectedIndex === -1 ? null : options[options.selectedIndex][name]; @@ -1625,6 +1625,8 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { .map(option => option[name]); }; + let selectedValues = getValue(/* isExport */ false); + const getItems = event => { const options = event.target.options; return Array.prototype.map.call(options, option => { @@ -1643,8 +1645,9 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { option.selected = values.has(option.value); } storage.setValue(id, { - value: getValue(event, /* isExport */ true), + value: getValue(/* isExport */ true), }); + selectedValues = getValue(/* isExport */ false); }, multipleSelection(event) { selectElement.multiple = true; @@ -1664,15 +1667,17 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { } } storage.setValue(id, { - value: getValue(event, /* isExport */ true), + value: getValue(/* isExport */ true), items: getItems(event), }); + selectedValues = getValue(/* isExport */ false); }, clear(event) { while (selectElement.length !== 0) { selectElement.remove(0); } storage.setValue(id, { value: null, items: [] }); + selectedValues = getValue(/* isExport */ false); }, insert(event) { const { index, displayValue, exportValue } = event.detail.insert; @@ -1687,9 +1692,10 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { selectElement.append(optionElement); } storage.setValue(id, { - value: getValue(event, /* isExport */ true), + value: getValue(/* isExport */ true), items: getItems(event), }); + selectedValues = getValue(/* isExport */ false); }, items(event) { const { items } = event.detail; @@ -1707,9 +1713,10 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { selectElement.options[0].selected = true; } storage.setValue(id, { - value: getValue(event, /* isExport */ true), + value: getValue(/* isExport */ true), items: getItems(event), }); + selectedValues = getValue(/* isExport */ false); }, indices(event) { const indices = new Set(event.detail.indices); @@ -1717,8 +1724,9 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { option.selected = indices.has(option.index); } storage.setValue(id, { - value: getValue(event, /* isExport */ true), + value: getValue(/* isExport */ true), }); + selectedValues = getValue(/* isExport */ false); }, editable(event) { event.target.disabled = !event.detail.editable; @@ -1728,18 +1736,19 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { }); selectElement.addEventListener("input", event => { - const exportValue = getValue(event, /* isExport */ true); - const value = getValue(event, /* isExport */ false); + const exportValue = getValue(/* isExport */ true); storage.setValue(id, { value: exportValue }); + event.preventDefault(); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { source: this, detail: { id, name: "Keystroke", - value, + value: selectedValues, changeEx: exportValue, - willCommit: true, + willCommit: false, commitKey: 1, keyDown: false, }, @@ -1761,7 +1770,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { ); } else { selectElement.addEventListener("input", function (event) { - storage.setValue(id, { value: getValue(event, /* isExport */ true) }); + storage.setValue(id, { value: getValue(/* isExport */ true) }); }); } diff --git a/src/scripting_api/event.js b/src/scripting_api/event.js index 7b0fdecb8dd22..d85f28fe12c9d 100644 --- a/src/scripting_api/event.js +++ b/src/scripting_api/event.js @@ -137,6 +137,7 @@ class EventDispatcher { case "Keystroke": savedChange = { value: event.value, + changeEx: event.changeEx, change: event.change, selStart: event.selStart, selEnd: event.selEnd, @@ -170,6 +171,16 @@ class EventDispatcher { if (event.willCommit) { this.runValidation(source, event); } 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)); let selStart, selEnd; if ( diff --git a/src/scripting_api/field.js b/src/scripting_api/field.js index 59501b912d4fc..af67c99313f58 100644 --- a/src/scripting_api/field.js +++ b/src/scripting_api/field.js @@ -242,6 +242,11 @@ class Field extends PDFObject { } set value(value) { + if (this._isChoice) { + this._setChoiceValue(value); + return; + } + if (value === "") { this._value = ""; } else if (typeof value === "string") { @@ -260,23 +265,37 @@ class Field extends PDFObject { } else { this._value = value; } - if (this._isChoice) { - if (this.multipleSelection) { - const values = new Set(value); - if (Array.isArray(this._currentValueIndices)) { - this._currentValueIndices.length = 0; - } else { - this._currentValueIndices = []; - } - this._items.forEach(({ displayValue }, i) => { - if (values.has(displayValue)) { - this._currentValueIndices.push(i); - } - }); + } + + _setChoiceValue(value) { + if (this.multipleSelection) { + if (!Array.isArray(value)) { + value = [value]; + } + const values = new Set(value); + if (Array.isArray(this._currentValueIndices)) { + this._currentValueIndices.length = 0; + this._value.length = 0; } else { - this._currentValueIndices = this._items.findIndex( - ({ displayValue }) => value === displayValue - ); + this._currentValueIndices = []; + 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; } } } diff --git a/test/integration/scripting_spec.js b/test/integration/scripting_spec.js index e63063abb6a31..f759584e646a4 100644 --- a/test/integration/scripting_spec.js +++ b/test/integration/scripting_spec.js @@ -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); + } + }) + ); + }); + }); }); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 92a9c74a470b1..6fbb661d2916a 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -561,3 +561,4 @@ !issue15753.pdf !issue15789.pdf !fields_order.pdf +!issue15815.pdf diff --git a/test/pdfs/issue15815.pdf b/test/pdfs/issue15815.pdf new file mode 100755 index 0000000000000..266b4ba3cd6c3 Binary files /dev/null and b/test/pdfs/issue15815.pdf differ diff --git a/test/test_manifest.json b/test/test_manifest.json index 552d44d4be5ef..a2f856e5b2e08 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -7240,5 +7240,32 @@ "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" + } + } } ]