Merge pull request #7898 from timvandermeij/acroforms-checkbox-radiobutton
Interactive forms: render button widget annotations (checkboxes and radio buttons)
This commit is contained in:
commit
017e9b98d1
@ -106,6 +106,8 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
|
||||
switch (fieldType) {
|
||||
case 'Tx':
|
||||
return new TextWidgetAnnotation(parameters);
|
||||
case 'Btn':
|
||||
return new ButtonWidgetAnnotation(parameters);
|
||||
case 'Ch':
|
||||
return new ChoiceWidgetAnnotation(parameters);
|
||||
}
|
||||
@ -767,6 +769,78 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
|
||||
return TextWidgetAnnotation;
|
||||
})();
|
||||
|
||||
var ButtonWidgetAnnotation = (function ButtonWidgetAnnotationClosure() {
|
||||
function ButtonWidgetAnnotation(params) {
|
||||
WidgetAnnotation.call(this, params);
|
||||
|
||||
this.data.checkBox = !this.hasFieldFlag(AnnotationFieldFlag.RADIO) &&
|
||||
!this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);
|
||||
if (this.data.checkBox) {
|
||||
if (!isName(this.data.fieldValue)) {
|
||||
return;
|
||||
}
|
||||
this.data.fieldValue = this.data.fieldValue.name;
|
||||
}
|
||||
|
||||
this.data.radioButton = this.hasFieldFlag(AnnotationFieldFlag.RADIO) &&
|
||||
!this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);
|
||||
if (this.data.radioButton) {
|
||||
this.data.fieldValue = this.data.buttonValue = null;
|
||||
|
||||
// The parent field's `V` entry holds a `Name` object with the appearance
|
||||
// state of whichever child field is currently in the "on" state.
|
||||
var fieldParent = params.dict.get('Parent');
|
||||
if (!isDict(fieldParent) || !fieldParent.has('V')) {
|
||||
return;
|
||||
}
|
||||
var fieldParentValue = fieldParent.get('V');
|
||||
if (!isName(fieldParentValue)) {
|
||||
return;
|
||||
}
|
||||
this.data.fieldValue = fieldParentValue.name;
|
||||
|
||||
// The button's value corresponds to its appearance state.
|
||||
var appearanceStates = params.dict.get('AP');
|
||||
if (!isDict(appearanceStates)) {
|
||||
return;
|
||||
}
|
||||
var normalAppearanceState = appearanceStates.get('N');
|
||||
if (!isDict(normalAppearanceState)) {
|
||||
return;
|
||||
}
|
||||
var keys = normalAppearanceState.getKeys();
|
||||
for (var i = 0, ii = keys.length; i < ii; i++) {
|
||||
if (keys[i] !== 'Off') {
|
||||
this.data.buttonValue = keys[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Util.inherit(ButtonWidgetAnnotation, WidgetAnnotation, {
|
||||
getOperatorList:
|
||||
function ButtonWidgetAnnotation_getOperatorList(evaluator, task,
|
||||
renderForms) {
|
||||
var operatorList = new OperatorList();
|
||||
|
||||
// Do not render form elements on the canvas when interactive forms are
|
||||
// enabled. The display layer is responsible for rendering them instead.
|
||||
if (renderForms) {
|
||||
return Promise.resolve(operatorList);
|
||||
}
|
||||
|
||||
if (this.appearance) {
|
||||
return Annotation.prototype.getOperatorList.call(this, evaluator, task,
|
||||
renderForms);
|
||||
}
|
||||
return Promise.resolve(operatorList);
|
||||
}
|
||||
});
|
||||
|
||||
return ButtonWidgetAnnotation;
|
||||
})();
|
||||
|
||||
var ChoiceWidgetAnnotation = (function ChoiceWidgetAnnotationClosure() {
|
||||
function ChoiceWidgetAnnotation(params) {
|
||||
WidgetAnnotation.call(this, params);
|
||||
|
@ -76,6 +76,15 @@ AnnotationElementFactory.prototype =
|
||||
switch (fieldType) {
|
||||
case 'Tx':
|
||||
return new TextWidgetAnnotationElement(parameters);
|
||||
case 'Btn':
|
||||
if (parameters.data.radioButton) {
|
||||
return new RadioButtonWidgetAnnotationElement(parameters);
|
||||
} else if (parameters.data.checkBox) {
|
||||
return new CheckboxWidgetAnnotationElement(parameters);
|
||||
} else {
|
||||
warn('Unimplemented button widget annotation: pushbutton');
|
||||
}
|
||||
break;
|
||||
case 'Ch':
|
||||
return new ChoiceWidgetAnnotationElement(parameters);
|
||||
}
|
||||
@ -531,6 +540,83 @@ var TextWidgetAnnotationElement = (
|
||||
})();
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @alias CheckboxWidgetAnnotationElement
|
||||
*/
|
||||
var CheckboxWidgetAnnotationElement =
|
||||
(function CheckboxWidgetAnnotationElementClosure() {
|
||||
function CheckboxWidgetAnnotationElement(parameters) {
|
||||
WidgetAnnotationElement.call(this, parameters,
|
||||
parameters.renderInteractiveForms);
|
||||
}
|
||||
|
||||
Util.inherit(CheckboxWidgetAnnotationElement, WidgetAnnotationElement, {
|
||||
/**
|
||||
* Render the checkbox widget annotation's HTML element
|
||||
* in the empty container.
|
||||
*
|
||||
* @public
|
||||
* @memberof CheckboxWidgetAnnotationElement
|
||||
* @returns {HTMLSectionElement}
|
||||
*/
|
||||
render: function CheckboxWidgetAnnotationElement_render() {
|
||||
this.container.className = 'buttonWidgetAnnotation checkBox';
|
||||
|
||||
var element = document.createElement('input');
|
||||
element.disabled = this.data.readOnly;
|
||||
element.type = 'checkbox';
|
||||
if (this.data.fieldValue && this.data.fieldValue !== 'Off') {
|
||||
element.setAttribute('checked', true);
|
||||
}
|
||||
|
||||
this.container.appendChild(element);
|
||||
return this.container;
|
||||
}
|
||||
});
|
||||
|
||||
return CheckboxWidgetAnnotationElement;
|
||||
})();
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @alias RadioButtonWidgetAnnotationElement
|
||||
*/
|
||||
var RadioButtonWidgetAnnotationElement =
|
||||
(function RadioButtonWidgetAnnotationElementClosure() {
|
||||
function RadioButtonWidgetAnnotationElement(parameters) {
|
||||
WidgetAnnotationElement.call(this, parameters,
|
||||
parameters.renderInteractiveForms);
|
||||
}
|
||||
|
||||
Util.inherit(RadioButtonWidgetAnnotationElement, WidgetAnnotationElement, {
|
||||
/**
|
||||
* Render the radio button widget annotation's HTML element
|
||||
* in the empty container.
|
||||
*
|
||||
* @public
|
||||
* @memberof RadioButtonWidgetAnnotationElement
|
||||
* @returns {HTMLSectionElement}
|
||||
*/
|
||||
render: function RadioButtonWidgetAnnotationElement_render() {
|
||||
this.container.className = 'buttonWidgetAnnotation radioButton';
|
||||
|
||||
var element = document.createElement('input');
|
||||
element.disabled = this.data.readOnly;
|
||||
element.type = 'radio';
|
||||
element.name = this.data.fieldName;
|
||||
if (this.data.fieldValue === this.data.buttonValue) {
|
||||
element.setAttribute('checked', true);
|
||||
}
|
||||
|
||||
this.container.appendChild(element);
|
||||
return this.container;
|
||||
}
|
||||
});
|
||||
|
||||
return RadioButtonWidgetAnnotationElement;
|
||||
})();
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @alias ChoiceWidgetAnnotationElement
|
||||
*/
|
||||
|
@ -15,6 +15,11 @@
|
||||
|
||||
/* Used for annotation layer tests */
|
||||
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.annotationLayer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@ -45,7 +50,9 @@
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input,
|
||||
.annotationLayer .textWidgetAnnotation textarea,
|
||||
.annotationLayer .choiceWidgetAnnotation select {
|
||||
.annotationLayer .choiceWidgetAnnotation select,
|
||||
.annotationLayer .buttonWidgetAnnotation.checkBox input,
|
||||
.annotationLayer .buttonWidgetAnnotation.radioButton input {
|
||||
background-color: rgba(0, 54, 255, 0.13);
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
@ -64,7 +71,9 @@
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input[disabled],
|
||||
.annotationLayer .textWidgetAnnotation textarea[disabled],
|
||||
.annotationLayer .choiceWidgetAnnotation select[disabled] {
|
||||
.annotationLayer .choiceWidgetAnnotation select[disabled],
|
||||
.annotationLayer .buttonWidgetAnnotation.checkBox input[disabled],
|
||||
.annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
@ -75,6 +84,14 @@
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.annotationLayer .buttonWidgetAnnotation.checkBox input,
|
||||
.annotationLayer .buttonWidgetAnnotation.radioButton input {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
-ms-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.annotationLayer .popupAnnotation {
|
||||
display: block !important;
|
||||
}
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -266,5 +266,6 @@
|
||||
!annotation-fileattachment.pdf
|
||||
!annotation-text-widget.pdf
|
||||
!annotation-choice-widget.pdf
|
||||
!annotation-button-widget.pdf
|
||||
!zero_descent.pdf
|
||||
!operator-in-TJ-array.pdf
|
||||
|
BIN
test/pdfs/annotation-button-widget.pdf
Normal file
BIN
test/pdfs/annotation-button-widget.pdf
Normal file
Binary file not shown.
@ -3312,6 +3312,20 @@
|
||||
"type": "eq",
|
||||
"forms": true
|
||||
},
|
||||
{ "id": "annotation-button-widget-annotations",
|
||||
"file": "pdfs/annotation-button-widget.pdf",
|
||||
"md5": "5cf23adfff84256d9cfe261bea96dade",
|
||||
"rounds": 1,
|
||||
"type": "eq",
|
||||
"annotations": true
|
||||
},
|
||||
{ "id": "annotation-button-widget-forms",
|
||||
"file": "pdfs/annotation-button-widget.pdf",
|
||||
"md5": "5cf23adfff84256d9cfe261bea96dade",
|
||||
"rounds": 1,
|
||||
"type": "eq",
|
||||
"forms": true
|
||||
},
|
||||
{ "id": "issue6108",
|
||||
"file": "pdfs/issue6108.pdf",
|
||||
"md5": "8961cb55149495989a80bf0487e0f076",
|
||||
|
@ -869,6 +869,65 @@ describe('Annotation layer', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('ButtonWidgetAnnotation', function() {
|
||||
var buttonWidgetDict;
|
||||
|
||||
beforeEach(function (done) {
|
||||
buttonWidgetDict = new Dict();
|
||||
buttonWidgetDict.set('Type', Name.get('Annot'));
|
||||
buttonWidgetDict.set('Subtype', Name.get('Widget'));
|
||||
buttonWidgetDict.set('FT', Name.get('Btn'));
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
buttonWidgetDict = null;
|
||||
});
|
||||
|
||||
it('should handle checkboxes', function() {
|
||||
buttonWidgetDict.set('V', Name.get('1'));
|
||||
|
||||
var buttonWidgetRef = new Ref(124, 0);
|
||||
var xref = new XRefMock([
|
||||
{ ref: buttonWidgetRef, data: buttonWidgetDict, }
|
||||
]);
|
||||
|
||||
var buttonWidgetAnnotation =
|
||||
annotationFactory.create(xref, buttonWidgetRef);
|
||||
expect(buttonWidgetAnnotation.data.checkBox).toEqual(true);
|
||||
expect(buttonWidgetAnnotation.data.fieldValue).toEqual('1');
|
||||
expect(buttonWidgetAnnotation.data.radioButton).toEqual(false);
|
||||
});
|
||||
|
||||
it('should handle radio buttons', function() {
|
||||
var parentDict = new Dict();
|
||||
parentDict.set('V', Name.get('1'));
|
||||
|
||||
var normalAppearanceStateDict = new Dict();
|
||||
normalAppearanceStateDict.set('2', null);
|
||||
|
||||
var appearanceStatesDict = new Dict();
|
||||
appearanceStatesDict.set('N', normalAppearanceStateDict);
|
||||
|
||||
buttonWidgetDict.set('Ff', AnnotationFieldFlag.RADIO);
|
||||
buttonWidgetDict.set('Parent', parentDict);
|
||||
buttonWidgetDict.set('AP', appearanceStatesDict);
|
||||
|
||||
var buttonWidgetRef = new Ref(124, 0);
|
||||
var xref = new XRefMock([
|
||||
{ ref: buttonWidgetRef, data: buttonWidgetDict, }
|
||||
]);
|
||||
|
||||
var buttonWidgetAnnotation =
|
||||
annotationFactory.create(xref, buttonWidgetRef);
|
||||
expect(buttonWidgetAnnotation.data.checkBox).toEqual(false);
|
||||
expect(buttonWidgetAnnotation.data.radioButton).toEqual(true);
|
||||
expect(buttonWidgetAnnotation.data.fieldValue).toEqual('1');
|
||||
expect(buttonWidgetAnnotation.data.buttonValue).toEqual('2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ChoiceWidgetAnnotation', function() {
|
||||
var choiceWidgetDict;
|
||||
|
||||
|
@ -43,7 +43,9 @@
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input,
|
||||
.annotationLayer .textWidgetAnnotation textarea,
|
||||
.annotationLayer .choiceWidgetAnnotation select {
|
||||
.annotationLayer .choiceWidgetAnnotation select,
|
||||
.annotationLayer .buttonWidgetAnnotation.checkBox input,
|
||||
.annotationLayer .buttonWidgetAnnotation.radioButton input {
|
||||
background-color: rgba(0, 54, 255, 0.13);
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
@ -62,7 +64,9 @@
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input[disabled],
|
||||
.annotationLayer .textWidgetAnnotation textarea[disabled],
|
||||
.annotationLayer .choiceWidgetAnnotation select[disabled] {
|
||||
.annotationLayer .choiceWidgetAnnotation select[disabled],
|
||||
.annotationLayer .buttonWidgetAnnotation.checkBox input[disabled],
|
||||
.annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
cursor: not-allowed;
|
||||
@ -70,7 +74,9 @@
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input:hover,
|
||||
.annotationLayer .textWidgetAnnotation textarea:hover,
|
||||
.annotationLayer .choiceWidgetAnnotation select:hover {
|
||||
.annotationLayer .choiceWidgetAnnotation select:hover,
|
||||
.annotationLayer .buttonWidgetAnnotation.checkBox input:hover,
|
||||
.annotationLayer .buttonWidgetAnnotation.radioButton input:hover {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
@ -97,6 +103,14 @@
|
||||
width: 115%;
|
||||
}
|
||||
|
||||
.annotationLayer .buttonWidgetAnnotation.checkBox input,
|
||||
.annotationLayer .buttonWidgetAnnotation.radioButton input {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
-ms-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.annotationLayer .popupWrapper {
|
||||
position: absolute;
|
||||
width: 20em;
|
||||
|
Loading…
Reference in New Issue
Block a user