mirror of
https://github.com/exyte/Macaw.git
synced 2024-10-26 04:49:57 +03:00
Merge pull request #733 from TomasLinhart/svg-parser-remove-force-unwraps
Removing force unwrapping in SVGParser
This commit is contained in:
commit
faf3ab8c37
@ -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)
|
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") {
|
if let opacity = svgElement?.attribute(by: "opacity") {
|
||||||
root.opacity = getOpacity(opacity.text)
|
root.opacity = getOpacity(opacity.text)
|
||||||
}
|
}
|
||||||
@ -466,25 +466,30 @@ open class SVGParser {
|
|||||||
contentUserSpace = false
|
contentUserSpace = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentNode: Node?
|
func parseContentNode() throws -> Node? {
|
||||||
if pattern.children.isEmpty {
|
if pattern.children.isEmpty {
|
||||||
if let parentPattern = parentPattern {
|
return parentPattern?.content
|
||||||
contentNode = parentPattern.content
|
} else if pattern.children.count == 1,
|
||||||
}
|
let child = pattern.children.first,
|
||||||
} else if pattern.children.count == 1,
|
let shape = try parseNode(child) as? Shape {
|
||||||
let shape = try parseNode(pattern.children.first!) as? Shape {
|
return shape
|
||||||
contentNode = shape
|
} else {
|
||||||
} else {
|
var shapes = [Shape]()
|
||||||
var shapes = [Shape]()
|
try pattern.children.forEach { indexer in
|
||||||
try pattern.children.forEach { indexer in
|
if let shape = try parseNode(indexer) as? Shape {
|
||||||
if let shape = try parseNode(indexer) as? Shape {
|
shapes.append(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,
|
bounds: bounds,
|
||||||
userSpace: userSpace,
|
userSpace: userSpace,
|
||||||
contentUserSpace: contentUserSpace)
|
contentUserSpace: contentUserSpace)
|
||||||
@ -760,14 +765,14 @@ open class SVGParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func getPatternFill(pattern: UserSpacePattern, locus: Locus?) -> Pattern {
|
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,
|
let tranform = BoundsUtils.transformForLocusInRespectiveCoords(respectiveLocus: pattern.bounds,
|
||||||
absoluteLocus: locus!)
|
absoluteLocus: locus)
|
||||||
return Pattern(content: pattern.content, bounds: pattern.bounds.applying(tranform), userSpace: true)
|
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,
|
if let patternNode = BoundsUtils.createNodeFromRespectiveCoords(respectiveNode: pattern.content,
|
||||||
absoluteLocus: locus!) {
|
absoluteLocus: locus) {
|
||||||
return Pattern(content: patternNode, bounds: pattern.bounds, userSpace: pattern.userSpace)
|
return Pattern(content: patternNode, bounds: pattern.bounds, userSpace: pattern.userSpace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -889,7 +894,7 @@ open class SVGParser {
|
|||||||
|
|
||||||
fileprivate func getTag(_ element: SWXMLHash.XMLElement) -> [String] {
|
fileprivate func getTag(_ element: SWXMLHash.XMLElement) -> [String] {
|
||||||
let id = element.allAttributes["id"]?.text
|
let id = element.allAttributes["id"]?.text
|
||||||
return id != nil ? [id!] : []
|
return id.map { [$0] } ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func getOpacity(_ styleParts: [String: String]) -> Double {
|
fileprivate func getOpacity(_ styleParts: [String: String]) -> Double {
|
||||||
@ -1200,9 +1205,22 @@ open class SVGParser {
|
|||||||
let text = shouldAddWhitespace ? " \(string)" : string
|
let text = shouldAddWhitespace ? " \(string)" : string
|
||||||
let attributes = getStyleAttributes([:], element: element)
|
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,
|
return Text(text: text,
|
||||||
font: getFont(attributes, fontName: fontName, fontWeight: fontWeight, fontSize: fontSize),
|
font: getFont(attributes, fontName: fontName, fontWeight: fontWeight, fontSize: fontSize),
|
||||||
fill: (attributes[SVGKeys.fill] != nil) ? getFillColor(attributes)! : fill,
|
fill: fillColor,
|
||||||
stroke: stroke ?? getStroke(attributes),
|
stroke: stroke ?? getStroke(attributes),
|
||||||
align: anchorToAlign(textAnchor ?? getTextAnchor(attributes)),
|
align: anchorToAlign(textAnchor ?? getTextAnchor(attributes)),
|
||||||
baseline: .alphabetic,
|
baseline: .alphabetic,
|
||||||
@ -1298,8 +1316,11 @@ open class SVGParser {
|
|||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
|
|
||||||
if clip.children.count == 1 {
|
if clip.children.count == 1, let child = clip.children.first {
|
||||||
let shape = try parseNode(clip.children.first!) as! Shape
|
guard let shape = try parseNode(child) as? Shape else {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
if shape.place != Transform.identity {
|
if shape.place != Transform.identity {
|
||||||
let locus = TransformedLocus(locus: shape.form, transform: shape.place)
|
let locus = TransformedLocus(locus: shape.form, transform: shape.place)
|
||||||
return UserSpaceLocus(locus: locus, userSpace: userSpace)
|
return UserSpaceLocus(locus: locus, userSpace: userSpace)
|
||||||
@ -1324,8 +1345,12 @@ open class SVGParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func parseMask(_ mask: XMLIndexer) throws -> UserSpaceNode? {
|
fileprivate func parseMask(_ mask: XMLIndexer) throws -> UserSpaceNode? {
|
||||||
|
guard let element = mask.element else {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
var userSpace = true
|
var userSpace = true
|
||||||
let styles = getStyleAttributes([:], element: mask.element!)
|
let styles = getStyleAttributes([:], element: element)
|
||||||
if let units = mask.element?.allAttributes["maskContentUnits"]?.text, units == "objectBoundingBox" {
|
if let units = mask.element?.allAttributes["maskContentUnits"]?.text, units == "objectBoundingBox" {
|
||||||
userSpace = false
|
userSpace = false
|
||||||
}
|
}
|
||||||
@ -1334,14 +1359,20 @@ open class SVGParser {
|
|||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
|
|
||||||
if mask.children.count == 1 {
|
if mask.children.count == 1, let child = mask.children.first {
|
||||||
let node = try parseNode(mask.children.first!, groupStyle: styles)!
|
guard let node = try parseNode(child, groupStyle: styles) else {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
return UserSpaceNode(node: node, userSpace: userSpace)
|
return UserSpaceNode(node: node, userSpace: userSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
var nodes = [Node]()
|
let nodes = try mask.children.reduce(into: [Node]()) { nodes, indexer in
|
||||||
try mask.children.forEach { indexer in
|
guard let element = indexer.element else {
|
||||||
let position = getPosition(indexer.element!)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let position = getPosition(element)
|
||||||
if let useNode = try parseUse(indexer, groupStyle: styles, place: position) {
|
if let useNode = try parseUse(indexer, groupStyle: styles, place: position) {
|
||||||
nodes.append(useNode)
|
nodes.append(useNode)
|
||||||
} else if let contentNode = try parseNode(indexer, groupStyle: styles) {
|
} else if let contentNode = try parseNode(indexer, groupStyle: styles) {
|
||||||
@ -1367,7 +1398,6 @@ open class SVGParser {
|
|||||||
}
|
}
|
||||||
effects.removeValue(forKey: filterIn)
|
effects.removeValue(forKey: filterIn)
|
||||||
|
|
||||||
let filterOut = element.allAttributes["result"]?.text
|
|
||||||
var resultingEffect: Effect? = .none
|
var resultingEffect: Effect? = .none
|
||||||
|
|
||||||
switch element.name {
|
switch element.name {
|
||||||
@ -1382,18 +1412,34 @@ open class SVGParser {
|
|||||||
}
|
}
|
||||||
case "feColorMatrix":
|
case "feColorMatrix":
|
||||||
if let type = element.allAttributes["type"]?.text {
|
if let type = element.allAttributes["type"]?.text {
|
||||||
var matrix: ColorMatrix?
|
func parseMatrix() -> ColorMatrix? {
|
||||||
if type == "saturate" {
|
if type == "saturate" {
|
||||||
matrix = ColorMatrix(saturate: getDoubleValue(element, attribute: "values")!)
|
guard let value = getDoubleValue(element, attribute: "values") else {
|
||||||
} else if type == "hueRotate" {
|
print("Invalid number value in \(element.name)")
|
||||||
let degrees = getDoubleValue(element, attribute: "values")!
|
return nil
|
||||||
matrix = ColorMatrix(hueRotate: degrees / 180 * Double.pi)
|
}
|
||||||
} else if type == "luminanceToAlpha" {
|
|
||||||
matrix = .luminanceToAlpha
|
return ColorMatrix(saturate: value)
|
||||||
} else { // "matrix"
|
} else if type == "hueRotate" {
|
||||||
matrix = ColorMatrix(values: getMatrix(element, attribute: "values"))
|
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"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resultingEffect = ColorMatrixEffect(matrix: matrix!, input: currentEffect)
|
|
||||||
|
guard let matrix = parseMatrix() else {
|
||||||
|
print("Invalid matrix in \(element.name)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resultingEffect = ColorMatrixEffect(matrix: matrix, input: currentEffect)
|
||||||
}
|
}
|
||||||
case "feBlend":
|
case "feBlend":
|
||||||
if let filterIn2 = element.allAttributes["in2"]?.text {
|
if let filterIn2 = element.allAttributes["in2"]?.text {
|
||||||
@ -1408,10 +1454,11 @@ open class SVGParser {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if filterOut == nil {
|
guard let filterOut = element.allAttributes["result"]?.text else {
|
||||||
return resultingEffect
|
return resultingEffect
|
||||||
}
|
}
|
||||||
effects[filterOut!] = resultingEffect
|
|
||||||
|
effects[filterOut] = resultingEffect
|
||||||
}
|
}
|
||||||
|
|
||||||
if effects.count == 1 {
|
if effects.count == 1 {
|
||||||
@ -1857,6 +1904,8 @@ private class PathDataReader {
|
|||||||
private var previous: UnicodeScalar?
|
private var previous: UnicodeScalar?
|
||||||
private var iterator: String.UnicodeScalarView.Iterator
|
private var iterator: String.UnicodeScalarView.Iterator
|
||||||
|
|
||||||
|
private static let spaces: Set<UnicodeScalar> = Set("\n\r\t ,".unicodeScalars)
|
||||||
|
|
||||||
init(input: String) {
|
init(input: String) {
|
||||||
self.input = input
|
self.input = input
|
||||||
self.iterator = input.unicodeScalars.makeIterator()
|
self.iterator = input.unicodeScalars.makeIterator()
|
||||||
@ -1944,9 +1993,9 @@ private class PathDataReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func skipSpaces() {
|
private func skipSpaces() {
|
||||||
var ch = current
|
var currentCharacter = current
|
||||||
while ch != nil && "\n\r\t ,".contains(String(ch!)) {
|
while let character = currentCharacter, Self.spaces.contains(character) {
|
||||||
ch = readNext()
|
currentCharacter = readNext()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user