#!/usr/bin/env python3 """Сжатие только скриншотов в Report/images/ (не PlantUML PNG). JPEG: quality=28 (ImageMagick). PNG-скриншоты (Gradle, ручная схема): pngquant quality 25–40. """ from __future__ import annotations import re import shutil import subprocess import sys from pathlib import Path # Имена из IMAGES_REGISTRY: Скриншот UI, Gradle, Ручная схема SCREENSHOT_STEMS = { "fig_05_local_vaults", "fig_06_encrypt_dialog", "fig_07_open_close_dialog", "fig_08_rename_delete_dialog", "fig_09_remote_vaults", "fig_10_yandex_oauth", "fig_12_tasks_screen", "fig_13_tasks_notification", "fig_33_storage_secrets_2fa", "fig_34_2fa_single_token", "fig_24_domain_class_manual", "fig_27_gradle_domain_test", "fig_28_gradle_usecases_test", "fig_29_gradle_ui_test", "fig_30_gradle_test_summary", "fig_31_gradle_connected_test", } JPEG_QUALITY = "28" PNGQUANT_QUALITY = "25-40" def stem(path: Path) -> str: return path.stem def is_screenshot(path: Path) -> bool: if path.suffix.lower() == ".jpg": return stem(path) in SCREENSHOT_STEMS or path.name.startswith("fig_") if path.suffix.lower() == ".png": return stem(path) in SCREENSHOT_STEMS return False def size_kb(path: Path) -> float: return path.stat().st_size / 1024 def compress_jpeg(path: Path, magick: str) -> None: tmp = path.with_suffix(path.suffix + ".tmp") subprocess.run( [magick, str(path), "-strip", "-quality", JPEG_QUALITY, str(tmp)], check=True, capture_output=True, ) tmp.replace(path) def compress_png(path: Path, pngquant: str) -> bool: tmp = path.with_suffix(".pngquant.tmp.png") for quality in (PNGQUANT_QUALITY, "15-55", "10-80"): try: subprocess.run( [ pngquant, f"--quality={quality}", "--force", "--output", str(tmp), str(path), ], check=True, capture_output=True, ) tmp.replace(path) return True except subprocess.CalledProcessError: if tmp.exists(): tmp.unlink() print(f" WARN: pngquant skip {path.name}", file=sys.stderr) return False def main() -> int: report = Path(__file__).resolve().parents[1] images = report / "images" magick = shutil.which("magick") or shutil.which("convert") pngquant = shutil.which("pngquant") if not magick: print("ERROR: ImageMagick (magick/convert) not found", file=sys.stderr) return 1 if not pngquant: print("ERROR: pngquant not found", file=sys.stderr) return 1 targets = sorted( p for p in images.iterdir() if p.is_file() and is_screenshot(p) ) if not targets: print("No screenshots to compress", file=sys.stderr) return 1 total_before = 0.0 total_after = 0.0 for path in targets: before = size_kb(path) total_before += before if path.suffix.lower() in {".jpg", ".jpeg"}: compress_jpeg(path, magick) else: compress_png(path, pngquant) after = size_kb(path) total_after += after pct = (1 - after / before) * 100 if before else 0 print(f" {path.name}: {before:.0f} KiB → {after:.0f} KiB (−{pct:.0f}%)") saved = (1 - total_after / total_before) * 100 if total_before else 0 print( f"OK: {len(targets)} screenshots, " f"{total_before:.0f} KiB → {total_after:.0f} KiB (−{saved:.0f}%)" ) return 0 if __name__ == "__main__": sys.exit(main())