From e63e6f24072d8429b8b4265d64dcf2d2d6616ed9 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 00:39:39 +0700 Subject: [PATCH 1/9] Prevent percent from being parsed as unit --- Source/svg/SVGParser.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 5bb08a77..f59baeb3 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -1649,12 +1649,12 @@ open class SVGParser { } fileprivate func dimensionFromString(_ string: String) -> SVGLength? { + if string.hasSuffix("%"), let value = Double(string.dropLast()) { + return SVGLength(percent: value) + } if let value = doubleFromString(string) { return SVGLength(pixels: value) } - if string.hasSuffix("%") { - return SVGLength(percent: Double(string.dropLast())!) - } return .none } From 1101d4fddc6dca475b16824895a407729ea19785 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 01:23:00 +0700 Subject: [PATCH 2/9] Parse numbers avoiding regex matching --- Source/svg/SVGParser.swift | 77 ++++++++++++++++++++------- Source/svg/SVGParserRegexHelper.swift | 14 ----- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index f59baeb3..7d23aa45 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -1659,30 +1659,27 @@ open class SVGParser { } fileprivate func doubleFromString(_ string: String) -> Double? { - if let doubleValue = Double(string) { - return doubleValue - } if string == "none" { return 0 } - guard let matcher = SVGParserRegexHelper.getUnitsIdenitifierMatcher() else { - return .none - } - let fullRange = NSRange(location: 0, length: string.count) - if let match = matcher.firstMatch(in: string, options: .reportCompletion, range: fullRange) { - let unitString = (string as NSString).substring(with: match.range(at: 1)) - let numberString = String(string.dropLast(unitString.count)) - let value = Double(numberString) ?? 0 - switch unitString { - case "px" : - return value - default: - print("SVG parsing error. Unit \(unitString) not supported") - return value - } - } - return .none + let scanner = Scanner(string: string) + let value = scanner.scannedDouble() + let unit = scanner.scannedCharacters(from: .unitCharacters) + + if !scanner.isAtEnd { + let junk = scanner.scannedUpToCharacters(from: []) ?? "" + print("Found trailing junk \"\(junk)\" in string \"\(string)\".") + return .none + } + + switch unit { + case nil, "px": + return value + default: + print("SVG parsing error. Unit \"\(unit ?? "")\" is not supported") + return value + } } fileprivate func getDoubleValueFromPercentage(_ element: SWXMLHash.XMLElement, attribute: String) -> Double? { @@ -2189,3 +2186,43 @@ fileprivate enum SVGKeys { static let color = "color" static let currentColor = "currentColor" } + +fileprivate extension Scanner { + /// A version of `scanDouble()`, available for an earlier OS. + func scannedDouble() -> Double? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanDouble() + } else { + var double: Double = 0 + return scanDouble(&double) ? double : nil + } + } + + /// A version of `scanCharacters(from:)`, available for an earlier OS. + func scannedCharacters(from set: CharacterSet) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanCharacters(from: set) + } else { + var string: NSString? = nil + return scanCharacters(from: set, into: &string) ? string as String? : nil + } + } + + /// A version of `scanUpToCharacters(from:)`, available for an earlier OS. + func scannedUpToCharacters(from set: CharacterSet) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanUpToCharacters(from: set) + } else { + var string: NSString? = nil + return scanUpToCharacters(from: set, into: &string) ? string as String? : nil + } + } +} + +fileprivate extension CharacterSet { + /// Latin alphabet characters. + static let latinAlphabet = CharacterSet(charactersIn: "a"..."z") + .union(CharacterSet(charactersIn: "A"..."Z")) + + static let unitCharacters = CharacterSet.latinAlphabet +} diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift index d50e0cc8..d251b9c3 100644 --- a/Source/svg/SVGParserRegexHelper.swift +++ b/Source/svg/SVGParserRegexHelper.swift @@ -6,13 +6,11 @@ class SVGParserRegexHelper { fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*" fileprivate static let textElementPattern = "((?s:.*))<\\/text>" fileprivate static let maskIdenitifierPattern = "url\\(#((?s:.*))\\)" - fileprivate static let unitsIdenitifierPattern = "([a-zA-Z]+)$" fileprivate static var transformMatcher: NSRegularExpression? fileprivate static var transformAttributeMatcher: NSRegularExpression? fileprivate static var textElementMatcher: NSRegularExpression? fileprivate static var maskIdenitifierMatcher: NSRegularExpression? - fileprivate static var unitsMatcher: NSRegularExpression? class func getTransformAttributeMatcher() -> NSRegularExpression? { if self.transformAttributeMatcher == nil { @@ -57,16 +55,4 @@ class SVGParserRegexHelper { } return self.maskIdenitifierMatcher } - - class func getUnitsIdenitifierMatcher() -> NSRegularExpression? { - if unitsMatcher == nil { - do { - unitsMatcher = try NSRegularExpression(pattern: unitsIdenitifierPattern, options: .caseInsensitive) - } catch { - - } - } - return unitsMatcher - } - } From c94d2d9256c1bf44b46998bcf7acceaba37790fb Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 01:31:52 +0700 Subject: [PATCH 3/9] Remove an unused regular expression --- Source/svg/SVGParserRegexHelper.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift index d251b9c3..e1a7c4ef 100644 --- a/Source/svg/SVGParserRegexHelper.swift +++ b/Source/svg/SVGParserRegexHelper.swift @@ -5,12 +5,10 @@ class SVGParserRegexHelper { fileprivate static let transformAttributePattern = "([a-z]+)\\(((\\-?\\d+\\.?\\d*e?\\-?\\d*\\s*,?\\s*)+)\\)" fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*" fileprivate static let textElementPattern = "((?s:.*))<\\/text>" - fileprivate static let maskIdenitifierPattern = "url\\(#((?s:.*))\\)" fileprivate static var transformMatcher: NSRegularExpression? fileprivate static var transformAttributeMatcher: NSRegularExpression? fileprivate static var textElementMatcher: NSRegularExpression? - fileprivate static var maskIdenitifierMatcher: NSRegularExpression? class func getTransformAttributeMatcher() -> NSRegularExpression? { if self.transformAttributeMatcher == nil { @@ -44,15 +42,4 @@ class SVGParserRegexHelper { } return self.textElementMatcher } - - class func getMaskIdenitifierMatcher() -> NSRegularExpression? { - if self.maskIdenitifierMatcher == nil { - do { - self.maskIdenitifierMatcher = try NSRegularExpression(pattern: maskIdenitifierPattern, options: .caseInsensitive) - } catch { - - } - } - return self.maskIdenitifierMatcher - } } From b0196e217d238ab0a491474b2c6a4f4819c979b1 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 01:51:36 +0700 Subject: [PATCH 4/9] Parse transform attributes using a scanner --- Source/svg/SVGParser.swift | 66 ++++++++++++++++----------- Source/svg/SVGParserRegexHelper.swift | 13 ------ 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 7d23aa45..10853e6b 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -534,21 +534,27 @@ open class SVGParser { fileprivate func parseTransformationAttribute(_ attributes: String, transform: Transform = Transform()) -> Transform { - guard let matcher = SVGParserRegexHelper.getTransformAttributeMatcher() else { - return transform - } - - let attributes = attributes.replacingOccurrences(of: "\n", with: "") - var finalTransform = transform - let fullRange = NSRange(location: 0, length: attributes.count) - - if let matchedAttribute = matcher.firstMatch(in: attributes, options: .reportCompletion, range: fullRange) { - - let attributeName = (attributes as NSString).substring(with: matchedAttribute.range(at: 1)) - let values = parseTransformValues((attributes as NSString).substring(with: matchedAttribute.range(at: 2))) + // Transform attribute regular grammar (whitespace characters are ignored): + // ([a-zA-Z]+)\(((-?\d+\.?\d*e?-?\d*,?)+)\) + // Group (1) is an attribute name. + // Group (2) is comma-separated numbers. + + var transform = transform + let scanner = Scanner(string: attributes) + + stopParse: while !scanner.isAtEnd { + guard let attributeName = scanner.scannedCharacters(from: .transformationAttributeCharacters), + scanner.scanString("(", into: nil), + let valuesString = scanner.scannedUpToString(")"), + scanner.scanString(")", into: nil) else { + break stopParse + } + + let values = parseTransformValues(valuesString) if values.isEmpty { return transform } + switch attributeName { case "translate": if let x = Double(values[0]) { @@ -556,7 +562,7 @@ open class SVGParser { if values.indices.contains(1) { y = Double(values[1]) ?? 0 } - finalTransform = transform.move(dx: x, dy: y) + transform = transform.move(dx: x, dy: y) } case "scale": if let x = Double(values[0]) { @@ -564,27 +570,27 @@ open class SVGParser { if values.indices.contains(1) { y = Double(values[1]) ?? x } - finalTransform = transform.scale(sx: x, sy: y) + transform = transform.scale(sx: x, sy: y) } case "rotate": if let angle = Double(values[0]) { if values.count == 1 { - finalTransform = transform.rotate(angle: degreesToRadians(angle)) + transform = transform.rotate(angle: degreesToRadians(angle)) } else if values.count == 3 { if let x = Double(values[1]), let y = Double(values[2]) { - finalTransform = transform.move(dx: x, dy: y).rotate(angle: degreesToRadians(angle)).move(dx: -x, dy: -y) + transform = transform.move(dx: x, dy: y).rotate(angle: degreesToRadians(angle)).move(dx: -x, dy: -y) } } } case "skewX": if let x = Double(values[0]) { let v = tan((x * Double.pi) / 180.0) - finalTransform = transform.shear(shx: v, shy: 0) + transform = transform.shear(shx: v, shy: 0) } case "skewY": if let y = Double(values[0]) { let y = tan((y * Double.pi) / 180.0) - finalTransform = transform.shear(shx: 0, shy: y) + transform = transform.shear(shx: 0, shy: y) } case "matrix": if values.count != 6 { @@ -595,18 +601,14 @@ open class SVGParser { let dx = Double(values[4]), let dy = Double(values[5]) { let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) - finalTransform = transform.concat(with: transformMatrix) + transform = transform.concat(with: transformMatrix) } default: - break + break stopParse } - let rangeToRemove = NSRange(location: 0, - length: matchedAttribute.range.location + matchedAttribute.range.length) - let newAttributeString = (attributes as NSString).replacingCharacters(in: rangeToRemove, with: "") - return parseTransformationAttribute(newAttributeString, transform: finalTransform) - } else { - return transform } + + return transform } /// Parse an RGB @@ -2217,6 +2219,16 @@ fileprivate extension Scanner { return scanUpToCharacters(from: set, into: &string) ? string as String? : nil } } + + /// A version of `scanUpToString(_:)`, available for an earlier OS. + func scannedUpToString(_ substring: String) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanUpToString(substring) + } else { + var string: NSString? = nil + return scanUpTo(substring, into: &string) ? string as String? : nil + } + } } fileprivate extension CharacterSet { @@ -2225,4 +2237,6 @@ fileprivate extension CharacterSet { .union(CharacterSet(charactersIn: "A"..."Z")) static let unitCharacters = CharacterSet.latinAlphabet + + static let transformationAttributeCharacters = CharacterSet.latinAlphabet } diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift index e1a7c4ef..6b2109f4 100644 --- a/Source/svg/SVGParserRegexHelper.swift +++ b/Source/svg/SVGParserRegexHelper.swift @@ -2,25 +2,12 @@ import Foundation class SVGParserRegexHelper { - fileprivate static let transformAttributePattern = "([a-z]+)\\(((\\-?\\d+\\.?\\d*e?\\-?\\d*\\s*,?\\s*)+)\\)" fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*" fileprivate static let textElementPattern = "((?s:.*))<\\/text>" fileprivate static var transformMatcher: NSRegularExpression? - fileprivate static var transformAttributeMatcher: NSRegularExpression? fileprivate static var textElementMatcher: NSRegularExpression? - class func getTransformAttributeMatcher() -> NSRegularExpression? { - if self.transformAttributeMatcher == nil { - do { - self.transformAttributeMatcher = try NSRegularExpression(pattern: transformAttributePattern, options: .caseInsensitive) - } catch { - - } - } - return self.transformAttributeMatcher - } - class func getTransformMatcher() -> NSRegularExpression? { if self.transformMatcher == nil { do { From 974b2d012c5f973e70d996da90b0d3aad5f8f6ac Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 02:04:15 +0700 Subject: [PATCH 5/9] Parse transform values using a scanner --- Source/svg/SVGParser.swift | 104 +++++++++++++------------- Source/svg/SVGParserRegexHelper.swift | 14 ---- 2 files changed, 52 insertions(+), 66 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 10853e6b..6fbf69f6 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -557,52 +557,51 @@ open class SVGParser { switch attributeName { case "translate": - if let x = Double(values[0]) { - var y: Double = 0 - if values.indices.contains(1) { - y = Double(values[1]) ?? 0 - } - transform = transform.move(dx: x, dy: y) - } + let x = values[0] + var y: Double = 0 + if values.indices ~= 1 { + y = values[1] + } + transform = transform.move(dx: x, dy: y) case "scale": - if let x = Double(values[0]) { - var y: Double = x - if values.indices.contains(1) { - y = Double(values[1]) ?? x - } - transform = transform.scale(sx: x, sy: y) - } + let x = values[0] + var y: Double = x + if values.indices ~= 1 { + y = values[1] + } + transform = transform.scale(sx: x, sy: y) case "rotate": - if let angle = Double(values[0]) { - if values.count == 1 { - transform = transform.rotate(angle: degreesToRadians(angle)) - } else if values.count == 3 { - if let x = Double(values[1]), let y = Double(values[2]) { - transform = transform.move(dx: x, dy: y).rotate(angle: degreesToRadians(angle)).move(dx: -x, dy: -y) - } - } - } + let angle = values[0] + if values.count == 1 { + transform = transform.rotate(angle: degreesToRadians(angle)) + } else if values.count == 3 { + let x = values[1] + let y = values[2] + transform = transform + .move(dx: x, dy: y) + .rotate(angle: degreesToRadians(angle)) + .move(dx: -x, dy: -y) + } case "skewX": - if let x = Double(values[0]) { - let v = tan((x * Double.pi) / 180.0) - transform = transform.shear(shx: v, shy: 0) - } + let x = values[0] + let v = tan((x * Double.pi) / 180.0) + transform = transform.shear(shx: v, shy: 0) case "skewY": - if let y = Double(values[0]) { - let y = tan((y * Double.pi) / 180.0) - transform = transform.shear(shx: 0, shy: y) - } + let y = values[0] + let v = tan((y * Double.pi) / 180.0) + transform = transform.shear(shx: 0, shy: v) case "matrix": if values.count != 6 { return transform } - if let m11 = Double(values[0]), let m12 = Double(values[1]), - let m21 = Double(values[2]), let m22 = Double(values[3]), - let dx = Double(values[4]), let dy = Double(values[5]) { - - let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) - transform = transform.concat(with: transformMatrix) - } + let m11 = values[0] + let m12 = values[1] + let m21 = values[2] + let m22 = values[3] + let dx = values[4] + let dy = values[5] + let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) + transform = transform.concat(with: transformMatrix) default: break stopParse } @@ -641,20 +640,21 @@ open class SVGParser { b: Int(blue.rounded(.up))) } - fileprivate func parseTransformValues(_ values: String, collectedValues: [String] = []) -> [String] { - guard let matcher = SVGParserRegexHelper.getTransformMatcher() else { - return collectedValues - } - var updatedValues: [String] = collectedValues - let fullRange = NSRange(location: 0, length: values.count) - if let matchedValue = matcher.firstMatch(in: values, options: .reportCompletion, range: fullRange) { - let value = (values as NSString).substring(with: matchedValue.range) - updatedValues.append(value) - let rangeToRemove = NSRange(location: 0, length: matchedValue.range.location + matchedValue.range.length) - let newValues = (values as NSString).replacingCharacters(in: rangeToRemove, with: "") - return parseTransformValues(newValues, collectedValues: updatedValues) - } - return updatedValues + fileprivate func parseTransformValues(_ values: String) -> [Double] { + // Parse comma-separated list of numbers. + var collectedValues: [Double] = [] + let scanner = Scanner(string: values) + + while !scanner.isAtEnd { + if let value = scanner.scannedDouble() { + collectedValues.append(value) + } else { + break + } + _ = scanner.scanString(",", into: nil) + } + + return collectedValues } fileprivate func getStyleAttributes(_ groupAttributes: [String: String], diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift index 6b2109f4..d55cd182 100644 --- a/Source/svg/SVGParserRegexHelper.swift +++ b/Source/svg/SVGParserRegexHelper.swift @@ -2,23 +2,9 @@ import Foundation class SVGParserRegexHelper { - fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*" fileprivate static let textElementPattern = "((?s:.*))<\\/text>" - - fileprivate static var transformMatcher: NSRegularExpression? fileprivate static var textElementMatcher: NSRegularExpression? - class func getTransformMatcher() -> NSRegularExpression? { - if self.transformMatcher == nil { - do { - self.transformMatcher = try NSRegularExpression(pattern: transformPattern, options: .caseInsensitive) - } catch { - - } - } - return self.transformMatcher - } - class func getTextElementMatcher() -> NSRegularExpression? { if self.textElementMatcher == nil { do { From 22233257edd21d8e663a02f25da98395efb84d67 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 19:10:32 +0700 Subject: [PATCH 6/9] Do not use regular expressions and double XML parsing for tspans --- Source/svg/SVGParser.swift | 206 +++++++++++--------------- Source/svg/SVGParserRegexHelper.swift | 18 --- 2 files changed, 85 insertions(+), 139 deletions(-) delete mode 100644 Source/svg/SVGParserRegexHelper.swift diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 6fbf69f6..25b6b9d8 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -1044,28 +1044,19 @@ open class SVGParser { fontWeight: fontWeight, pos: pos) } else { - guard let matcher = SVGParserRegexHelper.getTextElementMatcher() else { - return .none - } - let elementString = element.description - let fullRange = NSRange(location: 0, length: elementString.count) - if let match = matcher.firstMatch(in: elementString, options: .reportCompletion, range: fullRange) { - let tspans = (elementString as NSString).substring(with: match.range(at: 1)) - let rect = Rect(x: getDoubleValue(element, attribute: "x") ?? 0, - y: getDoubleValue(element, attribute: "y") ?? 0) - let collectedTspans = collectTspans(tspans, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: rect) - return Group(contents: collectedTspans, place: pos, tag: getTag(element)) - } + let rect = Rect(x: getDoubleValue(element, attribute: "x") ?? 0, + y: getDoubleValue(element, attribute: "y") ?? 0) + let collectedTspans = collectTspans(element.children, + textAnchor: textAnchor, + fill: fill, + stroke: stroke, + opacity: opacity, + fontName: fontName, + fontSize: fontSize, + fontWeight: fontWeight, + bounds: rect) + return Group(contents: collectedTspans, place: pos, tag: getTag(element)) } - return .none } fileprivate func anchorToAlign(_ textAnchor: String?) -> Align { @@ -1105,9 +1096,7 @@ open class SVGParser { // REFACTOR - fileprivate func collectTspans(_ tspan: String, - collectedTspans: [Node] = [], - withWhitespace: Bool = false, + fileprivate func collectTspans(_ contents: [XMLContent], textAnchor: String?, fill: Fill?, stroke: Stroke?, @@ -1116,99 +1105,78 @@ open class SVGParser { fontSize: Int?, fontWeight: String?, bounds: Rect) -> [Node] { - let fullString = tspan.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) as NSString - // exit recursion - if fullString.isEqual(to: "") { - return collectedTspans - } - var collection = collectedTspans - let tagRange = fullString.range(of: " element - let closingTagRange = fullString.range(of: "".lowercased()) - let tspanString = fullString.substring(to: closingTagRange.location + closingTagRange.length) - let tspanXml = SWXMLHash.parse(tspanString) - guard let indexer = tspanXml.children.first, - let text = parseTspan(indexer, - withWhitespace: withWhitespace, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: bounds, - previousCollectedTspan: collection.last) else { - - // skip this element if it can't be parsed - return collectTspans(fullString.substring(from: closingTagRange.location + closingTagRange.length), - collectedTspans: collectedTspans, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: bounds) - } - collection.append(text) - let nextString = fullString.substring(from: closingTagRange.location + closingTagRange.length) as NSString - var withWhitespace = false - if nextString.rangeOfCharacter(from: CharacterSet.whitespacesAndNewlines).location == 0 { - withWhitespace = true - } - return collectTspans(fullString.substring(from: closingTagRange.location + closingTagRange.length), - collectedTspans: collection, - withWhitespace: withWhitespace, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h)) - } - // parse as regular text element - var textString: NSString - if tagRange.location >= fullString.length { - textString = fullString - } else { - textString = fullString.substring(to: tagRange.location) as NSString - } - var nextStringWhitespace = false - var trimmedString = textString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) - if trimmedString.count != textString.length { - nextStringWhitespace = true - } - trimmedString = withWhitespace ? " \(trimmedString)" : trimmedString - let text = Text(text: trimmedString, - font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), - fill: fill, - stroke: stroke, - align: anchorToAlign(textAnchor), - baseline: .alphabetic, - place: Transform().move(dx: bounds.x + bounds.w, dy: bounds.y), opacity: opacity) - collection.append(text) - if tagRange.location >= fullString.length { // leave recursion - return collection - } - return collectTspans(fullString.substring(from: tagRange.location), - collectedTspans: collection, - withWhitespace: nextStringWhitespace, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h)) - } - - fileprivate func parseTspan(_ tspan: XMLIndexer, + var collection: [Node] = [] + var bounds = bounds + // Whether to add a space before the next non-whitespace-only text. + var addWhitespace = false + // Whether to preserve leading whitespaces before the next text + // by adding a single space prefix. + var preserveWhitespace = false + + for element in contents { + let text: Text? + if let textElement = element as? TextElement { + // parse as regular text element + let textString = textElement.text + let hasLeadingWhitespace = textString.first?.isWhitespace == true + let hasTrailingWhitespace = textString.last?.isWhitespace == true + + var trimmedString = textString.trimmingCharacters(in: .whitespacesAndNewlines) + let isWhitespaceOnly = trimmedString.isEmpty + + if hasLeadingWhitespace && preserveWhitespace && !isWhitespaceOnly { + trimmedString = " " + trimmedString + } + + addWhitespace = preserveWhitespace && hasTrailingWhitespace + preserveWhitespace = false + + if trimmedString.isEmpty { + continue + } + + let place = Transform().move(dx: bounds.x + bounds.w, dy: bounds.y) + + text = Text(text: trimmedString, + font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), + fill: fill, + stroke: stroke, + align: anchorToAlign(textAnchor), + baseline: .alphabetic, + place: place, + opacity: opacity) + } else if let tspanElement = element as? XMLElement, + tspanElement.name == "tspan" { + // parse as element + // ultimately skip it if it cannot be parsed + text = parseTspan(tspanElement, + withWhitespace: addWhitespace, + textAnchor: textAnchor, + fill: fill, + stroke: stroke, + opacity: opacity, + fontName: fontName, + fontSize: fontSize, + fontWeight: fontWeight, + bounds: bounds, + previousCollectedTspan: collection.last) + preserveWhitespace = true + addWhitespace = false + } else { + print("Skipped an unexpected element type: \(type(of: element)).") + text = nil + } + + if let text = text { + collection.append(text) + bounds = Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h) + } + } + + return collection + } + + fileprivate func parseTspan(_ element: XMLElement, withWhitespace: Bool = false, textAnchor: String?, fill: Fill?, @@ -1220,10 +1188,6 @@ open class SVGParser { bounds: Rect, previousCollectedTspan: Node?) -> Text? { - guard let element = tspan.element else { - return .none - } - let string = element.text var shouldAddWhitespace = withWhitespace let pos = getTspanPosition(element, diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift deleted file mode 100644 index d55cd182..00000000 --- a/Source/svg/SVGParserRegexHelper.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -class SVGParserRegexHelper { - - fileprivate static let textElementPattern = "((?s:.*))<\\/text>" - fileprivate static var textElementMatcher: NSRegularExpression? - - class func getTextElementMatcher() -> NSRegularExpression? { - if self.textElementMatcher == nil { - do { - self.textElementMatcher = try NSRegularExpression(pattern: textElementPattern, options: .caseInsensitive) - } catch { - - } - } - return self.textElementMatcher - } -} From e53ca25b11688c32b1c049d5de5d3f6e93bcb51d Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 19:26:36 +0700 Subject: [PATCH 7/9] Use spaces instead of tabs --- Source/svg/SVGParser.swift | 388 ++++++++++++++++++------------------- 1 file changed, 194 insertions(+), 194 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 25b6b9d8..aaf9cbe0 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -534,21 +534,21 @@ open class SVGParser { fileprivate func parseTransformationAttribute(_ attributes: String, transform: Transform = Transform()) -> Transform { - // Transform attribute regular grammar (whitespace characters are ignored): - // ([a-zA-Z]+)\(((-?\d+\.?\d*e?-?\d*,?)+)\) - // Group (1) is an attribute name. - // Group (2) is comma-separated numbers. - - var transform = transform - let scanner = Scanner(string: attributes) - - stopParse: while !scanner.isAtEnd { - guard let attributeName = scanner.scannedCharacters(from: .transformationAttributeCharacters), - scanner.scanString("(", into: nil), - let valuesString = scanner.scannedUpToString(")"), - scanner.scanString(")", into: nil) else { - break stopParse - } + // Transform attribute regular grammar (whitespace characters are ignored): + // ([a-zA-Z]+)\(((-?\d+\.?\d*e?-?\d*,?)+)\) + // Group (1) is an attribute name. + // Group (2) is comma-separated numbers. + + var transform = transform + let scanner = Scanner(string: attributes) + + stopParse: while !scanner.isAtEnd { + guard let attributeName = scanner.scannedCharacters(from: .transformationAttributeCharacters), + scanner.scanString("(", into: nil), + let valuesString = scanner.scannedUpToString(")"), + scanner.scanString(")", into: nil) else { + break stopParse + } let values = parseTransformValues(valuesString) if values.isEmpty { @@ -558,50 +558,50 @@ open class SVGParser { switch attributeName { case "translate": let x = values[0] - var y: Double = 0 - if values.indices ~= 1 { - y = values[1] - } - transform = transform.move(dx: x, dy: y) + var y: Double = 0 + if values.indices ~= 1 { + y = values[1] + } + transform = transform.move(dx: x, dy: y) case "scale": let x = values[0] - var y: Double = x - if values.indices ~= 1 { - y = values[1] - } - transform = transform.scale(sx: x, sy: y) + var y: Double = x + if values.indices ~= 1 { + y = values[1] + } + transform = transform.scale(sx: x, sy: y) case "rotate": let angle = values[0] - if values.count == 1 { - transform = transform.rotate(angle: degreesToRadians(angle)) - } else if values.count == 3 { - let x = values[1] - let y = values[2] - transform = transform - .move(dx: x, dy: y) - .rotate(angle: degreesToRadians(angle)) - .move(dx: -x, dy: -y) - } + if values.count == 1 { + transform = transform.rotate(angle: degreesToRadians(angle)) + } else if values.count == 3 { + let x = values[1] + let y = values[2] + transform = transform + .move(dx: x, dy: y) + .rotate(angle: degreesToRadians(angle)) + .move(dx: -x, dy: -y) + } case "skewX": let x = values[0] - let v = tan((x * Double.pi) / 180.0) - transform = transform.shear(shx: v, shy: 0) + let v = tan((x * Double.pi) / 180.0) + transform = transform.shear(shx: v, shy: 0) case "skewY": let y = values[0] - let v = tan((y * Double.pi) / 180.0) - transform = transform.shear(shx: 0, shy: v) + let v = tan((y * Double.pi) / 180.0) + transform = transform.shear(shx: 0, shy: v) case "matrix": if values.count != 6 { return transform } let m11 = values[0] let m12 = values[1] - let m21 = values[2] - let m22 = values[3] - let dx = values[4] - let dy = values[5] - let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) - transform = transform.concat(with: transformMatrix) + let m21 = values[2] + let m22 = values[3] + let dx = values[4] + let dy = values[5] + let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) + transform = transform.concat(with: transformMatrix) default: break stopParse } @@ -641,20 +641,20 @@ open class SVGParser { } fileprivate func parseTransformValues(_ values: String) -> [Double] { - // Parse comma-separated list of numbers. - var collectedValues: [Double] = [] - let scanner = Scanner(string: values) + // Parse comma-separated list of numbers. + var collectedValues: [Double] = [] + let scanner = Scanner(string: values) - while !scanner.isAtEnd { - if let value = scanner.scannedDouble() { - collectedValues.append(value) - } else { - break - } - _ = scanner.scanString(",", into: nil) - } + while !scanner.isAtEnd { + if let value = scanner.scannedDouble() { + collectedValues.append(value) + } else { + break + } + _ = scanner.scanString(",", into: nil) + } - return collectedValues + return collectedValues } fileprivate func getStyleAttributes(_ groupAttributes: [String: String], @@ -1044,18 +1044,18 @@ open class SVGParser { fontWeight: fontWeight, pos: pos) } else { - let rect = Rect(x: getDoubleValue(element, attribute: "x") ?? 0, - y: getDoubleValue(element, attribute: "y") ?? 0) - let collectedTspans = collectTspans(element.children, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: rect) - return Group(contents: collectedTspans, place: pos, tag: getTag(element)) + let rect = Rect(x: getDoubleValue(element, attribute: "x") ?? 0, + y: getDoubleValue(element, attribute: "y") ?? 0) + let collectedTspans = collectTspans(element.children, + textAnchor: textAnchor, + fill: fill, + stroke: stroke, + opacity: opacity, + fontName: fontName, + fontSize: fontSize, + fontWeight: fontWeight, + bounds: rect) + return Group(contents: collectedTspans, place: pos, tag: getTag(element)) } } @@ -1105,72 +1105,72 @@ open class SVGParser { fontSize: Int?, fontWeight: String?, bounds: Rect) -> [Node] { - var collection: [Node] = [] - var bounds = bounds - // Whether to add a space before the next non-whitespace-only text. - var addWhitespace = false - // Whether to preserve leading whitespaces before the next text - // by adding a single space prefix. - var preserveWhitespace = false - - for element in contents { - let text: Text? - if let textElement = element as? TextElement { - // parse as regular text element - let textString = textElement.text - let hasLeadingWhitespace = textString.first?.isWhitespace == true - let hasTrailingWhitespace = textString.last?.isWhitespace == true - - var trimmedString = textString.trimmingCharacters(in: .whitespacesAndNewlines) - let isWhitespaceOnly = trimmedString.isEmpty - - if hasLeadingWhitespace && preserveWhitespace && !isWhitespaceOnly { - trimmedString = " " + trimmedString - } - - addWhitespace = preserveWhitespace && hasTrailingWhitespace - preserveWhitespace = false - - if trimmedString.isEmpty { - continue - } - - let place = Transform().move(dx: bounds.x + bounds.w, dy: bounds.y) - - text = Text(text: trimmedString, - font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), - fill: fill, - stroke: stroke, - align: anchorToAlign(textAnchor), - baseline: .alphabetic, - place: place, - opacity: opacity) - } else if let tspanElement = element as? XMLElement, - tspanElement.name == "tspan" { - // parse as element - // ultimately skip it if it cannot be parsed - text = parseTspan(tspanElement, - withWhitespace: addWhitespace, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: bounds, - previousCollectedTspan: collection.last) - preserveWhitespace = true - addWhitespace = false - } else { - print("Skipped an unexpected element type: \(type(of: element)).") - text = nil - } - - if let text = text { - collection.append(text) - bounds = Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h) - } + var collection: [Node] = [] + var bounds = bounds + // Whether to add a space before the next non-whitespace-only text. + var addWhitespace = false + // Whether to preserve leading whitespaces before the next text + // by adding a single space prefix. + var preserveWhitespace = false + + for element in contents { + let text: Text? + if let textElement = element as? TextElement { + // parse as regular text element + let textString = textElement.text + let hasLeadingWhitespace = textString.first?.isWhitespace == true + let hasTrailingWhitespace = textString.last?.isWhitespace == true + + var trimmedString = textString.trimmingCharacters(in: .whitespacesAndNewlines) + let isWhitespaceOnly = trimmedString.isEmpty + + if hasLeadingWhitespace && preserveWhitespace && !isWhitespaceOnly { + trimmedString = " " + trimmedString + } + + addWhitespace = preserveWhitespace && hasTrailingWhitespace + preserveWhitespace = false + + if trimmedString.isEmpty { + continue + } + + let place = Transform().move(dx: bounds.x + bounds.w, dy: bounds.y) + + text = Text(text: trimmedString, + font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), + fill: fill, + stroke: stroke, + align: anchorToAlign(textAnchor), + baseline: .alphabetic, + place: place, + opacity: opacity) + } else if let tspanElement = element as? XMLElement, + tspanElement.name == "tspan" { + // parse as element + // ultimately skip it if it cannot be parsed + text = parseTspan(tspanElement, + withWhitespace: addWhitespace, + textAnchor: textAnchor, + fill: fill, + stroke: stroke, + opacity: opacity, + fontName: fontName, + fontSize: fontSize, + fontWeight: fontWeight, + bounds: bounds, + previousCollectedTspan: collection.last) + preserveWhitespace = true + addWhitespace = false + } else { + print("Skipped an unexpected element type: \(type(of: element)).") + text = nil + } + + if let text = text { + collection.append(text) + bounds = Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h) + } } return collection @@ -1629,23 +1629,23 @@ open class SVGParser { return 0 } - let scanner = Scanner(string: string) - let value = scanner.scannedDouble() - let unit = scanner.scannedCharacters(from: .unitCharacters) + let scanner = Scanner(string: string) + let value = scanner.scannedDouble() + let unit = scanner.scannedCharacters(from: .unitCharacters) - if !scanner.isAtEnd { - let junk = scanner.scannedUpToCharacters(from: []) ?? "" - print("Found trailing junk \"\(junk)\" in string \"\(string)\".") - return .none - } + if !scanner.isAtEnd { + let junk = scanner.scannedUpToCharacters(from: []) ?? "" + print("Found trailing junk \"\(junk)\" in string \"\(string)\".") + return .none + } - switch unit { - case nil, "px": - return value - default: - print("SVG parsing error. Unit \"\(unit ?? "")\" is not supported") - return value - } + switch unit { + case nil, "px": + return value + default: + print("SVG parsing error. Unit \"\(unit ?? "")\" is not supported") + return value + } } fileprivate func getDoubleValueFromPercentage(_ element: SWXMLHash.XMLElement, attribute: String) -> Double? { @@ -2154,53 +2154,53 @@ fileprivate enum SVGKeys { } fileprivate extension Scanner { - /// A version of `scanDouble()`, available for an earlier OS. - func scannedDouble() -> Double? { - if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - return scanDouble() - } else { - var double: Double = 0 - return scanDouble(&double) ? double : nil - } - } - - /// A version of `scanCharacters(from:)`, available for an earlier OS. - func scannedCharacters(from set: CharacterSet) -> String? { - if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - return scanCharacters(from: set) - } else { - var string: NSString? = nil - return scanCharacters(from: set, into: &string) ? string as String? : nil - } - } - - /// A version of `scanUpToCharacters(from:)`, available for an earlier OS. - func scannedUpToCharacters(from set: CharacterSet) -> String? { - if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - return scanUpToCharacters(from: set) - } else { - var string: NSString? = nil - return scanUpToCharacters(from: set, into: &string) ? string as String? : nil - } - } - - /// A version of `scanUpToString(_:)`, available for an earlier OS. - func scannedUpToString(_ substring: String) -> String? { - if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - return scanUpToString(substring) - } else { - var string: NSString? = nil - return scanUpTo(substring, into: &string) ? string as String? : nil - } - } + /// A version of `scanDouble()`, available for an earlier OS. + func scannedDouble() -> Double? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanDouble() + } else { + var double: Double = 0 + return scanDouble(&double) ? double : nil + } + } + + /// A version of `scanCharacters(from:)`, available for an earlier OS. + func scannedCharacters(from set: CharacterSet) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanCharacters(from: set) + } else { + var string: NSString? = nil + return scanCharacters(from: set, into: &string) ? string as String? : nil + } + } + + /// A version of `scanUpToCharacters(from:)`, available for an earlier OS. + func scannedUpToCharacters(from set: CharacterSet) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanUpToCharacters(from: set) + } else { + var string: NSString? = nil + return scanUpToCharacters(from: set, into: &string) ? string as String? : nil + } + } + + /// A version of `scanUpToString(_:)`, available for an earlier OS. + func scannedUpToString(_ substring: String) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanUpToString(substring) + } else { + var string: NSString? = nil + return scanUpTo(substring, into: &string) ? string as String? : nil + } + } } fileprivate extension CharacterSet { - /// Latin alphabet characters. - static let latinAlphabet = CharacterSet(charactersIn: "a"..."z") - .union(CharacterSet(charactersIn: "A"..."Z")) + /// Latin alphabet characters. + static let latinAlphabet = CharacterSet(charactersIn: "a"..."z") + .union(CharacterSet(charactersIn: "A"..."Z")) - static let unitCharacters = CharacterSet.latinAlphabet + static let unitCharacters = CharacterSet.latinAlphabet - static let transformationAttributeCharacters = CharacterSet.latinAlphabet + static let transformationAttributeCharacters = CharacterSet.latinAlphabet } From 708a0ab16ba1bf08c6264cc70634db8e469ed3e2 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 21:11:05 +0700 Subject: [PATCH 8/9] Scan an optional comma between transformation attributes --- Source/svg/SVGParser.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index aaf9cbe0..6c79ea89 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -550,6 +550,9 @@ open class SVGParser { break stopParse } + // Skip an optional comma after ")". + _ = scanner.scanString(",", into: nil) + let values = parseTransformValues(valuesString) if values.isEmpty { return transform From 7574dfbfb1ca6462bbbbbe0477e0321de69aa117 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 21:22:32 +0700 Subject: [PATCH 9/9] Update the project file --- Macaw.xcodeproj/project.pbxproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Macaw.xcodeproj/project.pbxproj b/Macaw.xcodeproj/project.pbxproj index 10d2385e..84cd4c12 100644 --- a/Macaw.xcodeproj/project.pbxproj +++ b/Macaw.xcodeproj/project.pbxproj @@ -233,7 +233,6 @@ 57614B661F83D15600875933 /* PinchEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E10C1E3B393900D1CB28 /* PinchEvent.swift */; }; 57614B671F83D15600875933 /* ContentsInterpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57A27BCE1E44C4EC0057BD3A /* ContentsInterpolation.swift */; }; 57614B681F83D15600875933 /* GroupRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E13E1E3B393900D1CB28 /* GroupRenderer.swift */; }; - 57614B691F83D15600875933 /* SVGParserRegexHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1491E3B393900D1CB28 /* SVGParserRegexHelper.swift */; }; 57614B6B1F83D15600875933 /* NSTimer+Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14E1E3B393900D1CB28 /* NSTimer+Closure.swift */; }; 57614B6C1F83D15600875933 /* SWXMLHash+TypeConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572CEFC51E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift */; }; 57614B6D1F83D15600875933 /* AnimationSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FF1E3B393900D1CB28 /* AnimationSequence.swift */; }; @@ -330,7 +329,6 @@ 57E5E1AA1E3B393900D1CB28 /* SVGConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1461E3B393900D1CB28 /* SVGConstants.swift */; }; 57E5E1AB1E3B393900D1CB28 /* SVGParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1471E3B393900D1CB28 /* SVGParser.swift */; }; 57E5E1AC1E3B393900D1CB28 /* SVGParserError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1481E3B393900D1CB28 /* SVGParserError.swift */; }; - 57E5E1AD1E3B393900D1CB28 /* SVGParserRegexHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1491E3B393900D1CB28 /* SVGParserRegexHelper.swift */; }; 57E5E1AE1E3B393900D1CB28 /* SVGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14A1E3B393900D1CB28 /* SVGView.swift */; }; 57E5E1AF1E3B393900D1CB28 /* CAAnimationClosure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14C1E3B393900D1CB28 /* CAAnimationClosure.swift */; }; 57E5E1B01E3B393900D1CB28 /* CGFloat+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14D1E3B393900D1CB28 /* CGFloat+Double.swift */; }; @@ -950,7 +948,6 @@ 57E5E1461E3B393900D1CB28 /* SVGConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGConstants.swift; sourceTree = ""; }; 57E5E1471E3B393900D1CB28 /* SVGParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGParser.swift; sourceTree = ""; }; 57E5E1481E3B393900D1CB28 /* SVGParserError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGParserError.swift; sourceTree = ""; }; - 57E5E1491E3B393900D1CB28 /* SVGParserRegexHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGParserRegexHelper.swift; sourceTree = ""; }; 57E5E14A1E3B393900D1CB28 /* SVGView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGView.swift; sourceTree = ""; }; 57E5E14C1E3B393900D1CB28 /* CAAnimationClosure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAAnimationClosure.swift; sourceTree = ""; }; 57E5E14D1E3B393900D1CB28 /* CGFloat+Double.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGFloat+Double.swift"; sourceTree = ""; }; @@ -1731,7 +1728,6 @@ 5B1A8C7520A15F7300E5FFAE /* SVGNodeLayout.swift */, 57E5E1471E3B393900D1CB28 /* SVGParser.swift */, 57E5E1481E3B393900D1CB28 /* SVGParserError.swift */, - 57E5E1491E3B393900D1CB28 /* SVGParserRegexHelper.swift */, C4820B171F458D0E008CE0FF /* SVGSerializer.swift */, 57E5E14A1E3B393900D1CB28 /* SVGView.swift */, ); @@ -2811,7 +2807,6 @@ 57614B671F83D15600875933 /* ContentsInterpolation.swift in Sources */, 57614B681F83D15600875933 /* GroupRenderer.swift in Sources */, 5B6E192820AC58F900454E7E /* RadialGradient.swift in Sources */, - 57614B691F83D15600875933 /* SVGParserRegexHelper.swift in Sources */, 5B6E192A20AC58F900454E7E /* Align.swift in Sources */, 57614B6B1F83D15600875933 /* NSTimer+Closure.swift in Sources */, 5B6E193620AC58F900454E7E /* Stop.swift in Sources */, @@ -2954,7 +2949,6 @@ 57A27BCF1E44C4EC0057BD3A /* ContentsInterpolation.swift in Sources */, 57E5E1A31E3B393900D1CB28 /* GroupRenderer.swift in Sources */, 5B6E192720AC58F900454E7E /* RadialGradient.swift in Sources */, - 57E5E1AD1E3B393900D1CB28 /* SVGParserRegexHelper.swift in Sources */, 5B6E192920AC58F900454E7E /* Align.swift in Sources */, 57E5E1B11E3B393900D1CB28 /* NSTimer+Closure.swift in Sources */, 30FF4962215CE97300FF653C /* MCAMediaTimingFillMode_iOS.swift in Sources */,