Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[react-pdf] canvas와 svg로 pdf를 rendering 할 수 있는 컴포넌트를 구현합니다. #19

Merged
merged 8 commits into from
Apr 23, 2024
42 changes: 42 additions & 0 deletions packages/react-pdf/src/components/Pages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {Fragment, PropsWithChildren, memo, useCallback} from 'react'

import {usePdfContext} from '../contexts/pdf'
import {PageCanvas} from './page/Canvas'
import {PageSvg} from './page/Svg'

export interface PagesProps {
renderMode?: 'canvas' | 'svg'
}

export const Pages = memo(function Pages({renderMode, children}: PropsWithChildren<PagesProps>) {
const {pdf} = usePdfContext()

const RenderPDF = useCallback(() => {
return Array.from({length: pdf.numPages}).map((_, index) => {
const pageNumber = index + 1
return (
<Fragment key={index}>
{renderMode === 'canvas' && <PageCanvas pageNumber={pageNumber} />}
{renderMode === 'svg' && <PageSvg pageNumber={pageNumber} />}
</Fragment>
)
})
}, [pdf.numPages, renderMode])

const RenderTextLayer = useCallback(() => {
return null
}, [])

const RenderAnnotationLayer = useCallback(() => {
return null
}, [])

return (
<>
<RenderPDF />
<RenderTextLayer />
<RenderAnnotationLayer />
{children}
</>
)
})
22 changes: 16 additions & 6 deletions packages/react-pdf/src/components/PdfViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import {ReactNode, useEffect, useState} from 'react'
import {ReactNode, useState} from 'react'

import {PDFProvider, PDFProviderContext} from '../contexts/pdf'
import {PDFProvider} from '../contexts/pdf'
import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect'
import {PDFDocumentProxy} from '../pdfjs-dist/types/pdfjs'
import {getPdfDocument} from '../utils/pdf'
import {Pages, PagesProps} from './Pages'

export type PDFViewerProps = Omit<PDFProviderContext, 'pdf'> & {
export type PDFViewerProps = PagesProps & {
pdfUrl: string

header?: ReactNode
footer?: ReactNode
tokenize?: boolean
onClickWords?: {target: string | RegExp; callback: () => void | Promise<void>}[]
Expand All @@ -20,10 +24,10 @@ export type PDFViewerProps = Omit<PDFProviderContext, 'pdf'> & {
}
}

export function PDFViewer({pdfUrl, renderMode, options}: PDFViewerProps) {
export function PDFViewer({pdfUrl, renderMode = 'canvas', header, footer, options}: PDFViewerProps) {
const [pdf, setPdf] = useState<PDFDocumentProxy | undefined>()

useEffect(() => {
useIsomorphicLayoutEffect(() => {
async function init() {
const pdfDocument = await getPdfDocument({
file: pdfUrl,
Expand All @@ -48,5 +52,11 @@ export function PDFViewer({pdfUrl, renderMode, options}: PDFViewerProps) {
return null
}

return <PDFProvider pdf={pdf} renderMode={renderMode}></PDFProvider>
return (
<PDFProvider pdf={pdf}>
{header}
<Pages renderMode={renderMode} />
{footer}
</PDFProvider>
)
}
35 changes: 32 additions & 3 deletions packages/react-pdf/src/components/page/Canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
import {memo} from 'react'
import {memo, useCallback} from 'react'

export const PageCanvas = memo(function PageCanvas() {
return null
import {usePdfContext} from '../../contexts/pdf'

interface PageCanvasProps {
pageNumber: number
}

export const PageCanvas = memo(function PageCanvas({pageNumber}: PageCanvasProps) {
const {pdf} = usePdfContext()

const drawCanvas = useCallback(
async (canvas: HTMLCanvasElement | null) => {
if (!canvas) {
return
}

const canvasContext = canvas.getContext('2d')
if (!canvasContext) {
return
}

const page = await pdf.getPage(pageNumber)
const viewport = page.getViewport({scale: 1, rotation: 0})

canvas.width = viewport.width
canvas.height = viewport.height
page.render({canvasContext, viewport})
},
[pageNumber, pdf],
)

return <canvas ref={drawCanvas} />
})
37 changes: 34 additions & 3 deletions packages/react-pdf/src/components/page/Svg.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
import {memo} from 'react'
import {memo, useCallback} from 'react'

export const PageSvg = memo(function PageSvg() {
return null
import {usePdfContext} from '../../contexts/pdf'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import * as pdfjs from '../../pdfjs-dist/legacy/build/pdf'

interface PageSvgProps {
pageNumber: number
}

export const PageSvg = memo(function PageSvg({pageNumber}: PageSvgProps) {
const {pdf} = usePdfContext()

const drawSvg = useCallback(
async (element: HTMLDivElement | null) => {
if (!element) {
return
}

const page = await pdf.getPage(pageNumber)
const viewport = page.getViewport({scale: 1, rotation: 0})
const operatorList = await page.getOperatorList()

element.setAttribute('width', `${viewport.width}px`)
element.setAttribute('height', `${viewport.height}px`)

const svgGfx = new pdfjs.SVGGraphics(page.commonObjs, page.objs)
const svg = await svgGfx.getSVG(operatorList, viewport)
element.appendChild(svg)
},
[pageNumber, pdf],
)

return <div ref={drawSvg} />
})
5 changes: 2 additions & 3 deletions packages/react-pdf/src/contexts/pdf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import type {PDFDocumentProxy} from '../pdfjs-dist/types/pdfjs'

export interface PDFProviderContext {
pdf: PDFDocumentProxy
renderMode?: 'canvas' | 'svg'
}

const Context = createContext<PDFProviderContext | undefined>(undefined)

export function PDFProvider({pdf, renderMode = 'canvas', children}: PropsWithChildren<PDFProviderContext>) {
const value = useMemo(() => ({pdf, renderMode}), [pdf, renderMode])
export function PDFProvider({pdf, children}: PropsWithChildren<PDFProviderContext>) {
const value = useMemo(() => ({pdf}), [pdf])

return <Context.Provider value={value}>{children}</Context.Provider>
}
Expand Down
3 changes: 3 additions & 0 deletions packages/react-pdf/src/hooks/useIsomorphicLayoutEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {useEffect, useLayoutEffect} from 'react'

export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect
7 changes: 7 additions & 0 deletions packages/react-pdf/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
export * from './utils/pdf'
export * from './components/page/Canvas'
export * from './components/page/Svg'
export * from './components/layer/Annotation'
export * from './components/layer/Text'
export * from './components/Pages'
export * from './contexts/pdf'
export * from './components/PdfViewer'
16 changes: 16 additions & 0 deletions packages/react-pdf/storybook/pdf-viewer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react'

import {PDFViewer} from '../dist/cjs'

const meta = {
title: 'React PDF',
component: 기본_PdfViewer,
}
export default meta

const PDF_URL =
'https://fs.pstatic.net/contents/resource/loan/personal-compare/required/0/1668583261818/gi_creditLoanNF_02.pdf'
yceffort-naver marked this conversation as resolved.
Show resolved Hide resolved

export function 기본_PdfViewer() {
return <PDFViewer pdfUrl={PDF_URL} />
}
Loading