pdftools / static /js /image-tools.js
Shivakafle038's picture
PDF Tools Web App - compress, convert, watermark removal
32a841c
/**
* Image Tools Module
* Remove Background, Add Watermark, Resize, Convert, Compress
*/
// =============== Remove Background ===============
let rembgFile = null;
function initRemoveBackground() {
const dropZone = document.getElementById('rembgDropZone');
const fileInput = document.getElementById('rembg_file');
const btn = document.getElementById('btnRembg');
const btnPreview = document.getElementById('btnRembgPreview');
const notice = document.getElementById('rembgNotice');
if (!dropZone || !fileInput) return;
// Check if rembg is available
fetch('/api/rembg-status')
.then(r => r.json())
.then(data => {
if (!data.available && notice) {
notice.style.display = 'flex';
}
})
.catch(() => {});
initDropZone(dropZone, fileInput, {
maxSize: 50 * 1024 * 1024,
onFile: (file) => {
rembgFile = file;
btn.disabled = !file;
btnPreview.disabled = !file;
document.getElementById('rembgPreviewCard').style.display = 'none';
if (file) showToast('Image Selected', file.name, 'success', 2000);
}
});
btnPreview.addEventListener('click', () => previewRemoveBackground());
btn.addEventListener('click', () => removeBackground());
}
async function previewRemoveBackground() {
if (!rembgFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnRembgPreview');
const previewCard = document.getElementById('rembgPreviewCard');
const originalImg = document.getElementById('rembgPreviewOriginal');
const processedImg = document.getElementById('rembgPreviewProcessed');
setButtonLoading(btn, true, 'Processing...');
try {
const fd = new FormData();
fd.append('file', rembgFile);
const res = await fetch('/api/preview/remove-background', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const data = await res.json();
originalImg.src = 'data:image/png;base64,' + data.original;
processedImg.src = 'data:image/png;base64,' + data.processed;
previewCard.style.display = 'block';
previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
showToast('Preview Ready', 'Compare original vs processed', 'success', 2000);
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
}
}
async function removeBackground() {
if (!rembgFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnRembg');
const progress = document.getElementById('rembgProgress');
setButtonLoading(btn, true, 'Processing...');
showProgress(progress, true, true);
try {
const fd = new FormData();
fd.append('file', rembgFile);
fd.append('output_name', document.getElementById('rembg_output').value || 'no-background.png');
const res = await fetch('/api/remove-background', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const blob = await res.blob();
const filename = document.getElementById('rembg_output').value || 'no-background.png';
downloadBlob(blob, filename.endsWith('.png') ? filename : filename + '.png');
showSuccessAnimation('Background Removed!', 'Download started');
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
showProgress(progress, false);
}
}
// =============== Add Watermark ===============
let imgWatermarkFile = null;
function initImageWatermark() {
const dropZone = document.getElementById('imgwatermarkDropZone');
const fileInput = document.getElementById('imgwatermark_file');
const btn = document.getElementById('btnImgWatermark');
const btnPreview = document.getElementById('btnImgWatermarkPreview');
if (!dropZone || !fileInput) return;
initDropZone(dropZone, fileInput, {
maxSize: 50 * 1024 * 1024,
onFile: (file) => {
imgWatermarkFile = file;
btn.disabled = !file;
btnPreview.disabled = !file;
document.getElementById('imgwatermarkPreviewCard').style.display = 'none';
if (file) showToast('Image Selected', file.name, 'success', 2000);
}
});
btnPreview.addEventListener('click', () => previewImageWatermark());
btn.addEventListener('click', () => addImageWatermark());
}
async function previewImageWatermark() {
if (!imgWatermarkFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const text = document.getElementById('imgwatermark_text').value;
if (!text.trim()) {
showToast('No Text', 'Enter watermark text', 'error');
return;
}
const btn = document.getElementById('btnImgWatermarkPreview');
const previewCard = document.getElementById('imgwatermarkPreviewCard');
const originalImg = document.getElementById('imgwatermarkPreviewOriginal');
const processedImg = document.getElementById('imgwatermarkPreviewProcessed');
setButtonLoading(btn, true, 'Loading...');
try {
const fd = new FormData();
fd.append('file', imgWatermarkFile);
fd.append('text', text);
fd.append('position', document.getElementById('imgwatermark_position').value);
fd.append('opacity', document.getElementById('imgwatermark_opacity').value);
fd.append('font_size', document.getElementById('imgwatermark_fontsize').value);
fd.append('color', document.getElementById('imgwatermark_color').value);
const res = await fetch('/api/preview/image-watermark', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const data = await res.json();
originalImg.src = 'data:image/jpeg;base64,' + data.original;
processedImg.src = 'data:image/jpeg;base64,' + data.processed;
previewCard.style.display = 'block';
previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
showToast('Preview Ready', 'Compare original vs watermarked', 'success', 2000);
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
}
}
async function addImageWatermark() {
if (!imgWatermarkFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const text = document.getElementById('imgwatermark_text').value;
if (!text.trim()) {
showToast('No Text', 'Enter watermark text', 'error');
return;
}
const btn = document.getElementById('btnImgWatermark');
const progress = document.getElementById('imgwatermarkProgress');
setButtonLoading(btn, true, 'Processing...');
showProgress(progress, true, true);
try {
const fd = new FormData();
fd.append('file', imgWatermarkFile);
fd.append('text', text);
fd.append('position', document.getElementById('imgwatermark_position').value);
fd.append('opacity', document.getElementById('imgwatermark_opacity').value);
fd.append('font_size', document.getElementById('imgwatermark_fontsize').value);
fd.append('color', document.getElementById('imgwatermark_color').value);
fd.append('output_name', document.getElementById('imgwatermark_output').value || 'watermarked');
const res = await fetch('/api/add-image-watermark', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const blob = await res.blob();
const contentDisposition = res.headers.get('content-disposition');
let filename = 'watermarked.jpg';
if (contentDisposition) {
const match = contentDisposition.match(/filename="?([^"]+)"?/);
if (match) filename = match[1];
}
downloadBlob(blob, filename);
showSuccessAnimation('Watermark Added!', 'Download started');
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
showProgress(progress, false);
}
}
// =============== Resize Image ===============
let resizeFile = null;
function initResizeImage() {
const dropZone = document.getElementById('resizeDropZone');
const fileInput = document.getElementById('resize_file');
const btn = document.getElementById('btnResize');
const btnPreview = document.getElementById('btnResizePreview');
const presetSelect = document.getElementById('resize_preset');
const widthInput = document.getElementById('resize_width');
const heightInput = document.getElementById('resize_height');
if (!dropZone || !fileInput) return;
initDropZone(dropZone, fileInput, {
maxSize: 50 * 1024 * 1024,
onFile: (file) => {
resizeFile = file;
btn.disabled = !file;
btnPreview.disabled = !file;
document.getElementById('resizePreviewCard').style.display = 'none';
if (file) showToast('Image Selected', file.name, 'success', 2000);
}
});
// Preset change handler
presetSelect.addEventListener('change', () => {
const preset = presetSelect.value;
if (preset !== 'custom') {
widthInput.disabled = true;
heightInput.disabled = true;
widthInput.placeholder = 'Auto';
heightInput.placeholder = 'Auto';
} else {
widthInput.disabled = false;
heightInput.disabled = false;
}
});
btnPreview.addEventListener('click', () => previewResizeImage());
btn.addEventListener('click', () => resizeImage());
}
async function previewResizeImage() {
if (!resizeFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnResizePreview');
const previewCard = document.getElementById('resizePreviewCard');
const originalImg = document.getElementById('resizePreviewOriginal');
const processedImg = document.getElementById('resizePreviewProcessed');
const originalSizeSpan = document.getElementById('resizeOriginalSize');
const newSizeSpan = document.getElementById('resizeNewSize');
setButtonLoading(btn, true, 'Loading...');
try {
const fd = new FormData();
fd.append('file', resizeFile);
fd.append('preset', document.getElementById('resize_preset').value);
fd.append('width', document.getElementById('resize_width').value || '0');
fd.append('height', document.getElementById('resize_height').value || '0');
fd.append('maintain_aspect', document.getElementById('resize_aspect').checked);
const res = await fetch('/api/preview/resize-image', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const data = await res.json();
originalImg.src = 'data:image/jpeg;base64,' + data.original;
processedImg.src = 'data:image/jpeg;base64,' + data.processed;
originalSizeSpan.textContent = `(${data.original_size})`;
newSizeSpan.textContent = `(${data.new_size})`;
previewCard.style.display = 'block';
previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
showToast('Preview Ready', `${data.original_size}${data.new_size}`, 'success', 2000);
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
}
}
async function resizeImage() {
if (!resizeFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnResize');
const progress = document.getElementById('resizeProgress');
setButtonLoading(btn, true, 'Resizing...');
showProgress(progress, true, true);
try {
const fd = new FormData();
fd.append('file', resizeFile);
fd.append('preset', document.getElementById('resize_preset').value);
fd.append('width', document.getElementById('resize_width').value || '0');
fd.append('height', document.getElementById('resize_height').value || '0');
fd.append('maintain_aspect', document.getElementById('resize_aspect').checked);
fd.append('output_name', document.getElementById('resize_output').value || 'resized');
const res = await fetch('/api/resize-image', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const blob = await res.blob();
const contentDisposition = res.headers.get('content-disposition');
let filename = 'resized.jpg';
if (contentDisposition) {
const match = contentDisposition.match(/filename="?([^"]+)"?/);
if (match) filename = match[1];
}
downloadBlob(blob, filename);
const newSize = res.headers.get('X-New-Size');
showSuccessAnimation('Image Resized!', newSize ? `New size: ${newSize}` : 'Download started');
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
showProgress(progress, false);
}
}
// =============== Convert Format ===============
let convertFile = null;
function initConvertImage() {
const dropZone = document.getElementById('convertDropZone');
const fileInput = document.getElementById('convert_file');
const btn = document.getElementById('btnConvert');
const formatSelect = document.getElementById('convert_format');
const qualityInput = document.getElementById('convert_quality');
if (!dropZone || !fileInput) return;
initDropZone(dropZone, fileInput, {
maxSize: 50 * 1024 * 1024,
onFile: (file) => {
convertFile = file;
btn.disabled = !file;
if (file) {
showToast('Image Selected', file.name, 'success', 2000);
// Trigger initial estimate
updateConvertImageEstimate();
}
}
});
// Format and quality change handlers with debounce
let estimateTimeout = null;
const triggerEstimate = () => {
if (estimateTimeout) clearTimeout(estimateTimeout);
estimateTimeout = setTimeout(() => {
updateConvertImageEstimate();
}, 300);
};
formatSelect.addEventListener('change', triggerEstimate);
qualityInput.addEventListener('input', triggerEstimate);
btn.addEventListener('click', () => convertImage());
}
async function updateConvertImageEstimate() {
if (!convertFile) return;
const format = document.getElementById('convert_format').value;
const quality = document.getElementById('convert_quality').value;
// Create estimate display if it doesn't exist
let displayDiv = document.getElementById('convertEstimate');
if (!displayDiv) {
displayDiv = document.createElement('div');
displayDiv.id = 'convertEstimate';
displayDiv.style.cssText = 'margin-top: 12px; padding: 12px; background: var(--border-light); border-radius: var(--radius); font-size: 13px;';
const btnGroup = document.querySelector('#page-convert .btn-group');
btnGroup.parentNode.insertBefore(displayDiv, btnGroup);
}
displayDiv.innerHTML = '<span style="color: var(--text-muted);">Estimating file size...</span>';
try {
const fd = new FormData();
fd.append('file', convertFile);
fd.append('target_format', format);
fd.append('quality', quality);
const res = await fetch('/api/estimate/convert-image', { method: 'POST', body: fd });
if (!res.ok) {
displayDiv.innerHTML = '<span style="color: var(--text-muted);">Could not estimate</span>';
return;
}
const data = await res.json();
const origKB = (data.original_size / 1024).toFixed(1);
const estKB = (data.estimated_size / 1024).toFixed(1);
const diff = data.estimated_size - data.original_size;
const diffPercent = ((diff / data.original_size) * 100).toFixed(1);
let sizeChange = '';
if (diff < 0) {
sizeChange = `<span style="color: var(--success); font-weight: 600;">${diffPercent}%</span>`;
} else if (diff > 0) {
sizeChange = `<span style="color: var(--warning); font-weight: 600;">+${diffPercent}%</span>`;
} else {
sizeChange = `<span style="color: var(--text-muted);">Same size</span>`;
}
displayDiv.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center;">
<span><strong>Original:</strong> ${origKB} KB</span>
<span style="color: var(--primary);">→</span>
<span><strong>${data.target_format}:</strong> ${estKB} KB</span>
${sizeChange}
</div>
`;
} catch (e) {
displayDiv.innerHTML = '<span style="color: var(--text-muted);">Could not estimate</span>';
}
}
async function convertImage() {
if (!convertFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnConvert');
const progress = document.getElementById('convertProgress');
setButtonLoading(btn, true, 'Converting...');
showProgress(progress, true, true);
try {
const fd = new FormData();
fd.append('file', convertFile);
fd.append('target_format', document.getElementById('convert_format').value);
fd.append('quality', document.getElementById('convert_quality').value);
fd.append('output_name', document.getElementById('convert_output').value || 'converted');
const res = await fetch('/api/convert-image', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const blob = await res.blob();
const contentDisposition = res.headers.get('content-disposition');
let filename = 'converted.jpg';
if (contentDisposition) {
const match = contentDisposition.match(/filename="?([^"]+)"?/);
if (match) filename = match[1];
}
downloadBlob(blob, filename);
const format = document.getElementById('convert_format').value.toUpperCase();
showSuccessAnimation('Image Converted!', `Converted to ${format}`);
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
showProgress(progress, false);
}
}
// =============== Compress Image ===============
let compressImgFile = null;
function initCompressImage() {
const dropZone = document.getElementById('compressimgDropZone');
const fileInput = document.getElementById('compressimg_file');
const btn = document.getElementById('btnCompressImg');
const btnPreview = document.getElementById('btnCompressImgPreview');
const qualitySlider = document.getElementById('compressimg_quality');
const qualityVal = document.getElementById('compressimg_quality_val');
if (!dropZone || !fileInput) return;
initDropZone(dropZone, fileInput, {
maxSize: 50 * 1024 * 1024,
onFile: (file) => {
compressImgFile = file;
btn.disabled = !file;
btnPreview.disabled = !file;
document.getElementById('compressimgResult').style.display = 'none';
document.getElementById('compressimgPreviewCard').style.display = 'none';
if (file) {
showToast('Image Selected', file.name, 'success', 2000);
// Trigger initial estimate
updateCompressImageEstimate();
}
}
});
// Quality slider with debounced estimate
let estimateTimeout = null;
qualitySlider.addEventListener('input', () => {
qualityVal.textContent = qualitySlider.value;
// Debounce the estimate call
if (estimateTimeout) clearTimeout(estimateTimeout);
estimateTimeout = setTimeout(() => {
updateCompressImageEstimate();
}, 300);
});
btnPreview.addEventListener('click', () => previewCompressImage());
btn.addEventListener('click', () => compressImage());
}
async function updateCompressImageEstimate() {
if (!compressImgFile) return;
const estimateDiv = document.getElementById('compressimgEstimate');
const quality = document.getElementById('compressimg_quality').value;
// Create estimate display if it doesn't exist
let displayDiv = estimateDiv;
if (!displayDiv) {
displayDiv = document.createElement('div');
displayDiv.id = 'compressimgEstimate';
displayDiv.style.cssText = 'margin-top: 12px; padding: 12px; background: var(--border-light); border-radius: var(--radius); font-size: 13px;';
const qualityGroup = document.getElementById('compressimg_quality').closest('.form-group');
qualityGroup.appendChild(displayDiv);
}
displayDiv.innerHTML = '<span style="color: var(--text-muted);">Estimating file size...</span>';
try {
const fd = new FormData();
fd.append('file', compressImgFile);
fd.append('quality', quality);
const res = await fetch('/api/estimate/compress-image', { method: 'POST', body: fd });
if (!res.ok) {
displayDiv.innerHTML = '<span style="color: var(--text-muted);">Could not estimate</span>';
return;
}
const data = await res.json();
const origKB = (data.original_size / 1024).toFixed(1);
const estKB = (data.estimated_size / 1024).toFixed(1);
const reduction = data.reduction_percent;
displayDiv.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center;">
<span><strong>Original:</strong> ${origKB} KB</span>
<span style="color: var(--primary);">→</span>
<span><strong>Estimated:</strong> ${estKB} KB</span>
<span style="color: ${reduction > 0 ? 'var(--success)' : 'var(--text-muted)'}; font-weight: 600;">
${reduction > 0 ? '-' + reduction + '%' : 'No reduction'}
</span>
</div>
`;
} catch (e) {
displayDiv.innerHTML = '<span style="color: var(--text-muted);">Could not estimate</span>';
}
}
async function previewCompressImage() {
if (!compressImgFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnCompressImgPreview');
const previewCard = document.getElementById('compressimgPreviewCard');
const originalImg = document.getElementById('compressimgPreviewOriginal');
const processedImg = document.getElementById('compressimgPreviewProcessed');
setButtonLoading(btn, true, 'Loading...');
try {
const fd = new FormData();
fd.append('file', compressImgFile);
fd.append('quality', document.getElementById('compressimg_quality').value);
const res = await fetch('/api/preview/compress-image', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const data = await res.json();
originalImg.src = 'data:image/jpeg;base64,' + data.original;
processedImg.src = 'data:image/jpeg;base64,' + data.processed;
previewCard.style.display = 'block';
previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
const origKB = (data.original_size / 1024).toFixed(1);
const compKB = (data.compressed_size / 1024).toFixed(1);
showToast('Preview Ready', `${origKB} KB → ${compKB} KB`, 'success', 2000);
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
}
}
async function compressImage() {
if (!compressImgFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnCompressImg');
const progress = document.getElementById('compressimgProgress');
const resultDiv = document.getElementById('compressimgResult');
const statsDiv = document.getElementById('compressimgStats');
setButtonLoading(btn, true, 'Compressing...');
showProgress(progress, true, true);
resultDiv.style.display = 'none';
try {
const fd = new FormData();
fd.append('file', compressImgFile);
fd.append('quality', document.getElementById('compressimg_quality').value);
fd.append('output_name', document.getElementById('compressimg_output').value || 'compressed');
const res = await fetch('/api/compress-image', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const blob = await res.blob();
const contentDisposition = res.headers.get('content-disposition');
let filename = 'compressed.jpg';
if (contentDisposition) {
const match = contentDisposition.match(/filename="?([^"]+)"?/);
if (match) filename = match[1];
}
downloadBlob(blob, filename);
// Show compression stats
const originalSize = res.headers.get('X-Original-Size');
const compressedSize = res.headers.get('X-Compressed-Size');
const reduction = res.headers.get('X-Reduction-Percent');
if (originalSize && compressedSize) {
const origKB = (parseInt(originalSize) / 1024).toFixed(1);
const compKB = (parseInt(compressedSize) / 1024).toFixed(1);
statsDiv.textContent = `${origKB} KB → ${compKB} KB (${reduction}% smaller)`;
resultDiv.style.display = 'block';
}
showSuccessAnimation('Image Compressed!', `Reduced by ${reduction}%`);
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
showProgress(progress, false);
}
}
// =============== Utility ===============
function downloadBlob(blob, filename) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
}
// =============== Enhance Image ===============
let enhanceImgFile = null;
function initEnhanceImage() {
const dropZone = document.getElementById('enhanceImgDropZone');
const fileInput = document.getElementById('enhanceimg_file');
const btn = document.getElementById('btnEnhanceImg');
const btnPreview = document.getElementById('btnEnhanceImgPreview');
if (!dropZone || !fileInput) return;
initDropZone(dropZone, fileInput, {
maxSize: 50 * 1024 * 1024,
onFile: (file) => {
enhanceImgFile = file;
btn.disabled = !file;
btnPreview.disabled = !file;
document.getElementById('enhanceimgPreviewCard').style.display = 'none';
if (file) showToast('Image Selected', file.name, 'success', 2000);
}
});
btnPreview.addEventListener('click', () => previewEnhanceImage());
btn.addEventListener('click', () => enhanceImage());
}
async function previewEnhanceImage() {
if (!enhanceImgFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnEnhanceImgPreview');
const previewCard = document.getElementById('enhanceimgPreviewCard');
const originalImg = document.getElementById('enhanceimgPreviewOriginal');
const processedImg = document.getElementById('enhanceimgPreviewProcessed');
setButtonLoading(btn, true, 'Loading...');
try {
const fd = new FormData();
fd.append('file', enhanceImgFile);
fd.append('level', document.getElementById('enhanceimg_level').value);
fd.append('upscale', document.getElementById('enhanceimg_upscale').value);
const res = await fetch('/api/preview/enhance-image', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const data = await res.json();
originalImg.src = 'data:image/jpeg;base64,' + data.original;
processedImg.src = 'data:image/jpeg;base64,' + data.processed;
previewCard.style.display = 'block';
previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
showToast('Preview Ready', 'Compare original vs enhanced', 'success', 2000);
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
}
}
async function enhanceImage() {
if (!enhanceImgFile) {
showToast('No Image', 'Upload an image first', 'error');
return;
}
const btn = document.getElementById('btnEnhanceImg');
const progress = document.getElementById('enhanceimgProgress');
setButtonLoading(btn, true, 'Enhancing...');
showProgress(progress, true, true);
try {
const fd = new FormData();
fd.append('file', enhanceImgFile);
fd.append('level', document.getElementById('enhanceimg_level').value);
fd.append('upscale', document.getElementById('enhanceimg_upscale').value);
fd.append('output_name', document.getElementById('enhanceimg_output').value || 'enhanced');
const res = await fetch('/api/enhance-image', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const blob = await res.blob();
const contentDisposition = res.headers.get('content-disposition');
let filename = 'enhanced.jpg';
if (contentDisposition) {
const match = contentDisposition.match(/filename="?([^"]+)"?/);
if (match) filename = match[1];
}
downloadBlob(blob, filename);
showSuccessAnimation('Image Enhanced!', 'Download started');
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
showProgress(progress, false);
}
}
// =============== Enhance PDF ===============
let enhancePdfFile = null;
function initEnhancePdf() {
const dropZone = document.getElementById('enhancePdfDropZone');
const fileInput = document.getElementById('enhancepdf_file');
const btn = document.getElementById('btnEnhancePdf');
if (!dropZone || !fileInput) return;
initDropZone(dropZone, fileInput, {
maxSize: 100 * 1024 * 1024,
onFile: (file) => {
enhancePdfFile = file;
btn.disabled = !file;
document.getElementById('enhancepdfResult').style.display = 'none';
if (file) showToast('PDF Selected', file.name, 'success', 2000);
}
});
btn.addEventListener('click', () => enhancePdf());
}
async function enhancePdf() {
if (!enhancePdfFile) {
showToast('No PDF', 'Upload a PDF first', 'error');
return;
}
const btn = document.getElementById('btnEnhancePdf');
const progress = document.getElementById('enhancepdfProgress');
const resultDiv = document.getElementById('enhancepdfResult');
const statsDiv = document.getElementById('enhancepdfStats');
setButtonLoading(btn, true, 'Enhancing...');
showProgress(progress, true, true);
resultDiv.style.display = 'none';
try {
const fd = new FormData();
fd.append('file', enhancePdfFile);
fd.append('level', document.getElementById('enhancepdf_level').value);
fd.append('output_name', document.getElementById('enhancepdf_output').value || 'enhanced.pdf');
const res = await fetch('/api/enhance-pdf', { method: 'POST', body: fd });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Failed' }));
throw new Error(err.detail);
}
const blob = await res.blob();
const filename = document.getElementById('enhancepdf_output').value || 'enhanced.pdf';
downloadBlob(blob, filename.endsWith('.pdf') ? filename : filename + '.pdf');
// Show stats
const originalSize = res.headers.get('X-Original-Size');
const enhancedSize = res.headers.get('X-Enhanced-Size');
const imagesEnhanced = res.headers.get('X-Images-Enhanced');
if (originalSize && enhancedSize) {
const origMB = (parseInt(originalSize) / 1024 / 1024).toFixed(2);
const enhMB = (parseInt(enhancedSize) / 1024 / 1024).toFixed(2);
const change = (((parseInt(enhancedSize) - parseInt(originalSize)) / parseInt(originalSize)) * 100).toFixed(1);
statsDiv.textContent = `${origMB} MB → ${enhMB} MB (${change > 0 ? '+' : ''}${change}%), ${imagesEnhanced} images enhanced`;
resultDiv.style.display = 'block';
}
showSuccessAnimation('PDF Enhanced!', `${imagesEnhanced} images improved`);
} catch (e) {
showToast('Error', e.message, 'error');
} finally {
setButtonLoading(btn, false);
showProgress(progress, false);
}
}
// =============== Initialize All ===============
function initImageTools() {
initRemoveBackground();
initImageWatermark();
initResizeImage();
initConvertImage();
initCompressImage();
initEnhanceImage();
initEnhancePdf();
}