Skip to content

Commit 3ae91f6

Browse files
committed
--format sfsymbol
1 parent e2a3bda commit 3ae91f6

File tree

6 files changed

+279
-149
lines changed

6 files changed

+279
-149
lines changed

CommandLine/CommandLine.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ extension SwiftDraw.CommandLine {
7373
case .swift:
7474
let code = CGTextRenderer.render(fileURL: config.input, size: config.size.renderSize, options: config.options)
7575
return code?.data(using: .utf8)
76+
case .sfsymbol:
77+
print("[--format sfsymbol] is an experimental feature.")
78+
let svg = try? SFSymbolRenderer.render(fileURL: config.input, options: config.options)
79+
return svg?.data(using: .utf8)
7680
case .jpeg, .pdf, .png:
7781
return SwiftDraw.Image(fileURL: config.input, options: config.options).flatMap { processImage($0, with: config) }
7882
}
@@ -86,7 +90,7 @@ extension SwiftDraw.CommandLine {
8690
return try? Image.pdfData(fileURL: config.input, size: config.size.cgValue)
8791
case .png:
8892
return image.pngData(size: config.size.cgValue, scale: config.scale.cgValue)
89-
case .swift:
93+
case .swift, .sfsymbol:
9094
preconditionFailure()
9195
}
9296
}

SwiftDraw/CommandLine.Configuration.swift

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ extension CommandLine {
4747
case pdf
4848
case png
4949
case swift
50+
case sfsymbol
5051
}
5152

5253
public enum Size: Equatable {
@@ -151,21 +152,23 @@ extension URL {
151152
}
152153

153154
func newURL(for format: CommandLine.Format, scale: CommandLine.Scale) -> URL {
154-
let newFilename = lastPathComponentName(scale: scale)
155-
let newFilenameWithExtension = "\(newFilename).\(format.pathExtension)"
155+
let suffix = Self.lastPathComponentSuffix(format: format, scale: scale)
156+
let newfilename = "\(lastPathComponentName)\(suffix).\(format.pathExtension)"
156157
return deletingLastPathComponent()
157-
.appendingPathComponent(newFilenameWithExtension)
158+
.appendingPathComponent(newfilename)
158159
.standardizedFileURL
159160
}
160161

161-
func lastPathComponentName(scale: CommandLine.Scale) -> String {
162-
switch scale {
163-
case .default:
164-
return lastPathComponentName
165-
case .retina:
166-
return "\(lastPathComponentName)@2x"
167-
case .superRetina:
168-
return "\(lastPathComponentName)@3x"
162+
static func lastPathComponentSuffix(format: CommandLine.Format, scale: CommandLine.Scale) -> String {
163+
switch (format, scale) {
164+
case (.sfsymbol, _):
165+
return "-symbol"
166+
case (.png, .retina):
167+
return "@2x"
168+
case (.png, .superRetina):
169+
return "@3x"
170+
default:
171+
return ""
169172
}
170173
}
171174
}
@@ -182,6 +185,8 @@ private extension CommandLine.Format {
182185
return "png"
183186
case .swift:
184187
return "swift"
188+
case .sfsymbol:
189+
return "svg"
185190
}
186191
}
187192
}

SwiftDraw/Formatter.XML.swift

Lines changed: 89 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -32,75 +32,97 @@
3232
import Foundation
3333

