* fix: by obtaining the width and height of the pdf and passing it to boundingRect, the problem of inaccurate positioning of file highlighting is fixed * feat: remove actualPositions from buildChunkHighlightstags/v0.1.0
| const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => { | const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => { | ||||
| const url = useGetDocumentUrl(documentId); | const url = useGetDocumentUrl(documentId); | ||||
| const state = useGetChunkHighlights(chunk); | |||||
| const { highlights: state, setWidthAndHeight } = useGetChunkHighlights(chunk); | |||||
| const ref = useRef<(highlight: IHighlight) => void>(() => {}); | const ref = useRef<(highlight: IHighlight) => void>(() => {}); | ||||
| const [loaded, setLoaded] = useState(false); | const [loaded, setLoaded] = useState(false); | ||||
| beforeLoad={<Skeleton active />} | beforeLoad={<Skeleton active />} | ||||
| workerSrc="/pdfjs-dist/pdf.worker.min.js" | workerSrc="/pdfjs-dist/pdf.worker.min.js" | ||||
| > | > | ||||
| {(pdfDocument) => ( | |||||
| <PdfHighlighter | |||||
| pdfDocument={pdfDocument} | |||||
| enableAreaSelection={(event) => event.altKey} | |||||
| onScrollChange={resetHash} | |||||
| scrollRef={(scrollTo) => { | |||||
| ref.current = scrollTo; | |||||
| setLoaded(true); | |||||
| }} | |||||
| onSelectionFinished={() => null} | |||||
| highlightTransform={( | |||||
| highlight, | |||||
| index, | |||||
| setTip, | |||||
| hideTip, | |||||
| viewportToScaled, | |||||
| screenshot, | |||||
| isScrolledTo, | |||||
| ) => { | |||||
| const isTextHighlight = !Boolean( | |||||
| highlight.content && highlight.content.image, | |||||
| ); | |||||
| {(pdfDocument) => { | |||||
| pdfDocument.getPage(1).then((page) => { | |||||
| const viewport = page.getViewport({ scale: 1 }); | |||||
| const width = viewport.width; | |||||
| const height = viewport.height; | |||||
| setWidthAndHeight(width, height); | |||||
| }); | |||||
| const component = isTextHighlight ? ( | |||||
| <Highlight | |||||
| isScrolledTo={isScrolledTo} | |||||
| position={highlight.position} | |||||
| comment={highlight.comment} | |||||
| /> | |||||
| ) : ( | |||||
| <AreaHighlight | |||||
| isScrolledTo={isScrolledTo} | |||||
| highlight={highlight} | |||||
| onChange={() => {}} | |||||
| /> | |||||
| ); | |||||
| return ( | |||||
| <PdfHighlighter | |||||
| pdfDocument={pdfDocument} | |||||
| enableAreaSelection={(event) => event.altKey} | |||||
| onScrollChange={resetHash} | |||||
| scrollRef={(scrollTo) => { | |||||
| ref.current = scrollTo; | |||||
| setLoaded(true); | |||||
| }} | |||||
| onSelectionFinished={() => null} | |||||
| highlightTransform={( | |||||
| highlight, | |||||
| index, | |||||
| setTip, | |||||
| hideTip, | |||||
| viewportToScaled, | |||||
| screenshot, | |||||
| isScrolledTo, | |||||
| ) => { | |||||
| const isTextHighlight = !Boolean( | |||||
| highlight.content && highlight.content.image, | |||||
| ); | |||||
| return ( | |||||
| <Popup | |||||
| popupContent={<HighlightPopup {...highlight} />} | |||||
| onMouseOver={(popupContent) => | |||||
| setTip(highlight, () => popupContent) | |||||
| } | |||||
| onMouseOut={hideTip} | |||||
| key={index} | |||||
| > | |||||
| {component} | |||||
| </Popup> | |||||
| ); | |||||
| }} | |||||
| highlights={state} | |||||
| /> | |||||
| )} | |||||
| const component = isTextHighlight ? ( | |||||
| <Highlight | |||||
| isScrolledTo={isScrolledTo} | |||||
| position={highlight.position} | |||||
| comment={highlight.comment} | |||||
| /> | |||||
| ) : ( | |||||
| <AreaHighlight | |||||
| isScrolledTo={isScrolledTo} | |||||
| highlight={highlight} | |||||
| onChange={() => {}} | |||||
| /> | |||||
| ); | |||||
| return ( | |||||
| <Popup | |||||
| popupContent={<HighlightPopup {...highlight} />} | |||||
| onMouseOver={(popupContent) => | |||||
| setTip(highlight, () => popupContent) | |||||
| } | |||||
| onMouseOut={hideTip} | |||||
| key={index} | |||||
| > | |||||
| {component} | |||||
| </Popup> | |||||
| ); | |||||
| }} | |||||
| highlights={state} | |||||
| /> | |||||
| ); | |||||
| }} | |||||
| </PdfLoader> | </PdfLoader> | ||||
| </div> | </div> | ||||
| ); | ); |
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | ||||
| import { api_host } from '@/utils/api'; | import { api_host } from '@/utils/api'; | ||||
| import { buildChunkHighlights } from '@/utils/documentUtils'; | import { buildChunkHighlights } from '@/utils/documentUtils'; | ||||
| import { useCallback, useMemo } from 'react'; | |||||
| import { useCallback, useMemo, useState } from 'react'; | |||||
| import { IHighlight } from 'react-pdf-highlighter'; | import { IHighlight } from 'react-pdf-highlighter'; | ||||
| import { useDispatch, useSelector } from 'umi'; | import { useDispatch, useSelector } from 'umi'; | ||||
| import { useGetKnowledgeSearchParams } from './routeHook'; | import { useGetKnowledgeSearchParams } from './routeHook'; | ||||
| return url; | return url; | ||||
| }; | }; | ||||
| export const useGetChunkHighlights = (selectedChunk: IChunk): IHighlight[] => { | |||||
| export const useGetChunkHighlights = (selectedChunk: IChunk) => { | |||||
| const [size, setSize] = useState({ width: 849, height: 1200 }); | |||||
| const highlights: IHighlight[] = useMemo(() => { | const highlights: IHighlight[] = useMemo(() => { | ||||
| return buildChunkHighlights(selectedChunk); | |||||
| }, [selectedChunk]); | |||||
| return buildChunkHighlights(selectedChunk, size); | |||||
| }, [selectedChunk, size]); | |||||
| const setWidthAndHeight = (width: number, height: number) => { | |||||
| setSize((pre) => { | |||||
| if (pre.height !== height || pre.width !== width) { | |||||
| return { height, width }; | |||||
| } | |||||
| return pre; | |||||
| }); | |||||
| }; | |||||
| return highlights; | |||||
| return { highlights, setWidthAndHeight }; | |||||
| }; | }; | ||||
| export const useFetchDocumentList = () => { | export const useFetchDocumentList = () => { |
| caption { | caption { | ||||
| color: @blurBackground; | color: @blurBackground; | ||||
| font-size: 20px; | |||||
| height: 50px; | |||||
| line-height: 50px; | |||||
| font-size: 14px; | |||||
| height: 20px; | |||||
| line-height: 20px; | |||||
| font-weight: 600; | font-weight: 600; | ||||
| margin-bottom: 10px; | |||||
| margin-bottom: 6px; | |||||
| } | } | ||||
| th { | th { |
| // TODO: merge with DocumentPreviewer | // TODO: merge with DocumentPreviewer | ||||
| const Preview = ({ selectedChunkId }: IProps) => { | const Preview = ({ selectedChunkId }: IProps) => { | ||||
| const url = useGetDocumentUrl(); | const url = useGetDocumentUrl(); | ||||
| const state = useGetChunkHighlights(selectedChunkId); | |||||
| const { highlights: state, setWidthAndHeight } = | |||||
| useGetChunkHighlights(selectedChunkId); | |||||
| const ref = useRef<(highlight: IHighlight) => void>(() => {}); | const ref = useRef<(highlight: IHighlight) => void>(() => {}); | ||||
| const resetHash = () => {}; | const resetHash = () => {}; | ||||
| beforeLoad={<Skeleton active />} | beforeLoad={<Skeleton active />} | ||||
| workerSrc="/pdfjs-dist/pdf.worker.min.js" | workerSrc="/pdfjs-dist/pdf.worker.min.js" | ||||
| > | > | ||||
| {(pdfDocument) => ( | |||||
| <PdfHighlighter | |||||
| pdfDocument={pdfDocument} | |||||
| enableAreaSelection={(event) => event.altKey} | |||||
| onScrollChange={resetHash} | |||||
| scrollRef={(scrollTo) => { | |||||
| ref.current = scrollTo; | |||||
| }} | |||||
| onSelectionFinished={() => null} | |||||
| highlightTransform={( | |||||
| highlight, | |||||
| index, | |||||
| setTip, | |||||
| hideTip, | |||||
| viewportToScaled, | |||||
| screenshot, | |||||
| isScrolledTo, | |||||
| ) => { | |||||
| const isTextHighlight = !Boolean( | |||||
| highlight.content && highlight.content.image, | |||||
| ); | |||||
| {(pdfDocument) => { | |||||
| pdfDocument.getPage(1).then((page) => { | |||||
| const viewport = page.getViewport({ scale: 1 }); | |||||
| const width = viewport.width; | |||||
| const height = viewport.height; | |||||
| setWidthAndHeight(width, height); | |||||
| }); | |||||
| const component = isTextHighlight ? ( | |||||
| <Highlight | |||||
| isScrolledTo={isScrolledTo} | |||||
| position={highlight.position} | |||||
| comment={highlight.comment} | |||||
| /> | |||||
| ) : ( | |||||
| <AreaHighlight | |||||
| isScrolledTo={isScrolledTo} | |||||
| highlight={highlight} | |||||
| onChange={() => {}} | |||||
| /> | |||||
| ); | |||||
| return ( | |||||
| <PdfHighlighter | |||||
| pdfDocument={pdfDocument} | |||||
| enableAreaSelection={(event) => event.altKey} | |||||
| onScrollChange={resetHash} | |||||
| scrollRef={(scrollTo) => { | |||||
| ref.current = scrollTo; | |||||
| }} | |||||
| onSelectionFinished={() => null} | |||||
| highlightTransform={( | |||||
| highlight, | |||||
| index, | |||||
| setTip, | |||||
| hideTip, | |||||
| viewportToScaled, | |||||
| screenshot, | |||||
| isScrolledTo, | |||||
| ) => { | |||||
| const isTextHighlight = !Boolean( | |||||
| highlight.content && highlight.content.image, | |||||
| ); | |||||
| return ( | |||||
| <Popup | |||||
| popupContent={<HighlightPopup {...highlight} />} | |||||
| onMouseOver={(popupContent) => | |||||
| setTip(highlight, () => popupContent) | |||||
| } | |||||
| onMouseOut={hideTip} | |||||
| key={index} | |||||
| > | |||||
| {component} | |||||
| </Popup> | |||||
| ); | |||||
| }} | |||||
| highlights={state} | |||||
| /> | |||||
| )} | |||||
| const component = isTextHighlight ? ( | |||||
| <Highlight | |||||
| isScrolledTo={isScrolledTo} | |||||
| position={highlight.position} | |||||
| comment={highlight.comment} | |||||
| /> | |||||
| ) : ( | |||||
| <AreaHighlight | |||||
| isScrolledTo={isScrolledTo} | |||||
| highlight={highlight} | |||||
| onChange={() => {}} | |||||
| /> | |||||
| ); | |||||
| return ( | |||||
| <Popup | |||||
| popupContent={<HighlightPopup {...highlight} />} | |||||
| onMouseOver={(popupContent) => | |||||
| setTip(highlight, () => popupContent) | |||||
| } | |||||
| onMouseOut={hideTip} | |||||
| key={index} | |||||
| > | |||||
| {component} | |||||
| </Popup> | |||||
| ); | |||||
| }} | |||||
| highlights={state} | |||||
| /> | |||||
| ); | |||||
| }} | |||||
| </PdfLoader> | </PdfLoader> | ||||
| </div> | </div> | ||||
| ); | ); |
| ); | ); | ||||
| }; | }; | ||||
| export const useGetChunkHighlights = ( | |||||
| selectedChunkId: string, | |||||
| ): IHighlight[] => { | |||||
| export const useGetChunkHighlights = (selectedChunkId: string) => { | |||||
| const [size, setSize] = useState({ width: 849, height: 1200 }); | |||||
| const selectedChunk: IChunk = useGetSelectedChunk(selectedChunkId); | const selectedChunk: IChunk = useGetSelectedChunk(selectedChunkId); | ||||
| const highlights: IHighlight[] = useMemo(() => { | const highlights: IHighlight[] = useMemo(() => { | ||||
| return buildChunkHighlights(selectedChunk); | |||||
| }, [selectedChunk]); | |||||
| return buildChunkHighlights(selectedChunk, size); | |||||
| }, [selectedChunk, size]); | |||||
| return highlights; | |||||
| const setWidthAndHeight = (width: number, height: number) => { | |||||
| setSize((pre) => { | |||||
| if (pre.height !== height || pre.width !== width) { | |||||
| return { height, width }; | |||||
| } | |||||
| return pre; | |||||
| }); | |||||
| }; | |||||
| return { highlights, setWidthAndHeight }; | |||||
| }; | }; | ||||
| export const useSelectChunkListLoading = () => { | export const useSelectChunkListLoading = () => { |
| import { UploadFile } from 'antd'; | import { UploadFile } from 'antd'; | ||||
| import { v4 as uuid } from 'uuid'; | import { v4 as uuid } from 'uuid'; | ||||
| export const buildChunkHighlights = (selectedChunk: IChunk) => { | |||||
| export const buildChunkHighlights = ( | |||||
| selectedChunk: IChunk, | |||||
| size: { width: number; height: number }, | |||||
| ) => { | |||||
| return Array.isArray(selectedChunk?.positions) && | return Array.isArray(selectedChunk?.positions) && | ||||
| selectedChunk.positions.every((x) => Array.isArray(x)) | selectedChunk.positions.every((x) => Array.isArray(x)) | ||||
| ? selectedChunk?.positions?.map((x) => { | ? selectedChunk?.positions?.map((x) => { | ||||
| const actualPositions = x.map((y, index) => | |||||
| index !== 0 ? y / 0.7 : y, | |||||
| ); | |||||
| const boundingRect = { | const boundingRect = { | ||||
| width: 849, | |||||
| height: 1200, | |||||
| x1: actualPositions[1], | |||||
| x2: actualPositions[2], | |||||
| y1: actualPositions[3], | |||||
| y2: actualPositions[4], | |||||
| width: size.width, | |||||
| height: size.height, | |||||
| x1: x[1], | |||||
| x2: x[2], | |||||
| y1: x[3], | |||||
| y2: x[4], | |||||
| }; | }; | ||||
| return { | return { | ||||
| id: uuid(), | id: uuid(), |