Stirling 2.0 (#3928)

# Description of Changes

<!--

File context for managing files between tools and views
Optimisation for large files
Updated Split to work with new file system and match Matts stepped
design closer

-->

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.

---------

Co-authored-by: Anthony Stirling <[email protected]>
This commit is contained in:
Reece Browne
2025-07-16 17:53:50 +01:00
committed by GitHub
co-authored by Anthony Stirling
parent 584e2ecee7
commit 922bbc9076
66 changed files with 8728 additions and 2519 deletions
+58 -2
View File
@@ -1,5 +1,19 @@
import { getDocument } from "pdfjs-dist";
/**
* Calculate thumbnail scale based on file size
* Smaller files get higher quality, larger files get lower quality
*/
export function calculateScaleFromFileSize(fileSize: number): number {
const MB = 1024 * 1024;
if (fileSize < 1 * MB) return 0.6; // < 1MB: High quality
if (fileSize < 5 * MB) return 0.4; // 1-5MB: Medium-high quality
if (fileSize < 15 * MB) return 0.3; // 5-15MB: Medium quality
if (fileSize < 30 * MB) return 0.2; // 15-30MB: Low-medium quality
return 0.15; // 30MB+: Low quality
}
/**
* Generate thumbnail for a PDF file during upload
* Returns base64 data URL or undefined if generation fails
@@ -14,6 +28,10 @@ export async function generateThumbnailForFile(file: File): Promise<string | und
try {
console.log('Generating thumbnail for', file.name);
// Calculate quality scale based on file size
const scale = calculateScaleFromFileSize(file.size);
console.log(`Using scale ${scale} for ${file.name} (${(file.size / 1024 / 1024).toFixed(1)}MB)`);
// Only read first 2MB for thumbnail generation to save memory
const chunkSize = 2 * 1024 * 1024; // 2MB
const chunk = file.slice(0, Math.min(chunkSize, file.size));
@@ -26,7 +44,7 @@ export async function generateThumbnailForFile(file: File): Promise<string | und
}).promise;
const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale: 0.2 }); // Smaller scale for memory efficiency
const viewport = page.getViewport({ scale }); // Dynamic scale based on file size
const canvas = document.createElement("canvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
@@ -45,7 +63,45 @@ export async function generateThumbnailForFile(file: File): Promise<string | und
return thumbnail;
} catch (error) {
console.warn('Failed to generate thumbnail for', file.name, error);
if (error instanceof Error) {
if (error.name === 'InvalidPDFException') {
console.warn(`PDF structure issue for ${file.name} - using fallback thumbnail`);
// Return a placeholder or try with full file instead of chunk
try {
const fullArrayBuffer = await file.arrayBuffer();
const pdf = await getDocument({
data: fullArrayBuffer,
disableAutoFetch: true,
disableStream: true,
verbosity: 0 // Reduce PDF.js warnings
}).promise;
const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale });
const canvas = document.createElement("canvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
const context = canvas.getContext("2d");
if (!context) {
throw new Error('Could not get canvas context');
}
await page.render({ canvasContext: context, viewport }).promise;
const thumbnail = canvas.toDataURL();
pdf.destroy();
return thumbnail;
} catch (fallbackError) {
console.warn('Fallback thumbnail generation also failed for', file.name, fallbackError);
return undefined;
}
} else {
console.warn('Failed to generate thumbnail for', file.name, error);
return undefined;
}
}
console.warn('Unknown error generating thumbnail for', file.name, error);
return undefined;
}
}