Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactive forms: render choice widget annotations #7671

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
switch (fieldType) {
case 'Tx':
return new TextWidgetAnnotation(parameters);
case 'Ch':
return new ChoiceWidgetAnnotation(parameters);
}
warn('Unimplemented widget field type "' + fieldType + '", ' +
'falling back to base field type.');
Expand Down Expand Up @@ -619,8 +621,8 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
var data = this.data;

data.annotationType = AnnotationType.WIDGET;
data.fieldValue = stringToPDFString(
Util.getInheritableProperty(dict, 'V') || '');
data.fieldValue = Util.getInheritableProperty(dict, 'V',
/* getArray = */ true);
data.alternativeText = stringToPDFString(dict.get('TU') || '');
data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
var fieldType = Util.getInheritableProperty(dict, 'FT');
Expand All @@ -632,6 +634,8 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
data.fieldFlags = 0;
}

data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);

// Hide signatures because we cannot validate them.
if (data.fieldType === 'Sig') {
this.setFlags(AnnotationFlag.HIDDEN);
Expand Down Expand Up @@ -693,6 +697,9 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
function TextWidgetAnnotation(params) {
WidgetAnnotation.call(this, params);

// The field value is always a string.
this.data.fieldValue = stringToPDFString(this.data.fieldValue || '');

// Determine the alignment of text in the field.
var alignment = Util.getInheritableProperty(params.dict, 'Q');
if (!isInt(alignment) || alignment < 0 || alignment > 2) {
Expand All @@ -708,7 +715,6 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
this.data.maxLen = maximumLength;

// Process field flags for the display layer.
this.data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE);
this.data.comb = this.hasFieldFlag(AnnotationFieldFlag.COMB) &&
!this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) &&
Expand Down Expand Up @@ -752,6 +758,62 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
return TextWidgetAnnotation;
})();

var ChoiceWidgetAnnotation = (function ChoiceWidgetAnnotationClosure() {
function ChoiceWidgetAnnotation(params) {
WidgetAnnotation.call(this, params);

// Determine the options. The options array may consist of strings or
// arrays. If the array consists of arrays, then the first element of
// each array is the export value and the second element of each array is
// the display value. If the array consists of strings, then these
// represent both the export and display value. In this case, we convert
// it to an array of arrays as well for convenience in the display layer.
this.data.options = [];

var options = params.dict.getArray('Opt');
if (isArray(options)) {
for (var i = 0, ii = options.length; i < ii; i++) {
var option = options[i];

this.data.options[i] = {
exportValue: isArray(option) ? option[0] : option,
displayValue: isArray(option) ? option[1] : option,
};
}
}

// Determine the field value. In this case, it may be a string or an
// array of strings. For convenience in the display layer, convert the
// string to an array of one string as well.
if (!isArray(this.data.fieldValue)) {
Copy link
Collaborator

@Snuffleupagus Snuffleupagus Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to my other comment about references inside of arrays, if this.data.fieldValue already is an array you will have problems in the display layer if an array element is a Ref.

Please refer to the comment on the data.fieldValue = Util.getInheritableProperty(dict, 'V'); line for a suggested solution.

this.data.fieldValue = [this.data.fieldValue];
}

// Process field flags for the display layer.
this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO);
this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT);
}

Util.inherit(ChoiceWidgetAnnotation, WidgetAnnotation, {
getOperatorList:
function ChoiceWidgetAnnotation_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);
}

return Annotation.prototype.getOperatorList.call(this, evaluator, task,
renderForms);
}
});

return ChoiceWidgetAnnotation;
})();

