highlights.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import { PDFJS } from '@/features/pdf-preview/util/pdf-js'
  2. import { PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs'
  3. import { HighlightData } from './types'
  4. export function buildHighlightElement(
  5. highlight: HighlightData,
  6. viewer: PDFViewer
  7. ) {
  8. const { viewport, div } = viewer.getPageView(highlight.page - 1)
  9. // page coordinates from synctex
  10. const rectangle = {
  11. left: highlight.h,
  12. right: highlight.h + highlight.width,
  13. top: highlight.v,
  14. bottom: highlight.v + highlight.height,
  15. }
  16. // needed because PDF page origin is at the bottom left
  17. const viewBoxHeight = viewport.viewBox[3] + 10
  18. // account for scaling
  19. const viewportRectangle = viewport.convertToViewportRectangle([
  20. rectangle.left,
  21. viewBoxHeight - rectangle.bottom,
  22. rectangle.right,
  23. viewBoxHeight - rectangle.top,
  24. ])
  25. // flip top/bottom, left/right if needed
  26. const normalizedRectangle = PDFJS.Util.normalizeRect(viewportRectangle)
  27. const [left, top, right, bottom] = normalizedRectangle
  28. // restrict to within the page container
  29. const clampedRectangle = {
  30. left: Math.max(left, 0),
  31. right: Math.min(right, div.clientWidth),
  32. top: Math.max(top, 0),
  33. bottom: Math.min(bottom, div.clientHeight),
  34. }
  35. // convert to screen positions
  36. const positions = {
  37. left: div.offsetLeft + clampedRectangle.left,
  38. right: div.offsetLeft + clampedRectangle.right,
  39. top: div.offsetTop + clampedRectangle.top,
  40. bottom: div.offsetTop + clampedRectangle.bottom,
  41. }
  42. const element = document.createElement('div')
  43. element.style.position = 'absolute'
  44. element.style.left = Math.floor(positions.left) + 'px'
  45. element.style.top = Math.floor(positions.top) + 'px'
  46. element.style.width = Math.floor(positions.right - positions.left) + 'px'
  47. element.style.height = Math.floor(positions.bottom - positions.top) + 'px'
  48. element.style.backgroundColor = 'rgb(255,255,0)'
  49. element.style.display = 'inline-block'
  50. element.style.scrollMargin = '72px'
  51. element.style.pointerEvents = 'none'
  52. element.style.opacity = '0'
  53. element.style.transition = 'opacity 1s'
  54. viewer.viewer?.append(element)
  55. return element
  56. }