Z + zwei Akzent-Quotes lieferten ein unsauber gerendertes PNG (Z fehlte aufgrund Font-Bounds-Mathematik bei großem Pointsize). Reduziert auf eine zentrierte Öffnungs-Anführung („) — ikonisch auch bei 40×40, klar erkennbar als Quotes-App. Platzhalter — vor Launch durch Designer-Icon ersetzen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
76 lines
2.5 KiB
Swift
76 lines
2.5 KiB
Swift
#!/usr/bin/env swift
|
||
// Generiert ein 1024×1024-AppIcon-PNG als Platzhalter für Zitare.
|
||
//
|
||
// Design (paper-Variant aus @mana/themes):
|
||
// - Background: warm Sienna (paper-primary, HSL 18 50% 38%)
|
||
// - Ein großes typografisches Anführungszeichen mittig in
|
||
// gebrochenem Weiß. Reduziert, klar lesbar bei jeder Größe.
|
||
//
|
||
// Aufruf:
|
||
// swift scripts/make-appicon.swift
|
||
//
|
||
// Schreibt nach: Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png
|
||
//
|
||
// Platzhalter — vor App-Store-Launch durch designtes Icon ersetzen.
|
||
|
||
import AppKit
|
||
import CoreGraphics
|
||
|
||
let size = 1024
|
||
let cs = CGColorSpaceCreateDeviceRGB()
|
||
guard let ctx = CGContext(
|
||
data: nil,
|
||
width: size, height: size,
|
||
bitsPerComponent: 8, bytesPerRow: 0,
|
||
space: cs,
|
||
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
|
||
) else {
|
||
print("CGContext creation failed")
|
||
exit(1)
|
||
}
|
||
|
||
// HSL(18, 50%, 38%) — paper-primary, warm Sienna
|
||
let background = CGColor(red: 0.57, green: 0.30, blue: 0.19, alpha: 1)
|
||
ctx.setFillColor(background)
|
||
ctx.fill(CGRect(x: 0, y: 0, width: size, height: size))
|
||
|
||
// Großes typografisches Öffnungs-Quote („) in gebrochenem Weiß.
|
||
// Georgia-Bold rendert es als zwei "Tropfen" — ikonisch genug, um
|
||
// auch im 60×60 oder 40×40 erkennbar zu bleiben.
|
||
let glyph = "\u{201C}" // “
|
||
let glyphFont = NSFont(name: "Georgia-Bold", size: 900)
|
||
?? NSFont.boldSystemFont(ofSize: 900)
|
||
let accent = NSColor(
|
||
srgbRed: 0.95, green: 0.93, blue: 0.88, alpha: 1.0
|
||
)
|
||
let attrs: [NSAttributedString.Key: Any] = [
|
||
.font: glyphFont,
|
||
.foregroundColor: accent,
|
||
]
|
||
let attrStr = NSAttributedString(string: glyph, attributes: attrs)
|
||
let line = CTLineCreateWithAttributedString(attrStr)
|
||
let bounds = CTLineGetImageBounds(line, ctx)
|
||
|
||
// Zentrieren auf der Canvas. CTLineGetImageBounds gibt das
|
||
// bounding-box im aktuellen Coordinate-System zurück (bottom-up
|
||
// in CG); origin.y kann negativ sein bei Glyphs mit Descender.
|
||
let tx = (CGFloat(size) - bounds.width) / 2.0 - bounds.origin.x
|
||
let ty = (CGFloat(size) - bounds.height) / 2.0 - bounds.origin.y
|
||
ctx.textPosition = CGPoint(x: tx, y: ty)
|
||
CTLineDraw(line, ctx)
|
||
|
||
guard let cgImage = ctx.makeImage() else {
|
||
print("makeImage failed")
|
||
exit(1)
|
||
}
|
||
let bitmap = NSBitmapImageRep(cgImage: cgImage)
|
||
guard let data = bitmap.representation(using: .png, properties: [:]) else {
|
||
print("PNG encoding failed")
|
||
exit(1)
|
||
}
|
||
|
||
let target = URL(
|
||
fileURLWithPath: "Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png"
|
||
)
|
||
try data.write(to: target)
|
||
print("Wrote \(target.path) (\(data.count) bytes)")
|