diff --git a/src/core/annotation.js b/src/core/annotation.js index d3aa434ea..d9119a951 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -87,6 +87,12 @@ class AnnotationFactory { case 'Circle': return new CircleAnnotation(parameters); + case 'PolyLine': + return new PolylineAnnotation(parameters); + + case 'Polygon': + return new PolygonAnnotation(parameters); + case 'Highlight': return new HighlightAnnotation(parameters); @@ -913,6 +919,39 @@ class CircleAnnotation extends Annotation { } } +class PolylineAnnotation extends Annotation { + constructor(parameters) { + super(parameters); + + this.data.annotationType = AnnotationType.POLYLINE; + + // The vertices array is an array of numbers representing the alternating + // horizontal and vertical coordinates, respectively, of each vertex. + // Convert this to an array of objects with x and y coordinates. + let dict = parameters.dict; + let rawVertices = dict.getArray('Vertices'); + + this.data.vertices = []; + for (let i = 0, ii = rawVertices.length; i < ii; i += 2) { + this.data.vertices.push({ + x: rawVertices[i], + y: rawVertices[i + 1], + }); + } + + this._preparePopup(dict); + } +} + +class PolygonAnnotation extends PolylineAnnotation { + constructor(parameters) { + // Polygons are specific forms of polylines, so reuse their logic. + super(parameters); + + this.data.annotationType = AnnotationType.POLYGON; + } +} + class HighlightAnnotation extends Annotation { constructor(parameters) { super(parameters); diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index de9048da5..41272e93c 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -80,6 +80,12 @@ class AnnotationElementFactory { case AnnotationType.CIRCLE: return new CircleAnnotationElement(parameters); + case AnnotationType.POLYLINE: + return new PolylineAnnotationElement(parameters); + + case AnnotationType.POLYGON: + return new PolygonAnnotationElement(parameters); + case AnnotationType.HIGHLIGHT: return new HighlightAnnotationElement(parameters); @@ -601,7 +607,7 @@ class PopupAnnotationElement extends AnnotationElement { render() { // Do not render popup annotations for parent elements with these types as // they create the popups themselves (because of custom trigger divs). - const IGNORE_TYPES = ['Line', 'Square', 'Circle']; + const IGNORE_TYPES = ['Line', 'Square', 'Circle', 'PolyLine', 'Polygon']; this.container.className = 'popupAnnotation'; @@ -911,6 +917,75 @@ class CircleAnnotationElement extends AnnotationElement { } } +class PolylineAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || + parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, /* ignoreBorder = */ true); + + this.containerClassName = 'polylineAnnotation'; + this.svgElementName = 'svg:polyline'; + } + + /** + * Render the polyline annotation's HTML element in the empty container. + * + * @public + * @memberof PolylineAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = this.containerClassName; + + // Create an invisible polyline with the same points that acts as the + // trigger for the popup. Only the polyline itself should trigger the + // popup, not the entire container. + let data = this.data; + let width = data.rect[2] - data.rect[0]; + let height = data.rect[3] - data.rect[1]; + let svg = this.svgFactory.create(width, height); + + // Convert the vertices array to a single points string that the SVG + // polyline element expects ("x1,y1 x2,y2 ..."). PDF coordinates are + // calculated from a bottom left origin, so transform the polyline + // coordinates to a top left origin for the SVG element. + let vertices = data.vertices; + let points = []; + for (let i = 0, ii = vertices.length; i < ii; i++) { + let x = vertices[i].x - data.rect[0]; + let y = data.rect[3] - vertices[i].y; + points.push(x + ',' + y); + } + points = points.join(' '); + + let borderWidth = data.borderStyle.width; + let polyline = this.svgFactory.createElement(this.svgElementName); + polyline.setAttribute('points', points); + polyline.setAttribute('stroke-width', borderWidth); + polyline.setAttribute('stroke', 'transparent'); + polyline.setAttribute('fill', 'none'); + + svg.appendChild(polyline); + this.container.append(svg); + + // Create the popup ourselves so that we can bind it to the polyline + // instead of to the entire container (which is the default). + this._createPopup(this.container, polyline, data); + + return this.container; + } +} + +class PolygonAnnotationElement extends PolylineAnnotationElement { + constructor(parameters) { + // Polygons are specific forms of polylines, so reuse their logic. + super(parameters); + + this.containerClassName = 'polygonAnnotation'; + this.svgElementName = 'svg:polygon'; + } +} + class HighlightAnnotationElement extends AnnotationElement { constructor(parameters) { let isRenderable = !!(parameters.data.hasPopup || diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 7f818c418..feb4db9b5 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -292,6 +292,7 @@ !annotation-text-widget.pdf !annotation-choice-widget.pdf !annotation-button-widget.pdf +!annotation-polyline-polygon.pdf !zero_descent.pdf !operator-in-TJ-array.pdf !issue7878.pdf diff --git a/test/pdfs/annotation-polyline-polygon.pdf b/test/pdfs/annotation-polyline-polygon.pdf new file mode 100755 index 000000000..ec5d05872 Binary files /dev/null and b/test/pdfs/annotation-polyline-polygon.pdf differ diff --git a/test/test_manifest.json b/test/test_manifest.json index a402a3a50..c724c35d7 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -3660,6 +3660,13 @@ "type": "eq", "forms": true }, + { "id": "annotation-polyline-polygon", + "file": "pdfs/annotation-polyline-polygon.pdf", + "md5": "e68611602f58c8ca70cc40575ba3b04e", + "rounds": 1, + "type": "eq", + "annotations": true + }, { "id": "issue6108", "file": "pdfs/issue6108.pdf", "md5": "8961cb55149495989a80bf0487e0f076", diff --git a/web/annotation_layer_builder.css b/web/annotation_layer_builder.css index a12048ce6..6154d748b 100644 --- a/web/annotation_layer_builder.css +++ b/web/annotation_layer_builder.css @@ -191,6 +191,8 @@ .annotationLayer .lineAnnotation svg line, .annotationLayer .squareAnnotation svg rect, .annotationLayer .circleAnnotation svg ellipse, +.annotationLayer .polylineAnnotation svg polyline, +.annotationLayer .polygonAnnotation svg polygon, .annotationLayer .stampAnnotation, .annotationLayer .fileAttachmentAnnotation { cursor: pointer;