mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-26 01:58:06 +02:00
Improved annotations' display/behavior.
Added an "InteractiveAnnotation" class to homogenize the annotations' structure (highlighting) and user interactions (for now, used for text and link annotations). Text annotations: The appearance (AP) has priority over the icon (Name). The popup extends horizontally (up to a limit) as well as vertically. Reduced the title's font size. The annotation's color (C) is used to color the popup's background. On top of the mouseover show/hide behavior, a click on the icon will lock the annotation open (for mobile purposes). It can be closed with another click on either the icon or the popup. An annotation printing is conditioned by its "print" bit Unsupported annotations are not displayed at all.
This commit is contained in:
parent
3e931048f2
commit
076b3433b4
8 changed files with 305 additions and 131 deletions
|
@ -21,6 +21,9 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var HIGHLIGHT_OFFSET = 4; // px
|
||||
var SUPPORTED_TYPES = ['Link', 'Text', 'Widget'];
|
||||
|
||||
var Annotation = (function AnnotationClosure() {
|
||||
// 12.5.5: Algorithm: Appearance streams
|
||||
function getTransformMatrix(rect, bbox, matrix) {
|
||||
|
@ -143,25 +146,50 @@ var Annotation = (function AnnotationClosure() {
|
|||
},
|
||||
|
||||
// TODO(mack): Remove this, it's not really that helpful.
|
||||
getEmptyContainer: function Annotation_getEmptyContainer(tagName, rect) {
|
||||
getEmptyContainer: function Annotation_getEmptyContainer(tagName, rect,
|
||||
borderWidth) {
|
||||
assert(!isWorker,
|
||||
'getEmptyContainer() should be called from main thread');
|
||||
|
||||
var bWidth = borderWidth || 0;
|
||||
|
||||
rect = rect || this.data.rect;
|
||||
var element = document.createElement(tagName);
|
||||
element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
|
||||
element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
|
||||
element.style.borderWidth = bWidth + 'px';
|
||||
var width = rect[2] - rect[0] - 2 * bWidth;
|
||||
var height = rect[3] - rect[1] - 2 * bWidth;
|
||||
element.style.width = width + 'px';
|
||||
element.style.height = height + 'px';
|
||||
return element;
|
||||
},
|
||||
|
||||
isInvisible: function Annotation_isInvisible() {
|
||||
var data = this.data;
|
||||
if (data && SUPPORTED_TYPES.indexOf(data.subtype) !== -1) {
|
||||
return false;
|
||||
} else {
|
||||
return !!(data &&
|
||||
data.annotationFlags && // Default: not invisible
|
||||
data.annotationFlags & 0x1); // Invisible
|
||||
}
|
||||
},
|
||||
|
||||
isViewable: function Annotation_isViewable() {
|
||||
var data = this.data;
|
||||
return !!(
|
||||
data &&
|
||||
(!data.annotationFlags ||
|
||||
!(data.annotationFlags & 0x22)) && // Hidden or NoView
|
||||
data.rect // rectangle is nessessary
|
||||
);
|
||||
return !!(!this.isInvisible() &&
|
||||
data &&
|
||||
(!data.annotationFlags ||
|
||||
!(data.annotationFlags & 0x22)) && // Hidden or NoView
|
||||
data.rect); // rectangle is nessessary
|
||||
},
|
||||
|
||||
isPrintable: function Annotation_isPrintable() {
|
||||
var data = this.data;
|
||||
return !!(!this.isInvisible() &&
|
||||
data &&
|
||||
data.annotationFlags && // Default: not printable
|
||||
data.annotationFlags & 0x4 && // Print
|
||||
data.rect); // rectangle is nessessary
|
||||
},
|
||||
|
||||
loadResources: function(keys) {
|
||||
|
@ -182,7 +210,7 @@ var Annotation = (function AnnotationClosure() {
|
|||
return promise;
|
||||
},
|
||||
|
||||
getOperatorList: function Annotation_getToOperatorList(evaluator) {
|
||||
getOperatorList: function Annotation_getOperatorList(evaluator) {
|
||||
|
||||
var promise = new LegacyPromise();
|
||||
|
||||
|
@ -289,7 +317,7 @@ var Annotation = (function AnnotationClosure() {
|
|||
|
||||
var annotation = new Constructor(params);
|
||||
|
||||
if (annotation.isViewable()) {
|
||||
if (annotation.isViewable() || annotation.isPrintable()) {
|
||||
return annotation;
|
||||
} else {
|
||||
warn('unimplemented annotation type: ' + subtype);
|
||||
|
@ -297,7 +325,7 @@ var Annotation = (function AnnotationClosure() {
|
|||
};
|
||||
|
||||
Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
|
||||
annotations, opList, pdfManager, partialEvaluator) {
|
||||
annotations, opList, pdfManager, partialEvaluator, intent) {
|
||||
|
||||
function reject(e) {
|
||||
annotationsReadyPromise.reject(e);
|
||||
|
@ -307,7 +335,11 @@ var Annotation = (function AnnotationClosure() {
|
|||
|
||||
var annotationPromises = [];
|
||||
for (var i = 0, n = annotations.length; i < n; ++i) {
|
||||
annotationPromises.push(annotations[i].getOperatorList(partialEvaluator));
|
||||
if (intent === 'display' && annotations[i].isViewable() ||
|
||||
intent === 'print' && annotations[i].isPrintable()) {
|
||||
annotationPromises.push(
|
||||
annotations[i].getOperatorList(partialEvaluator));
|
||||
}
|
||||
}
|
||||
Promise.all(annotationPromises).then(function(datas) {
|
||||
opList.addOp(OPS.beginAnnotations, []);
|
||||
|
@ -519,9 +551,64 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
|
|||
return TextWidgetAnnotation;
|
||||
})();
|
||||
|
||||
var InteractiveAnnotation = (function InteractiveAnnotationClosure() {
|
||||
function InteractiveAnnotation(params) {
|
||||
Annotation.call(this, params);
|
||||
}
|
||||
|
||||
Util.inherit(InteractiveAnnotation, Annotation, {
|
||||
hasHtml: function InteractiveAnnotation_hasHtml() {
|
||||
return true;
|
||||
},
|
||||
|
||||
highlight: function InteractiveAnnotation_highlight() {
|
||||
if (this.highlightElement &&
|
||||
this.highlightElement.hasAttribute('hidden')) {
|
||||
this.highlightElement.removeAttribute('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
unhighlight: function InteractiveAnnotation_unhighlight() {
|
||||
if (this.highlightElement &&
|
||||
!this.highlightElement.hasAttribute('hidden')) {
|
||||
this.highlightElement.setAttribute('hidden', true);
|
||||
}
|
||||
},
|
||||
|
||||
initContainer: function InteractiveAnnotation_initContainer() {
|
||||
|
||||
var item = this.data;
|
||||
var rect = item.rect;
|
||||
|
||||
var container = this.getEmptyContainer('section', rect, item.borderWidth);
|
||||
container.style.backgroundColor = item.color;
|
||||
|
||||
var color = item.color;
|
||||
var rgb = [];
|
||||
for (var i = 0; i < 3; ++i) {
|
||||
rgb[i] = Math.round(color[i] * 255);
|
||||
}
|
||||
item.colorCssRgb = Util.makeCssRgb(rgb);
|
||||
|
||||
var highlight = document.createElement('div');
|
||||
highlight.className = 'annotationHighlight';
|
||||
highlight.style.left = highlight.style.top = -HIGHLIGHT_OFFSET + 'px';
|
||||
highlight.style.right = highlight.style.bottom = -HIGHLIGHT_OFFSET + 'px';
|
||||
highlight.setAttribute('hidden', true);
|
||||
|
||||
this.highlightElement = highlight;
|
||||
container.appendChild(this.highlightElement);
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
|
||||
return InteractiveAnnotation;
|
||||
})();
|
||||
|
||||
var TextAnnotation = (function TextAnnotationClosure() {
|
||||
function TextAnnotation(params) {
|
||||
Annotation.call(this, params);
|
||||
InteractiveAnnotation.call(this, params);
|
||||
|
||||
if (params.data) {
|
||||
return;
|
||||
|
@ -534,22 +621,21 @@ var TextAnnotation = (function TextAnnotationClosure() {
|
|||
var title = dict.get('T');
|
||||
data.content = stringToPDFString(content || '');
|
||||
data.title = stringToPDFString(title || '');
|
||||
data.name = !dict.has('Name') ? 'Note' : dict.get('Name').name;
|
||||
|
||||
if (data.hasAppearance) {
|
||||
data.name = 'NoIcon';
|
||||
} else {
|
||||
data.name = dict.has('Name') ? dict.get('Name').name : 'Note';
|
||||
}
|
||||
|
||||
if (dict.has('C')) {
|
||||
data.hasBgColor = true;
|
||||
}
|
||||
}
|
||||
|
||||
var ANNOT_MIN_SIZE = 10;
|
||||
|
||||
Util.inherit(TextAnnotation, Annotation, {
|
||||
|
||||
getOperatorList: function TextAnnotation_getOperatorList(evaluator) {
|
||||
var promise = new LegacyPromise();
|
||||
promise.resolve(new OperatorList());
|
||||
return promise;
|
||||
},
|
||||
|
||||
hasHtml: function TextAnnotation_hasHtml() {
|
||||
return true;
|
||||
},
|
||||
Util.inherit(TextAnnotation, InteractiveAnnotation, {
|
||||
|
||||
getHtmlElement: function TextAnnotation_getHtmlElement(commonObjs) {
|
||||
assert(!isWorker, 'getHtmlElement() shall be called from main thread');
|
||||
|
@ -565,23 +651,40 @@ var TextAnnotation = (function TextAnnotationClosure() {
|
|||
rect[2] = rect[0] + (rect[3] - rect[1]); // make it square
|
||||
}
|
||||
|
||||
var container = this.getEmptyContainer('section', rect);
|
||||
var container = this.initContainer();
|
||||
container.className = 'annotText';
|
||||
|
||||
var image = document.createElement('img');
|
||||
var image = document.createElement('img');
|
||||
image.style.height = container.style.height;
|
||||
image.style.width = container.style.width;
|
||||
var iconName = item.name;
|
||||
image.src = PDFJS.imageResourcesPath + 'annotation-' +
|
||||
iconName.toLowerCase() + '.svg';
|
||||
image.alt = '[{{type}} Annotation]';
|
||||
image.dataset.l10nId = 'text_annotation_type';
|
||||
image.dataset.l10nArgs = JSON.stringify({type: iconName});
|
||||
|
||||
var contentWrapper = document.createElement('div');
|
||||
contentWrapper.className = 'annotTextContentWrapper';
|
||||
contentWrapper.style.left = Math.floor(rect[2] - rect[0] + 5) + 'px';
|
||||
contentWrapper.style.top = '-10px';
|
||||
|
||||
var content = document.createElement('div');
|
||||
content.className = 'annotTextContent';
|
||||
content.setAttribute('hidden', true);
|
||||
if (item.hasBgColor) {
|
||||
var color = item.color;
|
||||
var rgb = [];
|
||||
for (var i = 0; i < 3; ++i) {
|
||||
// Enlighten the color (70%)
|
||||
var c = Math.round(color[i] * 255);
|
||||
rgb[i] = Math.round((255 - c) * 0.7) + c;
|
||||
}
|
||||
content.style.backgroundColor = Util.makeCssRgb(rgb);
|
||||
}
|
||||
|
||||
var title = document.createElement('h1');
|
||||
var text = document.createElement('p');
|
||||
content.style.left = Math.floor(rect[2] - rect[0]) + 'px';
|
||||
content.style.top = '0px';
|
||||
title.textContent = item.title;
|
||||
|
||||
if (!item.content && !item.title) {
|
||||
|
@ -597,28 +700,57 @@ var TextAnnotation = (function TextAnnotationClosure() {
|
|||
}
|
||||
text.appendChild(e);
|
||||
|
||||
var showAnnotation = function showAnnotation() {
|
||||
container.style.zIndex += 1;
|
||||
content.removeAttribute('hidden');
|
||||
var pinned = false;
|
||||
|
||||
var showAnnotation = function showAnnotation(pin) {
|
||||
if (pin) {
|
||||
pinned = true;
|
||||
}
|
||||
if (content.hasAttribute('hidden')) {
|
||||
container.style.zIndex += 1;
|
||||
content.removeAttribute('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
var hideAnnotation = function hideAnnotation(e) {
|
||||
if (e.toElement || e.relatedTarget) { // No context menu is used
|
||||
var hideAnnotation = function hideAnnotation(unpin) {
|
||||
if (unpin) {
|
||||
pinned = false;
|
||||
}
|
||||
if (!content.hasAttribute('hidden') && !pinned) {
|
||||
container.style.zIndex -= 1;
|
||||
content.setAttribute('hidden', true);
|
||||
}
|
||||
};
|
||||
|
||||
content.addEventListener('mouseover', showAnnotation, false);
|
||||
content.addEventListener('mouseout', hideAnnotation, false);
|
||||
image.addEventListener('mouseover', showAnnotation, false);
|
||||
image.addEventListener('mouseout', hideAnnotation, false);
|
||||
var toggleAnnotation = function toggleAnnotation() {
|
||||
if (pinned) {
|
||||
hideAnnotation(true);
|
||||
} else {
|
||||
showAnnotation(true);
|
||||
}
|
||||
};
|
||||
|
||||
var self = this;
|
||||
image.addEventListener('click', function image_clickHandler() {
|
||||
toggleAnnotation();
|
||||
}, false);
|
||||
image.addEventListener('mouseover', function image_mouseOverHandler() {
|
||||
showAnnotation();
|
||||
}, false);
|
||||
image.addEventListener('mouseout', function image_mouseOutHandler() {
|
||||
hideAnnotation();
|
||||
}, false);
|
||||
|
||||
content.addEventListener('click', function content_clickHandler() {
|
||||
hideAnnotation(true);
|
||||
}, false);
|
||||
}
|
||||
|
||||
content.appendChild(title);
|
||||
content.appendChild(text);
|
||||
contentWrapper.appendChild(content);
|
||||
container.appendChild(image);
|
||||
container.appendChild(content);
|
||||
container.appendChild(contentWrapper);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
@ -629,7 +761,7 @@ var TextAnnotation = (function TextAnnotationClosure() {
|
|||
|
||||
var LinkAnnotation = (function LinkAnnotationClosure() {
|
||||
function LinkAnnotation(params) {
|
||||
Annotation.call(this, params);
|
||||
InteractiveAnnotation.call(this, params);
|
||||
|
||||
if (params.data) {
|
||||
return;
|
||||
|
@ -692,36 +824,28 @@ var LinkAnnotation = (function LinkAnnotationClosure() {
|
|||
return url;
|
||||
}
|
||||
|
||||
Util.inherit(LinkAnnotation, Annotation, {
|
||||
Util.inherit(LinkAnnotation, InteractiveAnnotation, {
|
||||
hasOperatorList: function LinkAnnotation_hasOperatorList() {
|
||||
return false;
|
||||
},
|
||||
|
||||
hasHtml: function LinkAnnotation_hasHtml() {
|
||||
return true;
|
||||
},
|
||||
|
||||
getHtmlElement: function LinkAnnotation_getHtmlElement(commonObjs) {
|
||||
var rect = this.data.rect;
|
||||
var element = document.createElement('a');
|
||||
var borderWidth = this.data.borderWidth;
|
||||
|
||||
element.style.borderWidth = borderWidth + 'px';
|
||||
var color = this.data.color;
|
||||
var rgb = [];
|
||||
for (var i = 0; i < 3; ++i) {
|
||||
rgb[i] = Math.round(color[i] * 255);
|
||||
}
|
||||
element.style.borderColor = Util.makeCssRgb(rgb);
|
||||
element.style.borderStyle = 'solid';
|
||||
var container = this.initContainer();
|
||||
container.className = 'annotLink';
|
||||
|
||||
var width = rect[2] - rect[0] - 2 * borderWidth;
|
||||
var height = rect[3] - rect[1] - 2 * borderWidth;
|
||||
element.style.width = width + 'px';
|
||||
element.style.height = height + 'px';
|
||||
var item = this.data;
|
||||
var rect = item.rect;
|
||||
|
||||
element.href = this.data.url || '';
|
||||
return element;
|
||||
container.style.borderColor = item.colorCssRgb;
|
||||
container.style.borderStyle = 'solid';
|
||||
|
||||
var link = document.createElement('a');
|
||||
link.href = link.title = this.data.url || '';
|
||||
|
||||
container.appendChild(link);
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue