Skip to content

Commit

Permalink
Merge pull request exyte#733 from TomasLinhart/svg-parser-remove-forc…
Browse files Browse the repository at this point in the history
…e-unwraps

Removing force unwrapping in SVGParser
  • Loading branch information
ystrot authored Oct 19, 2020
2 parents 471d305 + 2585d66 commit faf3ab8
Showing 1 changed file with 97 additions and 48 deletions.
145 changes: 97 additions & 48 deletions Source/svg/SVGParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ open class SVGParser {
}
}
}
let layout = svgElement != nil ? try parseViewBox(svgElement!) : nil
let layout = try svgElement.flatMap(parseViewBox)
try parseSvg(parsedXml.children)
let root = layout != nil ? SVGCanvas(layout: layout!, contents: nodes) : Group(contents: nodes)
let root = layout.flatMap { SVGCanvas(layout: $0, contents: nodes) } ?? Group(contents: nodes)
if let opacity = svgElement?.attribute(by: "opacity") {
root.opacity = getOpacity(opacity.text)
}
Expand Down Expand Up @@ -466,25 +466,30 @@ open class SVGParser {
contentUserSpace = false
}

var contentNode: Node?
if pattern.children.isEmpty {
if let parentPattern = parentPattern {
contentNode = parentPattern.content
}
} else if pattern.children.count == 1,
let shape = try parseNode(pattern.children.first!) as? Shape {
contentNode = shape
} else {
var shapes = [Shape]()
try pattern.children.forEach { indexer in
if let shape = try parseNode(indexer) as? Shape {
shapes.append(shape)
func parseContentNode() throws -> Node? {
if pattern.children.isEmpty {
return parentPattern?.content
} else if pattern.children.count == 1,
let child = pattern.children.first,
let shape = try parseNode(child) as? Shape {
return shape
} else {
var shapes = [Shape]()
try pattern.children.forEach { indexer in
if let shape = try parseNode(indexer) as? Shape {
shapes.append(shape)
}
}
return Group(contents: shapes)
}
contentNode = Group(contents: shapes)
}

return UserSpacePattern(content: contentNode!,
guard let contentNode = try parseContentNode() else {
print("Pattern does not contain any content.")
return .none
}

return UserSpacePattern(content: contentNode,
bounds: bounds,
userSpace: userSpace,
contentUserSpace: contentUserSpace)
Expand Down Expand Up @@ -760,14 +765,14 @@ open class SVGParser {
}

fileprivate func getPatternFill(pattern: UserSpacePattern, locus: Locus?) -> Pattern {
if pattern.userSpace == false && pattern.contentUserSpace == true {
if let locus = locus, pattern.userSpace == false && pattern.contentUserSpace == true {
let tranform = BoundsUtils.transformForLocusInRespectiveCoords(respectiveLocus: pattern.bounds,
absoluteLocus: locus!)
absoluteLocus: locus)
return Pattern(content: pattern.content, bounds: pattern.bounds.applying(tranform), userSpace: true)
}
if pattern.userSpace == true && pattern.contentUserSpace == false {
if let locus = locus, pattern.userSpace == true && pattern.contentUserSpace == false {
if let patternNode = BoundsUtils.createNodeFromRespectiveCoords(respectiveNode: pattern.content,
absoluteLocus: locus!) {
absoluteLocus: locus) {
return Pattern(content: patternNode, bounds: pattern.bounds, userSpace: pattern.userSpace)
}
}
Expand Down Expand Up @@ -889,7 +894,7 @@ open class SVGParser {

fileprivate func getTag(_ element: SWXMLHash.XMLElement) -> [String] {
let id = element.allAttributes["id"]?.text
return id != nil ? [id!] : []
return id.map { [$0] } ?? []
}

fileprivate func getOpacity(_ styleParts: [String: String]) -> Double {
Expand Down Expand Up @@ -1200,9 +1205,22 @@ open class SVGParser {
let text = shouldAddWhitespace ? " \(string)" : string
let attributes = getStyleAttributes([:], element: element)

var fillColor: Fill? {
guard let fillValue = attributes[SVGKeys.fill] else {
return fill
}

if let fillColor = getFillColor(attributes) {
return fillColor
}

print("Found invalid fill \(fillValue) in style attributes of \(element.name).")
return fill
}

return Text(text: text,
font: getFont(attributes, fontName: fontName, fontWeight: fontWeight, fontSize: fontSize),
fill: (attributes[SVGKeys.fill] != nil) ? getFillColor(attributes)! : fill,
fill: fillColor,
stroke: stroke ?? getStroke(attributes),
align: anchorToAlign(textAnchor ?? getTextAnchor(attributes)),
baseline: .alphabetic,
Expand Down Expand Up @@ -1298,8 +1316,11 @@ open class SVGParser {
return .none
}

if clip.children.count == 1 {
let shape = try parseNode(clip.children.first!) as! Shape
if clip.children.count == 1, let child = clip.children.first {
guard let shape = try parseNode(child) as? Shape else {
return .none
}

if shape.place != Transform.identity {
let locus = TransformedLocus(locus: shape.form, transform: shape.place)
return UserSpaceLocus(locus: locus, userSpace: userSpace)
Expand All @@ -1324,8 +1345,12 @@ open class SVGParser {
}

fileprivate func parseMask(_ mask: XMLIndexer) throws -> UserSpaceNode? {
guard let element = mask.element else {
return .none
}

var userSpace = true
let styles = getStyleAttributes([:], element: mask.element!)
let styles = getStyleAttributes([:], element: element)
if let units = mask.element?.allAttributes["maskContentUnits"]?.text, units == "objectBoundingBox" {
userSpace = false
}
Expand All @@ -1334,14 +1359,20 @@ open class SVGParser {
return .none
}

if mask.children.count == 1 {
let node = try parseNode(mask.children.first!, groupStyle: styles)!
if mask.children.count == 1, let child = mask.children.first {
guard let node = try parseNode(child, groupStyle: styles) else {
return .none
}

return UserSpaceNode(node: node, userSpace: userSpace)
}

var nodes = [Node]()
try mask.children.forEach { indexer in
let position = getPosition(indexer.element!)
let nodes = try mask.children.reduce(into: [Node]()) { nodes, indexer in
guard let element = indexer.element else {
return
}

let position = getPosition(element)
if let useNode = try parseUse(indexer, groupStyle: styles, place: position) {
nodes.append(useNode)
} else if let contentNode = try parseNode(indexer, groupStyle: styles) {
Expand All @@ -1367,7 +1398,6 @@ open class SVGParser {
}
effects.removeValue(forKey: filterIn)

let filterOut = element.allAttributes["result"]?.text
var resultingEffect: Effect? = .none

switch element.name {
Expand All @@ -1382,18 +1412,34 @@ open class SVGParser {
}
case "feColorMatrix":
if let type = element.allAttributes["type"]?.text {
var matrix: ColorMatrix?
if type == "saturate" {
matrix = ColorMatrix(saturate: getDoubleValue(element, attribute: "values")!)
} else if type == "hueRotate" {
let degrees = getDoubleValue(element, attribute: "values")!
matrix = ColorMatrix(hueRotate: degrees / 180 * Double.pi)
} else if type == "luminanceToAlpha" {
matrix = .luminanceToAlpha
} else { // "matrix"
matrix = ColorMatrix(values: getMatrix(element, attribute: "values"))
func parseMatrix() -> ColorMatrix? {
if type == "saturate" {
guard let value = getDoubleValue(element, attribute: "values") else {
print("Invalid number value in \(element.name)")
return nil
}

return ColorMatrix(saturate: value)
} else if type == "hueRotate" {
guard let degrees = getDoubleValue(element, attribute: "values") else {
print("Invalid number value in \(element.name)")
return nil
}

return ColorMatrix(hueRotate: degrees / 180 * Double.pi)
} else if type == "luminanceToAlpha" {
return .luminanceToAlpha
} else { // "matrix"
return ColorMatrix(values: getMatrix(element, attribute: "values"))
}
}

guard let matrix = parseMatrix() else {
print("Invalid matrix in \(element.name)")
continue
}
resultingEffect = ColorMatrixEffect(matrix: matrix!, input: currentEffect)

resultingEffect = ColorMatrixEffect(matrix: matrix, input: currentEffect)
}
case "feBlend":
if let filterIn2 = element.allAttributes["in2"]?.text {
Expand All @@ -1408,10 +1454,11 @@ open class SVGParser {
continue
}

if filterOut == nil {
guard let filterOut = element.allAttributes["result"]?.text else {
return resultingEffect
}
effects[filterOut!] = resultingEffect

effects[filterOut] = resultingEffect
}

if effects.count == 1 {
Expand Down Expand Up @@ -1857,6 +1904,8 @@ private class PathDataReader {
private var previous: UnicodeScalar?
private var iterator: String.UnicodeScalarView.Iterator

private static let spaces: Set<UnicodeScalar> = Set("\n\r\t ,".unicodeScalars)

init(input: String) {
self.input = input
self.iterator = input.unicodeScalars.makeIterator()
Expand Down Expand Up @@ -1944,9 +1993,9 @@ private class PathDataReader {
}

private func skipSpaces() {
var ch = current
while ch != nil && "\n\r\t ,".contains(String(ch!)) {
ch = readNext()
var currentCharacter = current
while let character = currentCharacter, Self.spaces.contains(character) {
currentCharacter = readNext()
}
}

Expand Down

0 comments on commit faf3ab8

Please sign in to comment.