Annotations: implement support for line annotations
This patch implements support for line annotations. Other viewers only show the popup annotation when hovering over the line, which may have any orientation. To make this possible, we render an invisible line (SVG element) over the line on the canvas that acts as the trigger for the popup annotation. This invisible line has the same starting coordinates, ending coordinates and width of the line on the canvas.
This commit is contained in:
parent
30d63b0c50
commit
e15a2ec523
@ -115,6 +115,9 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
|
||||
case 'Popup':
|
||||
return new PopupAnnotation(parameters);
|
||||
|
||||
case 'Line':
|
||||
return new LineAnnotation(parameters);
|
||||
|
||||
case 'Highlight':
|
||||
return new HighlightAnnotation(parameters);
|
||||
|
||||
@ -955,6 +958,8 @@ var PopupAnnotation = (function PopupAnnotationClosure() {
|
||||
return;
|
||||
}
|
||||
|
||||
var parentSubtype = parentItem.get('Subtype');
|
||||
this.data.parentType = isName(parentSubtype) ? parentSubtype.name : null;
|
||||
this.data.parentId = dict.getRaw('Parent').toString();
|
||||
this.data.title = stringToPDFString(parentItem.get('T') || '');
|
||||
this.data.contents = stringToPDFString(parentItem.get('Contents') || '');
|
||||
@ -983,6 +988,22 @@ var PopupAnnotation = (function PopupAnnotationClosure() {
|
||||
return PopupAnnotation;
|
||||
})();
|
||||
|
||||
var LineAnnotation = (function LineAnnotationClosure() {
|
||||
function LineAnnotation(parameters) {
|
||||
Annotation.call(this, parameters);
|
||||
|
||||
this.data.annotationType = AnnotationType.LINE;
|
||||
|
||||
var dict = parameters.dict;
|
||||
this.data.lineCoordinates = Util.normalizeRect(dict.getArray('L'));
|
||||
this._preparePopup(dict);
|
||||
}
|
||||
|
||||
Util.inherit(LineAnnotation, Annotation, {});
|
||||
|
||||
return LineAnnotation;
|
||||
})();
|
||||
|
||||
var HighlightAnnotation = (function HighlightAnnotationClosure() {
|
||||
function HighlightAnnotation(parameters) {
|
||||
Annotation.call(this, parameters);
|
||||
|
@ -93,6 +93,9 @@ AnnotationElementFactory.prototype =
|
||||
case AnnotationType.POPUP:
|
||||
return new PopupAnnotationElement(parameters);
|
||||
|
||||
case AnnotationType.LINE:
|
||||
return new LineAnnotationElement(parameters);
|
||||
|
||||
case AnnotationType.HIGHLIGHT:
|
||||
return new HighlightAnnotationElement(parameters);
|
||||
|
||||
@ -681,6 +684,10 @@ var ChoiceWidgetAnnotationElement = (
|
||||
* @alias PopupAnnotationElement
|
||||
*/
|
||||
var PopupAnnotationElement = (function PopupAnnotationElementClosure() {
|
||||
// Do not render popup annotations for parent elements with these types as
|
||||
// they create the popups themselves (because of custom trigger divs).
|
||||
var IGNORE_TYPES = ['Line'];
|
||||
|
||||
function PopupAnnotationElement(parameters) {
|
||||
var isRenderable = !!(parameters.data.title || parameters.data.contents);
|
||||
AnnotationElement.call(this, parameters, isRenderable);
|
||||
@ -697,6 +704,10 @@ var PopupAnnotationElement = (function PopupAnnotationElementClosure() {
|
||||
render: function PopupAnnotationElement_render() {
|
||||
this.container.className = 'popupAnnotation';
|
||||
|
||||
if (IGNORE_TYPES.indexOf(this.data.parentType) >= 0) {
|
||||
return this.container;
|
||||
}
|
||||
|
||||
var selector = '[data-annotation-id="' + this.data.parentId + '"]';
|
||||
var parentElement = this.layer.querySelector(selector);
|
||||
if (!parentElement) {
|
||||
@ -866,6 +877,69 @@ var PopupElement = (function PopupElementClosure() {
|
||||
return PopupElement;
|
||||
})();
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @alias LineAnnotationElement
|
||||
*/
|
||||
var LineAnnotationElement = (function LineAnnotationElementClosure() {
|
||||
var SVG_NS = 'http://www.w3.org/2000/svg';
|
||||
|
||||
function LineAnnotationElement(parameters) {
|
||||
var isRenderable = !!(parameters.data.hasPopup ||
|
||||
parameters.data.title || parameters.data.contents);
|
||||
AnnotationElement.call(this, parameters, isRenderable,
|
||||
/* ignoreBorder = */ true);
|
||||
}
|
||||
|
||||
Util.inherit(LineAnnotationElement, AnnotationElement, {
|
||||
/**
|
||||
* Render the line annotation's HTML element in the empty container.
|
||||
*
|
||||
* @public
|
||||
* @memberof LineAnnotationElement
|
||||
* @returns {HTMLSectionElement}
|
||||
*/
|
||||
render: function LineAnnotationElement_render() {
|
||||
this.container.className = 'lineAnnotation';
|
||||
|
||||
// Create an invisible line with the same starting and ending coordinates
|
||||
// that acts as the trigger for the popup. Only the line itself should
|
||||
// trigger the popup, not the entire container.
|
||||
var data = this.data;
|
||||
var width = data.rect[2] - data.rect[0];
|
||||
var height = data.rect[3] - data.rect[1];
|
||||
|
||||
var svg = document.createElementNS(SVG_NS, 'svg:svg');
|
||||
svg.setAttributeNS(null, 'version', '1.1');
|
||||
svg.setAttributeNS(null, 'width', width + 'px');
|
||||
svg.setAttributeNS(null, 'height', height + 'px');
|
||||
svg.setAttributeNS(null, 'preserveAspectRatio', 'none');
|
||||
svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height);
|
||||
|
||||
// PDF coordinates are calculated from a bottom left origin, so transform
|
||||
// the line coordinates to a top left origin for the SVG element.
|
||||
var line = document.createElementNS(SVG_NS, 'svg:line');
|
||||
line.setAttributeNS(null, 'x1', data.rect[2] - data.lineCoordinates[0]);
|
||||
line.setAttributeNS(null, 'y1', data.rect[3] - data.lineCoordinates[1]);
|
||||
line.setAttributeNS(null, 'x2', data.rect[2] - data.lineCoordinates[2]);
|
||||
line.setAttributeNS(null, 'y2', data.rect[3] - data.lineCoordinates[3]);
|
||||
line.setAttributeNS(null, 'stroke-width', data.borderStyle.width);
|
||||
line.setAttributeNS(null, 'stroke', 'transparent');
|
||||
|
||||
svg.appendChild(line);
|
||||
this.container.append(svg);
|
||||
|
||||
// Create the popup ourselves so that we can bind it to the line instead
|
||||
// of to the entire container (which is the default).
|
||||
this._createPopup(this.container, line, this.data);
|
||||
|
||||
return this.container;
|
||||
}
|
||||
});
|
||||
|
||||
return LineAnnotationElement;
|
||||
})();
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @alias HighlightAnnotationElement
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -268,6 +268,7 @@
|
||||
!annotation-strikeout.pdf
|
||||
!annotation-squiggly.pdf
|
||||
!annotation-highlight.pdf
|
||||
!annotation-line.pdf
|
||||
!annotation-fileattachment.pdf
|
||||
!annotation-text-widget.pdf
|
||||
!annotation-choice-widget.pdf
|
||||
|
BIN
test/pdfs/annotation-line.pdf
Executable file
BIN
test/pdfs/annotation-line.pdf
Executable file
Binary file not shown.
@ -3357,6 +3357,13 @@
|
||||
"type": "eq",
|
||||
"annotations": true
|
||||
},
|
||||
{ "id": "annotation-line",
|
||||
"file": "pdfs/annotation-line.pdf",
|
||||
"md5": "fde60608be2748f10fb6522cba425ca1",
|
||||
"rounds": 1,
|
||||
"type": "eq",
|
||||
"annotations": true
|
||||
},
|
||||
{ "id": "annotation-fileattachment",
|
||||
"file": "pdfs/annotation-fileattachment.pdf",
|
||||
"md5": "d20ecee4b53c81b2dd44c8715a1b4a83",
|
||||
|
@ -1287,6 +1287,27 @@ describe('annotation', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('LineAnnotation', function() {
|
||||
it('should set the line coordinates', function() {
|
||||
var lineDict = new Dict();
|
||||
lineDict.set('Type', Name.get('Annot'));
|
||||
lineDict.set('Subtype', Name.get('Line'));
|
||||
lineDict.set('L', [1, 2, 3, 4]);
|
||||
|
||||
var lineRef = new Ref(122, 0);
|
||||
var xref = new XRefMock([
|
||||
{ ref: lineRef, data: lineDict, }
|
||||
]);
|
||||
|
||||
var annotation = annotationFactory.create(xref, lineRef, pdfManagerMock,
|
||||
idFactoryMock);
|
||||
var data = annotation.data;
|
||||
expect(data.annotationType).toEqual(AnnotationType.LINE);
|
||||
|
||||
expect(data.lineCoordinates).toEqual([1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('FileAttachmentAnnotation', function() {
|
||||
it('should correctly parse a file attachment', function() {
|
||||
var fileStream = new StringStream(
|
||||
|
@ -143,6 +143,7 @@
|
||||
.annotationLayer .underlineAnnotation,
|
||||
.annotationLayer .squigglyAnnotation,
|
||||
.annotationLayer .strikeoutAnnotation,
|
||||
.annotationLayer .lineAnnotation svg line,
|
||||
.annotationLayer .fileAttachmentAnnotation {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user