Files
PDF-WEB/static/js/pdf-tools.js
SimolZimol 1097fa0c64 modified: QUICKSTART.md
modified:   app.py
	deleted:    output/.gitkeep
	modified:   static/css/style.css
	modified:   static/js/pdf-tools.js
	modified:   templates/pdf_tools.html
	deleted:    uploads/.gitkeep
2025-10-12 23:50:26 +02:00

511 lines
17 KiB
JavaScript

// PDF Tools - JavaScript Functionality
let pdfToolsFiles = [];
let mergeSortable = null;
let currentPdfFile = null;
// DOM elements
let mergeUploadArea, mergeFileInput, mergeFileList, mergeFilesContainer, mergePdfsBtn;
let splitUploadArea, splitFileInput, splitFileInfo, convertToImagesBtn;
let mergeResult, splitResult, mergeDownloadLink, splitDownloadLink;
let processingModal, errorArea, errorMessage;
// Initialize when page loads
document.addEventListener('DOMContentLoaded', function() {
initializeDOMElements();
setupEventListeners();
setupDragAndDrop();
});
function initializeDOMElements() {
// Merge elements
mergeUploadArea = document.getElementById('merge-upload-area');
mergeFileInput = document.getElementById('merge-file-input');
mergeFileList = document.getElementById('merge-file-list');
mergeFilesContainer = document.getElementById('merge-files-container');
mergePdfsBtn = document.getElementById('merge-pdfs-btn');
mergeResult = document.getElementById('merge-result');
mergeDownloadLink = document.getElementById('merge-download-link');
// Split elements
splitUploadArea = document.getElementById('split-upload-area');
splitFileInput = document.getElementById('split-file-input');
splitFileInfo = document.getElementById('split-file-info');
convertToImagesBtn = document.getElementById('convert-to-images-btn');
splitResult = document.getElementById('split-result');
splitDownloadLink = document.getElementById('split-download-link');
// Common elements
processingModal = new bootstrap.Modal(document.getElementById('processing-modal'));
errorArea = document.getElementById('pdf-tools-error');
errorMessage = document.getElementById('pdf-tools-error-message');
}
function setupEventListeners() {
// Merge PDF events
mergeFileInput.addEventListener('change', function(e) {
handleMergeFiles(Array.from(e.target.files));
});
mergeUploadArea.addEventListener('click', function() {
mergeFileInput.click();
});
mergePdfsBtn.addEventListener('click', mergePdfs);
// Split PDF events
splitFileInput.addEventListener('change', function(e) {
if (e.target.files.length > 0) {
handleSplitFile(e.target.files[0]);
}
});
splitUploadArea.addEventListener('click', function() {
splitFileInput.click();
});
convertToImagesBtn.addEventListener('click', convertPdfToImages);
// Tab change events
document.querySelectorAll('[data-bs-toggle="pill"]').forEach(tab => {
tab.addEventListener('shown.bs.tab', function(e) {
hideAllResults();
});
});
}
function setupDragAndDrop() {
// Merge area drag and drop
setupDragAndDrop(mergeUploadArea, handleMergeFiles);
// Split area drag and drop
setupDragAndDrop(splitUploadArea, function(files) {
if (files.length > 0) {
handleSplitFile(files[0]);
}
});
}
// Merge PDF functions
async function handleMergeFiles(files) {
hideAllResults();
if (!files || files.length === 0) {
showError('Keine Dateien ausgewählt.');
return;
}
// Filter PDF files
const pdfFiles = files.filter(file => file.type === 'application/pdf');
if (pdfFiles.length === 0) {
showError('Keine PDF-Dateien gefunden.');
return;
}
if (pdfFiles.length < 2) {
showError('Mindestens 2 PDF-Dateien erforderlich.');
return;
}
// Upload files one by one
pdfToolsFiles = [];
for (const file of pdfFiles) {
try {
const response = await uploadSinglePdf(file);
if (response.success) {
pdfToolsFiles.push(response);
}
} catch (error) {
showError(`Fehler beim Upload von ${file.name}: ${error.message}`);
return;
}
}
displayMergeFiles();
showNotification(`${pdfToolsFiles.length} PDF-Dateien erfolgreich hochgeladen.`, 'success');
}
async function uploadSinglePdf(file) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload-pdf', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
function displayMergeFiles() {
mergeFilesContainer.innerHTML = '';
pdfToolsFiles.forEach((file, index) => {
const fileItem = createMergeFileItem(file, index);
mergeFilesContainer.appendChild(fileItem);
});
mergeFileList.style.display = 'block';
mergePdfsBtn.disabled = pdfToolsFiles.length < 2;
// Setup sortable
setupMergeSortable();
}
function createMergeFileItem(file, index) {
const div = document.createElement('div');
div.className = 'list-group-item pdf-item-enhanced';
div.dataset.index = index;
// Initialize rotation if not set
if (file.rotation === undefined) {
file.rotation = 0;
}
// Create preview element
const previewElement = file.preview ?
`<img src="/uploads/${file.preview}" class="pdf-preview size-medium rotate-${file.rotation}"
alt="${file.original_name}" onclick="showPdfModal('${file.preview}', '${file.original_name}', ${index})">` :
`<div class="pdf-preview pdf-no-preview size-medium rotate-${file.rotation}">
<i class="fas fa-file-pdf"></i>
</div>`;
div.innerHTML = `
<div class="d-flex align-items-center">
<div class="pdf-preview-container me-3">
${previewElement}
</div>
<div class="pdf-info">
<div class="file-name fw-bold">${file.original_name}</div>
<div class="file-details text-muted">
${file.page_count} Seiten • ${formatFileSize(file.size)}
</div>
<div class="rotation-controls">
<button type="button" class="btn btn-sm btn-outline-danger rotation-btn"
onclick="rotatePdf(${index}, -90)" title="Links drehen">
<i class="fas fa-undo"></i>
</button>
<button type="button" class="btn btn-sm btn-outline-danger rotation-btn"
onclick="rotatePdf(${index}, 90)" title="Rechts drehen">
<i class="fas fa-redo"></i>
</button>
<small class="text-muted ms-2">${file.rotation}°</small>
</div>
</div>
<div class="pdf-controls">
<div class="d-flex gap-1 mb-2">
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="movePdfUp(${index})"
${index === 0 ? 'disabled' : ''} title="Nach oben">
<i class="fas fa-arrow-up"></i>
</button>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="movePdfDown(${index})"
${index === pdfToolsFiles.length - 1 ? 'disabled' : ''} title="Nach unten">
<i class="fas fa-arrow-down"></i>
</button>
</div>
<button type="button" class="btn btn-sm btn-outline-danger" onclick="removeMergeFile(${index})" title="Entfernen">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`;
return div;
}
function setupMergeSortable() {
if (mergeSortable) {
mergeSortable.destroy();
}
mergeSortable = setupSortable(mergeFilesContainer, function(evt) {
const item = pdfToolsFiles.splice(evt.oldIndex, 1)[0];
pdfToolsFiles.splice(evt.newIndex, 0, item);
displayMergeFiles();
});
}
function removeMergeFile(index) {
pdfToolsFiles.splice(index, 1);
if (pdfToolsFiles.length === 0) {
mergeFileList.style.display = 'none';
mergePdfsBtn.disabled = true;
} else {
displayMergeFiles();
}
showNotification('PDF-Datei entfernt.', 'info');
}
function rotatePdf(index, degrees) {
if (index >= 0 && index < pdfToolsFiles.length) {
pdfToolsFiles[index].rotation = (pdfToolsFiles[index].rotation + degrees) % 360;
if (pdfToolsFiles[index].rotation < 0) {
pdfToolsFiles[index].rotation += 360;
}
displayMergeFiles();
showNotification(`PDF um ${degrees}° gedreht.`, 'info');
}
}
function movePdfUp(index) {
if (index > 0) {
[pdfToolsFiles[index], pdfToolsFiles[index - 1]] = [pdfToolsFiles[index - 1], pdfToolsFiles[index]];
displayMergeFiles();
}
}
function movePdfDown(index) {
if (index < pdfToolsFiles.length - 1) {
[pdfToolsFiles[index], pdfToolsFiles[index + 1]] = [pdfToolsFiles[index + 1], pdfToolsFiles[index]];
displayMergeFiles();
}
}
function showPdfModal(previewFilename, originalName, index) {
// Create modal if it doesn't exist
let modal = document.getElementById('pdf-modal');
if (!modal) {
modal = document.createElement('div');
modal.className = 'modal fade image-modal';
modal.id = 'pdf-modal';
modal.setAttribute('tabindex', '-1');
modal.setAttribute('aria-hidden', 'true');
modal.innerHTML = `
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="pdf-modal-title">${originalName}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<img id="modal-pdf-preview" src="/uploads/${previewFilename}"
class="img-fluid rotate-${pdfToolsFiles[index].rotation}" alt="${originalName}">
<p class="text-muted mt-2 mb-0">Vorschau der ersten Seite</p>
</div>
<div class="modal-footer">
<div class="btn-group me-auto">
<button type="button" class="btn btn-outline-danger" onclick="rotatePdfInModal(${index}, -90)">
<i class="fas fa-undo me-1"></i>Links drehen
</button>
<button type="button" class="btn btn-outline-danger" onclick="rotatePdfInModal(${index}, 90)">
<i class="fas fa-redo me-1"></i>Rechts drehen
</button>
</div>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
} else {
// Update existing modal
document.getElementById('pdf-modal-title').textContent = originalName;
const modalPreview = document.getElementById('modal-pdf-preview');
modalPreview.src = `/uploads/${previewFilename}`;
modalPreview.alt = originalName;
modalPreview.className = `img-fluid rotate-${pdfToolsFiles[index].rotation}`;
// Update rotation buttons
const rotateButtons = modal.querySelectorAll('.btn-outline-danger');
rotateButtons[0].onclick = () => rotatePdfInModal(index, -90);
rotateButtons[1].onclick = () => rotatePdfInModal(index, 90);
}
// Show modal
const bootstrapModal = new bootstrap.Modal(modal);
bootstrapModal.show();
}
function rotatePdfInModal(index, degrees) {
rotatePdf(index, degrees);
// Update modal preview
const modalPreview = document.getElementById('modal-pdf-preview');
modalPreview.className = `img-fluid rotate-${pdfToolsFiles[index].rotation}`;
}
function updatePdfPreviewSize() {
const previewSize = document.getElementById('pdf-preview-size')?.value || 'medium';
const previews = document.querySelectorAll('.pdf-preview');
previews.forEach(preview => {
// Remove existing size classes
preview.classList.remove('size-small', 'size-medium', 'size-large');
// Add new size class
preview.classList.add('size-' + previewSize);
});
}
async function mergePdfs() {
if (pdfToolsFiles.length < 2) {
showError('Mindestens 2 PDF-Dateien erforderlich.');
return;
}
hideAllResults();
processingModal.show();
try {
// Include rotation data for each file
const filesWithRotation = pdfToolsFiles.map(file => ({
filename: file.filename,
rotation: file.rotation || 0,
original_name: file.original_name
}));
const response = await makeRequest('/api/merge-pdfs', {
method: 'POST',
body: JSON.stringify({ files: filesWithRotation })
});
if (response.success) {
showMergeResult(response.filename, response.message);
showNotification(response.message, 'success');
} else {
throw new Error(response.error || 'Zusammenführung fehlgeschlagen');
}
} catch (error) {
showError(`Fehler beim Zusammenführen: ${error.message}`);
} finally {
processingModal.hide();
}
}
function showMergeResult(filename, message) {
mergeResult.style.display = 'block';
mergeDownloadLink.href = `/download/${filename}`;
if (message) {
mergeResult.querySelector('p').textContent = message;
}
}
// Split PDF functions
async function handleSplitFile(file) {
hideAllResults();
if (!file) {
showError('Keine Datei ausgewählt.');
return;
}
if (file.type !== 'application/pdf') {
showError('Nur PDF-Dateien sind erlaubt.');
return;
}
try {
const response = await uploadSinglePdf(file);
if (response.success) {
currentPdfFile = response;
displaySplitFile();
showNotification('PDF-Datei erfolgreich hochgeladen.', 'success');
} else {
throw new Error(response.error || 'Upload fehlgeschlagen');
}
} catch (error) {
showError(`Upload-Fehler: ${error.message}`);
}
}
function displaySplitFile() {
document.getElementById('split-filename').textContent = currentPdfFile.original_name;
document.getElementById('split-file-details').textContent =
`${currentPdfFile.page_count} Seiten • ${formatFileSize(currentPdfFile.size)}`;
// PDF-Vorschau anzeigen falls verfügbar
const previewContainer = document.getElementById('split-preview-container');
if (currentPdfFile.preview) {
previewContainer.innerHTML = `
<img src="/uploads/${currentPdfFile.preview}" class="pdf-preview size-medium"
alt="${currentPdfFile.original_name}" onclick="showSplitPdfModal()">
`;
} else {
previewContainer.innerHTML = `
<div class="pdf-preview pdf-no-preview size-medium">
<i class="fas fa-file-pdf"></i>
</div>
`;
}
splitFileInfo.style.display = 'block';
}
function showSplitPdfModal() {
if (currentPdfFile && currentPdfFile.preview) {
showPdfModal(currentPdfFile.preview, currentPdfFile.original_name, 0);
}
}
async function convertPdfToImages() {
if (!currentPdfFile) {
showError('Keine PDF-Datei ausgewählt.');
return;
}
hideAllResults();
processingModal.show();
try {
const response = await makeRequest('/api/pdf-to-images', {
method: 'POST',
body: JSON.stringify({ filename: currentPdfFile.filename })
});
if (response.success) {
showSplitResult(response.filename, response.message);
showNotification(response.message, 'success');
} else {
throw new Error(response.error || 'Konvertierung fehlgeschlagen');
}
} catch (error) {
showError(`Konvertierungs-Fehler: ${error.message}`);
} finally {
processingModal.hide();
}
}
function showSplitResult(filename, message) {
splitResult.style.display = 'block';
splitDownloadLink.href = `/download/${filename}`;
if (message) {
splitResult.querySelector('p').textContent = message;
}
}
// Utility functions
function hideAllResults() {
if (mergeResult) mergeResult.style.display = 'none';
if (splitResult) splitResult.style.display = 'none';
if (errorArea) errorArea.style.display = 'none';
}
function showError(message) {
errorArea.style.display = 'block';
errorMessage.textContent = message;
// Scroll to error
errorArea.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
// Global functions
window.removeMergeFile = removeMergeFile;
window.rotatePdf = rotatePdf;
window.movePdfUp = movePdfUp;
window.movePdfDown = movePdfDown;
window.showPdfModal = showPdfModal;
window.rotatePdfInModal = rotatePdfInModal;
window.updatePdfPreviewSize = updatePdfPreviewSize;
window.showSplitPdfModal = showSplitPdfModal;