diff --git a/app.py b/app.py new file mode 100644 index 0000000..17b4958 --- /dev/null +++ b/app.py @@ -0,0 +1,302 @@ +import os +import uuid +from flask import Flask, render_template, request, jsonify, send_file, redirect, url_for, flash +from werkzeug.utils import secure_filename +from PIL import Image +import PyPDF2 +from reportlab.pdfgen import canvas +from reportlab.lib.pagesizes import letter, A4 +from reportlab.lib.utils import ImageReader +import io +import zipfile +import pdf2image +import tempfile +import shutil +from datetime import datetime + +app = Flask(__name__) +app.secret_key = 'your-secret-key-change-this' + +# Konfiguration +UPLOAD_FOLDER = 'uploads' +OUTPUT_FOLDER = 'output' +ALLOWED_IMAGE_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff'} +ALLOWED_PDF_EXTENSIONS = {'pdf'} +MAX_FILE_SIZE = 16 * 1024 * 1024 # 16MB + +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.config['OUTPUT_FOLDER'] = OUTPUT_FOLDER +app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE + +def allowed_file(filename, extensions): + """Überprüft ob die Datei erlaubt ist""" + return '.' in filename and \ + filename.rsplit('.', 1)[1].lower() in extensions + +def create_unique_filename(filename): + """Erstellt einen eindeutigen Dateinamen""" + name, ext = os.path.splitext(filename) + unique_id = str(uuid.uuid4())[:8] + return f"{name}_{unique_id}{ext}" + +def cleanup_old_files(): + """Löscht alte Dateien aus Upload und Output Ordnern""" + current_time = datetime.now() + + for folder in [UPLOAD_FOLDER, OUTPUT_FOLDER]: + if os.path.exists(folder): + for filename in os.listdir(folder): + file_path = os.path.join(folder, filename) + if os.path.isfile(file_path): + file_time = datetime.fromtimestamp(os.path.getctime(file_path)) + # Lösche Dateien die älter als 1 Stunde sind + if (current_time - file_time).seconds > 3600: + os.remove(file_path) + +@app.route('/') +def index(): + """Hauptseite mit Navigation""" + cleanup_old_files() + return render_template('index.html') + +@app.route('/images-to-pdf') +def images_to_pdf_page(): + """Seite für Bilder zu PDF Konvertierung""" + return render_template('images_to_pdf.html') + +@app.route('/pdf-tools') +def pdf_tools_page(): + """Seite für PDF Tools""" + return render_template('pdf_tools.html') + +@app.route('/api/upload-images', methods=['POST']) +def upload_images(): + """API Endpunkt für Bilder Upload""" + try: + if 'files' not in request.files: + return jsonify({'error': 'Keine Dateien ausgewählt'}), 400 + + files = request.files.getlist('files') + + if not files or files[0].filename == '': + return jsonify({'error': 'Keine Dateien ausgewählt'}), 400 + + uploaded_files = [] + + for file in files: + if file and allowed_file(file.filename, ALLOWED_IMAGE_EXTENSIONS): + filename = secure_filename(file.filename) + unique_filename = create_unique_filename(filename) + file_path = os.path.join(UPLOAD_FOLDER, unique_filename) + file.save(file_path) + uploaded_files.append({ + 'filename': unique_filename, + 'original_name': filename, + 'size': os.path.getsize(file_path) + }) + + if not uploaded_files: + return jsonify({'error': 'Keine gültigen Bilddateien gefunden'}), 400 + + return jsonify({ + 'success': True, + 'files': uploaded_files, + 'message': f'{len(uploaded_files)} Dateien erfolgreich hochgeladen' + }) + + except Exception as e: + return jsonify({'error': f'Fehler beim Upload: {str(e)}'}), 500 + +@app.route('/api/convert-images-to-pdf', methods=['POST']) +def convert_images_to_pdf(): + """Konvertiert hochgeladene Bilder zu PDF""" + try: + data = request.get_json() + filenames = data.get('filenames', []) + + if not filenames: + return jsonify({'error': 'Keine Dateien ausgewählt'}), 400 + + # PDF erstellen + output_filename = f"converted_images_{str(uuid.uuid4())[:8]}.pdf" + output_path = os.path.join(OUTPUT_FOLDER, output_filename) + + # Reportlab für PDF Erstellung + c = canvas.Canvas(output_path, pagesize=A4) + page_width, page_height = A4 + + for filename in filenames: + file_path = os.path.join(UPLOAD_FOLDER, filename) + + if os.path.exists(file_path): + # Bild öffnen und Größe anpassen + with Image.open(file_path) as img: + img_width, img_height = img.size + + # Seitenverhältnis berechnen + aspect_ratio = img_width / img_height + + # Maximale Größe auf der Seite (mit Rändern) + max_width = page_width - 40 + max_height = page_height - 40 + + if aspect_ratio > 1: # Querformat + new_width = min(max_width, img_width) + new_height = new_width / aspect_ratio + else: # Hochformat + new_height = min(max_height, img_height) + new_width = new_height * aspect_ratio + + # Zentrieren + x = (page_width - new_width) / 2 + y = (page_height - new_height) / 2 + + # Bild zur PDF hinzufügen + c.drawImage(file_path, x, y, width=new_width, height=new_height) + c.showPage() + + c.save() + + return jsonify({ + 'success': True, + 'filename': output_filename, + 'message': 'PDF erfolgreich erstellt' + }) + + except Exception as e: + return jsonify({'error': f'Fehler bei der Konvertierung: {str(e)}'}), 500 + +@app.route('/api/upload-pdf', methods=['POST']) +def upload_pdf(): + """API Endpunkt für PDF Upload""" + try: + if 'file' not in request.files: + return jsonify({'error': 'Keine Datei ausgewählt'}), 400 + + file = request.files['file'] + + if file.filename == '': + return jsonify({'error': 'Keine Datei ausgewählt'}), 400 + + if file and allowed_file(file.filename, ALLOWED_PDF_EXTENSIONS): + filename = secure_filename(file.filename) + unique_filename = create_unique_filename(filename) + file_path = os.path.join(UPLOAD_FOLDER, unique_filename) + file.save(file_path) + + # PDF Info lesen + with open(file_path, 'rb') as pdf_file: + pdf_reader = PyPDF2.PdfReader(pdf_file) + page_count = len(pdf_reader.pages) + + return jsonify({ + 'success': True, + 'filename': unique_filename, + 'original_name': filename, + 'page_count': page_count, + 'size': os.path.getsize(file_path) + }) + + return jsonify({'error': 'Nur PDF-Dateien sind erlaubt'}), 400 + + except Exception as e: + return jsonify({'error': f'Fehler beim Upload: {str(e)}'}), 500 + +@app.route('/api/pdf-to-images', methods=['POST']) +def pdf_to_images(): + """Konvertiert PDF zu Bildern""" + try: + data = request.get_json() + filename = data.get('filename') + + if not filename: + return jsonify({'error': 'Keine Datei angegeben'}), 400 + + file_path = os.path.join(UPLOAD_FOLDER, filename) + + if not os.path.exists(file_path): + return jsonify({'error': 'Datei nicht gefunden'}), 404 + + # PDF zu Bildern konvertieren + images = pdf2image.convert_from_path(file_path, dpi=200) + + # ZIP-Datei erstellen + zip_filename = f"pdf_images_{str(uuid.uuid4())[:8]}.zip" + zip_path = os.path.join(OUTPUT_FOLDER, zip_filename) + + with zipfile.ZipFile(zip_path, 'w') as zip_file: + for i, image in enumerate(images): + img_filename = f"page_{i+1}.png" + img_buffer = io.BytesIO() + image.save(img_buffer, format='PNG') + img_buffer.seek(0) + zip_file.writestr(img_filename, img_buffer.read()) + + return jsonify({ + 'success': True, + 'filename': zip_filename, + 'page_count': len(images), + 'message': f'{len(images)} Seiten als Bilder exportiert' + }) + + except Exception as e: + return jsonify({'error': f'Fehler bei der Konvertierung: {str(e)}'}), 500 + +@app.route('/api/merge-pdfs', methods=['POST']) +def merge_pdfs(): + """Fügt mehrere PDFs zusammen""" + try: + data = request.get_json() + filenames = data.get('filenames', []) + + if len(filenames) < 2: + return jsonify({'error': 'Mindestens 2 PDF-Dateien erforderlich'}), 400 + + # PDF Merger erstellen + merger = PyPDF2.PdfMerger() + + for filename in filenames: + file_path = os.path.join(UPLOAD_FOLDER, filename) + if os.path.exists(file_path): + merger.append(file_path) + + # Zusammengeführte PDF speichern + output_filename = f"merged_pdf_{str(uuid.uuid4())[:8]}.pdf" + output_path = os.path.join(OUTPUT_FOLDER, output_filename) + + with open(output_path, 'wb') as output_file: + merger.write(output_file) + + merger.close() + + return jsonify({ + 'success': True, + 'filename': output_filename, + 'message': f'{len(filenames)} PDFs erfolgreich zusammengeführt' + }) + + except Exception as e: + return jsonify({'error': f'Fehler beim Zusammenführen: {str(e)}'}), 500 + +@app.route('/download/') +def download_file(filename): + """Download einer generierten Datei""" + try: + file_path = os.path.join(OUTPUT_FOLDER, filename) + + if not os.path.exists(file_path): + flash('Datei nicht gefunden', 'error') + return redirect(url_for('index')) + + return send_file(file_path, as_attachment=True) + + except Exception as e: + flash(f'Fehler beim Download: {str(e)}', 'error') + return redirect(url_for('index')) + +if __name__ == '__main__': + # Ordner erstellen falls sie nicht existieren + os.makedirs(UPLOAD_FOLDER, exist_ok=True) + os.makedirs(OUTPUT_FOLDER, exist_ok=True) + + app.run(debug=True, host='127.0.0.1', port=5000) \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..0211c70 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,80 @@ + + + + + + {% block title %}PDF Editor{% endblock %} + + + + + + + + + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, message in messages %} + + {% endfor %} +
+ {% endif %} + {% endwith %} + + +
+ {% block content %}{% endblock %} +
+ + + + + + + + {% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/templates/images_to_pdf.html b/templates/images_to_pdf.html new file mode 100644 index 0000000..07d2d3a --- /dev/null +++ b/templates/images_to_pdf.html @@ -0,0 +1,171 @@ +{% extends "base.html" %} + +{% block title %}Bilder zu PDF - PDF Editor{% endblock %} + +{% block content %} +
+
+ +
+
+ +
+ +
+
+
+
+ Bilder hochladen +
+
+
+ +
+
+ +
Bilder hier hinziehen oder klicken zum Auswählen
+

+ Unterstützte Formate: JPG, PNG, GIF, BMP, TIFF
+ Maximale Dateigröße: 16 MB pro Datei +

+ + +
+
+ + + + + + + + + + + + +
+
+
+ + +
+
+
+
+ Einstellungen +
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+
+
+ Tipps +
+
+
+
    +
  • + + Ziehen Sie Dateien in der Liste, um die Reihenfolge zu ändern +
  • +
  • + + Hochauflösende Bilder ergeben bessere PDF-Qualität +
  • +
  • + + Jedes Bild wird auf eine separate PDF-Seite platziert +
  • +
  • + + Alle Dateien werden lokal verarbeitet - maximale Sicherheit +
  • +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..43bf1c0 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,154 @@ +{% extends "base.html" %} + +{% block title %}PDF Editor - Home{% endblock %} + +{% block content %} +
+
+ +
+

+ PDF Editor Web App +

+

+ Ihre lokale Lösung für PDF-Bearbeitung: Bilder zu PDF konvertieren, + PDFs zusammenführen, Seiten extrahieren und vieles mehr. +

+
+

Einfach zu bedienen, vollständig lokal und sicher.

+
+
+
+ +
+ +
+
+
+
+ +
+
Bilder zu PDF
+

+ Wählen Sie mehrere Bilder aus und konvertieren Sie diese + in eine einzige PDF-Datei. Unterstützt JPG, PNG, GIF und mehr. +

+
+ + Drag & Drop Support
+ Automatische Größenanpassung
+ Reihenfolge sortierbar +
+
+
+ +
+
+ + +
+
+
+
+ +
+
PDF Tools
+

+ Erweiterte PDF-Bearbeitungswerkzeuge für alle Ihre Anforderungen. + Zusammenführen, teilen und konvertieren. +

+
+ + PDFs zusammenführen
+ PDF zu Bildern
+ Seiten extrahieren +
+
+
+ +
+
+
+ + +
+
+
+
+
+ Wie es funktioniert +
+
+
+
+ +
+
1. Dateien hochladen
+

+ Ziehen Sie Ihre Dateien einfach in den Upload-Bereich + oder klicken Sie zum Auswählen. +

+
+
+
+ +
+
2. Bearbeiten
+

+ Sortieren Sie die Reihenfolge, wählen Sie Optionen + und starten Sie die Verarbeitung. +

+
+
+
+ +
+
3. Herunterladen
+

+ Laden Sie Ihre fertige PDF-Datei oder + das Ergebnis direkt herunter. +

+
+
+
+
+
+
+ + +
+
+

Warum PDF Editor wählen?

+
+
+ +
100% Lokal
+ Ihre Dateien verlassen nie Ihren Computer +
+
+ +
Schnell
+ Keine Upload-Wartezeiten, sofortige Verarbeitung +
+
+ +
Responsiv
+ Funktioniert auf Desktop, Tablet und Handy +
+
+ +
Open Source
+ Transparenter Code, den Sie verstehen können +
+
+
+
+{% endblock %} \ No newline at end of file