diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java b/trunk/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java new file mode 100644 index 000000000..d747de1f4 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.font.CharacterRanges; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; + +/** + * + * @author JPEXS + */ +public class FontEmbedDialog extends AppDialog implements ActionListener { + + private JComboBox sourceFont; + private JCheckBox[] rangeCheckboxes; + private String rangeNames[]; + private JLabel[] rangeSamples; + private JTextField individualCharsField; + private boolean result = false; + private JLabel individialSample; + private int style; + + public String getSelectedFont() { + return sourceFont.getSelectedItem().toString(); + } + + public Set getSelectedChars() { + Set chars = new TreeSet<>(); + Font f = new Font(getSelectedFont(), style, new JLabel().getFont().getSize()); + for (int i = 0; i < rangeCheckboxes.length; i++) { + if (rangeCheckboxes[i].isSelected()) { + int codes[] = CharacterRanges.rangeCodes(i); + for (int c : codes) { + if (f.canDisplay(c)) { + chars.add(c); + } + } + } + } + String indStr = individualCharsField.getText(); + for (int i = 0; i < indStr.length(); i++) { + if (f.canDisplay(indStr.codePointAt(i))) { + chars.add(indStr.codePointAt(i)); + } + } + return chars; + } + + @SuppressWarnings("unchecked") + public FontEmbedDialog(String selectedFont, String selectedChars, int style) { + setSize(900, 600); + this.style = style; + setDefaultCloseOperation(HIDE_ON_CLOSE); + setTitle(translate("dialog.title")); + + Container cnt = getContentPane(); + cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS)); + + individialSample = new JLabel(); + sourceFont = new JComboBox<>(new Vector(FontTag.fontNames)); + sourceFont.setSelectedItem(selectedFont); + cnt.add(sourceFont); + JPanel rangesPanel = new JPanel(); + rangesPanel.setLayout(new BoxLayout(rangesPanel, BoxLayout.Y_AXIS)); + int rc = CharacterRanges.rangeCount(); + rangeCheckboxes = new JCheckBox[rc]; + rangeSamples = new JLabel[rc]; + rangeNames = new String[rc]; + for (int i = 0; i < rc; i++) { + rangeNames[i] = CharacterRanges.rangeName(i); + rangeSamples[i] = new JLabel(""); + int codes[] = CharacterRanges.rangeCodes(i); + rangeCheckboxes[i] = new JCheckBox(rangeNames[i]); + JPanel rangeRowPanel = new JPanel(); + rangeRowPanel.setLayout(new BoxLayout(rangeRowPanel, BoxLayout.X_AXIS)); + rangeRowPanel.add(rangeCheckboxes[i]); + rangeRowPanel.add(Box.createHorizontalGlue()); + rangeRowPanel.add(rangeSamples[i]); + rangeRowPanel.setAlignmentX(0); + rangesPanel.add(rangeRowPanel); + } + cnt.add(new JScrollPane(rangesPanel)); + + + JPanel specialPanel = new JPanel(); + specialPanel.setLayout(new BoxLayout(specialPanel, BoxLayout.X_AXIS)); + specialPanel.add(new JLabel(translate("label.idividual"))); + individualCharsField = new JTextField(); + individualCharsField.setPreferredSize(new Dimension(100, individualCharsField.getPreferredSize().height)); + individialSample = new JLabel(); + specialPanel.add(individualCharsField); + cnt.add(specialPanel); + cnt.add(individialSample); + + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton okButton = new JButton(AppStrings.translate("button.ok")); + okButton.setActionCommand("OK"); + okButton.addActionListener(this); + JButton cancelButton = new JButton(AppStrings.translate("button.cancel")); + cancelButton.setActionCommand("CANCEL"); + cancelButton.addActionListener(this); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + cnt.add(buttonsPanel); + View.setWindowIcon(this); + View.centerScreen(this); + setModalityType(ModalityType.APPLICATION_MODAL); + individualCharsField.setText(selectedChars); + getRootPane().setDefaultButton(okButton); + sourceFont.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + updateCheckboxes(); + } + }); + updateCheckboxes(); + individualCharsField.addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + updateIndividual(); + } + }); + } + + private void updateIndividual() { + String chars = individualCharsField.getText(); + Font f = new Font(getSelectedFont(), style, new JLabel().getFont().getSize()); + String visibleChars = ""; + for (int i = 0; i < chars.length(); i++) { + if (f.canDisplay(chars.codePointAt(i))) { + visibleChars += "" + chars.charAt(i); + } + } + individialSample.setText(visibleChars); + } + + private void updateCheckboxes() { + String fontStr = sourceFont.getSelectedItem().toString(); + Font f = new Font(fontStr, style, new JLabel().getFont().getSize()); + int rc = CharacterRanges.rangeCount(); + for (int i = 0; i < rc; i++) { + rangeNames[i] = CharacterRanges.rangeName(i); + int codes[] = CharacterRanges.rangeCodes(i); + int avail = 0; + String sample = ""; + for (int c = 0; c < codes.length; c++) { + if (f.canDisplay(codes[c])) { + avail++; + if (avail < 20) { + sample += "" + (char) codes[c]; + } + } + } + rangeSamples[i].setText(sample); + rangeSamples[i].setFont(f); + rangeCheckboxes[i].setText(translate("range.description").replace("%available%", "" + avail).replace("%name%", rangeNames[i]).replace("%total%", "" + codes.length)); + } + individialSample.setFont(f); + updateIndividual(); + } + + @Override + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case "OK": + result = true; + setVisible(false); + break; + case "CANCEL": + result = false; + setVisible(false); + break; + } + } + + public boolean display() { + result = false; + setVisible(true); + return result; + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index f420b1f34..88628a849 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -157,6 +157,7 @@ import java.util.Set; import java.util.Stack; import java.util.Timer; import java.util.TimerTask; +import java.util.TreeSet; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; @@ -1216,6 +1217,10 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel fontAddCharsButton.addActionListener(this); fontAddCharsPanel.add(fontAddCharsButton); + JButton fontEmbedButton = new JButton(translate("button.font.embed")); + fontEmbedButton.setActionCommand("FONTEMBED"); + fontEmbedButton.addActionListener(this); + //fontAddCharsPanel.add(fontEmbedButton); fontParams1.add(fontAddCharsPanel); JPanel fontSelectionPanel = new JPanel(new FlowLayout()); @@ -1240,6 +1245,7 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel fontCharPanel.add(fontAddCharsPanel); fontCharPanel.add(fontSelectionPanel); fontParams1.add(fontCharPanel); + fontParams1.add(fontEmbedButton); fontPanel.setLayout(new BorderLayout()); fontParams1.add(Box.createVerticalGlue()); fontPanel.add(new JScrollPane(fontParams1), BorderLayout.CENTER); @@ -2280,9 +2286,70 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel } } + private void fontAddChars(FontTag ft, Set selChars, String selFont) { + FontTag f = (FontTag) oldValue; + String oldchars = f.getCharacters(swf.tags); + for (int ic : selChars) { + char c = (char) ic; + if (oldchars.indexOf((int) c) == -1) { + Font font = new Font(selFont, f.getFontStyle(), 1024); + if (!font.canDisplay(c)) { + View.showMessageDialog(null, translate("error.font.nocharacter").replace("%char%", "" + c), translate("error"), JOptionPane.ERROR_MESSAGE); + return; + } + } + } + + String[] yesno = new String[]{translate("button.yes"), translate("button.no"), translate("button.yes.all"), translate("button.no.all")}; + boolean yestoall = false; + boolean notoall = false; + for (int ic : selChars) { + char c = (char) ic; + if (oldchars.indexOf((int) c) > -1) { + int opt; //yes + if (!(yestoall || notoall)) { + opt = View.showOptionDialog(null, translate("message.font.add.exists").replace("%char%", "" + c), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, yesno, translate("button.yes")); + if (opt == 2) { + yestoall = true; + } + if (opt == 3) { + notoall = true; + } + } else if (yestoall) { + opt = 0; //yes + } else if (notoall) { + opt = 1; //no + } else { + opt = 1; + } + + if (opt == 1) { + continue; + } + } + f.addCharacter(swf.tags, c, fontSelection.getSelectedItem().toString()); + oldchars += c; + } + } + @Override public void actionPerformed(ActionEvent e) { switch (e.getActionCommand()) { + case "FONTEMBED": + if (oldValue instanceof FontTag) { + FontEmbedDialog fed = new FontEmbedDialog(fontSelection.getSelectedItem().toString(), fontAddCharactersField.getText(), ((FontTag) oldValue).getFontStyle()); + if (fed.display()) { + Set selChars = fed.getSelectedChars(); + if (!selChars.isEmpty()) { + String selFont = fed.getSelectedFont(); + fontSelection.setSelectedItem(selFont); + fontAddChars((FontTag) oldValue, selChars, selFont); + fontAddCharactersField.setText(""); + reload(true); + } + } + } + break; case "SELECTCOLOR": Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectcolor.title"), View.swfBackgroundColor); if (newColor != null) { @@ -2307,27 +2374,11 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel case "FONTADDCHARS": String newchars = fontAddCharactersField.getText(); if (oldValue instanceof FontTag) { - FontTag f = (FontTag) oldValue; - String oldchars = f.getCharacters(swf.tags); - - for (int i = 0; i < newchars.length(); i++) { - char c = newchars.charAt(i); - if (oldchars.indexOf((int) c) == -1) { - Font font = new Font(fontSelection.getSelectedItem().toString(), f.getFontStyle(), 1024); - if (!font.canDisplay(c)) { - View.showMessageDialog(null, translate("error.font.nocharacter").replace("%char%", "" + c), translate("error"), JOptionPane.ERROR_MESSAGE); - return; - } - } - } - - for (int i = 0; i < newchars.length(); i++) { - char c = newchars.charAt(i); - if (oldchars.indexOf((int) c) == -1) { - f.addCharacter(swf.tags, c, fontSelection.getSelectedItem().toString()); - oldchars += c; - } + Set selChars = new TreeSet<>(); + for (int c = 0; c < newchars.length(); c++) { + selChars.add(newchars.codePointAt(c)); } + fontAddChars((FontTag) oldValue, selChars, fontSelection.getSelectedItem().toString()); fontAddCharactersField.setText(""); reload(true); } diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/FontEmbedDialog.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/FontEmbedDialog.properties new file mode 100644 index 000000000..a35f9636e --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/FontEmbedDialog.properties @@ -0,0 +1,18 @@ +# Copyright (C) 2013 JPEXS +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +range.description = %name% (%available% of %total% characters) +dialog.title = Font embedding +label.idividual = Individual characters: \ No newline at end of file diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/FontEmbedDialog_cs.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/FontEmbedDialog_cs.properties new file mode 100644 index 000000000..097b29ccd --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/FontEmbedDialog_cs.properties @@ -0,0 +1,18 @@ +# Copyright (C) 2013 JPEXS +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +range.description = %name% (%available% z %total% znak\u016f) +dialog.title = Vlo\u017een\u00ed p\u00edsma +label.idividual = Individu\u00e1ln\u00ed znaky: \ No newline at end of file diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 90d038090..8fdf99f7c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -358,4 +358,8 @@ menu.tools.searchcache = Search browsers cache #after version 1.7.2u2 error.trait.exists = Trait with name "%name%" already exists. -button.addtrait = Add trait \ No newline at end of file +button.addtrait = Add trait +button.font.embed = Embed... +button.yes.all = Yes to all +button.no.all = No to all +message.font.add.exists = Character %char% already exists in the font tag.\nDo you want to replace it? \ No newline at end of file diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 1590a3b8f..0d47703c4 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -363,4 +363,8 @@ menu.tools.searchcache = Prohledat cache prohl\u00ed\u017ee\u010d\u016f #after version 1.7.2u2 error.trait.exists = Vlastnost s n\u00e1zvem "%name%" ji\u017e existuje. -button.addtrait = P\u0159idat vlastnost \ No newline at end of file +button.addtrait = P\u0159idat vlastnost +button.font.embed = Vlo\u017eit... +button.yes.all = Ano v\u0161em +button.no.all = Ne v\u0161em +message.font.add.exists = Znak %char% ji\u017e v tagu p\u00edsma existuje.\nChcete ho nahradit? \ No newline at end of file diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java index 80b8d0834..3ae1aa776 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java @@ -320,8 +320,12 @@ public class DefineFont2Tag extends FontTag { int code = (int) character; int pos = -1; + boolean exists = false; for (int i = 0; i < codeTable.size(); i++) { - if (codeTable.get(i) > code) { + if (codeTable.get(i) >= code) { + if (codeTable.get(i) == code) { + exists = true; + } pos = i; break; } @@ -330,17 +334,28 @@ public class DefineFont2Tag extends FontTag { pos = codeTable.size(); } - FontTag.shiftGlyphIndices(fontId, pos, tags); + if (!exists) { + FontTag.shiftGlyphIndices(fontId, pos, tags); + glyphShapeTable.add(pos, shp); + codeTable.add(pos, (int) character); + } else { + glyphShapeTable.set(pos, shp); + } - glyphShapeTable.add(pos, shp); - codeTable.add(pos, (int) character); if (fontFlagsHasLayout) { - fontBoundsTable.add(pos, shp.getBounds()); Font fnt = new Font(fontName, fontStyle, getDivider() * 1024); - fontAdvanceTable.add(pos, (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX())); + if (!exists) { + fontBoundsTable.add(pos, shp.getBounds()); + fontAdvanceTable.add(pos, (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX())); + } else { + fontBoundsTable.set(pos, shp.getBounds()); + fontAdvanceTable.set(pos, (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX())); + } + } + if (!exists) { + numGlyphs++; } - numGlyphs++; } @Override diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java index 84ea9eec7..310098c2f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java @@ -317,8 +317,12 @@ public class DefineFont3Tag extends FontTag { SHAPE shp = SHAPERECORD.systemFontCharacterToSHAPE(fontName, fontStyle, getDivider() * 1024, character); int code = (int) character; int pos = -1; + boolean exists = false; for (int i = 0; i < codeTable.size(); i++) { - if (codeTable.get(i) > code) { + if (codeTable.get(i) >= code) { + if (codeTable.get(i) == code) { + exists = true; + } pos = i; break; } @@ -327,15 +331,28 @@ public class DefineFont3Tag extends FontTag { pos = codeTable.size(); } - FontTag.shiftGlyphIndices(fontId, pos, tags); - glyphShapeTable.add(pos, shp); - codeTable.add(pos, (int) character); - if (fontFlagsHasLayout) { - fontBoundsTable.add(pos, shp.getBounds()); - Font fnt = new Font(fontName, fontStyle, getDivider() * 1024); - fontAdvanceTable.add(pos, (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX())); + if (!exists) { + FontTag.shiftGlyphIndices(fontId, pos, tags); + glyphShapeTable.add(pos, shp); + codeTable.add(pos, (int) character); + } else { + glyphShapeTable.set(pos, shp); + } + if (fontFlagsHasLayout) { + + Font fnt = new Font(fontName, fontStyle, getDivider() * 1024); + if (!exists) { + fontBoundsTable.add(pos, shp.getBounds()); + fontAdvanceTable.add(pos, (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX())); + } else { + fontBoundsTable.set(pos, shp.getBounds()); + fontAdvanceTable.set(pos, (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX())); + + } + } + if (!exists) { + numGlyphs++; } - numGlyphs++; } @Override diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java index f621cd781..2f3fc9c2c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java @@ -236,8 +236,12 @@ public class DefineFontTag extends FontTag { } int code = (int) character; int pos = -1; + boolean exists = false; for (int i = 0; i < codeTable.size(); i++) { - if (codeTable.get(i) > code) { + if (codeTable.get(i) >= code) { + if (codeTable.get(i) == code) { + exists = true; + } pos = i; break; } @@ -245,9 +249,13 @@ public class DefineFontTag extends FontTag { if (pos == -1) { pos = codeTable.size(); } - FontTag.shiftGlyphIndices(fontId, pos, tags); - glyphShapeTable.add(pos, shp); - codeTable.add(pos, (int) character); + if (!exists) { + FontTag.shiftGlyphIndices(fontId, pos, tags); + glyphShapeTable.add(pos, shp); + codeTable.add(pos, (int) character); + } else { + glyphShapeTable.set(pos, shp); + } }