diff --git a/example/src/App.tsx b/example/src/App.tsx index 88c3bad..77d4089 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,31 +1,51 @@ import React, {useState} from 'react'; import ReactFastPDF, {PDFPreviewer} from 'react-fast-pdf'; +import type {RotationDegrees} from 'react-fast-pdf'; import './index.css'; function App() { const [file, setFile] = useState(null); + const [rotation, setRotation] = useState(0); // `.default` is required when referencing the legacy CJS package. const packageName = ('default' in ReactFastPDF ? (ReactFastPDF.default as {PackageName: string}) : ReactFastPDF).PackageName; + const handleRotate = () => { + setRotation((prev) => ((prev + 90) % 360) as RotationDegrees); + }; + return (

Hello, I am {packageName}!

{file ? ( <> - +
+ + + +
) : ( diff --git a/src/PDFPreviewer.tsx b/src/PDFPreviewer.tsx index 8b88ad1..3cce600 100644 --- a/src/PDFPreviewer.tsx +++ b/src/PDFPreviewer.tsx @@ -1,13 +1,13 @@ import pdfWorkerSource from 'pdfjs-dist/build/pdf.worker.min.mjs'; -import React, {memo, useCallback, useLayoutEffect, useRef, useState} from 'react'; -import type {CSSProperties, ReactNode} from 'react'; -import times from 'lodash/times.js'; +import React, {useCallback, useLayoutEffect, useRef, useState, memo} from 'react'; +import type {CSSProperties, ReactNode, JSX} from 'react'; +import {times} from 'lodash'; import {VariableSizeList as List} from 'react-window'; import {Document, pdfjs} from 'react-pdf'; import 'react-pdf/dist/Page/AnnotationLayer.css'; import 'react-pdf/dist/Page/TextLayer.css'; -import type {PDFDocument, PageViewport} from './types.js'; +import type {PDFDocument, PageViewport, RotationDegrees} from './types.js'; import {pdfPreviewerStyles as styles} from './styles.js'; import PDFPasswordForm, {type PDFPasswordFormProps} from './PDFPasswordForm.js'; import PageRenderer from './PageRenderer.js'; @@ -28,6 +28,8 @@ type Props = { onLoadError?: () => void; containerStyle?: CSSProperties; contentContainerStyle?: CSSProperties; + /** Rotation angle for all pages (0, 90, 180, 270 degrees) */ + rotation?: RotationDegrees; }; type OnPasswordCallback = (password: string | null) => void; @@ -51,7 +53,8 @@ function PDFPreviewer({ contentContainerStyle, shouldShowErrorComponent = true, onLoadError, -}: Props) { + rotation = 0, +}: Props): JSX.Element { const [pageViewports, setPageViewports] = useState([]); const [numPages, setNumPages] = useState(0); const [containerWidth, setContainerWidth] = useState(0); @@ -103,6 +106,7 @@ function PDFPreviewer({ * Calculates a proper page height. The method should be called only when there are page viewports. * It is based on a ratio between the specific page viewport width and provided page width. * Also, the app should take into account the page borders. + * When rotation is 90 or 270 degrees, width and height are swapped. */ const calculatePageHeight = useCallback( (pageIndex: number) => { @@ -112,12 +116,18 @@ function PDFPreviewer({ const pageWidth = calculatePageWidth(); - const {width: pageViewportWidth, height: pageViewportHeight} = pageViewports[pageIndex]; + const {width: originalWidth, height: originalHeight} = pageViewports[pageIndex]; + + // Swap dimensions when rotated 90 or 270 degrees + const isRotated90or270 = rotation === 90 || rotation === 270; + const pageViewportWidth = isRotated90or270 ? originalHeight : originalWidth; + const pageViewportHeight = isRotated90or270 ? originalWidth : originalHeight; + const scale = pageWidth / pageViewportWidth; return pageViewportHeight * scale + PAGE_BORDER * 2; }, - [pageViewports, calculatePageWidth], + [pageViewports, calculatePageWidth, rotation], ); const estimatedPageHeight = calculatePageHeight(0); @@ -207,7 +217,7 @@ function PDFPreviewer({ if (containerWidth > 0 && containerHeight > 0) { listRef.current?.resetAfterIndex(0); } - }, [containerWidth, containerHeight]); + }, [containerWidth, containerHeight, rotation]); useLayoutEffect(() => { if (!containerRef.current) { @@ -230,7 +240,12 @@ function PDFPreviewer({ ref={containerRef} style={{...styles.container, ...containerStyle}} > -
+
@@ -251,7 +267,14 @@ function PDFPreviewer({ itemCount={numPages} itemSize={calculatePageHeight} estimatedItemSize={calculatePageHeight(0)} - itemData={{pageWidth, estimatedPageHeight, calculatePageHeight, getDevicePixelRatio, containerHeight, numPages}} + itemData={{ + pageWidth, + estimatedPageHeight, + calculatePageHeight, + getDevicePixelRatio, + containerHeight, + numPages, + }} > {PageRenderer} @@ -264,6 +287,6 @@ function PDFPreviewer({ ); } -PDFPasswordForm.displayName = 'PDFPreviewer'; +PDFPreviewer.displayName = 'PDFPreviewer'; export default memo(PDFPreviewer); diff --git a/src/index.ts b/src/index.ts index 7fb5b72..a638ea9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,10 @@ import PDFPreviewer from './PDFPreviewer.js'; +import type {RotationDegrees} from './types.js'; const PACKAGE_NAME = 'react-fast-pdf'; export {PDFPreviewer}; +export type {RotationDegrees}; export default { PackageName: PACKAGE_NAME, diff --git a/src/types.ts b/src/types.ts index 2c5255f..ca6996e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,4 +18,9 @@ type ComponentStyles = { [key: string]: CSSProperties; }; -export type {PDFDocument, PageViewport, ComponentStyles}; +/** + * Valid rotation angles for PDF pages (in degrees clockwise) + */ +type RotationDegrees = 0 | 90 | 180 | 270; + +export type {PDFDocument, PageViewport, ComponentStyles, RotationDegrees};