import * as plugins from './plugins.js'; import type { IPdfProcessor, IPreviewOptions, IPreviewResult } from './interfaces.js'; import { PreviewError } from './interfaces.js'; /** * PDF processor implementation using @push.rocks/smartpdf */ export class PdfProcessor implements IPdfProcessor { public readonly inputFormat = 'pdf' as const; public readonly outputFormat = 'jpeg' as const; private smartPdf: plugins.SmartPdf | null = null; /** * Initialize the PDF processor */ public async init(): Promise { try { this.smartPdf = await plugins.SmartPdf.create(); await this.smartPdf.start(); } catch (error) { throw new PreviewError( 'PROCESSING_FAILED', 'Failed to initialize PDF processor', error instanceof Error ? error : new Error(String(error)) ); } } /** * Clean up resources */ public async cleanup(): Promise { if (this.smartPdf) { try { await this.smartPdf.stop(); this.smartPdf = null; } catch (error) { console.warn('Warning: Failed to cleanly stop SmartPdf instance:', error); } } } /** * Process PDF and generate JPEG preview */ public async processPreview(input: Buffer, options: IPreviewOptions): Promise { if (!this.smartPdf) { throw new PreviewError('PROCESSING_FAILED', 'PDF processor not initialized'); } if (!input || input.length === 0) { throw new PreviewError('INVALID_INPUT', 'Input buffer is empty or invalid'); } try { // Validate PDF buffer await this.validatePdfBuffer(input); // Set default options const processOptions = { quality: options.quality ?? 80, width: options.width ?? 800, height: options.height ?? 600, page: options.page ?? 1, scale: options.scale ?? 1.0, }; // Validate options this.validateOptions(processOptions); // Generate JPEG from PDF using SmartPdf // Note: This is a placeholder implementation // TODO: Implement actual PDF to JPEG conversion using the correct SmartPdf API // For development purposes, create a mock JPEG buffer const buffer = Buffer.from('JPEG placeholder - implement PDF to JPEG conversion'); // In a real implementation, this would use SmartPdf to convert PDF to image // await this.smartPdf.convertToImage(input, options); return { buffer, dimensions: { width: processOptions.width, height: processOptions.height, }, size: buffer.length, mimeType: 'image/jpeg', }; } catch (error) { if (error instanceof PreviewError) { throw error; } // Handle specific SmartPdf errors if (error instanceof Error) { if (error.message.includes('invalid PDF')) { throw new PreviewError('PDF_CORRUPTED', 'Invalid or corrupted PDF file', error); } if (error.message.includes('page not found')) { throw new PreviewError('PAGE_NOT_FOUND', `Page ${options.page} not found in PDF`, error); } } throw new PreviewError( 'PROCESSING_FAILED', 'Unexpected error during PDF processing', error instanceof Error ? error : new Error(String(error)) ); } } /** * Validate PDF buffer format */ private async validatePdfBuffer(buffer: Buffer): Promise { // Check PDF magic bytes const pdfHeader = buffer.subarray(0, 4); if (!pdfHeader.equals(Buffer.from('%PDF'))) { throw new PreviewError('INVALID_INPUT', 'Input is not a valid PDF file'); } } /** * Validate processing options */ private validateOptions(options: Required): void { if (options.quality < 1 || options.quality > 100) { throw new PreviewError('INVALID_OPTIONS', 'Quality must be between 1 and 100'); } if (options.width <= 0 || options.height <= 0) { throw new PreviewError('INVALID_OPTIONS', 'Width and height must be positive numbers'); } if (options.page < 1) { throw new PreviewError('INVALID_OPTIONS', 'Page number must be 1 or greater'); } if (options.scale <= 0) { throw new PreviewError('INVALID_OPTIONS', 'Scale must be a positive number'); } } }