from pathlib import Path from PIL import Image, ImageDraw, ImageFont ROOT = Path(__file__).resolve().parents[1] OUTPUT = ROOT / "store-assets" GREEN = "#075E54" GREEN_LIGHT = "#83B6A5" ORANGE = "#F97316" INK = "#18342F" PAPER = "#F7FAF5" LINE = "#BCD3CB" BLUE = "#DCEAF0" def font(size: int, semibold: bool = False) -> ImageFont.FreeTypeFont: name = "seguisb.ttf" if semibold else "segoeui.ttf" return ImageFont.truetype(str(Path("C:/Windows/Fonts") / name), size) def draw_mark(draw: ImageDraw.ImageDraw, box: tuple[int, int, int, int]) -> None: left, top, right, bottom = box width = right - left height = bottom - top map_points = [ (left + 0.04 * width, top + 0.30 * height), (left + 0.32 * width, top + 0.18 * height), (left + 0.64 * width, top + 0.30 * height), (left + 0.94 * width, top + 0.18 * height), (left + 0.94 * width, top + 0.76 * height), (left + 0.64 * width, top + 0.88 * height), (left + 0.32 * width, top + 0.76 * height), (left + 0.04 * width, top + 0.88 * height), ] draw.polygon(map_points, fill=PAPER, outline="#E1ECE5", width=max(4, width // 38)) draw.line( [(left + 0.32 * width, top + 0.18 * height), (left + 0.32 * width, top + 0.76 * height)], fill=GREEN_LIGHT, width=max(4, width // 35), ) draw.line( [(left + 0.64 * width, top + 0.30 * height), (left + 0.64 * width, top + 0.88 * height)], fill=GREEN_LIGHT, width=max(4, width // 35), ) cx = left + 0.50 * width cy = top + 0.36 * height radius = 0.20 * width pin = [ (cx, cy + radius * 1.72), (cx - radius * 0.86, cy + radius * 0.20), (cx - radius, cy - radius * 0.18), (cx - radius * 0.72, cy - radius * 0.72), (cx - radius * 0.22, cy - radius), (cx + radius * 0.22, cy - radius), (cx + radius * 0.72, cy - radius * 0.72), (cx + radius, cy - radius * 0.18), (cx + radius * 0.86, cy + radius * 0.20), ] draw.polygon(pin, fill=ORANGE) draw.ellipse( (cx - radius, cy - radius, cx + radius, cy + radius), fill=ORANGE, outline="white", width=max(5, width // 32), ) hole = radius * 0.34 draw.ellipse((cx - hole, cy - hole, cx + hole, cy + hole), fill="white") def create_icon() -> None: image = Image.new("RGB", (512, 512), GREEN) draw = ImageDraw.Draw(image) draw_mark(draw, (82, 78, 430, 434)) image.save(OUTPUT / "play-store-icon-512.png", optimize=True) def create_feature_graphic() -> None: image = Image.new("RGB", (1024, 500), "#F3F7F5") draw = ImageDraw.Draw(image) draw.rectangle((0, 0, 20, 500), fill=GREEN) draw.rectangle((20, 0, 32, 500), fill=ORANGE) draw.text((78, 107), "LME", font=font(34, True), fill=ORANGE) draw.text((78, 154), "Mappe Catastali Pro", font=font(55, True), fill=INK) draw.text((80, 234), "Mappe, GPS e progetti GIS", font=font(27), fill=GREEN) draw.line((80, 290, 455, 290), fill=LINE, width=3) draw.text((80, 315), "Consulta • misura • organizza", font=font(21), fill="#46635D") panel = (595, 48, 965, 452) draw.rounded_rectangle(panel, radius=8, fill="white", outline=LINE, width=3) draw.polygon([(618, 86), (760, 65), (802, 164), (664, 190)], fill=BLUE, outline=GREEN_LIGHT) draw.polygon([(806, 75), (940, 110), (923, 236), (784, 205)], fill="#E9EFE7", outline=GREEN_LIGHT) draw.polygon([(625, 210), (779, 184), (817, 318), (665, 351)], fill="#F4E9D9", outline=GREEN_LIGHT) draw.polygon([(820, 232), (929, 256), (946, 394), (853, 420), (798, 336)], fill=BLUE, outline=GREEN_LIGHT) for x1, y1, x2, y2 in [ (630, 128, 932, 327), (646, 310, 915, 128), (704, 73, 857, 422), (607, 267, 948, 181), ]: draw.line((x1, y1, x2, y2), fill="#A4BDB5", width=3) draw_mark(draw, (706, 136, 883, 340)) image.save(OUTPUT / "feature-graphic-1024x500.png", optimize=True) def create_store_screenshot(raw_name: str, output_name: str, title: str, subtitle: str) -> None: raw = Image.open(OUTPUT / raw_name).convert("RGB") # Remove the Windows title bar and the desktop-only watermark/status area. app_view = raw.crop((0, 32, raw.width, min(raw.height, 500))) max_width, max_height = 1840, 760 scale = min(max_width / app_view.width, max_height / app_view.height) app_view = app_view.resize( (round(app_view.width * scale), round(app_view.height * scale)), Image.Resampling.LANCZOS, ) image = Image.new("RGB", (1920, 1080), "#F3F7F5") draw = ImageDraw.Draw(image) draw.rectangle((0, 0, 24, 1080), fill=GREEN) draw.rectangle((24, 0, 38, 1080), fill=ORANGE) draw.text((88, 54), title, font=font(52, True), fill=INK) draw.text((90, 126), subtitle, font=font(26), fill="#46635D") x = (1920 - app_view.width) // 2 y = 270 draw.rounded_rectangle( (x - 8, y - 8, x + app_view.width + 8, y + app_view.height + 8), radius=8, fill="white", outline=LINE, width=4, ) image.paste(app_view, (x, y)) image.save(OUTPUT / output_name, optimize=True) def main() -> None: OUTPUT.mkdir(exist_ok=True) create_icon() create_feature_graphic() screenshots = [ ( "windows-map-raw.jpg", "phone-01-map-1920x1080.png", "Mappe catastali e cartografia", "Consulta il territorio, cerca localita e lavora sulla mappa.", ), ( "windows-data-drawings-raw.jpg", "phone-02-data-drawings-1920x1080.png", "Importa dati e disegna", "KML, KMZ e GeoJSON insieme a misure e strumenti GIS.", ), ( "windows-projects-raw.jpg", "phone-03-projects-1920x1080.png", "Organizza i tuoi progetti", "Salva, riapri e condividi il lavoro sul campo.", ), ] for raw_name, output_name, title, subtitle in screenshots: if (OUTPUT / raw_name).exists(): create_store_screenshot(raw_name, output_name, title, subtitle) if __name__ == "__main__": main()