1
0
Fork 0
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:
Samuel Chantaraud 2014-03-07 10:48:42 -04:00
parent 3e931048f2
commit 076b3433b4
8 changed files with 305 additions and 131 deletions

View file

@ -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;
}
});