lichen-annotate-pdf
Version:
Annotation layer for pdf.js in vue fork of Aaron Leong pdf-annotate-vue
251 lines (214 loc) • 6.12 kB
JavaScript
import PDFJSAnnotate from '../PDFJSAnnotate';
import config from '../config';
import { appendChild } from '../render/appendChild';
import {
BORDER_COLOR,
disableUserSelect,
enableUserSelect,
findSVGAtPoint,
getMetadata,
convertToSvgRect
} from './utils';
let _enabled = false;
let _type;
let overlay;
let originY;
let originX;
/**
* Get the current window selection as rects
*
* @return {Array} An Array of rects
*/
function getSelectionRects () {
try {
let selection = window.getSelection();
let range = selection.getRangeAt(0);
let rects = range.getClientRects();
if (rects.length > 0 &&
rects[0].width > 0 &&
rects[0].height > 0) {
return rects;
}
}
catch (e) { }
return null;
}
/**
* Handle document.mousedown event
*
* @param {Event} e The DOM event to handle
*/
function handleDocumentMousedown (e) {
let svg;
if (_type !== 'area' || !(svg = findSVGAtPoint(e.clientX, e.clientY))) {
return;
}
let rect = svg.getBoundingClientRect();
originY = e.clientY;
originX = e.clientX;
overlay = document.createElement('div');
overlay.style.position = 'absolute';
overlay.style.top = `${originY - rect.top}px`;
overlay.style.left = `${originX - rect.left}px`;
overlay.style.border = `3px solid ${BORDER_COLOR}`;
overlay.style.borderRadius = '3px';
svg.parentNode.appendChild(overlay);
document.addEventListener('mousemove', handleDocumentMousemove);
disableUserSelect();
}
/**
* Handle document.mousemove event
*
* @param {Event} e The DOM event to handle
*/
function handleDocumentMousemove (e) {
let svg = overlay.parentNode.querySelector(config.annotationSvgQuery());
let rect = svg.getBoundingClientRect();
if (originX + (e.clientX - originX) < rect.right) {
overlay.style.width = `${e.clientX - originX}px`;
}
if (originY + (e.clientY - originY) < rect.bottom) {
overlay.style.height = `${e.clientY - originY}px`;
}
}
/**
* Handle document.mouseup event
*
* @param {Event} e The DOM event to handle
*/
function handleDocumentMouseup (e) {
let rects;
if (_type !== 'area' && (rects = getSelectionRects())) {
saveRect(_type, [...rects].map((r) => {
return {
top: r.top,
left: r.left,
width: r.width,
height: r.height
};
}));
}
else if (_type === 'area' && overlay) {
let svg = overlay.parentNode.querySelector(config.annotationSvgQuery());
let rect = svg.getBoundingClientRect();
saveRect(_type, [{
top: parseInt(overlay.style.top, 10) + rect.top,
left: parseInt(overlay.style.left, 10) + rect.left,
width: parseInt(overlay.style.width, 10),
height: parseInt(overlay.style.height, 10)
}]);
overlay.parentNode.removeChild(overlay);
overlay = null;
document.removeEventListener('mousemove', handleDocumentMousemove);
enableUserSelect();
}
}
/**
* Handle document.keyup event
*
* @param {Event} e The DOM event to handle
*/
function handleDocumentKeyup (e) {
// Cancel rect if Esc is pressed
if (e.keyCode === 27) {
let selection = window.getSelection();
selection.removeAllRanges();
if (overlay && overlay.parentNode) {
overlay.parentNode.removeChild(overlay);
overlay = null;
document.removeEventListener('mousemove', handleDocumentMousemove);
}
}
}
/**
* Save a rect annotation
*
* @param {String} type The type of rect (area, highlight, strikeout)
* @param {Array} rects The rects to use for annotation
* @param {String} color The color of the rects
*/
function saveRect (type, rects, color) {
let svg = findSVGAtPoint(rects[0].left, rects[0].top);
let annotation;
if (!svg) {
return;
}
let boundingRect = svg.getBoundingClientRect();
// console.log('saveRect', type)
if (!color) {
if (type === 'highlight') {
color = 'FFFF00';
}
else if (type === 'strikeout') {
color = 'FF0000';
}
}
// Initialize the annotation
annotation = {
type,
color,
rectangles: [...rects].map((r) => {
let offset = 0;
if (type === 'strikeout') {
offset = r.height / 2;
}
return convertToSvgRect({
y: (r.top + offset) - boundingRect.top,
x: r.left - boundingRect.left,
width: r.width,
height: r.height
}, svg);
}).filter((r) => r.width > 0 && r.height > 0 && r.x > -1 && r.y > -1)
};
// Short circuit if no rectangles exist
if (annotation.rectangles.length === 0) {
return;
}
// Special treatment for area as it only supports a single rect
if (type === 'area') {
let rect = annotation.rectangles[0];
delete annotation.rectangles;
annotation.x = rect.x;
annotation.y = rect.y;
annotation.width = rect.width;
annotation.height = rect.height;
}
let { documentId, pageNumber } = getMetadata(svg);
// Add the annotation
// console.log('annotation',annotation)
PDFJSAnnotate.getStoreAdapter().addAnnotation(documentId, pageNumber, annotation)
.then((annotation) => {
appendChild(svg, annotation);
});
PDFJSAnnotate.getStoreAdapter().getAnnotations('#viewer', 1).then(function (data) {
console.log(data)
})
}
/**
* Enable rect behavior
*/
export function enableRect (type) {
_type = type;
if (_enabled) { return; }
_enabled = true;
document.addEventListener('mouseup', handleDocumentMouseup);
document.addEventListener('mousedown', handleDocumentMousedown);
document.addEventListener('keyup', handleDocumentKeyup);
}
/**
* Disable rect behavior
*/
export function disableRect (val) {
if (val) {
_enabled = false;
document.removeEventListener('mouseup', handleDocumentMouseup);
document.removeEventListener('mousedown', handleDocumentMousedown);
document.removeEventListener('keyup', handleDocumentKeyup);
} else {
if (!_enabled) { return; }
_enabled = false;
document.removeEventListener('mouseup', handleDocumentMouseup);
document.removeEventListener('mousedown', handleDocumentMousedown);
document.removeEventListener('keyup', handleDocumentKeyup);
}
}