Skip to content

Commit 98fe094

Browse files
committed
Let non-viewable Popup Annotations inherit the parent's Annotation Flags if the parent is viewable
Fixes http://www.pdf-archive.com/2013/09/30/file2/file2.pdf. Note how it's not possible to show the various Popup Annotations in the above document. To fix that, this patch lets the Popup inherit the flags of the parent, in the special case where the parent is `viewable` *and* the Popup is not. In general, I don't think that a Popup must have the same flags set as the parent. However, it seems very strange to have a `viewable` parent annotation, and then not being able to view the Popup. Annoyingly the PDF specification doesn't, as far as I can find, mention anything about how this case should be handled, but this patch seem consistent with the actual behaviour in Adobe Reader.
1 parent 47b929b commit 98fe094

File tree

5 files changed

+91
-30
lines changed

5 files changed

+91
-30
lines changed

src/core/annotation.js

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -190,28 +190,49 @@ var Annotation = (function AnnotationClosure() {
190190
}
191191

192192
Annotation.prototype = {
193+
/**
194+
* @private
195+
*/
196+
_hasFlag: function Annotation_hasFlag(flags, flag) {
197+
return !!(flags & flag);
198+
},
199+
200+
/**
201+
* @private
202+
*/
203+
_isViewable: function Annotation_isViewable(flags) {
204+
return !this._hasFlag(flags, AnnotationFlag.INVISIBLE) &&
205+
!this._hasFlag(flags, AnnotationFlag.HIDDEN) &&
206+
!this._hasFlag(flags, AnnotationFlag.NOVIEW);
207+
},
208+
209+
/**
210+
* @private
211+
*/
212+
_isPrintable: function AnnotationFlag_isPrintable(flags) {
213+
return this._hasFlag(flags, AnnotationFlag.PRINT) &&
214+
!this._hasFlag(flags, AnnotationFlag.INVISIBLE) &&
215+
!this._hasFlag(flags, AnnotationFlag.HIDDEN);
216+
},
217+
193218
/**
194219
* @return {boolean}
195220
*/
196221
get viewable() {
197-
if (this.flags) {
198-
return !this.hasFlag(AnnotationFlag.INVISIBLE) &&
199-
!this.hasFlag(AnnotationFlag.HIDDEN) &&
200-
!this.hasFlag(AnnotationFlag.NOVIEW);
222+
if (this.flags === 0) {
223+
return true;
201224
}
202-
return true;
225+
return this._isViewable(this.flags);
203226
},
204227

205228
/**
206229
* @return {boolean}
207230
*/
208231
get printable() {
209-
if (this.flags) {
210-
return this.hasFlag(AnnotationFlag.PRINT) &&
211-
!this.hasFlag(AnnotationFlag.INVISIBLE) &&
212-
!this.hasFlag(AnnotationFlag.HIDDEN);
232+
if (this.flags === 0) {
233+
return false;
213234
}
214-
return false;
235+
return this._isPrintable(this.flags);
215236
},
216237

217238
/**
@@ -224,11 +245,7 @@ var Annotation = (function AnnotationClosure() {
224245
* @see {@link shared/util.js}
225246
*/
226247
setFlags: function Annotation_setFlags(flags) {
227-
if (isInt(flags)) {
228-
this.flags = flags;
229-
} else {
230-
this.flags = 0;
231-
}
248+
this.flags = (isInt(flags) && flags > 0) ? flags : 0;
232249
},
233250

234251
/**
@@ -242,10 +259,7 @@ var Annotation = (function AnnotationClosure() {
242259
* @see {@link shared/util.js}
243260
*/
244261
hasFlag: function Annotation_hasFlag(flag) {
245-
if (this.flags) {
246-
return (this.flags & flag) > 0;
247-
}
248-
return false;
262+
return this._hasFlag(this.flags, flag);
249263
},
250264

251265
/**
@@ -823,6 +837,16 @@ var PopupAnnotation = (function PopupAnnotationClosure() {
823837
this.setColor(parentItem.getArray('C'));
824838
this.data.color = this.color;
825839
}
840+
841+
// If the Popup annotation is not viewable, but the parent annotation is,
842+
// that is most likely a bug. Fallback to inherit the flags from the parent
843+
// annotation (this is consistent with the behaviour in Adobe Reader).
844+
if (!this.viewable) {
845+
var parentFlags = parentItem.get('F');
846+
if (this._isViewable(parentFlags)) {
847+
this.setFlags(parentFlags);
848+
}
849+
}
826850
}
827851

828852
Util.inherit(PopupAnnotation, Annotation, {});

test/pdfs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
!pr4922.pdf
100100
!pr6531_1.pdf
101101
!pr6531_2.pdf
102+
!pr7352.pdf
102103
!bug900822.pdf
103104
!issue918.pdf
104105
!issue1905.pdf

test/pdfs/pr7352.pdf

1.61 KB
Binary file not shown.

test/test_manifest.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,14 @@
18381838
"type": "eq",
18391839
"annotations": true
18401840
},
1841+
{ "id": "pr7352",
1842+
"file": "pdfs/pr7352.pdf",
1843+
"md5": "336abca4b313cb215b0569883f1f683d",
1844+
"link": false,
1845+
"rounds": 1,
1846+
"type": "eq",
1847+
"annotations": true
1848+
},
18411849
{ "id": "issue1002",
18421850
"file": "pdfs/issue1002.pdf",
18431851
"md5": "af62d6cd95079322d4af18edd960d15c",

test/unit/annotation_layer_spec.js

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ describe('Annotation layer', function() {
1515
}
1616
};
1717

18+
var annotationFactory;
19+
20+
beforeAll(function (done) {
21+
annotationFactory = new AnnotationFactory();
22+
done();
23+
});
24+
25+
afterAll(function () {
26+
annotationFactory = null;
27+
});
28+
1829
describe('Annotation', function() {
1930
it('should set and get flags', function() {
2031
var dict = new Dict();
@@ -185,17 +196,6 @@ describe('Annotation layer', function() {
185196
});
186197

187198
describe('LinkAnnotation', function() {
188-
var annotationFactory;
189-
190-
beforeAll(function (done) {
191-
annotationFactory = new AnnotationFactory();
192-
done();
193-
});
194-
195-
afterAll(function () {
196-
annotationFactory = null;
197-
});
198-
199199
it('should correctly parse a URI action', function() {
200200
var actionDict = new Dict();
201201
actionDict.set('Type', Name.get('Action'));
@@ -358,4 +358,32 @@ describe('Annotation layer', function() {
358358
expect(annotation.file.content).toEqual(stringToBytes('Test attachment'));
359359
});
360360
});
361+
362+
describe('PopupAnnotation', function() {
363+
it('should inherit the parent flags when the Popup is not viewable, ' +
364+
'but the parent is (PR 7352)', function () {
365+
var parentDict = new Dict();
366+
parentDict.set('Type', Name.get('Annot'));
367+
parentDict.set('Subtype', Name.get('Text'));
368+
parentDict.set('F', 28); // viewable
369+
370+
var popupDict = new Dict();
371+
popupDict.set('Type', Name.get('Annot'));
372+
popupDict.set('Subtype', Name.get('Popup'));
373+
popupDict.set('F', 25); // not viewable
374+
popupDict.set('Parent', parentDict);
375+
376+
var xrefMock = new XrefMock([popupDict]);
377+
var popupRef = new Ref(13, 0);
378+
379+
var popupAnnotation = annotationFactory.create(xrefMock, popupRef);
380+
var data = popupAnnotation.data;
381+
expect(data.annotationType).toEqual(AnnotationType.POPUP);
382+
383+
// Should not modify the `annotationFlags` returned e.g. through the API.
384+
expect(data.annotationFlags).toEqual(25);
385+
// The Popup should inherit the `viewable` property of the parent.
386+
expect(popupAnnotation.viewable).toEqual(true);
387+
});
388+
});
361389
});

0 commit comments

Comments
 (0)