From 78889646c8329fa9fc1ad9d8a92049df3c0f1516 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 26 Aug 2016 16:01:25 +0200 Subject: [PATCH] Create a fallback annotation `id` for entries in `Annots` dictionaries that are not indirect objects (issue 7569) According to the PDF specification, see http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf#page=86, entries in `Annots` dictionaries should be indirect objects, but obviously there're PDF generators that ignore this. Fixes 7569. --- src/core/annotation.js | 11 ++- src/core/document.js | 9 ++- test/unit/annotation_layer_spec.js | 123 ++++++++++++++++++++++------- 3 files changed, 110 insertions(+), 33 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index d6aa45d96..85ed31a7f 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -49,6 +49,7 @@ var warn = sharedUtil.warn; var Dict = corePrimitives.Dict; var isDict = corePrimitives.isDict; var isName = corePrimitives.isName; +var isRef = corePrimitives.isRef; var Stream = coreStream.Stream; var ColorSpace = coreColorSpace.ColorSpace; var ObjectLoader = coreObj.ObjectLoader; @@ -66,11 +67,14 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ { * @param {Object} ref * @returns {Annotation} */ - create: function AnnotationFactory_create(xref, ref) { + create: function AnnotationFactory_create(xref, ref, + uniquePrefix, idCounters) { var dict = xref.fetchIfRef(ref); if (!isDict(dict)) { return; } + var id = isRef(ref) ? ref.toString() : + 'annot_' + (uniquePrefix || '') + (++idCounters.obj); // Determine the annotation's subtype. var subtype = dict.get('Subtype'); @@ -80,8 +84,9 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ { var parameters = { xref: xref, dict: dict, - ref: ref, + ref: isRef(ref) ? ref : null, subtype: subtype, + id: id, }; switch (subtype) { @@ -185,7 +190,7 @@ var Annotation = (function AnnotationClosure() { // Expose public properties using a data object. this.data = {}; - this.data.id = params.ref.toString(); + this.data.id = params.id; this.data.subtype = params.subtype; this.data.annotationFlags = this.flags; this.data.rect = this.rectangle; diff --git a/src/core/document.js b/src/core/document.js index a23c9fd89..9e414dc90 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -76,6 +76,7 @@ var Page = (function PageClosure() { this.xref = xref; this.ref = ref; this.fontCache = fontCache; + this.uniquePrefix = 'p' + this.pageIndex + '_'; this.idCounters = { obj: 0 }; @@ -223,7 +224,7 @@ var Page = (function PageClosure() { var partialEvaluator = new PartialEvaluator(pdfManager, this.xref, handler, this.pageIndex, - 'p' + this.pageIndex + '_', + this.uniquePrefix, this.idCounters, this.fontCache, this.evaluatorOptions); @@ -290,7 +291,7 @@ var Page = (function PageClosure() { var contentStream = data[0]; var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, - 'p' + self.pageIndex + '_', + self.uniquePrefix, self.idCounters, self.fontCache, self.evaluatorOptions); @@ -325,7 +326,9 @@ var Page = (function PageClosure() { var annotationFactory = new AnnotationFactory(); for (var i = 0, n = annotationRefs.length; i < n; ++i) { var annotationRef = annotationRefs[i]; - var annotation = annotationFactory.create(this.xref, annotationRef); + var annotation = annotationFactory.create(this.xref, annotationRef, + this.uniquePrefix, + this.idCounters); if (annotation) { annotations.push(annotation); } diff --git a/test/unit/annotation_layer_spec.js b/test/unit/annotation_layer_spec.js index ea8cd02eb..db37855e9 100644 --- a/test/unit/annotation_layer_spec.js +++ b/test/unit/annotation_layer_spec.js @@ -1,18 +1,29 @@ /* globals expect, it, describe, Dict, Name, Annotation, AnnotationBorderStyle, AnnotationBorderStyleType, AnnotationType, AnnotationFlag, PDFJS, - beforeEach, afterEach, stringToBytes, AnnotationFactory, Ref, + beforeEach, afterEach, stringToBytes, AnnotationFactory, Ref, isRef, beforeAll, afterAll */ 'use strict'; describe('Annotation layer', function() { - function XrefMock(queue) { - this.queue = queue || []; - } - XrefMock.prototype = { - fetchIfRef: function() { - return this.queue.shift(); + function XRefMock(array) { + this.map = Object.create(null); + for (var elem in array) { + var obj = array[elem]; + var ref = obj.ref, data = obj.data; + this.map[ref.toString()] = data; } + } + XRefMock.prototype = { + fetch: function (ref) { + return this.map[ref.toString()]; + }, + fetchIfRef: function (obj) { + if (!isRef(obj)) { + return obj; + } + return this.fetch(obj); + }, }; var annotationFactory; @@ -27,14 +38,54 @@ describe('Annotation layer', function() { }); describe('AnnotationFactory', function () { + it('should get id for annotation', function () { + var annotationDict = new Dict(); + annotationDict.set('Type', Name.get('Annot')); + annotationDict.set('Subtype', Name.get('Link')); + + var annotationRef = new Ref(10, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); + + var annotation = annotationFactory.create(xref, annotationRef); + var data = annotation.data; + expect(data.annotationType).toEqual(AnnotationType.LINK); + + expect(data.id).toEqual('10R'); + }); + + it('should handle, and get fallback id\'s for, annotations that are not ' + + 'indirect objects (issue 7569)', function () { + var annotationDict = new Dict(); + annotationDict.set('Type', Name.get('Annot')); + annotationDict.set('Subtype', Name.get('Link')); + + var xref = new XRefMock(); + var uniquePrefix = 'p0_', idCounters = { obj: 0, }; + + var annotation1 = annotationFactory.create(xref, annotationDict, + uniquePrefix, idCounters); + var annotation2 = annotationFactory.create(xref, annotationDict, + uniquePrefix, idCounters); + var data1 = annotation1.data, data2 = annotation2.data; + expect(data1.annotationType).toEqual(AnnotationType.LINK); + expect(data2.annotationType).toEqual(AnnotationType.LINK); + + expect(data1.id).toEqual('annot_p0_1'); + expect(data2.id).toEqual('annot_p0_2'); + }); + it('should handle missing /Subtype', function () { var annotationDict = new Dict(); annotationDict.set('Type', Name.get('Annot')); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(1, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toBeUndefined(); }); @@ -213,10 +264,12 @@ describe('Annotation layer', function() { annotationDict.set('Subtype', Name.get('Link')); annotationDict.set('A', actionDict); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(820, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -236,10 +289,12 @@ describe('Annotation layer', function() { annotationDict.set('Subtype', Name.get('Link')); annotationDict.set('A', actionDict); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(353, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -258,10 +313,12 @@ describe('Annotation layer', function() { annotationDict.set('Subtype', Name.get('Link')); annotationDict.set('A', actionDict); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(798, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -283,10 +340,12 @@ describe('Annotation layer', function() { annotationDict.set('Subtype', Name.get('Link')); annotationDict.set('A', actionDict); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(489, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -308,10 +367,12 @@ describe('Annotation layer', function() { annotationDict.set('Subtype', Name.get('Link')); annotationDict.set('A', actionDict); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(495, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -333,10 +394,12 @@ describe('Annotation layer', function() { annotationDict.set('Subtype', Name.get('Link')); annotationDict.set('A', actionDict); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(489, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -357,10 +420,12 @@ describe('Annotation layer', function() { annotationDict.set('Subtype', Name.get('Link')); annotationDict.set('A', actionDict); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(12, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -374,10 +439,12 @@ describe('Annotation layer', function() { annotationDict.set('Subtype', Name.get('Link')); annotationDict.set('Dest', Name.get('LI0')); - var xrefMock = new XrefMock([annotationDict]); var annotationRef = new Ref(583, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); - var annotation = annotationFactory.create(xrefMock, annotationRef); + var annotation = annotationFactory.create(xref, annotationRef); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -431,10 +498,12 @@ describe('Annotation layer', function() { popupDict.set('F', 25); // not viewable popupDict.set('Parent', parentDict); - var xrefMock = new XrefMock([popupDict]); var popupRef = new Ref(13, 0); + var xref = new XRefMock([ + { ref: popupRef, data: popupDict, } + ]); - var popupAnnotation = annotationFactory.create(xrefMock, popupRef); + var popupAnnotation = annotationFactory.create(xref, popupRef); var data = popupAnnotation.data; expect(data.annotationType).toEqual(AnnotationType.POPUP);