app/app/src/main/java/fyi/jackson/drew/homeassistantwidgets/utils/svgparse/SVGParser.java
private static Path doPath(String s) {
        int n = s.length();
        ParserHelper ph = new ParserHelper(s, 0);
        ph.skipWhitespace();
        Path p = new Path();
        float lastX = 0;
        float lastY = 0;
        float lastX1 = 0;
        float lastY1 = 0;
        float subPathStartX = 0;
        float subPathStartY = 0;
        char prevCmd = 0;
        while (ph.pos < n) {
            char cmd = s.charAt(ph.pos);
            switch (cmd) {
                case '-':
                case '+':
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (prevCmd == 'm' || prevCmd == 'M') {
                        cmd = (char) ((prevCmd) - 1);
                        break;
                    } else if (("lhvcsqta").indexOf(Character.toLowerCase(prevCmd)) >= 0) {
                        cmd = prevCmd;
                        break;
                    }
                default: {
                    ph.advance();
                    prevCmd = cmd;
                }
            }

            boolean wasCurve = false;
            switch (cmd) {
                case 'M':
                case 'm': {
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'm') {
                        subPathStartX += x;
                        subPathStartY += y;
                        p.rMoveTo(x, y);
                        lastX += x;
                        lastY += y;
                    } else {
                        subPathStartX = x;
                        subPathStartY = y;
                        p.moveTo(x, y);
                        lastX = x;
                        lastY = y;
                    }
                    break;
                }
                case 'Z':
                case 'z': {
                    p.close();
                    p.moveTo(subPathStartX, subPathStartY);
                    lastX = subPathStartX;
                    lastY = subPathStartY;
                    lastX1 = subPathStartX;
                    lastY1 = subPathStartY;
                    wasCurve = true;
                    break;
                }
                case 'T':
                case 't':
                    // todo - smooth quadratic Bezier (two parameters)
                case 'L':
                case 'l': {
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'l') {
                        p.rLineTo(x, y);
                        lastX += x;
                        lastY += y;
                    } else {
                        p.lineTo(x, y);
                        lastX = x;
                        lastY = y;
                    }
                    break;
                }
                case 'H':
                case 'h': {
                    float x = ph.nextFloat();
                    if (cmd == 'h') {
                        p.rLineTo(x, 0);
                        lastX += x;
                    } else {
                        p.lineTo(x, lastY);
                        lastX = x;
                    }
                    break;
                }
                case 'V':
                case 'v': {
                    float y = ph.nextFloat();
                    if (cmd == 'v') {
                        p.rLineTo(0, y);
                        lastY += y;
                    } else {
                        p.lineTo(lastX, y);
                        lastY = y;
                    }
                    break;
                }
                case 'C':
                case 'c': {
                    wasCurve = true;
                    float x1 = ph.nextFloat();
                    float y1 = ph.nextFloat();
                    float x2 = ph.nextFloat();
                    float y2 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'c') {
                        x1 += lastX;
                        x2 += lastX;
                        x += lastX;
                        y1 += lastY;
                        y2 += lastY;
                        y += lastY;
                    }
                    p.cubicTo(x1, y1, x2, y2, x, y);
                    lastX1 = x2;
                    lastY1 = y2;
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'Q':
                case 'q':
                    // todo - quadratic Bezier (four parameters)
                case 'S':
                case 's': {
                    wasCurve = true;
                    float x2 = ph.nextFloat();
                    float y2 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (Character.isLowerCase(cmd)) {
                        x2 += lastX;
                        x += lastX;
                        y2 += lastY;
                        y += lastY;
                    }
                    float x1 = 2 * lastX - lastX1;
                    float y1 = 2 * lastY - lastY1;
                    p.cubicTo(x1, y1, x2, y2, x, y);
                    lastX1 = x2;
                    lastY1 = y2;
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'A':
                case 'a': {
                    float rx = ph.nextFloat();
                    float ry = ph.nextFloat();
                    float theta = ph.nextFloat();
                    int largeArc = ph.nextFlag();
                    int sweepArc = ph.nextFlag();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'a') {
                        x += lastX;
                        y += lastY;
                    }
                    drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc, sweepArc);
                    lastX = x;
                    lastY = y;
                    break;
                }
                default:
                    Log.w(TAG, "Invalid path command: " + cmd);
                    ph.advance();
            }
            if (!wasCurve) {
                lastX1 = lastX;
                lastY1 = lastY;
            }
            ph.skipWhitespace();
        }
        return p;
    }
