Skip to content

Commit

Permalink
Add support of Ink annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
leblanc-simon committed Oct 1, 2018
1 parent 25446db commit dbff4cf
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 1 deletion.
29 changes: 29 additions & 0 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ class AnnotationFactory {
case 'Polygon':
return new PolygonAnnotation(parameters);

case 'Ink':
return new InkAnnotation(parameters);

case 'Highlight':
return new HighlightAnnotation(parameters);

Expand Down Expand Up @@ -1013,6 +1016,32 @@ class PolygonAnnotation extends PolylineAnnotation {
}
}

class InkAnnotation extends Annotation {
constructor(parameters) {
super(parameters);

this.data.annotationType = AnnotationType.INK;

let dict = parameters.dict;
let rawInkLists = dict.getArray('InkList');
this.data.inkLists = [];
for (let i = 0, ii = rawInkLists.length; i < ii; ++i) {
// The raw ink lists array contains arrays 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.
this.data.inkLists.push([]);
for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) {
this.data.inkLists[i].push({
x: rawInkLists[i][j],
y: rawInkLists[i][j + 1],
});
}
}
this._preparePopup(dict);
}
}

class HighlightAnnotation extends Annotation {
constructor(parameters) {
super(parameters);
Expand Down
79 changes: 78 additions & 1 deletion src/display/annotation_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class AnnotationElementFactory {
case AnnotationType.POLYLINE:
return new PolylineAnnotationElement(parameters);

case AnnotationType.INK:
return new InkAnnotationElement(parameters);

case AnnotationType.POLYGON:
return new PolygonAnnotationElement(parameters);

Expand Down Expand Up @@ -628,7 +631,14 @@ 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', 'PolyLine', 'Polygon'];
const IGNORE_TYPES = [
'Line',
'Square',
'Circle',
'PolyLine',
'Polygon',
'Ink',
];

this.container.className = 'popupAnnotation';

Expand Down Expand Up @@ -1006,6 +1016,73 @@ class PolygonAnnotationElement extends PolylineAnnotationElement {
}
}

class InkAnnotationElement extends AnnotationElement {
constructor(parameters) {
let isRenderable = !!(parameters.data.hasPopup ||
parameters.data.title || parameters.data.contents);
super(parameters, isRenderable, /* ignoreBorder = */ true);

this.containerClassName = 'inkAnnotation';

// Use the polyline SVG element since it allows us to use coordinates
// directly and to draw both straight lines and curves.
this.svgElementName = 'svg:polyline';
}

/**
* Render the ink annotation's HTML element in the empty container.
*
* @public
* @memberof InkAnnotationElement
* @returns {HTMLSectionElement}
*/
render() {
this.container.className = this.containerClassName;

// Create an invisible polyline with the same points that acts as the
// trigger for the popup.
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);

let inkLists = data.inkLists;
for (let i = 0, ii = inkLists.length; i < ii; i++) {
let inkList = inkLists[i];
let points = [];

// Convert the ink list 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.
for (let j = 0, jj = inkList.length; j < jj; j++) {
let x = inkList[j].x - data.rect[0];
let y = data.rect[3] - inkList[j].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');

// 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);

svg.appendChild(polyline);
}

this.container.append(svg);

return this.container;
}
}

class HighlightAnnotationElement extends AnnotationElement {
constructor(parameters) {
let isRenderable = !!(parameters.data.hasPopup ||
Expand Down
1 change: 1 addition & 0 deletions test/pdfs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
!text_clip_cff_cid.pdf
!issue4801.pdf
!issue5334.pdf
!annotation-caret-ink.pdf
!bug1186827.pdf
!issue215.pdf
!issue5044.pdf
Expand Down
Binary file added test/pdfs/annotation-caret-ink.pdf
Binary file not shown.
7 changes: 7 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1776,6 +1776,13 @@
"lastPage": 1,
"type": "load"
},
{ "id": "annotation-caret-ink",
"file": "pdfs/annotation-caret-ink.pdf",
"md5": "6218ca235580d1975474c979e0128c2d",
"rounds": 1,
"type": "eq",
"annotations": true
},
{ "id": "bug1130815-eq",
"file": "pdfs/bug1130815.pdf",
"md5": "3ff3b550c3af766991b2a1b11d00de85",
Expand Down
55 changes: 55 additions & 0 deletions test/unit/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1435,4 +1435,59 @@ describe('annotation', function() {
}, done.fail);
});
});

describe('InkAnnotation', function() {
it('should handle a single ink list', function(done) {
const inkDict = new Dict();
inkDict.set('Type', Name.get('Annot'));
inkDict.set('Subtype', Name.get('Ink'));
inkDict.set('InkList', [[1, 1, 1, 2, 2, 2, 3, 3]]);

const inkRef = new Ref(142, 0);
const xref = new XRefMock([
{ ref: inkRef, data: inkDict, }
]);

AnnotationFactory.create(xref, inkRef, pdfManagerMock,
idFactoryMock).then(({ data, }) => {
expect(data.annotationType).toEqual(AnnotationType.INK);
expect(data.inkLists.length).toEqual(1);
expect(data.inkLists[0]).toEqual([
{ x: 1, y: 1, },
{ x: 1, y: 2, },
{ x: 2, y: 2, },
{ x: 3, y: 3, },
]);
done();
}, done.fail);
});

it('should handle multiple ink lists', function(done) {
const inkDict = new Dict();
inkDict.set('Type', Name.get('Annot'));
inkDict.set('Subtype', Name.get('Ink'));
inkDict.set('InkList', [
[1, 1, 1, 2],
[3, 3, 4, 5],
]);

const inkRef = new Ref(143, 0);
const xref = new XRefMock([
{ ref: inkRef, data: inkDict, }
]);

AnnotationFactory.create(xref, inkRef, pdfManagerMock,
idFactoryMock).then(({ data, }) => {
expect(data.annotationType).toEqual(AnnotationType.INK);
expect(data.inkLists.length).toEqual(2);
expect(data.inkLists[0]).toEqual([
{ x: 1, y: 1, }, { x: 1, y: 2, }
]);
expect(data.inkLists[1]).toEqual([
{ x: 3, y: 3, }, { x: 4, y: 5, }
]);
done();
}, done.fail);
});
});
});
1 change: 1 addition & 0 deletions web/annotation_layer_builder.css
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
.annotationLayer .circleAnnotation svg ellipse,
.annotationLayer .polylineAnnotation svg polyline,
.annotationLayer .polygonAnnotation svg polygon,
.annotationLayer .inkAnnotation svg polyline,
.annotationLayer .stampAnnotation,
.annotationLayer .fileAttachmentAnnotation {
cursor: pointer;
Expand Down

0 comments on commit dbff4cf

Please sign in to comment.