|
@@ -0,0 +1,192 @@
|
|
|
+/* eslint-disable @typescript-eslint/camelcase */
|
|
|
+import fetch from 'isomorphic-unfetch';
|
|
|
+
|
|
|
+import config from '../config';
|
|
|
+import apiPath from '../constants/apiPath';
|
|
|
+import { LINE_TYPE } from '../constants';
|
|
|
+import { AnnotationType, Position } from '../constants/type';
|
|
|
+import { xmlParser } from './dom';
|
|
|
+import { chunk } from './utility';
|
|
|
+
|
|
|
+type Props = {
|
|
|
+ color: string;
|
|
|
+ type: string;
|
|
|
+ opacity: number;
|
|
|
+ scale: number;
|
|
|
+}
|
|
|
+
|
|
|
+const resetPositionValue = (
|
|
|
+ {
|
|
|
+ top, left, bottom, right,
|
|
|
+ }: Position,
|
|
|
+ scale: number,
|
|
|
+): Position => ({
|
|
|
+ top: top / scale,
|
|
|
+ left: left / scale,
|
|
|
+ bottom: bottom / scale,
|
|
|
+ right: right / scale,
|
|
|
+});
|
|
|
+
|
|
|
+export const getAnnotationWithSelection = ({
|
|
|
+ color, type, opacity, scale,
|
|
|
+}: Props): AnnotationType[] | null => {
|
|
|
+ const selection: any = document.getSelection();
|
|
|
+ if (!selection.rangeCount) return [];
|
|
|
+
|
|
|
+ const {
|
|
|
+ startContainer,
|
|
|
+ startOffset,
|
|
|
+ endContainer,
|
|
|
+ endOffset,
|
|
|
+ } = selection.getRangeAt(0);
|
|
|
+ const appendInfo: AnnotationType[] = [];
|
|
|
+ const startElement = startContainer.parentNode as HTMLElement;
|
|
|
+ const endElement = endContainer.parentNode as HTMLElement;
|
|
|
+ const startPage = startElement?.parentNode?.parentNode as HTMLElement;
|
|
|
+ const endPage = endElement?.parentNode?.parentNode as HTMLElement;
|
|
|
+ const startPageNum = parseInt(startPage.getAttribute('data-page-num') as string, 10);
|
|
|
+ const endPageNum = parseInt(endPage.getAttribute('data-page-num') as string, 10);
|
|
|
+ const textLayer = startPage.querySelector('[data-id="text-layer"]') as HTMLElement;
|
|
|
+
|
|
|
+ if (startPageNum !== endPageNum) return null;
|
|
|
+ if (startOffset === endOffset && startOffset === endOffset) return null;
|
|
|
+
|
|
|
+ const startEle = startElement.cloneNode(true) as HTMLElement;
|
|
|
+ const endEle = endElement.cloneNode(true) as HTMLElement;
|
|
|
+
|
|
|
+ const startText = startElement.innerText.substring(0, startOffset);
|
|
|
+ const endText = endEle.innerText.substring(endOffset);
|
|
|
+
|
|
|
+ startEle.innerText = startText;
|
|
|
+ endEle.innerText = endText;
|
|
|
+ textLayer.appendChild(startEle);
|
|
|
+ textLayer.appendChild(endEle);
|
|
|
+
|
|
|
+ const startEleWidth = startEle.offsetWidth;
|
|
|
+ const endEleWidth = endEle.offsetWidth;
|
|
|
+
|
|
|
+ textLayer.removeChild(startEle);
|
|
|
+ textLayer.removeChild(endEle);
|
|
|
+
|
|
|
+ const info: AnnotationType = {};
|
|
|
+ const position: Position[] = [];
|
|
|
+
|
|
|
+ // left to right and up to down select
|
|
|
+ let startX = startElement.offsetLeft + startEleWidth;
|
|
|
+ let startY = startElement.offsetTop;
|
|
|
+ let endX = endElement.offsetLeft + endElement.offsetWidth - endEleWidth;
|
|
|
+ let endY = endElement.offsetTop + endElement.offsetHeight;
|
|
|
+
|
|
|
+ if (startX > endX && startY >= endY) {
|
|
|
+ // right to left and down to up select
|
|
|
+ startX = endElement.offsetLeft + startEleWidth;
|
|
|
+ startY = endElement.offsetTop;
|
|
|
+ endX = startElement.offsetLeft + startElement.offsetWidth - endEleWidth;
|
|
|
+ endY = startElement.offsetTop + startElement.offsetHeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ textLayer.childNodes.forEach((ele: any) => {
|
|
|
+ const {
|
|
|
+ offsetTop, offsetLeft, offsetHeight, offsetWidth,
|
|
|
+ } = ele;
|
|
|
+ const offsetRight = offsetLeft + offsetWidth;
|
|
|
+ const offsetBottom = offsetTop + offsetHeight;
|
|
|
+ let coords = {
|
|
|
+ top: 0, left: 0, right: 0, bottom: 0,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (offsetTop >= startY && offsetBottom <= endY) {
|
|
|
+ if (startElement === endElement) {
|
|
|
+ // start and end same element
|
|
|
+ coords = {
|
|
|
+ top: offsetTop,
|
|
|
+ bottom: offsetBottom,
|
|
|
+ left: startX,
|
|
|
+ right: endX,
|
|
|
+ };
|
|
|
+ } else if (
|
|
|
+ (offsetTop > startY && offsetBottom < endY) || (offsetLeft >= startX && offsetRight <= endX)
|
|
|
+ ) {
|
|
|
+ // middle element
|
|
|
+ coords = {
|
|
|
+ top: offsetTop,
|
|
|
+ bottom: offsetBottom,
|
|
|
+ left: offsetLeft,
|
|
|
+ right: offsetRight,
|
|
|
+ };
|
|
|
+ } else if (offsetTop === startY) {
|
|
|
+ // start line element
|
|
|
+ coords = {
|
|
|
+ top: offsetTop,
|
|
|
+ bottom: offsetBottom,
|
|
|
+ left: offsetLeft <= startX ? startX : offsetLeft,
|
|
|
+ right: offsetRight,
|
|
|
+ };
|
|
|
+ } else if (offsetBottom === endY) {
|
|
|
+ // end line element
|
|
|
+ coords = {
|
|
|
+ top: offsetTop,
|
|
|
+ bottom: offsetBottom,
|
|
|
+ left: offsetLeft,
|
|
|
+ right: offsetRight >= endX ? endX : offsetRight,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ position.push(resetPositionValue(coords, scale));
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ info.obj_type = LINE_TYPE[type];
|
|
|
+ info.obj_attr = {
|
|
|
+ page: startPageNum,
|
|
|
+ bdcolor: color,
|
|
|
+ position,
|
|
|
+ transparency: opacity * 0.01,
|
|
|
+ };
|
|
|
+ appendInfo.push(info);
|
|
|
+
|
|
|
+ return appendInfo;
|
|
|
+};
|
|
|
+
|
|
|
+export const fetchXfdf = (token: string): Promise<any> => (
|
|
|
+ fetch(`${config.API_HOST}${apiPath.getXfdf}?f=${token}`).then(res => res.text())
|
|
|
+);
|
|
|
+
|
|
|
+export const parseAnnotationFromXml = (xmlString: string): AnnotationType[] => {
|
|
|
+ const xmlDoc = xmlParser(xmlString);
|
|
|
+ let annotations = xmlDoc.firstElementChild.children[0].children;
|
|
|
+ annotations = Array.prototype.slice.call(annotations);
|
|
|
+ const filterAnnotations = annotations.reduce((acc: any[], cur: any) => {
|
|
|
+ if (
|
|
|
+ cur.tagName === 'highlight'
|
|
|
+ || cur.tagName === 'underline'
|
|
|
+ || cur.tagName === 'strikeout'
|
|
|
+ || cur.tagName === 'squiggly'
|
|
|
+ ) {
|
|
|
+ let tempArray = [];
|
|
|
+ if (cur.attributes.coords) {
|
|
|
+ const coords = cur.attributes.coords.value.split(',');
|
|
|
+ tempArray = chunk(coords, 8);
|
|
|
+ }
|
|
|
+
|
|
|
+ const position = tempArray.map((ele: string[]) => ({
|
|
|
+ top: parseInt(ele[5] as string, 10),
|
|
|
+ bottom: parseInt(ele[1], 10),
|
|
|
+ left: parseInt(ele[0], 10),
|
|
|
+ right: parseInt(ele[2], 10),
|
|
|
+ }));
|
|
|
+
|
|
|
+ acc.push({
|
|
|
+ obj_type: LINE_TYPE[cur.tagName],
|
|
|
+ obj_attr: {
|
|
|
+ page: parseInt(cur.attributes.page.value, 10),
|
|
|
+ bdcolor: cur.attributes.color.value,
|
|
|
+ position,
|
|
|
+ transparency: parseFloat(cur.attributes.opacity.value),
|
|
|
+ },
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return acc;
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ return filterAnnotations;
|
|
|
+};
|