package org.xmind.ui.internal.svgsupport;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xmind.gef.draw2d.graphics.Path;
public class SvgPathParser {
private static SvgPathParser instance;
private float centralX;
private float centralY;
private float clientWidth;
private float clientHeight;
private float lastPointX = 0;
private float lastPointY = 0;
private float lastKnotX = 0;
private float lastKnotY = 0;
private float top = 0;
private float bottom = 0;
private float left = 0;
private float right = 0;
private SvgPathParser() {
}
public void parseSvgPath(Path path, float centralX, float centralY,
float clientWidth, float clientHeight, String svgPath) {
this.centralX = centralX;
this.centralY = centralY;
this.clientWidth = clientWidth;
this.clientHeight = clientHeight;
for (int i = 0; i < 2; i++) {
final Matcher matchPathCmd = Pattern.compile(
"([MmLlHhVvAaQqTtCcSsZz])|([-+]?((\\d*\\.\\d+)|(\\d+))([eE][-+]?\\d+)?)") //$NON-NLS-1$
.matcher(svgPath);
LinkedList<String> tokens = new LinkedList<String>();
while (matchPathCmd.find())
tokens.addLast(matchPathCmd.group());
char curCmd = 'Z';
while (tokens.size() != 0) {
String curToken = tokens.removeFirst();
char initChar = curToken.charAt(0);
if ((initChar >= 'A' && initChar <= 'Z')
|| (initChar >= 'a' && initChar <= 'z'))
curCmd = initChar;
else
tokens.addFirst(curToken);
switch (curCmd) {
case 'M':
movetoAbs(path, nextFloat(tokens), nextFloat(tokens),
i != 0);
curCmd = 'L';
break;
case 'm':
movetoRel(path, nextFloat(tokens), nextFloat(tokens),
i != 0);
curCmd = 'l';
break;
case 'L':
linetoAbs(path, nextFloat(tokens), nextFloat(tokens),
i != 0);
break;
case 'l':
linetoRel(path, nextFloat(tokens), nextFloat(tokens),
i != 0);
break;
case 'H':
linetoHorizontalAbs(path, nextFloat(tokens), i != 0);
break;
case 'h':
linetoHorizontalRel(path, nextFloat(tokens), i != 0);
break;
case 'V':
linetoVerticalAbs(path, nextFloat(tokens), i != 0);
break;
case 'v':
linetoVerticalRel(path, nextFloat(tokens), i != 0);
break;
case 'A':
case 'a':
break;
case 'Q':
curvetoQuadraticAbs(path, nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), i != 0);
break;
case 'q':
curvetoQuadraticRel(path, nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), i != 0);
break;
case 'T':
curvetoQuadraticSmoothAbs(path, nextFloat(tokens),
nextFloat(tokens), i != 0);
break;
case 't':
curvetoQuadraticSmoothRel(path, nextFloat(tokens),
nextFloat(tokens), i != 0);
break;
case 'C':
curvetoCubicAbs(path, nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens), i != 0);
break;
case 'c':
curvetoCubicRel(path, nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens), i != 0);
break;
case 'S':
curvetoCubicSmoothAbs(path, nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), i != 0);
break;
case 's':
curvetoCubicSmoothRel(path, nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), i != 0);
break;
case 'Z':
case 'z':
if (i != 0) {
path.close();
left = 0;
right = 0;
top = 0;
bottom = 0;
}
lastPointX = 0;
lastPointY = 0;
lastKnotX = 0;
lastKnotY = 0;
break;
default:
throw new RuntimeException("Invalid path element"); //$NON-NLS-1$
}
}
}
}
private void movetoAbs(Path path, float x, float y, boolean draw) {
if (draw)
path.moveTo(correctX(x), correctY(y));
lastPointX = x;
lastPointY = y;
if (!draw) {
left = x;
right = x;
top = y;
bottom = y;
}
}
private void movetoRel(Path path, float x, float y, boolean draw) {
movetoAbs(path, x + lastPointX, y + lastPointY, draw);
}
private void linetoAbs(Path path, float x, float y, boolean draw) {
if (draw)
path.lineTo(correctX(x), correctY(y));
lastPointX = x;
lastPointY = y;
lastKnotX = x;
lastKnotY = y;
if (!draw)
calcTBLR(x, y);
}
private void linetoRel(Path path, float x, float y, boolean draw) {
linetoAbs(path, x + lastPointX, y + lastPointY, draw);
}
private void linetoHorizontalAbs(Path path, float x, boolean draw) {
linetoAbs(path, x, lastPointY, draw);
}
private void linetoHorizontalRel(Path path, float x, boolean draw) {
linetoHorizontalAbs(path, x + lastPointX, draw);
}
private void linetoVerticalAbs(Path path, float y, boolean draw) {
linetoAbs(path, lastPointX, y, draw);
}
private void linetoVerticalRel(Path path, float y, boolean draw) {
linetoVerticalAbs(path, y + lastPointY, draw);
}
private void curvetoQuadraticAbs(Path path, float x1, float y1, float x,
float y, boolean draw) {
if (draw)
path.quadTo(correctX(x1), correctY(y1), correctX(x), correctY(y));
lastPointX = x;
lastPointY = y;
lastKnotX = x1;
lastKnotY = y1;
if (!draw)
calcTBLR(x, y);
}
private void curvetoQuadraticRel(Path path, float x1, float y1, float x,
float y, boolean draw) {
curvetoQuadraticAbs(path, x1 + lastPointX, y1 + lastPointY,
x + lastPointX, y + lastPointY, draw);
}
private void curvetoQuadraticSmoothAbs(Path path, float x, float y,
boolean draw) {
curvetoQuadraticAbs(path, lastKnotX, lastKnotY, x, y, draw);
}
private void curvetoQuadraticSmoothRel(Path path, float x, float y,
boolean draw) {
curvetoQuadraticSmoothAbs(path, x + lastPointX, y + lastPointY, draw);
}
private void curvetoCubicAbs(Path path, float x1, float y1, float x2,
float y2, float x, float y, boolean draw) {
if (draw)
path.cubicTo(correctX(x1), correctY(y1), correctX(x2), correctY(y2),
correctX(x), correctY(y));
lastPointX = x;
lastPointY = y;
lastKnotX = x2;
lastKnotY = y2;
if (!draw)
calcTBLR(x, y);
}
private void curvetoCubicRel(Path path, float x1, float y1, float x2,
float y2, float x, float y, boolean draw) {
curvetoCubicAbs(path, x1 + lastPointX, y1 + lastPointY, x2 + lastPointX,
y2 + lastPointY, x + lastPointX, y + lastPointY, draw);
}
private void curvetoCubicSmoothAbs(Path path, float x2, float y2, float x,
float y, boolean draw) {
curvetoCubicAbs(path, lastKnotX * 2f - lastPointX,
lastKnotY * 2f - lastPointY, x2, y2, x, y, draw);
}
private void curvetoCubicSmoothRel(Path path, float x2, float y2, float x,
float y, boolean draw) {
curvetoCubicSmoothAbs(path, x2 + lastPointX, y2 + lastPointY,
x + lastPointX, y + lastPointY, draw);
}
private float nextFloat(LinkedList<String> tokens) {
String token = tokens.removeFirst();
return Float.parseFloat(token);
}
private void calcTBLR(float x, float y) {
left = Math.min(left, x);
right = Math.max(right, x);
top = Math.min(top, y);
bottom = Math.max(bottom, y);
}
private float correctX(float x) {
float baseX = x - (left + right) / 2;
baseX = baseX * clientWidth / (right - left);
return baseX + centralX;
}
private float correctY(float y) {
float baseY = y - (top + bottom) / 2;
baseY = baseY * clientHeight / (bottom - top);
return baseY + centralY;
}
public static SvgPathParser getInstance() {
if (instance == null)
instance = new SvgPathParser();
return instance;
}
}