Files
SaaS-PDF/backend/app/services/compress_image_service.py
Your Name d7f6228d7f الميزات: إضافة أدوات جديدة لمعالجة ملفات PDF، تشمل التلخيص والترجمة واستخراج الجداول.
- تفعيل مكون SummarizePdf لإنشاء ملخصات PDF باستخدام الذكاء الاصطناعي.

- تفعيل مكون TranslatePdf لترجمة محتوى PDF إلى لغات متعددة.

- تفعيل مكون TableExtractor لاستخراج الجداول من ملفات PDF.

- تحديث الصفحة الرئيسية والتوجيه ليشمل الأدوات الجديدة.

- إضافة ترجمات للأدوات الجديدة باللغات الإنجليزية والعربية والفرنسية.

- توسيع أنواع واجهة برمجة التطبيقات (API) لدعم الميزات الجديدة المتعلقة بمعالجة ملفات PDF. --feat: Initialize frontend with React, Vite, and Tailwind CSS

- Set up main entry point for React application.
- Create About, Home, NotFound, Privacy, and Terms pages with SEO support.
- Implement API service for file uploads and task management.
- Add global styles using Tailwind CSS.
- Create utility functions for SEO and text processing.
- Configure Vite for development and production builds.
- Set up Nginx configuration for serving frontend and backend.
- Add scripts for cleanup of expired files and sitemap generation.
- Implement deployment script for production environment.
2026-03-08 05:49:09 +02:00

91 lines
2.5 KiB
Python

"""Image compression service using Pillow."""
import os
import logging
from PIL import Image
logger = logging.getLogger(__name__)
class CompressImageError(Exception):
"""Custom exception for image compression failures."""
pass
FORMAT_MAP = {
"jpg": "JPEG",
"jpeg": "JPEG",
"png": "PNG",
"webp": "WEBP",
}
def compress_image(
input_path: str,
output_path: str,
quality: int = 75,
) -> dict:
"""
Compress an image by reducing quality and optimizing encoding.
Args:
input_path: Path to the input image
output_path: Path for the compressed image
quality: Output quality 1-100
Returns:
dict with original_size, compressed_size, reduction_percent
Raises:
CompressImageError: If compression fails
"""
quality = max(1, min(100, quality))
os.makedirs(os.path.dirname(output_path), exist_ok=True)
try:
original_size = os.path.getsize(input_path)
with Image.open(input_path) as img:
width, height = img.size
ext = os.path.splitext(output_path)[1].lower().strip(".")
pil_format = FORMAT_MAP.get(ext, "JPEG")
# Convert RGBA to RGB for JPEG
if pil_format == "JPEG" and img.mode in ("RGBA", "P", "LA"):
background = Image.new("RGB", img.size, (255, 255, 255))
if img.mode == "P":
img = img.convert("RGBA")
background.paste(
img, mask=img.split()[-1] if "A" in img.mode else None
)
img = background
save_kwargs = {"optimize": True}
if pil_format in ("JPEG", "WEBP"):
save_kwargs["quality"] = quality
elif pil_format == "PNG":
save_kwargs["compress_level"] = 9
img.save(output_path, format=pil_format, **save_kwargs)
compressed_size = os.path.getsize(output_path)
reduction = round(
(1 - compressed_size / original_size) * 100, 1
) if original_size > 0 else 0
logger.info(
f"Image compression: {original_size}{compressed_size} "
f"({reduction}% reduction)"
)
return {
"original_size": original_size,
"compressed_size": compressed_size,
"reduction_percent": reduction,
"width": width,
"height": height,
}
except (IOError, OSError, Image.DecompressionBombError) as e:
raise CompressImageError(f"Image compression failed: {str(e)}")