Corp/backend/employees/ldap/utils/image_utils.py
Игорь Надеин 4d04aafdb9 refactor: Replace inline CSS with components (Phase 3)
- Replaced feed-* inline styles in base.html with feed-cards.css component (~60 lines)
- Replaced ios-search inline styles in 3 templates with ios-search.css component
  * employees/employees_list.html
  * search/results.html
  * employees/department_list.html
- Eliminated ~150 lines of duplicate CSS across templates
- All 17 tests passing
2025-10-28 08:54:38 +03:00

74 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Утилиты для обработки изображений (аватары).
Функции для нормализации и конвертации аватаров пользователей в формат,
совместимый с Active Directory.
"""
import io
try:
from PIL import Image
except ImportError:
Image = None # type: ignore
def normalize_avatar_to_jpeg(
data: bytes, *, size_px: int = 120, max_kb: int = 100
) -> bytes:
"""Нормализует аватар в JPEG нужного размера и лимита.
Args:
data (bytes): Входной образ (PNG/JPEG/...).
size_px (int): Квадратный размер целевого изображения (по умолчанию 120).
max_kb (int): Максимальный размер файла в килобайтах
(ограничение AD ~100 KB).
Returns:
bytes: JPEG-данные, соответствующие ограничениям.
Raises:
RuntimeError: Если Pillow недоступен или не удалось обработать
изображение.
ValueError: Если вход пустой.
"""
if not data:
raise ValueError("avatar: пустые данные")
if Image is None:
raise RuntimeError(
"Pillow не установлен. Установите 'Pillow' для обработки аватаров."
)
try:
img = Image.open(io.BytesIO(data))
img = img.convert("RGB")
img = img.resize((size_px, size_px))
# бинарный поиск по качеству для соблюдения max_kb
lo, hi = 50, 95
best = None
while lo <= hi:
mid = (lo + hi) // 2
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=mid, optimize=True)
kb = len(buf.getvalue()) // 1024
if kb <= max_kb:
best = buf.getvalue()
lo = mid + 1
else:
hi = mid - 1
if best is None:
# минимально возможное
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=50, optimize=True)
best = buf.getvalue()
return best
except Exception as exc: # pragma: no cover
raise RuntimeError(f"avatar: ошибка нормализации: {exc}") from exc
__all__ = [
"normalize_avatar_to_jpeg",
]