diff --git a/trunk/src/com/jpexs/decompiler/flash/SWF.java b/trunk/src/com/jpexs/decompiler/flash/SWF.java index 78cd276ac..fdefaf759 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWF.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWF.java @@ -99,8 +99,11 @@ import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.Container; import com.jpexs.decompiler.flash.tags.base.ContainerItem; import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.timeline.Clip; import com.jpexs.decompiler.flash.timeline.DepthState; import com.jpexs.decompiler.flash.timeline.Frame; @@ -2279,7 +2282,7 @@ public final class SWF implements TreeItem, Timelined { if (exporter.exportedTags.containsKey(drawableTag)) { assetName = exporter.exportedTags.get(drawableTag); } else { - assetName = "asset" + exporter.exportedTags.size(); + assetName = getTagIdPrefix(drawableTag, exporter); exporter.exportedTags.put(drawableTag, assetName); exporter.createDefGroup(new ExportRectangle(drawable.getRect()), assetName); drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1); @@ -2298,6 +2301,22 @@ public final class SWF implements TreeItem, Timelined { } } + private static String getTagIdPrefix(Tag tag, SVGExporter exporter) { + if (tag instanceof ShapeTag) { + return exporter.getUniqueId("shape"); + } + if (tag instanceof MorphShapeTag) { + return exporter.getUniqueId("morphshape"); + } + if (tag instanceof DefineSpriteTag) { + return exporter.getUniqueId("sprite"); + } + if (tag instanceof TextTag) { + return exporter.getUniqueId("text"); + } + return exporter.getUniqueId("tag"); + } + public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor) { String key = "frame_" + frame + "_" + timeline.id + "_" + timeline.swf.hashCode(); SerializableImage image = getFromCache(key); diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java index 3e137b067..5632287a5 100644 --- a/trunk/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java @@ -60,6 +60,8 @@ public class SVGExporter { public List gradients; protected int lastPatternId; public Map exportedTags = new HashMap<>(); + public Map> exportedChars = new HashMap<>(); + private Map lastIds = new HashMap<>(); public SVGExporter(ExportRectangle bounds) { @@ -87,8 +89,10 @@ public class SVGExporter { public final void createDefGroup(ExportRectangle bounds, String id) { Element g = _svg.createElement("g"); - g.setAttribute("transform", "matrix(1, 0, 0, 1, " - + roundPixels20(-bounds.xMin / (double) SWF.unitDivisor) + ", " + roundPixels20(-bounds.yMin / (double) SWF.unitDivisor) + ")"); + if (bounds != null) { + g.setAttribute("transform", "matrix(1, 0, 0, 1, " + + roundPixels20(-bounds.xMin / (double) SWF.unitDivisor) + ", " + roundPixels20(-bounds.yMin / (double) SWF.unitDivisor) + ")"); + } if (id != null) { g.setAttribute("id", id); } @@ -101,7 +105,10 @@ public class SVGExporter { } public void endGroup() { - _svgGs.pop(); + Element g = _svgGs.pop(); + if (g.getChildNodes().getLength() == 0) { + g.getParentNode().removeChild(g); + } } public final void createSubGroup(Matrix transform, String id) { @@ -172,6 +179,17 @@ public class SVGExporter { _svgGs.peek().appendChild(image); } + public String getUniqueId(String prefix) { + Integer lastId = lastIds.get(prefix); + if (lastId == null) { + lastId = 0; + } else { + lastId++; + } + lastIds.put(prefix, lastId); + return prefix + lastId; + } + protected static double roundPixels20(double pixels) { return Math.round(pixels * 100) / 100.0; } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index 0746eefd6..1b2b3073c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.tags.base; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; @@ -42,6 +43,7 @@ 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.Font; @@ -396,10 +398,30 @@ public abstract class TextTag extends CharacterTag implements BoundedTag, Drawab if (entry.glyphIndex != -1) { // shapeNum: 1 SHAPE shape = glyphs.get(entry.glyphIndex); - exporter.createSubGroup(mat, null); - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, textColor, colorTransform); - shapeExporter.export(); - exporter.endGroup(); + char ch = font.glyphToChar(entry.glyphIndex); + + String charId = null; + Map chs; + if (exporter.exportedChars.containsKey(font)) { + chs = exporter.exportedChars.get(font); + if (chs.containsKey(ch)) { + charId = chs.get(ch); + } + } else { + chs = new HashMap(); + exporter.exportedChars.put(font, chs); + } + + if (charId == null) { + charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontName() + "_" + ch)); + exporter.createDefGroup(null, charId); + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, textColor, colorTransform); + shapeExporter.export(); + exporter.endGroup(); + chs.put(ch, charId); + } + + exporter.addImage(mat, bounds, charId); x += entry.glyphAdvance; } } diff --git a/trunk/src/com/jpexs/helpers/Helper.java b/trunk/src/com/jpexs/helpers/Helper.java index a2a44513c..39837e9c2 100644 --- a/trunk/src/com/jpexs/helpers/Helper.java +++ b/trunk/src/com/jpexs/helpers/Helper.java @@ -176,6 +176,26 @@ public class Helper { } return ret; } + + public static String getValidHtmlId(String text) { + // ID and NAME tokens must begin with a letter ([A-Za-z]) and + // may be followed by any number of letters, digits ([0-9]), + // hyphens ("-"), underscores ("_"), colons (":"), and periods ("."). + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); + if ((ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') + || (i > 0 && ((ch >= '0' && ch <= '9') + || ch == '-' || ch == '_' || ch == ':' || ch == '.'))) { + sb.append(ch); + } else { + sb.append('_'); + } + } + return sb.toString(); + } + private final static String SPACES12 = " "; private final static String ZEROS8 = "00000000";