Files
smartpreview/ts/smartpreview.ts
Juergen Kunz bc1c7edd35 feat(initial): add comprehensive PDF to JPEG preview library with dual-environment support
- 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
2025-08-03 21:44:01 +00:00

178 lines
5.2 KiB
TypeScript

import * as plugins from './plugins.js';
import type { IPreviewOptions, IPreviewResult, TSupportedInputFormat } from './interfaces.js';
import { PreviewError } from './interfaces.js';
import { PdfProcessor } from './pdfprocessor.js';
/**
* Main SmartPreview class for Node.js environment
* Provides unified API for generating previews from various document formats
*/
export class SmartPreview {
private pdfProcessor: PdfProcessor | null = null;
private isInitialized = false;
/**
* Create a new SmartPreview instance
*/
constructor() {
// Constructor kept minimal following async initialization pattern
}
/**
* Initialize the SmartPreview instance and all processors
*/
public async init(): Promise<void> {
if (this.isInitialized) {
return;
}
try {
// Initialize PDF processor
this.pdfProcessor = new PdfProcessor();
await this.pdfProcessor.init();
this.isInitialized = true;
} catch (error) {
await this.cleanup(); // Cleanup on initialization failure
throw new PreviewError(
'PROCESSING_FAILED',
'Failed to initialize SmartPreview',
error instanceof Error ? error : new Error(String(error))
);
}
}
/**
* Generate preview from buffer
* @param input - Buffer containing the document data
* @param options - Preview generation options
* @returns Promise resolving to preview result
*/
public async generatePreview(input: Buffer, options: IPreviewOptions = {}): Promise<IPreviewResult> {
if (!this.isInitialized) {
throw new PreviewError('PROCESSING_FAILED', 'SmartPreview not initialized. Call init() first.');
}
if (!input || input.length === 0) {
throw new PreviewError('INVALID_INPUT', 'Input buffer is empty or invalid');
}
// Detect format and route to appropriate processor
const format = await this.detectFormat(input);
switch (format) {
case 'pdf':
if (!this.pdfProcessor) {
throw new PreviewError('PROCESSING_FAILED', 'PDF processor not available');
}
return await this.pdfProcessor.processPreview(input, options);
default:
throw new PreviewError('UNSUPPORTED_FORMAT', `Format '${format}' is not supported`);
}
}
/**
* Generate preview from file path
* @param filePath - Path to the document file
* @param options - Preview generation options
* @returns Promise resolving to preview result
*/
public async generatePreviewFromFile(filePath: string, options: IPreviewOptions = {}): Promise<IPreviewResult> {
if (!plugins.fs.existsSync(filePath)) {
throw new PreviewError('INVALID_INPUT', `File not found: ${filePath}`);
}
try {
const buffer = await plugins.fs.promises.readFile(filePath);
return await this.generatePreview(buffer, options);
} catch (error) {
if (error instanceof PreviewError) {
throw error;
}
throw new PreviewError(
'PROCESSING_FAILED',
`Failed to read file: ${filePath}`,
error instanceof Error ? error : new Error(String(error))
);
}
}
/**
* Save preview to file
* @param input - Buffer containing the document data
* @param outputPath - Path where the preview should be saved
* @param options - Preview generation options
*/
public async savePreview(input: Buffer, outputPath: string, options: IPreviewOptions = {}): Promise<void> {
const result = await this.generatePreview(input, options);
try {
// Ensure output directory exists
const outputDir = plugins.path.dirname(outputPath);
await plugins.fs.promises.mkdir(outputDir, { recursive: true });
// Write preview to file
await plugins.fs.promises.writeFile(outputPath, result.buffer);
} catch (error) {
throw new PreviewError(
'PROCESSING_FAILED',
`Failed to save preview to: ${outputPath}`,
error instanceof Error ? error : new Error(String(error))
);
}
}
/**
* Get supported input formats
*/
public getSupportedFormats(): TSupportedInputFormat[] {
return ['pdf'];
}
/**
* Check if a format is supported
*/
public isFormatSupported(format: string): format is TSupportedInputFormat {
return this.getSupportedFormats().includes(format as TSupportedInputFormat);
}
/**
* Clean up resources
*/
public async cleanup(): Promise<void> {
if (this.pdfProcessor) {
await this.pdfProcessor.cleanup();
this.pdfProcessor = null;
}
this.isInitialized = false;
}
/**
* Detect document format from buffer
* @private
*/
private async detectFormat(buffer: Buffer): Promise<TSupportedInputFormat> {
// Check PDF magic bytes
if (buffer.length >= 4) {
const header = buffer.subarray(0, 4);
if (header.equals(Buffer.from('%PDF'))) {
return 'pdf';
}
}
// Future format detection can be added here
// Example: JPEG, PNG, TIFF, etc.
throw new PreviewError('UNSUPPORTED_FORMAT', 'Unable to detect supported format from input');
}
/**
* Static factory method for convenient instantiation
*/
public static async create(options: IPreviewOptions = {}): Promise<SmartPreview> {
const instance = new SmartPreview();
await instance.init();
return instance;
}
}