From 9114004d5b47f8b4ec201aef7957d8401f5721b7 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 27 Jul 2019 20:57:52 +0200 Subject: [PATCH] [api-minor] Implement quadpoints for annotations in the core layer --- src/core/annotation.js | 59 +++++++ test/unit/annotation_spec.js | 289 ++++++++++++++++++++++++++++++++++- 2 files changed, 347 insertions(+), 1 deletion(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index 4d817365d..830a31418 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -146,6 +146,39 @@ class AnnotationFactory { } } +function getQuadPoints(dict, rect) { + if (!dict.has('QuadPoints')) { + return null; + } + + // The region is described as a number of quadrilaterals. + // Each quadrilateral must consist of eight coordinates. + const quadPoints = dict.getArray('QuadPoints'); + if (!Array.isArray(quadPoints) || quadPoints.length % 8 > 0) { + return null; + } + + const quadPointsLists = []; + for (let i = 0, ii = quadPoints.length / 8; i < ii; i++) { + // Each series of eight numbers represents the coordinates for one + // quadrilateral in the order [x1, y1, x2, y2, x3, y3, x4, y4]. + // Convert this to an array of objects with x and y coordinates. + quadPointsLists.push([]); + for (let j = i * 8, jj = (i * 8) + 8; j < jj; j += 2) { + const x = quadPoints[j]; + const y = quadPoints[j + 1]; + + // The quadpoints should be ignored if any coordinate in the array + // lies outside the region specified by the rectangle. + if (x < rect[0] || x > rect[2] || y < rect[1] || y > rect[3]) { + return null; + } + quadPointsLists[i].push({ x, y, }); + } + } + return quadPointsLists; +} + function getTransformMatrix(rect, bbox, matrix) { // 12.5.5: Algorithm: Appearance streams let bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); @@ -1042,6 +1075,11 @@ class LinkAnnotation extends Annotation { this.data.annotationType = AnnotationType.LINK; + const quadPoints = getQuadPoints(params.dict, this.rectangle); + if (quadPoints) { + this.data.quadPoints = quadPoints; + } + Catalog.parseDestDictionary({ destDict: params.dict, resultObj: this.data, @@ -1211,6 +1249,11 @@ class HighlightAnnotation extends MarkupAnnotation { super(parameters); this.data.annotationType = AnnotationType.HIGHLIGHT; + + const quadPoints = getQuadPoints(parameters.dict, this.rectangle); + if (quadPoints) { + this.data.quadPoints = quadPoints; + } } } @@ -1219,6 +1262,11 @@ class UnderlineAnnotation extends MarkupAnnotation { super(parameters); this.data.annotationType = AnnotationType.UNDERLINE; + + const quadPoints = getQuadPoints(parameters.dict, this.rectangle); + if (quadPoints) { + this.data.quadPoints = quadPoints; + } } } @@ -1227,6 +1275,11 @@ class SquigglyAnnotation extends MarkupAnnotation { super(parameters); this.data.annotationType = AnnotationType.SQUIGGLY; + + const quadPoints = getQuadPoints(parameters.dict, this.rectangle); + if (quadPoints) { + this.data.quadPoints = quadPoints; + } } } @@ -1235,6 +1288,11 @@ class StrikeOutAnnotation extends MarkupAnnotation { super(parameters); this.data.annotationType = AnnotationType.STRIKEOUT; + + const quadPoints = getQuadPoints(parameters.dict, this.rectangle); + if (quadPoints) { + this.data.quadPoints = quadPoints; + } } } @@ -1262,4 +1320,5 @@ export { AnnotationBorderStyle, AnnotationFactory, MarkupAnnotation, + getQuadPoints, }; diff --git a/test/unit/annotation_spec.js b/test/unit/annotation_spec.js index 4b634632c..1b3664c18 100644 --- a/test/unit/annotation_spec.js +++ b/test/unit/annotation_spec.js @@ -14,7 +14,8 @@ */ import { - Annotation, AnnotationBorderStyle, AnnotationFactory, MarkupAnnotation + Annotation, AnnotationBorderStyle, AnnotationFactory, getQuadPoints, + MarkupAnnotation } from '../../src/core/annotation'; import { AnnotationBorderStyleType, AnnotationFieldFlag, AnnotationFlag, @@ -118,6 +119,69 @@ describe('annotation', function() { }); }); + describe('getQuadPoints', function() { + let dict, rect; + + beforeEach(function(done) { + dict = new Dict(); + rect = []; + done(); + }); + + afterEach(function() { + dict = null; + rect = null; + }); + + it('should ignore missing quadpoints', function() { + expect(getQuadPoints(dict, rect)).toEqual(null); + }); + + it('should ignore non-array values', function() { + dict.set('QuadPoints', 'foo'); + expect(getQuadPoints(dict, rect)).toEqual(null); + }); + + it('should ignore arrays where the length is not a multiple of eight', + function() { + dict.set('QuadPoints', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect(getQuadPoints(dict, rect)).toEqual(null); + }); + + it('should ignore quadpoints if one coordinate lies outside the rectangle', + function() { + rect = [10, 10, 20, 20]; + const inputs = [ + [11, 11, 12, 12, 9, 13, 14, 14], // Smaller than lower x coordinate. + [11, 11, 12, 12, 13, 9, 14, 14], // Smaller than lower y coordinate. + [11, 11, 12, 12, 21, 13, 14, 14], // Larger than upper x coordinate. + [11, 11, 12, 12, 13, 21, 14, 14], // Larger than upper y coordinate. + ]; + for (const input of inputs) { + dict.set('QuadPoints', input); + expect(getQuadPoints(dict, rect)).toEqual(null); + } + }); + + it('should process valid quadpoints arrays', function() { + rect = [10, 10, 20, 20]; + dict.set('QuadPoints', [ + 11, 11, 12, 12, 13, 13, 14, 14, + 15, 15, 16, 16, 17, 17, 18, 18, + ]); + expect(getQuadPoints(dict, rect)).toEqual([ + [ + { x: 11, y: 11, }, { x: 12, y: 12, }, + { x: 13, y: 13, }, { x: 14, y: 14, }, + ], + [ + { x: 15, y: 15, }, { x: 16, y: 16, }, + { x: 17, y: 17, }, { x: 18, y: 18, }, + ], + ]); + }); + }); + describe('Annotation', function() { let dict, ref; @@ -1001,6 +1065,49 @@ describe('annotation', function() { done(); }, done.fail); }); + + it('should not set quadpoints if not defined', function(done) { + const annotationDict = new Dict(); + annotationDict.set('Type', Name.get('Annot')); + annotationDict.set('Subtype', Name.get('Link')); + + const annotationRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); + + AnnotationFactory.create(xref, annotationRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.LINK); + expect(data.quadPoints).toBeUndefined(); + done(); + }, done.fail); + }); + + it('should set quadpoints if defined', function(done) { + const annotationDict = new Dict(); + annotationDict.set('Type', Name.get('Annot')); + annotationDict.set('Subtype', Name.get('Link')); + annotationDict.set('Rect', [10, 10, 20, 20]); + annotationDict.set('QuadPoints', [11, 11, 12, 12, 13, 13, 14, 14]); + + const annotationRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); + + AnnotationFactory.create(xref, annotationRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.LINK); + expect(data.quadPoints).toEqual([ + [ + { x: 11, y: 11, }, { x: 12, y: 12, }, + { x: 13, y: 13, }, { x: 14, y: 14, }, + ], + ]); + done(); + }, done.fail); + }); }); describe('WidgetAnnotation', function() { @@ -1862,4 +1969,184 @@ describe('annotation', function() { }, done.fail); }); }); + + describe('HightlightAnnotation', function() { + it('should not set quadpoints if not defined', function(done) { + const highlightDict = new Dict(); + highlightDict.set('Type', Name.get('Annot')); + highlightDict.set('Subtype', Name.get('Highlight')); + + const highlightRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: highlightRef, data: highlightDict, } + ]); + + AnnotationFactory.create(xref, highlightRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.HIGHLIGHT); + expect(data.quadPoints).toBeUndefined(); + done(); + }, done.fail); + }); + + it('should set quadpoints if defined', function(done) { + const highlightDict = new Dict(); + highlightDict.set('Type', Name.get('Annot')); + highlightDict.set('Subtype', Name.get('Highlight')); + highlightDict.set('Rect', [10, 10, 20, 20]); + highlightDict.set('QuadPoints', [11, 11, 12, 12, 13, 13, 14, 14]); + + const highlightRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: highlightRef, data: highlightDict, } + ]); + + AnnotationFactory.create(xref, highlightRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.HIGHLIGHT); + expect(data.quadPoints).toEqual([ + [ + { x: 11, y: 11, }, { x: 12, y: 12, }, + { x: 13, y: 13, }, { x: 14, y: 14, }, + ], + ]); + done(); + }, done.fail); + }); + }); + + describe('UnderlineAnnotation', function() { + it('should not set quadpoints if not defined', function(done) { + const underlineDict = new Dict(); + underlineDict.set('Type', Name.get('Annot')); + underlineDict.set('Subtype', Name.get('Underline')); + + const underlineRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: underlineRef, data: underlineDict, } + ]); + + AnnotationFactory.create(xref, underlineRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.UNDERLINE); + expect(data.quadPoints).toBeUndefined(); + done(); + }, done.fail); + }); + + it('should set quadpoints if defined', function(done) { + const underlineDict = new Dict(); + underlineDict.set('Type', Name.get('Annot')); + underlineDict.set('Subtype', Name.get('Underline')); + underlineDict.set('Rect', [10, 10, 20, 20]); + underlineDict.set('QuadPoints', [11, 11, 12, 12, 13, 13, 14, 14]); + + const underlineRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: underlineRef, data: underlineDict, } + ]); + + AnnotationFactory.create(xref, underlineRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.UNDERLINE); + expect(data.quadPoints).toEqual([ + [ + { x: 11, y: 11, }, { x: 12, y: 12, }, + { x: 13, y: 13, }, { x: 14, y: 14, }, + ], + ]); + done(); + }, done.fail); + }); + }); + + describe('SquigglyAnnotation', function() { + it('should not set quadpoints if not defined', function(done) { + const squigglyDict = new Dict(); + squigglyDict.set('Type', Name.get('Annot')); + squigglyDict.set('Subtype', Name.get('Squiggly')); + + const squigglyRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: squigglyRef, data: squigglyDict, } + ]); + + AnnotationFactory.create(xref, squigglyRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.SQUIGGLY); + expect(data.quadPoints).toBeUndefined(); + done(); + }, done.fail); + }); + + it('should set quadpoints if defined', function(done) { + const squigglyDict = new Dict(); + squigglyDict.set('Type', Name.get('Annot')); + squigglyDict.set('Subtype', Name.get('Squiggly')); + squigglyDict.set('Rect', [10, 10, 20, 20]); + squigglyDict.set('QuadPoints', [11, 11, 12, 12, 13, 13, 14, 14]); + + const squigglyRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: squigglyRef, data: squigglyDict, } + ]); + + AnnotationFactory.create(xref, squigglyRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.SQUIGGLY); + expect(data.quadPoints).toEqual([ + [ + { x: 11, y: 11, }, { x: 12, y: 12, }, + { x: 13, y: 13, }, { x: 14, y: 14, }, + ], + ]); + done(); + }, done.fail); + }); + }); + + describe('StrikeOutAnnotation', function() { + it('should not set quadpoints if not defined', function(done) { + const strikeOutDict = new Dict(); + strikeOutDict.set('Type', Name.get('Annot')); + strikeOutDict.set('Subtype', Name.get('StrikeOut')); + + const strikeOutRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: strikeOutRef, data: strikeOutDict, } + ]); + + AnnotationFactory.create(xref, strikeOutRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.STRIKEOUT); + expect(data.quadPoints).toBeUndefined(); + done(); + }, done.fail); + }); + + it('should set quadpoints if defined', function(done) { + const strikeOutDict = new Dict(); + strikeOutDict.set('Type', Name.get('Annot')); + strikeOutDict.set('Subtype', Name.get('StrikeOut')); + strikeOutDict.set('Rect', [10, 10, 20, 20]); + strikeOutDict.set('QuadPoints', [11, 11, 12, 12, 13, 13, 14, 14]); + + const strikeOutRef = Ref.get(121, 0); + const xref = new XRefMock([ + { ref: strikeOutRef, data: strikeOutDict, } + ]); + + AnnotationFactory.create(xref, strikeOutRef, pdfManagerMock, + idFactoryMock).then(({ data, }) => { + expect(data.annotationType).toEqual(AnnotationType.STRIKEOUT); + expect(data.quadPoints).toEqual([ + [ + { x: 11, y: 11, }, { x: 12, y: 12, }, + { x: 13, y: 13, }, { x: 14, y: 14, }, + ], + ]); + done(); + }, done.fail); + }); + }); });