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 41c1cbe90..8ce597858 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 @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.tags.base; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; @@ -181,16 +182,30 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable } public String getSystemFontName() { - Map fontPairs = Configuration.getFontToNameMap(); - String key = swf.getShortFileName() + "_" + getFontId() + "_" + getFontNameIntag(); - if (fontPairs.containsKey(key)) { - return fontPairs.get(key); + int fontId = getFontId(); + String selectedFont = swf.sourceFontNamesMap.get(fontId); + if (selectedFont == null) { + SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(swf.getShortFileName()); + String key = fontId + "_" + getFontNameIntag(); + if (swfConf != null) { + selectedFont = swfConf.fontPairingMap.get(key); + } } - key = getFontNameIntag(); - if (fontPairs.containsKey(key)) { - return fontPairs.get(key); + + if (selectedFont == null) { + selectedFont = Configuration.getFontToNameMap().get(getFontNameIntag()); } - return defaultFontName; + + if (selectedFont != null && FontTag.installedFontsByName.containsKey(selectedFont)) { + return selectedFont; + } + + // findInstalledFontName always returns an available font name + return FontTag.findInstalledFontName(getFontName()); + } + + public Font getSystemFont() { + return FontTag.installedFontsByName.get(getSystemFontName()); } protected void shiftGlyphIndices(int fontId, int startIndex) { 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 cdbded97d..0449be6f8 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 @@ -261,6 +261,12 @@ public abstract class StaticTextTag extends TextTag { writer.append("font ").append(rec.fontId).newLine(); writer.append("height ").append(rec.textHeight).newLine(); } + if (fnt != null) { + int letterSpacing = detectLetterSpacing(rec, fnt, rec.textHeight); + if (letterSpacing != 0) { + writer.append("letterspacing ").append(letterSpacing).newLine(); + } + } if (rec.styleFlagsHasColor) { if (getTextNum() == 1) { writer.append("color ").append(rec.textColor.toHexRGB()).newLine(); @@ -296,8 +302,8 @@ public abstract class StaticTextTag extends TextTag { RGBA colorA = null; int fontId = -1; int textHeight = -1; + int letterSpacing = 0; FontTag font = null; - String fontName = null; Integer x = null; Integer y = null; int currentX = 0; @@ -342,7 +348,6 @@ public abstract class StaticTextTag extends TextTag { } font = (FontTag) ft; - fontName = font.getSystemFontName(); } catch (NumberFormatException nfe) { throw new TextParseException("Invalid font id - number expected. Found: " + paramValue, lexer.yyline()); } @@ -354,6 +359,13 @@ public abstract class StaticTextTag extends TextTag { throw new TextParseException("Invalid font height - number expected. Found: " + paramValue, lexer.yyline()); } break; + case "letterspacing": + try { + letterSpacing = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new TextParseException("Invalid font letter spacing - number expected. Found: " + paramValue, lexer.yyline()); + } + break; case "x": try { x = Integer.parseInt(paramValue); @@ -527,17 +539,7 @@ public abstract class StaticTextTag extends TextTag { GLYPHENTRY ge = new GLYPHENTRY(); ge.glyphIndex = font.charToGlyph(c); - int advance; - if (font.hasLayout()) { - int kerningAdjustment = 0; - if (nextChar != null) { - kerningAdjustment = font.getCharKerningAdjustment(c, nextChar); - } - advance = (int) Math.round(((double) textHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment)) / (font.getDivider() * 1024.0)); - } else { - advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar)); - } - + int advance = getAdvance(font, ge.glyphIndex, textHeight, c, nextChar) + letterSpacing; ge.glyphAdvance = advance; tr.glyphEntries.add(ge); @@ -568,6 +570,42 @@ public abstract class StaticTextTag extends TextTag { return true; } + private int getAdvance(FontTag font, int glyphIndex, int textHeight, char c, Character nextChar) { + int advance; + if (font.hasLayout()) { + int kerningAdjustment = 0; + if (nextChar != null) { + kerningAdjustment = font.getCharKerningAdjustment(c, nextChar); + } + 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)); + } + + return advance; + } + + private int detectLetterSpacing(TEXTRECORD textRecord, FontTag font, int textHeight) { + int totalLetterSpacing = 0; + List glyphEntries = textRecord.glyphEntries; + for (int i = 0; i < glyphEntries.size(); i++) { + GLYPHENTRY glyph = glyphEntries.get(i); + GLYPHENTRY nextGlyph = null; + if (i + 1 < glyphEntries.size()) { + nextGlyph = glyphEntries.get(i + 1); + } + + char c = font.glyphToChar(glyph.glyphIndex); + Character nextChar = nextGlyph == null ? null : font.glyphToChar(nextGlyph.glyphIndex); + int advance = getAdvance(font, glyph.glyphIndex, textHeight, c, nextChar); + int letterSpacing = glyph.glyphAdvance - advance; + totalLetterSpacing += letterSpacing; + } + + return (int) Math.round(totalLetterSpacing / glyphEntries.size()); + } + @Override public void getNeededCharacters(Set needed) { for (TEXTRECORD tr : textRecords) { diff --git a/src/com/jpexs/decompiler/flash/gui/FontPanel.java b/src/com/jpexs/decompiler/flash/gui/FontPanel.java index 80d248654..68c00ed4b 100644 --- a/src/com/jpexs/decompiler/flash/gui/FontPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FontPanel.java @@ -18,7 +18,6 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; import com.jpexs.decompiler.flash.gui.helpers.TableLayoutHelper; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.FontTag; @@ -214,28 +213,7 @@ public class FontPanel extends JPanel { fontCharactersScrollPane.getVerticalScrollBar().scrollRectToVisible(new Rectangle(0, 0, 1, 1)); setAllowSave(false); - String selectedFont; - - if (swf.sourceFontNamesMap.containsKey(ft.getFontId())) { - selectedFont = swf.sourceFontNamesMap.get(ft.getFontId()); - } else { - SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(swf.getShortFileName()); - String key = ft.getFontId() + "_" + ft.getFontNameIntag(); - if (swfConf != null && swfConf.fontPairingMap.containsKey(key)) { - selectedFont = swfConf.fontPairingMap.get(key); - } else if (Configuration.getFontToNameMap().containsKey(ft.getFontNameIntag())) { - selectedFont = Configuration.getFontToNameMap().get(ft.getFontNameIntag()); - } else { - selectedFont = FontTag.findInstalledFontName(ft.getFontName()); - } - } - - Font selFont = FontTag.installedFontsByName.get(selectedFont); - if (selFont == null) { - selectedFont = FontTag.findInstalledFontName(ft.getFontName()); - selFont = FontTag.installedFontsByName.get(selectedFont); - } - + Font selFont = ft.getSystemFont(); fontFamilyNameSelection.setSelectedItem(new FontFamily(selFont)); fontFaceSelection.setSelectedItem(new FontFace(selFont)); diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index fcd4818fc..383764cb7 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -83,7 +83,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -1667,6 +1666,7 @@ public class Main { } private static void reloadLastSession() { + boolean openingFiles = false; if (Configuration.saveSessionOnExit.get()) { String lastSession = Configuration.lastSessionFiles.get(); if (lastSession != null && lastSession.length() > 0) { @@ -1694,17 +1694,16 @@ public class Main { sourceInfos[i] = new SWFSourceInfo(null, exfiles.get(i), extitle == null || extitle.isEmpty() ? null : extitle); } if (sourceInfos.length > 0) { + openingFiles = true; openFile(sourceInfos, () -> { mainFrame.getPanel().tagTree.setSelectionPathString(Configuration.lastSessionSelection.get()); setSessionLoaded(true); }); - } else { - setSessionLoaded(true); } - } else { - setSessionLoaded(true); } - } else { + } + + if (!openingFiles) { setSessionLoaded(true); } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index f7f1b1eea..a27b8ea81 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -2486,7 +2486,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se final Font f = FontTag.installedFontsByName.get(fontName); if (f == null || !f.canDisplay(character)) { String msg = translate("error.font.nocharacter").replace("%char%", "" + character); - logger.log(Level.SEVERE, msg + " FontId: " + font.getCharacterId() + " TextId: " + textTag.getCharacterId()); + logger.log(Level.SEVERE, "{0} FontId: {1} TextId: {2}", new Object[]{msg, font.getCharacterId(), textTag.getCharacterId()}); ignoreMissingCharacters = View.showConfirmDialog(null, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, showAgainIgnoreMissingCharacters,