/**
     * This is where the hard-to-parse paths are handled. Uppercase rules are absolute positions, lowercase are
     * relative. Types of path rules:
     * <p/>
     * <ol>
     * <li>M/m - (x y)+ - Move to (without drawing)
     * <li>Z/z - (no params) - Close path (back to starting point)
     * <li>L/l - (x y)+ - Line to
     * <li>H/h - x+ - Horizontal ine to
     * <li>V/v - y+ - Vertical line to
     * <li>C/c - (x1 y1 x2 y2 x y)+ - Cubic bezier to
     * <li>S/s - (x2 y2 x y)+ - Smooth cubic bezier to (shorthand that assumes the x2, y2 from previous C/S is the x1,
     * y1 of this bezier)
     * <li>Q/q - (x1 y1 x y)+ - Quadratic bezier to
     * <li>T/t - (x y)+ - Smooth quadratic bezier to (assumes previous control point is "reflection" of last one w.r.t.
     * to current point)
     * </ol>
     * <p/>
     * Numbers are separate by whitespace, comma or nothing at all (!) if they are self-delimiting, (ie. begin with a -
     * sign)
     *
     * @param s the path string from the XML
     */
This is where the hard-to-parse paths are handled. Uppercase rules are absolute positions, lowercase are
relative.

@param s the path string from the XML
This is where the hard - to - parse paths are handled . Uppercase rules are absolute positions lowercase are relative . @param s the path string from the XML
private static Path doPath(String s) {
        int n = s.length();
        ParserHelper ph = new ParserHelper(s, 0);
        ph.skipWhitespace();
        Path p = new Path();
        float lastX = 0;
        float lastY = 0;
        float lastX1 = 0;
        float lastY1 = 0;
        float subPathStartX = 0;
        float subPathStartY = 0;
        char prevCmd = 0;
        while (ph.pos < n) {
            char cmd = s.charAt(ph.pos);
            switch (cmd) {
                case '-':
                case '+':
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (prevCmd == 'm' || prevCmd == 'M') {
                        cmd = (char) ((prevCmd) - 1);
                        break;
                    } else if (("lhvcsqta").indexOf(Character.toLowerCase(prevCmd)) >= 0) {
                        cmd = prevCmd;
                        break;
                    }
                default: {
                    ph.advance();
                    prevCmd = cmd;
                }
            }
            boolean wasCurve = false;
            switch (cmd) {
                case 'M':
                case 'm': {
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'm') {
                        subPathStartX += x;
                        subPathStartY += y;
                        p.rMoveTo(x, y);
                        lastX += x;
                        lastY += y;
                    } else {
                        subPathStartX = x;
                        subPathStartY = y;
                        p.moveTo(x, y);
                        lastX = x;
                        lastY = y;
                    }
                    break;
                }
                case 'Z':
                case 'z': {
                    p.close();
                    p.moveTo(subPathStartX, subPathStartY);
                    lastX = subPathStartX;
                    lastY = subPathStartY;
                    lastX1 = subPathStartX;
                    lastY1 = subPathStartY;
                    wasCurve = true;
                    break;
                }
                case 'T':
                case 't':
                case 'L':
                case 'l': {
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'l') {
                        p.rLineTo(x, y);
                        lastX += x;
                        lastY += y;
                    } else {
                        p.lineTo(x, y);
                        lastX = x;
                        lastY = y;
                    }
                    break;
                }
                case 'H':
                case 'h': {
                    float x = ph.nextFloat();
                    if (cmd == 'h') {
                        p.rLineTo(x, 0);
                        lastX += x;
                    } else {
                        p.lineTo(x, lastY);
                        lastX = x;
                    }
                    break;
                }
                case 'V':
                case 'v': {
                    float y = ph.nextFloat();
                    if (cmd == 'v') {
                        p.rLineTo(0, y);
                        lastY += y;
                    } else {
                        p.lineTo(lastX, y);
                        lastY = y;
                    }
                    break;
                }
                case 'C':
                case 'c': {
                    wasCurve = true;
                    float x1 = ph.nextFloat();
                    float y1 = ph.nextFloat();
                    float x2 = ph.nextFloat();
                    float y2 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'c') {
                        x1 += lastX;
                        x2 += lastX;
                        x += lastX;
                        y1 += lastY;
                        y2 += lastY;
                        y += lastY;
                    }
                    p.cubicTo(x1, y1, x2, y2, x, y);
                    lastX1 = x2;
                    lastY1 = y2;
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'Q':
                case 'q':
                case 'S':
                case 's': {
                    wasCurve = true;
                    float x2 = ph.nextFloat();
                    float y2 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (Character.isLowerCase(cmd)) {
                        x2 += lastX;
                        x += lastX;
                        y2 += lastY;
                        y += lastY;
                    }
                    float x1 = 2 * lastX - lastX1;
                    float y1 = 2 * lastY - lastY1;
                    p.cubicTo(x1, y1, x2, y2, x, y);
                    lastX1 = x2;
                    lastY1 = y2;
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'A':
                case 'a': {
                    float rx = ph.nextFloat();
                    float ry = ph.nextFloat();
                    float theta = ph.nextFloat();
                    int largeArc = ph.nextFlag();
                    int sweepArc = ph.nextFlag();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'a') {
                        x += lastX;
                        y += lastY;
                    }
                    drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc, sweepArc);
                    lastX = x;
                    lastY = y;
                    break;
                }
                default:
                    Log.w(TAG, "Invalid path command: " + cmd);
                    ph.advance();
            }
            if (!wasCurve) {
                lastX1 = lastX;
                lastY1 = lastY;
            }
            ph.skipWhitespace();
        }
        return p;
    }
