Skip to content

Commit 51089ea

Browse files
committed
feat: add rotation
1 parent 0388690 commit 51089ea

4 files changed

Lines changed: 56 additions & 18 deletions

File tree

example/src/App.tsx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,51 @@
11
import React, {useState} from 'react';
22
import ReactFastPDF, {PDFPreviewer} from 'react-fast-pdf';
3+
import type {RotationDegrees} from 'react-fast-pdf';
34
import './index.css';
45

56
function App() {
67
const [file, setFile] = useState<string | null>(null);
8+
const [rotation, setRotation] = useState<RotationDegrees>(0);
79

810
// `.default` is required when referencing the legacy CJS package.
911
const packageName = ('default' in ReactFastPDF ? (ReactFastPDF.default as {PackageName: string}) : ReactFastPDF).PackageName;
1012

13+
const handleRotate = () => {
14+
setRotation((prev) => ((prev + 90) % 360) as RotationDegrees);
15+
};
16+
1117
return (
1218
<main className="container">
1319
<h1 className="title">Hello, I am {packageName}!</h1>
1420

1521
{file ? (
1622
<>
17-
<button
18-
className="button button_back"
19-
type="button"
20-
onClick={() => setFile(null)}
21-
>
22-
Back
23-
</button>
23+
<div style={{display: 'flex', gap: '10px', marginBottom: '10px', flexWrap: 'wrap'}}>
24+
<button
25+
className="button button_back"
26+
type="button"
27+
onClick={() => {
28+
setFile(null);
29+
setRotation(0);
30+
}}
31+
>
32+
Back
33+
</button>
34+
35+
<button
36+
className="button"
37+
type="button"
38+
onClick={handleRotate}
39+
>
40+
Rotate 90° (Current: {rotation}°)
41+
</button>
42+
</div>
2443

2544
<PDFPreviewer
2645
file={file}
2746
pageMaxWidth={1000}
2847
isSmallScreen={false}
48+
rotation={rotation}
2949
/>
3050
</>
3151
) : (

src/PDFPreviewer.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import pdfWorkerSource from 'pdfjs-dist/build/pdf.worker.min.mjs';
2-
import React, {memo, useCallback, useLayoutEffect, useRef, useState} from 'react';
3-
import type {CSSProperties, ReactNode} from 'react';
4-
import times from 'lodash/times.js';
2+
import React, {useCallback, useLayoutEffect, useRef, useState} from 'react';
3+
import type {CSSProperties, ReactNode, JSX} from 'react';
4+
import {times} from 'lodash';
55
import {VariableSizeList as List} from 'react-window';
66
import {Document, pdfjs} from 'react-pdf';
77
import 'react-pdf/dist/Page/AnnotationLayer.css';
88
import 'react-pdf/dist/Page/TextLayer.css';
99

10-
import type {PDFDocument, PageViewport} from './types.js';
10+
import type {PDFDocument, PageViewport, RotationDegrees} from './types.js';
1111
import {pdfPreviewerStyles as styles} from './styles.js';
1212
import PDFPasswordForm, {type PDFPasswordFormProps} from './PDFPasswordForm.js';
1313
import PageRenderer from './PageRenderer.js';
@@ -28,6 +28,8 @@ type Props = {
2828
onLoadError?: () => void;
2929
containerStyle?: CSSProperties;
3030
contentContainerStyle?: CSSProperties;
31+
/** Rotation angle for all pages (0, 90, 180, 270 degrees) */
32+
rotation?: RotationDegrees;
3133
};
3234

3335
type OnPasswordCallback = (password: string | null) => void;
@@ -51,7 +53,8 @@ function PDFPreviewer({
5153
contentContainerStyle,
5254
shouldShowErrorComponent = true,
5355
onLoadError,
54-
}: Props) {
56+
rotation = 0,
57+
}: Props): JSX.Element {
5558
const [pageViewports, setPageViewports] = useState<PageViewport[]>([]);
5659
const [numPages, setNumPages] = useState(0);
5760
const [containerWidth, setContainerWidth] = useState(0);
@@ -103,6 +106,7 @@ function PDFPreviewer({
103106
* Calculates a proper page height. The method should be called only when there are page viewports.
104107
* It is based on a ratio between the specific page viewport width and provided page width.
105108
* Also, the app should take into account the page borders.
109+
* When rotation is 90 or 270 degrees, width and height are swapped.
106110
*/
107111
const calculatePageHeight = useCallback(
108112
(pageIndex: number) => {
@@ -112,12 +116,18 @@ function PDFPreviewer({
112116

113117
const pageWidth = calculatePageWidth();
114118

115-
const {width: pageViewportWidth, height: pageViewportHeight} = pageViewports[pageIndex];
119+
const {width: originalWidth, height: originalHeight} = pageViewports[pageIndex];
120+
121+
// Swap dimensions when rotated 90 or 270 degrees
122+
const isRotated90or270 = rotation === 90 || rotation === 270;
123+
const pageViewportWidth = isRotated90or270 ? originalHeight : originalWidth;
124+
const pageViewportHeight = isRotated90or270 ? originalWidth : originalHeight;
125+
116126
const scale = pageWidth / pageViewportWidth;
117127

118128
return pageViewportHeight * scale + PAGE_BORDER * 2;
119129
},
120-
[pageViewports, calculatePageWidth],
130+
[pageViewports, calculatePageWidth, rotation],
121131
);
122132

123133
const estimatedPageHeight = calculatePageHeight(0);
@@ -207,7 +217,7 @@ function PDFPreviewer({
207217
if (containerWidth > 0 && containerHeight > 0) {
208218
listRef.current?.resetAfterIndex(0);
209219
}
210-
}, [containerWidth, containerHeight]);
220+
}, [containerWidth, containerHeight, rotation]);
211221

212222
useLayoutEffect(() => {
213223
if (!containerRef.current) {
@@ -238,6 +248,7 @@ function PDFPreviewer({
238248
error={shouldShowErrorComponent ? ErrorComponent : null}
239249
onLoadError={onLoadError}
240250
loading={LoadingComponent}
251+
rotate={rotation}
241252
onLoadSuccess={onDocumentLoadSuccess}
242253
onPassword={initiatePasswordChallenge}
243254
>
@@ -264,6 +275,6 @@ function PDFPreviewer({
264275
);
265276
}
266277

267-
PDFPasswordForm.displayName = 'PDFPreviewer';
278+
PDFPreviewer.displayName = 'PDFPreviewer';
268279

269-
export default memo(PDFPreviewer);
280+
export default PDFPreviewer;

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import PDFPreviewer from './PDFPreviewer.js';
2+
import type {RotationDegrees} from './types.js';
23

34
const PACKAGE_NAME = 'react-fast-pdf';
45

56
export {PDFPreviewer};
7+
export type {RotationDegrees};
68

79
export default {
810
PackageName: PACKAGE_NAME,

src/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,9 @@ type ComponentStyles = {
1818
[key: string]: CSSProperties;
1919
};
2020

21-
export type {PDFDocument, PageViewport, ComponentStyles};
21+
/**
22+
* Valid rotation angles for PDF pages (in degrees clockwise)
23+
*/
24+
type RotationDegrees = 0 | 90 | 180 | 270;
25+
26+
export type {PDFDocument, PageViewport, ComponentStyles, RotationDegrees};

0 commit comments

Comments
 (0)