- Add Node.js implementation using @push.rocks/smartpdf - Add browser implementation with PDF.js and Web Workers - Support configurable quality, dimensions, and page selection - Include comprehensive TypeScript definitions and error handling - Provide extensive test coverage for both environments - Add download functionality and browser compatibility checking
467 lines
13 KiB
Markdown
467 lines
13 KiB
Markdown
# @push.rocks/smartpreview 🖼️
|
|
|
|
> **Lightning-fast PDF to JPEG preview generation for Node.js and browsers** ⚡
|
|
|
|
Transform your PDFs into beautiful JPEG previews with zero hassle. Whether you're building a document viewer, thumbnail generator, or file management system, SmartPreview delivers high-quality results in both server and browser environments.
|
|
|
|
[](https://www.npmjs.com/package/@push.rocks/smartpreview)
|
|
|
|
## ✨ Features
|
|
|
|
- 🌐 **Universal**: Works seamlessly in Node.js and browsers
|
|
- ⚡ **Fast**: Web Worker-based processing for non-blocking operations
|
|
- 🎯 **Precise**: Configurable quality, dimensions, and page selection
|
|
- 🛡️ **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
- 🔧 **Extensible**: Built to support additional formats in the future
|
|
- 📦 **Zero Config**: Works out of the box with sensible defaults
|
|
- 🎨 **High Quality**: Professional-grade JPEG output with customizable compression
|
|
|
|
## 🚀 Quick Start
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
# Using pnpm (recommended)
|
|
pnpm install @push.rocks/smartpreview
|
|
|
|
# Using npm
|
|
npm install @push.rocks/smartpreview
|
|
|
|
# Using yarn
|
|
yarn add @push.rocks/smartpreview
|
|
```
|
|
|
|
### Node.js Usage
|
|
|
|
Perfect for server-side processing, APIs, and build tools:
|
|
|
|
```typescript
|
|
import { SmartPreview } from '@push.rocks/smartpreview';
|
|
|
|
// Initialize the preview generator
|
|
const preview = await SmartPreview.create();
|
|
|
|
// Generate preview from PDF buffer
|
|
const result = await preview.generatePreview(pdfBuffer, {
|
|
quality: 85,
|
|
width: 1200,
|
|
height: 800,
|
|
page: 1,
|
|
});
|
|
|
|
// result.buffer contains the JPEG data
|
|
console.log(
|
|
`Generated ${result.size} byte preview: ${result.dimensions.width}x${result.dimensions.height}`
|
|
);
|
|
|
|
// Don't forget to cleanup
|
|
await preview.cleanup();
|
|
```
|
|
|
|
### Browser Usage
|
|
|
|
Ideal for web applications, file uploads, and client-side processing:
|
|
|
|
```typescript
|
|
import { SmartPreview } from '@push.rocks/smartpreview/web';
|
|
|
|
// Initialize with progress tracking
|
|
const preview = await SmartPreview.create();
|
|
|
|
// Generate preview from file input
|
|
const fileInput = document.querySelector('input[type="file"]');
|
|
const file = fileInput.files[0];
|
|
|
|
const result = await preview.generatePreview(file, {
|
|
quality: 80,
|
|
width: 800,
|
|
height: 600,
|
|
onProgress: (progress, stage) => {
|
|
console.log(`${stage}: ${progress}%`);
|
|
},
|
|
});
|
|
|
|
// Use the result
|
|
const img = document.createElement('img');
|
|
img.src = result.dataUrl; // Ready-to-use data URL
|
|
document.body.appendChild(img);
|
|
|
|
// Or create a download link
|
|
const { url, cleanup } = await preview.createDownloadLink(file, {}, 'preview.jpg');
|
|
// Use url for download, call cleanup() when done
|
|
```
|
|
|
|
## 📚 API Reference
|
|
|
|
### Node.js API
|
|
|
|
#### `SmartPreview`
|
|
|
|
The main class for Node.js environments.
|
|
|
|
##### Methods
|
|
|
|
```typescript
|
|
// Factory method (recommended)
|
|
static async create(options?: IPreviewOptions): Promise<SmartPreview>
|
|
|
|
// Manual initialization
|
|
async init(): Promise<void>
|
|
async cleanup(): Promise<void>
|
|
|
|
// Preview generation
|
|
async generatePreview(buffer: Buffer, options?: IPreviewOptions): Promise<IPreviewResult>
|
|
async generatePreviewFromFile(filePath: string, options?: IPreviewOptions): Promise<IPreviewResult>
|
|
|
|
// Save directly to file
|
|
async savePreview(buffer: Buffer, outputPath: string, options?: IPreviewOptions): Promise<void>
|
|
|
|
// Utility methods
|
|
getSupportedFormats(): string[]
|
|
isFormatSupported(format: string): boolean
|
|
```
|
|
|
|
##### Node.js Result
|
|
|
|
```typescript
|
|
interface IPreviewResult {
|
|
buffer: Buffer; // JPEG image data
|
|
dimensions: {
|
|
// Actual dimensions
|
|
width: number;
|
|
height: number;
|
|
};
|
|
size: number; // File size in bytes
|
|
mimeType: 'image/jpeg'; // Always JPEG
|
|
}
|
|
```
|
|
|
|
### Browser API
|
|
|
|
#### `SmartPreview`
|
|
|
|
The main class for browser environments with additional web-specific features.
|
|
|
|
##### Methods
|
|
|
|
```typescript
|
|
// Factory method (recommended)
|
|
static async create(options?: IWebPreviewOptions): Promise<SmartPreview>
|
|
|
|
// Manual initialization
|
|
async init(): Promise<void>
|
|
async cleanup(): Promise<void>
|
|
|
|
// Preview generation
|
|
async generatePreview(input: TWebInputType, options?: IWebPreviewOptions): Promise<IPreviewResult>
|
|
async generatePreviewFromFile(file: File, options?: IWebPreviewOptions): Promise<IPreviewResult>
|
|
async generatePreviewFromUrl(url: string, options?: IWebPreviewOptions): Promise<IPreviewResult>
|
|
|
|
// Download functionality
|
|
async createDownloadLink(input: TWebInputType, options?: IWebPreviewOptions, filename?: string): Promise<{url: string, cleanup: () => void}>
|
|
async downloadPreview(input: TWebInputType, options?: IWebPreviewOptions, filename?: string): Promise<void>
|
|
|
|
// Browser compatibility
|
|
static getBrowserCompatibility(): {fileApi: boolean, webWorkers: boolean, offscreenCanvas: boolean, isSupported: boolean}
|
|
static isFileApiSupported(): boolean
|
|
static isWebWorkerSupported(): boolean
|
|
static isOffscreenCanvasSupported(): boolean
|
|
```
|
|
|
|
##### Web Input Types
|
|
|
|
```typescript
|
|
type TWebInputType = File | Blob | ArrayBuffer | Uint8Array | string; // string = data URL
|
|
```
|
|
|
|
##### Web Result
|
|
|
|
```typescript
|
|
interface IPreviewResult {
|
|
blob: Blob; // JPEG image blob
|
|
dimensions: {
|
|
// Actual dimensions
|
|
width: number;
|
|
height: number;
|
|
};
|
|
size: number; // File size in bytes
|
|
mimeType: 'image/jpeg'; // Always JPEG
|
|
dataUrl: string; // Ready-to-use data URL (optional)
|
|
}
|
|
```
|
|
|
|
### Configuration Options
|
|
|
|
#### Basic Options (`IPreviewOptions`)
|
|
|
|
```typescript
|
|
interface IPreviewOptions {
|
|
quality?: number; // JPEG quality 1-100 (default: 80)
|
|
width?: number; // Max width in pixels (default: 800)
|
|
height?: number; // Max height in pixels (default: 600)
|
|
page?: number; // PDF page number 1-based (default: 1)
|
|
scale?: number; // Scale factor (default: 1.0)
|
|
}
|
|
```
|
|
|
|
#### Web-Specific Options (`IWebPreviewOptions`)
|
|
|
|
```typescript
|
|
interface IWebPreviewOptions extends IPreviewOptions {
|
|
onProgress?: (progress: number, stage: string) => void; // Progress callback
|
|
timeout?: number; // Worker timeout in ms (default: 30000)
|
|
generateDataUrl?: boolean; // Generate data URL (default: true)
|
|
}
|
|
```
|
|
|
|
## 🎯 Advanced Examples
|
|
|
|
### Server-Side Batch Processing
|
|
|
|
```typescript
|
|
import { SmartPreview } from '@push.rocks/smartpreview';
|
|
import { promises as fs } from 'fs';
|
|
import path from 'path';
|
|
|
|
async function batchProcess(inputDir: string, outputDir: string) {
|
|
const preview = await SmartPreview.create();
|
|
|
|
try {
|
|
const files = await fs.readdir(inputDir);
|
|
const pdfFiles = files.filter((f) => f.endsWith('.pdf'));
|
|
|
|
for (const file of pdfFiles) {
|
|
const inputPath = path.join(inputDir, file);
|
|
const outputPath = path.join(outputDir, file.replace('.pdf', '.jpg'));
|
|
|
|
await preview.savePreview(await fs.readFile(inputPath), outputPath, {
|
|
quality: 90,
|
|
width: 1920,
|
|
height: 1080,
|
|
});
|
|
|
|
console.log(`✅ Processed: ${file}`);
|
|
}
|
|
} finally {
|
|
await preview.cleanup();
|
|
}
|
|
}
|
|
```
|
|
|
|
### Interactive File Upload
|
|
|
|
```typescript
|
|
import { SmartPreview } from '@push.rocks/smartpreview/web';
|
|
|
|
class FilePreviewHandler {
|
|
private preview: SmartPreview | null = null;
|
|
|
|
async init() {
|
|
// Check browser compatibility first
|
|
const compat = SmartPreview.getBrowserCompatibility();
|
|
if (!compat.isSupported) {
|
|
throw new Error('Browser not supported');
|
|
}
|
|
|
|
this.preview = await SmartPreview.create();
|
|
}
|
|
|
|
async handleFileUpload(file: File): Promise<string> {
|
|
if (!this.preview) throw new Error('Not initialized');
|
|
|
|
// Generate preview with progress tracking
|
|
const result = await this.preview.generatePreview(file, {
|
|
quality: 85,
|
|
width: 400,
|
|
height: 300,
|
|
onProgress: (progress, stage) => {
|
|
this.updateProgress(progress, stage);
|
|
},
|
|
});
|
|
|
|
return result.dataUrl;
|
|
}
|
|
|
|
private updateProgress(progress: number, stage: string) {
|
|
console.log(`${stage}: ${Math.round(progress)}%`);
|
|
// Update your UI here
|
|
}
|
|
|
|
async cleanup() {
|
|
if (this.preview) {
|
|
await this.preview.cleanup();
|
|
this.preview = null;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Multi-Page Preview Generation
|
|
|
|
```typescript
|
|
import { SmartPreview } from '@push.rocks/smartpreview';
|
|
|
|
async function generateMultiPagePreviews(pdfBuffer: Buffer, maxPages: number = 5) {
|
|
const preview = await SmartPreview.create();
|
|
const previews: Buffer[] = [];
|
|
|
|
try {
|
|
for (let page = 1; page <= maxPages; page++) {
|
|
try {
|
|
const result = await preview.generatePreview(pdfBuffer, {
|
|
page,
|
|
quality: 80,
|
|
width: 600,
|
|
height: 800,
|
|
});
|
|
|
|
previews.push(result.buffer);
|
|
console.log(
|
|
`📄 Generated preview for page ${page}: ${result.dimensions.width}x${result.dimensions.height}`
|
|
);
|
|
} catch (error) {
|
|
// Page doesn't exist, stop here
|
|
if (error.errorType === 'PAGE_NOT_FOUND') {
|
|
break;
|
|
}
|
|
throw error; // Re-throw other errors
|
|
}
|
|
}
|
|
} finally {
|
|
await preview.cleanup();
|
|
}
|
|
|
|
return previews;
|
|
}
|
|
```
|
|
|
|
## 🛠️ Development
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# Clone the repository
|
|
git clone https://code.foss.global/push.rocks/smartpreview.git
|
|
cd smartpreview
|
|
|
|
# Install dependencies
|
|
pnpm install
|
|
|
|
# Build the project
|
|
pnpm run build
|
|
|
|
# Run tests
|
|
pnpm test
|
|
```
|
|
|
|
### Testing
|
|
|
|
The project includes comprehensive tests for both Node.js and browser environments:
|
|
|
|
```bash
|
|
# Run all tests
|
|
pnpm test
|
|
|
|
# Run only Node.js tests
|
|
pnpm tstest test/test.node.ts
|
|
|
|
# Run only browser tests
|
|
pnpm tstest test/test.browser.ts --web
|
|
```
|
|
|
|
### Architecture
|
|
|
|
The library follows a dual-environment architecture:
|
|
|
|
```
|
|
📁 smartpreview/
|
|
├── 📁 ts/ # Node.js implementation
|
|
│ ├── smartpreview.ts # Main Node.js class
|
|
│ ├── pdfprocessor.ts # PDF processing with @push.rocks/smartpdf
|
|
│ └── interfaces.ts # Shared interfaces
|
|
├── 📁 ts_web/ # Browser implementation
|
|
│ ├── smartpreview.ts # Main browser class
|
|
│ ├── pdfprocessor.ts # Web PDF processor
|
|
│ ├── pdfworker.ts # PDF.js worker wrapper
|
|
│ └── interfaces.ts # Web-specific interfaces
|
|
└── 📁 test/ # Comprehensive test suite
|
|
```
|
|
|
|
## 🔧 Error Handling
|
|
|
|
SmartPreview provides detailed error information through typed errors:
|
|
|
|
```typescript
|
|
import { PreviewError } from '@push.rocks/smartpreview';
|
|
|
|
try {
|
|
const result = await preview.generatePreview(invalidPdf);
|
|
} catch (error) {
|
|
if (error instanceof PreviewError) {
|
|
switch (error.errorType) {
|
|
case 'PDF_CORRUPTED':
|
|
console.error('The PDF file is corrupted');
|
|
break;
|
|
case 'PAGE_NOT_FOUND':
|
|
console.error('Requested page does not exist');
|
|
break;
|
|
case 'INVALID_OPTIONS':
|
|
console.error('Invalid configuration options');
|
|
break;
|
|
default:
|
|
console.error(`Preview error: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error Types
|
|
|
|
- `INVALID_INPUT` - Input data is invalid or missing
|
|
- `UNSUPPORTED_FORMAT` - File format not supported
|
|
- `PROCESSING_FAILED` - General processing error
|
|
- `INVALID_OPTIONS` - Configuration options are invalid
|
|
- `PDF_CORRUPTED` - PDF file is corrupted or invalid
|
|
- `PAGE_NOT_FOUND` - Requested page doesn't exist
|
|
- `WORKER_ERROR` - Web worker error (browser only)
|
|
- `WORKER_TIMEOUT` - Worker timeout (browser only)
|
|
|
|
## 🌟 Why SmartPreview?
|
|
|
|
- **🚀 Performance**: Optimized for speed with worker-based processing
|
|
- **💪 Reliable**: Battle-tested with comprehensive error handling
|
|
- **🔒 Type-Safe**: Full TypeScript support prevents runtime errors
|
|
- **🌐 Universal**: One API works everywhere - Node.js, browsers, edge functions
|
|
- **🎨 Quality**: Professional-grade output with fine-tuned compression
|
|
- **📈 Scalable**: Built for high-volume production use
|
|
- **🔮 Future-Proof**: Extensible architecture ready for new formats
|
|
|
|
## 🤝 Contributing
|
|
|
|
We welcome contributions! Please see our [contribution guidelines](CONTRIBUTING.md) for details.
|
|
|
|
## 📄 Changelog
|
|
|
|
See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
|
|
|
|
---
|
|
|
|
## License and Legal Information
|
|
|
|
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
|
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
|
|
### Trademarks
|
|
|
|
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
|
|
|
### Company Information
|
|
|
|
Task Venture Capital GmbH
|
|
Registered at District court Bremen HRB 35230 HB, Germany
|
|
|
|
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
|
|
|
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|