#!/usr/bin/env python3 """ build-sprite.py — يبني sprite sheet لـ MapLibre من مجلد SVG. لماذا: MapLibre لا يقرأ ملفات SVG مفردة. يحتاج صورة PNG واحدة تُرصف فيها كل الآيقونات + فهرس JSON يحدد موضع وحجم كل واحدة. كان الستايل يشير إلى demotiles.maplibre.org (خادم تجريبي)، وحين يفشل تحميله يرسم MapLibre النص بلا آيقونة وبصمت — فتبدو الخريطة بأسماء بلا رموز. الاستخدام: pip install cairosvg pillow python3 infrastructure/scripts/build-sprite.py المخرجات في apps/web/public/: sprite.png / sprite.json (1x) sprite@2x.png / sprite@2x.json (شاشات Retina — MapLibre يطلبها تلقائياً) ثم في الستايل: "sprite": "https://map-saas.intaleqapp.com/sprite" (بلا امتداد — MapLibre يضيف .png/.json و@2x بنفسه) """ import json import os import sys try: import cairosvg from PIL import Image except ImportError: sys.exit("ينقص: pip install cairosvg pillow") ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) ICONS_DIR = os.path.join(ROOT, "apps", "web", "public", "icons") OUT_DIR = os.path.join(ROOT, "apps", "web", "public") BASE_SIZE = 24 # بكسل للآيقونة عند 1x PADDING = 2 # فاصل يمنع نزّ ألوان الجيران عند التصغير (bleeding) def build(scale: int, suffix: str) -> None: size = BASE_SIZE * scale pad = PADDING * scale svgs = sorted(f for f in os.listdir(ICONS_DIR) if f.endswith(".svg")) if not svgs: sys.exit(f"لا توجد ملفات SVG في {ICONS_DIR}") rendered = [] for fname in svgs: name = os.path.splitext(fname)[0] png_bytes = cairosvg.svg2png( url=os.path.join(ICONS_DIR, fname), output_width=size, output_height=size, ) import io rendered.append((name, Image.open(io.BytesIO(png_bytes)).convert("RGBA"))) # شبكة مربعة تقريباً: أقل هدراً من صف واحد طويل cols = max(1, int(len(rendered) ** 0.5 + 0.999)) rows = (len(rendered) + cols - 1) // cols sheet = Image.new( "RGBA", (cols * (size + pad) - pad, rows * (size + pad) - pad), (0, 0, 0, 0), ) index = {} for i, (name, img) in enumerate(rendered): x = (i % cols) * (size + pad) y = (i // cols) * (size + pad) sheet.paste(img, (x, y)) index[name] = { "x": x, "y": y, "width": size, "height": size, "pixelRatio": scale, "sdf": False, } sheet.save(os.path.join(OUT_DIR, f"sprite{suffix}.png")) with open(os.path.join(OUT_DIR, f"sprite{suffix}.json"), "w") as fh: json.dump(index, fh, indent=2) print(f"✅ sprite{suffix}: {len(index)} آيقونة، {sheet.width}×{sheet.height}px") return list(index) if __name__ == "__main__": names = build(1, "") build(2, "@2x") print("\nالآيقونات المتاحة لـ icon-image:") print(" " + ", ".join(names)) print("\n⚠️ طبقات الستايل تطلب أسماء قد لا تكون موجودة أعلاه") print(" (college, rail, tourist ...) — راجعها أو أضف SVG مقابلاً لكل ناقص.")