mirror of
https://github.com/exyte/Macaw.git
synced 2024-10-26 04:49:57 +03:00
Parse transform attributes using a scanner
This commit is contained in:
parent
c94d2d9256
commit
b0196e217d
@ -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.
|
||||
|
||||
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 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)))
|
||||
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,19 +601,15 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an RGB
|
||||
/// - returns: Color for the corresponding SVG color string in RGB notation.
|
||||
@ -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
|
||||
}
|
||||
|
@ -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 = "<text.*?>((?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 {
|
||||
|
Loading…
Reference in New Issue
Block a user