From 9d021819e854639d4a7430cea184296b595734d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 4 Nov 2023 22:05:47 +0100 Subject: [PATCH] Added #1799 Text tags editor has new parameter [space xxx] specifying exact letter advance value (add to the font advance + calculated letterspacing) Changed #802 - FLA export - calculating letterspacing on text with fonts without layout is now optional and turned off by default XFL converter - do not convert shapes twice Shape fixer - intersections - ignore problems --- CHANGELOG.md | 6 ++ .../flash/configuration/Configuration.java | 6 +- .../decompiler/flash/math/Intersections.java | 8 +- .../flash/tags/base/StaticTextTag.java | 79 +++++++++++++++---- .../decompiler/flash/tags/base/TextTag.java | 19 +++-- .../decompiler/flash/xfl/XFLConverter.java | 12 ++- .../locales/AdvancedSettingsDialog.properties | 3 + .../AdvancedSettingsDialog_cs.properties | 3 + 8 files changed, 111 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15781701b..8e7e1fae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ All notable changes to this project will be documented in this file. - Export Morphshape as start and end shape (SVG, PNG, BMP) - Directory selection dialog in directory configs in advanced settings - Status bar with info about edges on walking shaperecords +- [#1799] Text tags editor has new parameter `[space xxx]` specifying exact letter + advance value (add to the font advance + calculated letterspacing) ### Fixed - [#1306], [#1768] Maximizing window on other than main monitor @@ -50,6 +52,8 @@ All notable changes to this project will be documented in this file. ### Changed - Basic tag info panel always visible even when nothing to display (to avoid flickering) - SVG export - attributes like data-characterId and data-characterName moved under `ffdec:` namespace +- [#802] - FLA export - calculating letterspacing on text with fonts without layout is now optional + and turned off by default ## [19.1.2] - 2023-10-16 ### Fixed @@ -3235,6 +3239,7 @@ Major version of SWF to XML export changed to 2. [#1717]: https://www.free-decompiler.com/flash/issues/1717 [#2005]: https://www.free-decompiler.com/flash/issues/2005 [#2105]: https://www.free-decompiler.com/flash/issues/2105 +[#1799]: https://www.free-decompiler.com/flash/issues/1799 [#1306]: https://www.free-decompiler.com/flash/issues/1306 [#1768]: https://www.free-decompiler.com/flash/issues/1768 [#2013]: https://www.free-decompiler.com/flash/issues/2013 @@ -3249,6 +3254,7 @@ Major version of SWF to XML export changed to 2. [#2048]: https://www.free-decompiler.com/flash/issues/2048 [#2108]: https://www.free-decompiler.com/flash/issues/2108 [#2074]: https://www.free-decompiler.com/flash/issues/2074 +[#802]: https://www.free-decompiler.com/flash/issues/802 [#2099]: https://www.free-decompiler.com/flash/issues/2099 [#2090]: https://www.free-decompiler.com/flash/issues/2090 [#2079]: https://www.free-decompiler.com/flash/issues/2079 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index a550ce110..38f4dc841 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -966,7 +966,11 @@ public final class Configuration { @ConfigurationDefaultBoolean(true) @ConfigurationCategory("ui") - public static ConfigurationItem displayAs3PCodePanel = null; + public static ConfigurationItem displayAs3PCodePanel = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("export") + public static ConfigurationItem flaExportUseMappedFontLayout = null; private enum OSId { WINDOWS, OSX, UNIX diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/Intersections.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/Intersections.java index 636df6a30..f9b88f868 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/Intersections.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/Intersections.java @@ -207,7 +207,13 @@ public class Intersections { ) ); - List roots = poly.getRoots(); + List roots; + try { + roots = poly.getRoots(); + } catch (RuntimeException rex) { + //Y values of bounds must be of opposite sign. ??fixme?? + return result; + } for (double s : roots) { if (0 <= s && s <= 1) { Polynomial xp = new Polynomial(Arrays.asList( diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/StaticTextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/StaticTextTag.java index e3f8a97c7..93952c0ff 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/StaticTextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/StaticTextTag.java @@ -252,6 +252,7 @@ public abstract class StaticTextTag extends TextTag { writer.append("]"); int textHeight = 12; for (TEXTRECORD rec : textRecords) { + int letterSpacing = 0; if (rec.styleFlagsHasFont || rec.styleFlagsHasColor || rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) { writer.append("[").newLine(); if (rec.styleFlagsHasFont) { @@ -264,7 +265,7 @@ public abstract class StaticTextTag extends TextTag { textHeight = rec.textHeight; } if (fnt != null && !ignoreLetterSpacing) { - int letterSpacing = detectLetterSpacing(rec, fnt, textHeight); + letterSpacing = detectLetterSpacing(rec, fnt, textHeight); if (letterSpacing != 0) { writer.append("letterspacing ").append(letterSpacing).newLine(); } @@ -288,7 +289,24 @@ public abstract class StaticTextTag extends TextTag { if (fnt == null) { writer.append(AppResources.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId))); } else { - writer.hilightSpecial(Helper.escapeActionScriptString(rec.getText(fnt)).replace("[", "\\[").replace("]", "\\]"), HighlightSpecialType.TEXT); + for (int i = 0; i < rec.glyphEntries.size(); i++) { + GLYPHENTRY ge = rec.glyphEntries.get(i); + char c = fnt.glyphToChar(ge.glyphIndex); + String sc = ("" + c).replace("[", "\\[").replace("]", "\\]"); + writer.hilightSpecial(sc, HighlightSpecialType.TEXT); + + if (!ignoreLetterSpacing) { + Character nextChar = null; + if (i + 1 < rec.glyphEntries.size()) { + nextChar = fnt.glyphToChar(ge.glyphIndex); + } + int advance = getAdvance(fnt, ge.glyphIndex, textHeight, c, nextChar); + int delta = ge.glyphAdvance - advance; + if (delta != letterSpacing) { + writer.append("[space " + (delta - letterSpacing) + "]"); + } + } + } } } return new HighlightedText(writer); @@ -458,6 +476,20 @@ public abstract class StaticTextTag extends TextTag { throw new TextParseException("Invalid translatey value - number expected. Found: " + paramValue, lexer.yyline()); } break; + case "space": + try { + int space = Integer.parseInt(paramValue); + if (textRecords.isEmpty()) { + throw new TextParseException("space parameter must be placed after some text", lexer.yyline()); + } + TEXTRECORD lastRecord = textRecords.get(textRecords.size() - 1); + if (!lastRecord.glyphEntries.isEmpty()) { + lastRecord.glyphEntries.get(lastRecord.glyphEntries.size() - 1).glyphAdvance += space; + } + } catch (NumberFormatException nfe) { + throw new TextParseException("Invalid space value - number expected. Found: " + paramValue, lexer.yyline()); + } + break; default: throw new TextParseException("Unrecognized parameter name: " + paramName, lexer.yyline()); } @@ -580,35 +612,54 @@ public abstract class StaticTextTag extends TextTag { advance = (int) Math.round(((double) textHeight * (font.getGlyphAdvance(glyphIndex) + kerningAdjustment)) / (font.getDivider() * 1024.0)); } else { String fontName = font.getSystemFontName(); - advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar)); + advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar)); } return advance; } - public static int detectLetterSpacing(TEXTRECORD textRecord, FontTag font, int textHeight) { - int totalLetterSpacing = 0; + public static int detectLetterSpacing(TEXTRECORD textRecord, FontTag font, int textHeight) { + int minLetterSpacing = Integer.MAX_VALUE; + int numNegatives = 0; List glyphEntries = textRecord.glyphEntries; - - if (glyphEntries.isEmpty()) { + + if (glyphEntries.size() < 2) { return 0; } - for (int i = 0; i < glyphEntries.size(); i++) { + int numMin = 0; + for (int i = 0; i < glyphEntries.size() - 1; i++) { GLYPHENTRY glyph = glyphEntries.get(i); - GLYPHENTRY nextGlyph = null; + /*GLYPHENTRY nextGlyph = null; if (i + 1 < glyphEntries.size()) { nextGlyph = glyphEntries.get(i + 1); - } - + }*/ + GLYPHENTRY nextGlyph = glyphEntries.get(i + 1); char c = font.glyphToChar(glyph.glyphIndex); - Character nextChar = nextGlyph == null ? null : font.glyphToChar(nextGlyph.glyphIndex); + //Character nextChar = nextGlyph == null ? null : font.glyphToChar(nextGlyph.glyphIndex); + Character nextChar = font.glyphToChar(nextGlyph.glyphIndex); int advance = getAdvance(font, glyph.glyphIndex, textHeight, c, nextChar); int letterSpacing = glyph.glyphAdvance - advance; - totalLetterSpacing += letterSpacing; + //System.err.println("advance between char "+c+" and "+nextChar+": advance = " + advance + " glyphAdvance="+glyph.glyphAdvance+" delta:"+letterSpacing); + if (letterSpacing < 0) { + numNegatives++; + } + if (letterSpacing == minLetterSpacing) { + numMin++; + } + if (letterSpacing < minLetterSpacing) { + minLetterSpacing = letterSpacing; + numMin = 1; + } + } + if (minLetterSpacing < 0 && numNegatives < glyphEntries.size() / 2) { //a hack, use negative letterspacing only when 50% letters use it + minLetterSpacing = 0; + } + if (numMin == 1) { + return 0; } - return (int) Math.round(totalLetterSpacing / glyphEntries.size()); + return minLetterSpacing; } @Override 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 8e4a24a0e..4785d0468 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 @@ -346,17 +346,22 @@ public abstract class TextTag extends DrawableTag { if (nextEntry != null) { kerningAdjustment = font.getGlyphKerningAdjustment(entry.glyphIndex, nextEntry.glyphIndex); } - defaultAdvance = (int) (Math.round(textHeight * (font.getGlyphAdvance(entry.glyphIndex) + kerningAdjustment) / (1024.0 * font.getDivider()))); + defaultAdvance = (int) (Math.round(textHeight * (font.getGlyphAdvance(entry.glyphIndex) + kerningAdjustment) / (1024.0 * font.getDivider()))); + } else { defaultAdvance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(aFont, font.glyphToChar(entry.glyphIndex), nextEntry == null ? null : font.glyphToChar(nextEntry.glyphIndex))); } - int newLetterSpacing = adv - defaultAdvance; - if (e == 0 || e == rec.glyphEntries.size() - 1) { - if (rec.glyphEntries.size() == 1) { - letterSpacing = 0; + if (!font.hasLayout() && !Configuration.flaExportUseMappedFontLayout.get()) { + letterSpacing = 0; + } else { + int newLetterSpacing = adv - defaultAdvance; + if (e == 0 || e == rec.glyphEntries.size() - 1) { + if (rec.glyphEntries.size() == 1) { + letterSpacing = 0; + } + } else if (newLetterSpacing < letterSpacing) { + letterSpacing = newLetterSpacing; } - } else if (newLetterSpacing < letterSpacing) { - letterSpacing = newLetterSpacing; } x += adv; } 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 e16bf20b3..4f100eb9c 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 @@ -570,8 +570,15 @@ public class XFLConverter { } private static boolean shapeHasMultiLayers(SWF swf, HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles) throws XMLStreamException { - List layers = getShapeLayers(swf, characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, false); - return layers.size() > 1; + for (SHAPERECORD rec: shapeRecords) { + if (rec instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) rec; + if (scr.stateNewStyles) { + return true; + } + } + } + return false; } private static void convertShape(SWF swf, HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape, boolean useLayers, XFLXmlWriter writer) throws XMLStreamException { @@ -1636,6 +1643,7 @@ public class XFLConverter { Reference nextClipId = new Reference<>(-1); writer.writeStartElement("symbols"); for (int ch : characters.keySet()) { + //System.err.println("converting " + ch); CharacterTag symbol = characters.get(ch); if ((symbol instanceof ShapeTag) && nonLibraryShapes.contains(symbol.getCharacterId())) { continue; //shapes with 1 ocurrence and single layer are not added to library diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 251ce3c38..7b0d76e23 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -740,3 +740,6 @@ config.description.displayAs12PCodePanel = Show panel with disassembled P-code a config.name.displayAs3PCodePanel = Show AS3 P-code panel config.description.displayAs3PCodePanel = Show panel with disassembled P-code instructions for ActionScript 3 + +config.name.flaExportUseMappedFontLayout = FLA export - use mapped font layout +config.description.flaExportUseMappedFontLayout = Use assigned source font advance values when determining letterspacing when actual font has no layout during FLA export. diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties index 0c7dbfc37..66237ab18 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties @@ -730,3 +730,6 @@ config.description.displayAs12PCodePanel = Zobrazit panel s akcemi disassemblova config.name.displayAs3PCodePanel = Zobrazit AS3 panel s P-k\u00f3dem config.description.displayAs3PCodePanel = Zobrazit panel s instrukcemi disassemblovan\u00e9ho P-k\u00f3du pro ActionScript 3 + +config.name.flaExportUseMappedFontLayout = FLA export - pou\u017e\u00edvat namapovan\u00e9 rozvr\u017een\u00ed p\u00edsem +config.description.flaExportUseMappedFontLayout = B\u011bhem FLA exportu pou\u017e\u00edvat advance hodnoty z p\u0159i\u0159azen\u00e9ho zdrojov\u00e9ho p\u00edsma p\u0159i ur\u010dov\u00e1n\u00ed letterspacing hodnoty kdy\u017e vlastn\u00ed p\u00edsmo nem\u00e1 rozvr\u017een\u00ed