diff --git a/src/js/config/pdf-tools.ts b/src/js/config/pdf-tools.ts index 56d0cd32..c45b2ae0 100644 --- a/src/js/config/pdf-tools.ts +++ b/src/js/config/pdf-tools.ts @@ -6,7 +6,6 @@ export const singlePdfLoadTools = [ 'pdf-to-jpg', 'pdf-to-png', 'pdf-to-webp', - 'compress', 'pdf-to-greyscale', 'edit-metadata', 'remove-metadata', @@ -63,4 +62,5 @@ export const multiFileTools = [ 'alternate-merge', 'linearize', 'reverse-pages', + 'compress' ]; diff --git a/src/js/handlers/fileHandler.ts b/src/js/handlers/fileHandler.ts index 3c044137..cde50ee5 100644 --- a/src/js/handlers/fileHandler.ts +++ b/src/js/handlers/fileHandler.ts @@ -440,11 +440,7 @@ async function handleSinglePdfUpload(toolId, file) { } async function handleMultiFileUpload(toolId) { - if ( - toolId === 'merge' || - toolId === 'alternate-merge' || - toolId === 'reverse-pages' - ) { + if (toolId === 'merge' || toolId === 'alternate-merge' || toolId === 'reverse-pages' || toolId === 'compress') { const pdfFilesUnloaded: File[] = []; state.files.forEach((file) => { @@ -496,7 +492,12 @@ async function handleMultiFileUpload(toolId) { processBtn.onclick = func; } } - + if (toolId === 'compress') { + const optionsDiv = document.getElementById('compress-options'); + if (optionsDiv) { + optionsDiv.classList.remove('hidden'); + } + } if (toolId === 'merge') { toolLogic.merge.setup(); } else if (toolId === 'alternate-merge') { diff --git a/src/js/logic/compress.ts b/src/js/logic/compress.ts index 7db64b9b..7fdf3958 100644 --- a/src/js/logic/compress.ts +++ b/src/js/logic/compress.ts @@ -6,7 +6,7 @@ import { } from '../utils/helpers.js'; import { state } from '../state.js'; import * as pdfjsLib from 'pdfjs-dist'; - +import JSZip from 'jszip'; import { PDFDocument, PDFName, PDFDict, PDFStream, PDFNumber } from 'pdf-lib'; function dataUrlToBytes(dataUrl: any) { @@ -270,68 +270,89 @@ export async function compress() { const smartSettings = { ...settings[level].smart, removeMetadata: true }; const legacySettings = settings[level].legacy; + const pdfFiles = state.files.filter((f) => f.type === 'application/pdf'); + if (!pdfFiles.length) { + showAlert('Error', 'No PDF files found to compress.'); + return; + } + showLoader(`Compressing ${pdfFiles.length} PDF(s)...`); + + const zip = new JSZip(); + let totalOriginal = 0; + let totalCompressed = 0; try { - const originalFile = state.files[0]; - const arrayBuffer = await readFileAsArrayBuffer(originalFile); + for (let i = 0; i < pdfFiles.length; i++) { + const originalFile = pdfFiles[i]; + const arrayBuffer = await readFileAsArrayBuffer(originalFile); - let resultBytes; - let usedMethod; + let resultBytes; + let usedMethod; - if (algorithm === 'vector') { - showLoader('Running Vector (Smart) compression...'); - resultBytes = await performSmartCompression(arrayBuffer, smartSettings); - usedMethod = 'Vector'; - } else if (algorithm === 'photon') { - showLoader('Running Photon (Rasterize) compression...'); - resultBytes = await performLegacyCompression(arrayBuffer, legacySettings); - usedMethod = 'Photon'; - } else { - showLoader('Running Automatic (Vector first)...'); - const vectorResultBytes = await performSmartCompression( - arrayBuffer, - smartSettings - ); - - if (vectorResultBytes.length < originalFile.size) { - resultBytes = vectorResultBytes; - usedMethod = 'Vector (Automatic)'; + if (algorithm === 'vector') { + showLoader('Running Vector (Smart) compression...'); + resultBytes = await performSmartCompression(arrayBuffer, smartSettings); + usedMethod = 'Vector'; + } else if (algorithm === 'photon') { + showLoader('Running Photon (Rasterize) compression...'); + resultBytes = await performLegacyCompression(arrayBuffer, legacySettings); + usedMethod = 'Photon'; } else { - showAlert('Vector failed to reduce size. Trying Photon...', 'info'); - showLoader('Running Automatic (Photon fallback)...'); - resultBytes = await performLegacyCompression( + showLoader('Running Automatic (Vector first)...'); + const vectorResultBytes = await performSmartCompression( arrayBuffer, - legacySettings + smartSettings ); - usedMethod = 'Photon (Automatic)'; + + if (vectorResultBytes.length < originalFile.size) { + resultBytes = vectorResultBytes; + usedMethod = 'Vector (Automatic)'; + } else { + showAlert('Vector failed to reduce size. Trying Photon...', 'info'); + showLoader('Running Automatic (Photon fallback)...'); + resultBytes = await performLegacyCompression( + arrayBuffer, + legacySettings + ); + usedMethod = 'Photon (Automatic)'; + } } + + const originalSize = formatBytes(originalFile.size); + const compressedSize = formatBytes(resultBytes.length); + const savings = originalFile.size - resultBytes.length; + const savingsPercent = + savings > 0 ? ((savings / originalFile.size) * 100).toFixed(1) : 0; + + totalOriginal += originalFile.size; + totalCompressed += resultBytes.length; + + if (savings > 0) { + showAlert( + 'Compression Complete', + `Method: **${usedMethod}**. ` + + `File size reduced from ${originalSize} to ${compressedSize} (Saved ${savingsPercent}%).` + ); + } else { + showAlert( + 'Compression Finished', + `Method: **${usedMethod}**. ` + + `Could not reduce file size. Original: ${originalSize}, New: ${compressedSize}.`, + // @ts-expect-error TS(2554) FIXME: Expected 2 arguments, but got 3. + 'warning' + ); + } + zip.file(`compressed-${originalFile.name}`, resultBytes); } + const zipBlob = await zip.generateAsync({ type: 'blob' }); + downloadFile(zipBlob, 'compressed_pdfs.zip'); - const originalSize = formatBytes(originalFile.size); - const compressedSize = formatBytes(resultBytes.length); - const savings = originalFile.size - resultBytes.length; - const savingsPercent = - savings > 0 ? ((savings / originalFile.size) * 100).toFixed(1) : 0; + const totalSavings = totalOriginal - totalCompressed; + const totalPercent = ((totalSavings / totalOriginal) * 100).toFixed(1); - if (savings > 0) { - showAlert( - 'Compression Complete', - `Method: **${usedMethod}**. ` + - `File size reduced from ${originalSize} to ${compressedSize} (Saved ${savingsPercent}%).` - ); - } else { - showAlert( - 'Compression Finished', - `Method: **${usedMethod}**. ` + - `Could not reduce file size. Original: ${originalSize}, New: ${compressedSize}.`, - // @ts-expect-error TS(2554) FIXME: Expected 2 arguments, but got 3. - 'warning' - ); - } - - downloadFile( - new Blob([resultBytes], { type: 'application/pdf' }), - 'compressed-final.pdf' + showAlert( + 'All Files Compressed', + `Total saved: ${totalPercent}% (${formatBytes(totalOriginal)} → ${formatBytes(totalCompressed)})` ); } catch (e) { showAlert( diff --git a/src/js/ui.ts b/src/js/ui.ts index c53d62da..400e7456 100644 --- a/src/js/ui.ts +++ b/src/js/ui.ts @@ -532,7 +532,7 @@ export const toolTemplates = { compress: () => `
Reduce file size by choosing the compression method that best suits your document.
- ${createFileInputHTML()} + ${createFileInputHTML({ multiple: true, accept: 'application/pdf', showControls: true })}