Skip to content

Commit d803751

Browse files
committed
Preserve Accepted Styles for SFSymbols
1 parent 19f1fcc commit d803751

File tree

3 files changed

+83
-5
lines changed

3 files changed

+83
-5
lines changed

SwiftDraw/DOM.SVG.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ extension DOM {
7676

7777
struct StyleSheet {
7878

79-
enum Selector: Hashable {
80-
case element(String)
79+
enum Selector: Hashable, Comparable {
8180
case id(String)
81+
case element(String)
8282
case `class`(String)
8383
}
8484

SwiftDraw/Renderer.SFSymbol.swift

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ public struct SFSymbolRenderer {
6565
}
6666
var template = try SFSymbolTemplate.make()
6767

68+
template.svg.styles = image.styles.map(makeSymbolStyleSheet)
69+
6870
let boundsRegular = try makeBounds(svg: image, auto: Self.makeBounds(for: pathsRegular), for: .regular)
6971
template.regular.appendPaths(pathsRegular, from: boundsRegular)
7072

@@ -91,6 +93,29 @@ public struct SFSymbolRenderer {
9193
let result = formatter.encodeRootElement(element)
9294
return result
9395
}
96+
97+
func makeSymbolStyleSheet(from stylesheet: DOM.StyleSheet) -> DOM.StyleSheet {
98+
var copy = stylesheet
99+
for selector in stylesheet.attributes.keys {
100+
switch selector {
101+
case .class(let name):
102+
if SFSymbolRenderer.containsAcceptedName(name) {
103+
copy.attributes[selector] = stylesheet.attributes[selector]
104+
}
105+
case .id, .element:
106+
()
107+
}
108+
}
109+
return copy
110+
}
111+
112+
static func containsAcceptedName(_ string: String?) -> Bool {
113+
guard let string = string else { return false }
114+
return string.contains("hierarchical-") ||
115+
string.contains("monochrome-") ||
116+
string.contains("multicolor-") ||
117+
string.contains("SFSymbolsPreview")
118+
}
94119
}
95120

96121
extension SFSymbolRenderer {
@@ -165,19 +190,21 @@ extension SFSymbolRenderer {
165190
let ctm = ctm.concatenated(layer.transform.toMatrix())
166191
var paths = [SymbolPath]()
167192

193+
let symbolClass = containsAcceptedName(layer.class) ? layer.class : nil
194+
168195
for c in layer.contents {
169196
switch c {
170197
case let .shape(shape, stroke, fill):
171198
if let path = makePath(for: shape, stoke: stroke, fill: fill)?.applying(matrix: ctm) {
172199
if fill.rule == .evenodd {
173-
paths.append(SymbolPath(class: layer.class, path: path.makeNonZero()))
200+
paths.append(SymbolPath(class: symbolClass, path: path.makeNonZero()))
174201
} else {
175-
paths.append(SymbolPath(class: layer.class, path: path))
202+
paths.append(SymbolPath(class: symbolClass, path: path))
176203
}
177204
}
178205
case let .text(text, point, attributes):
179206
if let path = makePath(for: text, at: point, with: attributes) {
180-
paths.append(SymbolPath(class: layer.class, path: path.applying(matrix: ctm)))
207+
paths.append(SymbolPath(class: symbolClass, path: path.applying(matrix: ctm)))
181208
}
182209
case .layer(let l):
183210
paths.append(contentsOf: getSymbolPaths(for: l, ctm: ctm))

SwiftDraw/XML.Formatter.SVG.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ extension XML.Formatter {
6060
element.attributes["height"] = formatter.formatLength(svg.height)
6161
}
6262

63+
try element.children.append(contentsOf: makeStyles(svg.styles))
64+
6365
if let defs = try makeDefs(svg.defs) {
6466
element.children.append(defs)
6567
}
@@ -90,6 +92,21 @@ extension XML.Formatter {
9092
return element.children.isEmpty ? nil : element
9193
}
9294

95+
func makeStyles(_ sheets: [DOM.StyleSheet]) throws -> [XML.Element] {
96+
try sheets.compactMap(makeStyle)
97+
}
98+
99+
func makeStyle(_ sheet: DOM.StyleSheet) throws -> XML.Element? {
100+
let element = XML.Element(name: "style")
101+
102+
element.innerText = "\n" + sortSelectors(sheet.attributes.keys)
103+
.compactMap { encodeSelector($0, attributes: sheet.attributes[$0]!) }
104+
.joined(separator: "\n")
105+
.indenting(spaces: 8) + "\n "
106+
107+
return element.innerText?.isEmpty == false ? element : nil
108+
}
109+
93110
func makeGraphicsAttributes(from graphic: DOM.GraphicsElement) -> [String: String] {
94111
var attributes = makeGraphicsAttributes(from: graphic.attributes)
95112
attributes["id"] = graphic.id
@@ -370,5 +387,39 @@ extension XML.Formatter {
370387
return "\(cmd.rawValue)"
371388
}
372389
}
390+
391+
func sortSelectors<S: Sequence>(_ selectors: S) -> [DOM.StyleSheet.Selector] where S.Element == DOM.StyleSheet.Selector {
392+
let previews = selectors.filter { encodeSelector($0).contains("SFSymbolsPreview") }
393+
let other = selectors.filter { !encodeSelector($0).contains("SFSymbolsPreview") }
394+
return other.sorted() + previews.sorted()
395+
}
396+
397+
func encodeSelector(_ selector: DOM.StyleSheet.Selector, attributes: DOM.PresentationAttributes) -> String? {
398+
guard let style = makeStyleAttribute(from: attributes) else {
399+
return nil
400+
}
401+
let name = encodeSelector(selector)
402+
return "\(name) { \(style) }"
403+
}
404+
405+
func encodeSelector(_ selector: DOM.StyleSheet.Selector) -> String {
406+
switch selector {
407+
case .class(let name):
408+
return ".\(name)"
409+
case .element(let name):
410+
return name
411+
case .id(let name):
412+
return "#\(name)"
413+
}
414+
}
415+
}
416+
}
417+
418+
private extension String {
419+
func indenting(spaces: Int = 0) -> String {
420+
let pad = String(repeating: " ", count: spaces)
421+
return components(separatedBy: "\n")
422+
.map { "\(pad)\($0)" }
423+
.joined(separator: "\n")
373424
}
374425
}

0 commit comments

Comments
 (0)