Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removing force unwrapping in SVGParser #733

Merged
merged 1 commit into from
Oct 19, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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