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
This commit is contained in:
178
ts/smartpreview.ts
Normal file
178
ts/smartpreview.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user