1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-09-21 01:47:44 +03:00
Macaw/Source/svg/CSSParser.swift

135 lines
4.4 KiB
Swift

//
// CSSParser.swift
// Macaw
//
// Created by Yuri Strot on 10/26/18.
//
import Foundation
#if !CARTHAGE
import SWXMLHash
#endif
enum Selector {
case byId(String)
case byClass(String)
case byTag(String)
}
class CSSParser {
fileprivate var stylesByClass: [String: [String: String]] = [:]
fileprivate var stylesById: [String: [String: String]] = [:]
fileprivate var stylesByTag: [String: [String: String]] = [:]
func parse(content: String) {
let parts = content.components(separatedBy: .whitespacesAndNewlines).joined().split(separator: "{")
var separatedParts = [String.SubSequence]()
parts.forEach { substring in
separatedParts.append(contentsOf: substring.split(separator: "}"))
}
if separatedParts.count % 2 == 0 {
let headers = stride(from: 0, to: separatedParts.count, by: 2).map { String(separatedParts[$0]) }
let bodies = stride(from: 1, to: separatedParts.count, by: 2).map { separatedParts[$0] }
for (index, header) in headers.enumerated() {
for headerPart in header.split(separator: ",") where headerPart.count > 1 {
let selector = parseSelector(text: String(headerPart))
var currentStyles = getStyles(selector: selector)
if currentStyles == nil {
currentStyles = [String: String]()
}
let style = String(bodies[index])
let styleParts = style.components(separatedBy: ";")
styleParts.forEach { styleAttribute in
if !styleAttribute.isEmpty {
let currentStyle = styleAttribute.components(separatedBy: ":")
if currentStyle.count == 2 {
currentStyles![currentStyle[0]] = currentStyle[1]
}
}
}
setStyles(selector: selector, styles: currentStyles!)
}
}
}
}
func parseSelector(text: String) -> Selector {
if text.first == "#" {
return .byId(String(text.dropFirst()))
} else if text.first == "." {
return .byClass(String(text.dropFirst()))
}
return .byTag(text)
}
func getStyles(element: SWXMLHash.XMLElement) -> [String: String] {
var styleAttributes = [String: String]()
if let styles = stylesByTag[element.name] {
for (att, val) in styles {
if styleAttributes.index(forKey: att) == nil {
styleAttributes.updateValue(val, forKey: att)
}
}
}
if let classNamesString = element.allAttributes["class"]?.text {
let classNames = classNamesString.split(separator: " ")
classNames.forEach { className in
let classString = String(className)
if let styleAttributesFromTable = stylesByClass[classString] {
for (att, val) in styleAttributesFromTable {
if styleAttributes.index(forKey: att) == nil {
styleAttributes.updateValue(val, forKey: att)
}
}
}
}
}
if let idString = element.allAttributes["id"]?.text {
if let styleAttributesFromTable = stylesById[idString] {
for (att, val) in styleAttributesFromTable {
if styleAttributes.index(forKey: att) == nil {
styleAttributes.updateValue(val, forKey: att)
}
}
}
}
return styleAttributes
}
fileprivate func getStyles(selector: Selector) -> [String: String]? {
switch selector {
case .byId(let id):
return stylesById[id]
case .byTag(let tag):
return stylesByTag[tag]
case .byClass(let name):
return stylesByClass[name]
}
}
fileprivate func setStyles(selector: Selector, styles: [String: String]) {
switch selector {
case .byId(let id):
stylesById[id] = styles
case .byTag(let tag):
stylesByTag[tag] = styles
case .byClass(let name):
stylesByClass[name] = styles
}
}
}