src/com/larvalabs/svgandroid/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;
		RectF r = new RectF();
		char cmd = 'x';
		while (ph.pos < n) {
			char next = s.charAt(ph.pos);
			if (!Character.isDigit(next) && !(next == '.') && !(next == '-')) {
				cmd = next;
				ph.advance();
			} else if (cmd == 'M') { // implied command
				cmd = 'L';
			} else if (cmd == 'm') { // implied command
				cmd = 'l';
			} else { // implied command
				// Log.d(TAG, "Implied command: " + cmd);
			}
			p.computeBounds(r, true);
			// Log.d(TAG, "  " + cmd + " " + r);
			// Util.debug("* Commands remaining: '" + path + "'.");
			boolean wasCurve = false;
			switch (cmd) {
			case 'M':
			case 'm': {
				float x = ph.nextFloat();
				float y = ph.nextFloat();
				if (cmd == 'm') {
					p.rMoveTo(x, y);
					lastX += x;
					lastY += y;
				} else {
					p.moveTo(x, y);
					lastX = x;
					lastY = y;
				}
				break;
			}
			case 'Z':
			case 'z': {
				p.close();
				break;
			}
			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 'S':
			case 's': {
				wasCurve = true;
				float x2 = ph.nextFloat();
				float y2 = ph.nextFloat();
				float x = ph.nextFloat();
				float y = ph.nextFloat();
				if (cmd == 's') {
					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 = (int) ph.nextFloat();
				int sweepArc = (int) ph.nextFloat();
				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 == 1, sweepArc == 1);
				lastX = x;
				lastY = y;
				break;
			}
			default:
				Log.d(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;
		RectF r = new RectF();
		char cmd = 'x';
		while (ph.pos < n) {
			char next = s.charAt(ph.pos);
			if (!Character.isDigit(next) && !(next == '.') && !(next == '-')) {
				cmd = next;
				ph.advance();
			} else if (cmd == 'M') { 
				cmd = 'L';
			} else if (cmd == 'm') { 
				cmd = 'l';
			} else { 
			}
			p.computeBounds(r, true);
			boolean wasCurve = false;
			switch (cmd) {
			case 'M':
			case 'm': {
				float x = ph.nextFloat();
				float y = ph.nextFloat();
				if (cmd == 'm') {
					p.rMoveTo(x, y);
					lastX += x;
					lastY += y;
				} else {
					p.moveTo(x, y);
					lastX = x;
					lastY = y;
				}
				break;
			}
			case 'Z':
			case 'z': {
				p.close();
				break;
			}
			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 'S':
			case 's': {
				wasCurve = true;
				float x2 = ph.nextFloat();
				float y2 = ph.nextFloat();
				float x = ph.nextFloat();
				float y = ph.nextFloat();
				if (cmd == 's') {
					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 = (int) ph.nextFloat();
				int sweepArc = (int) ph.nextFloat();
				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 == 1, sweepArc == 1);
				lastX = x;
				lastY = y;
				break;
			}
			default:
				Log.d(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 ; RectF r = new RectF ( ) ; char cmd = 'x' ; while ( ph . pos < n ) { char next = s . charAt ( ph . pos ) ; if ( ! Character . isDigit ( next ) && ! ( next == '.' ) && ! ( next == '-' ) ) { cmd = next ; ph . advance ( ) ; } else if ( cmd == 'M' ) { cmd = 'L' ; } else if ( cmd == 'm' ) { cmd = 'l' ; } else { } p . computeBounds ( r , true ) ; boolean wasCurve = false ; switch ( cmd ) { case 'M' : case 'm' : { float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == 'm' ) { p . rMoveTo ( x , y ) ; lastX += x ; lastY += y ; } else { p . moveTo ( x , y ) ; lastX = x ; lastY = y ; } break ; } case 'Z' : case 'z' : { p . close ( ) ; break ; } 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 'S' : case 's' : { wasCurve = true ; float x2 = ph . nextFloat ( ) ; float y2 = ph . nextFloat ( ) ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == 's' ) { 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 = ( int ) ph . nextFloat ( ) ; int sweepArc = ( int ) ph . nextFloat ( ) ; 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 == 1 , sweepArc == 1 ) ; lastX = x ; lastY = y ; break ; } default : Log . d ( 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 .
// implied command // implied command // implied command // Log.d(TAG, "Implied command: " + cmd); // Log.d(TAG, "  " + cmd + " " + r); // Util.debug("* Commands remaining: '" + path + "'.");