From b0196e217d238ab0a491474b2c6a4f4819c979b1 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 01:51:36 +0700 Subject: [PATCH] Parse transform attributes using a scanner --- Source/svg/SVGParser.swift | 60 +++++++++++++++++---------- Source/svg/SVGParserRegexHelper.swift | 13 ------ 2 files changed, 37 insertions(+), 36 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 - } + // 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. - let attributes = attributes.replacingOccurrences(of: "\n", with: "") - var finalTransform = transform - let fullRange = NSRange(location: 0, length: attributes.count) + var transform = transform + let scanner = Scanner(string: attributes) - if let matchedAttribute = matcher.firstMatch(in: attributes, options: .reportCompletion, range: fullRange) { + 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 attributeName = (attributes as NSString).substring(with: matchedAttribute.range(at: 1)) - let values = parseTransformValues((attributes as NSString).substring(with: matchedAttribute.range(at: 2))) + 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 {