mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-29 10:24:38 +00:00
svg import improved
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.exporters.commonshape;
|
||||
|
||||
import com.jpexs.decompiler.flash.importers.SvgPathReader;
|
||||
import com.jpexs.decompiler.flash.types.MATRIX;
|
||||
import java.awt.geom.AffineTransform;
|
||||
|
||||
@@ -241,45 +240,107 @@ public final class Matrix implements Cloneable {
|
||||
+ rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")";
|
||||
}
|
||||
|
||||
public static Matrix parseSvgMatrix(String matrixStr, double translateDivisor, double unitDivisor) {
|
||||
matrixStr = matrixStr.trim();
|
||||
if (matrixStr.startsWith("matrix")) {
|
||||
matrixStr = matrixStr.substring(6).trim();
|
||||
if (matrixStr.startsWith("(") && matrixStr.endsWith(")")) {
|
||||
matrixStr = matrixStr.substring(1, matrixStr.length() - 1);
|
||||
SvgPathReader reader = new SvgPathReader(matrixStr);
|
||||
double scaleX = reader.readDouble() * unitDivisor;
|
||||
double rotateSkew0 = reader.readDouble() * unitDivisor;
|
||||
double rotateSkew1 = reader.readDouble() * unitDivisor;
|
||||
double scaleY = reader.readDouble() * unitDivisor;
|
||||
double translateX = reader.readDouble() * translateDivisor;
|
||||
double translateY = reader.readDouble() * translateDivisor;
|
||||
Matrix result = new Matrix();
|
||||
result.translateX = translateX;
|
||||
result.translateY = translateY;
|
||||
result.rotateSkew0 = rotateSkew0;
|
||||
result.rotateSkew1 = rotateSkew1;
|
||||
result.scaleX = scaleX;
|
||||
result.scaleY = scaleY;
|
||||
return result;
|
||||
}
|
||||
} else if (matrixStr.startsWith("translate")) {
|
||||
matrixStr = matrixStr.substring(9).trim();
|
||||
if (matrixStr.startsWith("(") && matrixStr.endsWith(")")) {
|
||||
matrixStr = matrixStr.substring(1, matrixStr.length() - 1);
|
||||
SvgPathReader reader = new SvgPathReader(matrixStr);
|
||||
double translateX = reader.readDouble() * translateDivisor;
|
||||
double translateY = reader.readDouble() * translateDivisor;
|
||||
Matrix result = new Matrix();
|
||||
result.translateX = translateX;
|
||||
result.translateY = translateY;
|
||||
result.scaleX = unitDivisor;
|
||||
result.scaleY = unitDivisor;
|
||||
return result;
|
||||
public static Matrix parseSvgMatrix(String transformStr, double translateDivisor, double unitDivisor) {
|
||||
Matrix ret = new Matrix();
|
||||
while (transformStr.length() > 0) {
|
||||
String funcName = transformStr.split("\\(")[0];
|
||||
transformStr = transformStr.substring(funcName.length() + 1);
|
||||
String params = transformStr.split("\\)")[0];
|
||||
transformStr = transformStr.substring(params.length() + 1);
|
||||
String[] args = params.split(",");
|
||||
funcName = funcName.trim();
|
||||
switch (funcName) {
|
||||
case "matrix":
|
||||
if (args.length == 6) {
|
||||
double scaleX = Double.parseDouble(args[0].trim()) * unitDivisor;
|
||||
double rotateSkew0 = Double.parseDouble(args[1].trim()) * unitDivisor;
|
||||
double rotateSkew1 = Double.parseDouble(args[2].trim()) * unitDivisor;
|
||||
double scaleY = Double.parseDouble(args[3].trim()) * unitDivisor;
|
||||
double translateX = Double.parseDouble(args[4].trim()) * translateDivisor;
|
||||
double translateY = Double.parseDouble(args[5].trim()) * translateDivisor;
|
||||
Matrix result = new Matrix();
|
||||
result.translateX = translateX;
|
||||
result.translateY = translateY;
|
||||
result.rotateSkew0 = rotateSkew0;
|
||||
result.rotateSkew1 = rotateSkew1;
|
||||
result.scaleX = scaleX;
|
||||
result.scaleY = scaleY;
|
||||
ret = ret.concatenate(result);
|
||||
}
|
||||
break;
|
||||
case "translate":
|
||||
if (args.length == 1 || args.length == 2) {
|
||||
double translateX = Double.parseDouble(args[0].trim()) * translateDivisor;
|
||||
double translateY = 0;
|
||||
if (args.length == 2) {
|
||||
translateY = Double.parseDouble(args[1].trim()) * translateDivisor;
|
||||
}
|
||||
|
||||
Matrix result = new Matrix();
|
||||
result.translateX = translateX;
|
||||
result.translateY = translateY;
|
||||
result.scaleX = unitDivisor;
|
||||
result.scaleY = unitDivisor;
|
||||
ret = ret.concatenate(result);
|
||||
}
|
||||
break;
|
||||
case "scale":
|
||||
if (args.length == 1 || args.length == 2) {
|
||||
double scaleX = Double.parseDouble(args[0].trim());
|
||||
double scaleY = scaleX;
|
||||
if (args.length == 2) {
|
||||
scaleY = Double.parseDouble(args[1].trim());
|
||||
}
|
||||
|
||||
Matrix result = new Matrix();
|
||||
result.scaleX = scaleX;
|
||||
result.scaleY = scaleY;
|
||||
ret = ret.concatenate(result);
|
||||
}
|
||||
break;
|
||||
case "skewX":
|
||||
if (args.length == 1) {
|
||||
double angle = Double.parseDouble(args[0].trim()) * Math.PI / 180;
|
||||
|
||||
Matrix result = new Matrix();
|
||||
result.rotateSkew1 = Math.tan(angle);
|
||||
ret = ret.concatenate(result);
|
||||
}
|
||||
break;
|
||||
case "skewY":
|
||||
if (args.length == 1) {
|
||||
double angle = Double.parseDouble(args[0].trim()) * Math.PI / 180;
|
||||
|
||||
Matrix result = new Matrix();
|
||||
result.rotateSkew0 = Math.tan(angle);
|
||||
ret = ret.concatenate(result);
|
||||
}
|
||||
break;
|
||||
case "rotate":
|
||||
if (args.length == 1 || args.length == 3) {
|
||||
double rotateAngle = Double.parseDouble(args[0].trim());
|
||||
double tx = 0;
|
||||
double ty = 0;
|
||||
if (args.length > 1) {
|
||||
tx = Double.parseDouble(args[1].trim());
|
||||
ty = Double.parseDouble(args[2].trim());
|
||||
}
|
||||
|
||||
double angleRad = -rotateAngle * Math.PI / 180;
|
||||
Matrix result = new Matrix();
|
||||
result.rotateSkew0 = -Math.sin(angleRad);
|
||||
result.rotateSkew1 = Math.sin(angleRad);
|
||||
result.scaleX = Math.cos(angleRad);
|
||||
result.scaleY = Math.cos(angleRad);
|
||||
result = result.preConcatenate(getTranslateInstance(tx * translateDivisor, ty * translateDivisor))
|
||||
.concatenate(getTranslateInstance(-tx * translateDivisor, -ty * translateDivisor));
|
||||
ret = ret.concatenate(result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private double roundPixels400(double pixels) {
|
||||
|
||||
@@ -54,7 +54,10 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
@@ -72,7 +75,11 @@ import org.xml.sax.SAXException;
|
||||
*/
|
||||
public class ShapeImporter {
|
||||
|
||||
private boolean cubicWarning = false;
|
||||
private final Set<String> shownWarnings = new HashSet<>();
|
||||
|
||||
private static final Color TRANSPARENT = new Color(0, true);
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
public Tag importImage(ShapeTag st, byte[] newData) throws IOException {
|
||||
return importImage(st, newData, 0, true);
|
||||
@@ -212,58 +219,56 @@ public class ShapeImporter {
|
||||
Element childElement = (Element) childNode;
|
||||
String tagName = childElement.getTagName();
|
||||
SvgStyle newStyle = style.apply(childElement);
|
||||
Matrix m = Matrix.parseSvgMatrix(childElement.getAttribute("transform"), SWF.unitDivisor, 1);
|
||||
Matrix m2 = m == null ? transform : m.concatenate(transform);
|
||||
if ("g".equals(tagName)) {
|
||||
Matrix m = Matrix.parseSvgMatrix(childElement.getAttribute("transform"), SWF.unitDivisor, 1);
|
||||
Matrix m2 = m == null ? transform : m.concatenate(transform);
|
||||
processSvgObject(shapeNum, shapes, childElement, m2, newStyle);
|
||||
} else if ("path".equals(tagName)) {
|
||||
processPath(shapeNum, shapes, childElement, transform, newStyle);
|
||||
processPath(shapeNum, shapes, childElement, m2, newStyle);
|
||||
} else {
|
||||
showWarning(tagName + "tagNotSupported", "The SVG tag '" + tagName + "' is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processPath(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
|
||||
String data = childElement.getAttribute("d");
|
||||
|
||||
char command = 0;
|
||||
private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List<PathCommand> commands, Matrix transform, SvgStyle style) {
|
||||
Point prevPoint = new Point(0, 0);
|
||||
Point startPoint = prevPoint;
|
||||
Point prevCControlPoint = null;
|
||||
double x0 = 0;
|
||||
double y0 = 0;
|
||||
|
||||
boolean newShape = true;
|
||||
SvgPathReader pathReader = new SvgPathReader(data);
|
||||
while (pathReader.hasNext()) {
|
||||
char newCommand;
|
||||
if ((newCommand = pathReader.readCommand()) != 0) {
|
||||
command = newCommand;
|
||||
}
|
||||
StyleChangeRecord scrStyle = getStyleChangeRecord(shapeNum, style);
|
||||
int fillStyle = scrStyle.fillStyle1;
|
||||
int lineStyle = scrStyle.lineStyle;
|
||||
scrStyle.stateFillStyle0 = true;
|
||||
scrStyle.stateFillStyle1 = true;
|
||||
scrStyle.stateLineStyle = true;
|
||||
scrStyle.fillStyle0 = 0;
|
||||
scrStyle.fillStyle1 = 0;
|
||||
scrStyle.lineStyle = 0;
|
||||
shapes.shapeRecords.add(scrStyle);
|
||||
|
||||
boolean isRelative = Character.isLowerCase(command);
|
||||
for (PathCommand command : commands) {
|
||||
|
||||
double x = x0;
|
||||
double y = y0;
|
||||
|
||||
Point p = null;
|
||||
char cmd = Character.toUpperCase(command);
|
||||
char cmd = Character.toUpperCase(command.command);
|
||||
switch (cmd) {
|
||||
case 'M':
|
||||
StyleChangeRecord scr;
|
||||
if (newShape) {
|
||||
newShape = false;
|
||||
scr = getStyleChangeRecord(shapeNum, style);
|
||||
} else {
|
||||
scr = new StyleChangeRecord();
|
||||
StyleChangeRecord scr = new StyleChangeRecord();
|
||||
if (fillStyle != 0) {
|
||||
scr.stateFillStyle1 = true;
|
||||
scr.fillStyle1 = fillStyle;
|
||||
}
|
||||
if (lineStyle != 0) {
|
||||
scr.lineStyle = lineStyle;
|
||||
scr.stateLineStyle = true;
|
||||
}
|
||||
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
x = command.params[0];
|
||||
y = command.params[1];
|
||||
|
||||
p = transform.transform(x, y);
|
||||
scr.moveDeltaX = (int) Math.round(p.x);
|
||||
@@ -287,12 +292,8 @@ public class ShapeImporter {
|
||||
break;
|
||||
case 'L':
|
||||
StraightEdgeRecord serl = new StraightEdgeRecord();
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
x = command.params[0];
|
||||
y = command.params[1];
|
||||
|
||||
p = transform.transform(x, y);
|
||||
serl.deltaX = (int) Math.round(p.x - prevPoint.x);
|
||||
@@ -300,14 +301,12 @@ public class ShapeImporter {
|
||||
prevPoint = p;
|
||||
System.out.println("L" + serl.deltaX + "," + serl.deltaY);
|
||||
serl.generalLineFlag = true;
|
||||
serl.simplify();
|
||||
shapes.shapeRecords.add(serl);
|
||||
break;
|
||||
case 'H':
|
||||
StraightEdgeRecord serh = new StraightEdgeRecord();
|
||||
x = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
}
|
||||
x = command.params[0];
|
||||
|
||||
p = transform.transform(x, y);
|
||||
serh.deltaX = (int) Math.round(p.x - prevPoint.x);
|
||||
@@ -317,38 +316,27 @@ public class ShapeImporter {
|
||||
break;
|
||||
case 'V':
|
||||
StraightEdgeRecord serv = new StraightEdgeRecord();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
y += y0;
|
||||
}
|
||||
y = command.params[0];
|
||||
|
||||
p = transform.transform(x, y);
|
||||
serv.deltaY = (int) Math.round(p.x - prevPoint.x);
|
||||
serv.deltaY = (int) Math.round(p.y - prevPoint.y);
|
||||
prevPoint = p;
|
||||
System.out.println("V" + serv.deltaX);
|
||||
System.out.println("V" + serv.deltaY);
|
||||
serv.vertLineFlag = true;
|
||||
shapes.shapeRecords.add(serv);
|
||||
break;
|
||||
case 'Q':
|
||||
CurvedEdgeRecord cer = new CurvedEdgeRecord();
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
x = command.params[0];
|
||||
y = command.params[1];
|
||||
|
||||
p = transform.transform(x, y);
|
||||
cer.controlDeltaX = (int) Math.round(p.x - prevPoint.x);
|
||||
cer.controlDeltaY = (int) Math.round(p.y - prevPoint.y);
|
||||
prevPoint = p;
|
||||
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
x = command.params[2];
|
||||
y = command.params[3];
|
||||
|
||||
p = transform.transform(x, y);
|
||||
cer.anchorDeltaX = (int) Math.round(p.x - prevPoint.x);
|
||||
@@ -358,49 +346,24 @@ public class ShapeImporter {
|
||||
shapes.shapeRecords.add(cer);
|
||||
break;
|
||||
case 'C':
|
||||
case 'S':
|
||||
if (!cubicWarning) {
|
||||
cubicWarning = true;
|
||||
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Cubic curves are not supported by Flash.");
|
||||
}
|
||||
showWarning("cubicCurvesNotSupported", "Cubic curves are not supported by Flash.");
|
||||
|
||||
// create at least something...
|
||||
Point pStart = prevPoint;
|
||||
Point pControl1;
|
||||
|
||||
if (cmd == 'C') {
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
x = command.params[0];
|
||||
y = command.params[1];
|
||||
|
||||
pControl1 = transform.transform(x, y);
|
||||
} else {
|
||||
if (prevCControlPoint != null) {
|
||||
pControl1 = new Point(2 * pStart.x - prevCControlPoint.x, 2 * pStart.y - prevCControlPoint.y);
|
||||
} else {
|
||||
pControl1 = pStart;
|
||||
}
|
||||
}
|
||||
pControl1 = transform.transform(x, y);
|
||||
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
x = command.params[2];
|
||||
y = command.params[3];
|
||||
|
||||
Point pControl2 = transform.transform(x, y);
|
||||
prevCControlPoint = pControl2;
|
||||
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
x = command.params[4];
|
||||
y = command.params[5];
|
||||
|
||||
p = transform.transform(x, y);
|
||||
|
||||
@@ -430,13 +393,216 @@ public class ShapeImporter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd != 'C') {
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
}
|
||||
|
||||
private void processPath(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
|
||||
String data = childElement.getAttribute("d");
|
||||
|
||||
char command = 0;
|
||||
Point startPoint = new Point(0, 0);
|
||||
Point prevCControlPoint = null;
|
||||
Point prevQControlPoint = null;
|
||||
double x0 = 0;
|
||||
double y0 = 0;
|
||||
|
||||
List<PathCommand> pathCommands = new ArrayList<>();
|
||||
SvgPathReader pathReader = new SvgPathReader(data);
|
||||
while (pathReader.hasNext()) {
|
||||
char newCommand;
|
||||
if ((newCommand = pathReader.readCommand()) != 0) {
|
||||
command = newCommand;
|
||||
}
|
||||
|
||||
boolean isRelative = Character.isLowerCase(command);
|
||||
|
||||
double x = x0;
|
||||
double y = y0;
|
||||
|
||||
char cmd = Character.toUpperCase(command);
|
||||
switch (cmd) {
|
||||
case 'M':
|
||||
PathCommand scr = new PathCommand();
|
||||
scr.command = 'M';
|
||||
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
|
||||
scr.params = new double[]{x, y};
|
||||
|
||||
pathCommands.add(scr);
|
||||
startPoint = new Point(x, y);
|
||||
|
||||
command = isRelative ? 'l' : 'L';
|
||||
break;
|
||||
case 'Z':
|
||||
PathCommand serz = new PathCommand();
|
||||
serz.command = 'Z';
|
||||
x = startPoint.x;
|
||||
y = startPoint.y;
|
||||
serz.params = new double[]{x, y};
|
||||
pathCommands.add(serz);
|
||||
break;
|
||||
case 'L':
|
||||
PathCommand serl = new PathCommand();
|
||||
serl.command = 'L';
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
|
||||
serl.params = new double[]{x, y};
|
||||
pathCommands.add(serl);
|
||||
break;
|
||||
case 'H':
|
||||
PathCommand serh = new PathCommand();
|
||||
serh.command = 'H';
|
||||
x = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
}
|
||||
|
||||
serh.params = new double[]{x};
|
||||
pathCommands.add(serh);
|
||||
break;
|
||||
case 'V':
|
||||
PathCommand serv = new PathCommand();
|
||||
serv.command = 'V';
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
y += y0;
|
||||
}
|
||||
|
||||
serv.params = new double[]{y};
|
||||
pathCommands.add(serv);
|
||||
break;
|
||||
case 'Q':
|
||||
case 'T':
|
||||
PathCommand cer = new PathCommand();
|
||||
cer.command = 'Q';
|
||||
|
||||
Point pControl;
|
||||
if (cmd == 'Q') {
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
|
||||
pControl = new Point(x, y);
|
||||
} else {
|
||||
if (prevQControlPoint != null) {
|
||||
pControl = new Point(2 * x0 - prevQControlPoint.x, 2 * y0 - prevQControlPoint.y);
|
||||
} else {
|
||||
pControl = new Point(x0, y0);
|
||||
}
|
||||
}
|
||||
|
||||
prevQControlPoint = pControl;
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
|
||||
cer.params = new double[]{pControl.x, pControl.y, x, y};
|
||||
pathCommands.add(cer);
|
||||
break;
|
||||
case 'C':
|
||||
case 'S':
|
||||
showWarning("cubicCurvesNotSupported", "Cubic curves are not supported by Flash.");
|
||||
|
||||
// create at least something...
|
||||
Point pControl1;
|
||||
if (cmd == 'C') {
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
|
||||
pControl1 = new Point(x, y);
|
||||
} else {
|
||||
if (prevCControlPoint != null) {
|
||||
pControl1 = new Point(2 * x0 - prevCControlPoint.x, 2 * y0 - prevCControlPoint.y);
|
||||
} else {
|
||||
pControl1 = new Point(x0, y0);
|
||||
}
|
||||
}
|
||||
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
|
||||
Point pControl2 = new Point(x, y);
|
||||
prevCControlPoint = pControl2;
|
||||
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
|
||||
PathCommand cerc = new PathCommand();
|
||||
cerc.command = 'C';
|
||||
cerc.params = new double[]{pControl1.x, pControl1.y, pControl2.x, pControl2.y, x, y};
|
||||
pathCommands.add(cerc);
|
||||
|
||||
break;
|
||||
case 'A':
|
||||
double rx = pathReader.readDouble();
|
||||
double ry = pathReader.readDouble();
|
||||
double xRot = pathReader.readDouble();
|
||||
int largeFlag = (int) pathReader.readDouble();
|
||||
int sweepFlag = (int) pathReader.readDouble();
|
||||
|
||||
x = pathReader.readDouble();
|
||||
y = pathReader.readDouble();
|
||||
if (isRelative) {
|
||||
x += x0;
|
||||
y += y0;
|
||||
}
|
||||
|
||||
// todo: draw arc, now draw only a line
|
||||
PathCommand sera = new PathCommand();
|
||||
sera.command = 'L';
|
||||
sera.params = new double[]{x, y};
|
||||
pathCommands.add(sera);
|
||||
|
||||
break;
|
||||
default:
|
||||
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Unknown command: {0}", command);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd != 'C' && cmd != 'S') {
|
||||
prevCControlPoint = null;
|
||||
}
|
||||
|
||||
if (cmd != 'Q' && cmd != 'T') {
|
||||
prevQControlPoint = null;
|
||||
}
|
||||
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
|
||||
processCommands(shapeNum, shapes, pathCommands, transform, style);
|
||||
}
|
||||
|
||||
private StyleChangeRecord getStyleChangeRecord(int shapeNum, SvgStyle style) {
|
||||
@@ -462,9 +628,34 @@ public class ShapeImporter {
|
||||
Color lineColor = style.getStrokeColorWithOpacity();
|
||||
if (lineColor != null) {
|
||||
scr.lineStyles.lineStyles = new LINESTYLE[1];
|
||||
scr.lineStyles.lineStyles[0] = shapeNum <= 3 ? new LINESTYLE() : new LINESTYLE2();
|
||||
scr.lineStyles.lineStyles[0].color = shapeNum >= 3 ? new RGBA(lineColor) : new RGB(lineColor);
|
||||
scr.lineStyles.lineStyles[0].width = (int) Math.round(style.strokeWidth * SWF.unitDivisor);
|
||||
LINESTYLE lineStyle = shapeNum <= 3 ? new LINESTYLE() : new LINESTYLE2();;
|
||||
lineStyle.color = shapeNum >= 3 ? new RGBA(lineColor) : new RGB(lineColor);
|
||||
lineStyle.width = (int) Math.round(style.strokeWidth * SWF.unitDivisor);
|
||||
SvgLineCap lineCap = style.strokeLineCap;
|
||||
SvgLineJoin lineJoin = style.strokeLineJoin;
|
||||
if (lineStyle instanceof LINESTYLE2) {
|
||||
LINESTYLE2 lineStyle2 = (LINESTYLE2) lineStyle;
|
||||
int swfCap = lineCap == SvgLineCap.BUTT ? LINESTYLE2.NO_CAP
|
||||
: lineCap == SvgLineCap.ROUND ? LINESTYLE2.ROUND_CAP
|
||||
: lineCap == SvgLineCap.SQUARE ? LINESTYLE2.SQUARE_CAP : 0;
|
||||
lineStyle2.startCapStyle = swfCap;
|
||||
lineStyle2.endCapStyle = swfCap;
|
||||
|
||||
int swfJoin = lineJoin == SvgLineJoin.MITER ? LINESTYLE2.MITER_JOIN
|
||||
: lineJoin == SvgLineJoin.ROUND ? LINESTYLE2.ROUND_JOIN
|
||||
: lineJoin == SvgLineJoin.BEVEL ? LINESTYLE2.BEVEL_JOIN : 0;
|
||||
lineStyle2.joinStyle = swfJoin;
|
||||
lineStyle2.miterLimitFactor = (int) style.strokeMiterLimit;
|
||||
} else {
|
||||
if (lineCap != SvgLineCap.ROUND) {
|
||||
showWarning("lineCapNotSupported", "LineCap style not supported in shape " + shapeNum);
|
||||
}
|
||||
if (lineJoin != SvgLineJoin.ROUND) {
|
||||
showWarning("lineJoinNotSupported", "LineJoin style not supported in shape " + shapeNum);
|
||||
}
|
||||
}
|
||||
|
||||
scr.lineStyles.lineStyles[0] = lineStyle;
|
||||
scr.lineStyle = 1;
|
||||
} else {
|
||||
scr.lineStyles.lineStyles = new LINESTYLE[0];
|
||||
@@ -482,7 +673,7 @@ public class ShapeImporter {
|
||||
// todo: honfika: named colors: http://www.w3.org/TR/SVG/types.html#ColorKeywords
|
||||
switch (rgbStr) {
|
||||
case "none":
|
||||
return new Color(0, true);
|
||||
return TRANSPARENT;
|
||||
}
|
||||
|
||||
if (rgbStr.startsWith("#")) {
|
||||
@@ -493,12 +684,30 @@ public class ShapeImporter {
|
||||
|
||||
int i = Integer.parseInt(s, 16);
|
||||
return new Color(i, false);
|
||||
} 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 SvgStyle {
|
||||
class PathCommand {
|
||||
|
||||
public char command;
|
||||
|
||||
public double[] params;
|
||||
}
|
||||
|
||||
enum SvgLineCap {
|
||||
|
||||
BUTT, ROUND, SQUARE
|
||||
}
|
||||
|
||||
enum SvgLineJoin {
|
||||
|
||||
MITER, ROUND, BEVEL
|
||||
}
|
||||
|
||||
class SvgStyle implements Cloneable {
|
||||
|
||||
public Color fillColor;
|
||||
|
||||
@@ -510,12 +719,24 @@ public class ShapeImporter {
|
||||
|
||||
public double strokeWidth;
|
||||
|
||||
public double strokeOpacity;
|
||||
|
||||
public SvgLineCap strokeLineCap;
|
||||
|
||||
public SvgLineJoin strokeLineJoin;
|
||||
|
||||
public double strokeMiterLimit;
|
||||
|
||||
public SvgStyle() {
|
||||
fillColor = Color.BLACK;
|
||||
fillOpacity = 1;
|
||||
strokeColor = null;
|
||||
strokeWidth = 1;
|
||||
strokeOpacity = 1;
|
||||
opacity = 1;
|
||||
strokeLineCap = SvgLineCap.BUTT;
|
||||
strokeLineJoin = SvgLineJoin.MITER;
|
||||
strokeMiterLimit = 4;
|
||||
}
|
||||
|
||||
public Color getFillColorWithOpacity() {
|
||||
@@ -536,7 +757,7 @@ public class ShapeImporter {
|
||||
return null;
|
||||
}
|
||||
|
||||
int opacity = (int) Math.round(this.opacity * 255);
|
||||
int opacity = (int) Math.round(this.opacity * strokeOpacity * 255);
|
||||
if (opacity == 255) {
|
||||
return strokeColor;
|
||||
}
|
||||
@@ -544,47 +765,127 @@ public class ShapeImporter {
|
||||
return new Color(strokeColor.getRed(), strokeColor.getGreen(), strokeColor.getBlue(), opacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SvgStyle clone() {
|
||||
try {
|
||||
SvgStyle ret = (SvgStyle) super.clone();
|
||||
return ret;
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyStyle(SvgStyle style, String name, String value) {
|
||||
if (value == null || value.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case "fill": {
|
||||
Color fillColor = parseColor(value);
|
||||
if (fillColor != null) {
|
||||
style.fillColor = fillColor == TRANSPARENT ? null : fillColor;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "fill-opacity": {
|
||||
double opacity = Double.parseDouble(value);
|
||||
style.fillOpacity = opacity;
|
||||
}
|
||||
break;
|
||||
case "stroke": {
|
||||
Color strokeColor = parseColor(value);
|
||||
if (strokeColor != null) {
|
||||
style.strokeColor = strokeColor == TRANSPARENT ? null : strokeColor;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "stroke-width": {
|
||||
double strokeWidth = Double.parseDouble(value);
|
||||
style.strokeWidth = strokeWidth;
|
||||
}
|
||||
break;
|
||||
case "stroke-opacity": {
|
||||
double opacity = Double.parseDouble(value);
|
||||
style.strokeOpacity = opacity;
|
||||
}
|
||||
break;
|
||||
case "stroke-linecap": {
|
||||
switch (value) {
|
||||
case "butt":
|
||||
style.strokeLineCap = SvgLineCap.BUTT;
|
||||
break;
|
||||
case "round":
|
||||
style.strokeLineCap = SvgLineCap.ROUND;
|
||||
break;
|
||||
case "square":
|
||||
style.strokeLineCap = SvgLineCap.SQUARE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "stroke-linejoin": {
|
||||
switch (value) {
|
||||
case "miter":
|
||||
style.strokeLineJoin = SvgLineJoin.MITER;
|
||||
break;
|
||||
case "round":
|
||||
style.strokeLineJoin = SvgLineJoin.ROUND;
|
||||
break;
|
||||
case "bevel":
|
||||
style.strokeLineJoin = SvgLineJoin.BEVEL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "stroke-miterlimit": {
|
||||
double strokeMiterLimit = Double.parseDouble(value);
|
||||
style.strokeMiterLimit = strokeMiterLimit;
|
||||
}
|
||||
case "opacity": {
|
||||
double opacity = Double.parseDouble(value);
|
||||
style.opacity = opacity;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private SvgStyle apply(Element element) {
|
||||
SvgStyle result = new SvgStyle();
|
||||
result.fillColor = fillColor;
|
||||
result.fillOpacity = fillOpacity;
|
||||
result.strokeColor = strokeColor;
|
||||
result.strokeWidth = strokeWidth;
|
||||
SvgStyle result = clone();
|
||||
|
||||
String attr = element.getAttribute("fill");
|
||||
Color fillColor = parseColor(attr);
|
||||
if (fillColor != null) {
|
||||
result.fillColor = fillColor;
|
||||
String[] styles = new String[]{
|
||||
"fill", "fill-opacity",
|
||||
"stroke", "stroke-width", "stroke-opacity", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit",
|
||||
"opacity"
|
||||
};
|
||||
|
||||
for (String style : styles) {
|
||||
if (element.hasAttribute(style)) {
|
||||
String attr = element.getAttribute(style);
|
||||
applyStyle(result, style, attr);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
attr = element.getAttribute("fill-opacity");
|
||||
if (attr.length() > 0) {
|
||||
double opacity = Double.parseDouble(attr);
|
||||
result.fillOpacity = opacity;
|
||||
}
|
||||
|
||||
attr = element.getAttribute("stroke");
|
||||
Color strokeColor = parseColor(attr);
|
||||
if (strokeColor != null) {
|
||||
result.strokeColor = strokeColor;
|
||||
}
|
||||
|
||||
attr = element.getAttribute("stroke-width");
|
||||
if (attr.length() > 0) {
|
||||
double strokeWidth = Double.parseDouble(attr);
|
||||
result.strokeWidth = strokeWidth;
|
||||
}
|
||||
|
||||
attr = element.getAttribute("opacity");
|
||||
if (attr.length() > 0) {
|
||||
double opacity = Double.parseDouble(attr);
|
||||
result.opacity = opacity;
|
||||
if (element.hasAttribute("style")) {
|
||||
String[] styleDefs = element.getAttribute("style").split(";");
|
||||
for (String styleDef : styleDefs) {
|
||||
String[] parts = styleDef.split(":", 2);
|
||||
applyStyle(result, parts[0], parts[1].trim());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private void showWarning(String name, String text) {
|
||||
if (!shownWarnings.contains(name)) {
|
||||
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, text);
|
||||
shownWarnings.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getShapeTagType(String format) {
|
||||
int res = 0;
|
||||
switch (format) {
|
||||
|
||||
@@ -49,6 +49,7 @@ public class SvgPathReader {
|
||||
return 0;
|
||||
}
|
||||
|
||||
readWhiteSpaces();
|
||||
char ch = peek();
|
||||
char command = 0;
|
||||
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
|
||||
@@ -63,6 +64,7 @@ public class SvgPathReader {
|
||||
public double readDouble() {
|
||||
int startPos = pos;
|
||||
|
||||
readWhiteSpaces();
|
||||
if (peek() == '-') {
|
||||
pos++;
|
||||
}
|
||||
@@ -89,6 +91,17 @@ public class SvgPathReader {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void readWhiteSpaces() {
|
||||
while (hasNext()) {
|
||||
char ch = peek();
|
||||
if (ch != ' ' && ch != '\r' && ch != '\n') {
|
||||
return;
|
||||
}
|
||||
|
||||
readChar();
|
||||
}
|
||||
}
|
||||
|
||||
private void readSeparators() {
|
||||
while (hasNext()) {
|
||||
char ch = peek();
|
||||
|
||||
@@ -34,6 +34,12 @@ public class LINESTYLE2 extends LINESTYLE implements Serializable {
|
||||
@SWFType(value = BasicType.UB, count = 2)
|
||||
public int joinStyle;
|
||||
|
||||
public static final int ROUND_CAP = 0;
|
||||
|
||||
public static final int NO_CAP = 1;
|
||||
|
||||
public static final int SQUARE_CAP = 2;
|
||||
|
||||
public static final int ROUND_JOIN = 0;
|
||||
|
||||
public static final int BEVEL_JOIN = 1;
|
||||
@@ -57,12 +63,6 @@ public class LINESTYLE2 extends LINESTYLE implements Serializable {
|
||||
@SWFType(value = BasicType.UB, count = 2)
|
||||
public int endCapStyle;
|
||||
|
||||
public static final int ROUND_CAP = 0;
|
||||
|
||||
public static final int NO_CAP = 1;
|
||||
|
||||
public static final int SQUARE_CAP = 2;
|
||||
|
||||
@SWFType(BasicType.UI16)
|
||||
@Conditional(value = "joinStyle", options = MITER_JOIN)
|
||||
public int miterLimitFactor;
|
||||
|
||||
Reference in New Issue
Block a user