- 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
178 lines
5.2 KiB
TypeScript
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;
|
|
}
|
|
} |