Skip to content

Commit 15e34dc

Browse files
committed
support nested svg
1 parent 3d021b8 commit 15e34dc

File tree

9 files changed

+144
-23
lines changed

9 files changed

+144
-23
lines changed

Samples.bundle/nested-svg.svg

Lines changed: 24 additions & 6 deletions
Loading

SwiftDraw/DOM.SVG.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,20 @@
3131

3232
extension DOM {
3333
final class SVG: GraphicsElement, ContainerElement {
34+
var x: Coordinate?
35+
var y: Coordinate?
3436
var width: Length
3537
var height: Length
3638
var viewBox: ViewBox?
37-
39+
3840
var childElements = [GraphicsElement]()
3941

4042
var styles = [StyleSheet]()
4143
var defs = Defs()
4244

43-
init(width: Length, height: Length) {
45+
init(x: Coordinate? = nil, y: Coordinate? = nil, width: Length, height: Length) {
46+
self.x = x
47+
self.y = y
4448
self.width = width
4549
self.height = height
4650
}

SwiftDraw/LayerTree.Builder.Layer.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ extension LayerTree.Builder {
5555
}
5656

5757
return .layer(l)
58-
5958
}
6059

6160
static func makeTextContents(from text: DOM.Text, with state: State) -> LayerTree.Layer.Contents {

SwiftDraw/LayerTree.Builder.swift

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,41 @@ extension LayerTree {
4444
}
4545

4646
func makeLayer() -> Layer {
47-
let l = makeLayer(from: svg, inheriting: State())
48-
l.transform = Builder.makeTransform(for: svg.viewBox,
49-
width: svg.width,
50-
height: svg.height)
47+
makeLayer(svg: svg, inheriting: State())
48+
}
49+
50+
func makeLayer(svg: DOM.SVG, inheriting previousState: State) -> Layer {
51+
let l = makeLayer(from: svg, inheriting: previousState)
52+
l.transform = Builder.makeTransform(
53+
x: svg.x,
54+
y: svg.y,
55+
viewBox: svg.viewBox,
56+
width: svg.width,
57+
height: svg.height
58+
)
5159
return l
5260
}
5361

54-
static func makeTransform(for viewBox: DOM.SVG.ViewBox?, width: DOM.Length, height: DOM.Length) -> [LayerTree.Transform] {
55-
guard let viewBox = viewBox else {
56-
return []
57-
}
62+
static func makeTransform(
63+
x: DOM.Coordinate?,
64+
y: DOM.Coordinate?,
65+
viewBox: DOM.SVG.ViewBox?,
66+
width: DOM.Length,
67+
height: DOM.Length
68+
) -> [LayerTree.Transform] {
69+
let position = LayerTree.Transform.translate(tx: x ?? 0, ty: y ?? 0)
70+
let viewBox = viewBox ?? DOM.SVG.ViewBox(x: 0, y: 0, width: .init(width), height: .init(height))
5871

5972
let sx = LayerTree.Float(width) / viewBox.width
6073
let sy = LayerTree.Float(height) / viewBox.height
6174
let scale = LayerTree.Transform.scale(sx: sx, sy: sy)
6275
let translate = LayerTree.Transform.translate(tx: -viewBox.x, ty: -viewBox.y)
6376

64-
var transform = [LayerTree.Transform]()
77+
var transform: [LayerTree.Transform] = []
78+
79+
if position != .translate(tx: 0, ty: 0) {
80+
transform.append(position)
81+
}
6582

6683
if scale != .scale(sx: 1, sy: 1) {
6784
transform.append(scale)
@@ -91,14 +108,26 @@ extension LayerTree {
91108
return l
92109
}
93110

111+
func makeChildLayer(from element: DOM.GraphicsElement, inheriting previousState: State) -> Layer {
112+
if let svg = element as? DOM.SVG {
113+
let layer = makeLayer(svg: svg, inheriting: previousState)
114+
let viewBox = svg.viewBox ?? DOM.SVG.ViewBox(x: 0, y: 0, width: .init(svg.width), height: .init(svg.height))
115+
let bounds = LayerTree.Rect(x: viewBox.x, y: viewBox.y, width: viewBox.width, height: viewBox.height)
116+
layer.clip = [ClipShape(shape: .rect(within: bounds, radii: .zero), transform: .identity)]
117+
return layer
118+
} else {
119+
return makeLayer(from: element, inheriting: previousState)
120+
}
121+
}
122+
94123
func makeAllContents(from element: DOM.GraphicsElement, with state: State) -> [Layer.Contents] {
95124
var all = [Layer.Contents]()
96125
if let contents = makeContents(from: element, with: state) {
97126
all.append(contents)
98127
}
99128
else if let container = element as? ContainerElement {
100129
container.childElements.forEach{
101-
let contents = Layer.Contents.layer(makeLayer(from: $0, inheriting: state))
130+
let contents = Layer.Contents.layer(makeChildLayer(from: $0, inheriting: state))
102131
all.append(contents)
103132
}
104133
}

SwiftDraw/Parser.XML.Element.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ extension XMLParser {
9898
case "use": ge = try parseUse(att)
9999
case "switch": ge = try parseSwitch(e)
100100
case "image": ge = try parseImage(att)
101+
case "svg": ge = try parseSVG(e)
101102
default: return nil
102103
}
103104

@@ -169,6 +170,16 @@ extension XMLParser {
169170
return group
170171
}
171172

173+
func parseGroupA(_ e: XML.Element) throws -> DOM.Group {
174+
guard e.name == "svg" else {
175+
throw Error.invalid
176+
}
177+
178+
let group = DOM.Group()
179+
group.childElements = try parseContainerChildren(e)
180+
return group
181+
}
182+
172183
func parseSwitch(_ e: XML.Element) throws -> DOM.Switch {
173184
guard e.name == "switch" else {
174185
throw Error.invalid

SwiftDraw/Parser.XML.SVG.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ extension XMLParser {
5656
guard let h = height else { throw XMLParser.Error.missingAttribute(name: "height") }
5757

5858
let svg = DOM.SVG(width: DOM.Length(w), height: DOM.Length(h))
59+
svg.x = try att.parseCoordinate("x")
60+
svg.y = try att.parseCoordinate("y")
5961
svg.childElements = try parseContainerChildren(e)
6062
svg.viewBox = try parseViewBox(try att.parseString("viewBox"))
6163

SwiftDrawTests/LayerTree.BuilderTests.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ final class LayerTreeBuilderTests: XCTestCase {
3838
typealias Contents = LayerTree.Layer.Contents
3939

4040
func testMakeViewBoxTransform() {
41-
var transform = LayerTree.Builder.makeTransform(for: nil, width: 100, height: 200)
41+
var transform = LayerTree.Builder.makeTransform(viewBox: nil, width: 100, height: 200)
4242
XCTAssertEqual(transform, [])
4343

4444
let viewbox = DOM.SVG.ViewBox(x: 0, y: 0, width: 200, height: 200)
45-
transform = LayerTree.Builder.makeTransform(for: viewbox, width: 100, height: 100)
45+
transform = LayerTree.Builder.makeTransform(viewBox: viewbox, width: 100, height: 100)
4646
XCTAssertEqual(transform, [.scale(sx: 0.5, sy: 0.5)])
4747

4848
let viewbox1 = DOM.SVG.ViewBox(x: 10, y: -10, width: 100, height: 100)
49-
transform = LayerTree.Builder.makeTransform(for: viewbox1, width: 100, height: 100)
49+
transform = LayerTree.Builder.makeTransform(viewBox: viewbox1, width: 100, height: 100)
5050
XCTAssertEqual(transform, [.translate(tx: -10, ty: 10)])
5151
}
5252

@@ -141,7 +141,7 @@ private extension LayerTree.FillAttributes {
141141
}
142142
}
143143

144-
private extension LayerTree.Builder {
144+
extension LayerTree.Builder {
145145

146146
static func makeStrokeAttributes(with state: State) -> LayerTree.StrokeAttributes {
147147
let builder = LayerTree.Builder(svg: DOM.SVG(width: 10, height: 10))
@@ -151,5 +151,19 @@ private extension LayerTree.Builder {
151151
func createClipShapes(for element: DOM.GraphicsElement) -> [LayerTree.Shape] {
152152
makeClipShapes(for: element).map(\.shape)
153153
}
154+
155+
static func makeTransform(
156+
viewBox: DOM.SVG.ViewBox?,
157+
width: DOM.Length,
158+
height: DOM.Length
159+
) -> [LayerTree.Transform] {
160+
makeTransform(
161+
x: nil,
162+
y: nil,
163+
viewBox: viewBox,
164+
width: width,
165+
height: height
166+
)
167+
}
154168
}
155169

SwiftDrawTests/ParserSVGImageTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,10 @@ final class ParserSVGImageTests: XCTestCase {
110110
XCTAssertEqual(svg.width, 550)
111111
XCTAssertEqual(svg.height, 350)
112112
}
113+
114+
func testNested() throws {
115+
let svg = try DOM.SVG.parse(fileNamed: "nested-svg.svg", in: .test)
116+
XCTAssertEqual(svg.width, 360)
117+
XCTAssertEqual(svg.height, 450)
118+
}
113119
}
Lines changed: 38 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)