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 7c0168ed5..cc8acabeb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -318,6 +318,19 @@ public final class SWF implements SWFContainerItem, Timelined { return getCharacters().get(characterId); } + public FontTag getFont(int fontId) { + CharacterTag characterTag = getCharacters().get(fontId); + if (characterTag instanceof FontTag) { + return (FontTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be a FontTag. characterId: {0}", fontId); + } + + return null; + } + public List getAbcList() { if (abcList == null) { synchronized (this) { @@ -1550,7 +1563,6 @@ public final class SWF implements SWFContainerItem, Timelined { fos.write(Utf8Helper.getBytes(((FontTag) ch).toHtmlCanvas(1))); fos.write(Utf8Helper.getBytes("}\r\n\r\n")); } else { - if (ch instanceof ImageTag) { ImageTag image = (ImageTag) ch; String format = image.getImageFormat(); @@ -2248,8 +2260,8 @@ public final class SWF implements SWFContainerItem, Timelined { if (!layer.isVisible) { continue; } - CharacterTag character = timeline.swf.getCharacter(layer.characterId); + CharacterTag character = timeline.swf.getCharacter(layer.characterId); if (colorTransform == null) { colorTransform = new ColorTransform(); } @@ -2398,6 +2410,7 @@ public final class SWF implements SWFContainerItem, Timelined { if (!layer.isVisible) { continue; } + CharacterTag character = timeline.swf.getCharacter(layer.characterId); Matrix mat = new Matrix(layer.matrix); mat = mat.preConcatenate(transformation); 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 0d75ef547..f05c6851e 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 @@ -435,11 +435,12 @@ public class FrameExporter { if (!layer.isVisible) { continue; } - CharacterTag character = timeline.swf.getCharacter(layer.characterId); + CharacterTag character = timeline.swf.getCharacter(layer.characterId); if (colorTransform == null) { colorTransform = new ColorTransform(); } + Matrix placeMatrix = new Matrix(layer.matrix); placeMatrix.scaleX /= unitDivisor; placeMatrix.scaleY /= unitDivisor; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java index 45efdad4d..addba79df 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java @@ -23,7 +23,6 @@ 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.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; import com.jpexs.decompiler.flash.tags.base.RenderContext; @@ -503,14 +502,10 @@ public class DefineEditTextTag extends TextTag { try { fontId = Integer.parseInt(paramValue); - CharacterTag characterTag = swf.getCharacter(fontId); - if (characterTag == null) { + FontTag ft = swf.getFont(fontId); + if (ft == null) { throw new TextParseException("Font not found.", lexer.yyline()); } - - if (!(characterTag instanceof FontTag)) { - throw new TextParseException("Character tag is not a Font tag. CharacterID: " + fontId, lexer.yyline()); - } } catch (NumberFormatException ne) { throw new TextParseException("Invalid font value. Number expected. Found: " + paramValue, lexer.yyline()); } @@ -641,6 +636,7 @@ public class DefineEditTextTag extends TextTag { hasFontClass = true; } this.autoSize = autoSize; + this.align = align; if ((leftMargin > -1) || (rightMargin > -1) || (indent > -1) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java index 863ca15ab..5e0b57ea6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java @@ -24,7 +24,6 @@ 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.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; import com.jpexs.decompiler.flash.tags.base.RenderContext; @@ -220,16 +219,12 @@ public class DefineText2Tag extends TextTag { try { fontId = Integer.parseInt(paramValue); - CharacterTag characterTag = swf.getCharacter(fontId); - if (characterTag == null) { + FontTag ft = swf.getFont(fontId); + if (ft == null) { throw new TextParseException("Font not found.", lexer.yyline()); } - if (!(characterTag instanceof FontTag)) { - throw new TextParseException("Character tag is not a Font tag. CharacterID: " + fontId, lexer.yyline()); - } - - font = (FontTag) characterTag; + font = (FontTag) ft; fontName = font.getSystemFontName(); } catch (NumberFormatException nfe) { throw new TextParseException("Invalid font id - number expected. Found: " + paramValue, lexer.yyline()); @@ -447,7 +442,7 @@ public class DefineText2Tag extends TextTag { @Override public boolean alignText(TextAlign textAlign) { - alignText(textRecords, textAlign); + alignText(swf, textRecords, textAlign); setModified(true); return true; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java index f000dbd46..3f58ecd38 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java @@ -25,7 +25,6 @@ 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.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; import com.jpexs.decompiler.flash.tags.base.RenderContext; @@ -226,16 +225,12 @@ public class DefineTextTag extends TextTag { try { fontId = Integer.parseInt(paramValue); - CharacterTag characterTag = swf.getCharacter(fontId); - if (characterTag == null) { + FontTag ft = swf.getFont(fontId); + if (ft == null) { throw new TextParseException("Font not found.", lexer.yyline()); } - if (!(characterTag instanceof FontTag)) { - throw new TextParseException("Character tag is not a Font tag. CharacterID: " + fontId, lexer.yyline()); - } - - font = (FontTag) characterTag; + font = (FontTag) ft; fontName = font.getSystemFontName(); } catch (NumberFormatException nfe) { throw new TextParseException("Invalid font id - number expected. Found: " + paramValue, lexer.yyline()); @@ -455,7 +450,7 @@ public class DefineTextTag extends TextTag { @Override public boolean alignText(TextAlign textAlign) { - alignText(textRecords, textAlign); + alignText(swf, textRecords, textAlign); setModified(true); return true; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index d866e2c7b..60e5e9993 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -27,7 +27,6 @@ import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; import com.jpexs.decompiler.flash.importers.TextImportResizeTextBoundsMode; -import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.text.TextAlign; import com.jpexs.decompiler.flash.tags.text.TextParseException; import com.jpexs.decompiler.flash.types.ColorTransform; @@ -117,9 +116,15 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { } } - public static void alignText(List textRecords, TextAlign textAlign) { + public static void alignText(SWF swf, List textRecords, TextAlign textAlign) { + int xMin = Integer.MAX_VALUE; int maxWidth = 0; for (TEXTRECORD tr : textRecords) { + int xOffset = tr.styleFlagsHasXOffset ? tr.xOffset : 0; + if (xOffset < xMin) { + xMin = xOffset; + } + int width = tr.getTotalAdvance(); if (width > maxWidth) { @@ -127,30 +132,98 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { } } + FontTag font = null; + for (TEXTRECORD tr : textRecords) { + if (tr.styleFlagsHasFont) { + FontTag font2 = swf.getFont(tr.fontId); + if (font2 != null) { + font = font2; + } + } + + /*if (tr.justified) { + // Text record was aligned in Justify mode earier + // restore the advances + for (GLYPHENTRY ge : tr.glyphEntries) { + char ch = font.glyphToChar(ge.glyphIndex); + if (Character.isWhitespace(ch)) { + ge.glyphAdvance = ge.originalAdvance; + } + } + }*/ int width = tr.getTotalAdvance(); switch (textAlign) { case LEFT: - tr.xOffset = 0; + tr.xOffset = xMin; tr.styleFlagsHasXOffset = true; break; case CENTER: - tr.xOffset = (maxWidth - width) / 2; + tr.xOffset = xMin + (maxWidth - width) / 2; tr.styleFlagsHasXOffset = true; break; case RIGHT: - tr.xOffset = maxWidth - width; + tr.xOffset = xMin + maxWidth - width; tr.styleFlagsHasXOffset = true; break; case JUSTIFY: - tr.xOffset = 0; + tr.xOffset = xMin; tr.styleFlagsHasXOffset = true; + + if (font != null) { + int diff = maxWidth - width; + if (diff > 0) { + int spaces = 0; + int spaces2 = 0; + int state = 0; + List glyphEntries = new ArrayList<>(); + List glyphEntries2 = new ArrayList<>(); + for (GLYPHENTRY ge : tr.glyphEntries) { + char ch = font.glyphToChar(ge.glyphIndex); + boolean whitespace = Character.isWhitespace(ch); + switch (state) { + case 0: + if (!whitespace) { + state = 1; + } + break; + case 1: + if (whitespace) { + spaces2++; + glyphEntries2.add(ge); + } else { + spaces += spaces2; + spaces2 = 0; + glyphEntries.addAll(glyphEntries2); + glyphEntries2.clear(); + } + break; + } + } + + if (spaces > 0) { + int fix = diff / spaces; + int remaining = diff - fix * spaces; + for (GLYPHENTRY ge : glyphEntries) { + int diff2 = fix; + if (remaining-- > 0) { + diff2++; + } + + //ge.originalAdvance = ge.glyphAdvance; + ge.glyphAdvance += diff2; + } + + //tr.justified = true; + } + } + } break; } } } - public static Map getTextRecordsAttributes(List list, List tags) { + public static Map getTextRecordsAttributes(List list, SWF swf) { Map att = new HashMap<>(); RECT textBounds = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); FontTag font = null; @@ -176,13 +249,9 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { for (int r = 0; r < list.size(); r++) { TEXTRECORD rec = list.get(r); if (rec.styleFlagsHasFont) { - for (Tag t : tags) { - if (t instanceof FontTag) { - FontTag ft = (FontTag) t; - if (ft.getFontId() == rec.fontId) { - font = ft; - } - } + FontTag font2 = swf.getFont(rec.fontId); + if (font2 != null) { + font = font2; } textHeight = rec.textHeight; glyphs = font.getGlyphShapeTable(); @@ -332,9 +401,9 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { } } if (rec.styleFlagsHasFont) { - CharacterTag character = swf.getCharacter(rec.fontId); - if (character instanceof FontTag) { - font = (FontTag) character; + FontTag font2 = swf.getFont(rec.fontId); + if (font2 != null) { + font = font2; } glyphs = font == null ? null : font.getGlyphShapeTable(); textHeight = rec.textHeight; @@ -381,7 +450,7 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { ExportRectangle result = null; for (TEXTRECORD rec : textRecords) { if (rec.styleFlagsHasFont) { - font = (FontTag) swf.getCharacter(rec.fontId); + font = swf.getFont(rec.fontId); glyphs = font == null ? null : font.getGlyphShapeTable(); textHeight = rec.textHeight; } @@ -472,7 +541,7 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { } } if (rec.styleFlagsHasFont) { - font = (FontTag) swf.getCharacter(rec.fontId); + font = swf.getFont(rec.fontId); fontId = rec.fontId; glyphs = font.getGlyphShapeTable(); textHeight = rec.textHeight; @@ -518,7 +587,7 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { } } if (rec.styleFlagsHasFont) { - font = (FontTag) swf.getCharacter(rec.fontId); + font = swf.getFont(rec.fontId); glyphs = font.getGlyphShapeTable(); textHeight = rec.textHeight; } 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 351e5263c..43be1c4ff 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 @@ -1282,7 +1282,7 @@ public class XFLConverter { MATRIX matrix = rec.placeMatrix; String recCharStr; if (character instanceof TextTag) { - recCharStr = convertText(null, tags, (TextTag) character, matrix, filters, null); + recCharStr = convertText(null, (TextTag) character, matrix, filters, null); } else if (character instanceof DefineVideoStreamTag) { recCharStr = convertVideoInstance(null, matrix, (DefineVideoStreamTag) character, null); } else { @@ -1904,7 +1904,7 @@ public class XFLConverter { } else { shapeTween = false; if (character instanceof TextTag) { - elements += convertText(instanceName, tags, (TextTag) character, matrix, filters, clipActions); + elements += convertText(instanceName, (TextTag) character, matrix, filters, clipActions); } else if (character instanceof DefineVideoStreamTag) { elements += convertVideoInstance(instanceName, matrix, (DefineVideoStreamTag) character, clipActions); } else { @@ -2332,7 +2332,7 @@ public class XFLConverter { return ret; } - public static String convertText(String instanceName, List tags, TextTag tag, MATRIX m, List filters, CLIPACTIONS clipActions) { + public static String convertText(String instanceName, TextTag tag, MATRIX m, List filters, CLIPACTIONS clipActions) { StringBuilder ret = new StringBuilder(); if (m == null) { @@ -2349,7 +2349,8 @@ public class XFLConverter { filterStr += ""; } - for (Tag t : tags) { + SWF swf = tag.getSwf(); + for (Tag t : swf.tags) { if (t instanceof CSMTextSettingsTag) { CSMTextSettingsTag c = (CSMTextSettingsTag) t; if (c.textID == tag.getCharacterId()) { @@ -2358,6 +2359,7 @@ public class XFLConverter { } } } + String fontRenderingMode = "standard"; String antiAlias = ""; if (csmts != null) { @@ -2386,19 +2388,12 @@ public class XFLConverter { textRecords = ((DefineText2Tag) tag).textRecords; } - looprec: for (TEXTRECORD rec : textRecords) { if (rec.styleFlagsHasFont) { - for (Tag t : tags) { - if (t instanceof FontTag) { - FontTag ft = (FontTag) t; - if (ft.getFontId() == rec.fontId) { - if (ft.isSmall()) { - fontRenderingMode = "bitmap"; - break looprec; - } - } - } + FontTag ft = swf.getFont(rec.fontId); + if (ft != null && ft.isSmall()) { + fontRenderingMode = "bitmap"; + break; } } } @@ -2412,7 +2407,7 @@ public class XFLConverter { ret.append(" instanceName=\"").append(xmlString(instanceName)).append("\""); } ret.append(antiAlias); - Map attrs = TextTag.getTextRecordsAttributes(textRecords, tags); + Map attrs = TextTag.getTextRecordsAttributes(textRecords, swf); ret.append(" width=\"").append(tag.getBounds().getWidth() / 2).append("\" height=\"").append(tag.getBounds().getHeight()).append("\" autoExpand=\"true\" isSelectable=\"false\">"); ret.append(matStr); @@ -2444,13 +2439,8 @@ public class XFLConverter { fontId = rec.fontId; fontName = null; textHeight = rec.textHeight; - font = null; - for (Tag t : tags) { - if (t instanceof FontTag) { - if (((FontTag) t).getFontId() == fontId) { - font = (FontTag) t; - } - } + font = swf.getFont(fontId); + for (Tag t : swf.tags) { if (t instanceof DefineFontNameTag) { if (((DefineFontNameTag) t).fontId == fontId) { fontName = ((DefineFontNameTag) t).fontName; @@ -2506,16 +2496,9 @@ public class XFLConverter { } else if (tag instanceof DefineEditTextTag) { DefineEditTextTag det = (DefineEditTextTag) tag; String tagName; - for (Tag t : tags) { - if (t instanceof FontTag) { - FontTag ft = (FontTag) t; - if (ft.getFontId() == det.fontId) { - if (ft.isSmall()) { - fontRenderingMode = "bitmap"; - break; - } - } - } + FontTag ft = swf.getFont(det.fontId); + if (ft != null && ft.isSmall()) { + fontRenderingMode = "bitmap"; } if (!det.useOutlines) { fontRenderingMode = "device"; @@ -2579,7 +2562,7 @@ public class XFLConverter { } if (det.html) { - ret.append(convertHTMLText(tags, det, txt)); + ret.append(convertHTMLText(swf.tags, det, txt)); } else { ret.append(""); ret.append("").append(xmlString(txt)).append(""); @@ -2598,18 +2581,12 @@ public class XFLConverter { } if (det.hasFont) { String fontName = null; - FontTag ft = null; - for (Tag u : tags) { + for (Tag u : swf.tags) { if (u instanceof DefineFontNameTag) { if (((DefineFontNameTag) u).fontId == det.fontId) { fontName = ((DefineFontNameTag) u).fontName; } } - if (u instanceof FontTag) { - if (((FontTag) u).getFontId() == det.fontId) { - ft = (FontTag) u; - } - } if (fontName != null && ft != null) { break; } diff --git a/src/com/jpexs/decompiler/flash/gui/TextPanel.java b/src/com/jpexs/decompiler/flash/gui/TextPanel.java index fd10ea922..5b8834214 100644 --- a/src/com/jpexs/decompiler/flash/gui/TextPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/TextPanel.java @@ -198,7 +198,7 @@ public class TextPanel extends JPanel implements ActionListener { textAlignLeftButton.setVisible(alignable); textAlignCenterButton.setVisible(alignable); textAlignRightButton.setVisible(alignable); - textAlignJustifyButton.setVisible(false); // todo + textAlignJustifyButton.setVisible(alignable); undoChangesButton.setVisible(item != null && item instanceof TextTag && ((Tag) item).isModified()); }