markup.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { ANNOTATION_TYPE } from '../constants';
  2. import { parseAnnotationObject, appendUserIdAndDate } from './annotation';
  3. type GetMarkupWithSelectionFunc = ({
  4. color,
  5. type,
  6. opacity,
  7. scale,
  8. }: {
  9. color: string;
  10. type: string;
  11. opacity: number;
  12. scale: number;
  13. textLayer: HTMLElement;
  14. }) => AnnotationType | null;
  15. export const getMarkupWithSelection: GetMarkupWithSelectionFunc = ({
  16. color,
  17. type,
  18. opacity,
  19. scale,
  20. textLayer,
  21. }) => {
  22. const selection: Selection | null = document.getSelection();
  23. if (!selection || selection.isCollapsed) return null;
  24. const {
  25. startContainer,
  26. startOffset,
  27. endContainer,
  28. endOffset,
  29. } = selection.getRangeAt(0);
  30. const startElement = startContainer.parentNode as HTMLElement;
  31. const endElement = endContainer.parentNode as HTMLElement;
  32. const startPage = startElement?.parentNode?.parentNode as HTMLElement;
  33. const startPageNum = parseInt(
  34. startPage.getAttribute('data-page-num') as string,
  35. 10
  36. );
  37. const pageHeight = startPage.offsetHeight;
  38. const startCloneEle = startElement.cloneNode(true) as HTMLElement;
  39. const endCloneEle = endElement.cloneNode(true) as HTMLElement;
  40. const startText = startElement.innerText.substring(0, startOffset);
  41. const endText = endElement.innerText.substring(endOffset);
  42. startCloneEle.innerText = startText;
  43. endCloneEle.innerText = endText;
  44. textLayer.appendChild(startCloneEle);
  45. textLayer.appendChild(endCloneEle);
  46. const startExtraWidth = startCloneEle.getBoundingClientRect().width;
  47. const endExtraWidth = endCloneEle.getBoundingClientRect().width;
  48. textLayer.removeChild(startCloneEle);
  49. textLayer.removeChild(endCloneEle);
  50. const info: AnnotationType = {
  51. obj_type: '',
  52. obj_attr: {
  53. page: 0,
  54. bdcolor: '',
  55. position: [],
  56. transparency: 0,
  57. },
  58. };
  59. const position: PositionType[] = [];
  60. const startRect = startElement.getBoundingClientRect();
  61. const endRect = endElement.getBoundingClientRect();
  62. // left to right and up to down select
  63. let startX = startElement.offsetLeft + startExtraWidth;
  64. let startY = startElement.offsetTop;
  65. let endX = endElement.offsetLeft + endRect.width - endExtraWidth;
  66. let endY = endElement.offsetTop + endRect.height;
  67. if (startX > endX && startY >= endY) {
  68. // right to left and down to up select
  69. startX = endElement.offsetLeft + startExtraWidth;
  70. startY = endElement.offsetTop;
  71. endX = startElement.offsetLeft + startRect.width - endExtraWidth;
  72. endY = startElement.offsetTop + startRect.height;
  73. }
  74. // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
  75. // @ts-ignore
  76. const textElements = [...textLayer.childNodes];
  77. let inRange = false;
  78. textElements.forEach((ele: HTMLElement) => {
  79. const { height, width } = ele.getBoundingClientRect();
  80. const { offsetTop, offsetLeft } = ele;
  81. const top = offsetTop;
  82. const left = offsetLeft;
  83. const right = offsetLeft + width;
  84. const bottom = offsetTop + height;
  85. const coords = {
  86. top,
  87. bottom,
  88. left,
  89. right,
  90. };
  91. if (startElement === ele && endElement === ele) {
  92. // in same element
  93. coords.left = startX;
  94. coords.right = endX;
  95. position.push(coords);
  96. } else if (startElement === ele) {
  97. // in start element
  98. coords.left = startX;
  99. position.push(coords);
  100. inRange = true;
  101. } else if (endElement === ele) {
  102. // in end element
  103. coords.right = endX;
  104. position.push(coords);
  105. inRange = false;
  106. } else if (inRange) {
  107. // middle element
  108. position.push(coords);
  109. }
  110. });
  111. for (let i = 1; i < position.length; i += 1) {
  112. if (
  113. position[i].top >= position[i - 1].top - 4 &&
  114. position[i].bottom <= position[i - 1].bottom + 4
  115. ) {
  116. position[i - 1].right = position[i].left;
  117. }
  118. }
  119. info.obj_type = ANNOTATION_TYPE[type];
  120. info.obj_attr = {
  121. page: startPageNum,
  122. bdcolor: color,
  123. position,
  124. transparency: opacity,
  125. };
  126. return appendUserIdAndDate(parseAnnotationObject(info, pageHeight, scale));
  127. };
  128. export default getMarkupWithSelection;