new file: app.py
new file: templates/base.html
This commit is contained in:
302
app.py
Normal file
302
app.py
Normal file
@@ -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/<filename>')
|
||||
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)
|
||||
80
templates/base.html
Normal file
80
templates/base.html
Normal file
@@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}PDF Editor{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{{ url_for('index') }}">
|
||||
<i class="fas fa-file-pdf me-2"></i>PDF Editor
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('index') }}">
|
||||
<i class="fas fa-home me-1"></i>Home
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('images_to_pdf_page') }}">
|
||||
<i class="fas fa-images me-1"></i>Bilder zu PDF
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('pdf_tools_page') }}">
|
||||
<i class="fas fa-tools me-1"></i>PDF Tools
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Flash Messages -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="container mt-3">
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'danger' if category == 'error' else 'success' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="container mt-4">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-light mt-5 py-4">
|
||||
<div class="container text-center">
|
||||
<p class="mb-0 text-muted">
|
||||
<i class="fas fa-file-pdf me-2"></i>PDF Editor Web App
|
||||
<span class="mx-2">|</span>
|
||||
Lokaler Flask Server
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
171
templates/images_to_pdf.html
Normal file
171
templates/images_to_pdf.html
Normal file
@@ -0,0 +1,171 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Bilder zu PDF - PDF Editor{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="page-header mb-4">
|
||||
<h2>
|
||||
<i class="fas fa-images me-2"></i>Bilder zu PDF konvertieren
|
||||
</h2>
|
||||
<p class="text-muted">
|
||||
Laden Sie mehrere Bilder hoch und konvertieren Sie diese in eine PDF-Datei.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Upload Area -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">
|
||||
<i class="fas fa-upload me-2"></i>Bilder hochladen
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- File Upload Area -->
|
||||
<div id="upload-area" class="upload-area border-dashed p-4 text-center">
|
||||
<div class="upload-content">
|
||||
<i class="fas fa-cloud-upload-alt fa-3x text-muted mb-3"></i>
|
||||
<h5>Bilder hier hinziehen oder klicken zum Auswählen</h5>
|
||||
<p class="text-muted mb-3">
|
||||
Unterstützte Formate: JPG, PNG, GIF, BMP, TIFF<br>
|
||||
Maximale Dateigröße: 16 MB pro Datei
|
||||
</p>
|
||||
<input type="file" id="file-input" multiple accept="image/*" class="d-none">
|
||||
<button type="button" class="btn btn-primary" onclick="document.getElementById('file-input').click()">
|
||||
<i class="fas fa-folder-open me-2"></i>Dateien auswählen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div id="upload-progress" class="mt-3" style="display: none;">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<small class="text-muted">Upload läuft...</small>
|
||||
<small id="progress-text" class="text-muted">0%</small>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div id="progress-bar" class="progress-bar" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File List -->
|
||||
<div id="file-list" class="mt-4" style="display: none;">
|
||||
<h6>Hochgeladene Dateien:</h6>
|
||||
<div id="files-container" class="list-group sortable-list">
|
||||
<!-- Files will be added here dynamically -->
|
||||
</div>
|
||||
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button id="convert-btn" class="btn btn-success" disabled>
|
||||
<i class="fas fa-file-pdf me-2"></i>Zu PDF konvertieren
|
||||
</button>
|
||||
<button id="clear-btn" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-trash me-2"></i>Alle entfernen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Result Area -->
|
||||
<div id="result-area" class="mt-4" style="display: none;">
|
||||
<div class="alert alert-success">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-check-circle fa-2x me-3"></i>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1">PDF erfolgreich erstellt!</h6>
|
||||
<p class="mb-0">Ihre Bilder wurden erfolgreich in eine PDF-Datei konvertiert.</p>
|
||||
</div>
|
||||
<a id="download-link" href="#" class="btn btn-success">
|
||||
<i class="fas fa-download me-2"></i>PDF herunterladen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error Area -->
|
||||
<div id="error-area" class="mt-4" style="display: none;">
|
||||
<div class="alert alert-danger">
|
||||
<h6><i class="fas fa-exclamation-triangle me-2"></i>Fehler</h6>
|
||||
<p id="error-message" class="mb-0"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-cog me-2"></i>Einstellungen
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Seitenformat:</label>
|
||||
<select id="page-format" class="form-select">
|
||||
<option value="A4">A4 (210 × 297 mm)</option>
|
||||
<option value="Letter">Letter (216 × 279 mm)</option>
|
||||
<option value="A3">A3 (297 × 420 mm)</option>
|
||||
<option value="A5">A5 (148 × 210 mm)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Bildanpassung:</label>
|
||||
<select id="image-fit" class="form-select">
|
||||
<option value="fit">An Seite anpassen</option>
|
||||
<option value="fill">Seite füllen</option>
|
||||
<option value="center">Zentrieren</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="maintain-aspect-ratio" checked>
|
||||
<label class="form-check-label">
|
||||
Seitenverhältnis beibehalten
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tips -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-lightbulb me-2"></i>Tipps
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li class="mb-2">
|
||||
<i class="fas fa-sort me-2 text-muted"></i>
|
||||
<small>Ziehen Sie Dateien in der Liste, um die Reihenfolge zu ändern</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<i class="fas fa-images me-2 text-muted"></i>
|
||||
<small>Hochauflösende Bilder ergeben bessere PDF-Qualität</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<i class="fas fa-file-pdf me-2 text-muted"></i>
|
||||
<small>Jedes Bild wird auf eine separate PDF-Seite platziert</small>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-shield-alt me-2 text-muted"></i>
|
||||
<small>Alle Dateien werden lokal verarbeitet - maximale Sicherheit</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/images-to-pdf.js') }}"></script>
|
||||
{% endblock %}
|
||||
154
templates/index.html
Normal file
154
templates/index.html
Normal file
@@ -0,0 +1,154 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}PDF Editor - Home{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- Hero Section -->
|
||||
<div class="jumbotron bg-gradient text-white rounded p-5 mb-5">
|
||||
<h1 class="display-4">
|
||||
<i class="fas fa-file-pdf me-3"></i>PDF Editor Web App
|
||||
</h1>
|
||||
<p class="lead">
|
||||
Ihre lokale Lösung für PDF-Bearbeitung: Bilder zu PDF konvertieren,
|
||||
PDFs zusammenführen, Seiten extrahieren und vieles mehr.
|
||||
</p>
|
||||
<hr class="my-4">
|
||||
<p>Einfach zu bedienen, vollständig lokal und sicher.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Bilder zu PDF -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 feature-card">
|
||||
<div class="card-body text-center">
|
||||
<div class="icon-wrapper mb-3">
|
||||
<i class="fas fa-images fa-3x text-primary"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Bilder zu PDF</h5>
|
||||
<p class="card-text">
|
||||
Wählen Sie mehrere Bilder aus und konvertieren Sie diese
|
||||
in eine einzige PDF-Datei. Unterstützt JPG, PNG, GIF und mehr.
|
||||
</p>
|
||||
<div class="features-list mb-3">
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-check me-2"></i>Drag & Drop Support<br>
|
||||
<i class="fas fa-check me-2"></i>Automatische Größenanpassung<br>
|
||||
<i class="fas fa-check me-2"></i>Reihenfolge sortierbar
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{{ url_for('images_to_pdf_page') }}" class="btn btn-primary w-100">
|
||||
<i class="fas fa-arrow-right me-2"></i>Jetzt starten
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PDF Tools -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 feature-card">
|
||||
<div class="card-body text-center">
|
||||
<div class="icon-wrapper mb-3">
|
||||
<i class="fas fa-tools fa-3x text-success"></i>
|
||||
</div>
|
||||
<h5 class="card-title">PDF Tools</h5>
|
||||
<p class="card-text">
|
||||
Erweiterte PDF-Bearbeitungswerkzeuge für alle Ihre Anforderungen.
|
||||
Zusammenführen, teilen und konvertieren.
|
||||
</p>
|
||||
<div class="features-list mb-3">
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-check me-2"></i>PDFs zusammenführen<br>
|
||||
<i class="fas fa-check me-2"></i>PDF zu Bildern<br>
|
||||
<i class="fas fa-check me-2"></i>Seiten extrahieren
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{{ url_for('pdf_tools_page') }}" class="btn btn-success w-100">
|
||||
<i class="fas fa-arrow-right me-2"></i>Tools öffnen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info Section -->
|
||||
<div class="row mt-5">
|
||||
<div class="col-12">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-info-circle me-2"></i>Wie es funktioniert
|
||||
</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="step-icon mb-3">
|
||||
<i class="fas fa-upload fa-2x text-primary"></i>
|
||||
</div>
|
||||
<h6>1. Dateien hochladen</h6>
|
||||
<p class="small text-muted">
|
||||
Ziehen Sie Ihre Dateien einfach in den Upload-Bereich
|
||||
oder klicken Sie zum Auswählen.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="step-icon mb-3">
|
||||
<i class="fas fa-cogs fa-2x text-warning"></i>
|
||||
</div>
|
||||
<h6>2. Bearbeiten</h6>
|
||||
<p class="small text-muted">
|
||||
Sortieren Sie die Reihenfolge, wählen Sie Optionen
|
||||
und starten Sie die Verarbeitung.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="step-icon mb-3">
|
||||
<i class="fas fa-download fa-2x text-success"></i>
|
||||
</div>
|
||||
<h6>3. Herunterladen</h6>
|
||||
<p class="small text-muted">
|
||||
Laden Sie Ihre fertige PDF-Datei oder
|
||||
das Ergebnis direkt herunter.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
<div class="row mt-5">
|
||||
<div class="col-12">
|
||||
<h3 class="text-center mb-4">Warum PDF Editor wählen?</h3>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3 text-center">
|
||||
<i class="fas fa-shield-alt fa-2x text-primary mb-2"></i>
|
||||
<h6>100% Lokal</h6>
|
||||
<small class="text-muted">Ihre Dateien verlassen nie Ihren Computer</small>
|
||||
</div>
|
||||
<div class="col-md-3 text-center">
|
||||
<i class="fas fa-bolt fa-2x text-warning mb-2"></i>
|
||||
<h6>Schnell</h6>
|
||||
<small class="text-muted">Keine Upload-Wartezeiten, sofortige Verarbeitung</small>
|
||||
</div>
|
||||
<div class="col-md-3 text-center">
|
||||
<i class="fas fa-mobile-alt fa-2x text-success mb-2"></i>
|
||||
<h6>Responsiv</h6>
|
||||
<small class="text-muted">Funktioniert auf Desktop, Tablet und Handy</small>
|
||||
</div>
|
||||
<div class="col-md-3 text-center">
|
||||
<i class="fas fa-code fa-2x text-info mb-2"></i>
|
||||
<h6>Open Source</h6>
|
||||
<small class="text-muted">Transparenter Code, den Sie verstehen können</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user