#220 Adding characters to font

Displaying font info
This commit is contained in:
Jindra Petk
2013-07-10 17:31:53 +02:00
parent 4e997b5279
commit d3c8ce808a
12 changed files with 541 additions and 164 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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<Integer, CharacterTag> 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);
}

View File

@@ -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;

View File

@@ -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<SHAPE> glyphShapeTable;
public List<Integer> codeTable;
public int fontAscent;
public int fontDescent;
public int fontLeading;
public int fontAdvanceTable[];
public RECT fontBoundsTable[];
public List<Integer> fontAdvanceTable;
public List<RECT> 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<Long> 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<SHAPE> getGlyphShapeTable() {
return glyphShapeTable;
}
@@ -304,4 +305,28 @@ public class DefineFont2Tag extends CharacterTag implements FontTag {
}
return -1;
}
@Override
public void addCharacter(List<Tag> 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<Tag> tags) {
String ret = "";
for (int i : codeTable) {
ret += (char) i;
}
return ret;
}
}

View File

@@ -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<SHAPE> glyphShapeTable;
public List<Integer> codeTable;
public int fontAscent;
public int fontDescent;
public int fontLeading;
public int fontAdvanceTable[];
public RECT fontBoundsTable[];
public List<Integer> fontAdvanceTable;
public List<RECT> 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<Long> 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<SHAPE> getGlyphShapeTable() {
return glyphShapeTable;
}
@@ -300,4 +311,26 @@ public class DefineFont3Tag extends CharacterTag implements FontTag {
}
return -1;
}
@Override
public void addCharacter(List<Tag> 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<Tag> tags) {
String ret = "";
for (int i : codeTable) {
ret += (char) i;
}
return ret;
}
}

View File

@@ -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<SHAPE> 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<Tag> 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<Integer> 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<SHAPE> getGlyphShapeTable() {
return glyphShapeTable;
}
@@ -215,4 +219,33 @@ public class DefineFontTag extends CharacterTag implements FontTag {
public int getDivider() {
return 1;
}
@Override
public void addCharacter(List<Tag> 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<Tag> 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;
}
}

View File

@@ -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<SHAPE> 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;

View File

@@ -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<SHAPE> 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;

View File

@@ -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<Tag> tags, int glyphIndex);
public abstract List<SHAPE> getGlyphShapeTable();
public int charToGlyph(List<Tag> tags, char c);
public abstract void addCharacter(List<Tag> tags, char character);
public int getGlyphAdvance(int glyphIndex);
public abstract char glyphToChar(List<Tag> tags, int glyphIndex);
public int getGlyphWidth(int glyphIndex);
public abstract int charToGlyph(List<Tag> tags, char c);
public String getFontName(List<Tag> tags);
public abstract int getGlyphAdvance(int glyphIndex);
public boolean isSmall();
public abstract int getGlyphWidth(int glyphIndex);
public boolean isBold();
public abstract String getFontName(List<Tag> 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<Tag> 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<Tag> tags);
}

View File

@@ -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<SHAPERECORD> 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<SHAPE> systemFontCharactersToSHAPES(String fontName, int fontStyle, int fontSize, String characters) {
List<SHAPE> 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<SHAPERECORD> 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);
}
}