spb/vrai-prix-ios Public
App iOS native de Vrai-Prix — estimation immobilière transparente pour le Québec (SwiftUI, MapKit, Swift Charts)
Swift 100%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2// Génère l'icône iOS 1024×1024 à partir du wordmark Vrai-Prix3// (papier #f5f3ee, encre #141814, accent #ff5148, Space Grotesk Bold),4// fidèle au logo du site : « Vrai » en encre + « Prix » en boîte encre5// pivotée de -2° avec texte accent et ombre décalée dure.6// Usage : swift make-icon.swift <fonts-dir> <sortie.png>7import AppKit8import CoreText910let args = CommandLine.arguments11guard args.count == 3 else {12 FileHandle.standardError.write("usage: swift make-icon.swift <fonts-dir> <out.png>\n".data(using: .utf8)!)13 exit(1)14}15let fontsDir = args[1]16let outPath = args[2]1718// Enregistrer Space Grotesk Bold19let fontURL = URL(fileURLWithPath: "\(fontsDir)/SpaceGrotesk-Bold.ttf")20CTFontManagerRegisterFontsForURL(fontURL as CFURL, .process, nil)2122func color(_ hex: UInt32) -> NSColor {23 NSColor(srgbRed: CGFloat((hex >> 16) & 0xff) / 255,24 green: CGFloat((hex >> 8) & 0xff) / 255,25 blue: CGFloat(hex & 0xff) / 255, alpha: 1)26}27let paper = color(0xF5F3EE)28let ink = color(0x141814)29let lime = color(0xFF5148)3031let S: CGFloat = 102432// contexte bitmap exact 1024×1024, sans alpha (exigence App Store)33guard let ctx = CGContext(34 data: nil, width: Int(S), height: Int(S), bitsPerComponent: 8, bytesPerRow: 0,35 space: CGColorSpace(name: CGColorSpace.sRGB)!,36 bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue37) else { exit(1) }38NSGraphicsContext.current = NSGraphicsContext(cgContext: ctx, flipped: false)3940// fond papier plein cadre (iOS applique lui-même le masque arrondi)41paper.setFill()42ctx.fill(CGRect(x: 0, y: 0, width: S, height: S))4344// grain très léger façon papier (points encre à faible alpha, déterministe)45ctx.setFillColor(ink.withAlphaComponent(0.028).cgColor)46var seed: UInt64 = 0x5EED_CAFE47func rnd() -> CGFloat {48 seed = seed &* 6364136223846793005 &+ 144269504088896340749 return CGFloat((seed >> 33) % 10_000) / 10_00050}51for _ in 0..<2600 {52 let x = rnd() * S, y = rnd() * S, r = 1.0 + rnd() * 1.653 ctx.fillEllipse(in: CGRect(x: x, y: y, width: r, height: r))54}5556let font = NSFont(name: "SpaceGrotesk-Bold", size: 252)!57func attrString(_ s: String, _ c: NSColor) -> NSAttributedString {58 NSAttributedString(string: s, attributes: [.font: font, .foregroundColor: c, .kern: -10])59}6061// mesures62let vrai = attrString("Vrai", ink)63let prix = attrString("Prix", lime)64let vraiSize = vrai.size()65let prixSize = prix.size()6667// boîte « Prix » : encre, coins arrondis, pivotée -2°, ombre décalée dure68let padX: CGFloat = 4669let padY: CGFloat = 670let boxW = prixSize.width + padX * 271let boxH = prixSize.height + padY * 272let gap: CGFloat = 347374let totalH = vraiSize.height + gap + boxH75// repère depuis le haut (coordonnées AppKit = origine en bas), remonté pour dégager la micro-étiquette76let topY = (S - totalH) / 2 + totalH + 567778// « Vrai » centré en haut du bloc79let vraiOrigin = CGPoint(x: (S - vraiSize.width) / 2, y: topY - vraiSize.height)80vrai.draw(at: vraiOrigin)8182// boîte pivotée autour de son centre83let boxCenter = CGPoint(x: S / 2, y: topY - vraiSize.height - gap - boxH / 2)84ctx.saveGState()85ctx.translateBy(x: boxCenter.x, y: boxCenter.y)86ctx.rotate(by: -2 * .pi / 180)8788// ombre décalée dure (signature du design)89let boxRect = CGRect(x: -boxW / 2, y: -boxH / 2, width: boxW, height: boxH)90let shadowPath = CGPath(roundedRect: boxRect.offsetBy(dx: 14, dy: -14), cornerWidth: 28, cornerHeight: 28, transform: nil)91ctx.addPath(shadowPath)92ctx.setFillColor(ink.withAlphaComponent(0.22).cgColor)93ctx.fillPath()9495let boxPath = CGPath(roundedRect: boxRect, cornerWidth: 28, cornerHeight: 28, transform: nil)96ctx.addPath(boxPath)97ctx.setFillColor(ink.cgColor)98ctx.fillPath()99100prix.draw(at: CGPoint(x: -prixSize.width / 2, y: -prixSize.height / 2 + 6))101ctx.restoreGState()102103// micro-étiquette mono en bas : trait + « QUÉBEC »104let microFont = NSFont(name: "SpaceGrotesk-Bold", size: 44)!105let micro = NSAttributedString(string: "QUÉBEC", attributes: [106 .font: microFont, .foregroundColor: color(0x9E2A25), .kern: 14,107])108let mSize = micro.size()109micro.draw(at: CGPoint(x: (S - mSize.width) / 2, y: 118))110let lineW: CGFloat = 56111ctx.setFillColor(color(0x9E2A25).cgColor)112ctx.fill(CGRect(x: (S - lineW) / 2, y: 96, width: lineW, height: 6))113114guard let cgImage = ctx.makeImage() else { exit(1) }115let rep = NSBitmapImageRep(cgImage: cgImage)116guard let png = rep.representation(using: .png, properties: [:]) else { exit(1) }117try! png.write(to: URL(fileURLWithPath: outPath))118print("OK \(outPath)")119