UNPKG

safeer-pdf-generator

Version:

Framework-agnostic PDF generation library with chunking, merging, S3 upload, and email delivery

214 lines 8.17 kB
import { MergeService } from '../core/MergeService.js'; import { isBuffer } from '../utils/guards.js'; import { MergeError } from '../core/errors.js'; import { noOpLogger } from '../config/defaults.js'; /** * Merge multiple PDF buffers into a single PDF */ export async function mergePdfs(pdfBuffers, options = {}) { const logger = options.logging || noOpLogger; // Validate inputs if (!Array.isArray(pdfBuffers) || pdfBuffers.length === 0) { throw new MergeError('At least one PDF buffer is required'); } // Validate all inputs are buffers for (let i = 0; i < pdfBuffers.length; i++) { if (!isBuffer(pdfBuffers[i])) { throw new MergeError(`Item at index ${i} is not a buffer`); } } const mergeService = new MergeService(logger); return await mergeService.mergePdfs(pdfBuffers, options); } /** * Merge PDF files from file paths */ export async function mergePdfFiles(filePaths, options = {}) { const { promises: fs } = await import('fs'); const logger = options.logging || noOpLogger; if (!Array.isArray(filePaths) || filePaths.length === 0) { throw new MergeError('At least one file path is required'); } logger.info(`Loading ${filePaths.length} PDF files for merging`); // Read all files const buffers = []; for (let i = 0; i < filePaths.length; i++) { const filePath = filePaths[i]; try { logger.debug(`Reading file: ${filePath}`); const buffer = await fs.readFile(filePath); buffers.push(buffer); } catch (error) { throw new MergeError(`Failed to read file at index ${i}: ${filePath}`, filePaths.length, error); } } return await mergePdfs(buffers, options); } /** * Analyze PDF buffers without merging */ export async function analyzePdfs(pdfBuffers, options = {}) { const logger = options.logging || noOpLogger; if (!Array.isArray(pdfBuffers) || pdfBuffers.length === 0) { throw new MergeError('At least one PDF buffer is required for analysis'); } const mergeService = new MergeService(logger); return await mergeService.analyzePdfs(pdfBuffers); } /** * Validate PDF buffers */ export async function validatePdfs(pdfBuffers, options = {}) { const logger = options.logging || noOpLogger; if (!Array.isArray(pdfBuffers)) { return { valid: false, errors: [{ chunkIndex: -1, error: 'Input is not an array' }], }; } if (pdfBuffers.length === 0) { return { valid: false, errors: [{ chunkIndex: -1, error: 'No PDF buffers provided' }], }; } const mergeService = new MergeService(logger); return await mergeService.validatePdfs(pdfBuffers); } /** * Split a PDF into individual page buffers */ export async function splitPdf(pdfBuffer, options = {}) { const logger = options.logging || noOpLogger; if (!isBuffer(pdfBuffer)) { throw new MergeError('Input must be a buffer'); } if (pdfBuffer.length === 0) { throw new MergeError('PDF buffer is empty'); } const mergeService = new MergeService(logger); return await mergeService.splitPdf(pdfBuffer); } /** * Extract specific pages from a PDF */ export async function extractPages(pdfBuffer, pageIndices, options = {}) { const logger = options.logging || noOpLogger; if (!isBuffer(pdfBuffer)) { throw new MergeError('Input must be a buffer'); } if (!Array.isArray(pageIndices) || pageIndices.length === 0) { throw new MergeError('Page indices must be a non-empty array'); } // Validate page indices are numbers (but allow negative/out-of-range, service will filter) for (const index of pageIndices) { if (typeof index !== 'number' || !Number.isInteger(index)) { throw new MergeError('All page indices must be integers'); } } const mergeService = new MergeService(logger); return await mergeService.extractPages(pdfBuffer, pageIndices); } /** * Get information about a PDF */ export async function getPdfInfo(pdfBuffer, options = {}) { const logger = options.logging || noOpLogger; if (!isBuffer(pdfBuffer)) { throw new MergeError('Input must be a buffer'); } if (pdfBuffer.length === 0) { throw new MergeError('PDF buffer is empty'); } const mergeService = new MergeService(logger); return await mergeService.getPdfInfo(pdfBuffer); } /** * Merge PDFs with custom page ranges */ export async function mergePdfsWithRanges(inputs, options = {}) { const logger = options.logging || noOpLogger; if (!Array.isArray(inputs) || inputs.length === 0) { throw new MergeError('At least one input is required'); } logger.info(`Merging ${inputs.length} PDFs with custom page ranges`); const extractedBuffers = []; const mergeService = new MergeService(logger); for (let i = 0; i < inputs.length; i++) { const input = inputs[i]; if (!isBuffer(input.buffer)) { throw new MergeError(`Input at index ${i} must have a buffer property`); } try { let pageIndices; if (input.pages) { // Use specific pages pageIndices = input.pages; } else if (input.startPage !== undefined || input.endPage !== undefined) { // Use page range const pdfInfo = await mergeService.getPdfInfo(input.buffer); const startPage = input.startPage || 0; const endPage = input.endPage !== undefined ? input.endPage : pdfInfo.pages - 1; pageIndices = []; for (let page = startPage; page <= Math.min(endPage, pdfInfo.pages - 1); page++) { pageIndices.push(page); } } else { // Include all pages - just add the buffer as-is extractedBuffers.push(input.buffer); continue; } // Extract the specified pages const extractedBuffer = await mergeService.extractPages(input.buffer, pageIndices); extractedBuffers.push(extractedBuffer); logger.debug(`Processed input ${i + 1}: extracted ${pageIndices.length} pages`); } catch (error) { throw new MergeError(`Failed to process input at index ${i}`, inputs.length, error); } } // Merge all extracted buffers return await mergePdfs(extractedBuffers, options); } /** * Batch merge multiple sets of PDFs */ export async function batchMergePdfs(batches, options = {}) { const logger = options.logging || noOpLogger; const concurrency = options.concurrency || 2; if (!Array.isArray(batches) || batches.length === 0) { throw new MergeError('At least one batch is required'); } logger.info(`Batch merging ${batches.length} PDF sets with concurrency ${concurrency}`); const results = []; let currentIndex = 0; const processBatch = async () => { while (currentIndex < batches.length) { const index = currentIndex++; if (index >= batches.length) return; const batch = batches[index]; const batchName = options.batchNames?.[index] || `batch-${index + 1}`; try { logger.debug(`Processing ${batchName} with ${batch.length} PDFs`); const mergedBuffer = await mergePdfs(batch, options); results[index] = mergedBuffer; logger.debug(`Completed ${batchName}`); } catch (error) { throw new MergeError(`Failed to merge ${batchName}`, batches.length, error); } } }; // Create worker promises with concurrency limit const workers = Array(Math.min(concurrency, batches.length)) .fill(null) .map(() => processBatch()); await Promise.all(workers); logger.info(`Batch merge completed: ${results.length} merged PDFs`); return results; } //# sourceMappingURL=mergePdfs.js.map