markup.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import { ANNOTATION_TYPE } from '../constants';
  2. import { AnnotationType, PositionType } from '../constants/type';
  3. import { parseAnnotationObject } from './annotation';
  4. const EXTEND_RANGE = 2;
  5. type ScalePositionFunc = (
  6. ({
  7. top, left, bottom, right,
  8. }: PositionType, scale: number) => PositionType
  9. );
  10. type GetMarkupWithSelectionFunc = (
  11. ({
  12. color, type, opacity, scale,
  13. }: {
  14. color: string;
  15. type: string;
  16. opacity: number;
  17. scale: number;
  18. }) => AnnotationType | null
  19. );
  20. const scalePosition: ScalePositionFunc = ({
  21. top, left, bottom, right,
  22. }, scale) => ({
  23. top: top / scale,
  24. left: left / scale,
  25. bottom: bottom / scale,
  26. right: right / scale,
  27. });
  28. export const getMarkupWithSelection: GetMarkupWithSelectionFunc = ({
  29. color, type, opacity, scale,
  30. }) => {
  31. const selection: any = document.getSelection();
  32. if (!selection.rangeCount) return null;
  33. const {
  34. startContainer,
  35. startOffset,
  36. endContainer,
  37. endOffset,
  38. } = selection.getRangeAt(0);
  39. const startElement = startContainer.parentNode as HTMLElement;
  40. const endElement = endContainer.parentNode as HTMLElement;
  41. const startPage = startElement?.parentNode?.parentNode as HTMLElement;
  42. const endPage = endElement?.parentNode?.parentNode as HTMLElement;
  43. const startPageNum = parseInt(startPage.getAttribute('data-page-num') as string, 10);
  44. const endPageNum = parseInt(endPage.getAttribute('data-page-num') as string, 10);
  45. const textLayer = startPage.querySelector('[data-id="text-layer"]') as HTMLElement;
  46. const pageHeight = startPage.offsetHeight;
  47. if (startPageNum !== endPageNum) return null;
  48. if (startOffset === endOffset && startOffset === endOffset) return null;
  49. const startEle = startElement.cloneNode(true) as HTMLElement;
  50. const endEle = endElement.cloneNode(true) as HTMLElement;
  51. const startText = startElement.innerText.substring(0, startOffset);
  52. const endText = endEle.innerText.substring(endOffset);
  53. startEle.innerText = startText;
  54. endEle.innerText = endText;
  55. textLayer.appendChild(startEle);
  56. textLayer.appendChild(endEle);
  57. const startEleWidth = startEle.offsetWidth;
  58. const endEleWidth = endEle.offsetWidth;
  59. textLayer.removeChild(startEle);
  60. textLayer.removeChild(endEle);
  61. const info: AnnotationType = {
  62. obj_type: '',
  63. obj_attr: {
  64. page: 0,
  65. bdcolor: '',
  66. position: [],
  67. transparency: 0,
  68. },
  69. };
  70. const position: PositionType[] = [];
  71. // left to right and up to down select
  72. let startX = startElement.offsetLeft + startEleWidth;
  73. let startY = startElement.offsetTop - EXTEND_RANGE;
  74. let endX = endElement.offsetLeft + endElement.offsetWidth - endEleWidth;
  75. let endY = endElement.offsetTop + endElement.offsetHeight + EXTEND_RANGE;
  76. if (startX > endX && startY >= endY) {
  77. // right to left and down to up select
  78. startX = endElement.offsetLeft + startEleWidth;
  79. startY = endElement.offsetTop - EXTEND_RANGE;
  80. endX = startElement.offsetLeft + startElement.offsetWidth - endEleWidth;
  81. endY = startElement.offsetTop + startElement.offsetHeight + EXTEND_RANGE;
  82. }
  83. // @ts-ignore
  84. const textElements = [...textLayer.childNodes];
  85. textElements.forEach((ele: any) => {
  86. const {
  87. offsetTop, offsetLeft, offsetHeight, offsetWidth,
  88. } = ele;
  89. const offsetRight = offsetLeft + offsetWidth;
  90. const offsetBottom = offsetTop + offsetHeight;
  91. let coords = {
  92. top: 0, left: 0, right: 0, bottom: 0,
  93. };
  94. if (offsetTop >= startY && offsetBottom <= endY) {
  95. if (startElement === endElement) {
  96. // start and end same element
  97. coords = {
  98. top: offsetTop,
  99. bottom: offsetBottom,
  100. left: startX,
  101. right: endX,
  102. };
  103. } else if (startElement === ele) {
  104. // start element
  105. coords = {
  106. top: offsetTop,
  107. bottom: offsetBottom,
  108. left: startX,
  109. right: offsetRight,
  110. };
  111. } else if (endElement === ele) {
  112. // end element
  113. coords = {
  114. top: offsetTop,
  115. bottom: offsetBottom,
  116. left: offsetLeft,
  117. right: endX,
  118. };
  119. } else if (
  120. (offsetLeft >= startX && offsetRight <= endX)
  121. || (offsetTop > (startY + 5) && offsetBottom < (endY - 5))
  122. || (offsetLeft >= startX && offsetBottom <= startY + offsetHeight + 5)
  123. || (offsetRight <= endX && offsetTop >= endX - offsetHeight - 5)
  124. ) {
  125. // middle element
  126. coords = {
  127. top: offsetTop,
  128. bottom: offsetBottom,
  129. left: offsetLeft,
  130. right: offsetRight,
  131. };
  132. }
  133. position.push(scalePosition(coords, scale));
  134. }
  135. });
  136. info.obj_type = ANNOTATION_TYPE[type];
  137. info.obj_attr = {
  138. page: startPageNum,
  139. bdcolor: color,
  140. position,
  141. transparency: opacity,
  142. };
  143. return parseAnnotationObject(info, pageHeight, scale);
  144. };
  145. export default parseAnnotationObject;