/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.draw.shapes;
import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.w3c.dom.Element;
import com.cburch.draw.model.AbstractCanvasObject;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeOption;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.util.UnmodifiableList;
public class SvgReader {
private SvgReader() { }
private static final Pattern PATH_REGEX = Pattern.compile("[a-zA-Z]|[-0-9.]+");
public static AbstractCanvasObject createShape(Element elt) {
String name = elt.getTagName();
AbstractCanvasObject ret;
if (name.equals("ellipse")) {
ret = createOval(elt);
} else if (name.equals("line")) {
ret = createLine(elt);
} else if (name.equals("path")) {
ret = createPath(elt);
} else if (name.equals("polyline")) {
ret = createPolyline(elt);
} else if (name.equals("polygon")) {
ret = createPolygon(elt);
} else if (name.equals("rect")) {
ret = createRectangle(elt);
} else if (name.equals("text")) {
ret = createText(elt);
} else {
return null;
}
List<Attribute<?>> attrs = ret.getAttributes();
if (attrs.contains(DrawAttr.PAINT_TYPE)) {
String stroke = elt.getAttribute("stroke");
String fill = elt.getAttribute("fill");
if (stroke.equals("") || stroke.equals("none")) {
ret.setValue(DrawAttr.PAINT_TYPE, DrawAttr.PAINT_FILL);
} else if (fill.equals("none")) {
ret.setValue(DrawAttr.PAINT_TYPE, DrawAttr.PAINT_STROKE);
} else {
ret.setValue(DrawAttr.PAINT_TYPE, DrawAttr.PAINT_STROKE_FILL);
}
}
attrs = ret.getAttributes(); // since changing paintType could change it
if (attrs.contains(DrawAttr.STROKE_WIDTH) && elt.hasAttribute("stroke-width")) {
Integer width = Integer.valueOf(elt.getAttribute("stroke-width"));
ret.setValue(DrawAttr.STROKE_WIDTH, width);
}
if (attrs.contains(DrawAttr.STROKE_COLOR)) {
String color = elt.getAttribute("stroke");
String opacity = elt.getAttribute("stroke-opacity");
if (!color.equals("none")) {
ret.setValue(DrawAttr.STROKE_COLOR, getColor(color, opacity));
}
}
if (attrs.contains(DrawAttr.FILL_COLOR)) {
String color = elt.getAttribute("fill");
if (color.equals("")) color = "#000000";
String opacity = elt.getAttribute("fill-opacity");
if (!color.equals("none")) {
ret.setValue(DrawAttr.FILL_COLOR, getColor(color, opacity));
}
}
return ret;
}
private static AbstractCanvasObject createRectangle(Element elt) {
int x = Integer.parseInt(elt.getAttribute("x"));
int y = Integer.parseInt(elt.getAttribute("y"));
int w = Integer.parseInt(elt.getAttribute("width"));
int h = Integer.parseInt(elt.getAttribute("height"));
if (elt.hasAttribute("rx")) {
AbstractCanvasObject ret = new RoundRectangle(x, y, w, h);
int rx = Integer.parseInt(elt.getAttribute("rx"));
ret.setValue(DrawAttr.CORNER_RADIUS, Integer.valueOf(rx));
return ret;
} else {
return new Rectangle(x, y, w, h);
}
}
private static AbstractCanvasObject createOval(Element elt) {
double cx = Double.parseDouble(elt.getAttribute("cx"));
double cy = Double.parseDouble(elt.getAttribute("cy"));
double rx = Double.parseDouble(elt.getAttribute("rx"));
double ry = Double.parseDouble(elt.getAttribute("ry"));
int x = (int) Math.round(cx - rx);
int y = (int) Math.round(cy - ry);
int w = (int) Math.round(rx * 2);
int h = (int) Math.round(ry * 2);
return new Oval(x, y, w, h);
}
private static AbstractCanvasObject createLine(Element elt) {
int x0 = Integer.parseInt(elt.getAttribute("x1"));
int y0 = Integer.parseInt(elt.getAttribute("y1"));
int x1 = Integer.parseInt(elt.getAttribute("x2"));
int y1 = Integer.parseInt(elt.getAttribute("y2"));
return new Line(x0, y0, x1, y1);
}
private static AbstractCanvasObject createPolygon(Element elt) {
return new Poly(true, parsePoints(elt.getAttribute("points")));
}
private static AbstractCanvasObject createPolyline(Element elt) {
return new Poly(false, parsePoints(elt.getAttribute("points")));
}
private static AbstractCanvasObject createText(Element elt) {
int x = Integer.parseInt(elt.getAttribute("x"));
int y = Integer.parseInt(elt.getAttribute("y"));
String text = elt.getTextContent();
Text ret = new Text(x, y, text);
String fontFamily = elt.getAttribute("font-family");
String fontStyle = elt.getAttribute("font-style");
String fontWeight = elt.getAttribute("font-weight");
String fontSize = elt.getAttribute("font-size");
int styleFlags = 0;
if (fontStyle.equals("italic")) styleFlags |= Font.ITALIC;
if (fontWeight.equals("bold")) styleFlags |= Font.BOLD;
int size = Integer.parseInt(fontSize);
ret.setValue(DrawAttr.FONT, new Font(fontFamily, styleFlags, size));
String alignStr = elt.getAttribute("text-anchor");
AttributeOption halign;
if (alignStr.equals("start")) {
halign = DrawAttr.ALIGN_LEFT;
} else if (alignStr.equals("end")) {
halign = DrawAttr.ALIGN_RIGHT;
} else {
halign = DrawAttr.ALIGN_CENTER;
}
ret.setValue(DrawAttr.ALIGNMENT, halign);
// fill color is handled after we return
return ret;
}
private static List<Location> parsePoints(String points) {
Pattern patt = Pattern.compile("[ ,\n\r\t]+");
String[] toks = patt.split(points);
Location[] ret = new Location[toks.length / 2];
for (int i = 0; i < ret.length; i++) {
int x = Integer.parseInt(toks[2 * i]);
int y = Integer.parseInt(toks[2 * i + 1]);
ret[i] = Location.create(x, y);
}
return UnmodifiableList.create(ret);
}
private static AbstractCanvasObject createPath(Element elt) {
Matcher patt = PATH_REGEX.matcher(elt.getAttribute("d"));
List<String> tokens = new ArrayList<String>();
int type = -1; // -1 error, 0 start, 1 curve, 2 polyline
while (patt.find()) {
String token = patt.group();
tokens.add(token);
if (Character.isLetter(token.charAt(0))) {
switch (token.charAt(0)) {
case 'M':
if (type == -1) type = 0;
else type = -1;
break;
case 'Q': case 'q':
if (type == 0) type = 1;
else type = -1;
break;
/* not supported
case 'L': case 'l':
case 'H': case 'h':
case 'V': case 'v':
if (type == 0 || type == 2) type = 2;
else type = -1;
break;
*/
default:
type = -1;
}
if (type == -1) {
throw new NumberFormatException("Unrecognized path command '" + token.charAt(0) + "'");
}
}
}
if (type == 1) {
if (tokens.size() == 8 && tokens.get(0).equals("M")
&& tokens.get(3).toUpperCase().equals("Q")) {
int x0 = Integer.parseInt(tokens.get(1));
int y0 = Integer.parseInt(tokens.get(2));
int x1 = Integer.parseInt(tokens.get(4));
int y1 = Integer.parseInt(tokens.get(5));
int x2 = Integer.parseInt(tokens.get(6));
int y2 = Integer.parseInt(tokens.get(7));
if (tokens.get(3).equals("q")) {
x1 += x0;
y1 += y0;
x2 += x0;
y2 += y0;
}
Location e0 = Location.create(x0, y0);
Location e1 = Location.create(x2, y2);
Location ct = Location.create(x1, y1);
return new Curve(e0, e1, ct);
} else {
throw new NumberFormatException("Unexpected format for curve");
}
} else {
throw new NumberFormatException("Unrecognized path");
}
}
private static Color getColor(String hue, String opacity) {
int r;
int g;
int b;
if (hue == null || hue.equals("")) {
r = 0;
g = 0;
b = 0;
} else {
r = Integer.parseInt(hue.substring(1, 3), 16);
g = Integer.parseInt(hue.substring(3, 5), 16);
b = Integer.parseInt(hue.substring(5, 7), 16);
}
int a;
if (opacity == null || opacity.equals("")) {
a = 255;
} else {
a = (int) Math.round(Double.parseDouble(opacity) * 255);
}
return new Color(r, g, b, a);
}
}