Android app for HART protocol field devices (Bluetooth SPP / USB CP210x). Kotlin, MVVM, Jetpack Navigation, Material Design. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
112 lines
4.0 KiB
Python
112 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Обработка скриншотов для RuStore.
|
||
- Масштабирование до 1080x1920
|
||
- Добавление подписи сверху на синей полосе
|
||
- Вывод в PNG
|
||
"""
|
||
|
||
from PIL import Image, ImageDraw, ImageFont
|
||
import os
|
||
|
||
INPUT_DIR = os.path.join(os.path.dirname(__file__), '..', 'temp_screenshots')
|
||
OUTPUT_DIR = os.path.join(os.path.dirname(__file__), '..', 'store_screenshots')
|
||
|
||
TARGET_W = 1080
|
||
TARGET_H = 1920
|
||
HEADER_H = 160 # высота полосы с подписью
|
||
HEADER_COLOR = (55, 71, 161) # синий как в приложении (#3747A1)
|
||
TEXT_COLOR = (255, 255, 255)
|
||
|
||
# Маппинг файлов → подписи (порядок по времени)
|
||
SCREENSHOTS = [
|
||
("photo_2026-03-17 23.08.50.jpeg", "Параметры устройства", "01_device.png"),
|
||
("photo_2026-03-17 23.08.49.jpeg", "Тренд температуры", "02_trend.png"),
|
||
("photo_2026-03-17 23.08.48.jpeg", "Loop Test", "03_loop_test.png"),
|
||
("photo_2026-03-17 23.08.47.jpeg", "Поиск устройств", "04_poll_scan.png"),
|
||
("photo_2026-03-17 23.08.45.jpeg", "Подключение", "05_scan.png"),
|
||
]
|
||
|
||
|
||
def find_font(size):
|
||
"""Ищем подходящий шрифт."""
|
||
font_paths = [
|
||
"/System/Library/Fonts/Helvetica.ttc",
|
||
"/System/Library/Fonts/SFNSDisplay.ttf",
|
||
"/System/Library/Fonts/Supplemental/Arial Bold.ttf",
|
||
"/System/Library/Fonts/Supplemental/Arial.ttf",
|
||
"/Library/Fonts/Arial Bold.ttf",
|
||
]
|
||
for p in font_paths:
|
||
if os.path.exists(p):
|
||
try:
|
||
return ImageFont.truetype(p, size)
|
||
except Exception:
|
||
continue
|
||
return ImageFont.load_default()
|
||
|
||
|
||
def process_screenshot(input_path, title, output_path):
|
||
img = Image.open(input_path).convert("RGB")
|
||
|
||
# Область для скриншота: TARGET_W x (TARGET_H - HEADER_H)
|
||
content_h = TARGET_H - HEADER_H
|
||
|
||
# Масштабируем скриншот, сохраняя пропорции, заполняя ширину
|
||
scale = TARGET_W / img.width
|
||
new_h = int(img.height * scale)
|
||
|
||
img_scaled = img.resize((TARGET_W, new_h), Image.LANCZOS)
|
||
|
||
# Обрезаем или центрируем по высоте
|
||
if new_h > content_h:
|
||
# Обрезаем снизу (статус-бар сверху важнее)
|
||
img_scaled = img_scaled.crop((0, 0, TARGET_W, content_h))
|
||
elif new_h < content_h:
|
||
# Центрируем на белом фоне
|
||
bg = Image.new("RGB", (TARGET_W, content_h), (255, 255, 255))
|
||
offset_y = (content_h - new_h) // 2
|
||
bg.paste(img_scaled, (0, offset_y))
|
||
img_scaled = bg
|
||
|
||
# Создаём итоговое изображение
|
||
result = Image.new("RGB", (TARGET_W, TARGET_H), (255, 255, 255))
|
||
|
||
# Рисуем шапку
|
||
draw = ImageDraw.Draw(result)
|
||
draw.rectangle([(0, 0), (TARGET_W, HEADER_H)], fill=HEADER_COLOR)
|
||
|
||
# Текст по центру шапки
|
||
font = find_font(48)
|
||
bbox = draw.textbbox((0, 0), title, font=font)
|
||
tw = bbox[2] - bbox[0]
|
||
th = bbox[3] - bbox[1]
|
||
tx = (TARGET_W - tw) // 2
|
||
ty = (HEADER_H - th) // 2
|
||
draw.text((tx, ty), title, fill=TEXT_COLOR, font=font)
|
||
|
||
# Вставляем скриншот под шапку
|
||
result.paste(img_scaled, (0, HEADER_H))
|
||
|
||
result.save(output_path, "PNG", optimize=True)
|
||
print(f" OK: {output_path} ({TARGET_W}x{TARGET_H})")
|
||
|
||
|
||
def main():
|
||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||
print(f"RuStore screenshots → {OUTPUT_DIR}\n")
|
||
|
||
for filename, title, out_name in SCREENSHOTS:
|
||
input_path = os.path.join(INPUT_DIR, filename)
|
||
output_path = os.path.join(OUTPUT_DIR, out_name)
|
||
if not os.path.exists(input_path):
|
||
print(f" SKIP: {filename} not found")
|
||
continue
|
||
process_screenshot(input_path, title, output_path)
|
||
|
||
print(f"\nГотово! {len(SCREENSHOTS)} скриншотов обработано.")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|