diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgColor.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgColor.java index 6a0d76baa..72dbe3a71 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgColor.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgColor.java @@ -24,10 +24,6 @@ import java.awt.Color; */ class SvgColor extends SvgFill { - public static final Color TRANSPARENT = new Color(0, true); - - public static final SvgColor SVG_TRANSPARENT = new SvgColor(TRANSPARENT); - public Color color; public SvgColor(int r, int g, int b, int opacity) { @@ -54,8 +50,6 @@ class SvgColor extends SvgFill { // named colors from: http://www.w3.org/TR/SVG/types.html#ColorKeywords switch (colorString) { - case "none": - return SVG_TRANSPARENT; case "aliceblue": return new SvgColor(240, 248, 255); case "antiquewhite": diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java index c6cf468ca..fe8aa500a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java @@ -386,86 +386,24 @@ public class SvgImporter { List pathCommands = new ArrayList<>(); SvgPathReader pathReader = new SvgPathReader(data); - while (pathReader.hasNext()) { - char newCommand; - if ((newCommand = pathReader.readCommand()) != 0) { - command = newCommand; - } + try { + while (pathReader.hasNext()) { + char newCommand; + if ((newCommand = pathReader.readCommand()) != 0) { + command = newCommand; + } - boolean isRelative = Character.isLowerCase(command); + boolean isRelative = Character.isLowerCase(command); - double x = x0; - double y = y0; + double x = x0; + double y = y0; - char cmd = Character.toUpperCase(command); - switch (cmd) { - case 'M': - PathCommand scr = new PathCommand(); - scr.command = 'M'; + 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; - 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) { @@ -473,31 +411,23 @@ public class SvgImporter { 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); - } + scr.params = new double[]{x, y}; - prevQControlPoint = pControl; - x = pathReader.readDouble(); - y = pathReader.readDouble(); - if (isRelative) { - x += x0; - y += y0; - } + pathCommands.add(scr); + startPoint = new Point(x, y); - 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') { + command = isRelative ? 'l' : 'L'; + break; + case 'Z': + PathCommand serz = new PathCommand(); + serz.command = 'Z'; + x = startPoint.x; + y = startPoint.y; + pathCommands.add(serz); + break; + case 'L': + PathCommand serl = new PathCommand(); + serl.command = 'L'; x = pathReader.readDouble(); y = pathReader.readDouble(); if (isRelative) { @@ -505,174 +435,248 @@ public class SvgImporter { 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 fi = pathReader.readDouble() * Math.PI / 180; - boolean largeFlag = (int) pathReader.readDouble() != 0; - boolean sweepFlag = (int) pathReader.readDouble() != 0; - - x = pathReader.readDouble(); - y = pathReader.readDouble(); - if (isRelative) { - x += x0; - y += y0; - } - - if (rx == 0 || ry == 0) { - // straight line to (x, y) - PathCommand sera = new PathCommand(); - sera.command = 'L'; - sera.params = new double[]{x, y}; - pathCommands.add(sera); - } else { - rx = Math.abs(rx); - ry = Math.abs(ry); - - 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; - - // Correction of out-of-range radii - double lambda = x1Comma * x1Comma / (rx * rx) + y1Comma * y1Comma / (ry * ry); - if (lambda > 1) { - double sqrtLambda = Math.sqrt(lambda); - rx = sqrtLambda * rx; - ry = sqrtLambda * ry; + 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; } - 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; + 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; } - 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; + serv.params = new double[]{y}; + pathCommands.add(serv); + break; + case 'Q': + case 'T': + PathCommand cer = new PathCommand(); + cer.command = 'Q'; - double px1 = (x1Comma - cxComma) / rx; - double py1 = (y1Comma - cyComma) / ry; - double theta1 = calcAngle(1, 0, px1, py1); - - double px2 = (-x1Comma - cxComma) / rx; - double py2 = (-y1Comma - cyComma) / ry; - double deltaTheta = calcAngle(px1, py1, px2, py2); - if (sweepFlag) { - if (deltaTheta < 0) { - deltaTheta += 2 * Math.PI; + Point pControl; + if (cmd == 'Q') { + x = pathReader.readDouble(); + y = pathReader.readDouble(); + if (isRelative) { + x += x0; + y += y0; } - } else if (deltaTheta > 0) { - deltaTheta -= 2 * Math.PI; + + 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); } - double rcp = Math.sqrt(4 - 2 * Math.sqrt(2)); - double delta = Math.signum(deltaTheta) * Math.PI / 4; + prevQControlPoint = pControl; + x = pathReader.readDouble(); + y = pathReader.readDouble(); + if (isRelative) { + x += x0; + y += y0; + } - int segmentCount = (int) Math.ceil(deltaTheta / delta); - double theta = theta1; + 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."); - PathCommand sera; - for (int i = 0; i < segmentCount - 1; i++) { - theta += delta; - /*sera = new PathCommand(); - sera.command = 'L'; - double x12 = Math.cos(theta) * rx; - double y12 = Math.sin(theta) * ry; - x1Comma = Math.cos(fi) * x12 - Math.sin(fi) * y12; - y1Comma = Math.sin(fi) * x12 + Math.cos(fi) * y12; - sera.params = new double[]{cx + x1Comma, cy + y1Comma}; - pathCommands.add(sera);*/ + // 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 fi = pathReader.readDouble() * Math.PI / 180; + boolean largeFlag = (int) pathReader.readDouble() != 0; + boolean sweepFlag = (int) pathReader.readDouble() != 0; + + x = pathReader.readDouble(); + y = pathReader.readDouble(); + if (isRelative) { + x += x0; + y += y0; + } + + if (rx == 0 || ry == 0) { + // straight line to (x, y) + PathCommand sera = new PathCommand(); + sera.command = 'L'; + sera.params = new double[]{x, y}; + pathCommands.add(sera); + } else { + rx = Math.abs(rx); + ry = Math.abs(ry); + + 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; + + // Correction of out-of-range radii + double lambda = x1Comma * x1Comma / (rx * rx) + y1Comma * y1Comma / (ry * ry); + if (lambda > 1) { + double sqrtLambda = Math.sqrt(lambda); + rx = sqrtLambda * rx; + ry = sqrtLambda * ry; + } + + 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; + + double px1 = (x1Comma - cxComma) / rx; + double py1 = (y1Comma - cyComma) / ry; + double theta1 = calcAngle(1, 0, px1, py1); + + double px2 = (-x1Comma - cxComma) / rx; + double py2 = (-y1Comma - cyComma) / ry; + double deltaTheta = calcAngle(px1, py1, px2, py2); + if (sweepFlag) { + if (deltaTheta < 0) { + deltaTheta += 2 * Math.PI; + } + } else if (deltaTheta > 0) { + deltaTheta -= 2 * Math.PI; + } + + double rcp = Math.sqrt(4 - 2 * Math.sqrt(2)); + double delta = Math.signum(deltaTheta) * Math.PI / 4; + + int segmentCount = (int) Math.ceil(deltaTheta / delta); + double theta = theta1; + + PathCommand sera; + for (int i = 0; i < segmentCount - 1; i++) { + theta += delta; + /*sera = new PathCommand(); + sera.command = 'L'; + double x12 = Math.cos(theta) * rx; + double y12 = Math.sin(theta) * ry; + x1Comma = Math.cos(fi) * x12 - Math.sin(fi) * y12; + y1Comma = Math.sin(fi) * x12 + Math.cos(fi) * y12; + sera.params = new double[]{cx + x1Comma, cy + y1Comma}; + pathCommands.add(sera);*/ + + sera = new PathCommand(); + sera.command = 'Q'; + double x12 = Math.cos(theta) * rx; + double y12 = Math.sin(theta) * ry; + x1Comma = Math.cos(fi) * x12 - Math.sin(fi) * y12; + y1Comma = Math.sin(fi) * x12 + Math.cos(fi) * y12; + + double theta2 = theta - delta / 2; + x12 = Math.cos(theta2) * rx * rcp; + y12 = Math.sin(theta2) * ry * rcp; + double x1Comma2 = Math.cos(fi) * x12 - Math.sin(fi) * y12; + double y1Comma2 = Math.sin(fi) * x12 + Math.cos(fi) * y12; + sera.params = new double[]{cx + x1Comma2, cy + y1Comma2, cx + x1Comma, cy + y1Comma}; + pathCommands.add(sera); + } sera = new PathCommand(); sera.command = 'Q'; - double x12 = Math.cos(theta) * rx; - double y12 = Math.sin(theta) * ry; + + theta += delta; + double diff = theta1 + deltaTheta - theta; + diff = -delta - diff; + theta = theta - delta - diff / 2; + + double rcpm = 1 + (rcp - 1) * (diff / delta) * (diff / delta); + double x12 = Math.cos(theta) * rx * rcpm; + double y12 = Math.sin(theta) * ry * rcpm; x1Comma = Math.cos(fi) * x12 - Math.sin(fi) * y12; y1Comma = Math.sin(fi) * x12 + Math.cos(fi) * y12; - - double theta2 = theta - delta / 2; - x12 = Math.cos(theta2) * rx * rcp; - y12 = Math.sin(theta2) * ry * rcp; - double x1Comma2 = Math.cos(fi) * x12 - Math.sin(fi) * y12; - double y1Comma2 = Math.sin(fi) * x12 + Math.cos(fi) * y12; - sera.params = new double[]{cx + x1Comma2, cy + y1Comma2, cx + x1Comma, cy + y1Comma}; + sera.params = new double[]{cx + x1Comma, cy + y1Comma, x, y}; pathCommands.add(sera); + /*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; + } - sera = new PathCommand(); - sera.command = 'Q'; + if (cmd != 'C' && cmd != 'S') { + prevCControlPoint = null; + } - theta += delta; - double diff = theta1 + deltaTheta - theta; - diff = -delta - diff; - theta = theta - delta - diff / 2; + if (cmd != 'Q' && cmd != 'T') { + prevQControlPoint = null; + } - double rcpm = 1 + (rcp - 1) * (diff / delta) * (diff / delta); - double x12 = Math.cos(theta) * rx * rcpm; - double y12 = Math.sin(theta) * ry * rcpm; - x1Comma = Math.cos(fi) * x12 - Math.sin(fi) * y12; - y1Comma = Math.sin(fi) * x12 + Math.cos(fi) * y12; - sera.params = new double[]{cx + x1Comma, cy + y1Comma, x, y}; - pathCommands.add(sera); - /*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; + x0 = x; + y0 = y; } - - if (cmd != 'C' && cmd != 'S') { - prevCControlPoint = null; - } - - if (cmd != 'Q' && cmd != 'T') { - prevQControlPoint = null; - } - - x0 = x; - y0 = y; + } catch (NumberFormatException e) { + // ignore remaining data as specified in SVG Specification F.2 Error processing } processCommands(shapeNum, shapes, pathCommands, transform, style); @@ -932,35 +936,39 @@ public class SvgImporter { List pathCommands = new ArrayList<>(); SvgPathReader pathReader = new SvgPathReader(data); - while (pathReader.hasNext()) { - double x = x0; - double y = y0; + try { + while (pathReader.hasNext()) { + double x = x0; + double y = y0; - Point p = null; - switch (command) { - case 'M': - PathCommand scr = new PathCommand(); - scr.command = 'M'; + 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}; + x = pathReader.readDouble(); + y = pathReader.readDouble(); + scr.params = new double[]{x, y}; - pathCommands.add(scr); - 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; + pathCommands.add(scr); + 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'; } - - x0 = x; - y0 = y; - command = 'L'; + } catch (NumberFormatException e) { + // ignore remaining data as specified in SVG Specification F.2 Error processing } if (close) { @@ -983,7 +991,6 @@ public class SvgImporter { byte[] pngData = Helper.readStream(pngUrl.openStream()); Helper.writeFile(name + ".original.png", pngData); } - //String svgDataS = new String(svgData); String svgDataS = Helper.readTextFile(name + ".original.svg"); SWF swf = new SWF(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java index 4697c2e7b..754b6f88f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java @@ -22,7 +22,6 @@ import com.jpexs.helpers.Helper; import java.awt.Color; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; @@ -388,18 +387,17 @@ class SvgStyle implements Cloneable { } } - private Color parseColor(String rgbStr) { - SvgFill fill = parseFill(new HashMap<>(), rgbStr, null); - return fill.toColor(); - } - - private SvgFill parseFill(Map idMap, String rgbStr, SvgStyle style) { - if (rgbStr == null) { + private SvgFill parseFill(Map idMap, String fillStr, SvgStyle style) { + if (fillStr == null) { return null; } + if (fillStr.equals("none")) { + return SvgTransparentFill.INSTANCE; + } + Pattern idPat = Pattern.compile("url\\(#([^)]+)\\).*"); - java.util.regex.Matcher mPat = idPat.matcher(rgbStr); + java.util.regex.Matcher mPat = idPat.matcher(fillStr); if (mPat.matches()) { String elementId = mPat.group(1); @@ -454,10 +452,10 @@ class SvgStyle implements Cloneable { return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); } - rgbStr = rgbStr.substring(elementId.length() + 6).trim(); // remove url(#...) + fillStr = fillStr.substring(elementId.length() + 6).trim(); // remove url(#...) } - SvgColor result = SvgColor.parse(rgbStr); + SvgColor result = SvgColor.parse(fillStr); if (result == null) { importer.showWarning("fillNotSupported", "Unknown fill style. Random color assigned."); return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); @@ -466,122 +464,183 @@ class SvgStyle implements Cloneable { return result; } - private void applyStyle(Map idMap, SvgStyle style, String name, String value) { - if (value == null || value.length() == 0) { - return; + private void applyStyle(Map idMap, SvgStyle style, SvgStyle parentStyle, SvgStyleProperty styleProperty, String value) { + boolean inherit = styleProperty.isInherited(); + if ("inherit".equals(value)) { + value = ""; + inherit = true; } - switch (name) { - case "color": { - Color color = parseColor(value); - if (color != null) { - style.color = color == SvgColor.TRANSPARENT ? null : color; - } - } - break; - case "fill": { - SvgFill fill = parseFill(idMap, value, style); - if (fill != null) { - style.fill = fill == SvgColor.SVG_TRANSPARENT ? null : fill; - } - } - break; - case "stop-color": { - if ("currentColor".equals(value)) { - if (style.parentStyle != null) { - style.stopColor = style.parentStyle.color; + boolean ok = value != null && value.length() != 0; + + String name = styleProperty.name(); + try { + if (ok) { + ok = false; + switch (name) { + case "color": { + Color color = SvgColor.parse(value).toColor(); + if (color != null) { + style.color = color; + ok = true; + } } - } else if ("inherit".equals(value)) { - importer.showWarning(value + "StopColorNotSupported", "The stop color value '" + value + "' is not supported."); - } else { - style.stopColor = parseColor(value); + break; + case "fill": { + SvgFill fill = parseFill(idMap, value, style); + if (fill != null) { + style.fill = fill == SvgTransparentFill.INSTANCE ? null : fill; + ok = true; + } + } + break; + case "fill-opacity": { + double opacity = Double.parseDouble(value); + style.fillOpacity = opacity; + ok = true; + } + break; + case "stroke": { + SvgFill strokeFill = parseFill(idMap, value, style); + if (strokeFill != null) { + style.strokeFill = strokeFill == SvgTransparentFill.INSTANCE ? null : strokeFill; + ok = true; + } + } + break; + case "stroke-width": { + double strokeWidth = Double.parseDouble(value); + style.strokeWidth = strokeWidth; + ok = true; + } + break; + case "stroke-opacity": { + double opacity = Double.parseDouble(value); + style.strokeOpacity = opacity; + ok = true; + } + break; + case "stroke-linecap": { + switch (value) { + case "butt": + style.strokeLineCap = SvgLineCap.BUTT; + ok = true; + break; + case "round": + style.strokeLineCap = SvgLineCap.ROUND; + ok = true; + break; + case "square": + style.strokeLineCap = SvgLineCap.SQUARE; + ok = true; + break; + } + } + break; + case "stroke-linejoin": { + switch (value) { + case "miter": + style.strokeLineJoin = SvgLineJoin.MITER; + ok = true; + break; + case "round": + style.strokeLineJoin = SvgLineJoin.ROUND; + ok = true; + break; + case "bevel": + style.strokeLineJoin = SvgLineJoin.BEVEL; + ok = true; + break; + } + } + break; + case "stroke-miterlimit": { + double strokeMiterLimit = Double.parseDouble(value); + style.strokeMiterLimit = strokeMiterLimit; + ok = true; + } + break; + case "opacity": { + double opacity = Double.parseDouble(value); + style.opacity = opacity; + ok = true; + } + break; + case "stop-color": { + if ("currentColor".equals(value)) { + if (style.parentStyle != null) { + style.stopColor = style.parentStyle.color; + } + } else { + //importer.showWarning(value + "StopColorNotSupported", "The stop color value '" + value + "' is not supported."); + style.stopColor = SvgColor.parse(value).toColor(); + ok = true; + } + } + break; + case "stop-opacity": { + //importer.showWarning(value + "StopOpacityNotSupported", "The stop opacity value '" + value + "' is not supported."); + double stopOpacity = Double.parseDouble(value); + style.stopOpacity = stopOpacity; + ok = true; + } + break; } } - break; - case "fill-opacity": { - double opacity = Double.parseDouble(value); - style.fillOpacity = opacity; + } catch (NumberFormatException ex) { + ok = false; + } + + if (!ok && inherit) { + switch (name) { + case "color": + style.color = parentStyle.color; + break; + case "fill": + style.fill = parentStyle.fill; + break; + case "fill-opacity": + style.fillOpacity = parentStyle.opacity; + break; + case "stroke": + style.strokeFill = parentStyle.strokeFill; + break; + case "stroke-width": + style.strokeWidth = parentStyle.strokeWidth; + break; + case "stroke-opacity": + style.strokeOpacity = parentStyle.opacity; + break; + case "stroke-linecap": + style.strokeLineCap = parentStyle.strokeLineCap; + break; + case "stroke-linejoin": + style.strokeLineJoin = parentStyle.strokeLineJoin; + break; + case "stroke-miterlimit": + style.strokeMiterLimit = parentStyle.strokeMiterLimit; + break; + case "opacity": + style.opacity = parentStyle.opacity; + break; + case "stop-color": + style.stopColor = parentStyle.stopColor; + break; + case "stop-opacity": + style.stopOpacity = parentStyle.stopOpacity; + break; } - break; - case "stop-opacity": { - if ("inherit".equals(value)) { - importer.showWarning(value + "StopOpacityNotSupported", "The stop opacity value '" + value + "' is not supported."); - } else { - double stopOpacity = Double.parseDouble(value); - style.stopOpacity = stopOpacity; - } - } - break; - case "stroke": { - SvgFill strokeFill = parseFill(idMap, value, style); - if (strokeFill != null) { - style.strokeFill = strokeFill == SvgColor.SVG_TRANSPARENT ? null : strokeFill; - } - } - 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; } } public SvgStyle apply(Element element, Map idMap) { - SvgStyle result = clone(); + SvgStyle result = new SvgStyle(importer); - String[] styles = new String[]{ - "color", "fill", "fill-opacity", - "stroke", "stroke-width", "stroke-opacity", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", - "opacity", "stop-color", "stop-opacity" - }; - - for (String style : styles) { - if (element.hasAttribute(style)) { - String attr = element.getAttribute(style).trim(); - applyStyle(idMap, result, style, attr); + for (SvgStyleProperty styleProperty : SvgStyleProperty.getProperties()) { + String name = styleProperty.name(); + if (element.hasAttribute(name)) { + String attr = element.getAttribute(name).trim(); + applyStyle(idMap, result, this, styleProperty, attr); } } @@ -589,7 +648,13 @@ class SvgStyle implements Cloneable { String[] styleDefs = element.getAttribute("style").split(";"); for (String styleDef : styleDefs) { String[] parts = styleDef.split(":", 2); - applyStyle(idMap, result, parts[0].trim(), parts[1].trim()); + String name = parts[0].trim(); + SvgStyleProperty styleProperty = SvgStyleProperty.getByName(name); + if (styleProperty == null) { + importer.showWarning(name + "StyleNotSupported", "The style '" + name + "' is not supported."); + } + + applyStyle(idMap, result, this, styleProperty, parts[1].trim()); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java new file mode 100644 index 000000000..13fc60344 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.svg; + +import com.sun.prism.paint.Color; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author JPEXS + */ +public class SvgStyleProperty { + + private final String name; + + private final boolean inherited; + + private final Object initial; + + public SvgStyleProperty(String name, boolean inherited, Object initial) { + this.name = name; + this.inherited = inherited; + this.initial = initial; + } + + public String name() { + return name; + } + + public boolean isInherited() { + return inherited; + } + + private static final Map properties; + + static { + Map p = new HashMap<>(); + p.put("color", new SvgStyleProperty("color", true, null /* depends on user agent */)); + p.put("fill", new SvgStyleProperty("fill", true, Color.BLACK)); + p.put("fill-opacity", new SvgStyleProperty("fill-opacity", true, 1.0)); + p.put("stroke", new SvgStyleProperty("stroke", true, null)); + p.put("stroke-width", new SvgStyleProperty("stroke-width", true, 1.0)); + p.put("stroke-opacity", new SvgStyleProperty("stroke-opacity", true, 1.0)); + p.put("stroke-linecap", new SvgStyleProperty("stroke-linecap", true, SvgLineCap.BUTT)); + p.put("stroke-linejoin", new SvgStyleProperty("stroke-linejoin", true, SvgLineJoin.MITER)); + p.put("stroke-miterlimit", new SvgStyleProperty("stroke-miterlimit", true, 4.0)); + p.put("opacity", new SvgStyleProperty("opacity", false, 1.0)); + p.put("stop-color", new SvgStyleProperty("stop-color", false, Color.BLACK)); + p.put("stop-opacity", new SvgStyleProperty("stop-opacity", false, 1.0)); + properties = p; + } + + public static Collection getProperties() { + return properties.values(); + } + + public static SvgStyleProperty getByName(String name) { + return properties.get(name); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgTransparentFill.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgTransparentFill.java new file mode 100644 index 000000000..b813fa459 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgTransparentFill.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.svg; + +import java.awt.Color; + +/** + * + * @author JPEXS + */ +class SvgTransparentFill extends SvgFill { + + private static final Color TRANSPARENT = new Color(0, true); + + public static SvgTransparentFill INSTANCE = new SvgTransparentFill(); + + private SvgTransparentFill() { + } + + @Override + public Color toColor() { + return TRANSPARENT; + } +}