From d3c8ce808afae780de57e83ef4ef167bc244e142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=F8=EDk?= Date: Wed, 10 Jul 2013 17:31:53 +0200 Subject: [PATCH] #220 Adding characters to font Displaying font info --- .../decompiler/flash/SWFOutputStream.java | 21 +- .../decompiler/flash/abc/avm2/AVM2Code.java | 1 - .../treemodel/clauses/ClassTreeItem.java | 1 - .../jpexs/decompiler/flash/gui/MainFrame.java | 107 ++++++++-- .../flash/tags/DefineBitsLosslessTag.java | 2 - .../decompiler/flash/tags/DefineFont2Tag.java | 107 ++++++---- .../decompiler/flash/tags/DefineFont3Tag.java | 119 +++++++---- .../decompiler/flash/tags/DefineFontTag.java | 61 ++++-- .../decompiler/flash/tags/DefineText2Tag.java | 15 +- .../decompiler/flash/tags/DefineTextTag.java | 15 +- .../decompiler/flash/tags/base/FontTag.java | 54 +++-- .../flash/types/shaperecords/SHAPERECORD.java | 202 ++++++++++++++++-- 12 files changed, 541 insertions(+), 164 deletions(-) diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java index e8cde5c2a..1139709c9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -431,9 +431,6 @@ public class SWFOutputStream extends OutputStream { * @return Number of bits */ public static int getNeededBitsS(int v) { - /*if (v == 0) { - //return 0; - }*/ int counter = 32; int mask = 0x80000000; final int val = (v < 0) ? -v : v; @@ -444,6 +441,22 @@ public class SWFOutputStream extends OutputStream { return counter + 1; } + /** + * Calculates number of bits needed for representing signed values + * + * @param first First Signed value + * @param params Next Signed values + * @return Number of bits + */ + public static int getNeededBitsS(int first, int... params) { + int nBits = 0; + nBits = enlargeBitCountS(nBits, first); + for (int i = 0; i < params.length; i++) { + nBits = enlargeBitCountS(nBits, params[i]); + } + return nBits; + } + private static long getIntPart(double value) { if (value < 0) { return (long) Math.ceil(value); @@ -478,7 +491,7 @@ public class SWFOutputStream extends OutputStream { return getNeededBitsS(k) + 16; } - private int enlargeBitCountS(int currentBitCount, int value) { + private static int enlargeBitCountS(int currentBitCount, int value) { int neededNew = getNeededBitsS(value); if (neededNew > currentBitCount) { return neededNew; diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index b54de88d1..a12287ccd 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -55,7 +55,6 @@ import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.abc.types.traits.Traits; import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.flash.graph.DuplicateItem; import com.jpexs.decompiler.flash.graph.Graph; import com.jpexs.decompiler.flash.graph.GraphPart; import com.jpexs.decompiler.flash.graph.GraphSourceItem; diff --git a/trunk/src/com/jpexs/decompiler/flash/action/treemodel/clauses/ClassTreeItem.java b/trunk/src/com/jpexs/decompiler/flash/action/treemodel/clauses/ClassTreeItem.java index bea671e98..27800fd56 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/treemodel/clauses/ClassTreeItem.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/treemodel/clauses/ClassTreeItem.java @@ -25,7 +25,6 @@ import com.jpexs.decompiler.flash.graph.ContinueItem; import com.jpexs.decompiler.flash.graph.GraphTargetItem; import com.jpexs.decompiler.flash.helpers.Helper; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; public class ClassTreeItem extends TreeItem implements Block { diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index 798c46359..d1605f98d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -76,14 +76,12 @@ import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.tags.base.AloneTag; import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; 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.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.text.ParseException; @@ -132,9 +130,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Random; import java.util.Set; import java.util.Stack; @@ -205,6 +201,7 @@ public class MainFrame extends AppFrame implements ActionListener, TreeSelection final static String DETAILCARDAS3NAVIGATOR = "Traits list"; final static String DETAILCARDEMPTYPANEL = "Empty card"; final static String CARDTEXTPANEL = "Text card"; + final static String CARDFONTPANEL = "Font card"; private LineMarkedEditorPane textValue; private JPEGTablesTag jtt; private HashMap characters; @@ -230,6 +227,14 @@ public class MainFrame extends AppFrame implements ActionListener, TreeSelection private JCheckBoxMenuItem miDecompile; private JCheckBoxMenuItem miCacheDisk; private JCheckBoxMenuItem miGotoMainClassOnStartup; + private JLabel fontNameLabel; + private JLabel fontIsBoldLabel; + private JLabel fontIsItalicLabel; + private JLabel fontAscentLabel; + private JLabel fontDescentLabel; + private JLabel fontLeadingLabel; + private JLabel fontCharactersLabel; + private JTextField fontAddCharactersField; public void setPercent(int percent) { progressBar.setValue(percent); @@ -777,8 +782,8 @@ public class MainFrame extends AppFrame implements ActionListener, TreeSelection - JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new FlowLayout()); + JPanel textButtonsPanel = new JPanel(); + textButtonsPanel.setLayout(new FlowLayout()); textSaveButton = new JButton(translate("button.save"), View.getIcon("save16")); @@ -796,26 +801,65 @@ public class MainFrame extends AppFrame implements ActionListener, TreeSelection textCancelButton.setActionCommand("CANCELTEXT"); textCancelButton.addActionListener(this); - buttonsPanel.add(textEditButton); - buttonsPanel.add(textSaveButton); - buttonsPanel.add(textCancelButton); + textButtonsPanel.add(textEditButton); + textButtonsPanel.add(textSaveButton); + textButtonsPanel.add(textCancelButton); textSaveButton.setVisible(false); textCancelButton.setVisible(false); - textTopPanel.add(buttonsPanel, BorderLayout.SOUTH); + textTopPanel.add(textButtonsPanel, BorderLayout.SOUTH); displayWithPreview = new JPanel(new CardLayout()); + displayWithPreview.add(textTopPanel, CARDTEXTPANEL); - JPanel textPanel = new JPanel(); - textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS)); - textPanel.add(textTopPanel); - //textPanel.add(textBottomPanel); - displayWithPreview.add(textPanel, CARDTEXTPANEL); - //displayWithPreview.setVisible(false); + JPanel fontPanel = new JPanel(); + JPanel fontParams2 = new JPanel(); + fontParams2.setLayout(null); + Component ctable[][] = new Component[][]{ + {new JLabel(translate("font.name")), fontNameLabel = new JLabel(translate("value.unknown"))}, + {new JLabel(translate("font.isbold")), fontIsBoldLabel = new JLabel(translate("value.unknown"))}, + {new JLabel(translate("font.isitalic")), fontIsItalicLabel = new JLabel(translate("value.unknown"))}, + {new JLabel(translate("font.ascent")), fontAscentLabel = new JLabel(translate("value.unknown"))}, + {new JLabel(translate("font.descent")), fontDescentLabel = new JLabel(translate("value.unknown"))}, + {new JLabel(translate("font.leading")), fontLeadingLabel = new JLabel(translate("value.unknown"))}, + {new JLabel(translate("font.characters")), fontCharactersLabel = new JLabel("")} + }; + int headerWidth = 100; + int borderLeft = 10; + + for (int i = 0; i < ctable.length; i++) { + ctable[i][0].setBounds(borderLeft, 25 * i, headerWidth, 25); + ctable[i][1].setBounds(borderLeft + headerWidth + borderLeft, 25 * i, 400, 25); + fontParams2.add(ctable[i][0]); + fontParams2.add(ctable[i][1]); + } + fontParams2.setPreferredSize(new Dimension(600, ctable.length * 25)); + + + JPanel fontParams1 = new JPanel(); + + fontParams1.setLayout(new BoxLayout(fontParams1, BoxLayout.Y_AXIS)); + fontParams1.add(fontParams2); + + JPanel fontAddCharsPanel = new JPanel(new FlowLayout()); + fontAddCharsPanel.add(new JLabel(translate("font.characters.add"))); + fontAddCharactersField = new JTextField(); + fontAddCharactersField.setPreferredSize(new Dimension(100, 25)); + fontAddCharsPanel.add(fontAddCharactersField); + JButton fontAddCharsButton = new JButton(translate("button.ok")); + fontAddCharsButton.setActionCommand("FONTADDCHARS"); + fontAddCharsButton.addActionListener(this); + fontAddCharsPanel.add(fontAddCharsButton); + + fontParams1.add(fontAddCharsPanel); + fontPanel.setLayout(new BorderLayout()); + fontPanel.add(fontParams1, BorderLayout.NORTH); + + displayWithPreview.add(fontPanel, CARDFONTPANEL); Component leftComponent = null; @@ -1618,6 +1662,21 @@ public class MainFrame extends AppFrame implements ActionListener, TreeSelection @Override public void actionPerformed(ActionEvent e) { switch (e.getActionCommand()) { + 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) { + f.addCharacter(swf.tags, c); + } + } + fontAddCharactersField.setText(""); + reload(true); + } + break; case "GOTODOCUMENTCLASSONSTARTUP": Configuration.setConfig("gotoMainClassOnStartup", miGotoMainClassOnStartup.isSelected()); break; @@ -2276,7 +2335,7 @@ public class MainFrame extends AppFrame implements ActionListener, TreeSelection imagePanel.setImage(((ImageTag) tagObj).getImage(swf.tags)); } else if ((tagObj instanceof DrawableTag) && (!(tagObj instanceof TextTag)) && (miInternalViewer.isSelected())) { showCard(CARDDRAWPREVIEWPANEL); - previewImagePanel.setDrawable((DrawableTag) tagObj, swf, characters);//.setImage(((DrawableTag) tagObj).toImage(1, swf.tags, swf.displayRect, characters)); + previewImagePanel.setDrawable((DrawableTag) tagObj, swf, characters); } else if (tagObj instanceof FrameNode && ((FrameNode) tagObj).isDisplayed() && (miInternalViewer.isSelected())) { showCard(CARDDRAWPREVIEWPANEL); FrameNode fn = (FrameNode) tagObj; @@ -2410,7 +2469,7 @@ public class MainFrame extends AppFrame implements ActionListener, TreeSelection } if (tagObj instanceof FontTag) { - int countGlyphs = ((FontTag) tagObj).getGlyphShapeTable().length; + int countGlyphs = ((FontTag) tagObj).getGlyphShapeTable().size(); int fontId = ((FontTag) tagObj).getFontId(); int sloupcu = (int) Math.ceil(Math.sqrt(countGlyphs)); int radku = (int) Math.ceil(((float) countGlyphs) / ((float) sloupcu)); @@ -2477,6 +2536,18 @@ public class MainFrame extends AppFrame implements ActionListener, TreeSelection previewSplitPane.setDividerLocation(previewSplitPane.getWidth() / 2); showDetailWithPreview(CARDTEXTPANEL); textValue.setText(((TextTag) tagObj).getFormattedText(swf.tags)); + } else if (tagObj instanceof FontTag) { + parametersPanel.setVisible(true); + previewSplitPane.setDividerLocation(previewSplitPane.getWidth() / 2); + FontTag ft = (FontTag) tagObj; + fontNameLabel.setText(ft.getFontName(swf.tags)); + fontIsBoldLabel.setText(ft.isBold() ? translate("yes") : translate("no")); + fontIsItalicLabel.setText(ft.isItalic() ? translate("yes") : translate("no")); + fontDescentLabel.setText("" + ft.getDescent()); + fontAscentLabel.setText("" + ft.getDescent()); + fontLeadingLabel.setText("" + ft.getDescent()); + fontCharactersLabel.setText("" + ft.getCharacters(swf.tags)); + showDetailWithPreview(CARDFONTPANEL); } else { parametersPanel.setVisible(false); } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java index 92d359782..6fbf4a9d0 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java @@ -19,7 +19,6 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.helpers.Helper; import com.jpexs.decompiler.flash.tags.base.AloneTag; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.types.BITMAPDATA; @@ -33,7 +32,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.ArrayList; import java.util.List; import java.util.zip.InflaterInputStream; import javax.imageio.ImageIO; diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java index 66ad9f62b..e20ef045b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java @@ -18,24 +18,26 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.types.KERNINGRECORD; import com.jpexs.decompiler.flash.types.LANGCODE; 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.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import javax.swing.JPanel; /** * * * @author JPEXS */ -public class DefineFont2Tag extends CharacterTag implements FontTag { +public class DefineFont2Tag extends FontTag { public int fontId; public boolean fontFlagsHasLayout; @@ -49,14 +51,13 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { public LANGCODE languageCode; public String fontName; public int numGlyphs; - public long offsetTable[]; - public SHAPE glyphShapeTable[]; + public List glyphShapeTable; public List codeTable; public int fontAscent; public int fontDescent; public int fontLeading; - public int fontAdvanceTable[]; - public RECT fontBoundsTable[]; + public List fontAdvanceTable; + public List fontBoundsTable; public KERNINGRECORD fontKerningTable[]; public static final int ID = 48; @@ -67,13 +68,13 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { @Override public int getGlyphWidth(int glyphIndex) { - return glyphShapeTable[glyphIndex].getBounds().getWidth(); + return glyphShapeTable.get(glyphIndex).getBounds().getWidth(); } @Override public int getGlyphAdvance(int glyphIndex) { if (fontFlagsHasLayout) { - return fontAdvanceTable[glyphIndex]; + return fontAdvanceTable.get(glyphIndex); } else { return getGlyphWidth(glyphIndex) + 20; } @@ -104,31 +105,31 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { sos.write(fontName.getBytes("utf-8")); sos.writeUI16(numGlyphs); - ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(baos2, version); + List offsetTable = new ArrayList<>(); + ByteArrayOutputStream baosGlyphShapes = new ByteArrayOutputStream(); + + SWFOutputStream sos3 = new SWFOutputStream(baosGlyphShapes, version); for (int i = 0; i < numGlyphs; i++) { + offsetTable.add((glyphShapeTable.size() + 1/*CodeTableOffset*/) * (fontFlagsWideOffsets ? 4 : 2) + sos3.getPos()); + sos3.writeSHAPE(glyphShapeTable.get(i), 1); + } + byte baGlyphShapes[] = baosGlyphShapes.toByteArray(); + for (Long offset : offsetTable) { if (fontFlagsWideOffsets) { - sos2.writeUI32(offsetTable[i]); + sos.writeUI32(offset); } else { - sos2.writeUI16((int) offsetTable[i]); + sos.writeUI16((int) (long) offset); } } - byte ba2[] = baos2.toByteArray(); - ByteArrayOutputStream baos3 = new ByteArrayOutputStream(); - SWFOutputStream sos3 = new SWFOutputStream(baos3, version); - for (int i = 0; i < numGlyphs; i++) { - sos3.writeSHAPE(glyphShapeTable[i], 1); - } - byte ba3[] = baos3.toByteArray(); - sos.write(ba2); if (numGlyphs > 0) { - long offset = ba2.length + ba3.length; + long offset = (glyphShapeTable.size() + 1/*CodeTableOffset*/) * (fontFlagsWideOffsets ? 4 : 2) + baGlyphShapes.length; if (fontFlagsWideOffsets) { - sos.writeUI32(offset + 4); + sos.writeUI32(offset); } else { - sos.writeUI16((int) (offset + 2)); + sos.writeUI16((int) offset); } - sos.write(ba3); + sos.write(baGlyphShapes); + for (int i = 0; i < numGlyphs; i++) { if (fontFlagsWideCodes) { @@ -143,10 +144,10 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { sos.writeSI16(fontDescent); sos.writeSI16(fontLeading); for (int i = 0; i < numGlyphs; i++) { - sos.writeSI16(fontAdvanceTable[i]); + sos.writeSI16(fontAdvanceTable.get(i)); } for (int i = 0; i < numGlyphs; i++) { - sos.writeRECT(fontBoundsTable[i]); + sos.writeRECT(fontBoundsTable.get(i)); } sos.writeUI16(fontKerningTable.length); for (int k = 0; k < fontKerningTable.length; k++) { @@ -164,6 +165,7 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { * * @param data Data bytes * @param version SWF version + * @param pos * @throws IOException */ public DefineFont2Tag(byte data[], int version, long pos) throws IOException { @@ -182,26 +184,25 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { int fontNameLen = sis.readUI8(); fontName = new String(sis.readBytes(fontNameLen)); numGlyphs = sis.readUI16(); - offsetTable = new long[numGlyphs]; - for (int i = 0; i < numGlyphs; i++) { + //offsetTable = new long[numGlyphs]; + for (int i = 0; i < numGlyphs; i++) { //offsetTable if (fontFlagsWideOffsets) { - offsetTable[i] = sis.readUI32(); + sis.readUI32(); } else { - offsetTable[i] = sis.readUI16(); + sis.readUI16(); } } - long codeTableOffset = 0; - if (numGlyphs > 0) { + if (numGlyphs > 0) { //codeTableOffset if (fontFlagsWideOffsets) { - codeTableOffset = sis.readUI32(); + sis.readUI32(); } else { - codeTableOffset = sis.readUI16(); + sis.readUI16(); } } - glyphShapeTable = new SHAPE[numGlyphs]; + glyphShapeTable = new ArrayList<>(); for (int i = 0; i < numGlyphs; i++) { - glyphShapeTable[i] = sis.readSHAPE(1); + glyphShapeTable.add(sis.readSHAPE(1)); } codeTable = new ArrayList<>(); //[numGlyphs]; @@ -216,13 +217,13 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { fontAscent = sis.readSI16(); fontDescent = sis.readSI16(); fontLeading = sis.readSI16(); - fontAdvanceTable = new int[numGlyphs]; + fontAdvanceTable = new ArrayList<>(); for (int i = 0; i < numGlyphs; i++) { - fontAdvanceTable[i] = sis.readSI16(); + fontAdvanceTable.add(sis.readSI16()); } - fontBoundsTable = new RECT[numGlyphs]; + fontBoundsTable = new ArrayList<>(); for (int i = 0; i < numGlyphs; i++) { - fontBoundsTable[i] = sis.readRECT(); + fontBoundsTable.add(sis.readRECT()); } int kerningCount = sis.readUI16(); fontKerningTable = new KERNINGRECORD[kerningCount]; @@ -238,7 +239,7 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { } @Override - public SHAPE[] getGlyphShapeTable() { + public List getGlyphShapeTable() { return glyphShapeTable; } @@ -304,4 +305,28 @@ public class DefineFont2Tag extends CharacterTag implements FontTag { } return -1; } + + @Override + public void addCharacter(List tags, char character) { + int fontStyle = getFontStyle(); + String fname = getFontName(tags); + + SHAPE shp = SHAPERECORD.systemFontCharacterToSHAPE(fname, fontStyle, getDivider() * 1000, character); + glyphShapeTable.add(shp); + fontBoundsTable.add(shp.getBounds()); + codeTable.add((int) character); + + Font fnt = new Font(fname, fontStyle, getDivider() * 1000); + fontAdvanceTable.add((new JPanel()).getFontMetrics(fnt).charWidth(character)); + numGlyphs++; + } + + @Override + public String getCharacters(List tags) { + String ret = ""; + for (int i : codeTable) { + ret += (char) i; + } + return ret; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java index a4536e041..3a484596b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java @@ -20,20 +20,22 @@ import com.jpexs.decompiler.flash.Configuration; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.abc.CopyOutputStream; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.types.KERNINGRECORD; import com.jpexs.decompiler.flash.types.LANGCODE; 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.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; +import javax.swing.JPanel; -public class DefineFont3Tag extends CharacterTag implements FontTag { +public class DefineFont3Tag extends FontTag { public int fontId; public boolean fontFlagsHasLayout; @@ -47,14 +49,14 @@ public class DefineFont3Tag extends CharacterTag implements FontTag { public LANGCODE languageCode; public String fontName; public int numGlyphs; - public long offsetTable[]; - public SHAPE glyphShapeTable[]; + //public long offsetTable[]; + public List glyphShapeTable; public List codeTable; public int fontAscent; public int fontDescent; public int fontLeading; - public int fontAdvanceTable[]; - public RECT fontBoundsTable[]; + public List fontAdvanceTable; + public List fontBoundsTable; public KERNINGRECORD fontKerningTable[]; public static final int ID = 75; @@ -65,13 +67,13 @@ public class DefineFont3Tag extends CharacterTag implements FontTag { @Override public int getGlyphWidth(int glyphIndex) { - return glyphShapeTable[glyphIndex].getBounds().getWidth() / 20; + return glyphShapeTable.get(glyphIndex).getBounds().getWidth() / 20; } @Override public int getGlyphAdvance(int glyphIndex) { if (fontFlagsHasLayout) { - return fontAdvanceTable[glyphIndex] / 20; + return fontAdvanceTable.get(glyphIndex) / 20; } else { return getGlyphWidth(glyphIndex) + 20; } @@ -103,25 +105,37 @@ public class DefineFont3Tag extends CharacterTag implements FontTag { int fontNameLen = sis.readUI8(); fontName = new String(sis.readBytes(fontNameLen)); numGlyphs = sis.readUI16(); - offsetTable = new long[numGlyphs]; - for (int i = 0; i < numGlyphs; i++) { + for (int i = 0; i < numGlyphs; i++) { //offsetTable if (fontFlagsWideOffsets) { - offsetTable[i] = sis.readUI32(); + sis.readUI32(); } else { - offsetTable[i] = sis.readUI16(); + sis.readUI16(); } } - long codeTableOffset = 0; if (numGlyphs > 0) { if (fontFlagsWideOffsets) { - codeTableOffset = sis.readUI32(); + sis.readUI32(); //codeTableOffset } else { - codeTableOffset = sis.readUI16(); + sis.readUI16(); //codeTableOffset } } - glyphShapeTable = new SHAPE[numGlyphs]; + glyphShapeTable = new ArrayList<>(); + SHAPE firstShape = null; + int cnt = 0; for (int i = 0; i < numGlyphs; i++) { - glyphShapeTable[i] = sis.readSHAPE(1); + SHAPE shp = sis.readSHAPE(1); + /*cnt++; + if (cnt > 2) { + shp = firstShape; + SHAPE generated = SHAPERECORD.systemFontCharacterToSHAPE("Times New Roman", 0, 1000, 'A'); + shp = generated; + } else { + firstShape = shp; + }*/ + glyphShapeTable.add(shp); + //shp=SHAPERECORD.systemFontCharacterToSHAPE("Times New Roman", 0, 20000 /*??*/, 'A'); + + //glyphShapeTable.add(sis.readSHAPE(1)); } codeTable = new ArrayList<>(); //int[numGlyphs]; for (int i = 0; i < numGlyphs; i++) { @@ -135,13 +149,13 @@ public class DefineFont3Tag extends CharacterTag implements FontTag { fontAscent = sis.readSI16(); fontDescent = sis.readSI16(); fontLeading = sis.readSI16(); - fontAdvanceTable = new int[numGlyphs]; + fontAdvanceTable = new ArrayList<>(); for (int i = 0; i < numGlyphs; i++) { - fontAdvanceTable[i] = sis.readSI16(); + fontAdvanceTable.add(sis.readSI16()); } - fontBoundsTable = new RECT[numGlyphs]; + fontBoundsTable = new ArrayList<>(); for (int i = 0; i < numGlyphs; i++) { - fontBoundsTable[i] = sis.readRECT(); + fontBoundsTable.add(sis.readRECT()); } int kerningCount = sis.readUI16(); fontKerningTable = new KERNINGRECORD[kerningCount]; @@ -180,33 +194,30 @@ public class DefineFont3Tag extends CharacterTag implements FontTag { sos.write(fontName.getBytes("utf-8")); sos.writeUI16(numGlyphs); - ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(baos2, version); - for (int i = 0; i < numGlyphs; i++) { - if (fontFlagsWideOffsets) { - sos2.writeUI32(offsetTable[i]); - } else { - sos2.writeUI16((int) offsetTable[i]); - } - } - byte ba2[] = baos2.toByteArray(); - ByteArrayOutputStream baos3 = new ByteArrayOutputStream(); + List offsetTable = new ArrayList<>(); + ByteArrayOutputStream baosGlyphShapes = new ByteArrayOutputStream(); - SWFOutputStream sos3 = new SWFOutputStream(baos3, version); + SWFOutputStream sos3 = new SWFOutputStream(baosGlyphShapes, version); for (int i = 0; i < numGlyphs; i++) { - sos3.writeSHAPE(glyphShapeTable[i], 1); + offsetTable.add((glyphShapeTable.size() + 1/*CodeTableOffset*/) * (fontFlagsWideOffsets ? 4 : 2) + sos3.getPos()); + sos3.writeSHAPE(glyphShapeTable.get(i), 1); } - byte ba3[] = baos3.toByteArray(); - sos.write(ba2); - if (numGlyphs > 0) { + byte baGlyphShapes[] = baosGlyphShapes.toByteArray(); + for (Long offset : offsetTable) { + if (fontFlagsWideOffsets) { + sos.writeUI32(offset); + } else { + sos.writeUI16((int) (long) offset); + } + } + if (numGlyphs > 0) { + long offset = (glyphShapeTable.size() + 1/*CodeTableOffset*/) * (fontFlagsWideOffsets ? 4 : 2) + baGlyphShapes.length; if (fontFlagsWideOffsets) { - long offset = ba2.length + ba3.length + 4; sos.writeUI32(offset); } else { - long offset = ba2.length + ba3.length + 2; sos.writeUI16((int) offset); } - sos.write(ba3); + sos.write(baGlyphShapes); for (int i = 0; i < numGlyphs; i++) { @@ -222,10 +233,10 @@ public class DefineFont3Tag extends CharacterTag implements FontTag { sos.writeSI16(fontDescent); sos.writeSI16(fontLeading); for (int i = 0; i < numGlyphs; i++) { - sos.writeSI16(fontAdvanceTable[i]); + sos.writeSI16(fontAdvanceTable.get(i)); } for (int i = 0; i < numGlyphs; i++) { - sos.writeRECT(fontBoundsTable[i]); + sos.writeRECT(fontBoundsTable.get(i)); } sos.writeUI16(fontKerningTable.length); for (int k = 0; k < fontKerningTable.length; k++) { @@ -244,7 +255,7 @@ public class DefineFont3Tag extends CharacterTag implements FontTag { } @Override - public SHAPE[] getGlyphShapeTable() { + public List getGlyphShapeTable() { return glyphShapeTable; } @@ -300,4 +311,26 @@ public class DefineFont3Tag extends CharacterTag implements FontTag { } return -1; } + + @Override + public void addCharacter(List tags, char character) { + int fontStyle = getFontStyle(); + String fname = getFontName(tags); + SHAPE shp = SHAPERECORD.systemFontCharacterToSHAPE(fname, fontStyle, getDivider() * 1000, character); + glyphShapeTable.add(shp); + fontBoundsTable.add(new RECT());//shp.getBounds()); + codeTable.add((int) character); + Font fnt = new Font(fname, fontStyle, getDivider() * 1000); + fontAdvanceTable.add((new JPanel()).getFontMetrics(fnt).charWidth(character)); + numGlyphs++; + } + + @Override + public String getCharacters(List tags) { + String ret = ""; + for (int i : codeTable) { + ret += (char) i; + } + return ret; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java index ac3e9d3da..0da7db499 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java @@ -18,13 +18,14 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; import java.util.List; /** @@ -32,11 +33,10 @@ import java.util.List; * * @author JPEXS */ -public class DefineFontTag extends CharacterTag implements FontTag { +public class DefineFontTag extends FontTag { public int fontId; - public int offsetTable[]; - public SHAPE glyphShapeTable[]; + public List glyphShapeTable; private DefineFontInfoTag fontInfoTag = null; private DefineFontInfo2Tag fontInfo2Tag = null; @@ -52,7 +52,7 @@ public class DefineFontTag extends CharacterTag implements FontTag { @Override public int getGlyphWidth(int glyphIndex) { - return glyphShapeTable[glyphIndex].getBounds().getWidth(); + return glyphShapeTable.get(glyphIndex).getBounds().getWidth(); } private void ensureFontInfo(List tags) { @@ -111,12 +111,17 @@ public class DefineFontTag extends CharacterTag implements FontTag { SWFOutputStream sos = new SWFOutputStream(os, version); try { sos.writeUI16(fontId); + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + List offsetTable = new ArrayList<>(); + SWFOutputStream sos2 = new SWFOutputStream(baos2, version); + for (SHAPE shape : glyphShapeTable) { + offsetTable.add(glyphShapeTable.size() * 2 + (int) sos2.getPos()); + sos2.writeSHAPE(shape, 1); + } for (int offset : offsetTable) { sos.writeUI16(offset); } - for (SHAPE shape : glyphShapeTable) { - sos.writeSHAPE(shape, 1); - } + sos.write(baos2.toByteArray()); } catch (IOException e) { } return baos.toByteArray(); @@ -136,14 +141,13 @@ public class DefineFontTag extends CharacterTag implements FontTag { fontId = sis.readUI16(); int firstOffset = sis.readUI16(); int nGlyphs = firstOffset / 2; - offsetTable = new int[nGlyphs]; - glyphShapeTable = new SHAPE[nGlyphs]; - offsetTable[0] = firstOffset; + glyphShapeTable = new ArrayList<>(); + for (int i = 1; i < nGlyphs; i++) { - offsetTable[i] = sis.readUI16(); + sis.readUI16(); //offset } for (int i = 0; i < nGlyphs; i++) { - glyphShapeTable[i] = sis.readSHAPE(1); + glyphShapeTable.add(sis.readSHAPE(1)); } } @@ -153,7 +157,7 @@ public class DefineFontTag extends CharacterTag implements FontTag { } @Override - public SHAPE[] getGlyphShapeTable() { + public List getGlyphShapeTable() { return glyphShapeTable; } @@ -215,4 +219,33 @@ public class DefineFontTag extends CharacterTag implements FontTag { public int getDivider() { return 1; } + + @Override + public void addCharacter(List tags, char character) { + glyphShapeTable.add(SHAPERECORD.systemFontCharacterToSHAPE(getFontName(tags), getFontStyle(), getDivider() * 1000, character)); + ensureFontInfo(tags); + if (fontInfoTag != null) { + fontInfoTag.codeTable.add((int) character); + } + if (fontInfo2Tag != null) { + fontInfo2Tag.codeTable.add((int) character); + } + } + + @Override + public String getCharacters(List tags) { + String ret = ""; + ensureFontInfo(tags); + if (fontInfoTag != null) { + for (int i : fontInfoTag.codeTable) { + ret += (char) i; + } + } + if (fontInfo2Tag != null) { + for (int i : fontInfo2Tag.codeTable) { + ret += (char) i; + } + } + return ret; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java index 5e19474e6..185c0711f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java @@ -329,13 +329,16 @@ public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag, for (int i = 0; i < txt.length(); i++) { char c = txt.charAt(i); tr.glyphEntries[i] = new GLYPHENTRY(); + if (!font.containsChar(tags, c)) { + font.addCharacter(tags, c); + } 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()); - } + /*if (tr.glyphEntries[i].glyphIndex == -1) { + throw new ParseException("Font does not contain glyph for character '" + c + "'", lexer.yyline()); + }*/ tr.glyphEntries[i].glyphAdvance = textHeight * font.getGlyphAdvance(tr.glyphEntries[i].glyphIndex) / 1024; int gw = textHeight * font.getGlyphWidth(tr.glyphEntries[i].glyphIndex) / 1024; @@ -458,7 +461,7 @@ public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag, g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - SHAPE glyphs[] = new SHAPE[0]; + List glyphs = new ArrayList<>(); for (TEXTRECORD rec : textRecords) { if (rec.styleFlagsHasColor) { textColor = rec.textColorA.toColor(); @@ -476,12 +479,12 @@ public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag, } for (GLYPHENTRY entry : rec.glyphEntries) { - RECT rect = SHAPERECORD.getBounds(glyphs[entry.glyphIndex].shapeRecords); + RECT rect = SHAPERECORD.getBounds(glyphs.get(entry.glyphIndex).shapeRecords); rect.Xmax /= font.getDivider(); rect.Xmin /= font.getDivider(); rect.Ymax /= font.getDivider(); rect.Ymin /= font.getDivider(); - BufferedImage img = SHAPERECORD.shapeToImage(tags, 4, null, null, glyphs[entry.glyphIndex].shapeRecords, textColor); + BufferedImage img = SHAPERECORD.shapeToImage(tags, 4, null, null, glyphs.get(entry.glyphIndex).shapeRecords, textColor); AffineTransform tr = new AffineTransform(); tr.setToIdentity(); float rat = textHeight / 1000f; diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java index f33fcafa4..94721488e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java @@ -328,13 +328,16 @@ public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag, for (int i = 0; i < txt.length(); i++) { char c = txt.charAt(i); tr.glyphEntries[i] = new GLYPHENTRY(); + if (!font.containsChar(tags, c)) { + font.addCharacter(tags, c); + } 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()); - } + /*if (tr.glyphEntries[i].glyphIndex == -1) { + throw new ParseException("Font does not contain glyph for character '" + c + "'", lexer.yyline()); + }*/ tr.glyphEntries[i].glyphAdvance = textHeight * font.getGlyphAdvance(tr.glyphEntries[i].glyphIndex) / 1024; int gw = textHeight * font.getGlyphWidth(tr.glyphEntries[i].glyphIndex) / 1024; @@ -471,7 +474,7 @@ public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag, g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - SHAPE glyphs[] = new SHAPE[0]; + List glyphs = new ArrayList<>(); for (TEXTRECORD rec : textRecords) { if (rec.styleFlagsHasColor) { textColor = rec.textColor.toColor(); @@ -489,12 +492,12 @@ public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag, } for (GLYPHENTRY entry : rec.glyphEntries) { - RECT rect = SHAPERECORD.getBounds(glyphs[entry.glyphIndex].shapeRecords); + RECT rect = SHAPERECORD.getBounds(glyphs.get(entry.glyphIndex).shapeRecords); rect.Xmax /= font.getDivider(); rect.Xmin /= font.getDivider(); rect.Ymax /= font.getDivider(); rect.Ymin /= font.getDivider(); - BufferedImage img = SHAPERECORD.shapeToImage(tags, 1, null, null, glyphs[entry.glyphIndex].shapeRecords, textColor); + BufferedImage img = SHAPERECORD.shapeToImage(tags, 1, null, null, glyphs.get(entry.glyphIndex).shapeRecords, textColor); AffineTransform tr = new AffineTransform(); tr.setToIdentity(); float rat = textHeight / 1000f; 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 d262330b4..4426a02a0 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java @@ -18,39 +18,63 @@ package com.jpexs.decompiler.flash.tags.base; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.types.SHAPE; +import java.awt.Font; import java.util.List; /** * * @author JPEXS */ -public interface FontTag extends AloneTag { +public abstract class FontTag extends CharacterTag implements AloneTag { - public int getFontId(); + public FontTag(int id, String name, byte data[], long pos) { + super(id, name, data, pos); + } - public SHAPE[] getGlyphShapeTable(); + public abstract int getFontId(); - public char glyphToChar(List tags, int glyphIndex); + public abstract List getGlyphShapeTable(); - public int charToGlyph(List tags, char c); + public abstract void addCharacter(List tags, char character); - public int getGlyphAdvance(int glyphIndex); + public abstract char glyphToChar(List tags, int glyphIndex); - public int getGlyphWidth(int glyphIndex); + public abstract int charToGlyph(List tags, char c); - public String getFontName(List tags); + public abstract int getGlyphAdvance(int glyphIndex); - public boolean isSmall(); + public abstract int getGlyphWidth(int glyphIndex); - public boolean isBold(); + public abstract String getFontName(List tags); - public boolean isItalic(); + public abstract boolean isSmall(); - public int getDivider(); + public abstract boolean isBold(); - public int getAscent(); + public abstract boolean isItalic(); - public int getDescent(); + public abstract int getDivider(); - public int getLeading(); + public abstract int getAscent(); + + public abstract int getDescent(); + + public abstract int getLeading(); + + public boolean containsChar(List tags, char character) { + return charToGlyph(tags, character) > -1; + } + + public int getFontStyle() { + int fontStyle = 0; + if (isBold()) { + fontStyle |= Font.BOLD; + } + if (isItalic()) { + fontStyle |= Font.ITALIC; + } + return fontStyle; + } + + public abstract String getCharacters(List tags); } 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 3874416f5..2871e6b0e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.types.shaperecords; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; @@ -29,9 +30,11 @@ import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPE; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Font; import java.awt.Graphics2D; import java.awt.LinearGradientPaint; import java.awt.MultipleGradientPaint.CycleMethod; @@ -39,9 +42,13 @@ import java.awt.Point; import java.awt.RadialGradientPaint; import java.awt.Rectangle; import java.awt.RenderingHints; +import java.awt.Shape; import java.awt.TexturePaint; +import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.HashMap; @@ -51,6 +58,7 @@ import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.JPanel; /** * @@ -66,19 +74,6 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { return ret; } - private static String edgesToStr(List records) { - int x = 0; - int y = 0; - String ret = ""; - for (SHAPERECORD rec : records) { - x = rec.changeX(x); - y = rec.changeY(y); - ret += ("[" + x + "," + y + "],"); - } - return ret; - - } - private static class Path { public boolean used = false; @@ -859,4 +854,185 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } public abstract boolean isMove(); + + public static List systemFontCharactersToSHAPES(String fontName, int fontStyle, int fontSize, String characters) { + List ret = new ArrayList<>(); + for (int i = 0; i < characters.length(); i++) { + ret.add(systemFontCharacterToSHAPE(fontName, fontStyle, fontSize, characters.charAt(i))); + } + return ret; + } + + public static SHAPE systemFontCharacterToSHAPE(String fontName, int fontStyle, int fontSize, char character) { + List retList = new ArrayList<>(); + Font f = new Font(fontName, fontStyle, fontSize); + GlyphVector v = f.createGlyphVector((new JPanel()).getFontMetrics(f).getFontRenderContext(), "" + character); + Shape shp = v.getOutline(); + double points[] = new double[6]; + double lastX = 0; + double lastY = 0; + double startX = 0; + double startY = 0; + 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; + StyleChangeRecord scr = new StyleChangeRecord(); + scr.stateMoveTo = true; + scr.moveDeltaX = (int) Math.round(points[0]); + scr.moveDeltaY = (int) Math.round(points[1]); + scr.moveBits = SWFOutputStream.getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY); + retList.add(scr); + 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)); + ser.numBits = SWFOutputStream.getNeededBitsS(ser.deltaX, ser.deltaY) - 2; + if (ser.numBits < 0) { + ser.numBits = 0; + } + retList.add(ser); + lastX = points[0]; + lastY = points[1]; + break; + case PathIterator.SEG_CUBICTO: + double cubicCoords[] = new double[]{ + lastX, lastY, + points[0], points[1], + points[2], points[3], + points[4], points[5] + }; + 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] - quadCoords[i][0])); + cer.anchorDeltaY = (int) Math.round((quadCoords[i][3] - quadCoords[i][1])); + cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2; + if (cer.numBits < 0) { + cer.numBits = 0; + } + lastX = quadCoords[i][2]; + lastY = quadCoords[i][3]; + retList.add(cer); + } + 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] - points[0])); + cer.anchorDeltaY = (int) Math.round((points[3] - 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); + lastX = points[2]; + lastY = points[3]; + break; + case PathIterator.SEG_CLOSE: //Closing line back to last SEG_MOVETO + if ((startX == lastX) && (startY == lastY)) { + break; + } + StraightEdgeRecord closeSer = new StraightEdgeRecord(); + closeSer.generalLineFlag = true; + closeSer.deltaX = (int) Math.round((startX - lastX)); + closeSer.deltaY = (int) Math.round((startY - lastY)); + closeSer.numBits = SWFOutputStream.getNeededBitsS(closeSer.deltaX, closeSer.deltaY) - 2; + if (closeSer.numBits < 0) { + closeSer.numBits = 0; + } + retList.add(closeSer); + lastX = startX; + lastY = startY; + break; + } + } + SHAPE shape = new SHAPE(); + StyleChangeRecord init; + if (!retList.isEmpty() && retList.get(0) instanceof StyleChangeRecord) { + init = (StyleChangeRecord) retList.get(0); + } else { + init = new StyleChangeRecord(); + retList.add(0, init); + } + + + retList.add(new EndShapeRecord()); + init.stateFillStyle0 = true; + init.fillStyle0 = 1; + shape.shapeRecords = retList; + shape.numFillBits = 1; + shape.numLineBits = 0; + return shape; + } + + // Taken from org.apache.fop.afp.util + private static double[][] approximateCubic(double[] cubicControlPointCoords) { + if (cubicControlPointCoords.length < 8) { + throw new IllegalArgumentException("Must have at least 8 coordinates"); + } + + //extract point objects from source array + Point2D p0 = new Point2D.Double(cubicControlPointCoords[0], cubicControlPointCoords[1]); + Point2D p1 = new Point2D.Double(cubicControlPointCoords[2], cubicControlPointCoords[3]); + Point2D p2 = new Point2D.Double(cubicControlPointCoords[4], cubicControlPointCoords[5]); + Point2D p3 = new Point2D.Double(cubicControlPointCoords[6], cubicControlPointCoords[7]); + + //calculates the useful base points + Point2D pa = getPointOnSegment(p0, p1, 3.0 / 4.0); + Point2D pb = getPointOnSegment(p3, p2, 3.0 / 4.0); + + //get 1/16 of the [P3, P0] segment + double dx = (p3.getX() - p0.getX()) / 16.0; + double dy = (p3.getY() - p0.getY()) / 16.0; + + //calculates control point 1 + Point2D pc1 = getPointOnSegment(p0, p1, 3.0 / 8.0); + + //calculates control point 2 + Point2D pc2 = getPointOnSegment(pa, pb, 3.0 / 8.0); + pc2 = movePoint(pc2, -dx, -dy); + + //calculates control point 3 + Point2D pc3 = getPointOnSegment(pb, pa, 3.0 / 8.0); + pc3 = movePoint(pc3, dx, dy); + + //calculates control point 4 + Point2D pc4 = getPointOnSegment(p3, p2, 3.0 / 8.0); + + //calculates the 3 anchor points + Point2D pa1 = getMidPoint(pc1, pc2); + Point2D pa2 = getMidPoint(pa, pb); + Point2D pa3 = getMidPoint(pc3, pc4); + + //return the points for the four quadratic curves + return new double[][]{ + {pc1.getX(), pc1.getY(), pa1.getX(), pa1.getY()}, + {pc2.getX(), pc2.getY(), pa2.getX(), pa2.getY()}, + {pc3.getX(), pc3.getY(), pa3.getX(), pa3.getY()}, + {pc4.getX(), pc4.getY(), p3.getX(), p3.getY()}}; + } + + private static Point2D.Double movePoint(Point2D point, double dx, double dy) { + return new Point2D.Double(point.getX() + dx, point.getY() + dy); + } + + private static Point2D getMidPoint(Point2D p0, Point2D p1) { + return getPointOnSegment(p0, p1, 0.5); + } + + private static Point2D getPointOnSegment(Point2D p0, Point2D p1, double ratio) { + double x = p0.getX() + ((p1.getX() - p0.getX()) * ratio); + double y = p0.getY() + ((p1.getY() - p0.getY()) * ratio); + return new Point2D.Double(x, y); + } }