diff --git a/package-lock.json b/package-lock.json
index deb47c70ee6ee..a1796563ec465 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -93,7 +93,7 @@
"react-content-loader": "^7.0.0",
"react-dom": "19.2.0",
"react-error-boundary": "^4.0.11",
- "react-fast-pdf": "^1.0.32",
+ "react-fast-pdf": "^1.0.33",
"react-is": "^18.3.1",
"react-map-gl": "^7.1.3",
"react-native": "0.83.1",
diff --git a/package.json b/package.json
index a60aa49e5cfae..b2c7bd555eff7 100644
--- a/package.json
+++ b/package.json
@@ -156,7 +156,7 @@
"react-content-loader": "^7.0.0",
"react-dom": "19.2.0",
"react-error-boundary": "^4.0.11",
- "react-fast-pdf": "^1.0.32",
+ "react-fast-pdf": "^1.0.33",
"react-is": "^18.3.1",
"react-map-gl": "^7.1.3",
"react-native": "0.83.1",
diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx
index e8f6568f98c0b..5196a3a99c660 100644
--- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx
+++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.tsx
@@ -2,7 +2,7 @@ import React, {memo} from 'react';
import PDFView from '@components/PDFView';
import type AttachmentViewPdfProps from './types';
-function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, isUsedAsChatAttachment, onLoadError}: AttachmentViewPdfProps) {
+function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, onPress, onToggleKeyboard, onLoadComplete, style, isUsedAsChatAttachment, onLoadError, rotation}: AttachmentViewPdfProps) {
return (
);
}
diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts
index dd5bae6e9e7b7..d10e9561735f2 100644
--- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts
+++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts
@@ -1,3 +1,4 @@
+import type {RotationDegrees} from 'react-fast-pdf';
import type {StyleProp, ViewStyle} from 'react-native';
import type {AttachmentViewProps} from '..';
@@ -16,6 +17,9 @@ type AttachmentViewPdfProps = Pick;
+
+ /** Controlled rotation angle for the PDF */
+ rotation?: RotationDegrees;
};
function checkIsFileImage(source: string | number | ImageURISource | ImageURISource[], fileName: string | undefined) {
@@ -131,6 +135,7 @@ function AttachmentView({
isUploading = false,
reportID,
transaction: transactionProp,
+ rotation,
}: AttachmentViewProps) {
const icons = useMemoizedLazyExpensifyIcons(['ArrowCircleClockwise', 'Gallery']);
const [transactionFromOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`);
@@ -255,6 +260,7 @@ function AttachmentView({
style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1}
isUsedAsChatAttachment={isUsedAsChatAttachment}
onLoadError={onPDFLoadError}
+ rotation={rotation}
/>
);
diff --git a/src/components/PDFView/index.tsx b/src/components/PDFView/index.tsx
index 9242f2a1bcadf..195ee25e514ef 100644
--- a/src/components/PDFView/index.tsx
+++ b/src/components/PDFView/index.tsx
@@ -22,7 +22,7 @@ import type {PDFViewProps} from './types';
const LOADING_THUMBNAIL_HEIGHT = 250;
const LOADING_THUMBNAIL_WIDTH = 250;
-function PDFView({onToggleKeyboard, fileName, onPress, isFocused, sourceURL, style, isUsedAsChatAttachment, onLoadError}: PDFViewProps) {
+function PDFView({onToggleKeyboard, fileName, onPress, isFocused, sourceURL, style, isUsedAsChatAttachment, onLoadError, rotation}: PDFViewProps) {
const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
@@ -117,6 +117,7 @@ function PDFView({onToggleKeyboard, fileName, onPress, isFocused, sourceURL, sty
}
shouldShowErrorComponent={false}
onLoadError={onLoadError}
+ rotation={rotation}
renderPasswordForm={({isPasswordInvalid, onSubmit, onPasswordChange}) => (
@@ -291,6 +293,7 @@ function AttachmentModalBaseContent({
isWorkspaceAvatar,
maybeIcon,
onNavigate,
+ pdfRotation,
report,
reportID,
setAttachmentError,
diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types.ts b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types.ts
index 94d737d12ce92..d15d17f528093 100644
--- a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types.ts
+++ b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types.ts
@@ -1,4 +1,5 @@
import type {RefObject} from 'react';
+import type {RotationDegrees} from 'react-fast-pdf';
import type {StyleProp, View, ViewStyle} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
@@ -142,6 +143,9 @@ type AttachmentModalBaseContentProps = {
/** Extra styles to pass for the attachment view container */
attachmentViewContainerStyles?: StyleProp;
+
+ /** Controlled rotation angle for the PDF */
+ pdfRotation?: RotationDegrees;
};
export type {
diff --git a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx
index 37ba7f306c5a6..d49f2e54fdc00 100644
--- a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx
+++ b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx
@@ -1,5 +1,6 @@
import {Str} from 'expensify-common';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import type {RotationDegrees} from 'react-fast-pdf';
import {View} from 'react-native';
import Button from '@components/Button';
import ConfirmModal from '@components/ConfirmModal';
@@ -161,11 +162,13 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre
const isEReceipt = transaction && !hasReceiptSource(transaction) && hasEReceipt(transaction);
const fileName = (isOdometerImage ? odometerFilename : receiptFilename) ?? '';
const isImage = !!fileName && Str.isImage(fileName);
+ const isPDF = !!fileName && Str.isPDF(fileName);
const fileType = isOdometerImage ? odometerFileType : (transaction?.receipt?.type ?? CONST.IMAGE_FILE_FORMAT.JPEG);
const isTrackExpenseActionValue = isTrackExpenseAction(parentReportAction);
const iouType = useMemo(() => iouTypeParam ?? (isTrackExpenseActionValue ? CONST.IOU.TYPE.TRACK : CONST.IOU.TYPE.SUBMIT), [isTrackExpenseActionValue, iouTypeParam]);
const [isDeleteReceiptConfirmModalVisible, setIsDeleteReceiptConfirmModalVisible] = useState(false);
+ const [pdfRotation, setPdfRotation] = useState(0);
const [isRotating, setIsRotating] = useState(false);
const [isCropping, setIsCropping] = useState(false);
const [isCropSaving, setIsCropSaving] = useState(false);
@@ -537,6 +540,14 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre
style={styles.transactionReceiptButton}
/>
)}
+ {isPDF && !isNative && (
+