From 9fffe118611fee2c8940ca16802b1d44d49c1059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 28 Feb 2021 20:34:02 +0100 Subject: [PATCH] #1561 Font editing - import kerning when adding characters --- CHANGELOG.md | 5 ++ .../decompiler/flash/helpers/FontHelper.java | 37 +++---------- .../decompiler/flash/tags/DefineFont2Tag.java | 38 +++++++++++++- .../decompiler/flash/tags/DefineFont3Tag.java | 38 +++++++++++++- .../decompiler/flash/tags/base/FontTag.java | 52 +++++++++++++++++++ .../decompiler/flash/gui/FontEmbedDialog.java | 2 + .../jpexs/decompiler/flash/gui/FontPanel.java | 4 +- 7 files changed, 143 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb9000793..3b8a2e2fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added +- #1561 Font editing - import kerning when adding characters + ### Fixed - #1623 Right side marker (gray line) in P-code - #1622 Slow scrolling (search results, advanced settings and others) @@ -16,6 +19,8 @@ All notable changes to this project will be documented in this file. - Decompilation - Goto handling - Not selecting proper script after restoring session - #1603 empty script after search selection +- Generic tag tree exception on save +- Copying to clipboard does not support transparency ### Removed - #1631 ActiveX Flash component download in windows installer diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/FontHelper.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/FontHelper.java index 7835434ed..8fb2b993f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/FontHelper.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/FontHelper.java @@ -163,37 +163,14 @@ public class FontHelper { return withKerningX - noKerningX; } - /** - * Gets all kerning pairs of a Font. It is very slow. - * - * @param font - * @param size - * @return - */ - public static List getFontKerningPairs(Font font, int size) { - //NOT AVAILABLE IN java9+ - /*File fontFile = getFontFile(font); - if (fontFile != null && fontFile.getName().toLowerCase().endsWith(".ttf")) { - KerningLoader k = new KerningLoader(); - try { - return k.loadFromTTF(fontFile, size); - } catch (IOException | FontFormatException ex) { - // ignore - } - } - List ret = new ArrayList<>(); + public static List getFontKerningPairs(File fontFile, int size) { - List availableChars = new ArrayList<>(); - for (char c1 = 0; c1 < Character.MAX_VALUE; c1++) { - if (font.canDisplay((int) c1)) { - availableChars.add(c1); - } + KerningLoader k = new KerningLoader(); + try { + return k.loadFromTTF(fontFile, size); + } catch (IOException | FontFormatException ex) { + // ignore } - for (char c1 : availableChars) { - ret.addAll(getFontKerningPairsOneChar(availableChars, font, c1)); - - } - return ret;*/ return new ArrayList<>(); } @@ -343,7 +320,7 @@ public class FontHelper { Object[] paths = Files.find(d.toPath(), Integer.MAX_VALUE, new BiPredicate() { @Override public boolean test(Path t, BasicFileAttributes u) { - return u.isRegularFile() && (t.endsWith(".ttf") || t.endsWith(".TTF")); + return u.isRegularFile() && (t.toString().endsWith(".ttf") || t.toString().endsWith(".TTF")); } }).toArray(); for (Object o : paths) { 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 b272b22fc..ec53da337 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 @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; @@ -482,6 +483,33 @@ public class DefineFont2Tag extends FontTag { fontBoundsTable.set(pos, shp.getBounds()); fontAdvanceTable.set(pos, (int) getDivider() * Math.round(FontHelper.getFontAdvance(advanceFont, character))); } + + for (int k = 0; k < fontKerningTable.size(); k++) { + if (fontKerningTable.get(k).fontKerningCode1 == character + || fontKerningTable.get(k).fontKerningCode2 == character) { + fontKerningTable.remove(k); + k--; + } + } + List kerning = getFontKerningPairs(font, (int) (getDivider() * 1024)); + for (FontHelper.KerningPair pair : kerning) { + if (pair.char1 != character && pair.char2 != character) { + continue; + } + int glyph1 = charToGlyph(pair.char1); + if (pair.char1 == character) { + + } else if (glyph1 == -1) { + continue; + } + int glyph2 = charToGlyph(pair.char2); + if (pair.char2 == character) { + + } else if (glyph2 == -1) { + continue; + } + fontKerningTable.add(new KERNINGRECORD(pair.char1, pair.char2, pair.kerning)); + } } checkWideParameters(); @@ -514,6 +542,14 @@ public class DefineFont2Tag extends FontTag { if (fontFlagsHasLayout) { fontBoundsTable.remove(pos); fontAdvanceTable.remove(pos); + + for (int i = 0; i < fontKerningTable.size(); i++) { + if (fontKerningTable.get(i).fontKerningCode1 == character + || fontKerningTable.get(i).fontKerningCode2 == character) { + fontKerningTable.remove(i); + i--; + } + } } shiftGlyphIndices(fontID, pos + 1, false); 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 150bff562..9fd6127e9 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 @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; @@ -475,6 +476,33 @@ public class DefineFont3Tag extends FontTag { fontBoundsTable.set(pos, shp.getBounds()); fontAdvanceTable.set(pos, (int) getDivider() * Math.round(FontHelper.getFontAdvance(advanceFont, character))); } + + for (int k = 0; k < fontKerningTable.size(); k++) { + if (fontKerningTable.get(k).fontKerningCode1 == character + || fontKerningTable.get(k).fontKerningCode2 == character) { + fontKerningTable.remove(k); + k--; + } + } + List kerning = getFontKerningPairs(font, (int) (getDivider() * 1024)); + for (FontHelper.KerningPair pair : kerning) { + if (pair.char1 != character && pair.char2 != character) { + continue; + } + int glyph1 = charToGlyph(pair.char1); + if (pair.char1 == character) { + + } else if (glyph1 == -1) { + continue; + } + int glyph2 = charToGlyph(pair.char2); + if (pair.char2 == character) { + + } else if (glyph2 == -1) { + continue; + } + fontKerningTable.add(new KERNINGRECORD(pair.char1, pair.char2, pair.kerning)); + } } checkWideParameters(); @@ -519,6 +547,14 @@ public class DefineFont3Tag extends FontTag { if (fontFlagsHasLayout) { fontBoundsTable.remove(pos); fontAdvanceTable.remove(pos); + + for (int i = 0; i < fontKerningTable.size(); i++) { + if (fontKerningTable.get(i).fontKerningCode1 == character + || fontKerningTable.get(i).fontKerningCode2 == character) { + fontKerningTable.remove(i); + i--; + } + } } shiftGlyphIndices(fontID, pos + 1, false); 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 0cbae15a1..2d430fbd6 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 @@ -42,8 +42,10 @@ import java.awt.font.GlyphMetrics; import java.awt.font.GlyphVector; import java.awt.geom.Area; import java.io.File; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -124,14 +126,22 @@ public abstract class FontTag extends DrawableTag implements AloneTag { } private static Map> installedFontsByFamily; + private static Map> installedFontFilesByFamily; private static Map installedFontsByName; + private static Map customFontToFile; + private static String defaultFontName; private static boolean firstLoaded = false; + private static Map>>> installedFontKerningPairsByFamily; + + private static Map>> customFontKerningPairs; + + private static void ensureLoaded() { if (!firstLoaded) { reload(); @@ -272,10 +282,52 @@ public abstract class FontTag extends DrawableTag implements AloneTag { return defaultFontName; } + protected static List getFontKerningPairs(Font font, int size) { + if (customFontToFile.containsKey(font)) { + if (!customFontKerningPairs.containsKey(font) || !customFontKerningPairs.get(font).containsKey(size)) { + if (!customFontKerningPairs.containsKey(font)) { + customFontKerningPairs.put(font, new HashMap<>()); + } + customFontKerningPairs.get(font).put(size, FontHelper.getFontKerningPairs(customFontToFile.get(font), size)); + } + return customFontKerningPairs.get(font).get(size); + } + if (installedFontKerningPairsByFamily.containsKey(font.getFamily(Locale.ENGLISH)) + && installedFontKerningPairsByFamily.get(font.getFamily()).containsKey(font.getFontName(Locale.ENGLISH)) + && installedFontKerningPairsByFamily.get(font.getFamily()).get(font.getFontName(Locale.ENGLISH)).containsKey(size)) { + return installedFontKerningPairsByFamily.get(font.getFamily()).get(font.getFontName(Locale.ENGLISH)).get(size); + } + + if (installedFontFilesByFamily.containsKey(font.getFamily(Locale.ENGLISH)) && installedFontFilesByFamily.get(font.getFamily()).containsKey(font.getFontName(Locale.ENGLISH))) { + File file = installedFontFilesByFamily.get(font.getFamily(Locale.ENGLISH)).get(font.getFontName(Locale.ENGLISH)); + if (!installedFontKerningPairsByFamily.containsKey(font.getFamily(Locale.ENGLISH))) { + installedFontKerningPairsByFamily.put(font.getFamily(Locale.ENGLISH), new HashMap<>()); + } + if (!installedFontKerningPairsByFamily.get(font.getFamily(Locale.ENGLISH)).containsKey(font.getFontName(Locale.ENGLISH))) { + installedFontKerningPairsByFamily.get(font.getFamily(Locale.ENGLISH)).put(font.getFontName(Locale.ENGLISH), new HashMap<>()); + } + + installedFontKerningPairsByFamily.get(font.getFamily(Locale.ENGLISH)).get(font.getFontName(Locale.ENGLISH)).put(size, FontHelper.getFontKerningPairs(file, size)); + } + if (installedFontKerningPairsByFamily.containsKey(font.getFamily(Locale.ENGLISH)) + && installedFontKerningPairsByFamily.get(font.getFamily()).containsKey(font.getFontName(Locale.ENGLISH)) + && installedFontKerningPairsByFamily.get(font.getFamily()).get(font.getFontName(Locale.ENGLISH)).containsKey(size)) { + return installedFontKerningPairsByFamily.get(font.getFamily()).get(font.getFontName(Locale.ENGLISH)).get(size); + } + return new ArrayList<>(); + } + + public static void addCustomFont(Font font, File file) { + customFontToFile.put(font, file); + } + public static void reload() { + installedFontKerningPairsByFamily = new HashMap<>(); installedFontsByFamily = FontHelper.getInstalledFonts(); installedFontFilesByFamily = FontHelper.getInstalledFontFiles(); installedFontsByName = new HashMap<>(); + customFontToFile = new HashMap<>(); + customFontKerningPairs = new HashMap<>(); for (String fam : installedFontsByFamily.keySet()) { for (String nam : installedFontsByFamily.get(fam).keySet()) { diff --git a/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java b/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java index af7e1a60e..c65c0679e 100644 --- a/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.tags.font.CharacterRanges; import com.jpexs.helpers.Helper; import java.awt.BorderLayout; @@ -383,6 +384,7 @@ public class FontEmbedDialog extends AppDialog { File selfile = Helper.fixDialogFile(fc.getSelectedFile()); try { customFont = Font.createFont(Font.TRUETYPE_FONT, selfile); + FontTag.addCustomFont(customFont, selfile); ttfFileRadio.setText(translate("ttffile.selection").replace("%fontname%", customFont.getName()).replace("%filename%", selfile.getName())); return true; } catch (FontFormatException ex) { diff --git a/src/com/jpexs/decompiler/flash/gui/FontPanel.java b/src/com/jpexs/decompiler/flash/gui/FontPanel.java index 04d4a9f53..633e695a0 100644 --- a/src/com/jpexs/decompiler/flash/gui/FontPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FontPanel.java @@ -138,7 +138,9 @@ public class FontPanel extends JPanel { for (int ic : selChars) { char c = (char) ic; if (oldchars.indexOf((int) c) == -1) { - font = font.deriveFont(f.getFontStyle(), 1024); + if (font.getSize() != 1024) { //Do not resize if not required so we can have single instance of custom fonts + font = font.deriveFont(f.getFontStyle(), 1024); + } if (!font.canDisplay(c)) { String msg = translate("error.font.nocharacter").replace("%char%", "" + c); Logger.getLogger(FontPanel.class.getName()).log(Level.SEVERE, msg);