diff --git a/CommandLine/CommandLine.swift b/CommandLine/CommandLine.swift index 0d720fd..3fbbc3b 100644 --- a/CommandLine/CommandLine.swift +++ b/CommandLine/CommandLine.swift @@ -90,6 +90,7 @@ Available keys for --format swift: Available keys for --format sfsymbol: --insets alignment of regular variant: top,left,bottom,right | auto + --size size category to generate: small, medium large. (default is small) --ultralight svg file of ultralight variant --ultralight-insets alignment of ultralight variant: top,left,bottom,right | auto --black svg file of black variant diff --git a/SwiftDraw/Sources/CommandLine/CommandLine+Process.swift b/SwiftDraw/Sources/CommandLine/CommandLine+Process.swift index b0ff38b..1875ae4 100644 --- a/SwiftDraw/Sources/CommandLine/CommandLine+Process.swift +++ b/SwiftDraw/Sources/CommandLine/CommandLine+Process.swift @@ -52,6 +52,7 @@ public extension CommandLine { return code.data(using: .utf8)! case .sfsymbol: let renderer = SFSymbolRenderer( + size: config.symbolSize ?? .small, options: config.options, insets: config.insets, insetsUltralight: config.insetsUltralight ?? config.insets, diff --git a/SwiftDraw/Sources/CommandLine/CommandLine.Configuration.swift b/SwiftDraw/Sources/CommandLine/CommandLine.Configuration.swift index d139850..50b60ef 100644 --- a/SwiftDraw/Sources/CommandLine/CommandLine.Configuration.swift +++ b/SwiftDraw/Sources/CommandLine/CommandLine.Configuration.swift @@ -48,6 +48,7 @@ extension CommandLine { public var scale: Scale public var options: SVG.Options public var precision: Int? + public var symbolSize: SFSymbolRenderer.SizeCategory? public var isLegacyInsetsEnabled: Bool } @@ -107,7 +108,7 @@ extension CommandLine { throw Error.invalid } - let size = try parseSize(from: modifiers[.size]) + let size = try parseSize(from: modifiers[.size], format: format) let scale = try parseScale(from: modifiers[.scale]) let precision = try parsePrecision(from: modifiers[.precision]) let insets = try parseInsets(from: modifiers[.insets]) ?? Insets() @@ -117,6 +118,7 @@ extension CommandLine { let black = try parseFileURL(file: modifiers[.black], within: baseDirectory) let blackInsets = try parseInsets(from: modifiers[.blackInsets]) let output = try parseFileURL(file: modifiers[.output], within: baseDirectory) + let symbolSize = try parseSymbolSize(from: modifiers[.size], format: format) let options = try parseOptions(from: modifiers) let result = source.newURL(for: format, scale: scale) @@ -134,6 +136,7 @@ extension CommandLine { scale: scale, options: options, precision: precision, + symbolSize: symbolSize, isLegacyInsetsEnabled: modifiers.keys.contains(.legacy) ) } @@ -178,8 +181,9 @@ extension CommandLine { return precision } - static func parseSize(from value: String??) throws -> Size { - guard let value = value, + static func parseSize(from value: String??, format: Format) throws -> Size { + guard format != .sfsymbol, + let value = value, let value = value else { return .default } @@ -196,6 +200,26 @@ extension CommandLine { return .custom(width: Int(width), height: Int(height)) } + static func parseSymbolSize(from value: String??, format: Format) throws -> SFSymbolRenderer.SizeCategory? { + guard format == .sfsymbol, + let value = value, + let value = value else { + return nil + } + + switch value { + case "small": + return .small + case "medium": + return .medium + case "large": + return .large + default: + throw Error.invalid + + } + } + static func parseAPI(from value: String??) throws -> API? { guard let value = value, let value = value else { diff --git a/SwiftDraw/Sources/Formatter/XML.Formatter.SVG.swift b/SwiftDraw/Sources/Formatter/XML.Formatter.SVG.swift index 3fccc4c..2c1e49f 100644 --- a/SwiftDraw/Sources/Formatter/XML.Formatter.SVG.swift +++ b/SwiftDraw/Sources/Formatter/XML.Formatter.SVG.swift @@ -328,17 +328,17 @@ extension XML.Formatter { case let .matrix(a: a, b: b, c: c, d: d, e: e, f: f): return "matrix(\(formatter.format(a,b,c,d,e,f)))" case let .translate(tx: tx, ty: ty): - return "translate(\(formatter.format(tx, ty))" + return "translate(\(formatter.format(tx, ty)))" case let .scale(sx: sx, sy: sy): - return "scale(\(formatter.format(sx, sy))" + return "scale(\(formatter.format(sx, sy)))" case let .rotate(angle: angle): - return "rotate(\(formatter.format(angle))" + return "rotate(\(formatter.format(angle)))" case let .rotatePoint(angle: angle, cx: cx, cy: cy): - return "rotate(\(formatter.format(angle, cx, cy))" + return "rotate(\(formatter.format(angle, cx, cy)))" case let .skewX(angle: angle): - return "skewX(\(formatter.format(angle))" + return "skewX(\(formatter.format(angle)))" case let .skewY(angle: angle): - return "skewY(\(formatter.format(angle))" + return "skewY(\(formatter.format(angle)))" } } diff --git a/SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift b/SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift index 436a4e9..f334832 100644 --- a/SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift +++ b/SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift @@ -34,6 +34,7 @@ import Foundation public struct SFSymbolRenderer { + private let size: SizeCategory private let options: SVG.Options private let insets: CommandLine.Insets private let insetsUltralight: CommandLine.Insets @@ -41,12 +42,22 @@ public struct SFSymbolRenderer { private let formatter: CoordinateFormatter private let isLegacyInsets: Bool - public init(options: SVG.Options, - insets: CommandLine.Insets, - insetsUltralight: CommandLine.Insets, - insetsBlack: CommandLine.Insets, - precision: Int, - isLegacyInsets: Bool) { + public enum SizeCategory { + case small + case medium + case large + } + + public init( + size: SizeCategory, + options: SVG.Options, + insets: CommandLine.Insets, + insetsUltralight: CommandLine.Insets, + insetsBlack: CommandLine.Insets, + precision: Int, + isLegacyInsets: Bool + ) { + self.size = size self.options = options self.insets = insets self.insetsUltralight = insetsUltralight @@ -94,6 +105,8 @@ public struct SFSymbolRenderer { template.black.appendPaths(pathsRegular, from: bounds, isLegacy: isLegacyInsets) } + template.setSize(size) + let element = try XML.Formatter.SVG(formatter: formatter).makeElement(from: template.svg) let formatter = XML.Formatter(spaces: 4) let result = formatter.encodeRootElement(element) @@ -356,25 +369,36 @@ struct SFSymbolTemplate { let svg: DOM.SVG + var typeReference: DOM.Path var ultralight: Variant var regular: Variant var black: Variant init(svg: DOM.SVG) throws { self.svg = svg + self.typeReference = try svg.group(id: "Guides").path(id: "H-reference") self.ultralight = try Variant(svg: svg, kind: "Ultralight") self.regular = try Variant(svg: svg, kind: "Regular") self.black = try Variant(svg: svg, kind: "Black") } + mutating func setSize(_ size: SFSymbolRenderer.SizeCategory) { + typeReference.attributes.transform = [.translate(tx: 0, ty: size.yOffset)] + ultralight.setSize(size) + regular.setSize(size) + black.setSize(size) + } + struct Variant { var left: Guide var contents: Contents var right: Guide + private var kind: String init(svg: DOM.SVG, kind: String) throws { let guides = try svg.group(id: "Guides") let symbols = try svg.group(id: "Symbols") + self.kind = kind self.left = try Guide(guides.path(id: "left-margin-\(kind)-S")) self.contents = try Contents(symbols.group(id: "\(kind)-S")) self.right = try Guide(guides.path(id: "right-margin-\(kind)-S")) @@ -385,6 +409,15 @@ struct SFSymbolTemplate { let maxX = right.x return .init(x: minX, y: 76, width: maxX - minX, height: 70) } + + mutating func setSize(_ size: SFSymbolRenderer.SizeCategory) { + left.setID("left-margin-\(kind)-\(size.name)") + left.y += size.yOffset + contents.setID("\(kind)-\(size.name)") + contents.setTransform(.translate(tx: 0, ty: size.yOffset)) + right.setID("right-margin-\(kind)-\(size.name)") + right.y += size.yOffset + } } struct Guide { @@ -394,6 +427,10 @@ struct SFSymbolTemplate { self.path = path } + func setID(_ id: String) { + path.id = id + } + var x: DOM.Float { get { guard case let .move(x, _, _) = path.segments[0] else { @@ -408,6 +445,21 @@ struct SFSymbolTemplate { path.segments[0] = .move(x: newValue, y: y, space: space) } } + + var y: DOM.Float { + get { + guard case let .move(_, y, _) = path.segments[0] else { + fatalError() + } + return y + } + set { + guard case let .move(x, _, space) = path.segments[0] else { + fatalError() + } + path.segments[0] = .move(x: x, y: newValue, space: space) + } + } } struct Contents { @@ -417,6 +469,10 @@ struct SFSymbolTemplate { self.group = group } + func setID(_ id: String) { + group.id = id + } + var paths: [DOM.Path] { get { group.childElements as! [DOM.Path] @@ -425,6 +481,35 @@ struct SFSymbolTemplate { group.childElements = newValue } } + + func setTransform(_ transform: DOM.Transform) { + group.attributes.transform = [transform] + } + } +} + +extension SFSymbolRenderer.SizeCategory { + + var name: String { + switch self { + case .small: + return "S" + case .medium: + return "M" + case .large: + return "L" + } + } + + var yOffset: Float { + switch self { + case .small: + return 0 + case .medium: + return 200 + case .large: + return 400 + } } } diff --git a/SwiftDraw/Tests/Renderer/Renderer.SFSymbolTests.swift b/SwiftDraw/Tests/Renderer/Renderer.SFSymbolTests.swift index 17cd181..7256f3e 100644 --- a/SwiftDraw/Tests/Renderer/Renderer.SFSymbolTests.swift +++ b/SwiftDraw/Tests/Renderer/Renderer.SFSymbolTests.swift @@ -188,6 +188,7 @@ private extension SFSymbolRenderer { static func render(fileURL: URL) throws -> String { let renderer = SFSymbolRenderer( + size: .small, options: [], insets: .init(), insetsUltralight: .init(), @@ -200,6 +201,7 @@ private extension SFSymbolRenderer { static func render(svg: DOM.SVG) throws -> String { let renderer = SFSymbolRenderer( + size: .small, options: [], insets: .init(), insetsUltralight: .init(),