private static Path doPath ( String s ) { int n = s . length ( ) ; ParserHelper ph = new ParserHelper ( s , 0 ) ; ph . skipWhitespace ( ) ; Path p = new Path ( ) ; float lastX = 0 ; float lastY = 0 ; float lastX1 = 0 ; float lastY1 = 0 ; float subPathStartX = 0 ; float subPathStartY = 0 ; char prevCmd = 0 ; while ( ph . pos < n ) { char cmd = s . charAt ( ph . pos ) ; switch ( cmd ) { case '-' : case '+' : case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : if ( prevCmd == 'm' || prevCmd == 'M' ) { cmd = ( char ) ( ( prevCmd ) - 1 ) ; break ; } else if ( ( "lhvcsqta" ) . indexOf ( Character . toLowerCase ( prevCmd ) ) >= 0 ) { cmd = prevCmd ; break ; } default : { ph . advance ( ) ; prevCmd = cmd ; } } boolean wasCurve = false ; switch ( cmd ) { case 'M' : case 'm' : { float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == 'm' ) { subPathStartX += x ; subPathStartY += y ; p . rMoveTo ( x , y ) ; lastX += x ; lastY += y ; } else { subPathStartX = x ; subPathStartY = y ; p . moveTo ( x , y ) ; lastX = x ; lastY = y ; } break ; } case 'Z' : case 'z' : { p . close ( ) ; p . moveTo ( subPathStartX , subPathStartY ) ; lastX = subPathStartX ; lastY = subPathStartY ; lastX1 = subPathStartX ; lastY1 = subPathStartY ; wasCurve = true ; break ; } case 'T' : case 't' : case 'L' : case 'l' : { float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == 'l' ) { p . rLineTo ( x , y ) ; lastX += x ; lastY += y ; } else { p . lineTo ( x , y ) ; lastX = x ; lastY = y ; } break ; } case 'H' : case 'h' : { float x = ph . nextFloat ( ) ; if ( cmd == 'h' ) { p . rLineTo ( x , 0 ) ; lastX += x ; } else { p . lineTo ( x , lastY ) ; lastX = x ; } break ; } case 'V' : case 'v' : { float y = ph . nextFloat ( ) ; if ( cmd == 'v' ) { p . rLineTo ( 0 , y ) ; lastY += y ; } else { p . lineTo ( lastX , y ) ; lastY = y ; } break ; } case 'C' : case 'c' : { wasCurve = true ; float x1 = ph . nextFloat ( ) ; float y1 = ph . nextFloat ( ) ; float x2 = ph . nextFloat ( ) ; float y2 = ph . nextFloat ( ) ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == 'c' ) { x1 += lastX ; x2 += lastX ; x += lastX ; y1 += lastY ; y2 += lastY ; y += lastY ; } p . cubicTo ( x1 , y1 , x2 , y2 , x , y ) ; lastX1 = x2 ; lastY1 = y2 ; lastX = x ; lastY = y ; break ; } case 'Q' : case 'q' : case 'S' : case 's' : { wasCurve = true ; float x2 = ph . nextFloat ( ) ; float y2 = ph . nextFloat ( ) ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( Character . isLowerCase ( cmd ) ) { x2 += lastX ; x += lastX ; y2 += lastY ; y += lastY ; } float x1 = 2 * lastX - lastX1 ; float y1 = 2 * lastY - lastY1 ; p . cubicTo ( x1 , y1 , x2 , y2 , x , y ) ; lastX1 = x2 ; lastY1 = y2 ; lastX = x ; lastY = y ; break ; } case 'A' : case 'a' : { float rx = ph . nextFloat ( ) ; float ry = ph . nextFloat ( ) ; float theta = ph . nextFloat ( ) ; int largeArc = ph . nextFlag ( ) ; int sweepArc = ph . nextFlag ( ) ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == 'a' ) { x += lastX ; y += lastY ; } drawArc ( p , lastX , lastY , x , y , rx , ry , theta , largeArc , sweepArc ) ; lastX = x ; lastY = y ; break ; } default : Log . w ( TAG , "Invalid path command: " + cmd ) ; ph . advance ( ) ; } if ( ! wasCurve ) { lastX1 = lastX ; lastY1 = lastY ; } ph . skipWhitespace ( ) ; } return p ; }
This is where the hard-to-parse paths are handled.
This is where the hard - to - parse paths are handled .
// todo - smooth quadratic Bezier (two parameters) // todo - quadratic Bezier (four parameters)