-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate-icon.swift
More file actions
91 lines (74 loc) · 3.28 KB
/
generate-icon.swift
File metadata and controls
91 lines (74 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env swift
import AppKit
// Render the "macbook.slash" SF Symbol into an .icns file for the app icon.
// Uses iconutil under the hood, which requires a .iconset folder with
// PNGs at specific sizes.
let iconsetPath = "AppIcon.iconset"
let icnsPath = "AppIcon.icns"
// Required sizes for a macOS .iconset
let sizes: [(name: String, size: CGFloat)] = [
("icon_16x16", 16),
("icon_16x16@2x", 32),
("icon_32x32", 32),
("icon_32x32@2x", 64),
("icon_128x128", 128),
("icon_128x128@2x", 256),
("icon_256x256", 256),
("icon_256x256@2x", 512),
("icon_512x512", 512),
("icon_512x512@2x", 1024),
]
// Create iconset directory
try? FileManager.default.removeItem(atPath: iconsetPath)
try! FileManager.default.createDirectory(atPath: iconsetPath, withIntermediateDirectories: true)
for entry in sizes {
let pixelSize = entry.size
let image = NSImage(size: NSSize(width: pixelSize, height: pixelSize))
image.lockFocus()
// Background: rounded rect with gradient (dark gray)
let rect = NSRect(x: 0, y: 0, width: pixelSize, height: pixelSize)
let cornerRadius = pixelSize * 0.22 // macOS icon corner radius
let path = NSBezierPath(roundedRect: rect, xRadius: cornerRadius, yRadius: cornerRadius)
// Gradient background
let gradient = NSGradient(starting: NSColor(white: 0.18, alpha: 1.0),
ending: NSColor(white: 0.08, alpha: 1.0))!
gradient.draw(in: path, angle: 270)
// Draw the SF Symbol centered in terminal green
let accentBlue = NSColor(red: 0.0, green: 0.48, blue: 1.0, alpha: 1.0)
if let symbol = NSImage(systemSymbolName: "macbook.slash", accessibilityDescription: nil) {
// Apply both size and color configuration
let sizeConfig = NSImage.SymbolConfiguration(pointSize: pixelSize * 0.45, weight: .medium)
let colorConfig = NSImage.SymbolConfiguration(paletteColors: [accentBlue])
let combined = sizeConfig.applying(colorConfig)
let configured = symbol.withSymbolConfiguration(combined)!
let symbolSize = configured.size
let x = (pixelSize - symbolSize.width) / 2
let y = (pixelSize - symbolSize.height) / 2
configured.draw(in: NSRect(x: x, y: y, width: symbolSize.width, height: symbolSize.height),
from: .zero, operation: .sourceOver, fraction: 1.0)
}
image.unlockFocus()
// Save as PNG
guard let tiffData = image.tiffRepresentation,
let bitmap = NSBitmapImageRep(data: tiffData),
let pngData = bitmap.representation(using: .png, properties: [:]) else {
print("Failed to render \(entry.name)")
continue
}
let filePath = "\(iconsetPath)/\(entry.name).png"
try! pngData.write(to: URL(fileURLWithPath: filePath))
print("Generated \(entry.name) (\(Int(pixelSize))px)")
}
// Convert iconset to icns using iconutil
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/iconutil")
process.arguments = ["-c", "icns", iconsetPath, "-o", icnsPath]
try! process.run()
process.waitUntilExit()
if process.terminationStatus == 0 {
print("\nCreated \(icnsPath)")
// Clean up iconset folder
try? FileManager.default.removeItem(atPath: iconsetPath)
} else {
print("iconutil failed with status \(process.terminationStatus)")
}