ajhahn.de
← eeco
Swift 144 lines
#!/usr/bin/env swift
// Render the eeco wordmark to a transparent PNG using the Orbitron font.
//
// Variants:
//   1: Regular  "eeco"  (whole word, color1)
//   2: Bold     "eeco"  (whole word, color1)
//   3: SemiBold "eeco"  (whole word, color1)
//   4: split — "e" SemiBold color1 + "eco" Regular color2  (the mark)
//
// Requires Orbitron-Regular.ttf, Orbitron-Bold.ttf, Orbitron-SemiBold.ttf
// in /Library/Fonts or ~/Library/Fonts.
//
// Reproduce the committed assets — neutral "e" + blue "eco" accent.
// The accent is the house blue ladder (mid #3B82F6 on light, bright
// #60A5FA on dark); the neutral is the theme ink:
//   swift scripts/render_logo.swift 4 assets/eeco_logo_light.png \
//        --color1 1a1a1a --color2 3B82F6
//   swift scripts/render_logo.swift 4 assets/eeco_logo_dark.png \
//        --color1 f5f5f5 --color2 60A5FA
//
// For variant 4: --color1 styles "e", --color2 styles "eco".
// For variants 1-3: --color1 styles the whole word; --color2 ignored.
// Default colors: black (000000).

import AppKit
import Foundation

func parseHexColor(_ hex: String) -> NSColor? {
    var h = hex
    if h.hasPrefix("#") { h.removeFirst() }
    guard h.count == 6, let v = UInt32(h, radix: 16) else { return nil }
    let r = CGFloat((v >> 16) & 0xff) / 255.0
    let g = CGFloat((v >>  8) & 0xff) / 255.0
    let b = CGFloat( v        & 0xff) / 255.0
    return NSColor(srgbRed: r, green: g, blue: b, alpha: 1.0)
}

let args = CommandLine.arguments
guard args.count >= 3, let variant = Int(args[1]) else {
    FileHandle.standardError.write(
        ("Usage: \(args[0]) <variant 1-4> <output.png> " +
         "[--font-size N] [--color1 RRGGBB] [--color2 RRGGBB]\n")
            .data(using: .utf8)!)
    exit(2)
}
let outputPath = args[2]

var fontSize: CGFloat = 400
var color1: NSColor = .black
var color2: NSColor = .black

var i = 3
while i < args.count {
    switch args[i] {
    case "--font-size":
        if i + 1 < args.count, let s = Double(args[i + 1]) {
            fontSize = CGFloat(s)
            i += 2
        } else { exit(2) }
    case "--color1":
        if i + 1 < args.count, let c = parseHexColor(args[i + 1]) {
            color1 = c
            i += 2
        } else { exit(2) }
    case "--color2":
        if i + 1 < args.count, let c = parseHexColor(args[i + 1]) {
            color2 = c
            i += 2
        } else { exit(2) }
    default:
        FileHandle.standardError.write("unknown arg: \(args[i])\n".data(using: .utf8)!)
        exit(2)
    }
}

func font(_ name: String, size: CGFloat) -> NSFont {
    guard let f = NSFont(name: name, size: size) else {
        FileHandle.standardError.write("Font \(name) not found\n".data(using: .utf8)!)
        exit(3)
    }
    return f
}

let regular  = font("Orbitron-Regular",  size: fontSize)
let bold     = font("Orbitron-Bold",     size: fontSize)
let semibold = font("Orbitron-SemiBold", size: fontSize)

let attrString: NSAttributedString
switch variant {
case 1:
    attrString = NSAttributedString(
        string: "eeco",
        attributes: [.font: regular, .foregroundColor: color1])
case 2:
    attrString = NSAttributedString(
        string: "eeco",
        attributes: [.font: bold, .foregroundColor: color1])
case 3:
    attrString = NSAttributedString(
        string: "eeco",
        attributes: [.font: semibold, .foregroundColor: color1])
case 4:
    let s = NSMutableAttributedString()
    s.append(NSAttributedString(
        string: "e",
        attributes: [.font: bold, .foregroundColor: color1]))
    s.append(NSAttributedString(
        string: "eco",
        attributes: [.font: regular, .foregroundColor: color2]))
    attrString = s
default:
    FileHandle.standardError.write("Variant must be 1-4\n".data(using: .utf8)!)
    exit(2)
}

let padding: CGFloat = fontSize * 0.15
let textSize = attrString.size()
let imageW = ceil(textSize.width + 2 * padding)
let imageH = ceil(textSize.height + 2 * padding)

let image = NSImage(size: NSSize(width: imageW, height: imageH))
image.lockFocus()
NSColor.clear.setFill()
NSRect(x: 0, y: 0, width: imageW, height: imageH).fill()
let drawY = (imageH - textSize.height) / 2
attrString.draw(at: NSPoint(x: padding, y: drawY))
image.unlockFocus()

guard let tiff = image.tiffRepresentation,
      let bitmap = NSBitmapImageRep(data: tiff),
      let png = bitmap.representation(using: .png, properties: [:])
else {
    FileHandle.standardError.write("Failed to encode PNG\n".data(using: .utf8)!)
    exit(4)
}

do {
    try png.write(to: URL(fileURLWithPath: outputPath))
    print("wrote \(outputPath) (\(Int(imageW))×\(Int(imageH)) px, variant \(variant))")
} catch {
    FileHandle.standardError.write("write failed: \(error)\n".data(using: .utf8)!)
    exit(5)
}