var TextAnnotation = (function TextAnnotationClosure() {
var DEFAULT_ICON_SIZE = 22; // px

Expand Down
68 changes: 64 additions & 4 deletions src/display/annotation_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ AnnotationElementFactory.prototype =
switch (fieldType) {
case 'Tx':
return new TextWidgetAnnotationElement(parameters);
case 'Ch':
return new ChoiceWidgetAnnotationElement(parameters);
}
return new WidgetAnnotationElement(parameters);

Expand Down Expand Up @@ -400,9 +402,7 @@ var TextAnnotationElement = (function TextAnnotationElementClosure() {
* @alias WidgetAnnotationElement
*/
var WidgetAnnotationElement = (function WidgetAnnotationElementClosure() {
function WidgetAnnotationElement(parameters) {
var isRenderable = parameters.renderInteractiveForms ||
(!parameters.data.hasAppearance && !!parameters.data.fieldValue);
function WidgetAnnotationElement(parameters, isRenderable) {
AnnotationElement.call(this, parameters, isRenderable);
}

Expand Down Expand Up @@ -432,7 +432,9 @@ var TextWidgetAnnotationElement = (
var TEXT_ALIGNMENT = ['left', 'center', 'right'];

function TextWidgetAnnotationElement(parameters) {
WidgetAnnotationElement.call(this, parameters);
var isRenderable = parameters.renderInteractiveForms ||
(!parameters.data.hasAppearance && !!parameters.data.fieldValue);
WidgetAnnotationElement.call(this, parameters, isRenderable);
}

Util.inherit(TextWidgetAnnotationElement, WidgetAnnotationElement, {
Expand Down Expand Up @@ -528,6 +530,64 @@ var TextWidgetAnnotationElement = (
return TextWidgetAnnotationElement;
})();

/**
* @class
* @alias ChoiceWidgetAnnotationElement
*/
var ChoiceWidgetAnnotationElement = (
function ChoiceWidgetAnnotationElementClosure() {
function ChoiceWidgetAnnotationElement(parameters) {
WidgetAnnotationElement.call(this, parameters,
parameters.renderInteractiveForms);
}

Util.inherit(ChoiceWidgetAnnotationElement, WidgetAnnotationElement, {
/**
* Render the choice widget annotation's HTML element in the empty
* container.
*
* @public
* @memberof ChoiceWidgetAnnotationElement
* @returns {HTMLSectionElement}
*/
render: function ChoiceWidgetAnnotationElement_render() {
this.container.className = 'choiceWidgetAnnotation';

var selectElement = document.createElement('select');
selectElement.disabled = this.data.readOnly;

if (!this.data.combo) {
// List boxes have a size and (optionally) multiple selection.
selectElement.size = this.data.options.length;

if (this.data.multiSelect) {
selectElement.multiple = true;
}
}

// Insert the options into the choice field.
for (var i = 0, ii = this.data.options.length; i < ii; i++) {
var option = this.data.options[i];

var optionElement = document.createElement('option');
optionElement.textContent = option.displayValue;
optionElement.value = option.exportValue;

if (this.data.fieldValue.indexOf(option.displayValue) >= 0) {
optionElement.setAttribute('selected', true);
}

selectElement.appendChild(optionElement);
}

this.container.appendChild(selectElement);
return this.container;
}
});

return ChoiceWidgetAnnotationElement;
})();

/**
* @class
* @alias PopupAnnotationElement
Expand Down
6 changes: 3 additions & 3 deletions src/shared/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -873,15 +873,15 @@ var Util = (function UtilClosure() {
}
};

Util.getInheritableProperty = function Util_getInheritableProperty(dict,
name) {
Util.getInheritableProperty =
function Util_getInheritableProperty(dict, name, getArray) {
while (dict && !dict.has(name)) {
dict = dict.get('Parent');
}
if (!dict) {
return null;
}
return dict.get(name);
return getArray ? dict.getArray(name) : dict.get(name);
};

Util.inherit = function Util_inherit(sub, base, prototype) {
Expand Down
6 changes: 4 additions & 2 deletions test/annotation_layer_test.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
}

.annotationLayer .textWidgetAnnotation input,
.annotationLayer .textWidgetAnnotation textarea {
.annotationLayer .textWidgetAnnotation textarea,
.annotationLayer .choiceWidgetAnnotation select {
background-color: rgba(0, 54, 255, 0.13);
border: 1px solid transparent;
box-sizing: border-box;
Expand All @@ -62,7 +63,8 @@
}

.annotationLayer .textWidgetAnnotation input[disabled],
.annotationLayer .textWidgetAnnotation textarea[disabled] {
.annotationLayer .textWidgetAnnotation textarea[disabled],
.annotationLayer .choiceWidgetAnnotation select[disabled] {
background: none;
border: 1px solid transparent;
}
Expand Down
1 change: 1 addition & 0 deletions test/pdfs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,5 @@
!annotation-highlight.pdf
!annotation-fileattachment.pdf
!annotation-text-widget.pdf
!annotation-choice-widget.pdf
!zero_descent.pdf
Binary file added test/pdfs/annotation-choice-widget.pdf
Binary file not shown.
14 changes: 14 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3216,6 +3216,20 @@
"type": "eq",
"forms": true
},
{ "id": "annotation-choice-widget-annotations",
"file": "pdfs/annotation-choice-widget.pdf",
"md5": "7dfb0d743a0da0f4a71b209ab43b0be5",
"rounds": 1,
"type": "eq",
"annotations": true
},
{ "id": "annotation-choice-widget-forms",
"file": "pdfs/annotation-choice-widget.pdf",
"md5": "7dfb0d743a0da0f4a71b209ab43b0be5",
"rounds": 1,
"type": "eq",
"forms": true
},
{ "id": "issue6108",
"file": "pdfs/issue6108.pdf",
"md5": "8961cb55149495989a80bf0487e0f076",
Expand Down
Loading