svg import improvement

This commit is contained in:
honfika@gmail.com
2015-12-18 15:34:02 +01:00
parent 1b1923eee1
commit 60a8fa8110

View File

@@ -225,6 +225,18 @@ public class ShapeImporter {
processSvgObject(shapeNum, shapes, childElement, m2, newStyle);
} else if ("path".equals(tagName)) {
processPath(shapeNum, shapes, childElement, m2, newStyle);
} else if ("circle".equals(tagName)) {
processCircle(shapeNum, shapes, childElement, m2, newStyle);
} else if ("ellipse".equals(tagName)) {
processEllipse(shapeNum, shapes, childElement, m2, newStyle);
} else if ("rect".equals(tagName)) {
processRect(shapeNum, shapes, childElement, m2, newStyle);
} else if ("line".equals(tagName)) {
processLine(shapeNum, shapes, childElement, m2, newStyle);
} else if ("polyline".equals(tagName)) {
processPolyline(shapeNum, shapes, childElement, m2, newStyle);
} else if ("polygon".equals(tagName)) {
processPolygon(shapeNum, shapes, childElement, m2, newStyle);
} else {
showWarning(tagName + "tagNotSupported", "The SVG tag '" + tagName + "' is not supported.");
}
@@ -567,9 +579,9 @@ public class ShapeImporter {
case 'A':
double rx = pathReader.readDouble();
double ry = pathReader.readDouble();
double xRot = pathReader.readDouble();
int largeFlag = (int) pathReader.readDouble();
int sweepFlag = (int) pathReader.readDouble();
double fi = pathReader.readDouble() * Math.PI / 180;
boolean largeFlag = (int) pathReader.readDouble() != 0;
boolean sweepFlag = (int) pathReader.readDouble() != 0;
x = pathReader.readDouble();
y = pathReader.readDouble();
@@ -578,9 +590,36 @@ public class ShapeImporter {
y += y0;
}
double x1 = x0;
double y1 = y0;
double x2 = x;
double y2 = y;
double d1 = (x1 - x2) / 2;
double d2 = (y1 - y2) / 2;
double x1Comma = Math.cos(fi) * d1 + Math.sin(fi) * d2;
double y1Comma = -Math.sin(fi) * d1 + Math.cos(fi) * d2;
double c = Math.sqrt((rx * rx * ry * ry - rx * rx * y1Comma * y1Comma - ry * ry * x1Comma * x1Comma) / (rx * rx * y1Comma * y1Comma + ry * ry * x1Comma * x1Comma));
double cxComma = c * rx * y1Comma / ry;
double cyComma = c * -ry * x1Comma / rx;
if (largeFlag == sweepFlag) {
cxComma = -cxComma;
cyComma = -cyComma;
}
double cx = Math.cos(fi) * cxComma - Math.sin(fi) * cyComma + (x1 + x2) / 2;
double cy = Math.sin(fi) * cxComma + Math.cos(fi) * cyComma + (y1 + y2) / 2;
// todo: draw arc, now draw only a line
PathCommand sera = new PathCommand();
sera.command = 'L';
sera.params = new double[]{cx, cy};
pathCommands.add(sera);
sera = new PathCommand();
sera.command = 'L';
sera.params = new double[]{x, y};
pathCommands.add(sera);
@@ -605,6 +644,301 @@ public class ShapeImporter {
processCommands(shapeNum, shapes, pathCommands, transform, style);
}
private void processCircle(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
String attr = childElement.getAttribute("cx");
double cx = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("cy");
double cy = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("r");
double r = attr.length() > 0 ? Double.parseDouble(attr) : 0;
double sqrt2RHalf = Math.sqrt(2) * r / 2;
double sqrt2Minus1R = (Math.sqrt(2) - 1) * r;
List<PathCommand> pathCommands = new ArrayList<>();
PathCommand scr = new PathCommand();
scr.command = 'M';
scr.params = new double[]{cx + r, cy};
pathCommands.add(scr);
double[] points = new double[]{
cx + r, cy - sqrt2Minus1R,
cx + sqrt2RHalf, cy - sqrt2RHalf,
cx + sqrt2Minus1R, cy - r,
cx, cy - r,
cx - sqrt2Minus1R, cy - r,
cx - sqrt2RHalf, cy - sqrt2RHalf,
cx - r, cy - sqrt2Minus1R,
cx - r, cy,
cx - r, cy + sqrt2Minus1R,
cx - sqrt2RHalf, cy + sqrt2RHalf,
cx - sqrt2Minus1R, cy + r,
cx, cy + r,
cx + sqrt2Minus1R, cy + r,
cx + sqrt2RHalf, cy + sqrt2RHalf,
cx + r, cy + sqrt2Minus1R,
cx + r, cy};
for (int i = 0; i < points.length; i += 4) {
PathCommand cer = new PathCommand();
cer.command = 'Q';
cer.params = new double[]{points[i], points[i + 1], points[i + 2], points[i + 3]};
pathCommands.add(cer);
}
processCommands(shapeNum, shapes, pathCommands, transform, style);
}
private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
String attr = childElement.getAttribute("cx");
double cx = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("cy");
double cy = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("rx");
double rx = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("ry");
double ry = attr.length() > 0 ? Double.parseDouble(attr) : 0;
double sqrt2RXHalf = Math.sqrt(2) * rx / 2;
double sqrt2Minus1RX = (Math.sqrt(2) - 1) * rx;
double sqrt2RYHalf = Math.sqrt(2) * ry / 2;
double sqrt2Minus1RY = (Math.sqrt(2) - 1) * ry;
List<PathCommand> pathCommands = new ArrayList<>();
PathCommand scr = new PathCommand();
scr.command = 'M';
scr.params = new double[]{cx + rx, cy};
pathCommands.add(scr);
double[] points = new double[]{
cx + rx, cy - sqrt2Minus1RY,
cx + sqrt2RXHalf, cy - sqrt2RYHalf,
cx + sqrt2Minus1RX, cy - ry,
cx, cy - ry,
cx - sqrt2Minus1RX, cy - ry,
cx - sqrt2RXHalf, cy - sqrt2RYHalf,
cx - rx, cy - sqrt2Minus1RY,
cx - rx, cy,
cx - rx, cy + sqrt2Minus1RY,
cx - sqrt2RXHalf, cy + sqrt2RYHalf,
cx - sqrt2Minus1RX, cy + ry,
cx, cy + ry,
cx + sqrt2Minus1RX, cy + ry,
cx + sqrt2RXHalf, cy + sqrt2RYHalf,
cx + rx, cy + sqrt2Minus1RY,
cx + rx, cy};
for (int i = 0; i < points.length; i += 4) {
PathCommand cer = new PathCommand();
cer.command = 'Q';
cer.params = new double[]{points[i], points[i + 1], points[i + 2], points[i + 3]};
pathCommands.add(cer);
}
processCommands(shapeNum, shapes, pathCommands, transform, style);
}
private void processRect(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
String attr = childElement.getAttribute("x");
double x = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("y");
double y = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("width");
double width = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("height");
double height = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("rx");
double rx = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("ry");
double ry = attr.length() > 0 ? Double.parseDouble(attr) : 0;
if (rx == 0 && ry != 0) {
rx = ry;
} else if (rx != 0 && ry == 0) {
ry = rx;
}
if (rx > width / 2) {
rx = width / 2;
}
if (ry > height / 2) {
ry = height / 2;
}
List<PathCommand> pathCommands = new ArrayList<>();
if (rx > 0 || ry > 0) {
PathCommand scr = new PathCommand();
scr.command = 'M';
scr.params = new double[]{x + width, y + ry};
pathCommands.add(scr);
double sqrt2RXHalf = Math.sqrt(2) * rx / 2;
double sqrt2Minus1RX = (Math.sqrt(2) - 1) * rx;
double sqrt2RYHalf = Math.sqrt(2) * ry / 2;
double sqrt2Minus1RY = (Math.sqrt(2) - 1) * ry;
double[] points = new double[]{
x + width, y + ry - sqrt2Minus1RY,
x + width - rx + sqrt2RXHalf, y + ry - sqrt2RYHalf,
x + width - rx + sqrt2Minus1RX, y,
x + width - rx, y,
x + rx, y,
x + rx - sqrt2Minus1RX, y,
x + rx - sqrt2RXHalf, y + ry - sqrt2RYHalf,
x, y + ry - sqrt2Minus1RY,
x, y + ry,
x, y + height - ry,
x, y + height - ry + sqrt2Minus1RY,
x + rx - sqrt2RXHalf, y + height - ry + sqrt2RYHalf,
x + rx - sqrt2Minus1RX, y + height,
x + rx, y + height,
x + width - rx, y + height,
x + width - rx + sqrt2Minus1RX, y + height,
x + width - rx + sqrt2RXHalf, y + height - ry + sqrt2RYHalf,
x + width, y + height - ry + sqrt2Minus1RY,
x + width, y + height - ry,
x + width, y + ry};
for (int i = 0; i < points.length;) {
if (i % 10 == 8) {
PathCommand cer = new PathCommand();
cer.command = 'L';
cer.params = new double[]{points[i], points[i + 1]};
pathCommands.add(cer);
i += 2;
} else {
PathCommand cer = new PathCommand();
cer.command = 'Q';
cer.params = new double[]{points[i], points[i + 1], points[i + 2], points[i + 3]};
pathCommands.add(cer);
i += 4;
}
}
} else {
PathCommand scr = new PathCommand();
scr.command = 'M';
scr.params = new double[]{x, y};
pathCommands.add(scr);
double[] points = new double[]{
x + width, y,
x + width, y + height,
x, y + height,
x, y};
for (int i = 0; i < points.length; i += 2) {
PathCommand cer = new PathCommand();
cer.command = 'L';
cer.params = new double[]{points[i], points[i + 1]};
pathCommands.add(cer);
}
}
processCommands(shapeNum, shapes, pathCommands, transform, style);
}
private void processLine(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
String attr = childElement.getAttribute("x1");
double x1 = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("y1");
double y1 = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("x2");
double x2 = attr.length() > 0 ? Double.parseDouble(attr) : 0;
attr = childElement.getAttribute("y2");
double y2 = attr.length() > 0 ? Double.parseDouble(attr) : 0;
List<PathCommand> pathCommands = new ArrayList<>();
PathCommand scr = new PathCommand();
scr.command = 'M';
scr.params = new double[]{x1, y1};
pathCommands.add(scr);
PathCommand cer = new PathCommand();
cer.command = 'L';
cer.params = new double[]{x2, y2};
pathCommands.add(cer);
processCommands(shapeNum, shapes, pathCommands, transform, style);
}
private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
processPolyline(shapeNum, shapes, childElement, transform, style, false);
}
private void processPolygon(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
processPolyline(shapeNum, shapes, childElement, transform, style, true);
}
private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean close) {
String data = childElement.getAttribute("points");
char command = 'M';
Point startPoint = new Point(0, 0);
double x0 = 0;
double y0 = 0;
List<PathCommand> pathCommands = new ArrayList<>();
SvgPathReader pathReader = new SvgPathReader(data);
while (pathReader.hasNext()) {
double x = x0;
double y = y0;
Point p = null;
switch (command) {
case 'M':
PathCommand scr = new PathCommand();
scr.command = 'M';
x = pathReader.readDouble();
y = pathReader.readDouble();
scr.params = new double[]{x, y};
pathCommands.add(scr);
startPoint = new Point(x, y);
break;
case 'L':
PathCommand serl = new PathCommand();
serl.command = 'L';
x = pathReader.readDouble();
y = pathReader.readDouble();
serl.params = new double[]{x, y};
pathCommands.add(serl);
break;
}
x0 = x;
y0 = y;
command = 'L';
}
if (close) {
PathCommand serz = new PathCommand();
serz.command = 'Z';
serz.params = new double[]{startPoint.x, startPoint.y};
pathCommands.add(serz);
}
processCommands(shapeNum, shapes, pathCommands, transform, style);
}
private StyleChangeRecord getStyleChangeRecord(int shapeNum, SvgStyle style) {
StyleChangeRecord scr = new StyleChangeRecord();
@@ -674,6 +1008,300 @@ public class ShapeImporter {
switch (rgbStr) {
case "none":
return TRANSPARENT;
case "aliceblue":
return new Color(240, 248, 255);
case "antiquewhite":
return new Color(250, 235, 215);
case "aqua":
return new Color(0, 255, 255);
case "aquamarine":
return new Color(127, 255, 212);
case "azure":
return new Color(240, 255, 255);
case "beige":
return new Color(245, 245, 220);
case "bisque":
return new Color(255, 228, 196);
case "black":
return new Color(0, 0, 0);
case "blanchedalmond":
return new Color(255, 235, 205);
case "blue":
return new Color(0, 0, 255);
case "blueviolet":
return new Color(138, 43, 226);
case "brown":
return new Color(165, 42, 42);
case "burlywood":
return new Color(222, 184, 135);
case "cadetblue":
return new Color(95, 158, 160);
case "chartreuse":
return new Color(127, 255, 0);
case "chocolate":
return new Color(210, 105, 30);
case "coral":
return new Color(255, 127, 80);
case "cornflowerblue":
return new Color(100, 149, 237);
case "cornsilk":
return new Color(255, 248, 220);
case "crimson":
return new Color(220, 20, 60);
case "cyan":
return new Color(0, 255, 255);
case "darkblue":
return new Color(0, 0, 139);
case "darkcyan":
return new Color(0, 139, 139);
case "darkgoldenrod":
return new Color(184, 134, 11);
case "darkgray":
return new Color(169, 169, 169);
case "darkgreen":
return new Color(0, 100, 0);
case "darkgrey":
return new Color(169, 169, 169);
case "darkkhaki":
return new Color(189, 183, 107);
case "darkmagenta":
return new Color(139, 0, 139);
case "darkolivegreen":
return new Color(85, 107, 47);
case "darkorange":
return new Color(255, 140, 0);
case "darkorchid":
return new Color(153, 50, 204);
case "darkred":
return new Color(139, 0, 0);
case "darksalmon":
return new Color(233, 150, 122);
case "darkseagreen":
return new Color(143, 188, 143);
case "darkslateblue":
return new Color(72, 61, 139);
case "darkslategray":
return new Color(47, 79, 79);
case "darkslategrey":
return new Color(47, 79, 79);
case "darkturquoise":
return new Color(0, 206, 209);
case "darkviolet":
return new Color(148, 0, 211);
case "deeppink":
return new Color(255, 20, 147);
case "deepskyblue":
return new Color(0, 191, 255);
case "dimgray":
return new Color(105, 105, 105);
case "dimgrey":
return new Color(105, 105, 105);
case "dodgerblue":
return new Color(30, 144, 255);
case "firebrick":
return new Color(178, 34, 34);
case "floralwhite":
return new Color(255, 250, 240);
case "forestgreen":
return new Color(34, 139, 34);
case "fuchsia":
return new Color(255, 0, 255);
case "gainsboro":
return new Color(220, 220, 220);
case "ghostwhite":
return new Color(248, 248, 255);
case "gold":
return new Color(255, 215, 0);
case "goldenrod":
return new Color(218, 165, 32);
case "gray":
return new Color(128, 128, 128);
case "grey":
return new Color(128, 128, 128);
case "green":
return new Color(0, 128, 0);
case "greenyellow":
return new Color(173, 255, 47);
case "honeydew":
return new Color(240, 255, 240);
case "hotpink":
return new Color(255, 105, 180);
case "indianred":
return new Color(205, 92, 92);
case "indigo":
return new Color(75, 0, 130);
case "ivory":
return new Color(255, 255, 240);
case "khaki":
return new Color(240, 230, 140);
case "lavender":
return new Color(230, 230, 250);
case "lavenderblush":
return new Color(255, 240, 245);
case "lawngreen":
return new Color(124, 252, 0);
case "lemonchiffon":
return new Color(255, 250, 205);
case "lightblue":
return new Color(173, 216, 230);
case "lightcoral":
return new Color(240, 128, 128);
case "lightcyan":
return new Color(224, 255, 255);
case "lightgoldenrodyellow":
return new Color(250, 250, 210);
case "lightgray":
return new Color(211, 211, 211);
case "lightgreen":
return new Color(144, 238, 144);
case "lightgrey":
return new Color(211, 211, 211);
case "lightpink":
return new Color(255, 182, 193);
case "lightsalmon":
return new Color(255, 160, 122);
case "lightseagreen":
return new Color(32, 178, 170);
case "lightskyblue":
return new Color(135, 206, 250);
case "lightslategray":
return new Color(119, 136, 153);
case "lightslategrey":
return new Color(119, 136, 153);
case "lightsteelblue":
return new Color(176, 196, 222);
case "lightyellow":
return new Color(255, 255, 224);
case "lime":
return new Color(0, 255, 0);
case "limegreen":
return new Color(50, 205, 50);
case "linen":
return new Color(250, 240, 230);
case "magenta":
return new Color(255, 0, 255);
case "maroon":
return new Color(128, 0, 0);
case "mediumaquamarine":
return new Color(102, 205, 170);
case "mediumblue":
return new Color(0, 0, 205);
case "mediumorchid":
return new Color(186, 85, 211);
case "mediumpurple":
return new Color(147, 112, 219);
case "mediumseagreen":
return new Color(60, 179, 113);
case "mediumslateblue":
return new Color(123, 104, 238);
case "mediumspringgreen":
return new Color(0, 250, 154);
case "mediumturquoise":
return new Color(72, 209, 204);
case "mediumvioletred":
return new Color(199, 21, 133);
case "midnightblue":
return new Color(25, 25, 112);
case "mintcream":
return new Color(245, 255, 250);
case "mistyrose":
return new Color(255, 228, 225);
case "moccasin":
return new Color(255, 228, 181);
case "navajowhite":
return new Color(255, 222, 173);
case "navy":
return new Color(0, 0, 128);
case "oldlace":
return new Color(253, 245, 230);
case "olive":
return new Color(128, 128, 0);
case "olivedrab":
return new Color(107, 142, 35);
case "orange":
return new Color(255, 165, 0);
case "orangered":
return new Color(255, 69, 0);
case "orchid":
return new Color(218, 112, 214);
case "palegoldenrod":
return new Color(238, 232, 170);
case "palegreen":
return new Color(152, 251, 152);
case "paleturquoise":
return new Color(175, 238, 238);
case "palevioletred":
return new Color(219, 112, 147);
case "papayawhip":
return new Color(255, 239, 213);
case "peachpuff":
return new Color(255, 218, 185);
case "peru":
return new Color(205, 133, 63);
case "pink":
return new Color(255, 192, 203);
case "plum":
return new Color(221, 160, 221);
case "powderblue":
return new Color(176, 224, 230);
case "purple":
return new Color(128, 0, 128);
case "red":
return new Color(255, 0, 0);
case "rosybrown":
return new Color(188, 143, 143);
case "royalblue":
return new Color(65, 105, 225);
case "saddlebrown":
return new Color(139, 69, 19);
case "salmon":
return new Color(250, 128, 114);
case "sandybrown":
return new Color(244, 164, 96);
case "seagreen":
return new Color(46, 139, 87);
case "seashell":
return new Color(255, 245, 238);
case "sienna":
return new Color(160, 82, 45);
case "silver":
return new Color(192, 192, 192);
case "skyblue":
return new Color(135, 206, 235);
case "slateblue":
return new Color(106, 90, 205);
case "slategray":
return new Color(112, 128, 144);
case "slategrey":
return new Color(112, 128, 144);
case "snow":
return new Color(255, 250, 250);
case "springgreen":
return new Color(0, 255, 127);
case "steelblue":
return new Color(70, 130, 180);
case "tan":
return new Color(210, 180, 140);
case "teal":
return new Color(0, 128, 128);
case "thistle":
return new Color(216, 191, 216);
case "tomato":
return new Color(255, 99, 71);
case "turquoise":
return new Color(64, 224, 208);
case "violet":
return new Color(238, 130, 238);
case "wheat":
return new Color(245, 222, 179);
case "white":
return new Color(255, 255, 255);
case "whitesmoke":
return new Color(245, 245, 245);
case "yellow":
return new Color(255, 255, 0);
case "yellowgreen":
return new Color(154, 205, 50);
}
if (rgbStr.startsWith("#")) {
@@ -684,10 +1312,34 @@ public class ShapeImporter {
int i = Integer.parseInt(s, 16);
return new Color(i, false);
} else if (rgbStr.startsWith("rgb")) {
rgbStr = rgbStr.substring(3).trim();
if (rgbStr.startsWith("(") && rgbStr.endsWith(")")) {
rgbStr = rgbStr.substring(1, rgbStr.length() - 1);
String[] args = rgbStr.split(",");
if (args.length == 3) {
String a0 = args[0].trim();
String a1 = args[1].trim();
String a2 = args[2].trim();
if (a0.endsWith("%") && a1.endsWith("%") && a2.endsWith("%")) {
int r = (int) Math.round(Integer.parseInt(a0.substring(0, a0.length() - 1)) * 255.0 / 100);
int g = (int) Math.round(Integer.parseInt(a1.substring(0, a1.length() - 1)) * 255.0 / 100);
int b = (int) Math.round(Integer.parseInt(a2.substring(0, a2.length() - 1)) * 255.0 / 100);
return new Color(r, g, b);
} else {
int r = Integer.parseInt(a0);
int g = Integer.parseInt(a1);
int b = Integer.parseInt(a2);
return new Color(r, g, b);
}
}
}
} else {
showWarning("fillNotSupported", "Only solid fills are supported. Random color assigned.");
return new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
}
return null;
}
class PathCommand {