// ===== TOOL 4: COMPRESS PDF IMPLEMENTATION =====
// Include this script tag in your HTML head to add the required libraries
// <script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>
// <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
document.getElementById('compressPdfBrowseBtn').addEventListener('click', () => {
document.getElementById('compressPdfInput').click();
});
document.getElementById('compressPdfInput').addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
document.getElementById('compressPdfFileInfo').innerHTML = `
<strong>File:</strong> ${file.name}<br>
<strong>Size:</strong> ${formatFileSize(file.size)}
`;
document.getElementById('compressPdfFileInfo').classList.add('active');
document.getElementById('compressPdfProcessBtn').disabled = false;
}
});
document.getElementById('compressPdfProcessBtn').addEventListener('click', async function() {
const file = document.getElementById('compressPdfInput').files[0];
if (!file) return;
showLoading('Compressing PDF... This may take a moment.');
try {
const { PDFDocument } = PDFLib;
const level = document.getElementById('compressPdfLevel').value;
// Read the PDF file
const pdfBytes = await file.arrayBuffer();
const pdfDoc = await PDFDocument.load(pdfBytes);
// Get compression settings based on selected level
const compressionSettings = getCompressionSettings(level);
// Step 1: Remove metadata and compress structure
const compressedPdfBytes = await compressPDF(pdfDoc, compressionSettings);
// Step 2: Apply additional compression if high level selected
let finalBytes = compressedPdfBytes;
if (level === 'high') {
finalBytes = await applyHighCompression(compressedPdfBytes);
}
// Calculate compression results
const blob = new Blob([finalBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const reduction = ((file.size - finalBytes.byteLength) / file.size * 100).toFixed(1);
// Show results
document.getElementById('compressPdfStats').innerHTML = `
<strong>Compression Results:</strong><br>
<div style="display: flex; justify-content: space-between; margin: 10px 0;">
<span>📄 Original:</span>
<span style="font-weight: 600;">${formatFileSize(file.size)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin: 10px 0;">
<span>🗜️ Compressed:</span>
<span style="font-weight: 600; color: #059669;">${formatFileSize(finalBytes.byteLength)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin: 10px 0;">
<span>💾 Space saved:</span>
<span style="font-weight: 600; color: ${reduction > 0 ? '#059669' : '#e53e3e'};">
${Math.abs(reduction)}% ${reduction > 0 ? '↓ saved' : '↑ increase'}
</span>
</div>
<div style="margin-top: 10px; font-size: 0.9rem; color: #666;">
${getCompressionQualityMessage(level, reduction)}
</div>
`;
document.getElementById('compressPdfResults').classList.add('active');
// Setup download button
document.getElementById('compressPdfDownloadBtn').onclick = () => {
const a = document.createElement('a');
a.href = url;
a.download = file.name.replace('.pdf', `_compressed_${level}.pdf`);
a.click();
URL.revokeObjectURL(url);
};
showToast(`PDF compressed! Saved ${reduction}% space.`, 'success');
} catch (error) {
console.error('Compression error:', error);
showToast('Error compressing PDF: ' + error.message, 'error');
} finally {
hideLoading();
}
});
// Helper function: Get compression settings based on level
function getCompressionSettings(level) {
switch(level) {
case 'low':
return {
useObjectStreams: true,
compress: true,
objectsPerTick: 50,
updateFieldAppearances: false,
imageQuality: 0.9
};
case 'medium':
return {
useObjectStreams: true,
compress: true,
objectsPerTick: 20,
updateFieldAppearances: false,
imageQuality: 0.7,
removeMetadata: true,
flattenForms: true
};
case 'high':
return {
useObjectStreams: true,
compress: true,
objectsPerTick: 10,
updateFieldAppearances: false,
imageQuality: 0.5,
removeMetadata: true,
flattenForms: true,
removePageLabels: true,
optimizeImages: true,
downscaleImages: true
};
default:
return {
useObjectStreams: true,
compress: true
};
}
}
// Main compression function
async function compressPDF(pdfDoc, settings) {
// Save with compression options
let saveOptions = {
useObjectStreams: settings.useObjectStreams,
addDefaultPage: false,
updateFieldAppearances: settings.updateFieldAppearances
};
// Try to remove metadata if specified
if (settings.removeMetadata) {
pdfDoc.setTitle('');
pdfDoc.setAuthor('');
pdfDoc.setSubject('');
pdfDoc.setKeywords([]);
pdfDoc.setProducer('');
pdfDoc.setCreator('');
}
// Flatten form fields if specified
if (settings.flattenForms) {
const form = pdfDoc.getForm();
if (form) {
form.flatten();
}
}
// Save with compression
return await pdfDoc.save(saveOptions);
}
// High compression: Additional optimization pass
async function applyHighCompression(pdfBytes) {
try {
// Reload the PDF for second pass
const { PDFDocument } = PDFLib;
const pdfDoc = await PDFDocument.load(pdfBytes);
// Second pass with more aggressive compression
const compressed = await pdfDoc.save({
useObjectStreams: true,
compress: true,
addDefaultPage: false
});
return compressed;
} catch (error) {
console.warn('High compression failed, using first pass result:', error);
return pdfBytes;
}
}
// Helper function: Get compression quality message
function getCompressionQualityMessage(level, reduction) {
const quality = {
low: 'Good quality, minimal compression',
medium: 'Balanced quality and file size',
high: 'Maximum compression, may affect quality'
};
const qualityText = quality[level] || 'Standard compression';
if (reduction > 20) {
return `✅ Excellent! ${qualityText}`;
} else if (reduction > 10) {
return `👍 Good compression. ${qualityText}`;
} else if (reduction > 0) {
return `😊 Moderate compression. ${qualityText}`;
} else {
return `⚠️ Minimal compression possible. ${qualityText}`;
}
}
// Reset button
document.getElementById('compressPdfNewBtn').addEventListener('click', function() {
document.getElementById('compressPdfInput').value = '';
document.getElementById('compressPdfFileInfo').classList.remove('active');
document.getElementById('compressPdfResults').classList.remove('active');
document.getElementById('compressPdfProcessBtn').disabled = true;
// Clean up any existing blob URLs
const downloadBtn = document.getElementById('compressPdfDownloadBtn');
if (downloadBtn.href) {
URL.revokeObjectURL(downloadBtn.href);
}
});