110 lines
4.1 KiB
Swift
110 lines
4.1 KiB
Swift
import AppKit
|
||
import CoreText
|
||
import SwiftUI
|
||
|
||
func runFonts(_ font: CTFont, _ text: String) -> String {
|
||
let line = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.font: font as NSFont]))
|
||
return (CTLineGetGlyphRuns(line) as! [CTRun]).map { run in
|
||
let f = (CTRunGetAttributes(run) as NSDictionary)[kCTFontAttributeName] as! CTFont
|
||
let file = (CTFontCopyAttribute(f, kCTFontURLAttribute) as? URL)?.lastPathComponent ?? "system"
|
||
let r = CTRunGetStringRange(run)
|
||
let part = (text as NSString).substring(with: NSRange(location: r.location, length: r.length))
|
||
return "\(part) → \(CTFontCopyPostScriptName(f)) (\(file))"
|
||
}.joined(separator: "\n")
|
||
}
|
||
|
||
struct FontSpike: View {
|
||
private let sample = "The quick brown fox jumps — 0O 1lI {}[] őŁżę → │"
|
||
|
||
var body: some View {
|
||
ScrollView {
|
||
VStack(alignment: .leading, spacing: 12) {
|
||
ForEach(Array([(400.0, false), (700.0, false), (400.0, true), (700.0, true)].enumerated()), id: \.offset) { _, style in
|
||
let (w, italic) = style
|
||
VStack(alignment: .leading) {
|
||
Text("weight \(Int(w))\(italic ? " italic" : "")")
|
||
.foregroundStyle(.secondary)
|
||
Text(sample)
|
||
.font(Fonts.font(16, weight: w, italic: italic))
|
||
Text(runFonts(Fonts.ct(16, weight: w, italic: italic), sample))
|
||
.foregroundStyle(.secondary)
|
||
}
|
||
}
|
||
VStack(alignment: .leading) {
|
||
Text("Fonts.font(16) with SwiftUI .bold() and .italic()")
|
||
.foregroundStyle(.secondary)
|
||
Text(sample)
|
||
.font(Fonts.font(16))
|
||
.bold()
|
||
.italic()
|
||
}
|
||
VStack(alignment: .leading) {
|
||
Text("System font, for comparison")
|
||
.foregroundStyle(.secondary)
|
||
Text(sample)
|
||
.font(.system(size: 16))
|
||
}
|
||
}
|
||
.frame(maxWidth: .infinity, alignment: .leading)
|
||
.padding(8)
|
||
}
|
||
}
|
||
}
|
||
|
||
struct SVGSpike: View {
|
||
@Environment(Server.self) private var server
|
||
@ViewState private var path = "web/favicon.svg"
|
||
@ViewState private var image: NSImage?
|
||
@ViewState private var failure = ""
|
||
|
||
var body: some View {
|
||
VStack(alignment: .leading) {
|
||
HStack {
|
||
TextField("Worktree path", text: $path)
|
||
.frame(width: 320)
|
||
Button("Load") {
|
||
Task {
|
||
await load()
|
||
}
|
||
}
|
||
}
|
||
if let image {
|
||
Text(verbatim: "NSImage size \(Int(image.size.width))×\(Int(image.size.height)), reps \(image.representations.map { String(describing: type(of: $0)) })")
|
||
.foregroundStyle(.secondary)
|
||
HStack(alignment: .top, spacing: 16) {
|
||
Image(nsImage: image)
|
||
Image(nsImage: image)
|
||
.resizable()
|
||
.scaledToFit()
|
||
.frame(width: 256, height: 256)
|
||
}
|
||
}
|
||
if !failure.isEmpty {
|
||
Text(failure)
|
||
}
|
||
Spacer()
|
||
}
|
||
.task {
|
||
await load()
|
||
}
|
||
}
|
||
|
||
private func load() async {
|
||
image = nil
|
||
failure = ""
|
||
guard !server.taskId.isEmpty else {
|
||
return
|
||
}
|
||
do {
|
||
let data = try await server.data(URLRequest(url: try server.url("/api/tasks/\(server.taskId)/image", [URLQueryItem(name: "path", value: path)])))
|
||
guard let img = NSImage(data: data) else {
|
||
failure = "image not available: \(path)"
|
||
return
|
||
}
|
||
image = img
|
||
} catch {
|
||
failure = "image not available: \(path) (\(error))"
|
||
}
|
||
}
|
||
}
|