Class

SVG String Path Tokenizer

The SVGPathStringTokenizer class breaks SVG strings into their command components.


Declaration

class SVGPathStringTokenizer 

Overview

This class is used to break an SVG string into its component commands prior to bring parsed into a CGPath.

Essentially, it produces an array of tokens (i.e. commands and values) that may look like:

["M","0","1"...]

Topics

Properties

separatorCharacterSet

Used to skip or omit separator characters from the Token array.

let separatorCharacterSet = CharacterSet(charactersIn: ...)

This removes spaces, tabs, new lines, carriage returns and commas.

commandCharacterSet

Used to identify SVG-specific command characters.

let commandCharacterSet = CharacterSet(charactersIn: "mMLlHhVvzZCcSsQqTt")

numberStartCharacterSet

Used to identify the characters necessary for reconstructing the first character of a value.

let numberStartCharacterSet = CharacterSet(charactersIn: "-+.0123456789")

numberCharacterSet

Used to identify the characters necessary for reconstructing a value.

let numberCharacterSet = CharacterSet(charactersIn: ".0123456789eE")

string

The string to tokenize.

var string: String

range

The range of the string to parse.

var range: Range<String.UnicodeScalarView.Index>

Initialization

Initialization requires a string.

init(string: String) {
    self.string = string
    range = string.unicodeScalars.startIndex..<string.unicodeScalars.endIndex
}

Tokenizing

tokenize()

The function responsible for initializing tokenization and returning an array of Tokens.

func tokenize() -> [Token] {
    var array = [Token]()
    while let token = nextToken() {
        array.append(token)
    }
    return array
}

nextToken()

This function attempts to grab the next token in the string. It returns a command, a value, or nil.

func nextToken() -> Token? {
    skipSeparators()

    guard let c = get() else {
        return nil
    }

    if commandCharacterSet.isMember(c) {
        return .command(c)
    }

    if numberStartCharacterSet.isMember(c) {
        var numberString = String(c)
        while let c = peek(), numberCharacterSet.isMember(c) {
            numberString += String(c)
            get()
        }

        if let value = Double(numberString) {
            return .value(CGFloat(value))
        }
    }

    return nil
}

get()

Attempts to grab the next UnicodeScalar element of string. It keeps track of the current place in string by updating range.

func get() -> UnicodeScalar? {
    guard range.lowerBound != range.upperBound else {
        return nil
    }
    let c = string.unicodeScalars[range.lowerBound]
    range = string.unicodeScalars.index(after: range.lowerBound)..<range.upperBound
    return c
}

peek()

Looks at the next UnicodeScalar in the string without updating range.

func peek() -> UnicodeScalar? {
    guard range.lowerBound != range.upperBound else {
        return nil
    }
    return string.unicodeScalars[range.lowerBound]
}

skipSeparators()

Skips separator tokens.

func skipSeparators() {
    while range.lowerBound != range.upperBound && separatorCharacterSet.isMember(string.unicodeScalars[range.lowerBound]) {
        range = string.unicodeScalars.index(after: range.lowerBound)..<range.upperBound
    }
}
background Made with Flow.
underscore Made with Flow.
line2 Made with Flow.
line1 Made with Flow.
circle Made with Flow.
hit Made with Flow.

result(s) found for “”.