3434
extension XML.Formatter {
35-
36-
struct CoordinateFormatter {
37-
var delimeter: Delimeter = .space
38-
var precision: Precision = .capped(max: 5)
39-
40-
enum Precision {
41-
case capped(max: Int)
42-
case maximum
43-
}
44-
45-
enum Delimeter: String {
46-
case space = " "
47-
case comma = ","
48-
}
49-
50-
func format(_ coordinates: DOM.Coordinate...) -> String {
51-
return coordinates.map { format(Double($0)) }.joined(separator: delimeter.rawValue)
52-
}
5335

54-
func format(_ coordinates: Double...) -> String {
55-
return coordinates.map { format($0) }.joined(separator: delimeter.rawValue)
56-
}
36+
struct CoordinateFormatter {
37+
var delimeter: Delimeter = .space
38+
var precision: Precision = .capped(max: 5)
5739

58-
func format(_ c: Double) -> String {
59-
switch precision {
60-
case .capped(let max):
61-
return format(c, capped: max)
62-
default:
63-
return String(describing: c)
64-
}
65-
}
40+
enum Precision {
41+
case capped(max: Int)
42+
case maximum
43+
}
6644

67-
func format(fraction n: Double, maxDigits: Int) -> String? {
68-
assert(n.sign == .plus)
69-
70-
let min = pow(Double(10), Double(-maxDigits)) - Double.ulpOfOne
71-
72-
guard n >= min else {
73-
return ""
74-
}
75-
76-
let s = String(format: "%.\(maxDigits)g", n)
77-
if s == "1" {
78-
return nil
79-
}
80-
let idx = s.index(s.startIndex, offsetBy: 1)
81-
return String(s[idx..<s.endIndex])
82-
}
83-
84-
func format(_ c: Double, capped: Int) -> String {
85-
let sign: String
86-
let n: (Double, Double)
87-
88-
if c.sign == .minus {
89-
sign = "-"
90-
n = modf(abs(c))
91-
} else {
92-
sign = ""
93-
n = modf(c)
94-
}
95-
96-
let integer = Int(n.0)
97-
let fraction = format(fraction: n.1, maxDigits: capped)
98-
99-
if let fraction = fraction {
100-
return "\(sign)\(integer)\(fraction)"
101-
} else {
102-
return "\(sign)\(integer + 1)"
103-
}
45+
enum Delimeter: String {
46+
case space = " "
47+
case comma = ","
48+
}
49+
50+
func formatLength(_ length: DOM.Length) -> String {
51+
return formatValue(Double(length))
52+
}
53+
54+
func format(_ coordinate: DOM.Coordinate?) -> String? {
55+
guard let coordinate = coordinate else {
56+
return nil
57+
}
58+
return formatValue(Double(coordinate))
59+
}
60+
61+
func format(_ coordinate: Double?) -> String? {
62+
guard let coordinate = coordinate else {
63+
return nil
64+
}
65+
return formatValue(coordinate)
66+
}
67+
68+
func format(_ coordinates: DOM.Coordinate...) -> String {
69+
return coordinates.map { formatValue(Double($0)) }.joined(separator: delimeter.rawValue)
70+
}
71+
72+
func format(_ coordinates: Double...) -> String {
73+
return coordinates.map { formatValue($0) }.joined(separator: delimeter.rawValue)
74+
}
75+
76+
func format(_ flag: Bool) -> String {
77+
flag ? "1" : "0"
78+
}
79+
80+
private func formatValue(_ c: Double) -> String {
81+
switch precision {
82+
case .capped(let max):
83+
return format(c, capped: max)
84+
default:
85+
return String(describing: c)
86+
}
87+
}
88+
89+
func format(fraction n: Double, maxDigits: Int) -> String? {
90+
assert(n.sign == .plus)
91+
92+
let min = pow(Double(10), Double(-maxDigits)) - Double.ulpOfOne
93+
94+
guard n >= min else {
95+
return ""
96+
}
97+
98+
let s = String(format: "%.\(maxDigits)g", n)
99+
if s == "1" {
100+
return nil
101+
}
102+
let idx = s.index(s.startIndex, offsetBy: 1)
103+
return String(s[idx..<s.endIndex])
104+
}
105+
106+
func format(_ c: Double, capped: Int) -> String {
107+
let sign: String
108+
let n: (Double, Double)
109+
110+
if c.sign == .minus {
111+
sign = "-"
112+
n = modf(abs(c))
113+
} else {
114+
sign = ""
115+
n = modf(c)
116+
}
117+
118+
let integer = Int(n.0)
119+
let fraction = format(fraction: n.1, maxDigits: capped)
120+
121+
if let fraction = fraction {
122+
return "\(sign)\(integer)\(fraction)"
123+
} else {
124+
return "\(sign)\(integer + 1)"
125+
}
126+
}
104127
}
105-
}
106128
}

SwiftDraw/Renderer.SFSymbol.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// Renderer.SFSymbol.swift
3+
// SwiftDraw
4+
//
5+
// Created by Simon Whitty on 18/8/22.
6+
// Copyright 2022 Simon Whitty
7+
//
8+
// Distributed under the permissive zlib license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/SwiftDraw
12+
//
13+
// This software is provided 'as-is', without any express or implied
14+
// warranty. In no event will the authors be held liable for any damages
15+
// arising from the use of this software.
16+
//
17+
// Permission is granted to anyone to use this software for any purpose,
18+
// including commercial applications, and to alter it and redistribute it
19+
// freely, subject to the following restrictions:
20+
//
21+
// 1. The origin of this software must not be misrepresented; you must not
22+
// claim that you wrote the original software. If you use this software
23+
// in a product, an acknowledgment in the product documentation would be
24+
// appreciated but is not required.
25+
//
26+
// 2. Altered source versions must be plainly marked as such, and must not be
27+
// misrepresented as being the original software.
28+
//
29+
// 3. This notice may not be removed or altered from any source distribution.
30+
//
31+
32+
import Foundation
33+
34+
35+
public final class SFSymbolRenderer {
36+
37+
func makeDOM(for layer: LayerTree.Layer) throws -> DOM.SVG {
38+
throw Error.invalid
39+
}
40+
41+
enum Error: Swift.Error {
42+
case invalid
43+
}
44+
}
45+
46+
public extension SFSymbolRenderer {
47+
48+
static func render(fileURL: URL, options: Image.Options) throws -> String {
49+
let svg = try DOM.SVG.parse(fileURL: fileURL)
50+
let element = try XML.Formatter.SVG().makeElement(from: svg)
51+
let formatter = XML.Formatter(spaces: 2)
52+
return formatter.encodeRootElement(element)
53+
}
54+
}

0 commit comments

Comments
 (0)