diff --git a/CommandLine/CommandLine.swift b/CommandLine/CommandLine.swift index 7a48004..cbc3793 100644 --- a/CommandLine/CommandLine.swift +++ b/CommandLine/CommandLine.swift @@ -94,6 +94,7 @@ Available keys for --format sfsymbol: --ultralightInsets alignment of ultralight variant: top,left,bottom,right | auto --black svg file of black variant --blackInsets alignment of black variant: top,left,bottom,right | auto + --legacy use the original, less precise alignment logic from earlier swiftdraw versions. """) diff --git a/DOM/Sources/Parser.XML.swift b/DOM/Sources/Parser.XML.swift index be338cc..220e0cd 100644 --- a/DOM/Sources/Parser.XML.swift +++ b/DOM/Sources/Parser.XML.swift @@ -49,8 +49,8 @@ package struct XMLParser { self.rawValue = rawValue } - package static let skipInvalidAttributes = Options(rawValue: 1) - package static let skipInvalidElements = Options(rawValue: 2) + package static let skipInvalidAttributes = Options(rawValue: 1 << 0) + package static let skipInvalidElements = Options(rawValue: 1 << 1) } package init(options: Options = [], filename: String? = nil) { diff --git a/SwiftDraw/Sources/CommandLine/CommandLine+Process.swift b/SwiftDraw/Sources/CommandLine/CommandLine+Process.swift index f892c41..b0ff38b 100644 --- a/SwiftDraw/Sources/CommandLine/CommandLine+Process.swift +++ b/SwiftDraw/Sources/CommandLine/CommandLine+Process.swift @@ -51,14 +51,19 @@ public extension CommandLine { precision: config.precision ?? 2) return code.data(using: .utf8)! case .sfsymbol: - let renderer = SFSymbolRenderer(options: config.options, - insets: config.insets, - insetsUltralight: config.insetsUltralight ?? config.insets, - insetsBlack: config.insetsBlack ?? config.insets, - precision: config.precision ?? 3) - let svg = try renderer.render(regular: config.input, - ultralight: config.inputUltralight, - black: config.inputBlack) + let renderer = SFSymbolRenderer( + options: config.options, + insets: config.insets, + insetsUltralight: config.insetsUltralight ?? config.insets, + insetsBlack: config.insetsBlack ?? config.insets, + precision: config.precision ?? 3, + isLegacyInsets: config.isLegacyInsetsEnabled + ) + let svg = try renderer.render( + regular: config.input, + ultralight: config.inputUltralight, + black: config.inputBlack + ) return svg.data(using: .utf8)! case .jpeg, .pdf, .png: #if canImport(CoreGraphics) diff --git a/SwiftDraw/Sources/CommandLine/CommandLine.Arguments.swift b/SwiftDraw/Sources/CommandLine/CommandLine.Arguments.swift index e6f17e7..6b81756 100644 --- a/SwiftDraw/Sources/CommandLine/CommandLine.Arguments.swift +++ b/SwiftDraw/Sources/CommandLine/CommandLine.Arguments.swift @@ -46,9 +46,15 @@ extension CommandLine { case black case blackInsets case hideUnsupportedFilters + case legacy var hasValue: Bool { - self != .hideUnsupportedFilters + switch self { + case .hideUnsupportedFilters, .legacy: + return false + default: + return true + } } } diff --git a/SwiftDraw/Sources/CommandLine/CommandLine.Configuration.swift b/SwiftDraw/Sources/CommandLine/CommandLine.Configuration.swift index 8e599a9..d68163a 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 isLegacyInsetsEnabled: Bool } public enum Format: String { @@ -119,19 +120,22 @@ extension CommandLine { let options = try parseOptions(from: modifiers) let result = source.newURL(for: format, scale: scale) - return Configuration(input: source, - inputUltralight: ultralight, - inputBlack: black, - output: output ?? result, - format: format, - size: size, - api: api, - insets: insets, - insetsUltralight: ultralightInsets, - insetsBlack: blackInsets, - scale: scale, - options: options, - precision: precision) + return Configuration( + input: source, + inputUltralight: ultralight, + inputBlack: black, + output: output ?? result, + format: format, + size: size, + api: api, + insets: insets, + insetsUltralight: ultralightInsets, + insetsBlack: blackInsets, + scale: scale, + options: options, + precision: precision, + isLegacyInsetsEnabled: modifiers.keys.contains(.legacy) + ) } static func parseFileURL(file: String, within directory: URL) throws -> URL { diff --git a/SwiftDraw/Sources/LayerTree/LayerTree.CommandOptimizer.swift b/SwiftDraw/Sources/LayerTree/LayerTree.CommandOptimizer.swift index 9ae1e92..51670c6 100644 --- a/SwiftDraw/Sources/LayerTree/LayerTree.CommandOptimizer.swift +++ b/SwiftDraw/Sources/LayerTree/LayerTree.CommandOptimizer.swift @@ -144,8 +144,8 @@ struct OptimizerOptions: OptionSet { self.rawValue = rawValue } - static let skipRedundantState = OptimizerOptions(rawValue: 1) - static let skipInitialSaveState = OptimizerOptions(rawValue: 2) + static let skipRedundantState = OptimizerOptions(rawValue: 1 << 0) + static let skipInitialSaveState = OptimizerOptions(rawValue: 1 << 1) } extension RendererCommand { diff --git a/SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift b/SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift index c896cad..ad7664a 100644 --- a/SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift +++ b/SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift @@ -39,18 +39,23 @@ public struct SFSymbolRenderer { private let insetsUltralight: CommandLine.Insets private let insetsBlack: CommandLine.Insets private let formatter: CoordinateFormatter + private let isLegacyInsets: Bool public init(options: SVG.Options, insets: CommandLine.Insets, insetsUltralight: CommandLine.Insets, insetsBlack: CommandLine.Insets, - precision: Int) { + precision: Int, + isLegacyInsets: Bool) { self.options = options self.insets = insets self.insetsUltralight = insetsUltralight self.insetsBlack = insetsBlack - self.formatter = CoordinateFormatter(delimeter: .comma, - precision: .capped(max: precision)) + self.formatter = CoordinateFormatter( + delimeter: .comma, + precision: .capped(max: precision) + ) + self.isLegacyInsets = isLegacyInsets } public func render(regular: URL, ultralight: URL?, black: URL?) throws -> String { @@ -68,25 +73,25 @@ public struct SFSymbolRenderer { template.svg.styles = image.styles.map(makeSymbolStyleSheet) - let boundsRegular = try makeBounds(svg: image, auto: Self.makeAutoBounds(for: pathsRegular), for: .regular) - template.regular.appendPaths(pathsRegular, from: boundsRegular) + let boundsRegular = try makeBounds(svg: image, auto: Self.makeAutoBounds(for: pathsRegular, isLegacy: isLegacyInsets), for: .regular) + template.regular.appendPaths(pathsRegular, from: boundsRegular, isLegacy: isLegacyInsets) if let ultralight = ultralight, let paths = Self.getPaths(for: ultralight) { - let bounds = try makeBounds(svg: ultralight, isRegularSVG: false, auto: Self.makeAutoBounds(for: paths), for: .ultralight) - template.ultralight.appendPaths(paths, from: bounds) + let bounds = try makeBounds(svg: ultralight, isRegularSVG: false, auto: Self.makeAutoBounds(for: paths, isLegacy: isLegacyInsets), for: .ultralight) + template.ultralight.appendPaths(paths, from: bounds, isLegacy: isLegacyInsets) } else { - let bounds = try makeBounds(svg: image, auto: Self.makeAutoBounds(for: pathsRegular), for: .ultralight) - template.ultralight.appendPaths(pathsRegular, from: bounds) + let bounds = try makeBounds(svg: image, auto: Self.makeAutoBounds(for: pathsRegular, isLegacy: isLegacyInsets), for: .ultralight) + template.ultralight.appendPaths(pathsRegular, from: bounds, isLegacy: isLegacyInsets) } if let black = black, let paths = Self.getPaths(for: black) { - let bounds = try makeBounds(svg: black, isRegularSVG: false, auto: Self.makeAutoBounds(for: paths), for: .black) - template.black.appendPaths(paths, from: bounds) + let bounds = try makeBounds(svg: black, isRegularSVG: false, auto: Self.makeAutoBounds(for: paths, isLegacy: isLegacyInsets), for: .black) + template.black.appendPaths(paths, from: bounds, isLegacy: isLegacyInsets) } else { - let bounds = try makeBounds(svg: image, auto: Self.makeAutoBounds(for: pathsRegular), for: .black) - template.black.appendPaths(pathsRegular, from: bounds) + let bounds = try makeBounds(svg: image, auto: Self.makeAutoBounds(for: pathsRegular, isLegacy: isLegacyInsets), for: .black) + template.black.appendPaths(pathsRegular, from: bounds, isLegacy: isLegacyInsets) } let element = try XML.Formatter.SVG(formatter: formatter).makeElement(from: template.svg) @@ -257,7 +262,7 @@ extension SFSymbolRenderer { #endif } - static func makeAutoBounds(for paths: [SymbolPath]) -> LayerTree.Rect { + static func makeAutoBounds(for paths: [SymbolPath], isLegacy: Bool = false) -> LayerTree.Rect { var min = LayerTree.Point.maximum var max = LayerTree.Point.minimum for p in paths { @@ -266,8 +271,10 @@ extension SFSymbolRenderer { max = max.maximum(combining: .init(bounds.maxX, bounds.maxY)) } - min.x -= 10 - max.x += 10 + if !isLegacy { + min.x -= 10 + max.x += 10 + } return LayerTree.Rect( x: min.x, @@ -516,7 +523,7 @@ private extension ContainerElement { private extension SFSymbolTemplate.Variant { - mutating func appendPaths(_ paths: [SFSymbolRenderer.SymbolPath], from source: LayerTree.Rect) { + mutating func appendPaths(_ paths: [SFSymbolRenderer.SymbolPath], from source: LayerTree.Rect, isLegacy: Bool = false) { let matrix = SFSymbolRenderer.makeTransformation(from: source, to: bounds) contents.paths = paths .map { @@ -527,9 +534,16 @@ private extension SFSymbolTemplate.Variant { } let midX = bounds.midX - let newWidth = ((source.width * matrix.a) / 2) - left.x = midX - newWidth - right.x = midX + newWidth + if isLegacy { + // preserve behaviour from earlier SwiftDraw versions with --legacy option + let newWidth = ((source.width * matrix.a) / 2) + 10 + left.x = min(left.x, midX - newWidth) + right.x = max(right.x, midX + newWidth) + } else { + let newWidth = ((source.width * matrix.a) / 2) + left.x = midX - newWidth + right.x = midX + newWidth + } } } diff --git a/SwiftDraw/Tests/Renderer/Renderer.SFSymbolTests.swift b/SwiftDraw/Tests/Renderer/Renderer.SFSymbolTests.swift index de93c75..17cd181 100644 --- a/SwiftDraw/Tests/Renderer/Renderer.SFSymbolTests.swift +++ b/SwiftDraw/Tests/Renderer/Renderer.SFSymbolTests.swift @@ -187,18 +187,26 @@ private extension DOM.SVG { private extension SFSymbolRenderer { static func render(fileURL: URL) throws -> String { - let renderer = SFSymbolRenderer(options: [], insets: .init(), - insetsUltralight: .init(), - insetsBlack: .init(), - precision: 3) + let renderer = SFSymbolRenderer( + options: [], + insets: .init(), + insetsUltralight: .init(), + insetsBlack: .init(), + precision: 3, + isLegacyInsets: false + ) return try renderer.render(regular: fileURL, ultralight: nil, black: nil) } static func render(svg: DOM.SVG) throws -> String { - let renderer = SFSymbolRenderer(options: [], insets: .init(), - insetsUltralight: .init(), - insetsBlack: .init(), - precision: 3) + let renderer = SFSymbolRenderer( + options: [], + insets: .init(), + insetsUltralight: .init(), + insetsBlack: .init(), + precision: 3, + isLegacyInsets: false + ) return try renderer.render(default: svg, ultralight: nil, black: nil) } }