diff --git a/examples/acroforms/forms.js b/examples/acroforms/forms.js index 732ea94e5c240..91924e576fdc4 100644 --- a/examples/acroforms/forms.js +++ b/examples/acroforms/forms.js @@ -91,7 +91,7 @@ function setupForm(div, content, viewport) { // select box is not supported } input.className = 'inputControl'; - input.name = item.fullName; + input.name = item.fieldName; input.title = item.alternativeText; assignFontStyle(input, item); bindInputItem(input, item); diff --git a/src/core/annotation.js b/src/core/annotation.js index c0338cd749b54..9c5b2f5b80109 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -621,6 +621,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { var data = this.data; data.annotationType = AnnotationType.WIDGET; + data.fieldName = this._constructFieldName(dict); data.fieldValue = Util.getInheritableProperty(dict, 'V', /* getArray = */ true); data.alternativeText = stringToPDFString(dict.get('TU') || ''); @@ -640,41 +641,49 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { if (data.fieldType === 'Sig') { this.setFlags(AnnotationFlag.HIDDEN); } + } - // Building the full field name by collecting the field and - // its ancestors 'T' data and joining them using '.'. - var fieldName = []; - var namedItem = dict; - var ref = params.ref; - while (namedItem) { - var parent = namedItem.get('Parent'); - var parentRef = namedItem.getRaw('Parent'); - var name = namedItem.get('T'); - if (name) { - fieldName.unshift(stringToPDFString(name)); - } else if (parent && ref) { - // The field name is absent, that means more than one field - // with the same name may exist. Replacing the empty name - // with the '`' plus index in the parent's 'Kids' array. - // This is not in the PDF spec but necessary to id the - // the input controls. - var kids = parent.get('Kids'); - var j, jj; - for (j = 0, jj = kids.length; j < jj; j++) { - var kidRef = kids[j]; - if (kidRef.num === ref.num && kidRef.gen === ref.gen) { - break; - } + Util.inherit(WidgetAnnotation, Annotation, { + /** + * Construct the (fully qualified) field name from the (partial) field + * names of the field and its ancestors. + * + * @private + * @memberof WidgetAnnotation + * @param {Dict} dict - Complete widget annotation dictionary + * @return {string} + */ + _constructFieldName: function WidgetAnnotation_constructFieldName(dict) { + // Both the `Parent` and `T` fields are optional. While at least one of + // them should be provided, bad PDF generators may fail to do so. + if (!dict.has('T') && !dict.has('Parent')) { + warn('Unknown field name, falling back to empty field name.'); + return ''; + } + + // If no parent exists, the partial and fully qualified names are equal. + if (!dict.has('Parent')) { + return stringToPDFString(dict.get('T')); + } + + // Form the fully qualified field name by appending the partial name to + // the parent's fully qualified name, separated by a period. + var fieldName = []; + if (dict.has('T')) { + fieldName.unshift(stringToPDFString(dict.get('T'))); + } + + var loopDict = dict; + while (loopDict.has('Parent')) { + loopDict = loopDict.get('Parent'); + + if (loopDict.has('T')) { + fieldName.unshift(stringToPDFString(loopDict.get('T'))); } - fieldName.unshift('`' + j); } - namedItem = parent; - ref = parentRef; - } - data.fullName = fieldName.join('.'); - } + return fieldName.join('.'); + }, - Util.inherit(WidgetAnnotation, Annotation, { /** * Check if a provided field flag is set. * diff --git a/test/unit/annotation_layer_spec.js b/test/unit/annotation_layer_spec.js index 16d461716dc18..dc5a43b6c4333 100644 --- a/test/unit/annotation_layer_spec.js +++ b/test/unit/annotation_layer_spec.js @@ -577,6 +577,74 @@ describe('Annotation layer', function() { }); }); + describe('WidgetAnnotation', function() { + var widgetDict; + + beforeEach(function (done) { + widgetDict = new Dict(); + widgetDict.set('Type', Name.get('Annot')); + widgetDict.set('Subtype', Name.get('Widget')); + + done(); + }); + + afterEach(function () { + widgetDict = null; + }); + + it('should handle unknown field names', function() { + var widgetRef = new Ref(20, 0); + var xref = new XRefMock([ + { ref: widgetRef, data: widgetDict, } + ]); + + var widgetAnnotation = annotationFactory.create(xref, widgetRef, + pdfManagerMock); + var data = widgetAnnotation.data; + expect(data.annotationType).toEqual(AnnotationType.WIDGET); + expect(data.fieldName).toEqual(''); + }); + + it('should construct the field name when there are no ancestors', + function() { + widgetDict.set('T', 'foo'); + + var widgetRef = new Ref(21, 0); + var xref = new XRefMock([ + { ref: widgetRef, data: widgetDict, } + ]); + + var widgetAnnotation = annotationFactory.create(xref, widgetRef, + pdfManagerMock); + var data = widgetAnnotation.data; + expect(data.annotationType).toEqual(AnnotationType.WIDGET); + expect(data.fieldName).toEqual('foo'); + }); + + it('should construct the field name when there are ancestors', function() { + var firstParent = new Dict(); + firstParent.set('T', 'foo'); + + var secondParent = new Dict(); + secondParent.set('Parent', firstParent); + secondParent.set('T', 'bar'); + + widgetDict.set('Parent', secondParent); + widgetDict.set('T', 'baz'); + + var widgetRef = new Ref(22, 0); + var xref = new XRefMock([ + { ref: widgetRef, data: widgetDict, } + ]); + + var widgetAnnotation = annotationFactory.create(xref, widgetRef, + pdfManagerMock); + var data = widgetAnnotation.data; + expect(data.annotationType).toEqual(AnnotationType.WIDGET); + expect(data.fieldName).toEqual('foo.bar.baz'); + }); + }); + describe('TextWidgetAnnotation', function() { var textWidgetDict;