From 16a27789e3c0a5ea4fb1dc88200bb21fdb8f3613 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 27 Dec 2015 10:19:27 +0100 Subject: [PATCH 01/14] swf.tags and sprite.subtags are private to avoid modifying the list without setting the isModified flag + some small chages --- .../flash/IdentifiersDeobfuscation.java | 4 +- .../decompiler/flash/ReadOnlyTagList.java | 62 +++++ .../src/com/jpexs/decompiler/flash/SWF.java | 249 ++++++++++++------ .../decompiler/flash/SWFInputStream.java | 2 +- .../decompiler/flash/SWFOutputStream.java | 2 +- .../com/jpexs/decompiler/flash/SWFSearch.java | 2 +- .../flash/exporters/BinaryDataExporter.java | 3 +- .../flash/exporters/FontExporter.java | 3 +- .../flash/exporters/FrameExporter.java | 22 +- .../flash/exporters/ImageExporter.java | 3 +- .../flash/exporters/MorphShapeExporter.java | 3 +- .../flash/exporters/MovieExporter.java | 5 +- .../flash/exporters/ShapeExporter.java | 3 +- .../flash/exporters/SoundExporter.java | 3 +- .../flash/exporters/SymbolClassExporter.java | 3 +- .../flash/exporters/TextExporter.java | 3 +- .../flash/importers/ImageImporter.java | 2 +- .../flash/importers/ShapeImporter.java | 75 +++--- .../flash/importers/SwfXmlImporter.java | 1 + .../flash/importers/SymbolClassImporter.java | 2 +- .../flash/tags/DefineButton2Tag.java | 41 +-- .../flash/tags/DefineButtonTag.java | 49 +--- .../decompiler/flash/tags/DefineFont2Tag.java | 2 +- .../decompiler/flash/tags/DefineFont3Tag.java | 8 +- .../decompiler/flash/tags/DefineFontTag.java | 4 +- .../flash/tags/DefineSpriteTag.java | 67 ++++- .../com/jpexs/decompiler/flash/tags/Tag.java | 11 +- .../decompiler/flash/tags/base/ButtonTag.java | 67 ++++- .../decompiler/flash/tags/base/FontTag.java | 6 +- .../flash/tags/gfx/DefineCompactedFont.java | 3 +- .../decompiler/flash/timeline/Timeline.java | 48 ++-- .../decompiler/flash/timeline/Timelined.java | 14 + .../decompiler/flash/xfl/XFLConverter.java | 79 +++--- .../decompiler/flash/ActionScript2Test.java | 2 +- .../flash/ActionScript2TestBase.java | 8 +- .../flash/ActionScript3ExecuteTest.java | 2 +- .../decompiler/flash/ActionScript3Test.java | 2 +- .../jpexs/decompiler/flash/RecompileTest.java | 2 +- .../flash/SwfXmlExportImportTest.java | 14 +- .../flash/generators/AS2Generator.java | 8 +- .../flash/generators/AS3Generator.java | 2 +- .../console/CommandLineArgumentParser.java | 37 +-- .../jpexs/decompiler/flash/gui/FontPanel.java | 8 +- .../flash/gui/LoadFromMemoryFrame.java | 2 +- .../jpexs/decompiler/flash/gui/MainPanel.java | 123 +++++---- .../decompiler/flash/gui/PreviewPanel.java | 8 +- .../flash/gui/debugger/DebuggerTools.java | 4 +- .../flash/gui/tagtree/TagTreeContextMenu.java | 10 +- .../flash/gui/tagtree/TagTreeModel.java | 4 +- .../flash/gui/AdobeFlashExecutor.java | 5 +- 50 files changed, 659 insertions(+), 433 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ReadOnlyTagList.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java index 232303cac..a84604f27 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java @@ -157,10 +157,10 @@ public class IdentifiersDeobfuscation { return sb.toString(); } - public void deobfuscateInstanceNames(boolean as3, HashMap namesMap, RenameType renameType, List tags, Map selected) { + public void deobfuscateInstanceNames(boolean as3, HashMap namesMap, RenameType renameType, Iterable tags, Map selected) { for (Tag t : tags) { if (t instanceof DefineSpriteTag) { - deobfuscateInstanceNames(as3, namesMap, renameType, ((DefineSpriteTag) t).subTags, selected); + deobfuscateInstanceNames(as3, namesMap, renameType, ((DefineSpriteTag) t).getTags(), selected); } if (t instanceof PlaceObjectTypeTag) { PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ReadOnlyTagList.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ReadOnlyTagList.java new file mode 100644 index 000000000..00c21dc5c --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ReadOnlyTagList.java @@ -0,0 +1,62 @@ +/* + * 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; + +import com.jpexs.decompiler.flash.tags.Tag; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class ReadOnlyTagList implements Iterable { + + public static final ReadOnlyTagList EMPTY = new ReadOnlyTagList(new ArrayList<>()); + + private final List list; + + public ReadOnlyTagList(List list) { + this.list = list; + } + + @Override + public Iterator iterator() { + return list.iterator(); + } + + public int size() { + return list.size(); + } + + public boolean isEmpty() { + return list.isEmpty(); + } + + public Tag get(int index) { + return list.get(index); + } + + public int indexOf(Tag tag) { + return list.indexOf(tag); + } + + public ArrayList toArrayList() { + return new ArrayList<>(list); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 340c6b803..ed8feeada 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -131,6 +131,7 @@ import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFField; import com.jpexs.decompiler.flash.types.filters.BlendComposite; import com.jpexs.decompiler.flash.types.filters.FILTER; import com.jpexs.decompiler.flash.xfl.FLAVersion; @@ -207,9 +208,12 @@ public final class SWF implements SWFContainerItem, Timelined { /** * Tags inside of file */ - public List tags = new ArrayList<>(); + @SWFField + private List tags = new ArrayList<>(); @Internal + public ReadOnlyTagList readOnlyTags; + public boolean hasEndTag = true; /** @@ -293,6 +297,9 @@ public final class SWF implements SWFContainerItem, Timelined { private static final Logger logger = Logger.getLogger(SWF.class.getName()); + @Internal + private boolean isModified; + @Internal private Timeline timeline; @@ -346,14 +353,16 @@ public final class SWF implements SWFContainerItem, Timelined { resetTimelines(this); updateCharacters(); - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag instanceof DefineSpriteTag) { DefineSpriteTag spriteTag = (DefineSpriteTag) tag; - for (Tag tag1 : spriteTag.subTags) { + for (Tag tag1 : spriteTag.getTags()) { tag1.setSwf(null); } - spriteTag.subTags.clear(); + for (int i = spriteTag.getTags().size() - 1; i >= 0; i--) { + spriteTag.removeTag(i); + } } if (tag instanceof DefineBinaryDataTag) { @@ -401,7 +410,7 @@ public final class SWF implements SWFContainerItem, Timelined { synchronized (this) { if (characters == null) { Map chars = new HashMap<>(); - parseCharacters(tags, chars); + parseCharacters(getTags(), chars); characters = Collections.unmodifiableMap(chars); } } @@ -415,7 +424,7 @@ public final class SWF implements SWFContainerItem, Timelined { synchronized (this) { if (dependentCharacters == null) { Map> dep = new HashMap<>(); - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag instanceof CharacterTag) { int characterId = ((CharacterTag) tag).getCharacterId(); Set needed = new HashSet<>(); @@ -529,7 +538,7 @@ public final class SWF implements SWFContainerItem, Timelined { synchronized (this) { if (abcList == null) { ArrayList newAbcList = new ArrayList<>(); - getAbcTags(tags, newAbcList); + getAbcTags(getTags(), newAbcList); abcList = newAbcList; } } @@ -544,7 +553,7 @@ public final class SWF implements SWFContainerItem, Timelined { } public MetadataTag getMetadata() { - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof MetadataTag) { return (MetadataTag) t; } @@ -554,7 +563,7 @@ public final class SWF implements SWFContainerItem, Timelined { } public FileAttributesTag getFileAttributes() { - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof FileAttributesTag) { return (FileAttributesTag) t; } @@ -564,7 +573,7 @@ public final class SWF implements SWFContainerItem, Timelined { } public SetBackgroundColorTag getBackgroundColor() { - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof SetBackgroundColorTag) { return (SetBackgroundColorTag) t; } @@ -574,7 +583,7 @@ public final class SWF implements SWFContainerItem, Timelined { } public EnableTelemetryTag getEnableTelemetry() { - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof EnableTelemetryTag) { return (EnableTelemetryTag) t; } @@ -597,7 +606,7 @@ public final class SWF implements SWFContainerItem, Timelined { if (jtt == null) { synchronized (this) { if (jtt == null) { - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof JPEGTablesTag) { jtt = (JPEGTablesTag) t; break; @@ -611,7 +620,7 @@ public final class SWF implements SWFContainerItem, Timelined { } public String getDocumentClass() { - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof SymbolClassTag) { SymbolClassTag sc = (SymbolClassTag) t; for (int i = 0; i < sc.tags.size(); i++) { @@ -672,7 +681,7 @@ public final class SWF implements SWFContainerItem, Timelined { public void resetTimelines(Timelined timelined) { timelined.resetTimeline(); if (timelined instanceof SWF) { - for (Tag t : ((SWF) timelined).tags) { + for (Tag t : ((SWF) timelined).getTags()) { if (t instanceof Timelined) { resetTimelines((Timelined) t); } @@ -680,7 +689,7 @@ public final class SWF implements SWFContainerItem, Timelined { } } - private void parseCharacters(List list, Map characters) { + private void parseCharacters(Iterable list, Map characters) { for (Tag t : list) { if (t instanceof CharacterTag) { int characterId = ((CharacterTag) t).getCharacterId(); @@ -693,7 +702,7 @@ public final class SWF implements SWFContainerItem, Timelined { } } if (t instanceof DefineSpriteTag) { - parseCharacters(((DefineSpriteTag) t).getSubTags(), characters); + parseCharacters(((DefineSpriteTag) t).getTags(), characters); } } } @@ -717,7 +726,7 @@ public final class SWF implements SWFContainerItem, Timelined { return false; } path.add(sprite.spriteId); - for (Tag t : sprite.subTags) { + for (Tag t : sprite.getTags()) { if (t instanceof DefineSpriteTag) { if (!isSpriteValid((DefineSpriteTag) t, path)) { return false; @@ -751,7 +760,7 @@ public final class SWF implements SWFContainerItem, Timelined { */ public List getTagData(int tagId) { List ret = new ArrayList<>(); - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag.getId() == tagId) { ret.add(tag); } @@ -817,7 +826,7 @@ public final class SWF implements SWFContainerItem, Timelined { sos.writeFIXED8(frameRate); sos.writeUI16(frameCount); - sos.writeTags(tags); + sos.writeTags(getTags()); if (hasEndTag) { sos.writeUI16(0); } @@ -940,7 +949,11 @@ public final class SWF implements SWFContainerItem, Timelined { @Override public boolean isModified() { - for (Tag tag : tags) { + if (isModified) { + return true; + } + + for (Tag tag : getTags()) { if (tag.isModified()) { return true; } @@ -948,14 +961,21 @@ public final class SWF implements SWFContainerItem, Timelined { return false; } + @Override + public void setModified(boolean value) { + isModified = value; + } + public void clearModified() { - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag.isModified()) { tag.createOriginalData(); tag.setModified(false); } } + isModified = false; + try { uncompressedData = saveToByteArray(); } catch (IOException ex) { @@ -1093,6 +1113,7 @@ public final class SWF implements SWFContainerItem, Timelined { hasEndTag = false; } this.tags = tags; + readOnlyTags = null; if (!checkOnly) { checkInvalidSprites(); updateCharacters(); @@ -1174,10 +1195,10 @@ public final class SWF implements SWFContainerItem, Timelined { return new Date(); } - private static void getAbcTags(List list, List actionScripts) { + private static void getAbcTags(Iterable list, List actionScripts) { for (Tag t : list) { if (t instanceof DefineSpriteTag) { - getAbcTags(((DefineSpriteTag) t).getSubTags(), actionScripts); + getAbcTags(((DefineSpriteTag) t).getTags(), actionScripts); } if (t instanceof ABCContainerTag) { actionScripts.add((ABCContainerTag) t); @@ -1187,7 +1208,7 @@ public final class SWF implements SWFContainerItem, Timelined { public void assignExportNamesToSymbols() { HashMap exportNames = new HashMap<>(); - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof ExportAssetsTag) { ExportAssetsTag eat = (ExportAssetsTag) t; for (int i = 0; i < eat.tags.size(); i++) { @@ -1199,7 +1220,7 @@ public final class SWF implements SWFContainerItem, Timelined { } } } - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof CharacterTag) { CharacterTag ct = (CharacterTag) t; if (exportNames.containsKey(ct.getCharacterId())) { @@ -1211,7 +1232,7 @@ public final class SWF implements SWFContainerItem, Timelined { public void assignClassesToSymbols() { HashMap classes = new HashMap<>(); - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof SymbolClassTag) { SymbolClassTag sct = (SymbolClassTag) t; for (int i = 0; i < sct.tags.size(); i++) { @@ -1221,7 +1242,7 @@ public final class SWF implements SWFContainerItem, Timelined { } } } - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof CharacterTag) { CharacterTag ct = (CharacterTag) t; if (classes.containsKey(ct.getCharacterId())) { @@ -1608,7 +1629,7 @@ public final class SWF implements SWFContainerItem, Timelined { public final void addEventListener(EventListener listener) { listeners.add(listener); - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof ABCContainerTag) { (((ABCContainerTag) t).getABC()).addEventListener(listener); } @@ -1617,7 +1638,7 @@ public final class SWF implements SWFContainerItem, Timelined { public final void removeEventListener(EventListener listener) { listeners.remove(listener); - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof ABCContainerTag) { (((ABCContainerTag) t).getABC()).removeEventListener(listener); } @@ -1630,13 +1651,13 @@ public final class SWF implements SWFContainerItem, Timelined { } } - public static void populateVideoFrames(int streamId, List tags, HashMap output) { + public static void populateVideoFrames(int streamId, Iterable tags, HashMap output) { for (Tag t : tags) { if (t instanceof VideoFrameTag) { output.put(((VideoFrameTag) t).frameNum, (VideoFrameTag) t); } if (t instanceof DefineSpriteTag) { - populateVideoFrames(streamId, ((DefineSpriteTag) t).getSubTags(), output); + populateVideoFrames(streamId, ((DefineSpriteTag) t).getTags(), output); } } } @@ -1907,7 +1928,7 @@ public final class SWF implements SWFContainerItem, Timelined { return ret; } - private void getVariables(List tags, String path, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { + private void getVariables(Iterable tags, String path, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { List processed = new ArrayList<>(); for (Tag t : tags) { String subPath = path + "/" + t.toString(); @@ -1921,7 +1942,7 @@ public final class SWF implements SWFContainerItem, Timelined { } } if (t instanceof DefineSpriteTag) { - getVariables(((DefineSpriteTag) t).getSubTags(), path + "/" + t.toString(), variables, actionsMap, functions, strings, usageTypes); + getVariables(((DefineSpriteTag) t).getTags(), path + "/" + t.toString(), variables, actionsMap, functions, strings, usageTypes); } } } @@ -1964,19 +1985,19 @@ public final class SWF implements SWFContainerItem, Timelined { } public int deobfuscateAS3Identifiers(RenameType renameType) { - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag instanceof ABCContainerTag) { ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, true); tag.setModified(true); } } - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag instanceof ABCContainerTag) { ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, false); tag.setModified(true); } } - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag instanceof SymbolClassTag) { SymbolClassTag sc = (SymbolClassTag) tag; for (int i = 0; i < sc.names.size(); i++) { @@ -1988,7 +2009,7 @@ public final class SWF implements SWFContainerItem, Timelined { sc.setModified(true); } } - deobfuscation.deobfuscateInstanceNames(true, deobfuscated, renameType, tags, new HashMap<>()); + deobfuscation.deobfuscateInstanceNames(true, deobfuscated, renameType, getTags(), new HashMap<>()); return deobfuscated.size(); } @@ -2026,7 +2047,7 @@ public final class SWF implements SWFContainerItem, Timelined { HashMap usageTypes = new HashMap<>(); int ret = 0; - getVariables(tags, "", allVariableNames, actionsMap, allFunctions, allStrings, usageTypes); + getVariables(getTags(), "", allVariableNames, actionsMap, allFunctions, allStrings, usageTypes); informListeners("rename", ""); int fc = 0; for (MyEntry it : allVariableNames) { @@ -2036,13 +2057,13 @@ public final class SWF implements SWFContainerItem, Timelined { informListeners("rename", "classes"); int classCount = 0; - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof DoInitActionTag) { classCount++; } } int cnt = 0; - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof DoInitActionTag) { cnt++; informListeners("rename", "class " + cnt + "/" + classCount); @@ -2252,7 +2273,7 @@ public final class SWF implements SWFContainerItem, Timelined { src.setModified(); } - deobfuscation.deobfuscateInstanceNames(false, deobfuscated, renameType, tags, selected); + deobfuscation.deobfuscateInstanceNames(false, deobfuscated, renameType, getTags(), selected); return ret; } @@ -2303,7 +2324,7 @@ public final class SWF implements SWFContainerItem, Timelined { public void clearImageCache() { frameCache.clear(); rectCache.clear(); - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag instanceof ImageTag) { ((ImageTag) tag).clearCache(); } @@ -2317,10 +2338,20 @@ public final class SWF implements SWFContainerItem, Timelined { IdentifiersDeobfuscation.clearCache(); } + public void clearReadOnlyListCache() { + readOnlyTags = null; + for (Tag tag : tags) { + if (tag instanceof DefineSpriteTag) { + ((DefineSpriteTag) tag).clearReadOnlyListCache(); + } + } + } + public void clearAllCache() { characters = null; abcList = null; timeline = null; + clearReadOnlyListCache(); clearImageCache(); clearScriptCache(); Cache.clearAll(); @@ -2876,13 +2907,15 @@ public final class SWF implements SWFContainerItem, Timelined { private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline) { Map stage = new HashMap<>(); Set dependingChars = new HashSet<>(); + Timelined timelined = timeline.timelined; + ReadOnlyTagList tags = timelined.getTags(); if (toRemove instanceof CharacterTag) { int characterId = ((CharacterTag) toRemove).getCharacterId(); if (characterId != 0) { dependingChars.add(characterId); - for (int i = 0; i < timeline.tags.size(); i++) { - Tag t = timeline.tags.get(i); + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); if (t instanceof CharacterIdTag) { CharacterIdTag c = (CharacterIdTag) t; Set needed = new HashSet<>(); @@ -2895,8 +2928,8 @@ public final class SWF implements SWFContainerItem, Timelined { } } - for (int i = 0; i < timeline.tags.size(); i++) { - Tag t = timeline.tags.get(i); + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); if (t instanceof RemoveTag) { RemoveTag rt = (RemoveTag) t; int depth = rt.getDepth(); @@ -2904,7 +2937,7 @@ public final class SWF implements SWFContainerItem, Timelined { int currentCharId = stage.get(depth); stage.remove(depth); if (dependingChars.contains(currentCharId)) { - timeline.tags.remove(i); + timelined.removeTag(i); i--; continue; } @@ -2917,7 +2950,7 @@ public final class SWF implements SWFContainerItem, Timelined { if (placeCharId != 0) { stage.put(depth, placeCharId); if (dependingChars.contains(placeCharId)) { - timeline.tags.remove(i); + timelined.removeTag(i); i--; continue; } @@ -2926,7 +2959,7 @@ public final class SWF implements SWFContainerItem, Timelined { if (t instanceof CharacterIdTag) { CharacterIdTag c = (CharacterIdTag) t; if (dependingChars.contains(c.getCharacterId())) { - timeline.tags.remove(i); + timelined.removeTag(i); i--; continue; } @@ -2935,13 +2968,13 @@ public final class SWF implements SWFContainerItem, Timelined { t.getNeededCharacters(needed); for (int dep : dependingChars) { if (needed.contains(dep)) { - timeline.tags.remove(i); + timelined.removeTag(i); i--; //continue; } } if (t == toRemove) { - timeline.tags.remove(i); + timelined.removeTag(i); i--; continue; } @@ -2958,10 +2991,12 @@ public final class SWF implements SWFContainerItem, Timelined { characterId = ((CharacterTag) toRemove).getCharacterId(); modified = timeline.removeCharacter(characterId); } - for (int i = 0; i < timeline.tags.size(); i++) { - Tag t = timeline.tags.get(i); + Timelined timelined = timeline.timelined; + ReadOnlyTagList tags = timelined.getTags(); + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); if (t == toRemove) { - timeline.tags.remove(t); + timelined.removeTag(t); i--; continue; } @@ -3002,6 +3037,16 @@ public final class SWF implements SWFContainerItem, Timelined { clearImageCache(); } + public void removeTag(int index) { + setModified(true); + tags.remove(index); + } + + public void removeTag(Tag tag) { + setModified(true); + tags.remove(tag); + } + public void removeTag(Tag tag, boolean removeDependencies) { Timelined timelined = tag.getTimelined(); removeTagInternal(timelined, tag, removeDependencies); @@ -3012,39 +3057,75 @@ public final class SWF implements SWFContainerItem, Timelined { private void removeTagInternal(Timelined timelined, Tag tag, boolean removeDependencies) { if (tag instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(tag.getId())) { - List tags; - if (timelined instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) timelined; - tags = sprite.getSubTags(); - } else { - tags = this.tags; - } - tags.remove(tag); - if (timelined instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) timelined; - sprite.setModified(true); - } + timelined.removeTag(tag); + timelined.setModified(true); timelined.resetTimeline(); } else { // timeline should be always the swf here if (removeDependencies) { removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); - if (timelined instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) timelined; - sprite.setModified(true); - } + timelined.setModified(true); } else { - removeTagFromTimeline(tag, timelined.getTimeline()); + boolean modified = removeTagFromTimeline(tag, timelined.getTimeline()); + if (modified) { + timelined.setModified(true); + } } } } + @Override + public ReadOnlyTagList getTags() { + if (readOnlyTags == null) { + readOnlyTags = new ReadOnlyTagList(tags); + } + + return readOnlyTags; + } + + /** + * Adds a tag to the SWF + * + * @param tag + */ + @Override + public void addTag(Tag tag) { + setModified(true); + tags.add(tag); + } + + /** + * Adds a tag to the SWF + * + * @param index + * @param tag + */ + @Override + public void addTag(int index, Tag tag) { + setModified(true); + tags.add(index, tag); + } + + /** + * Replaces a tag in the SWF + * + * @param oldTag + * @param newTag + */ + public void replaceTag(Tag oldTag, Tag newTag) { + setModified(true); + int index = tags.indexOf(oldTag); + if (index != -1) { + tags.set(index, newTag); + } + } + /** * Adds a tag to the SWF * If targetTreeItem is: * - Frame: adds the tag to the Frame. Frame can be a frame of the main * timeline or a DefineSprite frame - * - DefineSprite: adds the tag to the and of the DefineSprite's tag list + * - DefineSprite: adds the tag to the end of the DefineSprite's tag list * - Any other tag in the SWF: adds the new tag exactly before the specified * tag * - Other: adds the tag to the end of the SWF's tag list @@ -3064,13 +3145,7 @@ public final class SWF implements SWFContainerItem, Timelined { tag.setTimelined(timelined); - List tags; - if (timelined instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) timelined; - tags = sprite.subTags; - } else { - tags = swf.tags; - } + ReadOnlyTagList tags = timelined.getTags(); int index; if (frame != null) { @@ -3082,11 +3157,11 @@ public final class SWF implements SWFContainerItem, Timelined { } else if (timelined instanceof DefineSpriteTag) { index = -1; } else if (targetTreeItem instanceof Tag) { - if (tag instanceof CharacterIdTag && targetTreeItem instanceof CharacterTag) { + if (tag instanceof CharacterIdTag && !(tag instanceof CharacterTag) && targetTreeItem instanceof CharacterTag) { ((CharacterIdTag) tag).setCharacterId(((CharacterTag) targetTreeItem).getCharacterId()); } - index = tags.indexOf(targetTreeItem); // todo: honfika: why not index + 1? + index = tags.indexOf((Tag) targetTreeItem); // todo: honfika: why not index + 1? } else { index = -1; if (tag instanceof CharacterTag) { @@ -3101,9 +3176,9 @@ public final class SWF implements SWFContainerItem, Timelined { } if (index > -1) { - tags.add(index, tag); + timelined.addTag(index, tag); } else { - tags.add(tag); + timelined.addTag(tag); } timelined.resetTimeline(); @@ -3147,7 +3222,7 @@ public final class SWF implements SWFContainerItem, Timelined { int maxId = Math.max(tags.size(), getNextCharacterId()); int id = maxId; // first set the chatacter ids to surely not used ids - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag instanceof CharacterTag) { CharacterTag characterTag = (CharacterTag) tag; replaceCharacter(characterTag.getCharacterId(), id++); @@ -3155,7 +3230,7 @@ public final class SWF implements SWFContainerItem, Timelined { } // then set them to 1,2,3... id = 1; - for (Tag tag : tags) { + for (Tag tag : getTags()) { if (tag instanceof CharacterTag) { CharacterTag characterTag = (CharacterTag) tag; replaceCharacter(characterTag.getCharacterId(), id++); @@ -3165,7 +3240,7 @@ public final class SWF implements SWFContainerItem, Timelined { public boolean replaceCharacter(int oldCharacterId, int newCharacterId) { boolean modified = false; - for (Tag tag : tags) { + for (Tag tag : getTags()) { boolean modified2 = false; if (tag instanceof CharacterIdTag) { CharacterIdTag characterIdTag = (CharacterIdTag) tag; @@ -3343,7 +3418,7 @@ public final class SWF implements SWFContainerItem, Timelined { * @return the tag or null if not found */ public DebugIDTag getDebugId() { - for (Tag t : tags) { + for (Tag t : getTags()) { if (t instanceof DebugIDTag) { return (DebugIDTag) t; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java index 460ffce4b..a314cd201 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -1081,7 +1081,7 @@ public class SWFInputStream implements AutoCloseable { // out.println(Utils.formatHex((int)tag.getPos(), 8) + ": " + Utils.indent(level, "") + Utils.format(tag.toString(), 25 - 2*level) + " tagId="+tag.getId()+" len="+tag.getOrigDataLength()+": "+Utils.bytesToHexString(64, tag.getData(version), 0)); if (tag instanceof DefineSpriteTag) { int i = 0; - for (Tag subTag : ((DefineSpriteTag) tag).getSubTags()) { + for (Tag subTag : ((DefineSpriteTag) tag).getTags()) { dumpTag(out, subTag, i++, level + 1); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java index c91d9e83f..41c084ce3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -506,7 +506,7 @@ public class SWFOutputStream extends OutputStream { * @param tags List of tag values * @throws IOException */ - public void writeTags(List tags) throws IOException { + public void writeTags(Iterable tags) throws IOException { for (Tag tag : tags) { tag.writeTag(this); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFSearch.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFSearch.java index ef6ee2f9c..c81b599b6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFSearch.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFSearch.java @@ -93,7 +93,7 @@ public class SWFSearch { SWF swf = noCheck ? new SWF(pmi) : new SWF(pmi, null, null, null, false, true, true); boolean valid = swf.fileSize > 0 && swf.version > 0 - && (!swf.tags.isEmpty() || noCheck) + && (!swf.getTags().isEmpty() || noCheck) && swf.version <= SWF.MAX_VERSION; if (valid) { long limit = pmi.getPos(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/BinaryDataExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/BinaryDataExporter.java index 5d6c8adf6..7a5b622af 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/BinaryDataExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/BinaryDataExporter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.exporters; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; @@ -38,7 +39,7 @@ import java.util.List; */ public class BinaryDataExporter { - public List exportBinaryData(AbortRetryIgnoreHandler handler, String outdir, List tags, BinaryDataExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportBinaryData(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, BinaryDataExportSettings settings, EventListener evl) throws IOException, InterruptedException { List ret = new ArrayList<>(); if (tags.isEmpty()) { return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FontExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FontExporter.java index d14c41969..dba60a7b2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FontExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FontExporter.java @@ -23,6 +23,7 @@ import com.google.typography.font.tools.conversion.woff.WoffWriter; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.ApplicationInfo; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; @@ -57,7 +58,7 @@ import java.util.logging.Logger; */ public class FontExporter { - public List exportFonts(AbortRetryIgnoreHandler handler, String outdir, List tags, final FontExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportFonts(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final FontExportSettings settings, EventListener evl) throws IOException, InterruptedException { List ret = new ArrayList<>(); if (tags.isEmpty()) { return ret; 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 101938446..e8f624806 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 @@ -34,6 +34,7 @@ import com.jpexs.decompiler.flash.helpers.BMPFile; import com.jpexs.decompiler.flash.helpers.ImageHelper; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.timeline.DepthState; @@ -144,7 +145,7 @@ public class FrameExporter { public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List frames, final FrameExportSettings settings, final EventListener evl) throws IOException, InterruptedException { final List ret = new ArrayList<>(); - if (swf.tags.isEmpty()) { + if (swf.getTags().isEmpty()) { return ret; } Timeline tim0; @@ -180,7 +181,8 @@ public class FrameExporter { if (settings.mode == FrameExportMode.SVG) { for (int i = 0; i < frames.size(); i++) { if (evl != null) { - evl.handleExportingEvent("frame", i + 1, frames.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); + Tag parentTag = tim.getParentTag(); + evl.handleExportingEvent("frame", i + 1, frames.size(), parentTag == null ? "" : parentTag.getName()); } final int fi = i; @@ -205,7 +207,8 @@ public class FrameExporter { }, handler).run(); if (evl != null) { - evl.handleExportedEvent("frame", i + 1, frames.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); + Tag parentTag = tim.getParentTag(); + evl.handleExportedEvent("frame", i + 1, frames.size(), parentTag == null ? "" : parentTag.getName()); } } @@ -214,7 +217,8 @@ public class FrameExporter { if (settings.mode == FrameExportMode.CANVAS) { if (evl != null) { - evl.handleExportingEvent("canvas", 1, 1, tim.parentTag == null ? "" : tim.parentTag.getName()); + Tag parentTag = tim.getParentTag(); + evl.handleExportingEvent("canvas", 1, 1, parentTag == null ? "" : parentTag.getName()); } final Timeline ftim = tim; @@ -319,7 +323,8 @@ public class FrameExporter { }, handler).run(); if (evl != null) { - evl.handleExportedEvent("canvas", 1, 1, tim.parentTag == null ? "" : tim.parentTag.getName()); + Tag parentTag = tim.getParentTag(); + evl.handleExportedEvent("canvas", 1, 1, parentTag == null ? "" : parentTag.getName()); } return ret; } @@ -346,14 +351,17 @@ public class FrameExporter { return null; } + Tag parentTag = tim.getParentTag(); + String tagName = parentTag == null ? "" : parentTag.getName(); + if (evl != null) { - evl.handleExportingEvent("frame", pos + 1, fframes.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); + evl.handleExportingEvent("frame", pos + 1, fframes.size(), tagName); } BufferedImage result = SWF.frameToImageGet(ftim, fframes.get(pos++), 0, null, 0, ftim.displayRect, new Matrix(), new ColorTransform(), fbackgroundColor, false, settings.zoom).getBufferedImage(); if (evl != null) { - evl.handleExportedEvent("frame", pos, fframes.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); + evl.handleExportedEvent("frame", pos, fframes.size(), tagName); } return result; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java index cb7f0aead..75000719f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.exporters; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode; import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings; @@ -42,7 +43,7 @@ import java.util.List; */ public class ImageExporter { - public List exportImages(AbortRetryIgnoreHandler handler, String outdir, List tags, ImageExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportImages(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, ImageExportSettings settings, EventListener evl) throws IOException, InterruptedException { List ret = new ArrayList<>(); if (tags.isEmpty()) { return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java index acaa6730f..101b46e74 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.exporters; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; @@ -51,7 +52,7 @@ import java.util.Set; public class MorphShapeExporter { //TODO: implement morphshape export. How to handle 65536 frames? - public List exportMorphShapes(AbortRetryIgnoreHandler handler, final String outdir, List tags, final MorphShapeExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportMorphShapes(AbortRetryIgnoreHandler handler, final String outdir, ReadOnlyTagList tags, final MorphShapeExportSettings settings, EventListener evl) throws IOException, InterruptedException { List ret = new ArrayList<>(); if (tags.isEmpty()) { return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MovieExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MovieExporter.java index 01b7c109e..bacbe617a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MovieExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MovieExporter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.exporters; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; @@ -49,7 +50,7 @@ import java.util.List; */ public class MovieExporter { - public List exportMovies(AbortRetryIgnoreHandler handler, String outdir, List tags, final MovieExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportMovies(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final MovieExportSettings settings, EventListener evl) throws IOException, InterruptedException { List ret = new ArrayList<>(); if (tags.isEmpty()) { return ret; @@ -97,7 +98,7 @@ public class MovieExporter { public byte[] exportMovie(DefineVideoStreamTag videoStream, MovieExportMode mode) throws IOException { SWF swf = videoStream.getSwf(); HashMap frames = new HashMap<>(); - SWF.populateVideoFrames(videoStream.characterID, swf.tags, frames); + SWF.populateVideoFrames(videoStream.characterID, swf.getTags(), frames); if (frames.isEmpty()) { return SWFInputStream.BYTE_ARRAY_EMPTY; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java index 2d50093e6..7db211829 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.exporters; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; @@ -57,7 +58,7 @@ import java.util.Set; */ public class ShapeExporter { - public List exportShapes(AbortRetryIgnoreHandler handler, final String outdir, List tags, final ShapeExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportShapes(AbortRetryIgnoreHandler handler, final String outdir, ReadOnlyTagList tags, final ShapeExportSettings settings, EventListener evl) throws IOException, InterruptedException { List ret = new ArrayList<>(); if (tags.isEmpty()) { return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java index 59b8450c4..4d6489b51 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.exporters; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; @@ -52,7 +53,7 @@ import java.util.List; */ public class SoundExporter { - public List exportSounds(AbortRetryIgnoreHandler handler, String outdir, List tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportSounds(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException { List ret = new ArrayList<>(); if (tags.isEmpty()) { return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SymbolClassExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SymbolClassExporter.java index 3d2f79c7d..ccdd90f00 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SymbolClassExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SymbolClassExporter.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.exporters; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.tags.ExportAssetsTag; import com.jpexs.decompiler.flash.tags.SymbolClassTag; import com.jpexs.decompiler.flash.tags.Tag; @@ -39,7 +40,7 @@ public class SymbolClassExporter { public static final String SYMBOL_CLASS_EXPORT_FILENAME = "symbols.csv"; - public List exportNames(final String outdir, List tags, EventListener evl) throws IOException { + public List exportNames(final String outdir, ReadOnlyTagList tags, EventListener evl) throws IOException { List ret = new ArrayList<>(); int count = 0; for (Tag t : tags) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/TextExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/TextExporter.java index da936ebc2..7e5feb0a8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/TextExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/TextExporter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.exporters; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; @@ -48,7 +49,7 @@ public class TextExporter { public static final String TEXT_EXPORT_FILENAME_PLAIN = "textsplain.txt"; - public List exportTexts(AbortRetryIgnoreHandler handler, String outdir, List tags, final TextExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportTexts(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final TextExportSettings settings, EventListener evl) throws IOException, InterruptedException { List ret = new ArrayList<>(); if (tags.isEmpty()) { return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java index f2b19f54f..61b61f7f6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java @@ -96,7 +96,7 @@ public class ImageImporter extends TagImporter { } imageTag.setModified(true); - swf.tags.set(swf.tags.indexOf(it), imageTag); + swf.replaceTag(it, imageTag); swf.updateCharacters(); return imageTag; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java index e5db7d64f..35c03ec4c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.importers; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.ShapeExporter; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; @@ -95,7 +96,9 @@ public class ShapeImporter { private final Set shownWarnings = new HashSet<>(); - private final SvgColor TRANSPARENT = new SvgColor(new Color(0, true)); + private final Color TRANSPARENT = new Color(0, true); + + private final SvgColor SVG_TRANSPARENT = new SvgColor(TRANSPARENT); private ShapeTag shapeTag; @@ -169,13 +172,7 @@ public class ShapeImporter { throw new Error("Unsupported image type tag."); } - int idx = swf.tags.indexOf(st); - if (idx != -1) { - swf.tags.add(idx, imageTag); - } else { - swf.tags.add(imageTag); - } - + swf.addTag(imageTag, st); swf.updateCharacters(); return imageTag; } @@ -518,7 +515,6 @@ public class ShapeImporter { serz.command = 'Z'; x = startPoint.x; y = startPoint.y; - serz.params = new double[]{x, y}; pathCommands.add(serz); break; case 'L': @@ -821,13 +817,10 @@ public class ShapeImporter { double sqrt2RYHalf = Math.sqrt(2) * ry / 2; double sqrt2Minus1RY = (Math.sqrt(2) - 1) * ry; - Point startPoint = new Point(0, 0); List pathCommands = new ArrayList<>(); PathCommand scr = new PathCommand(); scr.command = 'M'; scr.params = new double[]{cx + rx, cy}; - startPoint.x = cx + rx; - startPoint.y = cy; pathCommands.add(scr); double[] points = new double[]{ @@ -871,16 +864,12 @@ public class ShapeImporter { 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 void processRect(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { - - Point startPoint = new Point(0, 0); - String attr = childElement.getAttribute("x"); double x = attr.length() > 0 ? Double.parseDouble(attr) : 0; @@ -920,8 +909,6 @@ public class ShapeImporter { scr.command = 'M'; scr.params = new double[]{x + width, y + ry}; pathCommands.add(scr); - startPoint.x = x + width; - startPoint.y = y + ry; double sqrt2RXHalf = Math.sqrt(2) * rx / 2; double sqrt2Minus1RX = (Math.sqrt(2) - 1) * rx; @@ -969,8 +956,6 @@ public class ShapeImporter { PathCommand scr = new PathCommand(); scr.command = 'M'; scr.params = new double[]{x, y}; - startPoint.x = x; - startPoint.y = y; pathCommands.add(scr); double[] points = new double[]{ @@ -990,7 +975,6 @@ public class ShapeImporter { PathCommand serz = new PathCommand(); serz.command = 'Z'; - serz.params = new double[]{startPoint.x, startPoint.y}; pathCommands.add(serz); processCommands(shapeNum, shapes, pathCommands, transform, style); @@ -1036,7 +1020,6 @@ public class ShapeImporter { String data = childElement.getAttribute("points"); char command = 'M'; - Point startPoint = new Point(0, 0); double x0 = 0; double y0 = 0; @@ -1057,7 +1040,6 @@ public class ShapeImporter { scr.params = new double[]{x, y}; pathCommands.add(scr); - startPoint = new Point(x, y); break; case 'L': PathCommand serl = new PathCommand(); @@ -1077,7 +1059,6 @@ public class ShapeImporter { if (close) { PathCommand serz = new PathCommand(); serz.command = 'Z'; - serz.params = new double[]{startPoint.x, startPoint.y}; pathCommands.add(serz); } @@ -1101,13 +1082,13 @@ public class ShapeImporter { SWF swf = new SWF(); DefineShape4Tag st = new DefineShape4Tag(swf); st = (DefineShape4Tag) (new ShapeImporter().importSvg(st, svgDataS)); - swf.tags.add(st); + swf.addTag(st); SerializableImage si = new SerializableImage(800, 600, BufferedImage.TYPE_4BYTE_ABGR); BitmapExporter.export(swf, st.shapes, Color.yellow, si, new Matrix(), new ColorTransform()); List li = new ArrayList<>(); li.add(st); ImageIO.write(si.getBufferedImage(), "PNG", new File(name + ".imported.png")); - new ShapeExporter().exportShapes(null, "./outex/", li, new ShapeExportSettings(ShapeExportMode.SVG, 1), null); + new ShapeExporter().exportShapes(null, "./outex/", new ReadOnlyTagList(li), new ShapeExportSettings(ShapeExportMode.SVG, 1), null); } //Test for SVG @@ -1641,11 +1622,11 @@ public class ShapeImporter { } private Color parseColor(String rgbStr) { - SvgFill fill = parseFill(new HashMap<>(), rgbStr); + SvgFill fill = parseFill(new HashMap<>(), rgbStr, null); return fill.toColor(); } - private SvgFill parseFill(Map idMap, String rgbStr) { + private SvgFill parseFill(Map idMap, String rgbStr, SvgStyle style) { if (rgbStr == null) { return null; } @@ -1653,7 +1634,7 @@ public class ShapeImporter { // named colors from: http://www.w3.org/TR/SVG/types.html#ColorKeywords switch (rgbStr) { case "none": - return TRANSPARENT; + return SVG_TRANSPARENT; case "aliceblue": return new SvgColor(240, 248, 255); case "antiquewhite": @@ -1959,11 +1940,11 @@ public class ShapeImporter { if (e != null) { String tagName = e.getTagName(); if ("linearGradient".equals(tagName)) { - return parseGradient(idMap, e, new SvgStyle()); //? new style + return parseGradient(idMap, e, new SvgStyle(style)); //? new style } if ("radialGradient".equals(tagName)) { - return parseGradient(idMap, e, new SvgStyle()); //? new style + return parseGradient(idMap, e, new SvgStyle(style)); //? new style } if ("pattern".equals(tagName)) { @@ -2168,6 +2149,8 @@ public class ShapeImporter { class SvgStyle implements Cloneable { + public Color color; + public SvgFill fill; public double opacity; @@ -2190,6 +2173,8 @@ public class ShapeImporter { public double strokeMiterLimit; + public SvgStyle parentStyle; + public SvgStyle() { fill = new SvgColor(Color.black); fillOpacity = 1; @@ -2204,6 +2189,11 @@ public class ShapeImporter { strokeMiterLimit = 4; } + public SvgStyle(SvgStyle parentStyle) { + this(); + this.parentStyle = parentStyle; + } + public SvgFill getFillWithOpacity() { if (fill == null) { return null; @@ -2272,15 +2262,26 @@ public class ShapeImporter { } switch (name) { + case "color": { + Color color = parseColor(value); + if (color != null) { + style.color = color == TRANSPARENT ? null : color; + } + } + break; case "fill": { - SvgFill fill = parseFill(idMap, value); + SvgFill fill = parseFill(idMap, value, style); if (fill != null) { - style.fill = fill == TRANSPARENT ? null : fill; + style.fill = fill == SVG_TRANSPARENT ? null : fill; } } break; case "stop-color": { - if ("inherit".equals(value) || "currentColor".equals(value)) { + if ("currentColor".equals(value)) { + if (style.parentStyle != null) { + style.stopColor = style.parentStyle.color; + } + } else if ("inherit".equals(value)) { showWarning(value + "StopColorNotSupported", "The stop color value '" + value + "' is not supported."); } else { style.stopColor = parseColor(value); @@ -2302,9 +2303,9 @@ public class ShapeImporter { } break; case "stroke": { - SvgFill strokeFill = parseFill(idMap, value); + SvgFill strokeFill = parseFill(idMap, value, style); if (strokeFill != null) { - style.strokeFill = strokeFill == TRANSPARENT ? null : strokeFill; + style.strokeFill = strokeFill == SVG_TRANSPARENT ? null : strokeFill; } } break; @@ -2362,7 +2363,7 @@ public class ShapeImporter { SvgStyle result = clone(); String[] styles = new String[]{ - "fill", "fill-opacity", + "color", "fill", "fill-opacity", "stroke", "stroke-width", "stroke-opacity", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "opacity", "stop-color", "stop-opacity" }; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java index ef0a0f561..d6593d501 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java @@ -142,6 +142,7 @@ public class SwfXmlImporter { DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); Document doc = docBuilder.parse(new InputSource(new StringReader(xml))); processElement(doc.getDocumentElement(), swf, swf, null); + swf.clearAllCache(); } catch (ParserConfigurationException | SAXException ex) { logger.log(Level.SEVERE, null, ex); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SymbolClassImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SymbolClassImporter.java index 0d2fe6ae5..28dd5d26c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SymbolClassImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SymbolClassImporter.java @@ -42,7 +42,7 @@ public class SymbolClassImporter { nameMap.put(characterId, name); } - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof ExportAssetsTag) { ExportAssetsTag eat = (ExportAssetsTag) tag; for (int i = 0; i < eat.tags.size(); i++) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java index 7a97708bb..d8c3543e7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java @@ -80,12 +80,6 @@ public class DefineButton2Tag extends ButtonTag implements ASMSourceContainer { */ public List actions = new ArrayList<>(); - private Timeline timeline; - - private boolean isSingleFrameInitialized; - - private boolean isSingleFrame; - /** * Constructor * @@ -258,40 +252,7 @@ public class DefineButton2Tag extends ButtonTag implements ASMSourceContainer { } @Override - public boolean isSingleFrame() { - if (!isSingleFrameInitialized) { - initialiteIsSingleFrame(); - } - return isSingleFrame; - } - - private synchronized void initialiteIsSingleFrame() { - if (!isSingleFrameInitialized) { - isSingleFrame = getTimeline().isSingleFrame(); - isSingleFrameInitialized = true; - } - } - - @Override - public Timeline getTimeline() { - if (timeline != null) { - return timeline; - } - - timeline = new Timeline(swf, this, new ArrayList<>(), buttonId, getRect()); - initTimeline(timeline); - return timeline; - } - - @Override - public void resetTimeline() { - if (timeline != null) { - timeline.reset(swf, this, new ArrayList<>(), buttonId, getRect()); - initTimeline(timeline); - } - } - - private void initTimeline(Timeline timeline) { + protected void initTimeline(Timeline timeline) { int maxDepth = 0; Frame frameUp = new Frame(timeline, 0); Frame frameDown = new Frame(timeline, 0); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index 696baf0ed..8d2d0bee6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -78,12 +78,6 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { @HideInRawEdit public ByteArrayRange actionBytes; - private Timeline timeline; - - private boolean isSingleFrameInitialized; - - private boolean isSingleFrame; - private String scriptName = "-"; @Override @@ -322,21 +316,6 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { return 1; } - @Override - public boolean isSingleFrame() { - if (!isSingleFrameInitialized) { - initialiteIsSingleFrame(); - } - return isSingleFrame; - } - - private synchronized void initialiteIsSingleFrame() { - if (!isSingleFrameInitialized) { - isSingleFrame = getTimeline().isSingleFrame(); - isSingleFrameInitialized = true; - } - } - @Override public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { return writer; @@ -358,27 +337,9 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { } @Override - public Timeline getTimeline() { - if (timeline != null) { - return timeline; - } - - timeline = new Timeline(swf, this, new ArrayList<>(), buttonId, getRect()); - initTimeline(timeline); - return timeline; - } - - @Override - public void resetTimeline() { - if (timeline != null) { - timeline.reset(swf, this, new ArrayList<>(), buttonId, getRect()); - initTimeline(timeline); - } - } - - private void initTimeline(Timeline timeline) { + protected void initTimeline(Timeline timeline) { ColorTransform clrTrans = null; - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DefineButtonCxformTag) { DefineButtonCxformTag cx = (DefineButtonCxformTag) t; clrTrans = cx.buttonColorTransform; @@ -417,17 +378,23 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { } timeline.addFrame(frameUp); + if (frameOver.layers.isEmpty()) { frameOver = frameUp; } + timeline.addFrame(frameOver); + if (frameDown.layers.isEmpty()) { frameDown = frameOver; } + timeline.addFrame(frameDown); + if (frameHit.layers.isEmpty()) { frameHit = frameUp; } + timeline.addFrame(frameHit); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java index ba64d8b1c..8e85b96d9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java @@ -466,7 +466,7 @@ public class DefineFont2Tag extends FontTag { } @Override - public String getCharacters(List tags) { + public String getCharacters() { StringBuilder ret = new StringBuilder(); for (int i : codeTable) { ret.append((char) i); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java index d9dde1ce2..5847c35ff 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java @@ -396,12 +396,12 @@ public class DefineFont3Tag extends FontTag { public void addCharacter(char character, Font font) { //Font Align Zones will be removed as adding new character zones is not supported:-( - for (int i = 0; i < swf.tags.size(); i++) { - Tag t = swf.tags.get(i); + for (int i = 0; i < swf.getTags().size(); i++) { + Tag t = swf.getTags().get(i); if (t instanceof DefineFontAlignZonesTag) { DefineFontAlignZonesTag fa = (DefineFontAlignZonesTag) t; if (fa.fontID == fontId) { - swf.tags.remove(i); + swf.removeTag(t); i--; } } @@ -467,7 +467,7 @@ public class DefineFont3Tag extends FontTag { } @Override - public String getCharacters(List tags) { + public String getCharacters() { StringBuilder ret = new StringBuilder(); for (int i : codeTable) { ret.append((char) i); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java index 6bf46e9bb..412c866c3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java @@ -139,7 +139,7 @@ public class DefineFontTag extends FontTag { private void ensureFontInfo() { if (fontInfoTag == null) { - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DefineFontInfoTag) { if (((DefineFontInfoTag) t).fontId == fontId) { fontInfoTag = (DefineFontInfoTag) t; @@ -331,7 +331,7 @@ public class DefineFontTag extends FontTag { } @Override - public String getCharacters(List tags) { + public String getCharacters() { StringBuilder ret = new StringBuilder(); ensureFontInfo(); if (fontInfoTag != null) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java index b6dff339b..aca45716b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.tags; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; @@ -33,6 +34,8 @@ import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFField; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; @@ -74,7 +77,11 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli /** * A series of tags */ - public List subTags; + @SWFField + private List subTags; + + @Internal + public ReadOnlyTagList readOnlyTags; public boolean hasEndTag; @@ -121,6 +128,7 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli subTags.remove(subTags.size() - 1); } this.subTags = subTags; + readOnlyTags = null; } /** @@ -133,7 +141,7 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli public void getData(SWFOutputStream sos) throws IOException { sos.writeUI16(spriteId); sos.writeUI16(frameCount); - sos.writeTags(subTags); + sos.writeTags(getTags()); if (hasEndTag) { sos.writeUI16(0); } @@ -142,7 +150,7 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli @Override public Timeline getTimeline() { if (timeline == null) { - timeline = new Timeline(swf, this, subTags, spriteId, getRect()); + timeline = new Timeline(swf, this, spriteId, getRect()); } return timeline; } @@ -150,7 +158,7 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli @Override public void resetTimeline() { if (timeline != null) { - timeline.reset(swf, this, subTags, spriteId, getRect()); + timeline.reset(swf, this, spriteId, getRect()); } } @@ -208,7 +216,7 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli ret = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); HashMap depthMap = new HashMap<>(); boolean foundSomething = false; - for (Tag t : subTags) { + for (Tag t : getTags()) { MATRIX m = null; int characterId = -1; if (t instanceof PlaceObjectTypeTag) { @@ -268,14 +276,10 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli return ret; } - public List getSubTags() { - return subTags; - } - @Override public void setModified(boolean value) { if (!value) { - for (Tag subTag : subTags) { + for (Tag subTag : getTags()) { subTag.setModified(false); } } @@ -283,17 +287,50 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli super.setModified(value); } + @Override + public ReadOnlyTagList getTags() { + if (readOnlyTags == null) { + readOnlyTags = new ReadOnlyTagList(subTags); + } + + return readOnlyTags; + } + + @Override + public void removeTag(int index) { + setModified(true); + subTags.remove(index); + } + + @Override + public void removeTag(Tag tag) { + setModified(true); + subTags.remove(tag); + } + + @Override + public void addTag(Tag tag) { + setModified(true); + subTags.add(tag); + } + + @Override + public void addTag(int index, Tag tag) { + setModified(true); + subTags.add(index, tag); + } + @Override public void createOriginalData() { super.createOriginalData(); - for (Tag subTag : subTags) { + for (Tag subTag : getTags()) { subTag.createOriginalData(); } } @Override public void getNeededCharacters(Set needed) { - for (Tag t : subTags) { + for (Tag t : getTags()) { if (t instanceof CharacterIdTag) { needed.add(((CharacterIdTag) t).getCharacterId()); } @@ -374,11 +411,15 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli if (super.isModified()) { return true; } - for (Tag t : subTags) { + for (Tag t : getTags()) { if (t.isModified()) { return true; } } return false; } + + public void clearReadOnlyListCache() { + readOnlyTags = null; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java index 3d86d4ca4..5ff815b0b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java @@ -152,7 +152,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { if (deep) { if (this instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) this; - for (Tag subTag : sprite.subTags) { + for (Tag subTag : sprite.getTags()) { subTag.setSwf(swf); } } @@ -426,6 +426,10 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { return SWFInputStream.resolveTag(copy, 0, false, true, false); } + public boolean canUndo() { + return originalRange != null && isModified(); + } + public void undo() throws InterruptedException, IOException { byte[] data = getOriginalData(); if (data == null) { //If the tag is newly created in GUI it has no original data @@ -534,8 +538,9 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { } public void setModified(boolean value) { + boolean oldValue = modified; modified = value; - if (value) { + if (value && oldValue != value) { informListeners(); } } @@ -607,7 +612,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { } public void getDependentCharacters(Set dependent) { - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof CharacterTag) { Set needed = new HashSet<>(); tag.getNeededCharactersDeep(needed); 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 337e1de16..ee016f941 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 @@ -16,11 +16,13 @@ */ package com.jpexs.decompiler.flash.tags.base; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.types.BUTTONRECORD; import com.jpexs.decompiler.flash.types.ColorTransform; @@ -47,6 +49,12 @@ public abstract class ButtonTag extends CharacterTag implements DrawableTag, Tim public static int FRAME_HITTEST = 3; + private Timeline timeline; + + private boolean isSingleFrameInitialized; + + private boolean isSingleFrame; + public ButtonTag(SWF swf, int id, String name, ByteArrayRange data) { super(swf, id, name, data); } @@ -81,7 +89,7 @@ public abstract class ButtonTag extends CharacterTag implements DrawableTag, Tim } public DefineButtonSoundTag getSounds() { - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DefineButtonSoundTag) { DefineButtonSoundTag st = (DefineButtonSoundTag) t; if (st.buttonId == getCharacterId()) { @@ -96,4 +104,61 @@ public abstract class ButtonTag extends CharacterTag implements DrawableTag, Tim public void toHtmlCanvas(StringBuilder result, double unitDivisor) { getTimeline().toHtmlCanvas(result, unitDivisor, Arrays.asList(0)); //TODO: handle states? } + + @Override + public boolean isSingleFrame() { + if (!isSingleFrameInitialized) { + initialiteIsSingleFrame(); + } + return isSingleFrame; + } + + private synchronized void initialiteIsSingleFrame() { + if (!isSingleFrameInitialized) { + isSingleFrame = getTimeline().isSingleFrame(); + isSingleFrameInitialized = true; + } + } + + @Override + public Timeline getTimeline() { + if (timeline != null) { + return timeline; + } + + timeline = new Timeline(swf, this, getCharacterId(), getRect()); + initTimeline(timeline); + return timeline; + } + + @Override + public void resetTimeline() { + if (timeline != null) { + timeline.reset(swf, this, getCharacterId(), getRect()); + initTimeline(timeline); + } + } + + protected abstract void initTimeline(Timeline timeline); + + @Override + public ReadOnlyTagList getTags() { + return ReadOnlyTagList.EMPTY; + } + + @Override + public void removeTag(int index) { + } + + @Override + public void removeTag(Tag tag) { + } + + @Override + public void addTag(Tag tag) { + } + + @Override + public void addTag(int index, Tag tag) { + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java index 8ce597858..fca1cbcfb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java @@ -153,7 +153,7 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable return fontStyle; } - public abstract String getCharacters(List tags); + public abstract String getCharacters(); @Override public String getName() { @@ -209,7 +209,7 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable } protected void shiftGlyphIndices(int fontId, int startIndex) { - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { List textRecords = null; if (t instanceof StaticTextTag) { textRecords = ((StaticTextTag) t).textRecords; @@ -379,7 +379,7 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable } public DefineFontNameTag getFontNameTag() { - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DefineFontNameTag) { DefineFontNameTag dfn = (DefineFontNameTag) t; if (dfn.fontId == getFontId()) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineCompactedFont.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineCompactedFont.java index 6c62cb6db..1db281ab1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineCompactedFont.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineCompactedFont.java @@ -21,7 +21,6 @@ import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.helpers.FontHelper; import com.jpexs.decompiler.flash.tags.DefineFont2Tag; -import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.types.KERNINGRECORD; import com.jpexs.decompiler.flash.types.LANGCODE; @@ -316,7 +315,7 @@ public final class DefineCompactedFont extends FontTag { } @Override - public String getCharacters(List tags) { + public String getCharacters() { FontType ft = fonts.get(0); StringBuilder ret = new StringBuilder(ft.glyphInfo.size()); for (GlyphInfoType gi : ft.glyphInfo) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 12f03bc4d..658d2469b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -70,14 +70,10 @@ public class Timeline { public Timelined timelined; - public Tag parentTag; - public int maxDepth; public int fontFrameNum = -1; - public List tags; - private final List frames = new ArrayList<>(); private final Map depthMaxFrame = new HashMap<>(); @@ -135,11 +131,15 @@ public class Timeline { return soundStramBlocks.get(head); } - public void reset(SWF swf) { - reset(swf, null, swf.tags, 0, swf.displayRect); + public Tag getParentTag() { + return timelined instanceof Tag ? (Tag) timelined : null; } - public void reset(SWF swf, Tag parentTag, List tags, int id, RECT displayRect) { + public void reset(SWF swf) { + reset(swf, swf, 0, swf.displayRect); + } + + public void reset(SWF swf, Timelined timelined, int id, RECT displayRect) { initialized = false; frames.clear(); depthMaxFrame.clear(); @@ -152,9 +152,7 @@ public class Timeline { this.swf = swf; this.displayRect = displayRect; this.frameRate = swf.frameRate; - this.timelined = parentTag == null ? swf : (Timelined) parentTag; - this.parentTag = parentTag; - this.tags = tags; + this.timelined = timelined; as2RootPackage = new AS2Package(null, null, swf); } @@ -207,17 +205,15 @@ public class Timeline { } public Timeline(SWF swf) { - this(swf, null, swf.tags, 0, swf.displayRect); + this(swf, swf, 0, swf.displayRect); } - public Timeline(SWF swf, Tag parentTag, List tags, int id, RECT displayRect) { + public Timeline(SWF swf, Timelined timelined, int id, RECT displayRect) { this.id = id; this.swf = swf; this.displayRect = displayRect; this.frameRate = swf.frameRate; - this.timelined = parentTag == null ? swf : (Timelined) parentTag; - this.parentTag = parentTag; - this.tags = tags; + this.timelined = timelined; as2RootPackage = new AS2Package(null, null, swf); } @@ -226,7 +222,7 @@ public class Timeline { Frame frame = new Frame(this, frameIdx++); frame.layersChanged = true; boolean newFrameNeeded = false; - for (Tag t : tags) { + for (Tag t : timelined.getTags()) { boolean isNested = ShowFrameTag.isNestedTagType(t.getId()); if (isNested) { newFrameNeeded = true; @@ -348,9 +344,9 @@ public class Timeline { calculateMaxDepthFrames(); createASPackages(); - if (parentTag == null) { + if (timelined instanceof SWF) { // popuplate only for main timeline - populateSoundStreamBlocks(0, tags); + populateSoundStreamBlocks(0, timelined.getTags()); } initialized = true; @@ -433,7 +429,7 @@ public class Timeline { } } - private void populateSoundStreamBlocks(int containerId, List tags) { + private void populateSoundStreamBlocks(int containerId, Iterable tags) { List blocks = null; for (Tag t : tags) { if (t instanceof SoundStreamHeadTypeTag) { @@ -446,7 +442,7 @@ public class Timeline { if (t instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) t; - populateSoundStreamBlocks(sprite.getCharacterId(), sprite.getSubTags()); + populateSoundStreamBlocks(sprite.getCharacterId(), sprite.getTags()); } if (blocks == null) { @@ -487,8 +483,8 @@ public class Timeline { public boolean replaceCharacter(int oldCharacterId, int newCharacterId) { boolean modified = false; - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); + for (int i = 0; i < timelined.getTags().size(); i++) { + Tag t = timelined.getTags().get(i); if (t instanceof CharacterIdTag && ((CharacterIdTag) t).getCharacterId() == oldCharacterId) { ((CharacterIdTag) t).setCharacterId(newCharacterId); ((Tag) t).setModified(true); @@ -500,10 +496,10 @@ public class Timeline { public boolean removeCharacter(int characterId) { boolean modified = false; - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); + for (int i = 0; i < timelined.getTags().size(); i++) { + Tag t = timelined.getTags().get(i); if (t instanceof CharacterIdTag && ((CharacterIdTag) t).getCharacterId() == characterId) { - tags.remove(i); + timelined.removeTag(i); i--; modified = true; } @@ -726,7 +722,7 @@ public class Timeline { public boolean equals(Object obj) { if (obj instanceof Timeline) { Timeline timelineObj = (Timeline) obj; - return timelined.equals(timelineObj.timelined) && parentTag == timelineObj.parentTag; + return timelined.equals(timelineObj.timelined); } return false; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timelined.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timelined.java index d6ac774ff..ba5059b76 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timelined.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timelined.java @@ -16,6 +16,8 @@ */ package com.jpexs.decompiler.flash.timeline; +import com.jpexs.decompiler.flash.ReadOnlyTagList; +import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.BoundedTag; /** @@ -27,4 +29,16 @@ public interface Timelined extends BoundedTag { public Timeline getTimeline(); public void resetTimeline(); + + public void setModified(boolean value); + + public ReadOnlyTagList getTags(); + + public void removeTag(int index); + + public void removeTag(Tag tag); + + public void addTag(Tag tag); + + public void addTag(int index, Tag tag); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index aa14ade00..f818a9700 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.xfl; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFCompression; @@ -216,8 +217,8 @@ public class XFLConverter { + "" - + "" - + ""); + + "" + + ""); } private static void convertLineStyle(HashMap characters, LINESTYLE2 ls, int shapeNum, StringBuilder ret) { @@ -759,7 +760,7 @@ public class XFLConverter { return layers; } - private static int getLayerCount(List tags) { + private static int getLayerCount(ReadOnlyTagList tags) { int maxDepth = 0; for (Tag t : tags) { if (t instanceof PlaceObjectTypeTag) { @@ -776,11 +777,11 @@ public class XFLConverter { return maxDepth; } - private static void walkShapeUsages(List timeLineTags, HashMap characters, HashMap usages) { + private static void walkShapeUsages(ReadOnlyTagList timeLineTags, HashMap characters, HashMap usages) { for (Tag t : timeLineTags) { if (t instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) t; - walkShapeUsages(sprite.subTags, characters, usages); + walkShapeUsages(sprite.getTags(), characters, usages); } if (t instanceof PlaceObjectTypeTag) { PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; @@ -811,7 +812,7 @@ public class XFLConverter { } } - private static List getNonLibraryShapes(List tags, HashMap characters) { + private static List getNonLibraryShapes(ReadOnlyTagList tags, HashMap characters) { HashMap usages = new HashMap<>(); walkShapeUsages(tags, characters, usages); List ret = new ArrayList<>(); @@ -829,7 +830,7 @@ public class XFLConverter { return ret; } - private static HashMap getCharacters(List tags) { + private static HashMap getCharacters(ReadOnlyTagList tags) { HashMap ret = new HashMap<>(); int maxId = 0; for (Tag t : tags) { @@ -1029,7 +1030,7 @@ public class XFLConverter { } } - private static String convertSymbolInstance(String name, MATRIX matrix, ColorTransform colorTransform, boolean cacheAsBitmap, int blendMode, List filters, boolean isVisible, RGBA backgroundColor, CLIPACTIONS clipActions, CharacterTag tag, HashMap characters, List tags, FLAVersion flaVersion) { + private static String convertSymbolInstance(String name, MATRIX matrix, ColorTransform colorTransform, boolean cacheAsBitmap, int blendMode, List filters, boolean isVisible, RGBA backgroundColor, CLIPACTIONS clipActions, CharacterTag tag, HashMap characters, ReadOnlyTagList tags, FLAVersion flaVersion) { StringBuilder ret = new StringBuilder(); if (matrix == null) { matrix = new MATRIX(); @@ -1167,7 +1168,7 @@ public class XFLConverter { return date.getTime() / 1000; } - private static void convertLibrary(SWF swf, Map characterVariables, Map characterClasses, List nonLibraryShapes, String backgroundColor, List tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion, StringBuilder ret) { + private static void convertLibrary(SWF swf, Map characterVariables, Map characterClasses, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion, StringBuilder ret) { //TODO: Imported assets //linkageImportForRS="true" linkageIdentifier="xxx" linkageURL="yyy.swf" @@ -1303,10 +1304,10 @@ public class XFLConverter { symbolStr.append(""); } else if (symbol instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) symbol; - if (sprite.subTags.isEmpty()) { //probably AS2 class + if (sprite.getTags().isEmpty()) { //probably AS2 class continue; } - symbolStr.append(convertTimeline(sprite.spriteId, nonLibraryShapes, backgroundColor, tags, sprite.getSubTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files)); + symbolStr.append(convertTimeline(sprite.spriteId, nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files)); } else if (symbol instanceof ShapeTag) { itemIcon = "1"; ShapeTag shape = (ShapeTag) symbol; @@ -1632,7 +1633,7 @@ public class XFLConverter { } } - private static void convertFrame(boolean shapeTween, HashMap characters, List tags, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap files, StringBuilder ret) { + private static void convertFrame(boolean shapeTween, HashMap characters, ReadOnlyTagList tags, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap files, StringBuilder ret) { DefineSoundTag sound = null; if (startSound != null) { for (Tag t : tags) { @@ -1759,7 +1760,7 @@ public class XFLConverter { return ret.toString(); } - private static void convertFrames(String prevStr, String afterStr, List nonLibraryShapes, List tags, List timelineTags, HashMap characters, int depth, FLAVersion flaVersion, HashMap files, StringBuilder ret) { + private static void convertFrames(String prevStr, String afterStr, List nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, int depth, FLAVersion flaVersion, HashMap files, StringBuilder ret) { StringBuilder ret2 = new StringBuilder(); prevStr += ""; int frame = -1; @@ -1785,7 +1786,7 @@ public class XFLConverter { MorphShapeTag shapeTweener = null; //Add ShowFrameTag to the end when there is one last missing - List timTags = new ArrayList<>(timelineTags); + List timTags = timelineTags.toArrayList(); boolean needsFrameAdd = false; SWF swf = null; for (int i = timTags.size() - 1; i >= 0; i--) { @@ -1933,7 +1934,7 @@ public class XFLConverter { } } - private static void convertFonts(List tags, StringBuilder ret) { + private static void convertFonts(ReadOnlyTagList tags, StringBuilder ret) { StringBuilder ret2 = new StringBuilder(); for (Tag t : tags) { if (t instanceof FontTag) { @@ -1960,7 +1961,7 @@ public class XFLConverter { } String embedRanges = ""; - String fontChars = font.getCharacters(tags); + String fontChars = font.getCharacters(); if ("".equals(fontChars)) { continue; } @@ -2003,7 +2004,7 @@ public class XFLConverter { } } - private static String convertActionScriptLayer(int spriteId, List tags, List timeLineTags, String backgroundColor) { + private static String convertActionScriptLayer(int spriteId, ReadOnlyTagList tags, ReadOnlyTagList timeLineTags, String backgroundColor) { StringBuilder ret = new StringBuilder(); String script = ""; @@ -2065,7 +2066,7 @@ public class XFLConverter { return retStr; } - private static String convertLabelsLayer(int spriteId, List tags, List timeLineTags, String backgroundColor) { + private static String convertLabelsLayer(int spriteId, ReadOnlyTagList tags, ReadOnlyTagList timeLineTags, String backgroundColor) { StringBuilder ret = new StringBuilder(); int duration = 0; int frame = 0; @@ -2121,7 +2122,7 @@ public class XFLConverter { return retStr; } - private static void convertSoundLayer(int layerIndex, String backgroundColor, HashMap characters, List tags, List timeLineTags, HashMap files, StringBuilder ret) { + private static void convertSoundLayer(int layerIndex, String backgroundColor, HashMap characters, ReadOnlyTagList tags, ReadOnlyTagList timeLineTags, HashMap files, StringBuilder ret) { StringBuilder ret2 = new StringBuilder(); StartSoundTag lastStartSound = null; SoundStreamHeadTypeTag lastSoundStreamHead = null; @@ -2179,7 +2180,7 @@ public class XFLConverter { if (ret2.length() > 0) { ret.append("" + "").append(ret2).append("" - + ""); + + ""); } } @@ -2193,7 +2194,7 @@ public class XFLConverter { return outlineColor.toHexRGB(); } - private static String convertTimeline(int spriteId, List nonLibraryShapes, String backgroundColor, List tags, List timelineTags, HashMap characters, String name, FLAVersion flaVersion, HashMap files) { + private static String convertTimeline(int spriteId, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, String name, FLAVersion flaVersion, HashMap files) { StringBuilder ret = new StringBuilder(); ret.append(""); ret.append(""); @@ -2292,7 +2293,7 @@ public class XFLConverter { }, handler).run(); } - private static Map getCharacterClasses(List tags) { + private static Map getCharacterClasses(ReadOnlyTagList tags) { Map ret = new HashMap<>(); for (Tag t : tags) { if (t instanceof SymbolClassTag) { @@ -2307,7 +2308,7 @@ public class XFLConverter { return ret; } - private static Map getCharacterVariables(List tags) { + private static Map getCharacterVariables(ReadOnlyTagList tags) { Map ret = new HashMap<>(); for (Tag t : tags) { if (t instanceof ExportAssetsTag) { @@ -2337,7 +2338,7 @@ public class XFLConverter { } SWF swf = tag.getSwf(); - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof CSMTextSettingsTag) { CSMTextSettingsTag c = (CSMTextSettingsTag) t; if (c.textID == tag.getCharacterId()) { @@ -2427,7 +2428,7 @@ public class XFLConverter { fontName = null; textHeight = rec.textHeight; font = swf.getFont(fontId); - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DefineFontNameTag) { if (((DefineFontNameTag) t).fontId == fontId) { fontName = ((DefineFontNameTag) t).fontName; @@ -2552,7 +2553,7 @@ public class XFLConverter { } if (det.html) { - ret.append(convertHTMLText(swf.tags, det, txt)); + ret.append(convertHTMLText(swf.getTags(), det, txt)); } else { ret.append(""); ret.append("").append(xmlString(txt)).append(""); @@ -2571,7 +2572,7 @@ public class XFLConverter { } if (det.hasFont) { String fontName = null; - for (Tag u : swf.tags) { + for (Tag u : swf.getTags()) { if (u instanceof DefineFontNameTag) { if (((DefineFontNameTag) u).fontId == det.fontId) { fontName = ((DefineFontNameTag) u).fontName; @@ -2677,10 +2678,10 @@ public class XFLConverter { } final HashMap files = new HashMap<>(); final HashMap datfiles = new HashMap<>(); - HashMap characters = getCharacters(swf.tags); - List nonLibraryShapes = getNonLibraryShapes(swf.tags, characters); - Map characterClasses = getCharacterClasses(swf.tags); - Map characterVariables = getCharacterVariables(swf.tags); + HashMap characters = getCharacters(swf.getTags()); + List nonLibraryShapes = getNonLibraryShapes(swf.getTags(), characters); + Map characterClasses = getCharacterClasses(swf.getTags()); + Map characterVariables = getCharacterVariables(swf.getTags()); String backgroundColor = "#ffffff"; SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); @@ -2701,22 +2702,22 @@ public class XFLConverter { domDocument.append(" height=\"").append(doubleToString(height)).append("\""); } domDocument.append(">"); - convertFonts(swf.tags, domDocument); - convertLibrary(swf, characterVariables, characterClasses, nonLibraryShapes, backgroundColor, swf.tags, characters, files, datfiles, flaVersion, domDocument); + convertFonts(swf.getTags(), domDocument); + convertLibrary(swf, characterVariables, characterClasses, nonLibraryShapes, backgroundColor, swf.getTags(), characters, files, datfiles, flaVersion, domDocument); domDocument.append(""); - domDocument.append(convertTimeline(0, nonLibraryShapes, backgroundColor, swf.tags, swf.tags, characters, "Scene 1", flaVersion, files)); + domDocument.append(convertTimeline(0, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files)); domDocument.append(""); domDocument.append(""); String domDocumentStr = prettyFormatXML(domDocument.toString()); - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DoInitActionTag) { DoInitActionTag dia = (DoInitActionTag) t; int chid = dia.getCharacterId(); if (characters.containsKey(chid)) { if (characters.get(chid) instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) characters.get(chid); - if (sprite.subTags.isEmpty()) { + if (sprite.getTags().isEmpty()) { String data = convertActionScript(dia); String expName = dia.getSwf().getExportName(dia.spriteId); expName = expName != null ? expName : "_unk_"; @@ -3136,7 +3137,7 @@ public class XFLConverter { ret.append(""); } - private static String convertHTMLText(List tags, DefineEditTextTag det, String html) { + private static String convertHTMLText(ReadOnlyTagList tags, DefineEditTextTag det, String html) { HTMLTextParser tparser = new HTMLTextParser(tags, det); XMLReader parser; try { @@ -3190,7 +3191,7 @@ public class XFLConverter { private String alignment = null; - private final List tags; + private final ReadOnlyTagList tags; private boolean bold = false; @@ -3216,7 +3217,7 @@ public class XFLConverter { public void warning(SAXParseException e) throws SAXException { } - public HTMLTextParser(List tags, DefineEditTextTag det) { + public HTMLTextParser(ReadOnlyTagList tags, DefineEditTextTag det) { if (det.hasFont) { String fontName = null; FontTag ft = null; diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java index 5ca1900b3..f7a698044 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java @@ -66,7 +66,7 @@ public class ActionScript2Test extends ActionScript2TestBase { int f = 0; DoActionTag lastDoa = null; - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DoActionTag) { lastDoa = (DoActionTag) t; } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2TestBase.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2TestBase.java index f1b89e932..93ef04069 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2TestBase.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2TestBase.java @@ -1,16 +1,16 @@ /* * 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. */ @@ -28,7 +28,7 @@ public class ActionScript2TestBase extends ActionScriptTestBase { protected SWF swf; protected DoActionTag getFirstActionTag() { - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DoActionTag) { return (DoActionTag) t; } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3ExecuteTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3ExecuteTest.java index 51b5afdf7..3d0ae677b 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3ExecuteTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3ExecuteTest.java @@ -56,7 +56,7 @@ public class ActionScript3ExecuteTest { swf = new SWF(is, false); }*/ DoABC2Tag tag = null; - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DoABC2Tag) { tag = (DoABC2Tag) t; break; diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java index cefa59ce3..6d86b2cca 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java @@ -59,7 +59,7 @@ public class ActionScript3Test extends ActionScriptTestBase { //Main.initLogging(false); swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false); DoABC2Tag tag = null; - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DoABC2Tag) { tag = (DoABC2Tag) t; break; diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/RecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/RecompileTest.java index 8ded84fd5..244e36e7a 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/RecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/RecompileTest.java @@ -85,7 +85,7 @@ public class RecompileTest extends FileTestBase { try { Configuration.debugCopy.set(false); SWF swf = new SWF(new BufferedInputStream(new FileInputStream(filePath)), false, false); - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (!(tag instanceof TagStub)) { Tag tag2 = tag.cloneTag(); if (tag2 instanceof TagStub) { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/SwfXmlExportImportTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/SwfXmlExportImportTest.java index 49b286ef7..43b90d6af 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/SwfXmlExportImportTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/SwfXmlExportImportTest.java @@ -99,13 +99,13 @@ public class SwfXmlExportImportTest extends FileTestBase { SWF swf2 = new SWF(); new SwfXmlImporter().importSwf(swf2, xml); - if (swf.tags.size() != swf2.tags.size()) { + if (swf.getTags().size() != swf2.getTags().size()) { throw new NotSameException(0); } - for (int i = 0; i < swf.tags.size(); i++) { - Tag oldTag = swf.tags.get(i); - Tag newTag = swf2.tags.get(i); + for (int i = 0; i < swf.getTags().size(); i++) { + Tag oldTag = swf.getTags().get(i); + Tag newTag = swf2.getTags().get(i); if (oldTag.getClass() != newTag.getClass()) { throw new NotSameException(0); } @@ -113,12 +113,12 @@ public class SwfXmlExportImportTest extends FileTestBase { if (oldTag instanceof DefineSpriteTag) { DefineSpriteTag oldSprite = (DefineSpriteTag) oldTag; DefineSpriteTag newSprite = (DefineSpriteTag) newTag; - if (oldSprite.subTags.size() != newSprite.subTags.size()) { + if (oldSprite.getTags().size() != newSprite.getTags().size()) { throw new NotSameException(0); } - for (int k = 0; k < oldSprite.subTags.size(); k++) { - compareTags(oldSprite.subTags.get(k), newSprite.subTags.get(k)); + for (int k = 0; k < oldSprite.getTags().size(); k++) { + compareTags(oldSprite.getTags().get(k), newSprite.getTags().get(k)); } } else if (!(oldTag instanceof FontTag)) { compareTags(oldTag, newTag); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS2Generator.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS2Generator.java index 6c9ef9425..a68d0e9fd 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS2Generator.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS2Generator.java @@ -1,16 +1,16 @@ /* * 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. */ @@ -45,7 +45,7 @@ public class AS2Generator { DoActionTag doa = null; int frame = 0; StringBuilder s = new StringBuilder(); - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DoActionTag) { doa = (DoActionTag) t; } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java index 6bf8efa32..69b0c2731 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java @@ -49,7 +49,7 @@ public class AS3Generator { Configuration.autoDeobfuscate.set(false); SWF swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false); DoABC2Tag tag = null; - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DoABC2Tag) { tag = (DoABC2Tag) t; break; diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index c6b70f13c..9d25db769 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.ApplicationInfo; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFBundle; import com.jpexs.decompiler.flash.SWFCompression; @@ -1098,7 +1099,7 @@ public class CommandLineArgumentParser { swf.swfList = new SWFList(); swf.swfList.sourceInfo = sourceInfo; boolean found = false; - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag.getId() == tagId) { found = true; break; @@ -1140,7 +1141,7 @@ public class CommandLineArgumentParser { swf.swfList = new SWFList(); swf.swfList.sourceInfo = sourceInfo; boolean found = false; - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof JPEGTablesTag) { JPEGTablesTag jtt = (JPEGTablesTag) tag; if (ImageTag.hasErrorHeader(jtt.jpegData)) { @@ -1326,7 +1327,7 @@ public class CommandLineArgumentParser { } List extags = new ArrayList<>(); - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof CharacterIdTag) { CharacterIdTag c = (CharacterIdTag) t; if (selectionIds.contains(c.getCharacterId())) { @@ -1385,37 +1386,37 @@ public class CommandLineArgumentParser { if (exportAll || exportFormats.contains("image")) { System.out.println("Exporting images..."); - new ImageExporter().exportImages(handler, outDir + (multipleExportTypes ? File.separator + ImageExportSettings.EXPORT_FOLDER_NAME : ""), extags, new ImageExportSettings(enumFromStr(formats.get("image"), ImageExportMode.class)), evl); + new ImageExporter().exportImages(handler, outDir + (multipleExportTypes ? File.separator + ImageExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new ImageExportSettings(enumFromStr(formats.get("image"), ImageExportMode.class)), evl); } if (exportAll || exportFormats.contains("shape")) { System.out.println("Exporting shapes..."); - new ShapeExporter().exportShapes(handler, outDir + (multipleExportTypes ? File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME : ""), extags, new ShapeExportSettings(enumFromStr(formats.get("shape"), ShapeExportMode.class), zoom), evl); + new ShapeExporter().exportShapes(handler, outDir + (multipleExportTypes ? File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new ShapeExportSettings(enumFromStr(formats.get("shape"), ShapeExportMode.class), zoom), evl); } if (exportAll || exportFormats.contains("morphshape")) { System.out.println("Exporting morphshapes..."); - new MorphShapeExporter().exportMorphShapes(handler, outDir + (multipleExportTypes ? File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME : ""), extags, new MorphShapeExportSettings(enumFromStr(formats.get("morphshape"), MorphShapeExportMode.class), zoom), evl); + new MorphShapeExporter().exportMorphShapes(handler, outDir + (multipleExportTypes ? File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new MorphShapeExportSettings(enumFromStr(formats.get("morphshape"), MorphShapeExportMode.class), zoom), evl); } if (exportAll || exportFormats.contains("movie")) { System.out.println("Exporting movies..."); - new MovieExporter().exportMovies(handler, outDir + (multipleExportTypes ? File.separator + MovieExportSettings.EXPORT_FOLDER_NAME : ""), extags, new MovieExportSettings(enumFromStr(formats.get("movie"), MovieExportMode.class)), evl); + new MovieExporter().exportMovies(handler, outDir + (multipleExportTypes ? File.separator + MovieExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new MovieExportSettings(enumFromStr(formats.get("movie"), MovieExportMode.class)), evl); } if (exportAll || exportFormats.contains("font")) { System.out.println("Exporting fonts..."); - new FontExporter().exportFonts(handler, outDir + (multipleExportTypes ? File.separator + FontExportSettings.EXPORT_FOLDER_NAME : ""), extags, new FontExportSettings(enumFromStr(formats.get("font"), FontExportMode.class)), evl); + new FontExporter().exportFonts(handler, outDir + (multipleExportTypes ? File.separator + FontExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new FontExportSettings(enumFromStr(formats.get("font"), FontExportMode.class)), evl); } if (exportAll || exportFormats.contains("sound")) { System.out.println("Exporting sounds..."); - new SoundExporter().exportSounds(handler, outDir + (multipleExportTypes ? File.separator + SoundExportSettings.EXPORT_FOLDER_NAME : ""), extags, new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class)), evl); + new SoundExporter().exportSounds(handler, outDir + (multipleExportTypes ? File.separator + SoundExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class)), evl); } if (exportAll || exportFormats.contains("binarydata")) { System.out.println("Exporting binaryData..."); - new BinaryDataExporter().exportBinaryData(handler, outDir + (multipleExportTypes ? File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME : ""), extags, new BinaryDataExportSettings(enumFromStr(formats.get("binarydata"), BinaryDataExportMode.class)), evl); + new BinaryDataExporter().exportBinaryData(handler, outDir + (multipleExportTypes ? File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new BinaryDataExportSettings(enumFromStr(formats.get("binarydata"), BinaryDataExportMode.class)), evl); } if (exportAll || exportFormats.contains("text")) { @@ -1424,7 +1425,7 @@ public class CommandLineArgumentParser { if (singleTextFile == null) { singleTextFile = Configuration.textExportSingleFile.get(); } - new TextExporter().exportTexts(handler, outDir + (multipleExportTypes ? File.separator + TextExportSettings.EXPORT_FOLDER_NAME : ""), extags, new TextExportSettings(enumFromStr(formats.get("text"), TextExportMode.class), singleTextFile, zoom), evl); + new TextExporter().exportTexts(handler, outDir + (multipleExportTypes ? File.separator + TextExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new TextExportSettings(enumFromStr(formats.get("text"), TextExportMode.class), singleTextFile, zoom), evl); } FrameExporter frameExporter = new FrameExporter(); @@ -1883,7 +1884,7 @@ public class CommandLineArgumentParser { SWF swf = new SWF(is, Configuration.parallelSpeedUp.get()); int totalPages = 0; - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DefineSpriteTag) { DefineSpriteTag ds = (DefineSpriteTag) t; if ("page1".equals(ds.getExportName())) { @@ -1898,7 +1899,7 @@ public class CommandLineArgumentParser { int page = 0; - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DefineSpriteTag) { DefineSpriteTag ds = (DefineSpriteTag) t; if ("page1".equals(ds.getExportName())) { @@ -2378,8 +2379,8 @@ public class CommandLineArgumentParser { System.err.println("Tag number should be integer"); System.exit(1); } - if (tagNo < 0 || tagNo >= swf.tags.size()) { - System.err.println("Tag number does not exist. Tag number should be between 0 and " + (swf.tags.size() - 1)); + if (tagNo < 0 || tagNo >= swf.getTags().size()) { + System.err.println("Tag number does not exist. Tag number should be between 0 and " + (swf.getTags().size() - 1)); System.exit(1); } @@ -2394,7 +2395,7 @@ public class CommandLineArgumentParser { Collections.sort(tagNumbersToRemove); for (int i = tagNumbersToRemove.size() - 1; i >= 0; i--) { - swf.tags.remove((int) tagNumbersToRemove.get(i)); + swf.removeTag((int) tagNumbersToRemove.get(i)); } try { @@ -2705,7 +2706,7 @@ public class CommandLineArgumentParser { pw.println("height=" + doubleToString(swf.displayRect.getHeight() / SWF.unitDivisor)); pw.println("frameCount=" + swf.frameCount); pw.println("frameRate=" + doubleToString(swf.frameRate)); - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof SetBackgroundColorTag) { pw.println("backgroundColor=" + ((SetBackgroundColorTag) t).backgroundColor.toHexRGB()); } @@ -2717,7 +2718,7 @@ public class CommandLineArgumentParser { pw.println(); pw.println("[tags]"); - pw.println("tagCount=" + swf.tags.size()); + pw.println("tagCount=" + swf.getTags().size()); pw.println("hasEndTag=" + swf.hasEndTag); pw.println("characterCount=" + (swf.getCharacters().size())); pw.println("maxCharacterId=" + (swf.getNextCharacterId() - 1)); diff --git a/src/com/jpexs/decompiler/flash/gui/FontPanel.java b/src/com/jpexs/decompiler/flash/gui/FontPanel.java index 68c00ed4b..718f30f0c 100644 --- a/src/com/jpexs/decompiler/flash/gui/FontPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FontPanel.java @@ -134,8 +134,7 @@ public class FontPanel extends JPanel { private void fontAddChars(FontTag ft, Set selChars, Font font) { FontTag f = (FontTag) mainPanel.tagTree.getCurrentTreeItem(); - SWF swf = ft.getSwf(); - String oldchars = f.getCharacters(swf.tags); + String oldchars = f.getCharacters(); for (int ic : selChars) { char c = (char) ic; if (oldchars.indexOf((int) c) == -1) { @@ -182,7 +181,8 @@ public class FontPanel extends JPanel { int fontId = ft.getFontId(); if (updateTextsCheckBox.isSelected()) { - for (Tag tag : swf.tags) { + SWF swf = ft.getSwf(); + for (Tag tag : swf.getTags()) { if (tag instanceof TextTag) { TextTag textTag = (TextTag) tag; if (textTag.getFontIds().contains(fontId)) { @@ -208,7 +208,7 @@ public class FontPanel extends JPanel { fontDescentLabel.setText(ft.getDescent() == -1 ? translate("value.unknown") : Integer.toString(ft.getDescent())); fontAscentLabel.setText(ft.getAscent() == -1 ? translate("value.unknown") : Integer.toString(ft.getAscent())); fontLeadingLabel.setText(ft.getLeading() == -1 ? translate("value.unknown") : Integer.toString(ft.getLeading())); - String chars = ft.getCharacters(swf.tags); + String chars = ft.getCharacters(); fontCharactersTextArea.setText(chars); fontCharactersScrollPane.getVerticalScrollBar().scrollRectToVisible(new Rectangle(0, 0, 1, 1)); setAllowSave(false); diff --git a/src/com/jpexs/decompiler/flash/gui/LoadFromMemoryFrame.java b/src/com/jpexs/decompiler/flash/gui/LoadFromMemoryFrame.java index e584f3b5e..7fc6f415d 100644 --- a/src/com/jpexs/decompiler/flash/gui/LoadFromMemoryFrame.java +++ b/src/com/jpexs/decompiler/flash/gui/LoadFromMemoryFrame.java @@ -140,7 +140,7 @@ public class LoadFromMemoryFrame extends AppFrame { long limit = pmi.getPos(); is.seek(0); is = new ReReadableInputStream(new LimitedInputStream(is, limit)); - if (swf.fileSize > 0 && swf.version > 0 && !swf.tags.isEmpty() && swf.version < 25/*Needs to be fixed when SWF versions reaches this value*/) { + if (swf.fileSize > 0 && swf.version > 0 && !swf.getTags().isEmpty() && swf.version < 25/*Needs to be fixed when SWF versions reaches this value*/) { SwfInMemory s = new SwfInMemory(is, swf.version, swf.fileSize, proc); String p = translate("swfitem").replace("%version%", Integer.toString(swf.version)).replace("%size%", Long.toString(swf.fileSize)); publish(s); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index a27b8ea81..ff4f59846 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.ApplicationInfo; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFBundle; import com.jpexs.decompiler.flash.SWFSourceInfo; @@ -858,17 +859,20 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return true; } - public boolean closeAll() { - boolean modified = false; + public boolean isModified() { for (SWFList swfList : swfs) { for (SWF swf : swfList) { if (swf.isModified()) { - modified = true; + return true; } } } - if (modified) { + return false; + } + + public boolean closeAll() { + if (isModified()) { boolean closeConfirmResult = closeConfirmation(swfs.size() == 1 ? swfs.get(0) : null); if (!closeConfirmResult) { return false; @@ -1167,47 +1171,47 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se EventListener evl = swf.getExportEventListener(); if (export.isOptionEnabled(ImageExportMode.class)) { - ret.addAll(new ImageExporter().exportImages(handler, selFile2 + File.separator + ImageExportSettings.EXPORT_FOLDER_NAME, images, + ret.addAll(new ImageExporter().exportImages(handler, selFile2 + File.separator + ImageExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(images), new ImageExportSettings(export.getValue(ImageExportMode.class)), evl)); } if (export.isOptionEnabled(ShapeExportMode.class)) { - ret.addAll(new ShapeExporter().exportShapes(handler, selFile2 + File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME, shapes, + ret.addAll(new ShapeExporter().exportShapes(handler, selFile2 + File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(shapes), new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl)); } if (export.isOptionEnabled(MorphShapeExportMode.class)) { - ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile2 + File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME, morphshapes, + ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile2 + File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(morphshapes), new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl)); } if (export.isOptionEnabled(TextExportMode.class)) { - ret.addAll(new TextExporter().exportTexts(handler, selFile2 + File.separator + TextExportSettings.EXPORT_FOLDER_NAME, texts, + ret.addAll(new TextExporter().exportTexts(handler, selFile2 + File.separator + TextExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(texts), new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl)); } if (export.isOptionEnabled(MovieExportMode.class)) { - ret.addAll(new MovieExporter().exportMovies(handler, selFile2 + File.separator + MovieExportSettings.EXPORT_FOLDER_NAME, movies, + ret.addAll(new MovieExporter().exportMovies(handler, selFile2 + File.separator + MovieExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(movies), new MovieExportSettings(export.getValue(MovieExportMode.class)), evl)); } if (export.isOptionEnabled(SoundExportMode.class)) { - ret.addAll(new SoundExporter().exportSounds(handler, selFile2 + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, sounds, + ret.addAll(new SoundExporter().exportSounds(handler, selFile2 + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(sounds), new SoundExportSettings(export.getValue(SoundExportMode.class)), evl)); } if (export.isOptionEnabled(BinaryDataExportMode.class)) { - ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile2 + File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME, binaryData, + ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile2 + File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(binaryData), new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl)); } if (export.isOptionEnabled(FontExportMode.class)) { - ret.addAll(new FontExporter().exportFonts(handler, selFile2 + File.separator + FontExportSettings.EXPORT_FOLDER_NAME, fonts, + ret.addAll(new FontExporter().exportFonts(handler, selFile2 + File.separator + FontExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(fonts), new FontExportSettings(export.getValue(FontExportMode.class)), evl)); } if (export.isOptionEnabled(SymbolClassExportMode.class)) { - ret.addAll(new SymbolClassExporter().exportNames(selFile2, symbolNames, evl)); + ret.addAll(new SymbolClassExporter().exportNames(selFile2, new ReadOnlyTagList(symbolNames), evl)); } FrameExporter frameExporter = new FrameExporter(); @@ -1278,47 +1282,47 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se EventListener evl = swf.getExportEventListener(); if (export.isOptionEnabled(ImageExportMode.class)) { - new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), new ImageExportSettings(export.getValue(ImageExportMode.class)), evl); } if (export.isOptionEnabled(ShapeExportMode.class)) { - new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl); } if (export.isOptionEnabled(MorphShapeExportMode.class)) { - new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl); } if (export.isOptionEnabled(TextExportMode.class)) { - new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl); } if (export.isOptionEnabled(MovieExportMode.class)) { - new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), new MovieExportSettings(export.getValue(MovieExportMode.class)), evl); } if (export.isOptionEnabled(SoundExportMode.class)) { - new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), new SoundExportSettings(export.getValue(SoundExportMode.class)), evl); } if (export.isOptionEnabled(BinaryDataExportMode.class)) { - new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl); } if (export.isOptionEnabled(FontExportMode.class)) { - new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), new FontExportSettings(export.getValue(FontExportMode.class)), evl); } if (export.isOptionEnabled(SymbolClassExportMode.class)) { - new SymbolClassExporter().exportNames(selFile, swf.tags, evl); + new SymbolClassExporter().exportNames(selFile, swf.getTags(), evl); } FrameExporter frameExporter = new FrameExporter(); @@ -1366,63 +1370,63 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (export.isOptionEnabled(ImageExportMode.class)) { for (ImageExportMode exportMode : ImageExportMode.values()) { - new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), new ImageExportSettings(exportMode), evl); } } if (export.isOptionEnabled(ShapeExportMode.class)) { for (ShapeExportMode exportMode : ShapeExportMode.values()) { - new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), new ShapeExportSettings(exportMode, export.getZoom()), evl); } } if (export.isOptionEnabled(MorphShapeExportMode.class)) { for (MorphShapeExportMode exportMode : MorphShapeExportMode.values()) { - new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), new MorphShapeExportSettings(exportMode, export.getZoom()), evl); } } if (export.isOptionEnabled(TextExportMode.class)) { for (TextExportMode exportMode : TextExportMode.values()) { - new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), new TextExportSettings(exportMode, Configuration.textExportSingleFile.get(), export.getZoom()), evl); } } if (export.isOptionEnabled(MovieExportMode.class)) { for (MovieExportMode exportMode : MovieExportMode.values()) { - new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), new MovieExportSettings(exportMode), evl); } } if (export.isOptionEnabled(SoundExportMode.class)) { for (SoundExportMode exportMode : SoundExportMode.values()) { - new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), new SoundExportSettings(exportMode), evl); } } if (export.isOptionEnabled(BinaryDataExportMode.class)) { for (BinaryDataExportMode exportMode : BinaryDataExportMode.values()) { - new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), new BinaryDataExportSettings(exportMode), evl); } } if (export.isOptionEnabled(FontExportMode.class)) { for (FontExportMode exportMode : FontExportMode.values()) { - new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), new FontExportSettings(exportMode), evl); } } if (export.isOptionEnabled(SymbolClassExportMode.class)) { for (SymbolClassExportMode exportMode : SymbolClassExportMode.values()) { - new SymbolClassExporter().exportNames(selFile, swf.tags, evl); + new SymbolClassExporter().exportNames(selFile, swf.getTags(), evl); } } @@ -1783,7 +1787,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); } List textTags = new ArrayList<>(); - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof TextTag) { textTags.add((TextTag) tag); } @@ -1834,7 +1838,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else { pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); } - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof TextTag) { TextTag textTag = (TextTag) tag; if (pat.matcher(textTag.getFormattedText().text).find()) { @@ -2403,7 +2407,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return; } - List tags = new ArrayList<>(swf.tags); + List tags = swf.getTags().toArrayList(); List toRemove = new ArrayList<>(); for (Tag tag : tags) { System.out.println(tag.getClass()); @@ -3232,17 +3236,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else if (treeItem instanceof Frame && internalViewer) { Frame fn = (Frame) treeItem; SWF swf = fn.getSwf(); - List controlTags = swf.tags; - int containerId = 0; - RECT rect = swf.displayRect; - int totalFrameCount = swf.frameCount; Timelined timelined = swf; if (fn.timeline.timelined instanceof DefineSpriteTag) { DefineSpriteTag parentSprite = (DefineSpriteTag) fn.timeline.timelined; - controlTags = parentSprite.subTags; - containerId = parentSprite.spriteId; - rect = parentSprite.getRect(); - totalFrameCount = parentSprite.frameCount; timelined = parentSprite; } @@ -3311,35 +3307,35 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se SWF swf = item.swf; switch (folderName) { case TagTreeModel.FOLDER_SHAPES: - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof ShapeTag) { folderPreviewItems.add(tag); } } break; case TagTreeModel.FOLDER_MORPHSHAPES: - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof MorphShapeTag) { folderPreviewItems.add(tag); } } break; case TagTreeModel.FOLDER_SPRITES: - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof DefineSpriteTag) { folderPreviewItems.add(tag); } } break; case TagTreeModel.FOLDER_BUTTONS: - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof ButtonTag) { folderPreviewItems.add(tag); } } break; case TagTreeModel.FOLDER_FONTS: - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof FontTag) { folderPreviewItems.add(tag); } @@ -3351,14 +3347,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } break; case TagTreeModel.FOLDER_IMAGES: - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof ImageTag) { folderPreviewItems.add(tag); } } break; case TagTreeModel.FOLDER_TEXTS: - for (Tag tag : swf.tags) { + for (Tag tag : swf.getTags()) { if (tag instanceof TextTag) { folderPreviewItems.add(tag); } @@ -3399,7 +3395,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public Timeline getTimeline() { if (tim == null) { - Timeline timeline = new Timeline(tag.getSwf(), null, new ArrayList<>(), ((CharacterTag) tag).getCharacterId(), getRect()); + Timeline timeline = new Timeline(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); initTimeline(timeline); tim = timeline; } @@ -3410,7 +3406,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public void resetTimeline() { if (tim != null) { - tim.reset(tag.getSwf(), null, new ArrayList<>(), ((CharacterTag) tag).getCharacterId(), getRect()); + tim.reset(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); initTimeline(tim); } } @@ -3473,6 +3469,31 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public int hashCode() { return tag.hashCode(); } + + @Override + public void setModified(boolean value) { + } + + @Override + public ReadOnlyTagList getTags() { + return ReadOnlyTagList.EMPTY; + } + + @Override + public void removeTag(int index) { + } + + @Override + public void removeTag(Tag tag) { + } + + @Override + public void addTag(Tag tag) { + } + + @Override + public void addTag(int index, Tag tag) { + } }; } diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index d3fc51d46..2ea4e1af8 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -624,7 +624,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel HashMap videoFrames = new HashMap<>(); if (treeItem instanceof DefineVideoStreamTag) { DefineVideoStreamTag vs = (DefineVideoStreamTag) treeItem; - SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); + SWF.populateVideoFrames(vs.getCharacterId(), swf.getTags(), videoFrames); frameCount = videoFrames.size(); } @@ -697,7 +697,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel Frame fn = (Frame) treeItem; Timelined parent = fn.timeline.timelined; List doneCharacters = new ArrayList<>(); - for (Tag t : fn.timeline.tags) { + for (Tag t : parent.getTags()) { if (t instanceof FileAttributesTag || t instanceof SetBackgroundColorTag) { continue; } @@ -1028,7 +1028,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } else if (treeItem instanceof DefineSpriteTag) { DefineSpriteTag s = (DefineSpriteTag) treeItem; Tag lastTag = null; - for (Tag t : s.subTags) { + for (Tag t : s.getTags()) { if (t instanceof EndTag) { break; } else if (t instanceof PlaceObjectTypeTag) { @@ -1042,7 +1042,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel lastTag = t; } } - if (!s.subTags.isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { + if (!s.getTags().isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { new ShowFrameTag(swf).writeTag(sos2); } } else { diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java index 16b9128a5..26bdf832a 100644 --- a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java +++ b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java @@ -129,7 +129,7 @@ public class DebuggerTools { ScriptPack found = getDebuggerScriptPack(swf); if (found != null) { ABCContainerTag tag = found.abc.parentTag; - swf.tags.remove((Tag) tag); + swf.removeTag((Tag) tag); swf.getAbcList().remove(tag); //Change all debugger calls to normal trace / Loader @@ -188,7 +188,7 @@ public class DebuggerTools { } //Add to target SWF ((Tag) ds).setSwf(swf); - swf.tags.add(swf.tags.indexOf(firstAbc), (Tag) ds); + swf.addTag((Tag) ds, (Tag) firstAbc); swf.getAbcList().add(swf.getAbcList().indexOf(firstAbc), ds); ((Tag) ds).setModified(true); } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index b57a4521e..8e515f9f6 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -478,9 +478,9 @@ public class TagTreeContextMenu extends JPopupMenu { SWF sourceSwf = items.get(0).getSwf(); for (TreeItem item : items) { Tag tag = (Tag) item; - sourceSwf.tags.remove(tag); + sourceSwf.removeTag(tag); tag.setSwf(targetSwf, true); - targetSwf.tags.add(tag); + targetSwf.addTag(tag); chechUniqueCharacterId(tag); targetSwf.updateCharacters(); tag.setModified(true); @@ -505,7 +505,7 @@ public class TagTreeContextMenu extends JPopupMenu { Tag tag = (Tag) item; Tag copyTag = tag.cloneTag(); copyTag.setSwf(targetSwf, true); - targetSwf.tags.add(copyTag); + targetSwf.addTag(copyTag); chechUniqueCharacterId(copyTag); targetSwf.updateCharacters(); copyTag.setModified(true); @@ -546,7 +546,7 @@ public class TagTreeContextMenu extends JPopupMenu { if (!copiedTags.contains(neededTag)) { copyTag = neededTag.cloneTag(); copyTag.setSwf(targetSwf, true); - targetSwf.tags.add(copyTag); + targetSwf.addTag(copyTag); int oldCharacterId = neededTag.getCharacterId(); int newCharacterId = chechUniqueCharacterId(copyTag); changedCharacterIds.put(oldCharacterId, newCharacterId); @@ -561,7 +561,7 @@ public class TagTreeContextMenu extends JPopupMenu { copyTag = tag.cloneTag(); copyTag.setSwf(targetSwf, true); - targetSwf.tags.add(copyTag); + targetSwf.addTag(copyTag); if (tag instanceof CharacterTag) { CharacterTag characterTag = (CharacterTag) copyTag; int oldCharacterId = characterTag.getCharacterId(); diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java index 0a9a08954..39579fc48 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java @@ -172,7 +172,7 @@ public class TagTreeModel implements TreeModel { private List getSoundStreams(DefineSpriteTag sprite) { List ret = new ArrayList<>(); - for (Tag t : sprite.subTags) { + for (Tag t : sprite.getTags()) { if (t instanceof SoundStreamHeadTypeTag) { ret.add((SoundStreamHeadTypeTag) t); } @@ -196,7 +196,7 @@ public class TagTreeModel implements TreeModel { List others = new ArrayList<>(); List emptyFolders = new ArrayList<>(); Map> mappedTags = new HashMap<>(); - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { TreeNodeType ttype = TagTree.getTreeNodeType(t); switch (ttype) { case SHAPE: diff --git a/test/com/jpexs/decompiler/flash/gui/AdobeFlashExecutor.java b/test/com/jpexs/decompiler/flash/gui/AdobeFlashExecutor.java index 629f3194d..c82aabe35 100644 --- a/test/com/jpexs/decompiler/flash/gui/AdobeFlashExecutor.java +++ b/test/com/jpexs/decompiler/flash/gui/AdobeFlashExecutor.java @@ -152,7 +152,6 @@ public class AdobeFlashExecutor { Map asms = swf.getASMs(true); ASMSource asm = asms.get("\\frame_1\\DoAction"); - int asmIndex = swf.tags.indexOf(asm); ActionList actionsList = asm.getActions(); FastActionList actions = new FastActionList(actionsList); @@ -173,7 +172,7 @@ public class AdobeFlashExecutor { actions2.add(new ActionReturn()); doaTag.setActions(actions2); - swf.tags.add(asmIndex, doaTag); + swf.addTag(doaTag, asm); i++; } @@ -211,7 +210,7 @@ public class AdobeFlashExecutor { SWF swf = new SWF(new BufferedInputStream(new FileInputStream(runFileAs3)), false); swf.version = SWF.MAX_VERSION; DoABC2Tag abcTag = null; - for (Tag t : swf.tags) { + for (Tag t : swf.getTags()) { if (t instanceof DoABC2Tag) { abcTag = ((DoABC2Tag) t); break; From 49778b88c1bb196af972d60360361e5ff5e67b57 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 27 Dec 2015 12:12:02 +0100 Subject: [PATCH 02/14] svg importer separated to multiple files --- .../flash/importers/ShapeImporter.java | 2288 +---------------- .../flash/importers/svg/SvgBitmapFill.java | 35 + .../flash/importers/svg/SvgColor.java | 389 +++ .../flash/importers/svg/SvgFill.java | 28 + .../flash/importers/svg/SvgGradient.java | 45 + .../flash/importers/svg/SvgGradientUnits.java | 26 + .../flash/importers/svg/SvgImporter.java | 1290 ++++++++++ .../flash/importers/svg/SvgInterpolation.java | 26 + .../flash/importers/svg/SvgLineCap.java | 26 + .../flash/importers/svg/SvgLineJoin.java | 26 + .../importers/svg/SvgLinearGradient.java | 33 + .../importers/svg/SvgRadialGradient.java | 35 + .../flash/importers/svg/SvgSpreadMethod.java | 26 + .../flash/importers/svg/SvgStop.java | 40 + .../flash/importers/svg/SvgStyle.java | 598 +++++ .../jpexs/decompiler/flash/gui/MainPanel.java | 7 +- 16 files changed, 2636 insertions(+), 2282 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgBitmapFill.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgColor.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgFill.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgGradient.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgGradientUnits.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgInterpolation.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLineCap.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLineJoin.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLinearGradient.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgRadialGradient.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgSpreadMethod.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStop.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java index 35c03ec4c..843b43cc6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java @@ -16,16 +16,8 @@ */ package com.jpexs.decompiler.flash.importers; -import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.exporters.ShapeExporter; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.Point; -import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; -import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; import com.jpexs.decompiler.flash.helpers.ImageHelper; -import com.jpexs.decompiler.flash.importers.svg.CubicToQuad; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; @@ -39,54 +31,12 @@ import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.FILLSTYLE; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.FOCALGRADIENT; -import com.jpexs.decompiler.flash.types.GRADIENT; -import com.jpexs.decompiler.flash.types.GRADRECORD; -import com.jpexs.decompiler.flash.types.LINESTYLE; -import com.jpexs.decompiler.flash.types.LINESTYLE2; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.Color; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; -import java.io.StringReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import javax.imageio.ImageIO; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; /** * @@ -94,16 +44,6 @@ import org.xml.sax.SAXException; */ public class ShapeImporter { - private final Set shownWarnings = new HashSet<>(); - - private final Color TRANSPARENT = new Color(0, true); - - private final SvgColor SVG_TRANSPARENT = new SvgColor(TRANSPARENT); - - private ShapeTag shapeTag; - - private final Random random = new Random(); - public Tag importImage(ShapeTag st, byte[] newData) throws IOException { return importImage(st, newData, 0, true); } @@ -124,7 +64,16 @@ public class ShapeImporter { return (Tag) st; } - private ImageTag addImage(ShapeTag st, byte[] newData, int tagType) throws IOException { + /** + * Adds an image tag before the specified tag + * + * @param st + * @param newData + * @param tagType + * @return + * @throws IOException + */ + public ImageTag addImage(Tag st, byte[] newData, int tagType) throws IOException { SWF swf = st.getSwf(); if (newData[0] == 'B' && newData[1] == 'M') { @@ -177,2223 +126,6 @@ public class ShapeImporter { return imageTag; } - public Tag importSvg(ShapeTag st, String svgXml) { - return importSvg(st, svgXml, true); - } - - // Generate id-element map, because getElementById does not work in some cases (namespaces?) - protected void populateIds(Element el, Map out) { - if (el.hasAttribute("id")) { - out.put(el.getAttribute("id"), el); - } - NodeList nodes = el.getChildNodes(); - for (int i = 0; i < nodes.getLength(); i++) { - if (nodes.item(i) instanceof Element) { - populateIds((Element) nodes.item(i), out); - } - } - } - - public Tag importSvg(ShapeTag st, String svgXml, boolean fill) { - shapeTag = st; - - SHAPEWITHSTYLE shapes = new SHAPEWITHSTYLE(); - shapes.fillStyles = new FILLSTYLEARRAY(); - shapes.lineStyles = new LINESTYLEARRAY(); - shapes.fillStyles.fillStyles = new FILLSTYLE[0]; - shapes.lineStyles.lineStyles = new LINESTYLE[0]; - - int shapeNum = st.getShapeNum(); - shapes.shapeRecords = new ArrayList<>(); - - try { - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - /*docFactory.setValidating(false); - docFactory.setNamespaceAware(true); - docFactory.setFeature("http://xml.org/sax/features/namespaces", false); - docFactory.setFeature("http://xml.org/sax/features/validation", false); - docFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);*/ - docFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - - Document doc = docBuilder.parse(new InputSource(new StringReader(svgXml))); - Element rootElement = doc.getDocumentElement(); - - Map idMap = new HashMap<>(); - populateIds(rootElement, idMap); - - if (!"svg".equals(rootElement.getTagName())) { - throw new IOException("SVG root element should be 'svg'"); - } - - SvgStyle style = new SvgStyle(); - style = style.apply(rootElement, idMap); - Matrix transform = new Matrix(); - processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style); - } catch (SAXException | IOException | ParserConfigurationException ex) { - Logger.getLogger(ShapeImporter.class.getName()).log(Level.SEVERE, null, ex); - } - - shapes.shapeRecords.add(new EndShapeRecord()); - - RECT rect = st.getRect(); - int origXmin = rect.Xmin; - int origYmin = rect.Ymin; - rect.Xmin -= origXmin; - rect.Xmax -= origXmin; - rect.Ymin -= origYmin; - rect.Ymax -= origYmin; - - if (!fill) { - // todo: how to calulate the real SVG size? - RECT bounds = shapes.getBounds(); - rect.Xmax = rect.Xmin + bounds.Xmax - Math.min(0, bounds.Xmin); - rect.Ymax = rect.Ymin + bounds.Ymax - Math.min(0, bounds.Ymin); - } - - st.shapes = shapes; - st.setModified(true); - - return (Tag) st; - } - - private void processSvgObject(Map idMap, int shapeNum, SHAPEWITHSTYLE shapes, Element element, Matrix transform, SvgStyle style) { - for (int i = 0; i < element.getChildNodes().getLength(); i++) { - Node childNode = element.getChildNodes().item(i); - if (childNode instanceof Element) { - Element childElement = (Element) childNode; - String tagName = childElement.getTagName(); - SvgStyle newStyle = style.apply(childElement, idMap); - Matrix m = Matrix.parseSvgMatrix(childElement.getAttribute("transform"), 1, 1); - Matrix m2 = m == null ? transform : transform.concatenate(m); - if ("g".equals(tagName)) { - processSvgObject(idMap, 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 if ("defs".equals(tagName) || "title".equals(tagName) || "desc".equals(tagName) - || "radialGradient".equals(tagName) || "linearGradient".equals(tagName)) { - // ignore - } else { - showWarning(tagName + "tagNotSupported", "The SVG tag '" + tagName + "' is not supported."); - } - } - } - } - - private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List commands, Matrix transform, SvgStyle style) { - Matrix transform2 = transform.preConcatenate(Matrix.getScaleInstance(SWF.unitDivisor)); - Point prevPoint = new Point(0, 0); - Point startPoint = prevPoint; - double x0 = 0; - double y0 = 0; - - 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; - - List newRecords = new ArrayList<>(); - - newRecords.add(scrStyle); - - LINESTYLE lineStyleObj = scrStyle.lineStyles.lineStyles.length < 1 ? null : scrStyle.lineStyles.lineStyles[0]; - LINESTYLE2 lineStyle2Obj = null; - if (lineStyleObj instanceof LINESTYLE2) { - lineStyle2Obj = (LINESTYLE2) lineStyleObj; - lineStyle2Obj.noClose = true; - } - - for (PathCommand command : commands) { - double x = x0; - double y = y0; - Point p; - char cmd = Character.toUpperCase(command.command); - switch (cmd) { - case 'M': - StyleChangeRecord scr = new StyleChangeRecord(); - if (fillStyle != 0) { - scr.stateFillStyle1 = true; - scr.fillStyle1 = fillStyle; - } - if (lineStyle != 0) { - scr.lineStyle = lineStyle; - scr.stateLineStyle = true; - } - - x = command.params[0]; - y = command.params[1]; - - p = transform2.transform(x, y); - scr.moveDeltaX = (int) Math.round(p.x); - scr.moveDeltaY = (int) Math.round(p.y); - prevPoint = p; - scr.stateMoveTo = true; - - newRecords.add(scr); - startPoint = p; - break; - case 'Z': - StraightEdgeRecord serz = new StraightEdgeRecord(); - p = startPoint; - serz.deltaX = (int) Math.round(p.x - prevPoint.x); - serz.deltaY = (int) Math.round(p.y - prevPoint.y); - prevPoint = p; - serz.generalLineFlag = true; - newRecords.add(serz); - if (lineStyle2Obj != null) { - lineStyle2Obj.noClose = false; - } - break; - case 'L': - StraightEdgeRecord serl = new StraightEdgeRecord(); - x = command.params[0]; - y = command.params[1]; - - p = transform2.transform(x, y); - serl.deltaX = (int) Math.round(p.x - prevPoint.x); - serl.deltaY = (int) Math.round(p.y - prevPoint.y); - prevPoint = p; - serl.generalLineFlag = true; - serl.simplify(); - newRecords.add(serl); - break; - case 'H': - StraightEdgeRecord serh = new StraightEdgeRecord(); - x = command.params[0]; - - p = transform2.transform(x, y); - serh.deltaX = (int) Math.round(p.x - prevPoint.x); - prevPoint = p; - newRecords.add(serh); - break; - case 'V': - StraightEdgeRecord serv = new StraightEdgeRecord(); - y = command.params[0]; - - p = transform2.transform(x, y); - serv.deltaY = (int) Math.round(p.y - prevPoint.y); - prevPoint = p; - serv.vertLineFlag = true; - newRecords.add(serv); - break; - case 'Q': - CurvedEdgeRecord cer = new CurvedEdgeRecord(); - x = command.params[0]; - y = command.params[1]; - - p = transform2.transform(x, y); - cer.controlDeltaX = (int) Math.round(p.x - prevPoint.x); - cer.controlDeltaY = (int) Math.round(p.y - prevPoint.y); - prevPoint = p; - - x = command.params[2]; - y = command.params[3]; - - p = transform2.transform(x, y); - cer.anchorDeltaX = (int) Math.round(p.x - prevPoint.x); - cer.anchorDeltaY = (int) Math.round(p.y - prevPoint.y); - prevPoint = p; - newRecords.add(cer); - break; - case 'C': - showWarning("cubicCurvesNotSupported", "Cubic curves are not supported by Flash."); - - // create at least something... - Point pStart = prevPoint; - Point pControl1; - - x = command.params[0]; - y = command.params[1]; - - pControl1 = transform2.transform(x, y); - - x = command.params[2]; - y = command.params[3]; - - Point pControl2 = transform2.transform(x, y); - - x = command.params[4]; - y = command.params[5]; - - p = transform2.transform(x, y); - - //StraightEdgeRecord serc = new StraightEdgeRecord(); - //serc.generalLineFlag = true; - //serc.deltaX = (int) Math.round(p.x - prevPoint.x); - //serc.deltaY = (int) Math.round(p.y - prevPoint.y); - //newRecords.add(serc); - List quadCoordinates = new CubicToQuad().cubicToQuad(pStart.x, pStart.y, pControl1.x, pControl1.y, pControl2.x, pControl2.y, p.x, p.y, 1); - for (int i = 2; i < quadCoordinates.size();) { - CurvedEdgeRecord cerc = new CurvedEdgeRecord(); - p = new Point(quadCoordinates.get(i++), quadCoordinates.get(i++)); - cerc.controlDeltaX = (int) Math.round(p.x - prevPoint.x); - cerc.controlDeltaY = (int) Math.round(p.y - prevPoint.y); - prevPoint = p; - - p = new Point(quadCoordinates.get(i++), quadCoordinates.get(i++)); - cerc.anchorDeltaX = (int) Math.round(p.x - prevPoint.x); - cerc.anchorDeltaY = (int) Math.round(p.y - prevPoint.y); - prevPoint = p; - newRecords.add(cerc); - } - - break; - default: - Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Unknown command: {0}", command); - return; - } - - x0 = x; - y0 = y; - } - applyStyleGradients(SHAPERECORD.getBounds(newRecords), scrStyle, transform2, shapeNum, style); - shapes.shapeRecords.addAll(newRecords); - } - - 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 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; - 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 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'; - - 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; - 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; - } - - 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 double calcAngle(double ux, double uy, double vx, double vy) { - double lu = Math.sqrt(ux * ux + uy * uy); - double lv = Math.sqrt(ux * ux + uy * uy); - double sign = Math.signum(ux * vy - uy * vx); - if (sign == 0) { - sign = 1; - } - - return sign * Math.acos(ux * vx + uy * vy / (lu * lv)); - } - - 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; - - processEllipse(shapeNum, shapes, transform, style, cx, cy, r, r); - } - - 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; - - processEllipse(shapeNum, shapes, transform, style, cx, cy, rx, ry); - } - - private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, double cx, double cy, double rx, double ry) { - 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 pathCommands = new ArrayList<>(); - PathCommand scr = new PathCommand(); - scr.command = 'M'; - scr.params = new double[]{cx + rx, cy}; - pathCommands.add(scr); - - double[] points = new double[]{ - rx, -sqrt2Minus1RY, - sqrt2RXHalf, -sqrt2RYHalf, - sqrt2Minus1RX, -ry, - 0, -ry, - -sqrt2Minus1RX, -ry, - -sqrt2RXHalf, -sqrt2RYHalf, - -rx, -sqrt2Minus1RY, - -rx, 0, - -rx, sqrt2Minus1RY, - -sqrt2RXHalf, sqrt2RYHalf, - -sqrt2Minus1RX, ry, - 0, ry, - sqrt2Minus1RX, ry, - sqrt2RXHalf, sqrt2RYHalf, - rx, sqrt2Minus1RY, - rx, 0}; - - for (int i = 0; i < points.length; i += 4) { - PathCommand cer = new PathCommand(); - cer.command = 'Q'; - cer.params = new double[]{cx + points[i], cy + points[i + 1], cx + points[i + 2], cy + points[i + 3]}; - - /*double tetha = 30; - tetha *= Math.PI / 180; - double x1 = points[i]; - double y1 = points[i + 1]; - double x2 = points[i + 2]; - double y2 = points[i + 3]; - - double x1Comma = Math.cos(tetha) * x1 + Math.sin(tetha) * y1; - double y1Comma = -Math.sin(tetha) * x1 + Math.cos(tetha) * y1; - double x2Comma = Math.cos(tetha) * x2 + Math.sin(tetha) * y2; - double y2Comma = -Math.sin(tetha) * x2 + Math.cos(tetha) * y2; - - cer.params = new double[]{cx + x1Comma, cy + y1Comma, cx + x2Comma, cy + y2Comma};*/ - pathCommands.add(cer); - } - - PathCommand serz = new PathCommand(); - serz.command = 'Z'; - pathCommands.add(serz); - - 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 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); - } - } - - PathCommand serz = new PathCommand(); - serz.command = 'Z'; - pathCommands.add(serz); - - 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 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'; - double x0 = 0; - double y0 = 0; - - List 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); - 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'; - pathCommands.add(serz); - } - - processCommands(shapeNum, shapes, pathCommands, transform, style); - } - - //Stub for w3 test. TODO: refactor and move to test directory. It's here because of easy access - compiling single file - private static void svgTest(String name) throws IOException, InterruptedException { - if (!new File(name + ".original.svg").exists()) { - URL svgUrl = new URL("http://www.w3.org/Graphics/SVG/Test/20061213/svggen/" + name + ".svg"); - byte[] svgData = Helper.readStream(svgUrl.openStream()); - Helper.writeFile(name + ".original.svg", svgData); - - URL pngUrl = new URL("http://www.w3.org/Graphics/SVG/Test/20061213/png/full-" + name + ".png"); - 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(); - DefineShape4Tag st = new DefineShape4Tag(swf); - st = (DefineShape4Tag) (new ShapeImporter().importSvg(st, svgDataS)); - swf.addTag(st); - SerializableImage si = new SerializableImage(800, 600, BufferedImage.TYPE_4BYTE_ABGR); - BitmapExporter.export(swf, st.shapes, Color.yellow, si, new Matrix(), new ColorTransform()); - List li = new ArrayList<>(); - li.add(st); - ImageIO.write(si.getBufferedImage(), "PNG", new File(name + ".imported.png")); - new ShapeExporter().exportShapes(null, "./outex/", new ReadOnlyTagList(li), new ShapeExportSettings(ShapeExportMode.SVG, 1), null); - } - - //Test for SVG - public static void main(String[] args) throws IOException, InterruptedException { - //svgTest("pservers-grad-01-b"); - svgTest("pservers-grad-07-b"); - } - - private void applyFillGradients(SvgFill fill, FILLSTYLE fillStyle, RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) { - if (fill == null || fillStyle == null) { - return; - } - if (fill instanceof SvgGradient) { - SvgGradient gfill = (SvgGradient) fill; - Matrix gradientMatrix = Matrix.parseSvgMatrix(gfill.gradientTransform, SWF.unitDivisor, 1); - gradientMatrix = transform.concatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)).concatenate(gradientMatrix); - fillStyle.gradientMatrix = gradientMatrix.toMATRIX(); - if (fill instanceof SvgLinearGradient) { - SvgLinearGradient lgfill = (SvgLinearGradient) fill; - fillStyle.fillStyleType = FILLSTYLE.LINEAR_GRADIENT; - fillStyle.gradient = new GRADIENT(); - double x1; - if (lgfill.x1.endsWith("%")) { - x1 = Double.parseDouble(lgfill.x1.substring(0, lgfill.x1.length() - 1)) / 100; - } else { - x1 = Double.parseDouble(lgfill.x1); - } - //x1 = x1 - (-819.2); - - double y1; - if (lgfill.y1.endsWith("%")) { - y1 = Double.parseDouble(lgfill.y1.substring(0, lgfill.y1.length() - 1)) / 100; - } else { - y1 = Double.parseDouble(lgfill.y1); - } - double x2; - if (lgfill.x2.endsWith("%")) { - x2 = Double.parseDouble(lgfill.x2.substring(0, lgfill.x2.length() - 1)) / 100; - } else { - x2 = Double.parseDouble(lgfill.x2); - } - //x2 = x2 - 819.2; - double y2; - if (lgfill.y2.endsWith("%")) { - y2 = Double.parseDouble(lgfill.y2.substring(0, lgfill.y2.length() - 1)) / 100; - } else { - y2 = Double.parseDouble(lgfill.y2); - } - x1 = x1 * SWF.unitDivisor; - y1 = y1 * SWF.unitDivisor; - x2 = x2 * SWF.unitDivisor; - y2 = y2 * SWF.unitDivisor; - - Matrix boundingBoxMatrix = new Matrix(); - if (lgfill.gradientUnits == SvgGradientUnits.OBJECT_BOUNDING_BOX) { - boundingBoxMatrix.scaleX = (bounds.Xmax - bounds.Xmin) / SWF.unitDivisor; - boundingBoxMatrix.rotateSkew0 = 0; - boundingBoxMatrix.rotateSkew1 = 0; - boundingBoxMatrix.scaleY = (bounds.Ymax - bounds.Ymin) / SWF.unitDivisor; - boundingBoxMatrix.translateX = bounds.Xmin; - boundingBoxMatrix.translateY = bounds.Ymin; - } - - Matrix xyMatrix = new Matrix(); - xyMatrix.scaleX = x2 - x1; - xyMatrix.rotateSkew0 = y2 - y1; - xyMatrix.rotateSkew1 = -xyMatrix.rotateSkew0; - xyMatrix.scaleY = xyMatrix.scaleX; - - xyMatrix = xyMatrix.preConcatenate(boundingBoxMatrix); - - Matrix zeroStartMatrix = Matrix.getTranslateInstance(0.5, 0); - - Matrix scaleMatrix = Matrix.getScaleInstance(1 / 16384.0 / 2); - Matrix transMatrix = Matrix.getTranslateInstance(x1, y1); - - Matrix tMatrix = new Matrix(); - tMatrix = tMatrix.preConcatenate(scaleMatrix); - tMatrix = tMatrix.preConcatenate(zeroStartMatrix); - tMatrix = tMatrix.preConcatenate(xyMatrix); - - tMatrix = tMatrix.preConcatenate(transMatrix); - Point p1 = tMatrix.transform(new Point(-16384, 0)); - Point p2 = tMatrix.transform(new Point(16384, 0)); - - tMatrix = tMatrix.preConcatenate(new Matrix(fillStyle.gradientMatrix)); - fillStyle.gradientMatrix = tMatrix.toMATRIX(); - } else if (fill instanceof SvgRadialGradient) { - SvgRadialGradient rgfill = (SvgRadialGradient) fill; - double cx; - if (rgfill.cx.endsWith("%")) { - cx = Double.parseDouble(rgfill.cx.substring(0, rgfill.cx.length() - 1)) / 100; - } else { - cx = Double.parseDouble(rgfill.cx); - } - double cy; - if (rgfill.cy.endsWith("%")) { - cy = Double.parseDouble(rgfill.cy.substring(0, rgfill.cy.length() - 1)) / 100; - } else { - cy = Double.parseDouble(rgfill.cy); - } - - double r; - if (rgfill.r.endsWith("%")) { - r = Double.parseDouble(rgfill.r.substring(0, rgfill.r.length() - 1)) / 100; - } else { - r = Double.parseDouble(rgfill.r); - } - - Matrix boundingBoxMatrix = new Matrix(); - if (rgfill.gradientUnits == SvgGradientUnits.OBJECT_BOUNDING_BOX) { - boundingBoxMatrix.scaleX = (bounds.Xmax - bounds.Xmin) / SWF.unitDivisor; - boundingBoxMatrix.rotateSkew0 = 0; - boundingBoxMatrix.rotateSkew1 = 0; - boundingBoxMatrix.scaleY = (bounds.Ymax - bounds.Ymin) / SWF.unitDivisor; - boundingBoxMatrix.translateX = bounds.Xmin; - boundingBoxMatrix.translateY = bounds.Ymin; - } - - fillStyle.gradientMatrix = Matrix.getTranslateInstance(SWF.unitDivisor * cx, SWF.unitDivisor * cy).concatenate(new Matrix(fillStyle.gradientMatrix)).concatenate(Matrix.getScaleInstance(r / 819.2)).preConcatenate(boundingBoxMatrix).toMATRIX(); - - double fx; - if (rgfill.fx.endsWith("%")) { - fx = Double.parseDouble(rgfill.fx.substring(0, rgfill.fx.length() - 1)) / 100; - } else { - fx = Double.parseDouble(rgfill.fx); - } - double fy; - if (rgfill.fy.endsWith("%")) { - fy = Double.parseDouble(rgfill.fy.substring(0, rgfill.fy.length() - 1)) / 100; - } else { - fy = Double.parseDouble(rgfill.fy); - } - if (!rgfill.fx.equals(rgfill.cx) || !rgfill.fy.equals(rgfill.cy)) { - fillStyle.fillStyleType = FILLSTYLE.FOCAL_RADIAL_GRADIENT; - fillStyle.gradient = new FOCALGRADIENT(); - FOCALGRADIENT fg = (FOCALGRADIENT) fillStyle.gradient; - double f = Math.sqrt((fx - cx) * (fx - cx) + (fy - cy) * (fy - cy)) / 819.2; - fg.focalPoint = (float) f; - } else { - fillStyle.fillStyleType = FILLSTYLE.RADIAL_GRADIENT; - fillStyle.gradient = new GRADIENT(); - } - } - switch (gfill.spreadMethod) { - case PAD: - fillStyle.gradient.spreadMode = GRADIENT.SPREAD_PAD_MODE; - break; - case REFLECT: - fillStyle.gradient.spreadMode = GRADIENT.SPREAD_REFLECT_MODE; - break; - case REPEAT: - fillStyle.gradient.spreadMode = GRADIENT.SPREAD_REPEAT_MODE; - break; - } - switch (gfill.interpolation) { - case LINEAR_RGB: - fillStyle.gradient.interpolationMode = GRADIENT.INTERPOLATION_LINEAR_RGB_MODE; - break; - case SRGB: - fillStyle.gradient.interpolationMode = GRADIENT.INTERPOLATION_RGB_MODE; - break; - } - - fillStyle.gradient.gradientRecords = new GRADRECORD[gfill.stops.size()]; - for (int i = 0; i < gfill.stops.size(); i++) { - SvgStop stop = gfill.stops.get(i); - Color color = stop.color; - color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) Math.round(color.getAlpha() * style.opacity)); - fillStyle.gradient.gradientRecords[i] = new GRADRECORD(); - fillStyle.gradient.gradientRecords[i].inShape3 = shapeNum >= 3; - fillStyle.gradient.gradientRecords[i].color = getRGB(shapeNum, color); - fillStyle.gradient.gradientRecords[i].ratio = (int) Math.round(stop.offset * 255); - } - } else if (fill instanceof SvgBitmapFill) { - SvgBitmapFill bfill = (SvgBitmapFill) fill; - fillStyle.fillStyleType = FILLSTYLE.REPEATING_BITMAP; - fillStyle.bitmapId = bfill.characterId; - Matrix fillMatrix = Matrix.parseSvgMatrix(bfill.patternTransform, SWF.unitDivisor, SWF.unitDivisor); - fillMatrix = transform.concatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)).concatenate(fillMatrix); - fillStyle.bitmapMatrix = fillMatrix.toMATRIX(); - } - } - - private void applyStyleGradients(RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) { - SvgFill fill = style.getFillWithOpacity(); - if (fill != null) { - applyFillGradients(fill, scr.fillStyles.fillStyles[0], bounds, scr, transform, shapeNum, style); - } - SvgFill strokeFill = style.getStrokeFillWithOpacity(); - if (strokeFill != null) { - if (scr.lineStyles.lineStyles.length > 0 && scr.lineStyles.lineStyles[0] instanceof LINESTYLE2) { - applyFillGradients(strokeFill, ((LINESTYLE2) scr.lineStyles.lineStyles[0]).fillType, bounds, scr, transform, shapeNum, style); - } - } - } - - private StyleChangeRecord getStyleChangeRecord(int shapeNum, SvgStyle style) { - StyleChangeRecord scr = new StyleChangeRecord(); - - scr.stateNewStyles = true; - scr.fillStyles = new FILLSTYLEARRAY(); - scr.stateFillStyle1 = true; - scr.stateLineStyle = true; - SvgFill fill = style.getFillWithOpacity(); - if (fill != null) { - scr.fillStyles.fillStyles = new FILLSTYLE[1]; - scr.fillStyles.fillStyles[0] = new FILLSTYLE(); - if (fill instanceof SvgColor) { - Color colorFill = fill.toColor(); - scr.fillStyles.fillStyles[0].color = getRGB(shapeNum, colorFill); - scr.fillStyles.fillStyles[0].fillStyleType = FILLSTYLE.SOLID; - } else if (fill instanceof SvgGradient) { - //...apply in second step - applyStyleGradients - } - - scr.fillStyle1 = 1; - } else { - scr.fillStyles.fillStyles = new FILLSTYLE[0]; - scr.fillStyle1 = 0; - } - - scr.lineStyles = new LINESTYLEARRAY(); - SvgFill strokeFill = style.getStrokeFillWithOpacity(); - if (strokeFill != null) { - Color lineColor = strokeFill.toColor(); - - scr.lineStyles.lineStyles = new LINESTYLE[1]; - LINESTYLE lineStyle = shapeNum <= 3 ? new LINESTYLE() : new LINESTYLE2(); - lineStyle.color = getRGB(shapeNum, 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; - if (!(strokeFill instanceof SvgColor)) { - lineStyle2.hasFillFlag = true; - lineStyle2.fillType = new FILLSTYLE(); - //...apply in second step - applyStyleGradients - }//Single color does not need fillType attribute - - 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 = (float) 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]; - scr.lineStyle = 0; - } - - return scr; - } - - private RGB getRGB(int shapeNum, Color color) { - if (shapeNum < 3 && color.getAlpha() != 0xff) { - showWarning("transparentColorNotSupported", "Transparent color is not supported in shape " + shapeNum); - } - - return shapeNum >= 3 ? new RGBA(color) : new RGB(color); - } - - private class SvgStop implements Comparable { - - public Color color; - - public double offset; - - public SvgStop(Color color, double offset) { - this.color = color; - this.offset = offset; - } - - @Override - public int compareTo(SvgStop o) { - return (int) Math.signum(offset - o.offset); - } - } - - //FIXME - matrices - private SvgFill parseGradient(Map idMap, Element el, SvgStyle style) { - SvgGradientUnits gradientUnits = null; - String gradientTransform = null; - SvgSpreadMethod spreadMethod = null; - SvgInterpolation interpolation = null; - - String x1 = null; - String y1 = null; - String x2 = null; - String y2 = null; - - String cx = null; - String cy = null; - String fx = null; - String fy = null; - String r = null; - - List stops = new ArrayList<>(); - //inheritance: - if (el.hasAttribute("xlink:href")) { - String parent = el.getAttribute("xlink:href"); - if (parent.startsWith("#")) { - String parentId = parent.substring(1); - Element parent_el = idMap.get(parentId); - if (parent_el == null) { - showWarning("fillNotSupported", "Parent gradient not found."); - return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); - } - - if ("linearGradient".equals(el.getTagName()) && parent_el.getTagName().equals(el.getTagName())) { - SvgLinearGradient parentFill = (SvgLinearGradient) parseGradient(idMap, parent_el, style); - gradientUnits = parentFill.gradientUnits; - gradientTransform = parentFill.gradientTransform; - spreadMethod = parentFill.spreadMethod; - - x1 = parentFill.x1; - y1 = parentFill.y1; - x2 = parentFill.x2; - y2 = parentFill.y2; - interpolation = parentFill.interpolation; - stops = parentFill.stops; - } - if ("radialGradient".equals(el.getTagName()) && parent_el.getTagName().equals(el.getTagName())) { - SvgRadialGradient parentFill = (SvgRadialGradient) parseGradient(idMap, parent_el, style); - gradientUnits = parentFill.gradientUnits; - gradientTransform = parentFill.gradientTransform; - spreadMethod = parentFill.spreadMethod; - - cx = parentFill.cx; - cy = parentFill.cy; - fx = parentFill.fx; - fy = parentFill.fy; - r = parentFill.r; - interpolation = parentFill.interpolation; - stops = parentFill.stops; - } - - } else { - showWarning("fillNotSupported", "Parent gradient invalid."); - return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); - } - } - - if (el.hasAttribute("gradientUnits")) { - switch (el.getAttribute("gradientUnits")) { - case "userSpaceOnUse": - gradientUnits = SvgGradientUnits.USER_SPACE_ON_USE; - break; - case "objectBoundingBox": - gradientUnits = SvgGradientUnits.OBJECT_BOUNDING_BOX; - break; - default: - showWarning("fillNotSupported", "Unsupported gradientUnits: " + el.getAttribute("gradientUnits")); - return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); - } - - } - if (el.hasAttribute("gradientTransform")) { - gradientTransform = el.getAttribute("gradientTransform"); - } - if (el.hasAttribute("spreadMethod")) { - switch (el.getAttribute("spreadMethod")) { - case "pad": - spreadMethod = SvgSpreadMethod.PAD; - break; - case "reflect": - spreadMethod = SvgSpreadMethod.REFLECT; - break; - case "repeat": - spreadMethod = SvgSpreadMethod.REPEAT; - break; - } - } - if (el.hasAttribute("x1")) { - x1 = el.getAttribute("x1").trim(); - } - if (el.hasAttribute("y1")) { - y1 = el.getAttribute("y1").trim(); - } - if (el.hasAttribute("x2")) { - x2 = el.getAttribute("x2").trim(); - } - if (el.hasAttribute("y2")) { - y2 = el.getAttribute("y2").trim(); - } - - if (el.hasAttribute("cx")) { - cx = el.getAttribute("cx").trim(); - } - if (el.hasAttribute("cy")) { - cy = el.getAttribute("cy").trim(); - } - if (el.hasAttribute("fx")) { - fx = el.getAttribute("fx").trim(); - } - if (el.hasAttribute("fy")) { - fy = el.getAttribute("fy").trim(); - } - if (el.hasAttribute("r")) { - r = el.getAttribute("r").trim(); - } - if (el.hasAttribute("color-interpolation") && interpolation == null) { //prefer inherit - switch (el.getAttribute("color-interpolation")) { - case "sRGB": - interpolation = SvgInterpolation.SRGB; - break; - case "linearRGB": - interpolation = SvgInterpolation.LINEAR_RGB; - break; - case "auto": //without preference, put SRGB there - interpolation = SvgInterpolation.SRGB; - break; - } - } - if (interpolation == null) { - interpolation = SvgInterpolation.SRGB; - } - - if (gradientUnits == null) { - gradientUnits = SvgGradientUnits.OBJECT_BOUNDING_BOX; - } - if (spreadMethod == null) { - spreadMethod = SvgSpreadMethod.PAD; - } - - if (x1 == null) { - x1 = "0%"; - } - if (y1 == null) { - y1 = "0%"; - } - - if (x2 == null) { - x2 = "100%"; - } - if (y2 == null) { - y2 = "0%"; - } - - if (cx == null) { - cx = "50%"; - } - if (cy == null) { - cy = "50%"; - } - - if (r == null) { - r = "50%"; - } - if (fx == null) { - fx = cx; - } - if (fy == null) { - fy = cy; - } - - NodeList stopNodes = el.getElementsByTagName("stop"); - boolean stopsCleared = false; - for (int i = 0; i < stopNodes.getLength(); i++) { - Node node = stopNodes.item(i); - if (node instanceof Element) { - Element stopEl = (Element) node; - SvgStyle newStyle = style.apply(stopEl, idMap); - - String offsetStr = stopEl.getAttribute("offset"); - double offset; - if (offsetStr.endsWith("%")) { - offset = Double.parseDouble(offsetStr.substring(0, offsetStr.length() - 1)) / 100; - } else { - offset = Double.parseDouble(offsetStr); - } - Color color = newStyle.stopColor; - if (color == null) { - color = Color.BLACK; - } - - int alpha = (int) Math.round(newStyle.stopOpacity * 255); - color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); - if (!stopsCleared) { //It has some stop nodes -> remove all inherited stops - stopsCleared = true; - stops = new ArrayList<>(); - } - stops.add(new SvgStop(color, offset)); - } - } - - if ("linearGradient".equals(el.getTagName())) { - SvgLinearGradient ret = new SvgLinearGradient(); - ret.x1 = x1; - ret.y1 = y1; - ret.x2 = x2; - ret.y2 = y2; - ret.spreadMethod = spreadMethod; - ret.gradientTransform = gradientTransform; - ret.gradientUnits = gradientUnits; - ret.stops = stops; - ret.interpolation = interpolation; - return ret; - } else if ("radialGradient".equals(el.getTagName())) { - SvgRadialGradient ret = new SvgRadialGradient(); - ret.cx = cx; - ret.cy = cy; - ret.fx = fx; - ret.fy = fy; - ret.r = r; - ret.spreadMethod = spreadMethod; - ret.gradientTransform = gradientTransform; - ret.gradientUnits = gradientUnits; - ret.stops = stops; - ret.interpolation = interpolation; - return ret; - } else { - return null; - } - } - - 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) { - return null; - } - - // named colors from: http://www.w3.org/TR/SVG/types.html#ColorKeywords - switch (rgbStr) { - case "none": - return SVG_TRANSPARENT; - case "aliceblue": - return new SvgColor(240, 248, 255); - case "antiquewhite": - return new SvgColor(250, 235, 215); - case "aqua": - return new SvgColor(0, 255, 255); - case "aquamarine": - return new SvgColor(127, 255, 212); - case "azure": - return new SvgColor(240, 255, 255); - case "beige": - return new SvgColor(245, 245, 220); - case "bisque": - return new SvgColor(255, 228, 196); - case "black": - return new SvgColor(0, 0, 0); - case "blanchedalmond": - return new SvgColor(255, 235, 205); - case "blue": - return new SvgColor(0, 0, 255); - case "blueviolet": - return new SvgColor(138, 43, 226); - case "brown": - return new SvgColor(165, 42, 42); - case "burlywood": - return new SvgColor(222, 184, 135); - case "cadetblue": - return new SvgColor(95, 158, 160); - case "chartreuse": - return new SvgColor(127, 255, 0); - case "chocolate": - return new SvgColor(210, 105, 30); - case "coral": - return new SvgColor(255, 127, 80); - case "cornflowerblue": - return new SvgColor(100, 149, 237); - case "cornsilk": - return new SvgColor(255, 248, 220); - case "crimson": - return new SvgColor(220, 20, 60); - case "cyan": - return new SvgColor(0, 255, 255); - case "darkblue": - return new SvgColor(0, 0, 139); - case "darkcyan": - return new SvgColor(0, 139, 139); - case "darkgoldenrod": - return new SvgColor(184, 134, 11); - case "darkgray": - return new SvgColor(169, 169, 169); - case "darkgreen": - return new SvgColor(0, 100, 0); - case "darkgrey": - return new SvgColor(169, 169, 169); - case "darkkhaki": - return new SvgColor(189, 183, 107); - case "darkmagenta": - return new SvgColor(139, 0, 139); - case "darkolivegreen": - return new SvgColor(85, 107, 47); - case "darkorange": - return new SvgColor(255, 140, 0); - case "darkorchid": - return new SvgColor(153, 50, 204); - case "darkred": - return new SvgColor(139, 0, 0); - case "darksalmon": - return new SvgColor(233, 150, 122); - case "darkseagreen": - return new SvgColor(143, 188, 143); - case "darkslateblue": - return new SvgColor(72, 61, 139); - case "darkslategray": - return new SvgColor(47, 79, 79); - case "darkslategrey": - return new SvgColor(47, 79, 79); - case "darkturquoise": - return new SvgColor(0, 206, 209); - case "darkviolet": - return new SvgColor(148, 0, 211); - case "deeppink": - return new SvgColor(255, 20, 147); - case "deepskyblue": - return new SvgColor(0, 191, 255); - case "dimgray": - return new SvgColor(105, 105, 105); - case "dimgrey": - return new SvgColor(105, 105, 105); - case "dodgerblue": - return new SvgColor(30, 144, 255); - case "firebrick": - return new SvgColor(178, 34, 34); - case "floralwhite": - return new SvgColor(255, 250, 240); - case "forestgreen": - return new SvgColor(34, 139, 34); - case "fuchsia": - return new SvgColor(255, 0, 255); - case "gainsboro": - return new SvgColor(220, 220, 220); - case "ghostwhite": - return new SvgColor(248, 248, 255); - case "gold": - return new SvgColor(255, 215, 0); - case "goldenrod": - return new SvgColor(218, 165, 32); - case "gray": - return new SvgColor(128, 128, 128); - case "grey": - return new SvgColor(128, 128, 128); - case "green": - return new SvgColor(0, 128, 0); - case "greenyellow": - return new SvgColor(173, 255, 47); - case "honeydew": - return new SvgColor(240, 255, 240); - case "hotpink": - return new SvgColor(255, 105, 180); - case "indianred": - return new SvgColor(205, 92, 92); - case "indigo": - return new SvgColor(75, 0, 130); - case "ivory": - return new SvgColor(255, 255, 240); - case "khaki": - return new SvgColor(240, 230, 140); - case "lavender": - return new SvgColor(230, 230, 250); - case "lavenderblush": - return new SvgColor(255, 240, 245); - case "lawngreen": - return new SvgColor(124, 252, 0); - case "lemonchiffon": - return new SvgColor(255, 250, 205); - case "lightblue": - return new SvgColor(173, 216, 230); - case "lightcoral": - return new SvgColor(240, 128, 128); - case "lightcyan": - return new SvgColor(224, 255, 255); - case "lightgoldenrodyellow": - return new SvgColor(250, 250, 210); - case "lightgray": - return new SvgColor(211, 211, 211); - case "lightgreen": - return new SvgColor(144, 238, 144); - case "lightgrey": - return new SvgColor(211, 211, 211); - case "lightpink": - return new SvgColor(255, 182, 193); - case "lightsalmon": - return new SvgColor(255, 160, 122); - case "lightseagreen": - return new SvgColor(32, 178, 170); - case "lightskyblue": - return new SvgColor(135, 206, 250); - case "lightslategray": - return new SvgColor(119, 136, 153); - case "lightslategrey": - return new SvgColor(119, 136, 153); - case "lightsteelblue": - return new SvgColor(176, 196, 222); - case "lightyellow": - return new SvgColor(255, 255, 224); - case "lime": - return new SvgColor(0, 255, 0); - case "limegreen": - return new SvgColor(50, 205, 50); - case "linen": - return new SvgColor(250, 240, 230); - case "magenta": - return new SvgColor(255, 0, 255); - case "maroon": - return new SvgColor(128, 0, 0); - case "mediumaquamarine": - return new SvgColor(102, 205, 170); - case "mediumblue": - return new SvgColor(0, 0, 205); - case "mediumorchid": - return new SvgColor(186, 85, 211); - case "mediumpurple": - return new SvgColor(147, 112, 219); - case "mediumseagreen": - return new SvgColor(60, 179, 113); - case "mediumslateblue": - return new SvgColor(123, 104, 238); - case "mediumspringgreen": - return new SvgColor(0, 250, 154); - case "mediumturquoise": - return new SvgColor(72, 209, 204); - case "mediumvioletred": - return new SvgColor(199, 21, 133); - case "midnightblue": - return new SvgColor(25, 25, 112); - case "mintcream": - return new SvgColor(245, 255, 250); - case "mistyrose": - return new SvgColor(255, 228, 225); - case "moccasin": - return new SvgColor(255, 228, 181); - case "navajowhite": - return new SvgColor(255, 222, 173); - case "navy": - return new SvgColor(0, 0, 128); - case "oldlace": - return new SvgColor(253, 245, 230); - case "olive": - return new SvgColor(128, 128, 0); - case "olivedrab": - return new SvgColor(107, 142, 35); - case "orange": - return new SvgColor(255, 165, 0); - case "orangered": - return new SvgColor(255, 69, 0); - case "orchid": - return new SvgColor(218, 112, 214); - case "palegoldenrod": - return new SvgColor(238, 232, 170); - case "palegreen": - return new SvgColor(152, 251, 152); - case "paleturquoise": - return new SvgColor(175, 238, 238); - case "palevioletred": - return new SvgColor(219, 112, 147); - case "papayawhip": - return new SvgColor(255, 239, 213); - case "peachpuff": - return new SvgColor(255, 218, 185); - case "peru": - return new SvgColor(205, 133, 63); - case "pink": - return new SvgColor(255, 192, 203); - case "plum": - return new SvgColor(221, 160, 221); - case "powderblue": - return new SvgColor(176, 224, 230); - case "purple": - return new SvgColor(128, 0, 128); - case "red": - return new SvgColor(255, 0, 0); - case "rosybrown": - return new SvgColor(188, 143, 143); - case "royalblue": - return new SvgColor(65, 105, 225); - case "saddlebrown": - return new SvgColor(139, 69, 19); - case "salmon": - return new SvgColor(250, 128, 114); - case "sandybrown": - return new SvgColor(244, 164, 96); - case "seagreen": - return new SvgColor(46, 139, 87); - case "seashell": - return new SvgColor(255, 245, 238); - case "sienna": - return new SvgColor(160, 82, 45); - case "silver": - return new SvgColor(192, 192, 192); - case "skyblue": - return new SvgColor(135, 206, 235); - case "slateblue": - return new SvgColor(106, 90, 205); - case "slategray": - return new SvgColor(112, 128, 144); - case "slategrey": - return new SvgColor(112, 128, 144); - case "snow": - return new SvgColor(255, 250, 250); - case "springgreen": - return new SvgColor(0, 255, 127); - case "steelblue": - return new SvgColor(70, 130, 180); - case "tan": - return new SvgColor(210, 180, 140); - case "teal": - return new SvgColor(0, 128, 128); - case "thistle": - return new SvgColor(216, 191, 216); - case "tomato": - return new SvgColor(255, 99, 71); - case "turquoise": - return new SvgColor(64, 224, 208); - case "violet": - return new SvgColor(238, 130, 238); - case "wheat": - return new SvgColor(245, 222, 179); - case "white": - return new SvgColor(255, 255, 255); - case "whitesmoke": - return new SvgColor(245, 245, 245); - case "yellow": - return new SvgColor(255, 255, 0); - case "yellowgreen": - return new SvgColor(154, 205, 50); - } - - Pattern idPat = Pattern.compile("url\\(#([^)]+)\\).*"); - java.util.regex.Matcher mPat = idPat.matcher(rgbStr); - - if (mPat.matches()) { - String elementId = mPat.group(1); - Element e = idMap.get(elementId); - if (e != null) { - String tagName = e.getTagName(); - if ("linearGradient".equals(tagName)) { - return parseGradient(idMap, e, new SvgStyle(style)); //? new style - } - - if ("radialGradient".equals(tagName)) { - return parseGradient(idMap, e, new SvgStyle(style)); //? new style - } - - if ("pattern".equals(tagName)) { - Element element = null; - NodeList childNodes = e.getChildNodes(); - for (int i = 0; i < childNodes.getLength(); i++) { - if (childNodes.item(i) instanceof Element) { - if (element != null) { - element = null; - break; - } - - element = (Element) childNodes.item(i); - } - } - - if (element != null && "image".equals(element.getTagName())) { - String attr = element.getAttribute("xlink:href").trim(); - // this is ugly, but supports the format which is exported by FFDec - if (attr.startsWith("data:image/") && attr.contains("base64,")) { - String base64 = attr.substring(attr.indexOf("base64,") + 7); - byte[] data = Helper.base64StringToByteArray(base64); - try { - ImageTag imageTag = addImage(shapeTag, data, 0); - SvgBitmapFill bitmapFill = new SvgBitmapFill(); - bitmapFill.characterId = imageTag.characterID; - if (e.hasAttribute("patternTransform")) { - bitmapFill.patternTransform = e.getAttribute("patternTransform"); - } - - return bitmapFill; - } catch (IOException ex) { - Logger.getLogger(ShapeImporter.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - } - - showWarning("fillNotSupported", "Unknown fill style. Random color assigned."); - return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); - } - - rgbStr = rgbStr.substring(elementId.length() + 6).trim(); // remove url(#...) - } - - if (rgbStr.startsWith("#")) { - String s = rgbStr.substring(1); - if (s.length() == 3) { - s = "" + s.charAt(0) + s.charAt(0) + s.charAt(1) + s.charAt(1) + s.charAt(2) + s.charAt(2); - } - - int i = Integer.parseInt(s, 16); - return new SvgColor(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 SvgColor(r, g, b); - } else { - int r = Integer.parseInt(a0); - int g = Integer.parseInt(a1); - int b = Integer.parseInt(a2); - return new SvgColor(r, g, b); - } - } - } - } else { - showWarning("fillNotSupported", "Only solid fills are supported. Random color assigned."); - //showWarning("fillNotSupported", "Unknown fill style. Random color assigned."); - return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); - } - - return null; - } - - class PathCommand { - - public char command; - - public double[] params; - } - - enum SvgLineCap { - - BUTT, ROUND, SQUARE - } - - enum SvgLineJoin { - - MITER, ROUND, BEVEL - } - - enum SvgSpreadMethod { - - PAD, REFLECT, REPEAT - } - - enum SvgGradientUnits { - - USER_SPACE_ON_USE, OBJECT_BOUNDING_BOX - } - - abstract class SvgFill implements Cloneable { - - public abstract Color toColor(); - } - - enum SvgInterpolation { - - SRGB, LINEAR_RGB - } - - abstract class SvgGradient extends SvgFill { - - public List stops; - - public SvgGradientUnits gradientUnits; - - public String gradientTransform; - - public SvgSpreadMethod spreadMethod; - - public SvgInterpolation interpolation; - - @Override - public Color toColor() { - if (stops.isEmpty()) { - return Color.BLACK; - } - return stops.get(0).color; - } - } - - class SvgLinearGradient extends SvgGradient { - - public String x1; - - public String y1; - - public String x2; - - public String y2; - //xlink? - } - - class SvgRadialGradient extends SvgGradient { - - public String cx; - - public String cy; - - public String r; - - public String fx; - - public String fy; - //xlink? - } - - class SvgBitmapFill extends SvgFill { - - public String patternTransform; - - private int characterId; - - @Override - public Color toColor() { - return Color.BLACK; - } - } - - class SvgColor extends SvgFill { - - public Color color; - - public SvgColor(int r, int g, int b, int opacity) { - this(new Color(r, g, b, opacity)); - } - - public SvgColor(int r, int g, int b) { - this(new Color(r, g, b)); - } - - public SvgColor(Color color) { - this.color = color; - } - - @Override - public Color toColor() { - return this.color; - } - } - - class SvgStyle implements Cloneable { - - public Color color; - - public SvgFill fill; - - public double opacity; - - public double fillOpacity; - - public SvgFill strokeFill; - - public Color stopColor; - - public double stopOpacity; - - public double strokeWidth; - - public double strokeOpacity; - - public SvgLineCap strokeLineCap; - - public SvgLineJoin strokeLineJoin; - - public double strokeMiterLimit; - - public SvgStyle parentStyle; - - public SvgStyle() { - fill = new SvgColor(Color.black); - fillOpacity = 1; - strokeFill = null; - strokeWidth = 1; - strokeOpacity = 1; - opacity = 1; - stopOpacity = 1; - stopColor = null; - strokeLineCap = SvgLineCap.BUTT; - strokeLineJoin = SvgLineJoin.MITER; - strokeMiterLimit = 4; - } - - public SvgStyle(SvgStyle parentStyle) { - this(); - this.parentStyle = parentStyle; - } - - public SvgFill getFillWithOpacity() { - if (fill == null) { - return null; - } - if (!(fill instanceof SvgColor)) { - return fill; - } - Color fillColor = ((SvgColor) fill).color; - - int opacity = (int) Math.round(this.opacity * fillOpacity * 255); - if (opacity == 255) { - return fill; - } - - return new SvgColor(fillColor.getRed(), fillColor.getGreen(), fillColor.getBlue(), opacity); - } - - public SvgFill getStrokeFillWithOpacity() { - if (strokeFill == null) { - return null; - } - if (!(strokeFill instanceof SvgColor)) { - return strokeFill; - } - Color strokeFillColor = ((SvgColor) strokeFill).color; - - int opacity = (int) Math.round(this.opacity * strokeOpacity * 255); - if (opacity == 255) { - return strokeFill; - } - - return new SvgColor(strokeFillColor.getRed(), strokeFillColor.getGreen(), strokeFillColor.getBlue(), opacity); - } - - public SvgFill getStrokeColorWithOpacity() { - if (strokeFill == null) { - return null; - } - if (!(strokeFill instanceof SvgColor)) { - return strokeFill; - } - - Color strokeColor = ((SvgColor) strokeFill).color; - - int opacity = (int) Math.round(this.opacity * strokeOpacity * 255); - if (opacity == 255) { - return strokeFill; - } - - return new SvgColor(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(Map idMap, SvgStyle style, String name, String value) { - if (value == null || value.length() == 0) { - return; - } - - switch (name) { - case "color": { - Color color = parseColor(value); - if (color != null) { - style.color = color == TRANSPARENT ? null : color; - } - } - break; - case "fill": { - SvgFill fill = parseFill(idMap, value, style); - if (fill != null) { - style.fill = fill == SVG_TRANSPARENT ? null : fill; - } - } - break; - case "stop-color": { - if ("currentColor".equals(value)) { - if (style.parentStyle != null) { - style.stopColor = style.parentStyle.color; - } - } else if ("inherit".equals(value)) { - showWarning(value + "StopColorNotSupported", "The stop color value '" + value + "' is not supported."); - } else { - style.stopColor = parseColor(value); - } - } - break; - case "fill-opacity": { - double opacity = Double.parseDouble(value); - style.fillOpacity = opacity; - } - break; - case "stop-opacity": { - if ("inherit".equals(value)) { - 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 == 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; - } - } - - private SvgStyle apply(Element element, Map idMap) { - SvgStyle result = clone(); - - 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); - } - } - - if (element.hasAttribute("style")) { - String[] styleDefs = element.getAttribute("style").split(";"); - for (String styleDef : styleDefs) { - String[] parts = styleDef.split(":", 2); - applyStyle(idMap, result, parts[0].trim(), 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) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgBitmapFill.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgBitmapFill.java new file mode 100644 index 000000000..3fa966976 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgBitmapFill.java @@ -0,0 +1,35 @@ +/* + * 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 SvgBitmapFill extends SvgFill { + + public String patternTransform; + + public int characterId; + + @Override + public Color toColor() { + return Color.BLACK; + } +} 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 new file mode 100644 index 000000000..6a0d76baa --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgColor.java @@ -0,0 +1,389 @@ +/* + * 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 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) { + this(new Color(r, g, b, opacity)); + } + + public SvgColor(int r, int g, int b) { + this(new Color(r, g, b)); + } + + public SvgColor(Color color) { + this.color = color; + } + + @Override + public Color toColor() { + return this.color; + } + + public static SvgColor parse(String colorString) { + if (colorString == null) { + return null; + } + + // 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": + return new SvgColor(250, 235, 215); + case "aqua": + return new SvgColor(0, 255, 255); + case "aquamarine": + return new SvgColor(127, 255, 212); + case "azure": + return new SvgColor(240, 255, 255); + case "beige": + return new SvgColor(245, 245, 220); + case "bisque": + return new SvgColor(255, 228, 196); + case "black": + return new SvgColor(0, 0, 0); + case "blanchedalmond": + return new SvgColor(255, 235, 205); + case "blue": + return new SvgColor(0, 0, 255); + case "blueviolet": + return new SvgColor(138, 43, 226); + case "brown": + return new SvgColor(165, 42, 42); + case "burlywood": + return new SvgColor(222, 184, 135); + case "cadetblue": + return new SvgColor(95, 158, 160); + case "chartreuse": + return new SvgColor(127, 255, 0); + case "chocolate": + return new SvgColor(210, 105, 30); + case "coral": + return new SvgColor(255, 127, 80); + case "cornflowerblue": + return new SvgColor(100, 149, 237); + case "cornsilk": + return new SvgColor(255, 248, 220); + case "crimson": + return new SvgColor(220, 20, 60); + case "cyan": + return new SvgColor(0, 255, 255); + case "darkblue": + return new SvgColor(0, 0, 139); + case "darkcyan": + return new SvgColor(0, 139, 139); + case "darkgoldenrod": + return new SvgColor(184, 134, 11); + case "darkgray": + return new SvgColor(169, 169, 169); + case "darkgreen": + return new SvgColor(0, 100, 0); + case "darkgrey": + return new SvgColor(169, 169, 169); + case "darkkhaki": + return new SvgColor(189, 183, 107); + case "darkmagenta": + return new SvgColor(139, 0, 139); + case "darkolivegreen": + return new SvgColor(85, 107, 47); + case "darkorange": + return new SvgColor(255, 140, 0); + case "darkorchid": + return new SvgColor(153, 50, 204); + case "darkred": + return new SvgColor(139, 0, 0); + case "darksalmon": + return new SvgColor(233, 150, 122); + case "darkseagreen": + return new SvgColor(143, 188, 143); + case "darkslateblue": + return new SvgColor(72, 61, 139); + case "darkslategray": + return new SvgColor(47, 79, 79); + case "darkslategrey": + return new SvgColor(47, 79, 79); + case "darkturquoise": + return new SvgColor(0, 206, 209); + case "darkviolet": + return new SvgColor(148, 0, 211); + case "deeppink": + return new SvgColor(255, 20, 147); + case "deepskyblue": + return new SvgColor(0, 191, 255); + case "dimgray": + return new SvgColor(105, 105, 105); + case "dimgrey": + return new SvgColor(105, 105, 105); + case "dodgerblue": + return new SvgColor(30, 144, 255); + case "firebrick": + return new SvgColor(178, 34, 34); + case "floralwhite": + return new SvgColor(255, 250, 240); + case "forestgreen": + return new SvgColor(34, 139, 34); + case "fuchsia": + return new SvgColor(255, 0, 255); + case "gainsboro": + return new SvgColor(220, 220, 220); + case "ghostwhite": + return new SvgColor(248, 248, 255); + case "gold": + return new SvgColor(255, 215, 0); + case "goldenrod": + return new SvgColor(218, 165, 32); + case "gray": + return new SvgColor(128, 128, 128); + case "grey": + return new SvgColor(128, 128, 128); + case "green": + return new SvgColor(0, 128, 0); + case "greenyellow": + return new SvgColor(173, 255, 47); + case "honeydew": + return new SvgColor(240, 255, 240); + case "hotpink": + return new SvgColor(255, 105, 180); + case "indianred": + return new SvgColor(205, 92, 92); + case "indigo": + return new SvgColor(75, 0, 130); + case "ivory": + return new SvgColor(255, 255, 240); + case "khaki": + return new SvgColor(240, 230, 140); + case "lavender": + return new SvgColor(230, 230, 250); + case "lavenderblush": + return new SvgColor(255, 240, 245); + case "lawngreen": + return new SvgColor(124, 252, 0); + case "lemonchiffon": + return new SvgColor(255, 250, 205); + case "lightblue": + return new SvgColor(173, 216, 230); + case "lightcoral": + return new SvgColor(240, 128, 128); + case "lightcyan": + return new SvgColor(224, 255, 255); + case "lightgoldenrodyellow": + return new SvgColor(250, 250, 210); + case "lightgray": + return new SvgColor(211, 211, 211); + case "lightgreen": + return new SvgColor(144, 238, 144); + case "lightgrey": + return new SvgColor(211, 211, 211); + case "lightpink": + return new SvgColor(255, 182, 193); + case "lightsalmon": + return new SvgColor(255, 160, 122); + case "lightseagreen": + return new SvgColor(32, 178, 170); + case "lightskyblue": + return new SvgColor(135, 206, 250); + case "lightslategray": + return new SvgColor(119, 136, 153); + case "lightslategrey": + return new SvgColor(119, 136, 153); + case "lightsteelblue": + return new SvgColor(176, 196, 222); + case "lightyellow": + return new SvgColor(255, 255, 224); + case "lime": + return new SvgColor(0, 255, 0); + case "limegreen": + return new SvgColor(50, 205, 50); + case "linen": + return new SvgColor(250, 240, 230); + case "magenta": + return new SvgColor(255, 0, 255); + case "maroon": + return new SvgColor(128, 0, 0); + case "mediumaquamarine": + return new SvgColor(102, 205, 170); + case "mediumblue": + return new SvgColor(0, 0, 205); + case "mediumorchid": + return new SvgColor(186, 85, 211); + case "mediumpurple": + return new SvgColor(147, 112, 219); + case "mediumseagreen": + return new SvgColor(60, 179, 113); + case "mediumslateblue": + return new SvgColor(123, 104, 238); + case "mediumspringgreen": + return new SvgColor(0, 250, 154); + case "mediumturquoise": + return new SvgColor(72, 209, 204); + case "mediumvioletred": + return new SvgColor(199, 21, 133); + case "midnightblue": + return new SvgColor(25, 25, 112); + case "mintcream": + return new SvgColor(245, 255, 250); + case "mistyrose": + return new SvgColor(255, 228, 225); + case "moccasin": + return new SvgColor(255, 228, 181); + case "navajowhite": + return new SvgColor(255, 222, 173); + case "navy": + return new SvgColor(0, 0, 128); + case "oldlace": + return new SvgColor(253, 245, 230); + case "olive": + return new SvgColor(128, 128, 0); + case "olivedrab": + return new SvgColor(107, 142, 35); + case "orange": + return new SvgColor(255, 165, 0); + case "orangered": + return new SvgColor(255, 69, 0); + case "orchid": + return new SvgColor(218, 112, 214); + case "palegoldenrod": + return new SvgColor(238, 232, 170); + case "palegreen": + return new SvgColor(152, 251, 152); + case "paleturquoise": + return new SvgColor(175, 238, 238); + case "palevioletred": + return new SvgColor(219, 112, 147); + case "papayawhip": + return new SvgColor(255, 239, 213); + case "peachpuff": + return new SvgColor(255, 218, 185); + case "peru": + return new SvgColor(205, 133, 63); + case "pink": + return new SvgColor(255, 192, 203); + case "plum": + return new SvgColor(221, 160, 221); + case "powderblue": + return new SvgColor(176, 224, 230); + case "purple": + return new SvgColor(128, 0, 128); + case "red": + return new SvgColor(255, 0, 0); + case "rosybrown": + return new SvgColor(188, 143, 143); + case "royalblue": + return new SvgColor(65, 105, 225); + case "saddlebrown": + return new SvgColor(139, 69, 19); + case "salmon": + return new SvgColor(250, 128, 114); + case "sandybrown": + return new SvgColor(244, 164, 96); + case "seagreen": + return new SvgColor(46, 139, 87); + case "seashell": + return new SvgColor(255, 245, 238); + case "sienna": + return new SvgColor(160, 82, 45); + case "silver": + return new SvgColor(192, 192, 192); + case "skyblue": + return new SvgColor(135, 206, 235); + case "slateblue": + return new SvgColor(106, 90, 205); + case "slategray": + return new SvgColor(112, 128, 144); + case "slategrey": + return new SvgColor(112, 128, 144); + case "snow": + return new SvgColor(255, 250, 250); + case "springgreen": + return new SvgColor(0, 255, 127); + case "steelblue": + return new SvgColor(70, 130, 180); + case "tan": + return new SvgColor(210, 180, 140); + case "teal": + return new SvgColor(0, 128, 128); + case "thistle": + return new SvgColor(216, 191, 216); + case "tomato": + return new SvgColor(255, 99, 71); + case "turquoise": + return new SvgColor(64, 224, 208); + case "violet": + return new SvgColor(238, 130, 238); + case "wheat": + return new SvgColor(245, 222, 179); + case "white": + return new SvgColor(255, 255, 255); + case "whitesmoke": + return new SvgColor(245, 245, 245); + case "yellow": + return new SvgColor(255, 255, 0); + case "yellowgreen": + return new SvgColor(154, 205, 50); + } + + if (colorString.startsWith("#")) { + String s = colorString.substring(1); + if (s.length() == 3) { + s = "" + s.charAt(0) + s.charAt(0) + s.charAt(1) + s.charAt(1) + s.charAt(2) + s.charAt(2); + } + + int i = Integer.parseInt(s, 16); + return new SvgColor(new Color(i, false)); + } else if (colorString.startsWith("rgb")) { + colorString = colorString.substring(3).trim(); + if (colorString.startsWith("(") && colorString.endsWith(")")) { + colorString = colorString.substring(1, colorString.length() - 1); + String[] args = colorString.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 SvgColor(r, g, b); + } else { + int r = Integer.parseInt(a0); + int g = Integer.parseInt(a1); + int b = Integer.parseInt(a2); + return new SvgColor(r, g, b); + } + } + } + } + + return null; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgFill.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgFill.java new file mode 100644 index 000000000..0a375e4ec --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgFill.java @@ -0,0 +1,28 @@ +/* + * 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 + */ +abstract class SvgFill implements Cloneable { + + public abstract Color toColor(); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgGradient.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgGradient.java new file mode 100644 index 000000000..13380ce7b --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgGradient.java @@ -0,0 +1,45 @@ +/* + * 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; +import java.util.List; + +/** + * + * @author JPEXS + */ +abstract class SvgGradient extends SvgFill { + + public List stops; + + public SvgGradientUnits gradientUnits; + + public String gradientTransform; + + public SvgSpreadMethod spreadMethod; + + public SvgInterpolation interpolation; + + @Override + public Color toColor() { + if (stops.isEmpty()) { + return Color.BLACK; + } + return stops.get(0).color; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgGradientUnits.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgGradientUnits.java new file mode 100644 index 000000000..71fcb236b --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgGradientUnits.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +enum SvgGradientUnits { + + USER_SPACE_ON_USE, OBJECT_BOUNDING_BOX +} 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 new file mode 100644 index 000000000..c6cf468ca --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java @@ -0,0 +1,1290 @@ +/* + * 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.jpexs.decompiler.flash.ReadOnlyTagList; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.ShapeExporter; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.Point; +import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; +import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; +import com.jpexs.decompiler.flash.importers.ShapeImporter; +import com.jpexs.decompiler.flash.importers.SvgPathReader; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.FOCALGRADIENT; +import com.jpexs.decompiler.flash.types.GRADIENT; +import com.jpexs.decompiler.flash.types.GRADRECORD; +import com.jpexs.decompiler.flash.types.LINESTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * + * @author JPEXS + */ +public class SvgImporter { + + private final Set shownWarnings = new HashSet<>(); + + ShapeTag shapeTag; + + public Tag importSvg(ShapeTag st, String svgXml) { + return importSvg(st, svgXml, true); + } + + public Tag importSvg(ShapeTag st, String svgXml, boolean fill) { + shapeTag = st; + + SHAPEWITHSTYLE shapes = new SHAPEWITHSTYLE(); + shapes.fillStyles = new FILLSTYLEARRAY(); + shapes.lineStyles = new LINESTYLEARRAY(); + shapes.fillStyles.fillStyles = new FILLSTYLE[0]; + shapes.lineStyles.lineStyles = new LINESTYLE[0]; + + int shapeNum = st.getShapeNum(); + shapes.shapeRecords = new ArrayList<>(); + + try { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + /*docFactory.setValidating(false); + docFactory.setNamespaceAware(true); + docFactory.setFeature("http://xml.org/sax/features/namespaces", false); + docFactory.setFeature("http://xml.org/sax/features/validation", false); + docFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);*/ + docFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + + Document doc = docBuilder.parse(new InputSource(new StringReader(svgXml))); + Element rootElement = doc.getDocumentElement(); + + Map idMap = new HashMap<>(); + populateIds(rootElement, idMap); + + if (!"svg".equals(rootElement.getTagName())) { + throw new IOException("SVG root element should be 'svg'"); + } + + SvgStyle style = new SvgStyle(this); + style = style.apply(rootElement, idMap); + Matrix transform = new Matrix(); + processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style); + } catch (SAXException | IOException | ParserConfigurationException ex) { + Logger.getLogger(ShapeImporter.class.getName()).log(Level.SEVERE, null, ex); + } + + shapes.shapeRecords.add(new EndShapeRecord()); + + RECT rect = st.getRect(); + int origXmin = rect.Xmin; + int origYmin = rect.Ymin; + rect.Xmin -= origXmin; + rect.Xmax -= origXmin; + rect.Ymin -= origYmin; + rect.Ymax -= origYmin; + + if (!fill) { + // todo: how to calulate the real SVG size? + RECT bounds = shapes.getBounds(); + rect.Xmax = rect.Xmin + bounds.Xmax - Math.min(0, bounds.Xmin); + rect.Ymax = rect.Ymin + bounds.Ymax - Math.min(0, bounds.Ymin); + } + + st.shapes = shapes; + st.setModified(true); + + return (Tag) st; + } + + // Generate id-element map, because getElementById does not work in some cases (namespaces?) + protected void populateIds(Element el, Map out) { + if (el.hasAttribute("id")) { + out.put(el.getAttribute("id"), el); + } + NodeList nodes = el.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + if (nodes.item(i) instanceof Element) { + populateIds((Element) nodes.item(i), out); + } + } + } + + private void processSvgObject(Map idMap, int shapeNum, SHAPEWITHSTYLE shapes, Element element, Matrix transform, SvgStyle style) { + for (int i = 0; i < element.getChildNodes().getLength(); i++) { + Node childNode = element.getChildNodes().item(i); + if (childNode instanceof Element) { + Element childElement = (Element) childNode; + String tagName = childElement.getTagName(); + SvgStyle newStyle = style.apply(childElement, idMap); + Matrix m = Matrix.parseSvgMatrix(childElement.getAttribute("transform"), 1, 1); + Matrix m2 = m == null ? transform : transform.concatenate(m); + if ("g".equals(tagName)) { + processSvgObject(idMap, 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 if ("defs".equals(tagName) || "title".equals(tagName) || "desc".equals(tagName) + || "radialGradient".equals(tagName) || "linearGradient".equals(tagName)) { + // ignore + } else { + showWarning(tagName + "tagNotSupported", "The SVG tag '" + tagName + "' is not supported."); + } + } + } + } + + private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List commands, Matrix transform, SvgStyle style) { + Matrix transform2 = transform.preConcatenate(Matrix.getScaleInstance(SWF.unitDivisor)); + Point prevPoint = new Point(0, 0); + Point startPoint = prevPoint; + double x0 = 0; + double y0 = 0; + + 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; + + List newRecords = new ArrayList<>(); + + newRecords.add(scrStyle); + + LINESTYLE lineStyleObj = scrStyle.lineStyles.lineStyles.length < 1 ? null : scrStyle.lineStyles.lineStyles[0]; + LINESTYLE2 lineStyle2Obj = null; + if (lineStyleObj instanceof LINESTYLE2) { + lineStyle2Obj = (LINESTYLE2) lineStyleObj; + lineStyle2Obj.noClose = true; + } + + for (PathCommand command : commands) { + double x = x0; + double y = y0; + Point p; + char cmd = Character.toUpperCase(command.command); + switch (cmd) { + case 'M': + StyleChangeRecord scr = new StyleChangeRecord(); + if (fillStyle != 0) { + scr.stateFillStyle1 = true; + scr.fillStyle1 = fillStyle; + } + if (lineStyle != 0) { + scr.lineStyle = lineStyle; + scr.stateLineStyle = true; + } + + x = command.params[0]; + y = command.params[1]; + + p = transform2.transform(x, y); + scr.moveDeltaX = (int) Math.round(p.x); + scr.moveDeltaY = (int) Math.round(p.y); + prevPoint = p; + scr.stateMoveTo = true; + + newRecords.add(scr); + startPoint = p; + break; + case 'Z': + StraightEdgeRecord serz = new StraightEdgeRecord(); + p = startPoint; + serz.deltaX = (int) Math.round(p.x - prevPoint.x); + serz.deltaY = (int) Math.round(p.y - prevPoint.y); + prevPoint = p; + serz.generalLineFlag = true; + newRecords.add(serz); + if (lineStyle2Obj != null) { + lineStyle2Obj.noClose = false; + } + break; + case 'L': + StraightEdgeRecord serl = new StraightEdgeRecord(); + x = command.params[0]; + y = command.params[1]; + + p = transform2.transform(x, y); + serl.deltaX = (int) Math.round(p.x - prevPoint.x); + serl.deltaY = (int) Math.round(p.y - prevPoint.y); + prevPoint = p; + serl.generalLineFlag = true; + serl.simplify(); + newRecords.add(serl); + break; + case 'H': + StraightEdgeRecord serh = new StraightEdgeRecord(); + x = command.params[0]; + + p = transform2.transform(x, y); + serh.deltaX = (int) Math.round(p.x - prevPoint.x); + prevPoint = p; + newRecords.add(serh); + break; + case 'V': + StraightEdgeRecord serv = new StraightEdgeRecord(); + y = command.params[0]; + + p = transform2.transform(x, y); + serv.deltaY = (int) Math.round(p.y - prevPoint.y); + prevPoint = p; + serv.vertLineFlag = true; + newRecords.add(serv); + break; + case 'Q': + CurvedEdgeRecord cer = new CurvedEdgeRecord(); + x = command.params[0]; + y = command.params[1]; + + p = transform2.transform(x, y); + cer.controlDeltaX = (int) Math.round(p.x - prevPoint.x); + cer.controlDeltaY = (int) Math.round(p.y - prevPoint.y); + prevPoint = p; + + x = command.params[2]; + y = command.params[3]; + + p = transform2.transform(x, y); + cer.anchorDeltaX = (int) Math.round(p.x - prevPoint.x); + cer.anchorDeltaY = (int) Math.round(p.y - prevPoint.y); + prevPoint = p; + newRecords.add(cer); + break; + case 'C': + showWarning("cubicCurvesNotSupported", "Cubic curves are not supported by Flash."); + + // create at least something... + Point pStart = prevPoint; + Point pControl1; + + x = command.params[0]; + y = command.params[1]; + + pControl1 = transform2.transform(x, y); + + x = command.params[2]; + y = command.params[3]; + + Point pControl2 = transform2.transform(x, y); + + x = command.params[4]; + y = command.params[5]; + + p = transform2.transform(x, y); + + //StraightEdgeRecord serc = new StraightEdgeRecord(); + //serc.generalLineFlag = true; + //serc.deltaX = (int) Math.round(p.x - prevPoint.x); + //serc.deltaY = (int) Math.round(p.y - prevPoint.y); + //newRecords.add(serc); + List quadCoordinates = new CubicToQuad().cubicToQuad(pStart.x, pStart.y, pControl1.x, pControl1.y, pControl2.x, pControl2.y, p.x, p.y, 1); + for (int i = 2; i < quadCoordinates.size();) { + CurvedEdgeRecord cerc = new CurvedEdgeRecord(); + p = new Point(quadCoordinates.get(i++), quadCoordinates.get(i++)); + cerc.controlDeltaX = (int) Math.round(p.x - prevPoint.x); + cerc.controlDeltaY = (int) Math.round(p.y - prevPoint.y); + prevPoint = p; + + p = new Point(quadCoordinates.get(i++), quadCoordinates.get(i++)); + cerc.anchorDeltaX = (int) Math.round(p.x - prevPoint.x); + cerc.anchorDeltaY = (int) Math.round(p.y - prevPoint.y); + prevPoint = p; + newRecords.add(cerc); + } + + break; + default: + Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Unknown command: {0}", command); + return; + } + + x0 = x; + y0 = y; + } + applyStyleGradients(SHAPERECORD.getBounds(newRecords), scrStyle, transform2, shapeNum, style); + shapes.shapeRecords.addAll(newRecords); + } + + 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 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; + 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 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'; + + 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; + 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; + } + + 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 double calcAngle(double ux, double uy, double vx, double vy) { + double lu = Math.sqrt(ux * ux + uy * uy); + double lv = Math.sqrt(ux * ux + uy * uy); + double sign = Math.signum(ux * vy - uy * vx); + if (sign == 0) { + sign = 1; + } + + return sign * Math.acos(ux * vx + uy * vy / (lu * lv)); + } + + 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; + + processEllipse(shapeNum, shapes, transform, style, cx, cy, r, r); + } + + 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; + + processEllipse(shapeNum, shapes, transform, style, cx, cy, rx, ry); + } + + private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, double cx, double cy, double rx, double ry) { + 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 pathCommands = new ArrayList<>(); + PathCommand scr = new PathCommand(); + scr.command = 'M'; + scr.params = new double[]{cx + rx, cy}; + pathCommands.add(scr); + + double[] points = new double[]{ + rx, -sqrt2Minus1RY, + sqrt2RXHalf, -sqrt2RYHalf, + sqrt2Minus1RX, -ry, + 0, -ry, + -sqrt2Minus1RX, -ry, + -sqrt2RXHalf, -sqrt2RYHalf, + -rx, -sqrt2Minus1RY, + -rx, 0, + -rx, sqrt2Minus1RY, + -sqrt2RXHalf, sqrt2RYHalf, + -sqrt2Minus1RX, ry, + 0, ry, + sqrt2Minus1RX, ry, + sqrt2RXHalf, sqrt2RYHalf, + rx, sqrt2Minus1RY, + rx, 0}; + + for (int i = 0; i < points.length; i += 4) { + PathCommand cer = new PathCommand(); + cer.command = 'Q'; + cer.params = new double[]{cx + points[i], cy + points[i + 1], cx + points[i + 2], cy + points[i + 3]}; + + /*double tetha = 30; + tetha *= Math.PI / 180; + double x1 = points[i]; + double y1 = points[i + 1]; + double x2 = points[i + 2]; + double y2 = points[i + 3]; + + double x1Comma = Math.cos(tetha) * x1 + Math.sin(tetha) * y1; + double y1Comma = -Math.sin(tetha) * x1 + Math.cos(tetha) * y1; + double x2Comma = Math.cos(tetha) * x2 + Math.sin(tetha) * y2; + double y2Comma = -Math.sin(tetha) * x2 + Math.cos(tetha) * y2; + + cer.params = new double[]{cx + x1Comma, cy + y1Comma, cx + x2Comma, cy + y2Comma};*/ + pathCommands.add(cer); + } + + PathCommand serz = new PathCommand(); + serz.command = 'Z'; + pathCommands.add(serz); + + 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 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); + } + } + + PathCommand serz = new PathCommand(); + serz.command = 'Z'; + pathCommands.add(serz); + + 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 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'; + double x0 = 0; + double y0 = 0; + + List 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); + 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'; + pathCommands.add(serz); + } + + processCommands(shapeNum, shapes, pathCommands, transform, style); + } + + //Stub for w3 test. TODO: refactor and move to test directory. It's here because of easy access - compiling single file + private static void svgTest(String name) throws IOException, InterruptedException { + if (!new File(name + ".original.svg").exists()) { + URL svgUrl = new URL("http://www.w3.org/Graphics/SVG/Test/20061213/svggen/" + name + ".svg"); + byte[] svgData = Helper.readStream(svgUrl.openStream()); + Helper.writeFile(name + ".original.svg", svgData); + + URL pngUrl = new URL("http://www.w3.org/Graphics/SVG/Test/20061213/png/full-" + name + ".png"); + 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(); + DefineShape4Tag st = new DefineShape4Tag(swf); + st = (DefineShape4Tag) (new SvgImporter().importSvg(st, svgDataS)); + swf.addTag(st); + SerializableImage si = new SerializableImage(800, 600, BufferedImage.TYPE_4BYTE_ABGR); + BitmapExporter.export(swf, st.shapes, Color.yellow, si, new Matrix(), new ColorTransform()); + List li = new ArrayList<>(); + li.add(st); + ImageIO.write(si.getBufferedImage(), "PNG", new File(name + ".imported.png")); + new ShapeExporter().exportShapes(null, "./outex/", new ReadOnlyTagList(li), new ShapeExportSettings(ShapeExportMode.SVG, 1), null); + } + + //Test for SVG + public static void main(String[] args) throws IOException, InterruptedException { + //svgTest("pservers-grad-01-b"); + svgTest("pservers-grad-07-b"); + } + + private void applyFillGradients(SvgFill fill, FILLSTYLE fillStyle, RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) { + if (fill == null || fillStyle == null) { + return; + } + if (fill instanceof SvgGradient) { + SvgGradient gfill = (SvgGradient) fill; + Matrix gradientMatrix = Matrix.parseSvgMatrix(gfill.gradientTransform, SWF.unitDivisor, 1); + gradientMatrix = transform.concatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)).concatenate(gradientMatrix); + fillStyle.gradientMatrix = gradientMatrix.toMATRIX(); + if (fill instanceof SvgLinearGradient) { + SvgLinearGradient lgfill = (SvgLinearGradient) fill; + fillStyle.fillStyleType = FILLSTYLE.LINEAR_GRADIENT; + fillStyle.gradient = new GRADIENT(); + double x1; + if (lgfill.x1.endsWith("%")) { + x1 = Double.parseDouble(lgfill.x1.substring(0, lgfill.x1.length() - 1)) / 100; + } else { + x1 = Double.parseDouble(lgfill.x1); + } + //x1 = x1 - (-819.2); + + double y1; + if (lgfill.y1.endsWith("%")) { + y1 = Double.parseDouble(lgfill.y1.substring(0, lgfill.y1.length() - 1)) / 100; + } else { + y1 = Double.parseDouble(lgfill.y1); + } + double x2; + if (lgfill.x2.endsWith("%")) { + x2 = Double.parseDouble(lgfill.x2.substring(0, lgfill.x2.length() - 1)) / 100; + } else { + x2 = Double.parseDouble(lgfill.x2); + } + //x2 = x2 - 819.2; + double y2; + if (lgfill.y2.endsWith("%")) { + y2 = Double.parseDouble(lgfill.y2.substring(0, lgfill.y2.length() - 1)) / 100; + } else { + y2 = Double.parseDouble(lgfill.y2); + } + x1 = x1 * SWF.unitDivisor; + y1 = y1 * SWF.unitDivisor; + x2 = x2 * SWF.unitDivisor; + y2 = y2 * SWF.unitDivisor; + + Matrix boundingBoxMatrix = new Matrix(); + if (lgfill.gradientUnits == SvgGradientUnits.OBJECT_BOUNDING_BOX) { + boundingBoxMatrix.scaleX = (bounds.Xmax - bounds.Xmin) / SWF.unitDivisor; + boundingBoxMatrix.rotateSkew0 = 0; + boundingBoxMatrix.rotateSkew1 = 0; + boundingBoxMatrix.scaleY = (bounds.Ymax - bounds.Ymin) / SWF.unitDivisor; + boundingBoxMatrix.translateX = bounds.Xmin; + boundingBoxMatrix.translateY = bounds.Ymin; + } + + Matrix xyMatrix = new Matrix(); + xyMatrix.scaleX = x2 - x1; + xyMatrix.rotateSkew0 = y2 - y1; + xyMatrix.rotateSkew1 = -xyMatrix.rotateSkew0; + xyMatrix.scaleY = xyMatrix.scaleX; + + xyMatrix = xyMatrix.preConcatenate(boundingBoxMatrix); + + Matrix zeroStartMatrix = Matrix.getTranslateInstance(0.5, 0); + + Matrix scaleMatrix = Matrix.getScaleInstance(1 / 16384.0 / 2); + Matrix transMatrix = Matrix.getTranslateInstance(x1, y1); + + Matrix tMatrix = new Matrix(); + tMatrix = tMatrix.preConcatenate(scaleMatrix); + tMatrix = tMatrix.preConcatenate(zeroStartMatrix); + tMatrix = tMatrix.preConcatenate(xyMatrix); + + tMatrix = tMatrix.preConcatenate(transMatrix); + Point p1 = tMatrix.transform(new Point(-16384, 0)); + Point p2 = tMatrix.transform(new Point(16384, 0)); + + tMatrix = tMatrix.preConcatenate(new Matrix(fillStyle.gradientMatrix)); + fillStyle.gradientMatrix = tMatrix.toMATRIX(); + } else if (fill instanceof SvgRadialGradient) { + SvgRadialGradient rgfill = (SvgRadialGradient) fill; + double cx; + if (rgfill.cx.endsWith("%")) { + cx = Double.parseDouble(rgfill.cx.substring(0, rgfill.cx.length() - 1)) / 100; + } else { + cx = Double.parseDouble(rgfill.cx); + } + double cy; + if (rgfill.cy.endsWith("%")) { + cy = Double.parseDouble(rgfill.cy.substring(0, rgfill.cy.length() - 1)) / 100; + } else { + cy = Double.parseDouble(rgfill.cy); + } + + double r; + if (rgfill.r.endsWith("%")) { + r = Double.parseDouble(rgfill.r.substring(0, rgfill.r.length() - 1)) / 100; + } else { + r = Double.parseDouble(rgfill.r); + } + + Matrix boundingBoxMatrix = new Matrix(); + if (rgfill.gradientUnits == SvgGradientUnits.OBJECT_BOUNDING_BOX) { + boundingBoxMatrix.scaleX = (bounds.Xmax - bounds.Xmin) / SWF.unitDivisor; + boundingBoxMatrix.rotateSkew0 = 0; + boundingBoxMatrix.rotateSkew1 = 0; + boundingBoxMatrix.scaleY = (bounds.Ymax - bounds.Ymin) / SWF.unitDivisor; + boundingBoxMatrix.translateX = bounds.Xmin; + boundingBoxMatrix.translateY = bounds.Ymin; + } + + fillStyle.gradientMatrix = Matrix.getTranslateInstance(SWF.unitDivisor * cx, SWF.unitDivisor * cy).concatenate(new Matrix(fillStyle.gradientMatrix)).concatenate(Matrix.getScaleInstance(r / 819.2)).preConcatenate(boundingBoxMatrix).toMATRIX(); + + double fx; + if (rgfill.fx.endsWith("%")) { + fx = Double.parseDouble(rgfill.fx.substring(0, rgfill.fx.length() - 1)) / 100; + } else { + fx = Double.parseDouble(rgfill.fx); + } + double fy; + if (rgfill.fy.endsWith("%")) { + fy = Double.parseDouble(rgfill.fy.substring(0, rgfill.fy.length() - 1)) / 100; + } else { + fy = Double.parseDouble(rgfill.fy); + } + if (!rgfill.fx.equals(rgfill.cx) || !rgfill.fy.equals(rgfill.cy)) { + fillStyle.fillStyleType = FILLSTYLE.FOCAL_RADIAL_GRADIENT; + fillStyle.gradient = new FOCALGRADIENT(); + FOCALGRADIENT fg = (FOCALGRADIENT) fillStyle.gradient; + double f = Math.sqrt((fx - cx) * (fx - cx) + (fy - cy) * (fy - cy)) / 819.2; + fg.focalPoint = (float) f; + } else { + fillStyle.fillStyleType = FILLSTYLE.RADIAL_GRADIENT; + fillStyle.gradient = new GRADIENT(); + } + } + switch (gfill.spreadMethod) { + case PAD: + fillStyle.gradient.spreadMode = GRADIENT.SPREAD_PAD_MODE; + break; + case REFLECT: + fillStyle.gradient.spreadMode = GRADIENT.SPREAD_REFLECT_MODE; + break; + case REPEAT: + fillStyle.gradient.spreadMode = GRADIENT.SPREAD_REPEAT_MODE; + break; + } + switch (gfill.interpolation) { + case LINEAR_RGB: + fillStyle.gradient.interpolationMode = GRADIENT.INTERPOLATION_LINEAR_RGB_MODE; + break; + case SRGB: + fillStyle.gradient.interpolationMode = GRADIENT.INTERPOLATION_RGB_MODE; + break; + } + + fillStyle.gradient.gradientRecords = new GRADRECORD[gfill.stops.size()]; + for (int i = 0; i < gfill.stops.size(); i++) { + SvgStop stop = gfill.stops.get(i); + Color color = stop.color; + color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) Math.round(color.getAlpha() * style.opacity)); + fillStyle.gradient.gradientRecords[i] = new GRADRECORD(); + fillStyle.gradient.gradientRecords[i].inShape3 = shapeNum >= 3; + fillStyle.gradient.gradientRecords[i].color = getRGB(shapeNum, color); + fillStyle.gradient.gradientRecords[i].ratio = (int) Math.round(stop.offset * 255); + } + } else if (fill instanceof SvgBitmapFill) { + SvgBitmapFill bfill = (SvgBitmapFill) fill; + fillStyle.fillStyleType = FILLSTYLE.REPEATING_BITMAP; + fillStyle.bitmapId = bfill.characterId; + Matrix fillMatrix = Matrix.parseSvgMatrix(bfill.patternTransform, SWF.unitDivisor, SWF.unitDivisor); + fillMatrix = transform.concatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)).concatenate(fillMatrix); + fillStyle.bitmapMatrix = fillMatrix.toMATRIX(); + } + } + + private void applyStyleGradients(RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) { + SvgFill fill = style.getFillWithOpacity(); + if (fill != null) { + applyFillGradients(fill, scr.fillStyles.fillStyles[0], bounds, scr, transform, shapeNum, style); + } + SvgFill strokeFill = style.getStrokeFillWithOpacity(); + if (strokeFill != null) { + if (scr.lineStyles.lineStyles.length > 0 && scr.lineStyles.lineStyles[0] instanceof LINESTYLE2) { + applyFillGradients(strokeFill, ((LINESTYLE2) scr.lineStyles.lineStyles[0]).fillType, bounds, scr, transform, shapeNum, style); + } + } + } + + private StyleChangeRecord getStyleChangeRecord(int shapeNum, SvgStyle style) { + StyleChangeRecord scr = new StyleChangeRecord(); + + scr.stateNewStyles = true; + scr.fillStyles = new FILLSTYLEARRAY(); + scr.stateFillStyle1 = true; + scr.stateLineStyle = true; + SvgFill fill = style.getFillWithOpacity(); + if (fill != null) { + scr.fillStyles.fillStyles = new FILLSTYLE[1]; + scr.fillStyles.fillStyles[0] = new FILLSTYLE(); + if (fill instanceof SvgColor) { + Color colorFill = fill.toColor(); + scr.fillStyles.fillStyles[0].color = getRGB(shapeNum, colorFill); + scr.fillStyles.fillStyles[0].fillStyleType = FILLSTYLE.SOLID; + } else if (fill instanceof SvgGradient) { + //...apply in second step - applyStyleGradients + } + + scr.fillStyle1 = 1; + } else { + scr.fillStyles.fillStyles = new FILLSTYLE[0]; + scr.fillStyle1 = 0; + } + + scr.lineStyles = new LINESTYLEARRAY(); + SvgFill strokeFill = style.getStrokeFillWithOpacity(); + if (strokeFill != null) { + Color lineColor = strokeFill.toColor(); + + scr.lineStyles.lineStyles = new LINESTYLE[1]; + LINESTYLE lineStyle = shapeNum <= 3 ? new LINESTYLE() : new LINESTYLE2(); + lineStyle.color = getRGB(shapeNum, 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; + if (!(strokeFill instanceof SvgColor)) { + lineStyle2.hasFillFlag = true; + lineStyle2.fillType = new FILLSTYLE(); + //...apply in second step - applyStyleGradients + }//Single color does not need fillType attribute + + 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 = (float) 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]; + scr.lineStyle = 0; + } + + return scr; + } + + private RGB getRGB(int shapeNum, Color color) { + if (shapeNum < 3 && color.getAlpha() != 0xff) { + showWarning("transparentColorNotSupported", "Transparent color is not supported in shape " + shapeNum); + } + + return shapeNum >= 3 ? new RGBA(color) : new RGB(color); + } + + class PathCommand { + + public char command; + + public double[] params; + } + + void showWarning(String name, String text) { + if (!shownWarnings.contains(name)) { + Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, text); + shownWarnings.add(name); + } + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgInterpolation.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgInterpolation.java new file mode 100644 index 000000000..b0b9f4512 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgInterpolation.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +enum SvgInterpolation { + + SRGB, LINEAR_RGB +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLineCap.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLineCap.java new file mode 100644 index 000000000..7b0343beb --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLineCap.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +enum SvgLineCap { + + BUTT, ROUND, SQUARE +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLineJoin.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLineJoin.java new file mode 100644 index 000000000..de0218790 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLineJoin.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +enum SvgLineJoin { + + MITER, ROUND, BEVEL +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLinearGradient.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLinearGradient.java new file mode 100644 index 000000000..32f5f935e --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgLinearGradient.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +class SvgLinearGradient extends SvgGradient { + + public String x1; + + public String y1; + + public String x2; + + public String y2; + //xlink? +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgRadialGradient.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgRadialGradient.java new file mode 100644 index 000000000..e56a5f228 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgRadialGradient.java @@ -0,0 +1,35 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +class SvgRadialGradient extends SvgGradient { + + public String cx; + + public String cy; + + public String r; + + public String fx; + + public String fy; + //xlink? +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgSpreadMethod.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgSpreadMethod.java new file mode 100644 index 000000000..b48b259c3 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgSpreadMethod.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +enum SvgSpreadMethod { + + PAD, REFLECT, REPEAT +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStop.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStop.java new file mode 100644 index 000000000..c60c2fde8 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStop.java @@ -0,0 +1,40 @@ +/* + * 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 SvgStop implements Comparable { + + public Color color; + + public double offset; + + public SvgStop(Color color, double offset) { + this.color = color; + this.offset = offset; + } + + @Override + public int compareTo(SvgStop o) { + return (int) Math.signum(offset - o.offset); + } +} 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 new file mode 100644 index 000000000..4697c2e7b --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java @@ -0,0 +1,598 @@ +/* + * 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.jpexs.decompiler.flash.importers.ShapeImporter; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +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; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * + * @author JPEXS + */ +class SvgStyle implements Cloneable { + + public Color color; + + public SvgFill fill; + + public double opacity; + + public double fillOpacity; + + public SvgFill strokeFill; + + public Color stopColor; + + public double stopOpacity; + + public double strokeWidth; + + public double strokeOpacity; + + public SvgLineCap strokeLineCap; + + public SvgLineJoin strokeLineJoin; + + public double strokeMiterLimit; + + public SvgStyle parentStyle; + + private final SvgImporter importer; + + private final Random random = new Random(); + + public SvgStyle(SvgImporter importer) { + this.importer = importer; + fill = new SvgColor(Color.black); + fillOpacity = 1; + strokeFill = null; + strokeWidth = 1; + strokeOpacity = 1; + opacity = 1; + stopOpacity = 1; + stopColor = null; + strokeLineCap = SvgLineCap.BUTT; + strokeLineJoin = SvgLineJoin.MITER; + strokeMiterLimit = 4; + } + + public SvgStyle(SvgStyle parentStyle) { + this(parentStyle.importer); + this.parentStyle = parentStyle; + } + + public SvgFill getFillWithOpacity() { + if (fill == null) { + return null; + } + if (!(fill instanceof SvgColor)) { + return fill; + } + Color fillColor = ((SvgColor) fill).color; + + int opacity = (int) Math.round(this.opacity * fillOpacity * 255); + if (opacity == 255) { + return fill; + } + + return new SvgColor(fillColor.getRed(), fillColor.getGreen(), fillColor.getBlue(), opacity); + } + + public SvgFill getStrokeFillWithOpacity() { + if (strokeFill == null) { + return null; + } + if (!(strokeFill instanceof SvgColor)) { + return strokeFill; + } + Color strokeFillColor = ((SvgColor) strokeFill).color; + + int opacity = (int) Math.round(this.opacity * strokeOpacity * 255); + if (opacity == 255) { + return strokeFill; + } + + return new SvgColor(strokeFillColor.getRed(), strokeFillColor.getGreen(), strokeFillColor.getBlue(), opacity); + } + + public SvgFill getStrokeColorWithOpacity() { + if (strokeFill == null) { + return null; + } + if (!(strokeFill instanceof SvgColor)) { + return strokeFill; + } + + Color strokeColor = ((SvgColor) strokeFill).color; + + int opacity = (int) Math.round(this.opacity * strokeOpacity * 255); + if (opacity == 255) { + return strokeFill; + } + + return new SvgColor(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(); + } + } + + //FIXME - matrices + private SvgFill parseGradient(Map idMap, Element el, SvgStyle style) { + SvgGradientUnits gradientUnits = null; + String gradientTransform = null; + SvgSpreadMethod spreadMethod = null; + SvgInterpolation interpolation = null; + + String x1 = null; + String y1 = null; + String x2 = null; + String y2 = null; + + String cx = null; + String cy = null; + String fx = null; + String fy = null; + String r = null; + + List stops = new ArrayList<>(); + //inheritance: + if (el.hasAttribute("xlink:href")) { + String parent = el.getAttribute("xlink:href"); + if (parent.startsWith("#")) { + String parentId = parent.substring(1); + Element parent_el = idMap.get(parentId); + if (parent_el == null) { + importer.showWarning("fillNotSupported", "Parent gradient not found."); + return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); + } + + if ("linearGradient".equals(el.getTagName()) && parent_el.getTagName().equals(el.getTagName())) { + SvgLinearGradient parentFill = (SvgLinearGradient) parseGradient(idMap, parent_el, style); + gradientUnits = parentFill.gradientUnits; + gradientTransform = parentFill.gradientTransform; + spreadMethod = parentFill.spreadMethod; + + x1 = parentFill.x1; + y1 = parentFill.y1; + x2 = parentFill.x2; + y2 = parentFill.y2; + interpolation = parentFill.interpolation; + stops = parentFill.stops; + } + if ("radialGradient".equals(el.getTagName()) && parent_el.getTagName().equals(el.getTagName())) { + SvgRadialGradient parentFill = (SvgRadialGradient) parseGradient(idMap, parent_el, style); + gradientUnits = parentFill.gradientUnits; + gradientTransform = parentFill.gradientTransform; + spreadMethod = parentFill.spreadMethod; + + cx = parentFill.cx; + cy = parentFill.cy; + fx = parentFill.fx; + fy = parentFill.fy; + r = parentFill.r; + interpolation = parentFill.interpolation; + stops = parentFill.stops; + } + + } else { + importer.showWarning("fillNotSupported", "Parent gradient invalid."); + return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); + } + } + + if (el.hasAttribute("gradientUnits")) { + switch (el.getAttribute("gradientUnits")) { + case "userSpaceOnUse": + gradientUnits = SvgGradientUnits.USER_SPACE_ON_USE; + break; + case "objectBoundingBox": + gradientUnits = SvgGradientUnits.OBJECT_BOUNDING_BOX; + break; + default: + importer.showWarning("fillNotSupported", "Unsupported gradientUnits: " + el.getAttribute("gradientUnits")); + return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); + } + + } + if (el.hasAttribute("gradientTransform")) { + gradientTransform = el.getAttribute("gradientTransform"); + } + if (el.hasAttribute("spreadMethod")) { + switch (el.getAttribute("spreadMethod")) { + case "pad": + spreadMethod = SvgSpreadMethod.PAD; + break; + case "reflect": + spreadMethod = SvgSpreadMethod.REFLECT; + break; + case "repeat": + spreadMethod = SvgSpreadMethod.REPEAT; + break; + } + } + if (el.hasAttribute("x1")) { + x1 = el.getAttribute("x1").trim(); + } + if (el.hasAttribute("y1")) { + y1 = el.getAttribute("y1").trim(); + } + if (el.hasAttribute("x2")) { + x2 = el.getAttribute("x2").trim(); + } + if (el.hasAttribute("y2")) { + y2 = el.getAttribute("y2").trim(); + } + + if (el.hasAttribute("cx")) { + cx = el.getAttribute("cx").trim(); + } + if (el.hasAttribute("cy")) { + cy = el.getAttribute("cy").trim(); + } + if (el.hasAttribute("fx")) { + fx = el.getAttribute("fx").trim(); + } + if (el.hasAttribute("fy")) { + fy = el.getAttribute("fy").trim(); + } + if (el.hasAttribute("r")) { + r = el.getAttribute("r").trim(); + } + if (el.hasAttribute("color-interpolation") && interpolation == null) { //prefer inherit + switch (el.getAttribute("color-interpolation")) { + case "sRGB": + interpolation = SvgInterpolation.SRGB; + break; + case "linearRGB": + interpolation = SvgInterpolation.LINEAR_RGB; + break; + case "auto": //without preference, put SRGB there + interpolation = SvgInterpolation.SRGB; + break; + } + } + if (interpolation == null) { + interpolation = SvgInterpolation.SRGB; + } + + if (gradientUnits == null) { + gradientUnits = SvgGradientUnits.OBJECT_BOUNDING_BOX; + } + if (spreadMethod == null) { + spreadMethod = SvgSpreadMethod.PAD; + } + + if (x1 == null) { + x1 = "0%"; + } + if (y1 == null) { + y1 = "0%"; + } + + if (x2 == null) { + x2 = "100%"; + } + if (y2 == null) { + y2 = "0%"; + } + + if (cx == null) { + cx = "50%"; + } + if (cy == null) { + cy = "50%"; + } + + if (r == null) { + r = "50%"; + } + if (fx == null) { + fx = cx; + } + if (fy == null) { + fy = cy; + } + + NodeList stopNodes = el.getElementsByTagName("stop"); + boolean stopsCleared = false; + for (int i = 0; i < stopNodes.getLength(); i++) { + Node node = stopNodes.item(i); + if (node instanceof Element) { + Element stopEl = (Element) node; + SvgStyle newStyle = style.apply(stopEl, idMap); + + String offsetStr = stopEl.getAttribute("offset"); + double offset; + if (offsetStr.endsWith("%")) { + offset = Double.parseDouble(offsetStr.substring(0, offsetStr.length() - 1)) / 100; + } else { + offset = Double.parseDouble(offsetStr); + } + Color color = newStyle.stopColor; + if (color == null) { + color = Color.BLACK; + } + + int alpha = (int) Math.round(newStyle.stopOpacity * 255); + color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); + if (!stopsCleared) { //It has some stop nodes -> remove all inherited stops + stopsCleared = true; + stops = new ArrayList<>(); + } + stops.add(new SvgStop(color, offset)); + } + } + + if ("linearGradient".equals(el.getTagName())) { + SvgLinearGradient ret = new SvgLinearGradient(); + ret.x1 = x1; + ret.y1 = y1; + ret.x2 = x2; + ret.y2 = y2; + ret.spreadMethod = spreadMethod; + ret.gradientTransform = gradientTransform; + ret.gradientUnits = gradientUnits; + ret.stops = stops; + ret.interpolation = interpolation; + return ret; + } else if ("radialGradient".equals(el.getTagName())) { + SvgRadialGradient ret = new SvgRadialGradient(); + ret.cx = cx; + ret.cy = cy; + ret.fx = fx; + ret.fy = fy; + ret.r = r; + ret.spreadMethod = spreadMethod; + ret.gradientTransform = gradientTransform; + ret.gradientUnits = gradientUnits; + ret.stops = stops; + ret.interpolation = interpolation; + return ret; + } else { + return null; + } + } + + 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) { + return null; + } + + Pattern idPat = Pattern.compile("url\\(#([^)]+)\\).*"); + java.util.regex.Matcher mPat = idPat.matcher(rgbStr); + + if (mPat.matches()) { + String elementId = mPat.group(1); + Element e = idMap.get(elementId); + if (e != null) { + String tagName = e.getTagName(); + if ("linearGradient".equals(tagName)) { + return parseGradient(idMap, e, new SvgStyle(style)); //? new style + } + + if ("radialGradient".equals(tagName)) { + return parseGradient(idMap, e, new SvgStyle(style)); //? new style + } + + if ("pattern".equals(tagName)) { + Element element = null; + NodeList childNodes = e.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + if (childNodes.item(i) instanceof Element) { + if (element != null) { + element = null; + break; + } + + element = (Element) childNodes.item(i); + } + } + + if (element != null && "image".equals(element.getTagName())) { + String attr = element.getAttribute("xlink:href").trim(); + // this is ugly, but supports the format which is exported by FFDec + if (attr.startsWith("data:image/") && attr.contains("base64,")) { + String base64 = attr.substring(attr.indexOf("base64,") + 7); + byte[] data = Helper.base64StringToByteArray(base64); + try { + ImageTag imageTag = new ShapeImporter().addImage(importer.shapeTag, data, 0); + SvgBitmapFill bitmapFill = new SvgBitmapFill(); + bitmapFill.characterId = imageTag.characterID; + if (e.hasAttribute("patternTransform")) { + bitmapFill.patternTransform = e.getAttribute("patternTransform"); + } + + return bitmapFill; + } catch (IOException ex) { + Logger.getLogger(ShapeImporter.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + } + + importer.showWarning("fillNotSupported", "Unknown fill style. Random color assigned."); + return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); + } + + rgbStr = rgbStr.substring(elementId.length() + 6).trim(); // remove url(#...) + } + + SvgColor result = SvgColor.parse(rgbStr); + if (result == null) { + importer.showWarning("fillNotSupported", "Unknown fill style. Random color assigned."); + return new SvgColor(random.nextInt(256), random.nextInt(256), random.nextInt(256)); + } + + return result; + } + + private void applyStyle(Map idMap, SvgStyle style, String name, String value) { + if (value == null || value.length() == 0) { + return; + } + + 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; + } + } 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-opacity": { + double opacity = Double.parseDouble(value); + style.fillOpacity = opacity; + } + 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(); + + 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); + } + } + + if (element.hasAttribute("style")) { + String[] styleDefs = element.getAttribute("style").split(";"); + for (String styleDef : styleDefs) { + String[] parts = styleDef.split(":", 2); + applyStyle(idMap, result, parts[0].trim(), parts[1].trim()); + } + } + + return result; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index ff4f59846..a9daf860e 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -99,6 +99,7 @@ import com.jpexs.decompiler.flash.importers.ShapeImporter; import com.jpexs.decompiler.flash.importers.SwfXmlImporter; import com.jpexs.decompiler.flash.importers.SymbolClassImporter; import com.jpexs.decompiler.flash.importers.TextImporter; +import com.jpexs.decompiler.flash.importers.svg.SvgImporter; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; @@ -2632,8 +2633,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se data = Helper.readFile(selfile.getAbsolutePath()); } try { - ShapeImporter shapeImporter = new ShapeImporter(); - Tag newTag = svgText != null ? shapeImporter.importSvg(st, svgText) : shapeImporter.importImage(st, data); + Tag newTag = svgText != null ? new SvgImporter().importSvg(st, svgText) : new ShapeImporter().importImage(st, data); SWF swf = st.getSwf(); if (newTag != null) { refreshTree(swf); @@ -2682,8 +2682,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se data = Helper.readFile(selfile.getAbsolutePath()); } try { - ShapeImporter shapeImporter = new ShapeImporter(); - Tag newTag = svgText != null ? shapeImporter.importSvg(st, svgText, false) : shapeImporter.importImage(st, data, 0, false); + Tag newTag = svgText != null ? new SvgImporter().importSvg(st, svgText, false) : new ShapeImporter().importImage(st, data, 0, false); SWF swf = st.getSwf(); if (newTag != null) { refreshTree(swf); From 0940243fa10fcd91c1c569dc6612bda0e2b720eb Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 27 Dec 2015 13:59:19 +0100 Subject: [PATCH 03/14] #980 Export to Fla Problems fixed --- .../com/jpexs/decompiler/flash/xfl/XFLConverter.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index f818a9700..9b326b0b9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -1966,8 +1966,16 @@ public class XFLConverter { continue; } String embeddedCharacters = fontChars; - embeddedCharacters = embeddedCharacters.replace("\u00A0", ""); //nonbreak space - embeddedCharacters = embeddedCharacters.replace(".", ""); + embeddedCharacters = embeddedCharacters.replace("\u00A0", ""); // nonbreak space + embeddedCharacters = embeddedCharacters.replace(".", ""); // todo: honfika: why? + for (char i = 0; i < 32; i++) { + if (i == 9 || i == 10 || i == 13) { + continue; + } + + embeddedCharacters = embeddedCharacters.replace("" + i, ""); // not supported in xml + } + boolean hasAllRanges = false; for (int r = 0; r < CharacterRanges.rangeCount(); r++) { int[] codes = CharacterRanges.rangeCodes(r); From d0e03b7be9c1ac98fb3018d64efe9a79233cb85d Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 27 Dec 2015 17:34:36 +0100 Subject: [PATCH 04/14] svg style inheritance (not ready) --- .../flash/importers/svg/SvgColor.java | 6 - .../flash/importers/svg/SvgImporter.java | 551 +++++++++--------- .../flash/importers/svg/SvgStyle.java | 299 ++++++---- .../flash/importers/svg/SvgStyleProperty.java | 76 +++ .../importers/svg/SvgTransparentFill.java | 38 ++ 5 files changed, 575 insertions(+), 395 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgTransparentFill.java 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; + } +} From de696eeb18dd1a2ee2a87f625e6f69f1a083daeb Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 27 Dec 2015 19:07:32 +0100 Subject: [PATCH 05/14] svg importer fixes --- .../flash/importers/svg/SvgImporter.java | 381 +++++++++++++++++- .../flash/importers/svg/SvgStyle.java | 52 ++- 2 files changed, 411 insertions(+), 22 deletions(-) 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 fe8aa500a..ea5dc1c94 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 @@ -695,29 +695,29 @@ public class SvgImporter { 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; + double cx = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("cy"); - double cy = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double cy = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("r"); - double r = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double r = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; processEllipse(shapeNum, shapes, transform, style, cx, cy, r, r); } 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; + double cx = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("cy"); - double cy = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double cy = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("rx"); - double rx = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double rx = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("ry"); - double ry = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double ry = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; processEllipse(shapeNum, shapes, transform, style, cx, cy, rx, ry); } @@ -782,22 +782,22 @@ public class SvgImporter { 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; + double x = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("y"); - double y = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double y = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("width"); - double width = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double width = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("height"); - double height = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double height = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("rx"); - double rx = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double rx = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("ry"); - double ry = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double ry = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; if (rx == 0 && ry != 0) { rx = ry; @@ -893,16 +893,16 @@ public class SvgImporter { 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; + double x1 = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("y1"); - double y1 = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double y1 = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("x2"); - double x2 = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double x2 = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; attr = childElement.getAttribute("y2"); - double y2 = attr.length() > 0 ? Double.parseDouble(attr) : 0; + double y2 = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; List pathCommands = new ArrayList<>(); PathCommand scr = new PathCommand(); @@ -997,7 +997,7 @@ public class SvgImporter { DefineShape4Tag st = new DefineShape4Tag(swf); st = (DefineShape4Tag) (new SvgImporter().importSvg(st, svgDataS)); swf.addTag(st); - SerializableImage si = new SerializableImage(800, 600, BufferedImage.TYPE_4BYTE_ABGR); + SerializableImage si = new SerializableImage(480, 360, BufferedImage.TYPE_4BYTE_ABGR); BitmapExporter.export(swf, st.shapes, Color.yellow, si, new Matrix(), new ColorTransform()); List li = new ArrayList<>(); li.add(st); @@ -1007,8 +1007,281 @@ public class SvgImporter { //Test for SVG public static void main(String[] args) throws IOException, InterruptedException { - //svgTest("pservers-grad-01-b"); + svgTest("animate-elem-02-t"); + svgTest("animate-elem-03-t"); + svgTest("animate-elem-04-t"); + svgTest("animate-elem-05-t"); + svgTest("animate-elem-06-t"); + svgTest("animate-elem-07-t"); + svgTest("animate-elem-08-t"); + svgTest("animate-elem-09-t"); + svgTest("animate-elem-10-t"); + svgTest("animate-elem-11-t"); + svgTest("animate-elem-12-t"); + svgTest("animate-elem-13-t"); + svgTest("animate-elem-14-t"); + svgTest("animate-elem-15-t"); + svgTest("animate-elem-17-t"); + svgTest("animate-elem-19-t"); + svgTest("animate-elem-20-t"); + svgTest("animate-elem-21-t"); + svgTest("animate-elem-22-b"); + svgTest("animate-elem-23-t"); + svgTest("animate-elem-24-t"); + svgTest("animate-elem-25-t"); + svgTest("animate-elem-26-t"); + svgTest("animate-elem-27-t"); + svgTest("animate-elem-28-t"); + svgTest("animate-elem-29-b"); + svgTest("animate-elem-30-t"); + svgTest("animate-elem-31-t"); + svgTest("animate-elem-32-t"); + svgTest("animate-elem-33-t"); + svgTest("animate-elem-34-t"); + svgTest("animate-elem-36-t"); + svgTest("animate-elem-37-t"); + svgTest("animate-elem-39-t"); + svgTest("animate-elem-40-t"); + svgTest("animate-elem-41-t"); + svgTest("animate-elem-44-t"); + svgTest("animate-elem-46-t"); + svgTest("animate-elem-52-t"); + svgTest("animate-elem-60-t"); + svgTest("animate-elem-61-t"); + svgTest("animate-elem-62-t"); + svgTest("animate-elem-63-t"); + svgTest("animate-elem-64-t"); + svgTest("animate-elem-65-t"); + svgTest("animate-elem-66-t"); + svgTest("animate-elem-67-t"); + svgTest("animate-elem-68-t"); + svgTest("animate-elem-69-t"); + svgTest("animate-elem-70-t"); + svgTest("animate-elem-77-t"); + svgTest("animate-elem-78-t"); + svgTest("animate-elem-80-t"); + svgTest("animate-elem-81-t"); + svgTest("animate-elem-82-t"); + svgTest("animate-elem-83-t"); + svgTest("animate-elem-84-t"); + svgTest("animate-elem-85-t"); + svgTest("color-prof-01-f"); + svgTest("color-prop-01-b"); + svgTest("color-prop-02-f"); + svgTest("color-prop-03-t"); + svgTest("coords-coord-01-t"); + svgTest("coords-coord-02-t"); + svgTest("coords-trans-01-b"); + svgTest("coords-trans-02-t"); + svgTest("coords-trans-03-t"); + svgTest("coords-trans-04-t"); + svgTest("coords-trans-05-t"); + svgTest("coords-trans-06-t"); + svgTest("coords-units-01-b"); + svgTest("coords-units-02-b"); + svgTest("coords-units-03-b"); + svgTest("coords-viewattr-01-b"); + svgTest("coords-viewattr-02-b"); + svgTest("coords-viewattr-03-b"); + svgTest("extend-namespace-01-f"); + svgTest("filters-blend-01-b"); + svgTest("filters-color-01-b"); + svgTest("filters-composite-02-b"); + svgTest("filters-comptran-01-b"); + svgTest("filters-conv-01-f"); + svgTest("filters-diffuse-01-f"); + svgTest("filters-displace-01-f"); + svgTest("filters-example-01-b"); + svgTest("filters-felem-01-b"); + svgTest("filters-gauss-01-b"); + svgTest("filters-image-01-b"); + svgTest("filters-light-01-f"); + svgTest("filters-morph-01-f"); + svgTest("filters-offset-01-b"); + svgTest("filters-specular-01-f"); + svgTest("filters-tile-01-b"); + svgTest("filters-turb-01-f"); + svgTest("fonts-desc-02-t"); + svgTest("fonts-elem-01-t"); + svgTest("fonts-elem-02-t"); + svgTest("fonts-elem-03-b"); + svgTest("fonts-elem-04-b"); + svgTest("fonts-elem-05-t"); + svgTest("fonts-elem-06-t"); + svgTest("fonts-elem-07-b"); + svgTest("fonts-glyph-02-t"); + svgTest("fonts-glyph-03-t"); + svgTest("fonts-glyph-04-t"); + svgTest("fonts-kern-01-t"); + svgTest("interact-cursor-01-f"); + svgTest("interact-dom-01-b"); + svgTest("interact-events-01-b"); + svgTest("interact-order-01-b"); + svgTest("interact-order-02-b"); + svgTest("interact-order-03-b"); + svgTest("interact-zoom-01-t"); + svgTest("linking-a-01-b"); + svgTest("linking-a-02-b"); + svgTest("linking-a-03-b"); + svgTest("linking-a-04-t"); + svgTest("linking-a-05-t"); + svgTest("linking-a-07-t"); + svgTest("linking-uri-01-b"); + svgTest("linking-uri-02-b"); + svgTest("linking-uri-03-t"); + svgTest("masking-intro-01-f"); + svgTest("masking-mask-01-b"); + svgTest("masking-opacity-01-b"); + svgTest("masking-path-01-b"); + svgTest("masking-path-02-b"); + svgTest("masking-path-03-b"); + svgTest("masking-path-04-b"); + svgTest("masking-path-05-f"); + svgTest("metadata-example-01-b"); + svgTest("painting-fill-01-t"); + svgTest("painting-fill-02-t"); + svgTest("painting-fill-03-t"); + svgTest("painting-fill-04-t"); + svgTest("painting-fill-05-b"); + svgTest("painting-marker-01-f"); + svgTest("painting-marker-02-f"); + svgTest("painting-marker-03-f"); + svgTest("painting-render-01-b"); + svgTest("painting-stroke-01-t"); + svgTest("painting-stroke-02-t"); + svgTest("painting-stroke-03-t"); + svgTest("painting-stroke-04-t"); + svgTest("painting-stroke-07-t"); + svgTest("paths-data-01-t"); + svgTest("paths-data-02-t"); + svgTest("paths-data-03-f"); + svgTest("paths-data-04-t"); + svgTest("paths-data-05-t"); + svgTest("paths-data-06-t"); + svgTest("paths-data-07-t"); + svgTest("paths-data-08-t"); + svgTest("paths-data-09-t"); + svgTest("paths-data-10-t"); + svgTest("paths-data-12-t"); + svgTest("paths-data-13-t"); + svgTest("paths-data-14-t"); + svgTest("paths-data-15-t"); + svgTest("pservers-grad-01-b"); + svgTest("pservers-grad-02-b"); + svgTest("pservers-grad-03-b"); + svgTest("pservers-grad-04-b"); + svgTest("pservers-grad-05-b"); + svgTest("pservers-grad-06-b"); svgTest("pservers-grad-07-b"); + svgTest("pservers-grad-08-b"); + svgTest("pservers-grad-09-b"); + svgTest("pservers-grad-10-b"); + svgTest("pservers-grad-11-b"); + svgTest("pservers-grad-12-b"); + svgTest("pservers-grad-13-b"); + svgTest("pservers-grad-14-b"); + svgTest("pservers-grad-15-b"); + svgTest("pservers-grad-16-b"); + svgTest("pservers-grad-17-b"); + svgTest("pservers-grad-18-b"); + svgTest("pservers-grad-19-b"); + svgTest("pservers-pattern-01-b"); + svgTest("render-elems-01-t"); + svgTest("render-elems-02-t"); + svgTest("render-elems-03-t"); + svgTest("render-elems-06-t"); + svgTest("render-elems-07-t"); + svgTest("render-elems-08-t"); + svgTest("render-groups-01-b"); + svgTest("render-groups-03-t"); + svgTest("script-handle-01-b"); + svgTest("script-handle-02-b"); + svgTest("script-handle-03-b"); + svgTest("script-handle-04-b"); + svgTest("shapes-circle-01-t"); + svgTest("shapes-circle-02-t"); + svgTest("shapes-ellipse-01-t"); + svgTest("shapes-ellipse-02-t"); + svgTest("shapes-intro-01-t"); + svgTest("shapes-line-01-t"); + svgTest("shapes-polygon-01-t"); + svgTest("shapes-polyline-01-t"); + svgTest("shapes-rect-01-t"); + svgTest("shapes-rect-02-t"); + svgTest("struct-cond-01-t"); + svgTest("struct-cond-02-t"); + svgTest("struct-cond-03-t"); + svgTest("struct-defs-01-t"); + svgTest("struct-dom-01-b"); + svgTest("struct-dom-02-b"); + svgTest("struct-dom-03-b"); + svgTest("struct-dom-04-b"); + svgTest("struct-dom-05-b"); + svgTest("struct-dom-06-b"); + svgTest("struct-frag-01-t"); + svgTest("struct-frag-02-t"); + svgTest("struct-frag-03-t"); + svgTest("struct-frag-04-t"); + svgTest("struct-frag-05-t"); + svgTest("struct-frag-06-t"); + svgTest("struct-group-01-t"); + svgTest("struct-group-02-b"); + svgTest("struct-group-03-t"); + svgTest("struct-image-01-t"); + svgTest("struct-image-02-b"); + svgTest("struct-image-03-t"); + svgTest("struct-image-04-t"); + svgTest("struct-image-05-b"); + svgTest("struct-image-06-t"); + svgTest("struct-image-07-t"); + svgTest("struct-image-08-t"); + svgTest("struct-image-09-t"); + svgTest("struct-image-10-t"); + svgTest("struct-symbol-01-b"); + svgTest("struct-use-01-t"); + svgTest("struct-use-03-t"); + svgTest("struct-use-05-b"); + svgTest("styling-css-01-b"); + svgTest("styling-css-02-b"); + svgTest("styling-css-03-b"); + svgTest("styling-css-04-f"); + svgTest("styling-css-05-b"); + svgTest("styling-css-06-b"); + svgTest("styling-inherit-01-b"); + svgTest("styling-pres-01-t"); + svgTest("text-align-01-b"); + svgTest("text-align-02-b"); + svgTest("text-align-03-b"); + svgTest("text-align-04-b"); + svgTest("text-align-05-b"); + svgTest("text-align-06-b"); + svgTest("text-align-08-b"); + svgTest("text-altglyph-01-b"); + svgTest("text-deco-01-b"); + svgTest("text-fonts-01-t"); + svgTest("text-fonts-02-t"); + svgTest("text-fonts-03-t"); + svgTest("text-intro-01-t"); + svgTest("text-intro-02-b"); + svgTest("text-intro-03-b"); + svgTest("text-intro-04-t"); + svgTest("text-intro-05-t"); + svgTest("text-path-01-b"); + svgTest("text-spacing-01-b"); + svgTest("text-text-01-b"); + svgTest("text-text-03-b"); + svgTest("text-text-04-t"); + svgTest("text-text-05-t"); + svgTest("text-text-06-t"); + svgTest("text-text-07-t"); + svgTest("text-text-08-b"); + svgTest("text-tref-01-b"); + svgTest("text-tselect-01-b"); + svgTest("text-tselect-02-f"); + svgTest("text-tspan-01-b"); + svgTest("text-ws-01-t"); + svgTest("text-ws-02-t"); + svgTest("types-basicDOM-01-b"); } private void applyFillGradients(SvgFill fill, FILLSTYLE fillStyle, RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) { @@ -1168,6 +1441,7 @@ public class SvgImporter { } fillStyle.gradient.gradientRecords = new GRADRECORD[gfill.stops.size()]; + int prevRatio = -1; for (int i = 0; i < gfill.stops.size(); i++) { SvgStop stop = gfill.stops.get(i); Color color = stop.color; @@ -1175,7 +1449,12 @@ public class SvgImporter { fillStyle.gradient.gradientRecords[i] = new GRADRECORD(); fillStyle.gradient.gradientRecords[i].inShape3 = shapeNum >= 3; fillStyle.gradient.gradientRecords[i].color = getRGB(shapeNum, color); - fillStyle.gradient.gradientRecords[i].ratio = (int) Math.round(stop.offset * 255); + int ratio = Math.max((int) Math.round(stop.offset * 255), prevRatio + 1); + fillStyle.gradient.gradientRecords[i].ratio = ratio; + prevRatio = ratio; + if (prevRatio == 255) { + break; + } } } else if (fill instanceof SvgBitmapFill) { SvgBitmapFill bfill = (SvgBitmapFill) fill; @@ -1281,6 +1560,68 @@ public class SvgImporter { return shapeNum >= 3 ? new RGBA(color) : new RGB(color); } + private double parseCoordinate(String value, double relativeTo) { + return parseLength(value, relativeTo); + } + + private double parseLength(String value, double relativeTo) { + if (value == null) { + throw new NumberFormatException(); + } + + value = value.toLowerCase(); + String unit = null; + if (value.endsWith("em") + || value.endsWith("ex") + || value.endsWith("px") + || value.endsWith("in") + || value.endsWith("cm") + || value.endsWith("mm") + || value.endsWith("pt") + || value.endsWith("pc")) { + unit = value.substring(value.length() - 2); + value = value.substring(0, value.length() - 2); + } else if (value.endsWith("%")) { + unit = "%"; + value = value.substring(0, value.length() - 1); + } + + double result = Double.parseDouble(value); + if (unit != null) { + switch (unit) { + case "em": + case "ex": + // todo: font things + break; + case "in": + result *= getDpi(); + break; + case "pt": + result *= getDpi() / 72; + break; + case "pc": + result *= getDpi() / 6; + break; + case "cm": + result *= getDpi() / 2.54; + break; + case "mm": + result *= getDpi() / 25.4; + break; + case "%": + result = relativeTo * result / 100; + break; + } + } + + return result; + } + + private double getDpi() { + return 96; + + } + class PathCommand { public char command; 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 754b6f88f..ece9a924a 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 @@ -66,6 +66,8 @@ class SvgStyle implements Cloneable { private final SvgImporter importer; + private final double epsilon = 0.001; + private final Random random = new Random(); public SvgStyle(SvgImporter importer) { @@ -98,6 +100,14 @@ class SvgStyle implements Cloneable { Color fillColor = ((SvgColor) fill).color; int opacity = (int) Math.round(this.opacity * fillOpacity * 255); + if (opacity > 255) { + opacity = 255; + } + + if (opacity < 0) { + opacity = 0; + } + if (opacity == 255) { return fill; } @@ -115,6 +125,14 @@ class SvgStyle implements Cloneable { Color strokeFillColor = ((SvgColor) strokeFill).color; int opacity = (int) Math.round(this.opacity * strokeOpacity * 255); + if (opacity > 255) { + opacity = 255; + } + + if (opacity < 0) { + opacity = 0; + } + if (opacity == 255) { return strokeFill; } @@ -366,7 +384,7 @@ class SvgStyle implements Cloneable { ret.spreadMethod = spreadMethod; ret.gradientTransform = gradientTransform; ret.gradientUnits = gradientUnits; - ret.stops = stops; + ret.stops = fixStops(stops); ret.interpolation = interpolation; return ret; } else if ("radialGradient".equals(el.getTagName())) { @@ -379,7 +397,7 @@ class SvgStyle implements Cloneable { ret.spreadMethod = spreadMethod; ret.gradientTransform = gradientTransform; ret.gradientUnits = gradientUnits; - ret.stops = stops; + ret.stops = fixStops(stops); ret.interpolation = interpolation; return ret; } else { @@ -387,6 +405,36 @@ class SvgStyle implements Cloneable { } } + private List fixStops(List stops) { + if (stops.isEmpty()) { + stops.add(new SvgStop(SvgTransparentFill.INSTANCE.toColor(), 0)); + stops.add(new SvgStop(SvgTransparentFill.INSTANCE.toColor(), 1)); + } else if (stops.size() == 1) { + SvgStop stop0 = stops.get(0); + stop0.offset = 0; + stops.add(new SvgStop(stop0.color, 1)); + } + + double offset = 0; + for (SvgStop stop : stops) { + if (stop.offset < offset) { + stop.offset = offset; + } + + if (stop.offset > 1) { + stop.offset = 1; + } + + offset = stop.offset; + } + + if (Math.abs(offset - 1) > epsilon) { + stops.add(new SvgStop(stops.get(stops.size() - 1).color, 1)); + } + + return stops; + } + private SvgFill parseFill(Map idMap, String fillStr, SvgStyle style) { if (fillStr == null) { return null; From b3148f83a8db102ba24b699a60f5d74fdfe897f8 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 27 Dec 2015 19:13:40 +0100 Subject: [PATCH 06/14] fix --- .../com/jpexs/decompiler/flash/importers/svg/SvgStyle.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ece9a924a..87f6a1398 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 @@ -700,9 +700,9 @@ class SvgStyle implements Cloneable { SvgStyleProperty styleProperty = SvgStyleProperty.getByName(name); if (styleProperty == null) { importer.showWarning(name + "StyleNotSupported", "The style '" + name + "' is not supported."); + } else { + applyStyle(idMap, result, this, styleProperty, parts[1].trim()); } - - applyStyle(idMap, result, this, styleProperty, parts[1].trim()); } } From 9c7ecca459d570cd7050885cea9bfc85ced939ad Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 27 Dec 2015 20:22:53 +0100 Subject: [PATCH 07/14] svg fixes --- .../flash/exporters/ShapeExporter.java | 18 +-- .../flash/importers/svg/SvgColor.java | 6 +- .../flash/importers/svg/SvgImporter.java | 12 +- .../flash/importers/svg/SvgStyle.java | 124 +++++++++++------- 4 files changed, 93 insertions(+), 67 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java index 7db211829..36a214035 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java @@ -30,7 +30,6 @@ import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; import com.jpexs.decompiler.flash.helpers.BMPFile; import com.jpexs.decompiler.flash.helpers.ImageHelper; import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.RenderContext; import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; @@ -81,28 +80,25 @@ public class ShapeExporter { int currentIndex = 1; for (final Tag t : tags) { if (t instanceof ShapeTag) { + final ShapeTag st = (ShapeTag) t; if (evl != null) { evl.handleExportingEvent("shape", currentIndex, count, t.getName()); } - int characterID = 0; - if (t instanceof CharacterTag) { - characterID = ((CharacterTag) t).getCharacterId(); - } - String ext = "svg"; + int characterID = st.getCharacterId(); + String ext = ".svg"; if (settings.mode == ShapeExportMode.PNG) { - ext = "png"; + ext = ".png"; } if (settings.mode == ShapeExportMode.BMP) { - ext = "bmp"; + ext = ".bmp"; } if (settings.mode == ShapeExportMode.CANVAS) { - ext = "html"; + ext = ".html"; } - final File file = new File(outdir + File.separator + characterID + "." + ext); + final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName() + ext)); new RetryTask(() -> { - ShapeTag st = (ShapeTag) t; switch (settings.mode) { case SVG: try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) { 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 72dbe3a71..8c251f1ea 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 @@ -364,9 +364,9 @@ class SvgColor extends SvgFill { 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); + int r = (int) Math.round(Double.parseDouble(a0.substring(0, a0.length() - 1)) * 255.0 / 100); + int g = (int) Math.round(Double.parseDouble(a1.substring(0, a1.length() - 1)) * 255.0 / 100); + int b = (int) Math.round(Double.parseDouble(a2.substring(0, a2.length() - 1)) * 255.0 / 100); return new SvgColor(r, g, b); } else { int r = Integer.parseInt(a0); 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 ea5dc1c94..ec5e7a881 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 @@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; import com.jpexs.decompiler.flash.importers.ShapeImporter; import com.jpexs.decompiler.flash.importers.SvgPathReader; import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.types.ColorTransform; @@ -985,14 +986,14 @@ public class SvgImporter { if (!new File(name + ".original.svg").exists()) { URL svgUrl = new URL("http://www.w3.org/Graphics/SVG/Test/20061213/svggen/" + name + ".svg"); byte[] svgData = Helper.readStream(svgUrl.openStream()); - Helper.writeFile(name + ".original.svg", svgData); + Helper.writeFile(name + ".orig.svg", svgData); URL pngUrl = new URL("http://www.w3.org/Graphics/SVG/Test/20061213/png/full-" + name + ".png"); byte[] pngData = Helper.readStream(pngUrl.openStream()); - Helper.writeFile(name + ".original.png", pngData); + Helper.writeFile(name + ".orig.png", pngData); } - String svgDataS = Helper.readTextFile(name + ".original.svg"); + String svgDataS = Helper.readTextFile(name + ".orig.svg"); SWF swf = new SWF(); DefineShape4Tag st = new DefineShape4Tag(swf); st = (DefineShape4Tag) (new SvgImporter().importSvg(st, svgDataS)); @@ -1002,6 +1003,11 @@ public class SvgImporter { List li = new ArrayList<>(); li.add(st); ImageIO.write(si.getBufferedImage(), "PNG", new File(name + ".imported.png")); + ExportAssetsTag eat = new ExportAssetsTag(swf); + eat.tags.add(st.getCharacterId()); + eat.names.add(name); + swf.addTag(eat); + swf.assignExportNamesToSymbols(); new ShapeExporter().exportShapes(null, "./outex/", new ReadOnlyTagList(li), new ShapeExportSettings(ShapeExportMode.SVG, 1), null); } 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 87f6a1398..1d5af35b9 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,6 +22,7 @@ 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; @@ -512,8 +513,8 @@ class SvgStyle implements Cloneable { return result; } - private void applyStyle(Map idMap, SvgStyle style, SvgStyle parentStyle, SvgStyleProperty styleProperty, String value) { - boolean inherit = styleProperty.isInherited(); + private boolean applyStyle(Map idMap, SvgStyle style, SvgStyle parentStyle, SvgStyleProperty styleProperty, String value) { + boolean inherit = false; if ("inherit".equals(value)) { value = ""; inherit = true; @@ -639,73 +640,96 @@ class SvgStyle implements Cloneable { 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; - } + if (inherit) { + applyInheritedStyle(style, parentStyle, styleProperty); + ok = true; + } + + return ok; + } + + private void applyInheritedStyle(SvgStyle style, SvgStyle parentStyle, SvgStyleProperty styleProperty) { + String name = styleProperty.name(); + 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; } } public SvgStyle apply(Element element, Map idMap) { SvgStyle result = new SvgStyle(importer); - 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); - } - } - + Map styleValues = new HashMap<>(); if (element.hasAttribute("style")) { String[] styleDefs = element.getAttribute("style").split(";"); for (String styleDef : styleDefs) { + if (!styleDef.contains(":")) { + continue; + } + String[] parts = styleDef.split(":", 2); String name = parts[0].trim(); + String value = parts[1].trim(); SvgStyleProperty styleProperty = SvgStyleProperty.getByName(name); if (styleProperty == null) { importer.showWarning(name + "StyleNotSupported", "The style '" + name + "' is not supported."); } else { - applyStyle(idMap, result, this, styleProperty, parts[1].trim()); + styleValues.put(name, value); } } } + for (SvgStyleProperty styleProperty : SvgStyleProperty.getProperties()) { + String name = styleProperty.name(); + boolean ok = false; + if (styleValues.containsKey(name)) { + ok = applyStyle(idMap, result, this, styleProperty, styleValues.get(name)); + } + + if (!ok && element.hasAttribute(name)) { + String attr = element.getAttribute(name).trim(); + ok = applyStyle(idMap, result, this, styleProperty, attr); + } + + if (!ok && styleProperty.isInherited()) { + applyInheritedStyle(result, this, styleProperty); + } + } + return result; } } From 5ab44babaed5d947910a3c274f958e5b9694f1b9 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sun, 27 Dec 2015 21:00:17 +0100 Subject: [PATCH 08/14] svg testing: shape size fix --- .../com/jpexs/decompiler/flash/importers/svg/SvgImporter.java | 2 ++ 1 file changed, 2 insertions(+) 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 ec5e7a881..90e0273ff 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 @@ -1008,6 +1008,8 @@ public class SvgImporter { eat.names.add(name); swf.addTag(eat); swf.assignExportNamesToSymbols(); + st.shapeBounds.Xmax = (int) (si.getWidth() * SWF.unitDivisor); + st.shapeBounds.Ymax = (int) (si.getHeight() * SWF.unitDivisor); new ShapeExporter().exportShapes(null, "./outex/", new ReadOnlyTagList(li), new ShapeExportSettings(ShapeExportMode.SVG, 1), null); } From 27cdd48a1f4d10d84688bd11ae9eec26447ea38e Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Mon, 28 Dec 2015 08:30:16 +0100 Subject: [PATCH 09/14] svg style inheritance fix --- .../flash/importers/svg/SvgImporter.java | 401 ++++++++------- .../flash/importers/svg/SvgStyle.java | 478 ++++++++---------- .../flash/importers/svg/SvgStyleProperty.java | 10 +- 3 files changed, 421 insertions(+), 468 deletions(-) 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 90e0273ff..2f6591da5 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 @@ -121,8 +121,7 @@ public class SvgImporter { throw new IOException("SVG root element should be 'svg'"); } - SvgStyle style = new SvgStyle(this); - style = style.apply(rootElement, idMap); + SvgStyle style = new SvgStyle(this, idMap, rootElement); Matrix transform = new Matrix(); processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style); } catch (SAXException | IOException | ParserConfigurationException ex) { @@ -171,7 +170,7 @@ public class SvgImporter { if (childNode instanceof Element) { Element childElement = (Element) childNode; String tagName = childElement.getTagName(); - SvgStyle newStyle = style.apply(childElement, idMap); + SvgStyle newStyle = new SvgStyle(this, idMap, childElement); Matrix m = Matrix.parseSvgMatrix(childElement.getAttribute("transform"), 1, 1); Matrix m2 = m == null ? transform : transform.concatenate(m); if ("g".equals(tagName)) { @@ -1015,64 +1014,64 @@ public class SvgImporter { //Test for SVG public static void main(String[] args) throws IOException, InterruptedException { - svgTest("animate-elem-02-t"); - svgTest("animate-elem-03-t"); - svgTest("animate-elem-04-t"); - svgTest("animate-elem-05-t"); - svgTest("animate-elem-06-t"); - svgTest("animate-elem-07-t"); - svgTest("animate-elem-08-t"); - svgTest("animate-elem-09-t"); - svgTest("animate-elem-10-t"); - svgTest("animate-elem-11-t"); - svgTest("animate-elem-12-t"); - svgTest("animate-elem-13-t"); - svgTest("animate-elem-14-t"); - svgTest("animate-elem-15-t"); - svgTest("animate-elem-17-t"); - svgTest("animate-elem-19-t"); - svgTest("animate-elem-20-t"); - svgTest("animate-elem-21-t"); - svgTest("animate-elem-22-b"); - svgTest("animate-elem-23-t"); - svgTest("animate-elem-24-t"); - svgTest("animate-elem-25-t"); - svgTest("animate-elem-26-t"); - svgTest("animate-elem-27-t"); - svgTest("animate-elem-28-t"); - svgTest("animate-elem-29-b"); - svgTest("animate-elem-30-t"); - svgTest("animate-elem-31-t"); - svgTest("animate-elem-32-t"); - svgTest("animate-elem-33-t"); - svgTest("animate-elem-34-t"); - svgTest("animate-elem-36-t"); - svgTest("animate-elem-37-t"); - svgTest("animate-elem-39-t"); - svgTest("animate-elem-40-t"); - svgTest("animate-elem-41-t"); - svgTest("animate-elem-44-t"); - svgTest("animate-elem-46-t"); - svgTest("animate-elem-52-t"); - svgTest("animate-elem-60-t"); - svgTest("animate-elem-61-t"); - svgTest("animate-elem-62-t"); - svgTest("animate-elem-63-t"); - svgTest("animate-elem-64-t"); - svgTest("animate-elem-65-t"); - svgTest("animate-elem-66-t"); - svgTest("animate-elem-67-t"); - svgTest("animate-elem-68-t"); - svgTest("animate-elem-69-t"); - svgTest("animate-elem-70-t"); - svgTest("animate-elem-77-t"); - svgTest("animate-elem-78-t"); - svgTest("animate-elem-80-t"); - svgTest("animate-elem-81-t"); - svgTest("animate-elem-82-t"); - svgTest("animate-elem-83-t"); - svgTest("animate-elem-84-t"); - svgTest("animate-elem-85-t"); +// svgTest("animate-elem-02-t"); +// svgTest("animate-elem-03-t"); +// svgTest("animate-elem-04-t"); +// svgTest("animate-elem-05-t"); +// svgTest("animate-elem-06-t"); +// svgTest("animate-elem-07-t"); +// svgTest("animate-elem-08-t"); +// svgTest("animate-elem-09-t"); +// svgTest("animate-elem-10-t"); +// svgTest("animate-elem-11-t"); +// svgTest("animate-elem-12-t"); +// svgTest("animate-elem-13-t"); +// svgTest("animate-elem-14-t"); +// svgTest("animate-elem-15-t"); +// svgTest("animate-elem-17-t"); +// svgTest("animate-elem-19-t"); +// svgTest("animate-elem-20-t"); +// svgTest("animate-elem-21-t"); +// svgTest("animate-elem-22-b"); +// svgTest("animate-elem-23-t"); +// svgTest("animate-elem-24-t"); +// svgTest("animate-elem-25-t"); +// svgTest("animate-elem-26-t"); +// svgTest("animate-elem-27-t"); +// svgTest("animate-elem-28-t"); +// svgTest("animate-elem-29-b"); +// svgTest("animate-elem-30-t"); +// svgTest("animate-elem-31-t"); +// svgTest("animate-elem-32-t"); +// svgTest("animate-elem-33-t"); +// svgTest("animate-elem-34-t"); +// svgTest("animate-elem-36-t"); +// svgTest("animate-elem-37-t"); +// svgTest("animate-elem-39-t"); +// svgTest("animate-elem-40-t"); +// svgTest("animate-elem-41-t"); +// svgTest("animate-elem-44-t"); +// svgTest("animate-elem-46-t"); +// svgTest("animate-elem-52-t"); +// svgTest("animate-elem-60-t"); +// svgTest("animate-elem-61-t"); +// svgTest("animate-elem-62-t"); +// svgTest("animate-elem-63-t"); +// svgTest("animate-elem-64-t"); +// svgTest("animate-elem-65-t"); +// svgTest("animate-elem-66-t"); +// svgTest("animate-elem-67-t"); +// svgTest("animate-elem-68-t"); +// svgTest("animate-elem-69-t"); +// svgTest("animate-elem-70-t"); +// svgTest("animate-elem-77-t"); +// svgTest("animate-elem-78-t"); +// svgTest("animate-elem-80-t"); +// svgTest("animate-elem-81-t"); +// svgTest("animate-elem-82-t"); +// svgTest("animate-elem-83-t"); +// svgTest("animate-elem-84-t"); +// svgTest("animate-elem-85-t"); svgTest("color-prof-01-f"); svgTest("color-prop-01-b"); svgTest("color-prop-02-f"); @@ -1092,60 +1091,60 @@ public class SvgImporter { svgTest("coords-viewattr-02-b"); svgTest("coords-viewattr-03-b"); svgTest("extend-namespace-01-f"); - svgTest("filters-blend-01-b"); - svgTest("filters-color-01-b"); - svgTest("filters-composite-02-b"); - svgTest("filters-comptran-01-b"); - svgTest("filters-conv-01-f"); - svgTest("filters-diffuse-01-f"); - svgTest("filters-displace-01-f"); - svgTest("filters-example-01-b"); - svgTest("filters-felem-01-b"); - svgTest("filters-gauss-01-b"); - svgTest("filters-image-01-b"); - svgTest("filters-light-01-f"); - svgTest("filters-morph-01-f"); - svgTest("filters-offset-01-b"); - svgTest("filters-specular-01-f"); - svgTest("filters-tile-01-b"); - svgTest("filters-turb-01-f"); - svgTest("fonts-desc-02-t"); - svgTest("fonts-elem-01-t"); - svgTest("fonts-elem-02-t"); - svgTest("fonts-elem-03-b"); - svgTest("fonts-elem-04-b"); - svgTest("fonts-elem-05-t"); - svgTest("fonts-elem-06-t"); - svgTest("fonts-elem-07-b"); - svgTest("fonts-glyph-02-t"); - svgTest("fonts-glyph-03-t"); - svgTest("fonts-glyph-04-t"); - svgTest("fonts-kern-01-t"); - svgTest("interact-cursor-01-f"); - svgTest("interact-dom-01-b"); - svgTest("interact-events-01-b"); - svgTest("interact-order-01-b"); - svgTest("interact-order-02-b"); - svgTest("interact-order-03-b"); - svgTest("interact-zoom-01-t"); - svgTest("linking-a-01-b"); - svgTest("linking-a-02-b"); - svgTest("linking-a-03-b"); - svgTest("linking-a-04-t"); - svgTest("linking-a-05-t"); - svgTest("linking-a-07-t"); - svgTest("linking-uri-01-b"); - svgTest("linking-uri-02-b"); - svgTest("linking-uri-03-t"); - svgTest("masking-intro-01-f"); - svgTest("masking-mask-01-b"); - svgTest("masking-opacity-01-b"); - svgTest("masking-path-01-b"); - svgTest("masking-path-02-b"); - svgTest("masking-path-03-b"); - svgTest("masking-path-04-b"); - svgTest("masking-path-05-f"); - svgTest("metadata-example-01-b"); +// svgTest("filters-blend-01-b"); +// svgTest("filters-color-01-b"); +// svgTest("filters-composite-02-b"); +// svgTest("filters-comptran-01-b"); +// svgTest("filters-conv-01-f"); +// svgTest("filters-diffuse-01-f"); +// svgTest("filters-displace-01-f"); +// svgTest("filters-example-01-b"); +// svgTest("filters-felem-01-b"); +// svgTest("filters-gauss-01-b"); +// svgTest("filters-image-01-b"); +// svgTest("filters-light-01-f"); +// svgTest("filters-morph-01-f"); +// svgTest("filters-offset-01-b"); +// svgTest("filters-specular-01-f"); +// svgTest("filters-tile-01-b"); +// svgTest("filters-turb-01-f"); +// svgTest("fonts-desc-02-t"); +// svgTest("fonts-elem-01-t"); +// svgTest("fonts-elem-02-t"); +// svgTest("fonts-elem-03-b"); +// svgTest("fonts-elem-04-b"); +// svgTest("fonts-elem-05-t"); +// svgTest("fonts-elem-06-t"); +// svgTest("fonts-elem-07-b"); +// svgTest("fonts-glyph-02-t"); +// svgTest("fonts-glyph-03-t"); +// svgTest("fonts-glyph-04-t"); +// svgTest("fonts-kern-01-t"); +// svgTest("interact-cursor-01-f"); +// svgTest("interact-dom-01-b"); +// svgTest("interact-events-01-b"); +// svgTest("interact-order-01-b"); +// svgTest("interact-order-02-b"); +// svgTest("interact-order-03-b"); +// svgTest("interact-zoom-01-t"); +// svgTest("linking-a-01-b"); +// svgTest("linking-a-02-b"); +// svgTest("linking-a-03-b"); +// svgTest("linking-a-04-t"); +// svgTest("linking-a-05-t"); +// svgTest("linking-a-07-t"); +// svgTest("linking-uri-01-b"); +// svgTest("linking-uri-02-b"); +// svgTest("linking-uri-03-t"); +// svgTest("masking-intro-01-f"); +// svgTest("masking-mask-01-b"); +// svgTest("masking-opacity-01-b"); +// svgTest("masking-path-01-b"); +// svgTest("masking-path-02-b"); +// svgTest("masking-path-03-b"); +// svgTest("masking-path-04-b"); +// svgTest("masking-path-05-f"); +// svgTest("metadata-example-01-b"); svgTest("painting-fill-01-t"); svgTest("painting-fill-02-t"); svgTest("painting-fill-03-t"); @@ -1202,10 +1201,10 @@ public class SvgImporter { svgTest("render-elems-08-t"); svgTest("render-groups-01-b"); svgTest("render-groups-03-t"); - svgTest("script-handle-01-b"); - svgTest("script-handle-02-b"); - svgTest("script-handle-03-b"); - svgTest("script-handle-04-b"); +// svgTest("script-handle-01-b"); +// svgTest("script-handle-02-b"); +// svgTest("script-handle-03-b"); +// svgTest("script-handle-04-b"); svgTest("shapes-circle-01-t"); svgTest("shapes-circle-02-t"); svgTest("shapes-ellipse-01-t"); @@ -1216,80 +1215,80 @@ public class SvgImporter { svgTest("shapes-polyline-01-t"); svgTest("shapes-rect-01-t"); svgTest("shapes-rect-02-t"); - svgTest("struct-cond-01-t"); - svgTest("struct-cond-02-t"); - svgTest("struct-cond-03-t"); - svgTest("struct-defs-01-t"); - svgTest("struct-dom-01-b"); - svgTest("struct-dom-02-b"); - svgTest("struct-dom-03-b"); - svgTest("struct-dom-04-b"); - svgTest("struct-dom-05-b"); - svgTest("struct-dom-06-b"); - svgTest("struct-frag-01-t"); - svgTest("struct-frag-02-t"); - svgTest("struct-frag-03-t"); - svgTest("struct-frag-04-t"); - svgTest("struct-frag-05-t"); - svgTest("struct-frag-06-t"); - svgTest("struct-group-01-t"); - svgTest("struct-group-02-b"); - svgTest("struct-group-03-t"); - svgTest("struct-image-01-t"); - svgTest("struct-image-02-b"); - svgTest("struct-image-03-t"); - svgTest("struct-image-04-t"); - svgTest("struct-image-05-b"); - svgTest("struct-image-06-t"); - svgTest("struct-image-07-t"); - svgTest("struct-image-08-t"); - svgTest("struct-image-09-t"); - svgTest("struct-image-10-t"); - svgTest("struct-symbol-01-b"); - svgTest("struct-use-01-t"); - svgTest("struct-use-03-t"); - svgTest("struct-use-05-b"); - svgTest("styling-css-01-b"); - svgTest("styling-css-02-b"); - svgTest("styling-css-03-b"); - svgTest("styling-css-04-f"); - svgTest("styling-css-05-b"); - svgTest("styling-css-06-b"); - svgTest("styling-inherit-01-b"); - svgTest("styling-pres-01-t"); - svgTest("text-align-01-b"); - svgTest("text-align-02-b"); - svgTest("text-align-03-b"); - svgTest("text-align-04-b"); - svgTest("text-align-05-b"); - svgTest("text-align-06-b"); - svgTest("text-align-08-b"); - svgTest("text-altglyph-01-b"); - svgTest("text-deco-01-b"); - svgTest("text-fonts-01-t"); - svgTest("text-fonts-02-t"); - svgTest("text-fonts-03-t"); - svgTest("text-intro-01-t"); - svgTest("text-intro-02-b"); - svgTest("text-intro-03-b"); - svgTest("text-intro-04-t"); - svgTest("text-intro-05-t"); - svgTest("text-path-01-b"); - svgTest("text-spacing-01-b"); - svgTest("text-text-01-b"); - svgTest("text-text-03-b"); - svgTest("text-text-04-t"); - svgTest("text-text-05-t"); - svgTest("text-text-06-t"); - svgTest("text-text-07-t"); - svgTest("text-text-08-b"); - svgTest("text-tref-01-b"); - svgTest("text-tselect-01-b"); - svgTest("text-tselect-02-f"); - svgTest("text-tspan-01-b"); - svgTest("text-ws-01-t"); - svgTest("text-ws-02-t"); - svgTest("types-basicDOM-01-b"); +// svgTest("struct-cond-01-t"); +// svgTest("struct-cond-02-t"); +// svgTest("struct-cond-03-t"); +// svgTest("struct-defs-01-t"); +// svgTest("struct-dom-01-b"); +// svgTest("struct-dom-02-b"); +// svgTest("struct-dom-03-b"); +// svgTest("struct-dom-04-b"); +// svgTest("struct-dom-05-b"); +// svgTest("struct-dom-06-b"); +// svgTest("struct-frag-01-t"); +// svgTest("struct-frag-02-t"); +// svgTest("struct-frag-03-t"); +// svgTest("struct-frag-04-t"); +// svgTest("struct-frag-05-t"); +// svgTest("struct-frag-06-t"); +// svgTest("struct-group-01-t"); +// svgTest("struct-group-02-b"); +// svgTest("struct-group-03-t"); +// svgTest("struct-image-01-t"); +// svgTest("struct-image-02-b"); +// svgTest("struct-image-03-t"); +// svgTest("struct-image-04-t"); +// svgTest("struct-image-05-b"); +// svgTest("struct-image-06-t"); +// svgTest("struct-image-07-t"); +// svgTest("struct-image-08-t"); +// svgTest("struct-image-09-t"); +// svgTest("struct-image-10-t"); +// svgTest("struct-symbol-01-b"); +// svgTest("struct-use-01-t"); +// svgTest("struct-use-03-t"); +// svgTest("struct-use-05-b"); +// svgTest("styling-css-01-b"); +// svgTest("styling-css-02-b"); +// svgTest("styling-css-03-b"); +// svgTest("styling-css-04-f"); +// svgTest("styling-css-05-b"); +// svgTest("styling-css-06-b"); +// svgTest("styling-inherit-01-b"); +// svgTest("styling-pres-01-t"); +// svgTest("text-align-01-b"); +// svgTest("text-align-02-b"); +// svgTest("text-align-03-b"); +// svgTest("text-align-04-b"); +// svgTest("text-align-05-b"); +// svgTest("text-align-06-b"); +// svgTest("text-align-08-b"); +// svgTest("text-altglyph-01-b"); +// svgTest("text-deco-01-b"); +// svgTest("text-fonts-01-t"); +// svgTest("text-fonts-02-t"); +// svgTest("text-fonts-03-t"); +// svgTest("text-intro-01-t"); +// svgTest("text-intro-02-b"); +// svgTest("text-intro-03-b"); +// svgTest("text-intro-04-t"); +// svgTest("text-intro-05-t"); +// svgTest("text-path-01-b"); +// svgTest("text-spacing-01-b"); +// svgTest("text-text-01-b"); +// svgTest("text-text-03-b"); +// svgTest("text-text-04-t"); +// svgTest("text-text-05-t"); +// svgTest("text-text-06-t"); +// svgTest("text-text-07-t"); +// svgTest("text-text-08-b"); +// svgTest("text-tref-01-b"); +// svgTest("text-tselect-01-b"); +// svgTest("text-tselect-02-f"); +// svgTest("text-tspan-01-b"); +// svgTest("text-ws-01-t"); +// svgTest("text-ws-02-t"); +// svgTest("types-basicDOM-01-b"); } private void applyFillGradients(SvgFill fill, FILLSTYLE fillStyle, RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) { @@ -1453,7 +1452,7 @@ public class SvgImporter { for (int i = 0; i < gfill.stops.size(); i++) { SvgStop stop = gfill.stops.get(i); Color color = stop.color; - color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) Math.round(color.getAlpha() * style.opacity)); + color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) Math.round(color.getAlpha() * style.getOpacity())); fillStyle.gradient.gradientRecords[i] = new GRADRECORD(); fillStyle.gradient.gradientRecords[i].inShape3 = shapeNum >= 3; fillStyle.gradient.gradientRecords[i].color = getRGB(shapeNum, color); @@ -1476,7 +1475,7 @@ public class SvgImporter { private void applyStyleGradients(RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) { SvgFill fill = style.getFillWithOpacity(); - if (fill != null) { + if (fill != null && fill != SvgTransparentFill.INSTANCE) { applyFillGradients(fill, scr.fillStyles.fillStyles[0], bounds, scr, transform, shapeNum, style); } SvgFill strokeFill = style.getStrokeFillWithOpacity(); @@ -1495,7 +1494,7 @@ public class SvgImporter { scr.stateFillStyle1 = true; scr.stateLineStyle = true; SvgFill fill = style.getFillWithOpacity(); - if (fill != null) { + if (fill != null && fill != SvgTransparentFill.INSTANCE) { scr.fillStyles.fillStyles = new FILLSTYLE[1]; scr.fillStyles.fillStyles[0] = new FILLSTYLE(); if (fill instanceof SvgColor) { @@ -1520,9 +1519,9 @@ public class SvgImporter { scr.lineStyles.lineStyles = new LINESTYLE[1]; LINESTYLE lineStyle = shapeNum <= 3 ? new LINESTYLE() : new LINESTYLE2(); lineStyle.color = getRGB(shapeNum, lineColor); - lineStyle.width = (int) Math.round(style.strokeWidth * SWF.unitDivisor); - SvgLineCap lineCap = style.strokeLineCap; - SvgLineJoin lineJoin = style.strokeLineJoin; + lineStyle.width = (int) Math.round(style.getStrokeWidth() * SWF.unitDivisor); + SvgLineCap lineCap = style.getStrokeLineCap(); + SvgLineJoin lineJoin = style.getStrokeLineJoin(); if (lineStyle instanceof LINESTYLE2) { LINESTYLE2 lineStyle2 = (LINESTYLE2) lineStyle; int swfCap = lineCap == SvgLineCap.BUTT ? LINESTYLE2.NO_CAP @@ -1534,13 +1533,13 @@ public class SvgImporter { lineStyle2.hasFillFlag = true; lineStyle2.fillType = new FILLSTYLE(); //...apply in second step - applyStyleGradients - }//Single color does not need fillType attribute + } // Single color does not need fillType attribute 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 = (float) style.strokeMiterLimit; + lineStyle2.miterLimitFactor = (float) style.getStrokeMiterLimit(); } else { if (lineCap != SvgLineCap.ROUND) { showWarning("lineCapNotSupported", "LineCap style not supported in shape " + shapeNum); 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 1d5af35b9..335cb7d6e 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 @@ -37,61 +37,142 @@ import org.w3c.dom.NodeList; * * @author JPEXS */ -class SvgStyle implements Cloneable { +class SvgStyle { - public Color color; - - public SvgFill fill; - - public double opacity; - - public double fillOpacity; - - public SvgFill strokeFill; - - public Color stopColor; - - public double stopOpacity; - - public double strokeWidth; - - public double strokeOpacity; - - public SvgLineCap strokeLineCap; - - public SvgLineJoin strokeLineJoin; - - public double strokeMiterLimit; - - public SvgStyle parentStyle; + private final Element element; private final SvgImporter importer; + private final Map idMap; + private final double epsilon = 0.001; private final Random random = new Random(); - public SvgStyle(SvgImporter importer) { + public SvgStyle(SvgImporter importer, Map idMap, Element element) { this.importer = importer; - fill = new SvgColor(Color.black); - fillOpacity = 1; - strokeFill = null; - strokeWidth = 1; - strokeOpacity = 1; - opacity = 1; - stopOpacity = 1; - stopColor = null; - strokeLineCap = SvgLineCap.BUTT; - strokeLineJoin = SvgLineJoin.MITER; - strokeMiterLimit = 4; + this.idMap = idMap; + this.element = element; } - public SvgStyle(SvgStyle parentStyle) { - this(parentStyle.importer); - this.parentStyle = parentStyle; + private Map getStyleAttributeValues(Element element) { + // todo: cache + Map styleValues = new HashMap<>(); + if (element.hasAttribute("style")) { + String[] styleDefs = element.getAttribute("style").split(";"); + for (String styleDef : styleDefs) { + if (!styleDef.contains(":")) { + continue; + } + + String[] parts = styleDef.split(":", 2); + String name = parts[0].trim(); + String value = parts[1].trim(); + SvgStyleProperty styleProperty = SvgStyleProperty.getByName(name); + if (styleProperty == null) { + importer.showWarning(name + "StyleNotSupported", "The style '" + name + "' is not supported."); + } else { + styleValues.put(name, value); + } + } + } + + return styleValues; + } + + private E getValue(Element element, String name) { + return getValue(element, name, false); + } + + @SuppressWarnings("unchecked") + private E getValue(Element element, String name, boolean inherit) { + Map styleValues = getStyleAttributeValues(element); + if (styleValues.containsKey(name)) { + String value = styleValues.get(name); + if ("inherit".equals(value)) { + if (element.getParentNode() instanceof Element) { + return getValue((Element) element.getParentNode(), name, true); + } + } else { + Object result = getStyleValue(this, name, value); + if (result != null) { + return (E) result; + } + } + } + + if (element.hasAttribute(name)) { + String value = element.getAttribute(name).trim(); + if ("inherit".equals(value)) { + if (element.getParentNode() instanceof Element) { + return getValue((Element) element.getParentNode(), name, true); + } + } else { + Object result = getStyleValue(this, name, value); + if (result != null) { + return (E) result; + } + } + } + + SvgStyleProperty p = SvgStyleProperty.getByName(name); + if (inherit || p.isInherited() && element.getParentNode() instanceof Element) { + return getValue((Element) element.getParentNode(), name); + } + + return (E) p.getInitialValue(); + } + + public Color getColor() { + return getValue(element, "color"); + } + + public SvgFill getFill() { + return getValue(element, "fill"); + } + + public double getFillOpacity() { + return getValue(element, "fill-opacity"); + } + + public SvgFill getStroke() { + return getValue(element, "stroke"); + } + + public double getStrokeWidth() { + return getValue(element, "stroke-width"); + } + + public double getStrokeOpacity() { + return getValue(element, "stroke-opacity"); + } + + public SvgLineCap getStrokeLineCap() { + return getValue(element, "stroke-linecap"); + } + + public SvgLineJoin getStrokeLineJoin() { + return getValue(element, "stroke-linejoin"); + } + + public double getStrokeMiterLimit() { + return getValue(element, "stroke-miterlimit"); + } + + public double getOpacity() { + return getValue(element, "opacity"); + } + + public Color getStopColor() { + return getValue(element, "stop-color"); + } + + public double getStopOpacity() { + return getValue(element, "stop-opacity"); } public SvgFill getFillWithOpacity() { + SvgFill fill = getFill(); if (fill == null) { return null; } @@ -100,7 +181,7 @@ class SvgStyle implements Cloneable { } Color fillColor = ((SvgColor) fill).color; - int opacity = (int) Math.round(this.opacity * fillOpacity * 255); + int opacity = (int) Math.round(getOpacity() * getFillOpacity() * 255); if (opacity > 255) { opacity = 255; } @@ -117,6 +198,7 @@ class SvgStyle implements Cloneable { } public SvgFill getStrokeFillWithOpacity() { + SvgFill strokeFill = getStroke(); if (strokeFill == null) { return null; } @@ -125,7 +207,7 @@ class SvgStyle implements Cloneable { } Color strokeFillColor = ((SvgColor) strokeFill).color; - int opacity = (int) Math.round(this.opacity * strokeOpacity * 255); + int opacity = (int) Math.round(getOpacity() * getStopOpacity() * 255); if (opacity > 255) { opacity = 255; } @@ -142,6 +224,7 @@ class SvgStyle implements Cloneable { } public SvgFill getStrokeColorWithOpacity() { + SvgFill strokeFill = getStroke(); if (strokeFill == null) { return null; } @@ -151,7 +234,7 @@ class SvgStyle implements Cloneable { Color strokeColor = ((SvgColor) strokeFill).color; - int opacity = (int) Math.round(this.opacity * strokeOpacity * 255); + int opacity = (int) Math.round(getOpacity() * getStrokeOpacity() * 255); if (opacity == 255) { return strokeFill; } @@ -159,18 +242,8 @@ class SvgStyle implements Cloneable { return new SvgColor(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(); - } - } - //FIXME - matrices - private SvgFill parseGradient(Map idMap, Element el, SvgStyle style) { + private SvgFill parseGradient(Map idMap, Element el) { SvgGradientUnits gradientUnits = null; String gradientTransform = null; SvgSpreadMethod spreadMethod = null; @@ -200,7 +273,7 @@ class SvgStyle implements Cloneable { } if ("linearGradient".equals(el.getTagName()) && parent_el.getTagName().equals(el.getTagName())) { - SvgLinearGradient parentFill = (SvgLinearGradient) parseGradient(idMap, parent_el, style); + SvgLinearGradient parentFill = (SvgLinearGradient) parseGradient(idMap, parent_el); gradientUnits = parentFill.gradientUnits; gradientTransform = parentFill.gradientTransform; spreadMethod = parentFill.spreadMethod; @@ -213,7 +286,7 @@ class SvgStyle implements Cloneable { stops = parentFill.stops; } if ("radialGradient".equals(el.getTagName()) && parent_el.getTagName().equals(el.getTagName())) { - SvgRadialGradient parentFill = (SvgRadialGradient) parseGradient(idMap, parent_el, style); + SvgRadialGradient parentFill = (SvgRadialGradient) parseGradient(idMap, parent_el); gradientUnits = parentFill.gradientUnits; gradientTransform = parentFill.gradientTransform; spreadMethod = parentFill.spreadMethod; @@ -352,7 +425,7 @@ class SvgStyle implements Cloneable { Node node = stopNodes.item(i); if (node instanceof Element) { Element stopEl = (Element) node; - SvgStyle newStyle = style.apply(stopEl, idMap); + SvgStyle newStyle = new SvgStyle(importer, idMap, stopEl); String offsetStr = stopEl.getAttribute("offset"); double offset; @@ -361,12 +434,12 @@ class SvgStyle implements Cloneable { } else { offset = Double.parseDouble(offsetStr); } - Color color = newStyle.stopColor; + Color color = newStyle.getStopColor(); if (color == null) { color = Color.BLACK; } - int alpha = (int) Math.round(newStyle.stopOpacity * 255); + int alpha = (int) Math.round(newStyle.getStopOpacity() * 255); color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); if (!stopsCleared) { //It has some stop nodes -> remove all inherited stops stopsCleared = true; @@ -436,7 +509,7 @@ class SvgStyle implements Cloneable { return stops; } - private SvgFill parseFill(Map idMap, String fillStr, SvgStyle style) { + private SvgFill parseFill(Map idMap, String fillStr) { if (fillStr == null) { return null; } @@ -454,11 +527,11 @@ class SvgStyle implements Cloneable { if (e != null) { String tagName = e.getTagName(); if ("linearGradient".equals(tagName)) { - return parseGradient(idMap, e, new SvgStyle(style)); //? new style + return parseGradient(idMap, e); } if ("radialGradient".equals(tagName)) { - return parseGradient(idMap, e, new SvgStyle(style)); //? new style + return parseGradient(idMap, e); } if ("pattern".equals(tagName)) { @@ -513,223 +586,100 @@ class SvgStyle implements Cloneable { return result; } - private boolean applyStyle(Map idMap, SvgStyle style, SvgStyle parentStyle, SvgStyleProperty styleProperty, String value) { - boolean inherit = false; - if ("inherit".equals(value)) { - value = ""; - inherit = true; + private Object getStyleValue(SvgStyle style, String name, String value) { + if (value == null || value.length() == 0) { + return null; } - 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; - } + switch (name) { + case "color": { + Color color = SvgColor.parse(value).toColor(); + if (color != null) { + return color; } - break; - case "fill": { - SvgFill fill = parseFill(idMap, value, style); + } + break; + case "fill": { + if ("currentColor".equals(value)) { + return new SvgColor(style.getColor()); + } else { + SvgFill fill = parseFill(idMap, value); if (fill != null) { - style.fill = fill == SvgTransparentFill.INSTANCE ? null : fill; - ok = true; + return fill; } } - 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 "fill-opacity": { + double opacity = Double.parseDouble(value); + return opacity; + } + case "stroke": { + if ("currentColor".equals(value)) { + return new SvgColor(style.getColor()); + } else { + SvgFill stroke = parseFill(idMap, value); + if (stroke != null) { + return stroke; } } - break; - case "stroke-width": { - double strokeWidth = Double.parseDouble(value); - style.strokeWidth = strokeWidth; - ok = true; + } + break; + case "stroke-width": { + double strokeWidth = Double.parseDouble(value); + return strokeWidth; + } + case "stroke-opacity": { + double opacity = Double.parseDouble(value); + return opacity; + } + case "stroke-linecap": { + switch (value) { + case "butt": + return SvgLineCap.BUTT; + case "round": + return SvgLineCap.ROUND; + case "square": + return SvgLineCap.SQUARE; } - break; - case "stroke-opacity": { - double opacity = Double.parseDouble(value); - style.strokeOpacity = opacity; - ok = true; + } + break; + case "stroke-linejoin": { + switch (value) { + case "miter": + return SvgLineJoin.MITER; + case "round": + return SvgLineJoin.ROUND; + case "bevel": + return SvgLineJoin.BEVEL; } - 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-miterlimit": { + double strokeMiterLimit = Double.parseDouble(value); + return strokeMiterLimit; + } + case "opacity": { + double opacity = Double.parseDouble(value); + return opacity; + } + case "stop-color": { + if ("currentColor".equals(value)) { + return style.getColor(); + } else { + return SvgColor.parse(value).toColor(); } - 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; + } + case "stop-opacity": { + double stopOpacity = Double.parseDouble(value); + return stopOpacity; } } } catch (NumberFormatException ex) { - ok = false; + //ignore } - if (inherit) { - applyInheritedStyle(style, parentStyle, styleProperty); - ok = true; - } - - return ok; - } - - private void applyInheritedStyle(SvgStyle style, SvgStyle parentStyle, SvgStyleProperty styleProperty) { - String name = styleProperty.name(); - 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; - } - } - - public SvgStyle apply(Element element, Map idMap) { - SvgStyle result = new SvgStyle(importer); - - Map styleValues = new HashMap<>(); - if (element.hasAttribute("style")) { - String[] styleDefs = element.getAttribute("style").split(";"); - for (String styleDef : styleDefs) { - if (!styleDef.contains(":")) { - continue; - } - - String[] parts = styleDef.split(":", 2); - String name = parts[0].trim(); - String value = parts[1].trim(); - SvgStyleProperty styleProperty = SvgStyleProperty.getByName(name); - if (styleProperty == null) { - importer.showWarning(name + "StyleNotSupported", "The style '" + name + "' is not supported."); - } else { - styleValues.put(name, value); - } - } - } - - for (SvgStyleProperty styleProperty : SvgStyleProperty.getProperties()) { - String name = styleProperty.name(); - boolean ok = false; - if (styleValues.containsKey(name)) { - ok = applyStyle(idMap, result, this, styleProperty, styleValues.get(name)); - } - - if (!ok && element.hasAttribute(name)) { - String attr = element.getAttribute(name).trim(); - ok = applyStyle(idMap, result, this, styleProperty, attr); - } - - if (!ok && styleProperty.isInherited()) { - applyInheritedStyle(result, this, styleProperty); - } - } - - return result; + return null; } } 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 index 13fc60344..1bfd67ac2 100644 --- 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 @@ -16,7 +16,7 @@ */ package com.jpexs.decompiler.flash.importers.svg; -import com.sun.prism.paint.Color; +import java.awt.Color; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -47,12 +47,16 @@ public class SvgStyleProperty { return inherited; } + public Object getInitialValue() { + return initial; + } + 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("color", new SvgStyleProperty("color", true, Color.BLACK /* depends on user agent */)); + p.put("fill", new SvgStyleProperty("fill", true, new SvgColor(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)); From 78147540f4c7957dafb656b324352fd2c562205e Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Mon, 28 Dec 2015 08:31:46 +0100 Subject: [PATCH 10/14] SvgPathReader moved to svg folder --- .../com/jpexs/decompiler/flash/importers/svg/SvgImporter.java | 1 - .../decompiler/flash/importers/{ => svg}/SvgPathReader.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/{ => svg}/SvgPathReader.java (98%) 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 2f6591da5..e7ca7f041 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 @@ -25,7 +25,6 @@ import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; import com.jpexs.decompiler.flash.importers.ShapeImporter; -import com.jpexs.decompiler.flash.importers.SvgPathReader; import com.jpexs.decompiler.flash.tags.DefineShape4Tag; import com.jpexs.decompiler.flash.tags.ExportAssetsTag; import com.jpexs.decompiler.flash.tags.Tag; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SvgPathReader.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgPathReader.java similarity index 98% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SvgPathReader.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgPathReader.java index 85611823b..a7ec68060 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SvgPathReader.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgPathReader.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. */ -package com.jpexs.decompiler.flash.importers; +package com.jpexs.decompiler.flash.importers.svg; /** * From 68401e2c4e8791668a11f3cb638f20d7e90b5858 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Mon, 28 Dec 2015 09:03:01 +0100 Subject: [PATCH 11/14] percent calculation simplified --- .../flash/importers/svg/SvgImporter.java | 84 ++++++------------- .../flash/importers/svg/SvgStyle.java | 7 +- 2 files changed, 28 insertions(+), 63 deletions(-) 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 e7ca7f041..782e319f8 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 @@ -1303,33 +1303,11 @@ public class SvgImporter { SvgLinearGradient lgfill = (SvgLinearGradient) fill; fillStyle.fillStyleType = FILLSTYLE.LINEAR_GRADIENT; fillStyle.gradient = new GRADIENT(); - double x1; - if (lgfill.x1.endsWith("%")) { - x1 = Double.parseDouble(lgfill.x1.substring(0, lgfill.x1.length() - 1)) / 100; - } else { - x1 = Double.parseDouble(lgfill.x1); - } - //x1 = x1 - (-819.2); + double x1 = parseCoordinate(lgfill.x1, 1/* todo: how much is 100%? */); + double y1 = parseCoordinate(lgfill.y1, 1/* todo: how much is 100%? */); + double x2 = parseCoordinate(lgfill.x2, 1/* todo: how much is 100%? */); + double y2 = parseCoordinate(lgfill.y2, 1/* todo: how much is 100%? */); - double y1; - if (lgfill.y1.endsWith("%")) { - y1 = Double.parseDouble(lgfill.y1.substring(0, lgfill.y1.length() - 1)) / 100; - } else { - y1 = Double.parseDouble(lgfill.y1); - } - double x2; - if (lgfill.x2.endsWith("%")) { - x2 = Double.parseDouble(lgfill.x2.substring(0, lgfill.x2.length() - 1)) / 100; - } else { - x2 = Double.parseDouble(lgfill.x2); - } - //x2 = x2 - 819.2; - double y2; - if (lgfill.y2.endsWith("%")) { - y2 = Double.parseDouble(lgfill.y2.substring(0, lgfill.y2.length() - 1)) / 100; - } else { - y2 = Double.parseDouble(lgfill.y2); - } x1 = x1 * SWF.unitDivisor; y1 = y1 * SWF.unitDivisor; x2 = x2 * SWF.unitDivisor; @@ -1371,25 +1349,9 @@ public class SvgImporter { fillStyle.gradientMatrix = tMatrix.toMATRIX(); } else if (fill instanceof SvgRadialGradient) { SvgRadialGradient rgfill = (SvgRadialGradient) fill; - double cx; - if (rgfill.cx.endsWith("%")) { - cx = Double.parseDouble(rgfill.cx.substring(0, rgfill.cx.length() - 1)) / 100; - } else { - cx = Double.parseDouble(rgfill.cx); - } - double cy; - if (rgfill.cy.endsWith("%")) { - cy = Double.parseDouble(rgfill.cy.substring(0, rgfill.cy.length() - 1)) / 100; - } else { - cy = Double.parseDouble(rgfill.cy); - } - - double r; - if (rgfill.r.endsWith("%")) { - r = Double.parseDouble(rgfill.r.substring(0, rgfill.r.length() - 1)) / 100; - } else { - r = Double.parseDouble(rgfill.r); - } + double cx = parseCoordinate(rgfill.cx, 1/* todo: how much is 100%? */); + double cy = parseCoordinate(rgfill.cy, 1/* todo: how much is 100%? */); + double r = parseLength(rgfill.r, 1/* todo: how much is 100%? */); Matrix boundingBoxMatrix = new Matrix(); if (rgfill.gradientUnits == SvgGradientUnits.OBJECT_BOUNDING_BOX) { @@ -1403,18 +1365,8 @@ public class SvgImporter { fillStyle.gradientMatrix = Matrix.getTranslateInstance(SWF.unitDivisor * cx, SWF.unitDivisor * cy).concatenate(new Matrix(fillStyle.gradientMatrix)).concatenate(Matrix.getScaleInstance(r / 819.2)).preConcatenate(boundingBoxMatrix).toMATRIX(); - double fx; - if (rgfill.fx.endsWith("%")) { - fx = Double.parseDouble(rgfill.fx.substring(0, rgfill.fx.length() - 1)) / 100; - } else { - fx = Double.parseDouble(rgfill.fx); - } - double fy; - if (rgfill.fy.endsWith("%")) { - fy = Double.parseDouble(rgfill.fy.substring(0, rgfill.fy.length() - 1)) / 100; - } else { - fy = Double.parseDouble(rgfill.fy); - } + double fx = parseCoordinate(rgfill.fx, 1/* todo: how much is 100%? */); + double fy = parseCoordinate(rgfill.fy, 1/* todo: how much is 100%? */); if (!rgfill.fx.equals(rgfill.cx) || !rgfill.fy.equals(rgfill.cy)) { fillStyle.fillStyleType = FILLSTYLE.FOCAL_RADIAL_GRADIENT; fillStyle.gradient = new FOCALGRADIENT(); @@ -1623,6 +1575,24 @@ public class SvgImporter { return result; } + public double parseNumberOrPercent(String value) { + if (value == null) { + throw new NumberFormatException(); + } + + boolean percent = value.endsWith("%"); + if (percent) { + value = value.substring(0, value.length() - 1); + } + + double result = Double.parseDouble(value); + if (percent) { + result /= 100; + } + + return result; + } + private double getDpi() { return 96; 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 335cb7d6e..9d2709f93 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 @@ -428,12 +428,7 @@ class SvgStyle { SvgStyle newStyle = new SvgStyle(importer, idMap, stopEl); String offsetStr = stopEl.getAttribute("offset"); - double offset; - if (offsetStr.endsWith("%")) { - offset = Double.parseDouble(offsetStr.substring(0, offsetStr.length() - 1)) / 100; - } else { - offset = Double.parseDouble(offsetStr); - } + double offset = importer.parseNumberOrPercent(offsetStr); Color color = newStyle.getStopColor(); if (color == null) { color = Color.BLACK; From 792b627cd937f1d68b01bfefa727ff2428142a49 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Mon, 28 Dec 2015 10:12:20 +0100 Subject: [PATCH 12/14] add custom command line parameters --- .../flash/helpers/SWFDecompilerPlugin.java | 2 ++ .../console/CommandLineArgumentParser.java | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/SWFDecompilerPlugin.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/SWFDecompilerPlugin.java index 825b1be5b..48accbfaa 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/SWFDecompilerPlugin.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/SWFDecompilerPlugin.java @@ -52,6 +52,8 @@ public class SWFDecompilerPlugin { private static final List listeners = new ArrayList<>(); + public static String[] customParameters = new String[0]; + public static File getPluginsDir() { File pluginPath = null; diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 9d25db769..79121ee73 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -81,6 +81,7 @@ import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.helpers.CheckResources; import com.jpexs.decompiler.flash.helpers.FileTextWriter; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.flash.importers.AS2ScriptImporter; import com.jpexs.decompiler.flash.importers.AS3ScriptImporter; import com.jpexs.decompiler.flash.importers.BinaryDataImporter; @@ -468,6 +469,11 @@ public class CommandLineArgumentParser { out.println(" ...WARNING: Injected/SWD script filenames may be different than from standard compiler"); } + if (filter == null || filter.equals("custom")) { + out.println(" " + (cnt++) + ") -custom []..."); + out.println(" ...Forwards all parameters after the -custom parameter to the plugins"); + } + printCmdLineUsageExamples(out, filter); } @@ -701,6 +707,8 @@ public class CommandLineArgumentParser { parseRemoveCharacter(args, true); } else if (command.equals("importscript")) { parseImportScript(args); + } else if (command.equals("importscript")) { + parseCustom(args); } else if (command.equals("as3compiler")) { ActionScript3Parser.compile(null /*?*/, args.pop(), args.pop(), 0, 0); } else if (nextParam.equals("--debugtool")) { @@ -2490,6 +2498,15 @@ public class CommandLineArgumentParser { } } + private static void parseCustom(Stack args) { + String[] customParameters = new String[args.size()]; + for (int i = 0; i < customParameters.length; i++) { + customParameters[i] = args.pop(); + } + + SWFDecompilerPlugin.customParameters = customParameters; + } + private static void replaceAS2PCode(String text, ASMSource src) throws IOException, InterruptedException { System.out.println("Replace AS1/2 PCode"); if (text.trim().startsWith(Helper.hexData)) { From 36745f98ff09fbabaf65ee7360564b743bd3e45e Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Mon, 28 Dec 2015 10:58:09 +0100 Subject: [PATCH 13/14] svg size calculation --- .../flash/exporters/commonshape/Matrix.java | 21 ++-- .../flash/importers/svg/SvgImporter.java | 97 ++++++++++++++----- 2 files changed, 88 insertions(+), 30 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java index 631a21906..b7f2c120d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java @@ -240,6 +240,18 @@ public final class Matrix implements Cloneable { + rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")"; } + public static String[] parseSvgNumberList(String params) { + while (params.contains(" ")) { + params = params.replaceAll(" ", " "); + } + + params = params.trim(); + params = params.replace(", ", ","); + params = params.replace(" ", ","); + String[] args = params.split(","); + return args; + } + public static Matrix parseSvgMatrix(String transformStr, double translateDivisor, double unitDivisor) { Matrix ret = new Matrix(); while (transformStr != null && transformStr.length() > 0) { @@ -247,14 +259,7 @@ public final class Matrix implements Cloneable { transformStr = transformStr.substring(funcName.length() + 1); String params = transformStr.split("\\)")[0]; transformStr = transformStr.substring(params.length() + 1).trim(); - while (params.contains(" ")) { - params = params.replaceAll(" ", " "); - } - - params = params.trim(); - params = params.replace(", ", ","); - params = params.replace(" ", ","); - String[] args = params.split(","); + String[] args = parseSvgNumberList(params); funcName = funcName.trim(); switch (funcName) { case "matrix": 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 782e319f8..cd96ba4c2 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 @@ -50,6 +50,7 @@ import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; import com.jpexs.helpers.Helper; import com.jpexs.helpers.SerializableImage; import java.awt.Color; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -84,6 +85,8 @@ public class SvgImporter { ShapeTag shapeTag; + private Rectangle2D.Double viewBox; + public Tag importSvg(ShapeTag st, String svgXml) { return importSvg(st, svgXml, true); } @@ -100,6 +103,7 @@ public class SvgImporter { int shapeNum = st.getShapeNum(); shapes.shapeRecords = new ArrayList<>(); + Rectangle2D.Double viewBox = null; try { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); /*docFactory.setValidating(false); @@ -120,6 +124,46 @@ public class SvgImporter { throw new IOException("SVG root element should be 'svg'"); } + double width = 800; + double height = 600; + + if (rootElement.hasAttribute("viewBox")) { + String params = rootElement.getAttribute("viewBox"); + String[] args = Matrix.parseSvgNumberList(params); + viewBox = new Rectangle2D.Double(); + if (args.length > 0) { + viewBox.x = parseNumber(args[0]); + } + if (args.length > 1) { + viewBox.y = parseNumber(args[1]); + } + if (args.length > 2) { + viewBox.width = parseNumber(args[2]); + } + if (args.length > 3) { + viewBox.height = parseNumber(args[3]); + } + + width = viewBox.width; + height = viewBox.height; + } + + if (rootElement.hasAttribute("width")) { + width = parseLength(rootElement.getAttribute("width"), width); + } + + if (rootElement.hasAttribute("height")) { + height = parseLength(rootElement.getAttribute("height"), height); + } + + if (viewBox == null) { + viewBox = new Rectangle2D.Double(); + viewBox.width = width; + viewBox.height = height; + } + + this.viewBox = viewBox; + SvgStyle style = new SvgStyle(this, idMap, rootElement); Matrix transform = new Matrix(); processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style); @@ -137,11 +181,11 @@ public class SvgImporter { rect.Ymin -= origYmin; rect.Ymax -= origYmin; - if (!fill) { - // todo: how to calulate the real SVG size? - RECT bounds = shapes.getBounds(); - rect.Xmax = rect.Xmin + bounds.Xmax - Math.min(0, bounds.Xmin); - rect.Ymax = rect.Ymin + bounds.Ymax - Math.min(0, bounds.Ymin); + if (!fill && viewBox != null) { + rect.Xmin = (int) Math.round(viewBox.x * SWF.unitDivisor); + rect.Ymin = (int) Math.round(viewBox.y * SWF.unitDivisor); + rect.Xmax = (int) Math.round((viewBox.x + viewBox.width) * SWF.unitDivisor); + rect.Ymax = (int) Math.round((viewBox.y + viewBox.height) * SWF.unitDivisor); } st.shapes = shapes; @@ -694,29 +738,29 @@ public class SvgImporter { private void processCircle(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { String attr = childElement.getAttribute("cx"); - double cx = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double cx = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; attr = childElement.getAttribute("cy"); - double cy = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double cy = attr.length() > 0 ? parseCoordinate(attr, viewBox.height) : 0; attr = childElement.getAttribute("r"); - double r = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; + double r = attr.length() > 0 ? parseLength(attr, viewBox.width/* todo: how much is 100%? */) : 0; processEllipse(shapeNum, shapes, transform, style, cx, cy, r, r); } private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { String attr = childElement.getAttribute("cx"); - double cx = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double cx = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; attr = childElement.getAttribute("cy"); - double cy = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double cy = attr.length() > 0 ? parseCoordinate(attr, viewBox.height) : 0; attr = childElement.getAttribute("rx"); - double rx = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; + double rx = attr.length() > 0 ? parseLength(attr, viewBox.width) : 0; attr = childElement.getAttribute("ry"); - double ry = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; + double ry = attr.length() > 0 ? parseLength(attr, viewBox.height) : 0; processEllipse(shapeNum, shapes, transform, style, cx, cy, rx, ry); } @@ -781,22 +825,22 @@ public class SvgImporter { private void processRect(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { String attr = childElement.getAttribute("x"); - double x = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double x = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; attr = childElement.getAttribute("y"); - double y = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double y = attr.length() > 0 ? parseCoordinate(attr, viewBox.height) : 0; attr = childElement.getAttribute("width"); - double width = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; + double width = attr.length() > 0 ? parseLength(attr, viewBox.width) : 0; attr = childElement.getAttribute("height"); - double height = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; + double height = attr.length() > 0 ? parseLength(attr, viewBox.height) : 0; attr = childElement.getAttribute("rx"); - double rx = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; + double rx = attr.length() > 0 ? parseLength(attr, viewBox.width) : 0; attr = childElement.getAttribute("ry"); - double ry = attr.length() > 0 ? parseLength(attr, 100/* todo: how much is 100%? */) : 0; + double ry = attr.length() > 0 ? parseLength(attr, viewBox.height) : 0; if (rx == 0 && ry != 0) { rx = ry; @@ -892,16 +936,16 @@ public class SvgImporter { private void processLine(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { String attr = childElement.getAttribute("x1"); - double x1 = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double x1 = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; attr = childElement.getAttribute("y1"); - double y1 = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double y1 = attr.length() > 0 ? parseCoordinate(attr, viewBox.height) : 0; attr = childElement.getAttribute("x2"); - double x2 = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double x2 = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; attr = childElement.getAttribute("y2"); - double y2 = attr.length() > 0 ? parseCoordinate(attr, 100/* todo: how much is 100%? */) : 0; + double y2 = attr.length() > 0 ? parseCoordinate(attr, viewBox.height) : 0; List pathCommands = new ArrayList<>(); PathCommand scr = new PathCommand(); @@ -1575,6 +1619,15 @@ public class SvgImporter { return result; } + public double parseNumber(String value) { + if (value == null) { + throw new NumberFormatException(); + } + + double result = Double.parseDouble(value); + return result; + } + public double parseNumberOrPercent(String value) { if (value == null) { throw new NumberFormatException(); From 316526512f5da6da7735696397cabcb220108dc8 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Mon, 28 Dec 2015 11:34:12 +0100 Subject: [PATCH 14/14] transparent stroke fix --- .../com/jpexs/decompiler/flash/importers/svg/SvgImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cd96ba4c2..df31d8da2 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 @@ -1508,7 +1508,7 @@ public class SvgImporter { scr.lineStyles = new LINESTYLEARRAY(); SvgFill strokeFill = style.getStrokeFillWithOpacity(); - if (strokeFill != null) { + if (strokeFill != null && strokeFill != SvgTransparentFill.INSTANCE) { Color lineColor = strokeFill.toColor(); scr.lineStyles.lineStyles = new LINESTYLE[1];