From 54e31bfe8adf4bd5da9ed7dd2dbc71a56bec6576 Mon Sep 17 00:00:00 2001 From: Reece Date: Tue, 25 Nov 2025 14:44:18 +0000 Subject: [PATCH] Handle non-pdf gracefully in viewer --- .../core/components/viewer/LocalEmbedPDF.tsx | 22 +++++ frontend/src/core/utils/fileUtils.test.ts | 87 +++++++++++++++++++ frontend/src/core/utils/fileUtils.ts | 23 +++++ 3 files changed, 132 insertions(+) create mode 100644 frontend/src/core/utils/fileUtils.test.ts diff --git a/frontend/src/core/components/viewer/LocalEmbedPDF.tsx b/frontend/src/core/components/viewer/LocalEmbedPDF.tsx index c08eabcbb7..5ba5e3d283 100644 --- a/frontend/src/core/components/viewer/LocalEmbedPDF.tsx +++ b/frontend/src/core/components/viewer/LocalEmbedPDF.tsx @@ -41,6 +41,7 @@ import { HistoryAPIBridge } from '@app/components/viewer/HistoryAPIBridge'; import type { SignatureAPI, HistoryAPI } from '@app/components/viewer/viewerTypes'; import { ExportAPIBridge } from '@app/components/viewer/ExportAPIBridge'; import { BookmarkAPIBridge } from '@app/components/viewer/BookmarkAPIBridge'; +import { isPdfFile } from '@app/utils/fileUtils'; interface LocalEmbedPDFProps { file?: File | Blob; @@ -171,6 +172,27 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur ); } + // Check if the file is actually a PDF + if (file && !isPdfFile(file)) { + const fileName = 'name' in file ? file.name : 'Unknown file'; + return ( +
+ +
📄
+ + Cannot Preview File + + + The viewer only supports PDF files. This file appears to be a different format. + + + {fileName} + +
+
+ ); + } + if (isLoading || !engine || !pdfUrl) { return ; } diff --git a/frontend/src/core/utils/fileUtils.test.ts b/frontend/src/core/utils/fileUtils.test.ts new file mode 100644 index 0000000000..b833c5f6d5 --- /dev/null +++ b/frontend/src/core/utils/fileUtils.test.ts @@ -0,0 +1,87 @@ +import { describe, it, expect } from 'vitest'; +import { isPdfFile, detectFileExtension, formatFileSize } from './fileUtils'; + +describe('fileUtils', () => { + describe('isPdfFile', () => { + it('should return true for PDF files with correct MIME type', () => { + const pdfFile = new File(['content'], 'document.pdf', { type: 'application/pdf' }); + expect(isPdfFile(pdfFile)).toBe(true); + }); + + it('should return true for PDF files with .pdf extension even without MIME type', () => { + const pdfFile = new File(['content'], 'document.pdf', { type: '' }); + expect(isPdfFile(pdfFile)).toBe(true); + }); + + it('should return false for non-PDF files', () => { + const txtFile = new File(['content'], 'document.txt', { type: 'text/plain' }); + expect(isPdfFile(txtFile)).toBe(false); + }); + + it('should return false for image files', () => { + const imageFile = new File(['content'], 'image.png', { type: 'image/png' }); + expect(isPdfFile(imageFile)).toBe(false); + }); + + it('should return false for null', () => { + expect(isPdfFile(null)).toBe(false); + }); + + it('should return false for undefined', () => { + expect(isPdfFile(undefined)).toBe(false); + }); + + it('should handle file-like objects with name and type', () => { + const fileLike = { name: 'test.pdf', type: 'application/pdf' }; + expect(isPdfFile(fileLike)).toBe(true); + }); + + it('should handle file-like objects with PDF extension but no type', () => { + const fileLike = { name: 'test.pdf', type: '' }; + expect(isPdfFile(fileLike)).toBe(true); + }); + }); + + describe('detectFileExtension', () => { + it('should detect PDF extension', () => { + expect(detectFileExtension('document.pdf')).toBe('pdf'); + }); + + it('should detect extension in uppercase', () => { + expect(detectFileExtension('document.PDF')).toBe('pdf'); + }); + + it('should return empty string for files without extension', () => { + expect(detectFileExtension('document')).toBe(''); + }); + + it('should handle multiple dots in filename', () => { + expect(detectFileExtension('my.document.pdf')).toBe('pdf'); + }); + + it('should normalize jpeg to jpg', () => { + expect(detectFileExtension('image.jpeg')).toBe('jpg'); + }); + }); + + describe('formatFileSize', () => { + it('should format bytes', () => { + expect(formatFileSize(0)).toBe('0 B'); + expect(formatFileSize(500)).toBe('500 B'); + }); + + it('should format kilobytes', () => { + expect(formatFileSize(1024)).toBe('1 KB'); + expect(formatFileSize(2048)).toBe('2 KB'); + }); + + it('should format megabytes', () => { + expect(formatFileSize(1024 * 1024)).toBe('1 MB'); + expect(formatFileSize(5 * 1024 * 1024)).toBe('5 MB'); + }); + + it('should format gigabytes', () => { + expect(formatFileSize(1024 * 1024 * 1024)).toBe('1 GB'); + }); + }); +}); diff --git a/frontend/src/core/utils/fileUtils.ts b/frontend/src/core/utils/fileUtils.ts index b7e3a429ca..0f14714019 100644 --- a/frontend/src/core/utils/fileUtils.ts +++ b/frontend/src/core/utils/fileUtils.ts @@ -51,3 +51,26 @@ export function detectFileExtension(filename: string): string { return extension; } + +/** + * Checks if a file is a PDF based on extension and MIME type + * @param file - File or file-like object with name and type properties + * @returns true if the file appears to be a PDF + */ +export function isPdfFile(file: { name?: string; type?: string } | File | Blob | null | undefined): boolean { + if (!file) return false; + + const name = 'name' in file ? file.name : undefined; + const type = file.type; + + // Check MIME type first (most reliable) + if (type === 'application/pdf') return true; + + // Check file extension as fallback + if (name) { + const ext = detectFileExtension(name); + if (ext === 'pdf') return true; + } + + return false; +}