diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java index 07b11af65..e170fa5af 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java @@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode; import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode; import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings; @@ -40,6 +41,7 @@ import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ButtonTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.tags.base.RenderContext; import com.jpexs.decompiler.flash.tags.base.TextTag; @@ -107,6 +109,7 @@ import net.kroo.elliot.GifSequenceWriter; import net.weiner.kevin.AnimatedGifEncoder; import org.monte.media.VideoFormatKeys; import org.monte.media.avi.AVIWriter; +import org.w3c.dom.Element; /** * Frame exporter. @@ -129,6 +132,9 @@ public class FrameExporter { case SVG: fem = FrameExportMode.SVG; break; + case SVG_COMBINED: + fem = FrameExportMode.SVG; + break; case WEBP: fem = FrameExportMode.WEBP; break; @@ -139,16 +145,16 @@ public class FrameExporter { throw new Error("Unsupported button export mode: " + settings.mode); } - if (frames == null) { + /*if (frames == null) { frames = new ArrayList<>(); frames.add(ButtonTag.FRAME_UP); frames.add(ButtonTag.FRAME_OVER); frames.add(ButtonTag.FRAME_DOWN); frames.add(ButtonTag.FRAME_HITTEST); - } + }*/ FrameExportSettings fes = new FrameExportSettings(fem, settings.zoom, true, settings.aaScale); - return exportFrames(handler, outdir, swf, containerId, frames, 1, fes, evl, true); + return exportFrames(handler, outdir, swf, containerId, frames, 1, fes, evl, true, settings.mode == ButtonExportMode.SVG_COMBINED); } public List exportSpriteFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List frames, int subframesLength, SpriteExportSettings settings, EventListener evl) throws IOException, InterruptedException { @@ -275,10 +281,6 @@ public class FrameExporter { } } - public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List frames, int subFramesLength, final FrameExportSettings settings, final EventListener evl) throws IOException, InterruptedException { - return exportFrames(handler, outdir, swf, containerId, frames, subFramesLength, settings, evl, false); - } - private String getFrameFileName(int frame, boolean button) { String buttonSuffix = ""; if (button) { @@ -300,7 +302,11 @@ public class FrameExporter { return "" + frame + buttonSuffix; } - public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List frames, int subFramesLength, final FrameExportSettings settings, final EventListener evl, boolean button) throws IOException, InterruptedException { + public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List frames, int subFramesLength, final FrameExportSettings settings, final EventListener evl) throws IOException, InterruptedException { + return exportFrames(handler, outdir, swf, containerId, frames, subFramesLength, settings, evl, false, false); + } + + private List exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List frames, int subFramesLength, final FrameExportSettings settings, final EventListener evl, boolean button, boolean combined) throws IOException, InterruptedException { final List ret = new ArrayList<>(); if (CancellableWorker.isInterrupted()) { return ret; @@ -309,6 +315,7 @@ public class FrameExporter { if (swf.getTags().isEmpty()) { return ret; } + Timelined timelined; Timeline tim0; List paths = new ArrayList<>(); String subPath = ""; @@ -317,10 +324,12 @@ public class FrameExporter { } if (containerId == 0) { + timelined = swf; tim0 = swf.getTimeline(); paths.add(subPath); } else { - tim0 = ((Timelined) swf.getCharacter(containerId)).getTimeline(); + timelined = (Timelined) swf.getCharacter(containerId); + tim0 = timelined.getTimeline(); Set classNames = swf.getCharacter(containerId).getClassNames(); if (Configuration.as3ExportNamesUseClassNamesOnly.get() && !classNames.isEmpty()) { @@ -367,23 +376,12 @@ public class FrameExporter { Map normalizedFonts = new LinkedHashMap<>(); Map normalizedTexts = new LinkedHashMap<>(); normalizer.normalizeFonts(tim.timelined.getSwf(), normalizedFonts, normalizedTexts); - - int max = frames.size(); - if (subFramesLength > 1) { - max = subFramesLength; - } - for (int i = 0; i < max; i++) { - if (evl != null) { - Tag parentTag = tim.getParentTag(); - evl.handleExportingEvent("frame", i + 1, max, parentTag == null ? "" : parentTag.getName()); - } - - final int fi = i; + + if (combined) { final Color fbackgroundColor = backgroundColor; for (File foutdir : foutdirs) { new RetryTask(() -> { - int frame = subFramesLength > 1 ? fframes.get(0) : fframes.get(fi); - File f = new File(foutdir + File.separator + getFrameFileName(((subFramesLength > 1 ? fi : fframes.get(fi)) + 1), button) + ".svg"); + File f = new File(foutdir + File.separator + "combined.svg"); try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) { ExportRectangle rect = new ExportRectangle(tim.displayRect); rect.xMax *= settings.zoom; @@ -391,21 +389,56 @@ public class FrameExporter { rect.xMin *= settings.zoom; rect.yMin *= settings.zoom; SVGExporter exporter = new SVGExporter(rect, settings.zoom, "frame", fbackgroundColor); - exporter.setNormalizedFonts(normalizedFonts, normalizedTexts); - tim.toSVG(frame, subFramesLength > 1 ? fi : 0, null, 0, exporter, null, 0, new Matrix(), new Matrix()); + exporter.setNormalizedFonts(normalizedFonts, normalizedTexts); + //Here is not timelined.toSVG, but drawableTag.toSVG, which draws combined button + ((DrawableTag) timelined).toSVG(0, 0, exporter, 0, null, 0, new Matrix(), new Matrix()); + + fos.write(Utf8Helper.getBytes(exporter.getSVG())); } ret.add(f); }, handler).run(); } - - if (CancellableWorker.isInterrupted()) { - break; + } else { + int max = frames.size(); + if (subFramesLength > 1) { + max = subFramesLength; } + for (int i = 0; i < max; i++) { + if (evl != null) { + Tag parentTag = tim.getParentTag(); + evl.handleExportingEvent("frame", i + 1, max, parentTag == null ? "" : parentTag.getName()); + } - if (evl != null) { - Tag parentTag = tim.getParentTag(); - evl.handleExportedEvent("frame", i + 1, max, parentTag == null ? "" : parentTag.getName()); + final int fi = i; + final Color fbackgroundColor = backgroundColor; + for (File foutdir : foutdirs) { + new RetryTask(() -> { + int frame = subFramesLength > 1 ? fframes.get(0) : fframes.get(fi); + File f = new File(foutdir + File.separator + getFrameFileName(((subFramesLength > 1 ? fi : fframes.get(fi)) + 1), button) + ".svg"); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) { + ExportRectangle rect = new ExportRectangle(tim.displayRect); + rect.xMax *= settings.zoom; + rect.yMax *= settings.zoom; + rect.xMin *= settings.zoom; + rect.yMin *= settings.zoom; + SVGExporter exporter = new SVGExporter(rect, settings.zoom, "frame", fbackgroundColor); + exporter.setNormalizedFonts(normalizedFonts, normalizedTexts); + tim.toSVG(frame, subFramesLength > 1 ? fi : 0, null, 0, exporter, null, 0, new Matrix(), new Matrix()); + fos.write(Utf8Helper.getBytes(exporter.getSVG())); + } + ret.add(f); + }, handler).run(); + } + + if (CancellableWorker.isInterrupted()) { + break; + } + + if (evl != null) { + Tag parentTag = tim.getParentTag(); + evl.handleExportedEvent("frame", i + 1, max, parentTag == null ? "" : parentTag.getName()); + } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java index 527441e7d..f30e9d08c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java @@ -100,6 +100,8 @@ public class SVGExporter implements RequiresNormalizedFonts { private Map normalizedFonts = new LinkedHashMap<>(); private Map normalizedTexts = new LinkedHashMap<>(); + + private boolean buttonStyleAdded = false; @Override public void setNormalizedFonts(Map normalizedFonts, Map normalizedTexts) { @@ -656,7 +658,55 @@ public class SVGExporter implements RequiresNormalizedFonts { } } - public void addStyle(String fontFace, byte[] data, FontExportMode mode) { + public void addStyle(String css) { + String value = getStyle().getTextContent(); + value += Helper.newLine; + value += css; + getStyle().setTextContent(value); + } + + public void requireButtonStyle() { + if (buttonStyleAdded) { + return; + } + buttonStyleAdded = true; + addStyle(" .button-frame {\n" + + " pointer-events: none;\n" + + " }\n" + + "\n" + + " .button-frame-up,\n" + + " .button-frame-over,\n" + + " .button-frame-down {\n" + + " opacity: 0;\n" + + " }\n" + + "\n" + + " .button .button-frame-up {\n" + + " opacity: 1;\n" + + " }\n" + + "\n" + + " .button:hover .button-frame-up {\n" + + " opacity: 0;\n" + + " }\n" + + " .button:hover .button-frame-over {\n" + + " opacity: 1;\n" + + " }\n" + + "\n" + + " .button:active .button-frame-over {\n" + + " opacity: 0;\n" + + " }\n" + + " .button:active .button-frame-down {\n" + + " opacity: 1;\n" + + " }\n" + + "\n" + + " .button-frame-hittest {\n" + + " opacity: 0;\n" + + " pointer-events: all;\n" + + " cursor: pointer;\n" + + " }\n"); + + } + + public void addFontFace(String fontFace, byte[] data, FontExportMode mode) { if (!fontFaces.contains(fontFace)) { fontFaces.add(fontFace); String base64Data = Helper.byteArrayToBase64String(data); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ButtonExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ButtonExportMode.java index 10d838069..e90bc2afc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ButtonExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ButtonExportMode.java @@ -32,6 +32,10 @@ public enum ButtonExportMode { * SVG - Scalable Vector Graphics */ SVG, + /** + * SVG - Scalable Vector Graphics - Combined button + */ + SVG_COMBINED, /** * BMP - Windows Bitmap */ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java index d72ba9c2e..e09dbcee1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java @@ -40,6 +40,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import org.w3c.dom.Element; /** * Base class for button tags. @@ -134,7 +135,36 @@ public abstract class ButtonTag extends DrawableTag implements Timelined { @Override public void toSVG(int frame, int time, SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException { - getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation); + //getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation); + exporter.requireButtonStyle(); + + Timeline tim = getTimeline(); + + Element buttonGroup = exporter.createSubGroup(new Matrix(), null); + buttonGroup.setAttribute("class", "button"); + + Element upGroup = exporter.createSubGroup(new Matrix(), null); + upGroup.setAttribute("class", "button-frame button-frame-up"); + tim.toSVG(ButtonTag.FRAME_UP, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation); + exporter.endGroup(); + + Element overGroup = exporter.createSubGroup(new Matrix(), null); + overGroup.setAttribute("class", "button-frame button-frame-over"); + tim.toSVG(ButtonTag.FRAME_OVER, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation); + exporter.endGroup(); + + Element downGroup = exporter.createSubGroup(new Matrix(), null); + downGroup.setAttribute("class", "button-frame button-frame-down"); + tim.toSVG(ButtonTag.FRAME_DOWN, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation); + exporter.endGroup(); + + Element hitTestGroup = exporter.createSubGroup(new Matrix(), null); + hitTestGroup.setAttribute("class", "button-frame button-frame-hittest"); + tim.toSVG(ButtonTag.FRAME_HITTEST, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation); + exporter.endGroup(); + + exporter.endGroup(); //buttonGroup + } /** diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index 7e447d56c..4dd40bc1d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -1114,7 +1114,7 @@ public abstract class TextTag extends DrawableTag { exporter.addToGroup(textElement); FontExportMode fontExportMode = FontExportMode.WOFF; - exporter.addStyle(fontFamily, new FontExporter().exportFont(font, fontExportMode), fontExportMode); + exporter.addFontFace(fontFamily, new FontExporter().exportFont(font, fontExportMode), fontExportMode); if (hasOffset) { exporter.endGroup(); diff --git a/src/com/jpexs/decompiler/flash/console/help.txt b/src/com/jpexs/decompiler/flash/console/help.txt index 7f0a2b8ec..711b9095c 100644 --- a/src/com/jpexs/decompiler/flash/console/help.txt +++ b/src/com/jpexs/decompiler/flash/console/help.txt @@ -356,6 +356,7 @@ Pre-options: sprite:webp_animated - WEBP animated format for Sprites button:png - PNG format for Buttons button:svg - SVG format for Buttons + button:svg_combined - SVG combined format for Buttons button:bmp - BMP format for Buttons button:webp - WEBP format for Buttons image:png_gif_jpeg - PNG/GIF/JPEG format for Images diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties index ce130a70d..f2fd7149c 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties @@ -120,4 +120,7 @@ morph.numberOfFrames.invalid = Invalid number of frames. Enter positive integer arrow = \ud83e\udc06 frames.apng = APNG sprites.apng = APNG -morphshapes.apng = APNG \ No newline at end of file +morphshapes.apng = APNG + +#after 25.1.3 +buttons.svg_combined = SVG combined diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties index 96fe864e5..84283332d 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties @@ -117,4 +117,8 @@ morph.duration.seconds = s morph.numberOfFrames = Po\u010det sn\u00edmk\u016f prom\u011bny morph.duration.invalid = Neplatn\u00e1 d\u00e9lka prom\u011bny. Zadejte kladn\u00e9 desetinn\u00e9 \u010d\u00edslo ve vte\u0159in\u00e1ch. morph.numberOfFrames.invalid = Neplatn\u00fd po\u010det sn\u00edmk\u016f. Zadejte kladn\u00e9 cel\u00e9 \u010d\u00edslo >= 2. -arrow = \ud83e\udc06 \ No newline at end of file +arrow = \ud83e\udc06 + + +#after 25.1.3 +buttons.svg_combined = SVG kombinovan\u00e9 diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_de.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_de.properties index 27f87b79a..2a2af8eda 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_de.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_de.properties @@ -116,3 +116,6 @@ arrow = \ud83e\udc06 frames.apng = APNG sprites.apng = APNG morphshapes.apng = APNG + +#after 25.1.3 +buttons.svg_combined = SVG kombiniertes diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_sk.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_sk.properties index ecb3f91f1..7c6f82988 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_sk.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_sk.properties @@ -121,3 +121,6 @@ arrow = \ud83e\udc06 frames.apng = APNG sprites.apng = APNG morphshapes.apng = APNG + +#after 25.1.3 +buttons.svg_combined = SVG kombinovan\u00e9