/*
* This file is part of LaTeXDraw.
* Copyright (c) 2005-2017 Arnaud BLOUIN
* LaTeXDraw is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later version.
* LaTeXDraw is distributed without any warranty; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package net.sf.latexdraw.parsers.svg.parsers;
import java.text.ParseException;
import net.sf.latexdraw.parsers.svg.path.SVGPathHandler;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegArc;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegClosePath;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegCurvetoCubic;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegCurvetoCubicSmooth;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegCurvetoQuadratic;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegCurvetoQuadraticSmooth;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegLineto;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegLinetoHorizontal;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegLinetoVertical;
import net.sf.latexdraw.parsers.svg.path.SVGPathSegMoveto;
/**
* Defines an SVGPath parser.
* @author Arnaud BLOUIN
* @since 0.1
*/
public class SVGPathParser extends SVGNumberParser {
/** The handler of the SVGPath. @since 0.1 */
protected SVGPathHandler handler;
/**
* The main constructor.
* @param code The path to parse.
* @param handler The handler of the path.
*/
public SVGPathParser(final String code, final SVGPathHandler handler) {
super(code);
this.handler = handler;
}
@Override
public void parse() throws ParseException {
setPosition(0);
skipWSP();
if(getChar()!='m' && getChar()!='M')// The first command must be a moveto command.
throw new ParseException("moveto command (m|M) expected.", getPosition());//$NON-NLS-1$
parseMoveto(getChar()=='m');
while(!isEOC()) {
skipWSP();
switch(getChar()) {
case EOC:
break;
case 'Z':
case 'z':
parseClosepath();
break;
case 'm':
parseMoveto(true);
break;
case 'M':
parseMoveto(false);
break;
case 'l':
parseLineto(true);
break;
case 'L':
parseLineto(false);
break;
case 'h':
parseHorizontalLineto(true);
break;
case 'H':
parseHorizontalLineto(false);
break;
case 'v':
parseVerticalLineto(true);
break;
case 'V':
parseVerticalLineto(false);
break;
case 'a':
parseEllipticalArcto(true);
break;
case 'A':
parseEllipticalArcto(false);
break;
case 'c':
parseCurveto(true);
break;
case 'C':
parseCurveto(false);
break;
case 'q':
parseQuadraticBezierCurveto(true);
break;
case 'Q':
parseQuadraticBezierCurveto(false);
break;
case 't':
parseShorthandQuadraticBezierCurveto(true);
break;
case 'T':
parseShorthandQuadraticBezierCurveto(false);
break;
case 's':
parseShorthandCurveto(true);
break;
case 'S':
parseShorthandCurveto(false);
break;
default:
throw new ParseException("Invalid token:" + getChar(), getPosition());//$NON-NLS-1$
}
}
}
/**
* Parses an SVGPath smooth curveto.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
* @since 0.1
*/
protected void parseShorthandCurveto(final boolean isRelative) throws ParseException {
double x;
double y;
double x2;
double y2;
nextChar();
skipWSP();
x2 = parseNumber(false);
skipWSPComma();
y2 = parseNumber(false);
skipWSPComma();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegCurvetoCubicSmooth(x, y, x2, y2, isRelative));
while(!isEOC() && isNumber(false)) {
x2 = parseNumber(false);
skipWSPComma();
y2 = parseNumber(false);
skipWSPComma();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegCurvetoCubicSmooth(x, y, x2, y2, isRelative));
}
}
/**
* Parses an SVGPath closepath.
*/
protected void parseClosepath() {
handler.onPathSeg(new SVGPathSegClosePath());
nextChar();
}
/**
* Parses an SVGPath horizontal lineto.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
*/
protected void parseHorizontalLineto(final boolean isRelative) throws ParseException {
double h;
nextChar();
skipWSP();
h = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegLinetoHorizontal(h, isRelative));
while(!isEOC() && isNumber(false)) {
h = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegLinetoHorizontal(h, isRelative));
}
}
/**
* Parses an SVGPath vertical lineto.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
*/
protected void parseVerticalLineto(final boolean isRelative) throws ParseException {
double v;
nextChar();
skipWSP();
v = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegLinetoVertical(v, isRelative));
while(!isEOC() && isNumber(false)) {
v = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegLinetoVertical(v, isRelative));
}
}
/**
* Parses an SVGPath arc.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
*/
protected void parseEllipticalArcto(final boolean isRelative) throws ParseException {
double x;
double y;
double rx;
double ry;
double angle;
boolean laf;
boolean sf;
nextChar();
skipWSP();
rx = parseNumber(true);
skipWSPComma();
ry = parseNumber(true);
skipWSPComma();
angle = parseNumber(false);
skipWSPComma();
laf = parseFlag();
skipWSPComma();
sf = parseFlag();
skipWSPComma();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegArc(x, y, rx, ry, angle, laf, sf, isRelative));
while(!isEOC() && isNumber(true)) {
rx = parseNumber(true);
skipWSPComma();
ry = parseNumber(true);
skipWSPComma();
angle = parseNumber(false);
skipWSPComma();
laf = parseFlag();
skipWSPComma();
sf = parseFlag();
skipWSPComma();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegArc(x, y, rx, ry, angle, laf, sf, isRelative));
}
}
/**
* Parses an SVGPath curveto.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
*/
protected void parseCurveto(final boolean isRelative) throws ParseException {
double x;
double y;
double x1;
double x2;
double y1;
double y2;
nextChar();
skipWSP();
x1 = parseNumber(false);
skipWSPComma();
y1 = parseNumber(false);
skipWSPComma();
x2 = parseNumber(false);
skipWSPComma();
y2 = parseNumber(false);
skipWSPComma();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegCurvetoCubic(x, y, x1, y1, x2, y2, isRelative));
while(!isEOC() && isNumber(false)) {
x1 = parseNumber(false);
skipWSPComma();
y1 = parseNumber(false);
skipWSPComma();
x2 = parseNumber(false);
skipWSPComma();
y2 = parseNumber(false);
skipWSPComma();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegCurvetoCubic(x, y, x1, y1, x2, y2, isRelative));
}
}
/**
* Parses an SVGPath quadratic curveto.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
*/
protected void parseQuadraticBezierCurveto(final boolean isRelative) throws ParseException {
double x;
double y;
double x1;
double y1;
nextChar();
skipWSP();
x1 = parseNumber(false);
skipWSPComma();
y1 = parseNumber(false);
skipWSPComma();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegCurvetoQuadratic(x, y, x1, y1, isRelative));
while(!isEOC() && isNumber(false)) {
x1 = parseNumber(false);
skipWSPComma();
y1 = parseNumber(false);
skipWSPComma();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegCurvetoQuadratic(x, y, x1, y1, isRelative));
}
}
/**
* Parses an SVGPath quadratic smooth curveto.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
*/
protected void parseShorthandQuadraticBezierCurveto(final boolean isRelative) throws ParseException {
double x;
double y;
nextChar();
skipWSP();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegCurvetoQuadraticSmooth(x, y, isRelative));
while(!isEOC() && isNumber(false)) {
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegLineto(x, y, isRelative));
}
}
/**
* Parses an SVGPath lineto.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
*/
protected void parseLineto(final boolean isRelative) throws ParseException {
double x;
double y;
nextChar();
skipWSP();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegLineto(x, y, isRelative));
while(!isEOC() && isNumber(false)) {
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegLineto(x, y, isRelative));
}
}
/**
* Parses an SVGPath moveto.
* @param isRelative True if segment is relative.
* @throws ParseException If a problem occurs.
*/
protected void parseMoveto(final boolean isRelative) throws ParseException {
double x;
double y;
nextChar();
skipWSP();
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegMoveto(x, y, isRelative));
while(!isEOC() && isNumber(false)) {
x = parseNumber(false);
skipWSPComma();
y = parseNumber(false);
skipWSPComma();
handler.onPathSeg(new SVGPathSegLineto(x, y, isRelative));
}
}
}