diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java index 8cee77563..5079d4cc5 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -3020,9 +3020,9 @@ public class SWFInputStream extends InputStream { for (int i = 0; i < numZoneData; i++) { ret.zonedata[i] = readZONEDATA(); } - readUB(6); - ret.zoneMaskX = readUB(1) == 1; + readUB(6); ret.zoneMaskY = readUB(1) == 1; + ret.zoneMaskX = readUB(1) == 1; return ret; } diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java index 1139709c9..e857b22d8 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -1592,8 +1592,8 @@ public class SWFOutputStream extends OutputStream { writeZONEDATA(value.zonedata[i]); } writeUB(6, 0); - writeUB(1, value.zoneMaskX ? 1 : 0); writeUB(1, value.zoneMaskY ? 1 : 0); + writeUB(1, value.zoneMaskX ? 1 : 0); } /** diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/Main.java b/trunk/src/com/jpexs/decompiler/flash/gui/Main.java index f63f60fd7..b61fefb75 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/Main.java @@ -36,22 +36,36 @@ import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; import com.jpexs.decompiler.flash.helpers.Cache; import com.jpexs.decompiler.flash.helpers.Helper; +import com.jpexs.decompiler.flash.types.ZONERECORD; import com.sun.jna.Platform; import com.sun.jna.WString; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.QuadCurve2D; import java.io.*; import java.net.Socket; import java.net.URLDecoder; import java.security.AccessController; import java.security.PrivilegedAction; +import java.text.AttributedCharacterIterator.Attribute; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Locale; +import java.util.Map; import java.util.Properties; import java.util.Scanner; import java.util.logging.ConsoleHandler; @@ -63,9 +77,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JFileChooser; import javax.swing.JFrame; +import static javax.swing.JFrame.EXIT_ON_CLOSE; import javax.swing.JOptionPane; +import javax.swing.JPanel; import javax.swing.SwingWorker; import javax.swing.UIManager; +import static javax.swing.WindowConstants.HIDE_ON_CLOSE; import javax.swing.filechooser.FileFilter; /** @@ -598,6 +615,7 @@ public class Main { * @throws IOException */ public static void main(String[] args) throws IOException { + loadProperties(); Configuration.loadFromFile(getConfigFile(), getReplacementsFile()); int pos = 0; @@ -613,6 +631,102 @@ public class Main { View.setLookAndFeel(); + /*View.execInEventDispatch(new Runnable() { + @Override + public void run() { + new JFrame() { + private int size = 15*1024; + @Override + public void setVisible(boolean b) { + addKeyListener(new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + size+=10; + repaint(); + } + +}); + addWindowListener(new WindowAdapter(){ + + @Override + public void windowClosing(WindowEvent e) { + super.windowClosing(e); + System.exit(0); + } + + }); + getContentPane().add(new JPanel() { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + AffineTransform t = AffineTransform.getTranslateInstance(0, 0); + t.translate(200, 400); + t.scale((((float)size)/1024)/(2000),(((float)size)/1024)/2000); + + g2d.setTransform(t); + + Font f = new Font("Cordia New", 0, size); + GlyphVector v = f.createGlyphVector((new JPanel()).getFontMetrics(f).getFontRenderContext(), "C"); + Shape shp = v.getOutline(); + g2d.setPaint(Color.black); + g.setColor(Color.black); + double points[] = new double[6]; + double lastX = 0; + double lastY = 0; + double startX = 0; + double startY = 0; + GeneralPath path=new GeneralPath(); + for (PathIterator it = shp.getPathIterator(null); !it.isDone(); it.next()) { + int type = it.currentSegment(points); + + switch (type) { + case PathIterator.SEG_MOVETO: + + lastX = (points[0]); + lastY = (points[1]); + startX = lastX; + startY = lastY; + path.moveTo(lastX, lastY); + break; + case PathIterator.SEG_LINETO: + //g2d.drawLine((int) lastX, (int) lastY, (int) points[0], (int) points[1]); + lastX = (points[0]); + lastY = (points[1]); + path.lineTo(lastX, lastY); + break; + case PathIterator.SEG_CUBICTO: + throw new RuntimeException("NOCUBIC"); + case PathIterator.SEG_QUADTO: + //g2d.draw(new QuadCurve2D.Double(lastX, lastY, points[0], points[1], points[2], points[3])); + path.quadTo(points[0], points[1], points[2], points[3]); + lastX = (points[2]); + lastY = (points[3]); + break; + case PathIterator.SEG_CLOSE: //Closing line back to last SEG_MOVETO + if ((startX == lastX) && (startY == lastY)) { + break; + } + //g2d.drawLine((int) lastX, (int) lastY, (int) points[0], (int) points[1]); + path.lineTo(startX, startY); + lastX = startX; + lastY = startY; + break; + } + } + g2d.draw(path); + } + }); + setDefaultCloseOperation(HIDE_ON_CLOSE); + setSize(500, 500); + super.setVisible(b); + } + }.setVisible(true); + } + }); + if (true) { + return; + }//*/ if ((Boolean) Configuration.getConfig("cacheOnDisk", Boolean.TRUE)) { Cache.setStorageType(Cache.STORAGE_FILES); } else { @@ -1234,7 +1348,7 @@ public class Main { if ((!exists) && add) { //add Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\ffdec"); Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\ffdec", "", "Open with FFDec"); - Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\ffdec\\command"); + Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\ffdec\\ "); Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\ffdec\\command", "", "\"" + appDir + exeName + "\" \"%1\""); } if (exists && (!add)) { //remove diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index 843a9ce45..26974e612 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -82,6 +82,7 @@ import com.jpexs.decompiler.flash.tags.base.Container; import com.jpexs.decompiler.flash.tags.base.DrawableTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.tags.base.TextTag; @@ -122,6 +123,8 @@ import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; @@ -139,6 +142,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.Set; import java.util.Stack; @@ -276,6 +280,7 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel private ErrorLogFrame errorLogFrame; private ComponentListener fontChangeList; private JComboBox fontSelection; + private Map sourceFontsMap = new HashMap<>(); private AbortRetryIgnoreHandler errorHandler = new AbortRetryIgnoreHandler() { @Override public int handle(Throwable thrown) { @@ -1162,6 +1167,15 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel fontSelection.setSelectedIndex(0); fontSelection.setSelectedItem("Times New Roman"); fontSelection.setSelectedItem("Arial"); + fontSelection.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (oldValue instanceof FontTag) { + FontTag f = (FontTag) oldValue; + sourceFontsMap.put(f.getFontId(), (String) fontSelection.getSelectedItem()); + } + } + }); fontSelectionPanel.add(fontSelection); JPanel fontCharPanel = new JPanel(); @@ -2147,6 +2161,18 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel 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)){ + JOptionPane.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) { @@ -2331,8 +2357,35 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel case "SAVETEXT": if (oldValue instanceof TextTag) { try { - ((TextTag) oldValue).setFormattedText(swf.tags, textValue.getText(), fontSelection.getSelectedItem().toString()); - setEditText(false); + if (((TextTag) oldValue).setFormattedText(new MissingCharacterHandler() { + @Override + public boolean handle(FontTag font, List tags, char character) { + String fontName = sourceFontsMap.get(font.getFontId()); + if (fontName == null) { + fontName = font.getFontName(tags); + } + // + if (!fontNames.contains(fontName)) { + fontName = "Times New Roman"; + } + if (!fontNames.contains(fontName)) { + fontName = "Arial"; + } + if (!fontNames.contains(fontName)) { + fontName = fontSelection.getItemAt(0); + } + Font f = new Font(fontName, font.getFontStyle(), 18); + if (!f.canDisplay(character)) { + JOptionPane.showMessageDialog(null, translate("error.font.nocharacter").replace("%char%", ""+character), translate("error"), JOptionPane.ERROR_MESSAGE); + return false; + } + font.addCharacter(tags, character, fontName); + return true; + + } + }, swf.tags, textValue.getText(), fontSelection.getSelectedItem().toString())) { + setEditText(false); + } } catch (ParseException ex) { JOptionPane.showMessageDialog(null, translate("error.text.invalid").replace("%text%", ex.text).replace("%line%", "" + ex.line), translate("error"), JOptionPane.ERROR_MESSAGE); } @@ -3026,10 +3079,14 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel fontLeadingLabel.setText(ft.getLeading() == -1 ? translate("value.unknown") : "" + ft.getLeading()); String chars = ft.getCharacters(swf.tags); fontCharactersTextArea.setText(chars); - fontSelection.setSelectedIndex(0); - fontSelection.setSelectedItem("Times New Roman"); - fontSelection.setSelectedItem("Arial"); - fontSelection.setSelectedItem(ft.getFontName(swf.tags)); + if (sourceFontsMap.containsKey(ft.getFontId())) { + fontSelection.setSelectedItem(sourceFontsMap.get(ft.getFontId())); + } else { + fontSelection.setSelectedIndex(0); + fontSelection.setSelectedItem("Times New Roman"); + fontSelection.setSelectedItem("Arial"); + fontSelection.setSelectedItem(ft.getFontName(swf.tags)); + } fontChangeList.componentResized(null); showDetailWithPreview(CARDFONTPANEL); } else { 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 7fbb11356..acad30069 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -331,4 +331,6 @@ menu.general = General menu.language = Language startup.welcometo = Welcome to -startup.selectopen = Click open icon on the top panel or drag SWF file to this window to start. \ No newline at end of file +startup.selectopen = Click open icon on the top panel or drag SWF file to this window to start. + +error.font.nocharacter = Selected source font does not contain character "%char%". \ 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 9fd147710..a65f2cf06 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 @@ -329,4 +329,6 @@ menu.general = Hlavn\u00ed menu.language = Jazyk startup.welcometo = V\u00edtejte v programu -startup.selectopen = Pro za\u010d\u00e1tek klikn\u011bte na otev\u0159\u00edt v horn\u00edm panelu nebo p\u0159et\u00e1hn\u011bte SWF soubor p\u0159\u00edmo do tohoto okna. \ No newline at end of file +startup.selectopen = Pro za\u010d\u00e1tek klikn\u011bte na otev\u0159\u00edt v horn\u00edm panelu nebo p\u0159et\u00e1hn\u011bte SWF soubor p\u0159\u00edmo do tohoto okna. + +error.font.nocharacter = Vybran\u00e9 zdrojov\u00e9 p\u00edsmo neobsahuje znak "%char%". \ No newline at end of file diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java index 3f0cacb81..32c9656f6 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.tags.base.BoundedTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.text.ParseException; import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; @@ -163,7 +164,7 @@ public class DefineEditTextTag extends CharacterTag implements BoundedTag, TextT } @Override - public void setFormattedText(List tags, String text, String fontName) throws ParseException { + public boolean setFormattedText(MissingCharacterHandler missingCharHandler, List tags, String text, String fontName) throws ParseException { try { TextLexer lexer = new TextLexer(new InputStreamReader(new ByteArrayInputStream(text.getBytes("UTF-8")), "UTF-8")); ParsedSymbol s = null; @@ -336,6 +337,7 @@ public class DefineEditTextTag extends CharacterTag implements BoundedTag, TextT } } + this.bounds = bounds; if (text.length() > 0) { initialText = text; this.hasText = true; @@ -384,8 +386,9 @@ public class DefineEditTextTag extends CharacterTag implements BoundedTag, TextT } catch (IOException ex) { Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); + return false; } - + return true; } @@ -464,6 +467,7 @@ public class DefineEditTextTag extends CharacterTag implements BoundedTag, TextT /** * Constructor * + * @param swf * @param data Data bytes * @param version SWF version * @param pos diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java index 328321a05..e71c78428 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java @@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import java.awt.Font; +import java.awt.font.GlyphVector; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -313,13 +314,28 @@ public class DefineFont2Tag extends FontTag { int fontStyle = getFontStyle(); SHAPE shp = SHAPERECORD.systemFontCharacterToSHAPE(fontName, fontStyle, getDivider() * 1024, character); - glyphShapeTable.add(shp); - codeTable.add((int) character); + + int code = (int) character; + int pos = -1; + for (int i = 0; i < codeTable.size(); i++) { + if (codeTable.get(i) > code) { + pos = i; + break; + } + } + if (pos == -1) { + pos = codeTable.size(); + } + + FontTag.shiftGlyphIndices(fontId, pos, tags); + + glyphShapeTable.add(pos, shp); + codeTable.add(pos, (int) character); if (fontFlagsHasLayout) { - fontBoundsTable.add(shp.getBounds()); + fontBoundsTable.add(pos, shp.getBounds()); Font fnt = new Font(fontName, fontStyle, getDivider() * 1024); - fontAdvanceTable.add((new JPanel()).getFontMetrics(fnt).charWidth(character)); + fontAdvanceTable.add(pos, (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX())); } numGlyphs++; } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java index 85baff216..e7f7ab1f9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java @@ -32,8 +32,11 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.JPanel; public class DefineFont3Tag extends FontTag { @@ -301,14 +304,39 @@ public class DefineFont3Tag extends FontTag { @Override public void addCharacter(List tags, char character, String fontName) { + + //Font Align Zones will be removed as adding new character zones is not supported:-( + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t instanceof DefineFontAlignZonesTag) { + DefineFontAlignZonesTag fa = (DefineFontAlignZonesTag) t; + if (fa.fontID == fontId) { + tags.remove(i); + i--; + } + } + } int fontStyle = getFontStyle(); SHAPE shp = SHAPERECORD.systemFontCharacterToSHAPE(fontName, fontStyle, getDivider() * 1024, character); - glyphShapeTable.add(shp); - codeTable.add((int) character); + int code = (int) character; + int pos = -1; + for (int i = 0; i < codeTable.size(); i++) { + if (codeTable.get(i) > code) { + pos = i; + break; + } + } + if (pos == -1) { + pos = codeTable.size(); + } + + FontTag.shiftGlyphIndices(fontId, pos, tags); + glyphShapeTable.add(pos, shp); + codeTable.add(pos, (int) character); if (fontFlagsHasLayout) { - fontBoundsTable.add(shp.getBounds()); + fontBoundsTable.add(pos, shp.getBounds()); Font fnt = new Font(fontName, fontStyle, getDivider() * 1024); - fontAdvanceTable.add((new JPanel()).getFontMetrics(fnt).charWidth(character)); + fontAdvanceTable.add(pos, (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX())); } numGlyphs++; } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java index fb9462ae4..33659271f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java @@ -225,14 +225,30 @@ public class DefineFontTag extends FontTag { @Override public void addCharacter(List tags, char character, String fontName) { - glyphShapeTable.add(SHAPERECORD.systemFontCharacterToSHAPE(fontName, getFontStyle(), getDivider() * 1024, character)); + SHAPE shp = SHAPERECORD.systemFontCharacterToSHAPE(fontName, getFontStyle(), getDivider() * 1024, character); + List codeTable=new ArrayList<>(); ensureFontInfo(tags); if (fontInfoTag != null) { - fontInfoTag.codeTable.add((int) character); + codeTable =fontInfoTag.codeTable; } if (fontInfo2Tag != null) { - fontInfo2Tag.codeTable.add((int) character); + codeTable =fontInfo2Tag.codeTable; } + int code=(int)character; + int pos=-1; + for(int i=0;icode){ + pos = i; + break; + } + } + if(pos==-1){ + pos = codeTable.size(); + } + FontTag.shiftGlyphIndices(fontId, pos, tags); + glyphShapeTable.add(pos,shp); + codeTable.add(pos,(int)character); + } @Override diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java index c761b502d..ed8324028 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.tags.base.BoundedTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.DrawableTag; import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.text.ParseException; import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; @@ -164,7 +165,7 @@ public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag, } @Override - public void setFormattedText(List tags, String text, String fontName) throws ParseException { + public boolean setFormattedText(MissingCharacterHandler missingCharHandler,List tags, String text, String fontName) throws ParseException { try { TextLexer lexer = new TextLexer(new InputStreamReader(new ByteArrayInputStream(text.getBytes("UTF-8")), "UTF-8")); ParsedSymbol s = null; @@ -336,12 +337,12 @@ public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag, char c = txt.charAt(i); tr.glyphEntries[i] = new GLYPHENTRY(); if (!font.containsChar(tags, c)) { - font.addCharacter(tags, c, fontName); + if(!missingCharHandler.handle(font, tags, c)){ + return false; + } } tr.glyphEntries[i].glyphIndex = font.charToGlyph(tags, c); - - /*if (tr.glyphEntries[i].glyphIndex == -1) { throw new ParseException("Font does not contain glyph for character '" + c + "'", lexer.yyline()); }*/ @@ -381,7 +382,9 @@ public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag, } catch (UnsupportedEncodingException ex) { Logger.getLogger(DefineText2Tag.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { + return false; } + return true; } @Override @@ -423,6 +426,7 @@ public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag, /** * Constructor * + * @param swf * @param data Data bytes * @param version SWF version * @param pos diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java index 5a84869c0..d7ab506a7 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.tags.base.BoundedTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.DrawableTag; import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.text.ParseException; import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; @@ -164,7 +165,7 @@ public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag, } @Override - public void setFormattedText(List tags, String text, String fontName) throws ParseException { + public boolean setFormattedText(MissingCharacterHandler missingCharHandler,List tags, String text, String fontName) throws ParseException { try { TextLexer lexer = new TextLexer(new InputStreamReader(new ByteArrayInputStream(text.getBytes("UTF-8")), "UTF-8")); ParsedSymbol s = null; @@ -335,7 +336,9 @@ public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag, char c = txt.charAt(i); tr.glyphEntries[i] = new GLYPHENTRY(); if (!font.containsChar(tags, c)) { - font.addCharacter(tags, c, fontName); + if(!missingCharHandler.handle(font, tags, c)){ + return false; + } } tr.glyphEntries[i].glyphIndex = font.charToGlyph(tags, c); @@ -381,7 +384,9 @@ public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag, } catch (UnsupportedEncodingException ex) { Logger.getLogger(DefineTextTag.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { + return false; } + return true; } @Override diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java index 366bfb2dd..208caac15 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java @@ -17,9 +17,14 @@ package com.jpexs.decompiler.flash.tags.base; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.tags.DefineText2Tag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; import java.awt.Font; +import java.util.ArrayList; import java.util.List; /** @@ -94,4 +99,30 @@ public abstract class FontTag extends CharacterTag implements AloneTag { } return name + " (" + getCharacterId() + nameAppend + ")"; } + + public static void shiftGlyphIndices(int fontId,int startIndex, List tags) { + List textRecords = new ArrayList<>(); + for (Tag t : tags) { + if (t instanceof DefineTextTag) { + textRecords.addAll(((DefineTextTag) t).textRecords); + } + if (t instanceof DefineText2Tag) { + textRecords.addAll(((DefineTextTag) t).textRecords); + } + } + int curFontId=0; + for (TEXTRECORD tr : textRecords) { + if(tr.styleFlagsHasFont){ + curFontId = tr.fontId; + } + if(curFontId!=fontId){ + continue; + } + for (GLYPHENTRY en : tr.glyphEntries) { + if (en.glyphIndex >= startIndex) { + en.glyphIndex++; + } + } + } + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/MissingCharacterHandler.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/MissingCharacterHandler.java new file mode 100644 index 000000000..f2cdc286b --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/MissingCharacterHandler.java @@ -0,0 +1,44 @@ +/* + * 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.tags.base; + +import com.jpexs.decompiler.flash.tags.Tag; +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.util.Arrays; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class MissingCharacterHandler { + protected static List fontNames = Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()); + + public boolean handle(FontTag font,List tags,char character){ + String fontName = font.getFontName(tags); + if(!fontNames.contains(fontName)){ + return false; + } + Font f=new Font(fontName,font.getFontStyle(),18); + if(!f.canDisplay(character)){ + return false; + } + font.addCharacter(tags, character, fontName); + return true; + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index ffb26d962..1681f206d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -34,7 +34,7 @@ public interface TextTag { public String getFormattedText(List tags); - public void setFormattedText(List tags, String text, String fontName) throws ParseException; + public boolean setFormattedText(MissingCharacterHandler missingCharHandler,List tags, String text, String fontName) throws ParseException; public int getCharacterId(); diff --git a/trunk/src/com/jpexs/decompiler/flash/types/ZONERECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/ZONERECORD.java index 6c501cccb..3182d39b2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/ZONERECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/ZONERECORD.java @@ -16,6 +16,14 @@ */ package com.jpexs.decompiler.flash.types; +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.LineMetrics; +import javax.swing.JFrame; + /** * Represents 32-bit alpha, red, green and blue value * @@ -26,7 +34,7 @@ public class ZONERECORD { public ZONEDATA zonedata[] = new ZONEDATA[0]; public boolean zoneMaskX; public boolean zoneMaskY; - + @Override public String toString() { String ret = "[ZONERECORD data:"; diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java index 44fc9ac77..bcf20d47b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -35,6 +35,7 @@ import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; +import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.LinearGradientPaint; @@ -48,8 +49,10 @@ import java.awt.TexturePaint; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; +import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; +import java.awt.geom.QuadCurve2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; @@ -60,6 +63,7 @@ import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.JFrame; import javax.swing.JPanel; /** @@ -366,7 +370,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { GeneralPath ret = new GeneralPath(); int x = startX; int y = startY; - boolean wasMoveTo = false; + boolean wasMoveTo = false; for (SHAPERECORD rec : edges) { int nx = rec.changeX(x); int ny = rec.changeY(y); @@ -870,7 +874,12 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } private static List existingFonts = Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()); - public static SHAPE systemFontCharacterToSHAPE(String fontName, int fontStyle, int fontSize, char character) { + public static SHAPE systemFontCharacterToSHAPE(final String fontName, final int fontStyle, int fontSize, char character) { + int multiplier = 1; + if(fontSize>1024){ + multiplier = fontSize/1024; + fontSize = 1024; + } List retList = new ArrayList<>(); String defaultFonts[] = new String[]{"Times New Roman", "Arial"}; Font f = null; @@ -887,7 +896,6 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { if (f == null) { f = new Font("Dialog", fontStyle, fontSize); //Fallback to DIALOG } - GlyphVector v = f.createGlyphVector((new JPanel()).getFontMetrics(f).getFontRenderContext(), "" + character); Shape shp = v.getOutline(); double points[] = new double[6]; @@ -896,13 +904,13 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { int startX = 0; int startY = 0; for (PathIterator it = shp.getPathIterator(null); !it.isDone(); it.next()) { - int type = it.currentSegment(points); + int type = it.currentSegment(points); switch (type) { case PathIterator.SEG_MOVETO: StyleChangeRecord scr = new StyleChangeRecord(); scr.stateMoveTo = true; - scr.moveDeltaX = (int) Math.round(points[0]); - scr.moveDeltaY = (int) Math.round(points[1]); + scr.moveDeltaX = multiplier*(int) Math.round(points[0]); + scr.moveDeltaY = multiplier*(int) Math.round(points[1]); scr.moveBits = SWFOutputStream.getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY); retList.add(scr); lastX = (int) Math.round(points[0]); @@ -911,10 +919,14 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { startY = lastY; break; case PathIterator.SEG_LINETO: - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.generalLineFlag = true; - ser.deltaX = ((int) Math.round(points[0])) - lastX; - ser.deltaY = ((int) Math.round(points[1])) - lastY; + StraightEdgeRecord ser = new StraightEdgeRecord(); + ser.deltaX = multiplier*(((int) Math.round(points[0])) - lastX); + ser.deltaY = multiplier*(((int) Math.round(points[1])) - lastY); + + ser.generalLineFlag = ser.deltaX!=0 && ser.deltaY!=0; + if(ser.deltaX == 0){ + ser.vertLineFlag = true; + } ser.numBits = SWFOutputStream.getNeededBitsS(ser.deltaX, ser.deltaY) - 2; if (ser.numBits < 0) { ser.numBits = 0; @@ -933,11 +945,11 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { double quadCoords[][] = approximateCubic(cubicCoords); for (int i = 0; i < quadCoords.length; i++) { CurvedEdgeRecord cer = new CurvedEdgeRecord(); - cer.controlDeltaX = ((int) Math.round(quadCoords[i][0])) - lastX; - cer.controlDeltaY = ((int) Math.round(quadCoords[i][1])) - lastY; - cer.anchorDeltaX = ((int) Math.round(quadCoords[i][2])) - ((int) Math.round(quadCoords[i][0])); - cer.anchorDeltaY = ((int) Math.round(quadCoords[i][3])) - ((int) Math.round(quadCoords[i][1])); - cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2; + cer.controlDeltaX = multiplier*(((int) Math.round(quadCoords[i][0])) - lastX); + cer.controlDeltaY = multiplier*(((int) Math.round(quadCoords[i][1])) - lastY); + cer.anchorDeltaX = multiplier*(((int) Math.round(quadCoords[i][2])) - ((int) Math.round(quadCoords[i][0]))); + cer.anchorDeltaY = multiplier*(((int) Math.round(quadCoords[i][3])) - ((int) Math.round(quadCoords[i][1]))); + cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2; if (cer.numBits < 0) { cer.numBits = 0; } @@ -948,17 +960,17 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { break; case PathIterator.SEG_QUADTO: CurvedEdgeRecord cer = new CurvedEdgeRecord(); - cer.controlDeltaX = ((int) Math.round(points[0])) - lastX; - cer.controlDeltaY = ((int) Math.round(points[1])) - lastY; - cer.anchorDeltaX = ((int) Math.round(points[2])) - (int) Math.round(points[0]); - cer.anchorDeltaY = ((int) Math.round(points[3])) - (int) Math.round(points[1]); + cer.controlDeltaX = multiplier*(((int) Math.round(points[0])) - lastX); + cer.controlDeltaY = multiplier*(((int) Math.round(points[1])) - lastY); + cer.anchorDeltaX = multiplier*(((int) Math.round(points[2])) - (int) Math.round(points[0])); + cer.anchorDeltaY = multiplier*(((int) Math.round(points[3])) - (int) Math.round(points[1])); cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2; if (cer.numBits < 0) { cer.numBits = 0; } - retList.add(cer); + retList.add(cer); lastX = (int) Math.round(points[2]); - lastY = (int) Math.round(points[3]); + lastY = (int) Math.round(points[3]); break; case PathIterator.SEG_CLOSE: //Closing line back to last SEG_MOVETO if ((startX == lastX) && (startY == lastY)) { @@ -966,8 +978,8 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } StraightEdgeRecord closeSer = new StraightEdgeRecord(); closeSer.generalLineFlag = true; - closeSer.deltaX = (int) Math.round((startX - lastX)); - closeSer.deltaY = (int) Math.round((startY - lastY)); + closeSer.deltaX = multiplier*((int) Math.round((startX - lastX))); + closeSer.deltaY = multiplier*((int) Math.round((startY - lastY))); closeSer.numBits = SWFOutputStream.getNeededBitsS(closeSer.deltaX, closeSer.deltaY) - 2; if (closeSer.numBits < 0) { closeSer.numBits = 0;