remove character functionality

This commit is contained in:
2016-06-08 15:22:32 +02:00
parent fc0e941470
commit eaca6361c7
13 changed files with 5064 additions and 4852 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -145,11 +145,17 @@ public class DefineFontInfo2Tag extends FontInfoTag {
}
@Override
public void addCharacter(int index, int character) {
public void addFontCharacter(int index, int character) {
codeTable.add(index, character);
setModified(true);
}
@Override
public void removeFontCharacter(int index) {
codeTable.remove(index);
setModified(true);
}
@Override
public String getFontName() {
return fontName;
@@ -146,11 +146,17 @@ public class DefineFontInfoTag extends FontInfoTag {
}
@Override
public void addCharacter(int index, int character) {
public void addFontCharacter(int index, int character) {
codeTable.add(index, character);
setModified(true);
}
@Override
public void removeFontCharacter(int index) {
codeTable.remove(index);
setModified(true);
}
@Override
public String getFontName() {
return fontName;
@@ -1,346 +1,382 @@
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
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.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.FontInfoTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.helpers.ByteArrayRange;
import java.awt.Font;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
@SWFVersion(from = 1)
public class DefineFontTag extends FontTag {
public static final int ID = 10;
public static final String NAME = "DefineFont";
@SWFType(BasicType.UI16)
public int fontId;
public List<SHAPE> glyphShapeTable;
@Internal
private FontInfoTag fontInfoTag = null;
/**
* Constructor
*
* @param swf
*/
public DefineFontTag(SWF swf) {
super(swf, ID, NAME, null);
fontId = swf.getNextCharacterId();
glyphShapeTable = new ArrayList<>();
}
/**
* Constructor
*
* @param sis
* @param data
* @throws IOException
*/
public DefineFontTag(SWFInputStream sis, ByteArrayRange data) throws IOException {
super(sis.getSwf(), ID, NAME, data);
readData(sis, data, 0, false, false, false);
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
fontId = sis.readUI16("fontId");
glyphShapeTable = new ArrayList<>();
if (sis.available() > 0) {
long pos = sis.getPos();
int firstOffset = sis.readUI16("firstOffset");
int nGlyphs = firstOffset / 2;
long[] offsetTable = new long[nGlyphs];
offsetTable[0] = firstOffset;
for (int i = 1; i < nGlyphs; i++) {
offsetTable[i] = sis.readUI16("offset");
}
for (int i = 0; i < nGlyphs; i++) {
sis.seek(pos + offsetTable[i]);
glyphShapeTable.add(sis.readSHAPE(1, false, "shape"));
}
}
}
/**
* Gets data bytes
*
* @param sos SWF output stream
* @throws java.io.IOException
*/
@Override
public void getData(SWFOutputStream sos) throws IOException {
sos.writeUI16(fontId);
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
List<Integer> offsetTable = new ArrayList<>();
SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion());
for (SHAPE shape : glyphShapeTable) {
offsetTable.add(glyphShapeTable.size() * 2 + (int) sos2.getPos());
sos2.writeSHAPE(shape, 1);
}
for (int offset : offsetTable) {
sos.writeUI16(offset);
}
sos.write(baos2.toByteArray());
}
@Override
public boolean isSmall() {
return false;
}
@Override
public double getGlyphAdvance(int glyphIndex) {
return -1;
}
@Override
public int getGlyphWidth(int glyphIndex) {
return glyphShapeTable.get(glyphIndex).getBounds().getWidth();
}
private void ensureFontInfo() {
if (fontInfoTag == null) {
List<CharacterIdTag> characterIdTags = swf.getCharacterIdTags(fontId);
if (characterIdTags != null) {
for (CharacterIdTag t : characterIdTags) {
if (t instanceof FontInfoTag) {
if (((FontInfoTag) t).fontID == fontId) {
fontInfoTag = (FontInfoTag) t;
break;
}
}
}
}
}
}
@Override
public char glyphToChar(int glyphIndex) {
ensureFontInfo();
if (fontInfoTag != null) {
return (char) (int) fontInfoTag.getCodeTable().get(glyphIndex);
} else {
return '?';
}
}
@Override
public int charToGlyph(char c) {
ensureFontInfo();
if (fontInfoTag != null) {
return fontInfoTag.getCodeTable().indexOf((int) c);
}
return -1;
}
@Override
public List<SHAPE> getGlyphShapeTable() {
return glyphShapeTable;
}
@Override
public int getCharacterId() {
return fontId;
}
@Override
public void setCharacterId(int characterId) {
this.fontId = characterId;
}
@Override
public String getFontNameIntag() {
ensureFontInfo();
if (fontInfoTag != null) {
return fontInfoTag.getFontName();
}
return null;
}
@Override
public boolean isBold() {
if (fontInfoTag != null) {
return fontInfoTag.getFontFlagsBold();
}
return false;
}
@Override
public boolean isItalic() {
if (fontInfoTag != null) {
return fontInfoTag.getFontFlagsItalic();
}
return false;
}
@Override
public boolean isSmallEditable() {
return false;
}
@Override
public boolean isBoldEditable() {
return fontInfoTag != null;
}
@Override
public boolean isItalicEditable() {
return fontInfoTag != null;
}
@Override
public void setSmall(boolean value) {
}
@Override
public void setBold(boolean value) {
if (fontInfoTag != null) {
fontInfoTag.setFontFlagsBold(value);
}
}
@Override
public void setItalic(boolean value) {
if (fontInfoTag != null) {
fontInfoTag.setFontFlagsItalic(value);
}
}
@Override
public int getAscent() {
return -1;
}
@Override
public int getDescent() {
return -1;
}
@Override
public int getLeading() {
return -1;
}
@Override
public double getDivider() {
return 1;
}
@Override
public void addCharacter(char character, Font font) {
SHAPE shp = SHAPERECORD.fontCharacterToSHAPE(font, (int) Math.round(getDivider() * 1024), character);
ensureFontInfo();
int code = (int) character;
int pos = -1;
boolean exists = false;
if (fontInfoTag != null) {
List<Integer> codeTable = fontInfoTag.getCodeTable();
for (int i = 0; i < codeTable.size(); i++) {
if (codeTable.get(i) >= code) {
if (codeTable.get(i) == code) {
exists = true;
}
pos = i;
break;
}
}
if (pos == -1) {
pos = codeTable.size();
}
} else {
pos = 0;
}
if (!exists) {
shiftGlyphIndices(fontId, pos);
glyphShapeTable.add(pos, shp);
if (fontInfoTag != null) {
fontInfoTag.addCharacter(pos, (int) character);
}
} else {
glyphShapeTable.set(pos, shp);
}
setModified(true);
}
@Override
public void setAdvanceValues(Font font) {
throw new UnsupportedOperationException("Setting the advance values for DefineFontTag is not supported.");
}
@Override
public int getCharacterCount() {
ensureFontInfo();
if (fontInfoTag != null) {
List<Integer> codeTable = fontInfoTag.getCodeTable();
return codeTable.size();
}
return 0;
}
@Override
public String getCharacters() {
ensureFontInfo();
if (fontInfoTag != null) {
List<Integer> codeTable = fontInfoTag.getCodeTable();
StringBuilder ret = new StringBuilder(codeTable.size());
for (int i : codeTable) {
ret.append((char) i);
}
return ret.toString();
}
return "";
}
@Override
public int getGlyphKerningAdjustment(int glyphIndex, int nextGlyphIndex) {
return 0;
}
@Override
public int getCharKerningAdjustment(char c1, char c2) {
return 0;
}
}
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
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.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.FontInfoTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.helpers.ByteArrayRange;
import java.awt.Font;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
@SWFVersion(from = 1)
public class DefineFontTag extends FontTag {
public static final int ID = 10;
public static final String NAME = "DefineFont";
@SWFType(BasicType.UI16)
public int fontId;
public List<SHAPE> glyphShapeTable;
@Internal
private FontInfoTag fontInfoTag = null;
/**
* Constructor
*
* @param swf
*/
public DefineFontTag(SWF swf) {
super(swf, ID, NAME, null);
fontId = swf.getNextCharacterId();
glyphShapeTable = new ArrayList<>();
}
/**
* Constructor
*
* @param sis
* @param data
* @throws IOException
*/
public DefineFontTag(SWFInputStream sis, ByteArrayRange data) throws IOException {
super(sis.getSwf(), ID, NAME, data);
readData(sis, data, 0, false, false, false);
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
fontId = sis.readUI16("fontId");
glyphShapeTable = new ArrayList<>();
if (sis.available() > 0) {
long pos = sis.getPos();
int firstOffset = sis.readUI16("firstOffset");
int nGlyphs = firstOffset / 2;
long[] offsetTable = new long[nGlyphs];
offsetTable[0] = firstOffset;
for (int i = 1; i < nGlyphs; i++) {
offsetTable[i] = sis.readUI16("offset");
}
for (int i = 0; i < nGlyphs; i++) {
sis.seek(pos + offsetTable[i]);
glyphShapeTable.add(sis.readSHAPE(1, false, "shape"));
}
}
}
/**
* Gets data bytes
*
* @param sos SWF output stream
* @throws java.io.IOException
*/
@Override
public void getData(SWFOutputStream sos) throws IOException {
sos.writeUI16(fontId);
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
List<Integer> offsetTable = new ArrayList<>();
SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion());
for (SHAPE shape : glyphShapeTable) {
offsetTable.add(glyphShapeTable.size() * 2 + (int) sos2.getPos());
sos2.writeSHAPE(shape, 1);
}
for (int offset : offsetTable) {
sos.writeUI16(offset);
}
sos.write(baos2.toByteArray());
}
@Override
public boolean isSmall() {
return false;
}
@Override
public double getGlyphAdvance(int glyphIndex) {
return -1;
}
@Override
public int getGlyphWidth(int glyphIndex) {
return glyphShapeTable.get(glyphIndex).getBounds().getWidth();
}
private void ensureFontInfo() {
if (fontInfoTag == null) {
List<CharacterIdTag> characterIdTags = swf.getCharacterIdTags(fontId);
if (characterIdTags != null) {
for (CharacterIdTag t : characterIdTags) {
if (t instanceof FontInfoTag) {
if (((FontInfoTag) t).fontID == fontId) {
fontInfoTag = (FontInfoTag) t;
break;
}
}
}
}
}
}
@Override
public char glyphToChar(int glyphIndex) {
ensureFontInfo();
if (fontInfoTag != null) {
return (char) (int) fontInfoTag.getCodeTable().get(glyphIndex);
} else {
return '?';
}
}
@Override
public int charToGlyph(char c) {
ensureFontInfo();
if (fontInfoTag != null) {
return fontInfoTag.getCodeTable().indexOf((int) c);
}
return -1;
}
@Override
public List<SHAPE> getGlyphShapeTable() {
return glyphShapeTable;
}
@Override
public int getCharacterId() {
return fontId;
}
@Override
public void setCharacterId(int characterId) {
this.fontId = characterId;
}
@Override
public String getFontNameIntag() {
ensureFontInfo();
if (fontInfoTag != null) {
return fontInfoTag.getFontName();
}
return null;
}
@Override
public boolean isBold() {
if (fontInfoTag != null) {
return fontInfoTag.getFontFlagsBold();
}
return false;
}
@Override
public boolean isItalic() {
if (fontInfoTag != null) {
return fontInfoTag.getFontFlagsItalic();
}
return false;
}
@Override
public boolean isSmallEditable() {
return false;
}
@Override
public boolean isBoldEditable() {
return fontInfoTag != null;
}
@Override
public boolean isItalicEditable() {
return fontInfoTag != null;
}
@Override
public void setSmall(boolean value) {
}
@Override
public void setBold(boolean value) {
if (fontInfoTag != null) {
fontInfoTag.setFontFlagsBold(value);
}
}
@Override
public void setItalic(boolean value) {
if (fontInfoTag != null) {
fontInfoTag.setFontFlagsItalic(value);
}
}
@Override
public int getAscent() {
return -1;
}
@Override
public int getDescent() {
return -1;
}
@Override
public int getLeading() {
return -1;
}
@Override
public double getDivider() {
return 1;
}
@Override
public void addCharacter(char character, Font font) {
SHAPE shp = SHAPERECORD.fontCharacterToSHAPE(font, (int) Math.round(getDivider() * 1024), character);
ensureFontInfo();
int code = (int) character;
int pos = -1;
boolean exists = false;
if (fontInfoTag != null) {
List<Integer> codeTable = fontInfoTag.getCodeTable();
for (int i = 0; i < codeTable.size(); i++) {
if (codeTable.get(i) >= code) {
if (codeTable.get(i) == code) {
exists = true;
}
pos = i;
break;
}
}
if (pos == -1) {
pos = codeTable.size();
}
} else {
pos = 0;
}
if (!exists) {
shiftGlyphIndices(fontId, pos, true);
glyphShapeTable.add(pos, shp);
if (fontInfoTag != null) {
fontInfoTag.addFontCharacter(pos, (int) character);
}
} else {
glyphShapeTable.set(pos, shp);
}
setModified(true);
getSwf().clearImageCache();
}
@Override
public boolean removeCharacter(char character) {
ensureFontInfo();
if (fontInfoTag == null) {
return false;
}
int code = (int) character;
int pos = -1;
List<Integer> codeTable = fontInfoTag.getCodeTable();
for (int i = 0; i < codeTable.size(); i++) {
if (codeTable.get(i) >= code) {
if (codeTable.get(i) == code) {
pos = i;
break;
}
return false;
}
}
if (pos == -1) {
return false;
}
glyphShapeTable.remove(pos);
fontInfoTag.removeFontCharacter(pos);
shiftGlyphIndices(fontId, pos + 1, false);
setModified(true);
getSwf().clearImageCache();
return true;
}
@Override
public void setAdvanceValues(Font font) {
throw new UnsupportedOperationException("Setting the advance values for DefineFontTag is not supported.");
}
@Override
public int getCharacterCount() {
ensureFontInfo();
if (fontInfoTag != null) {
List<Integer> codeTable = fontInfoTag.getCodeTable();
return codeTable.size();
}
return 0;
}
@Override
public String getCharacters() {
ensureFontInfo();
if (fontInfoTag != null) {
List<Integer> codeTable = fontInfoTag.getCodeTable();
StringBuilder ret = new StringBuilder(codeTable.size());
for (int i : codeTable) {
ret.append((char) i);
}
return ret.toString();
}
return "";
}
@Override
public int getGlyphKerningAdjustment(int glyphIndex, int nextGlyphIndex) {
return 0;
}
@Override
public int getCharKerningAdjustment(char c1, char c2) {
return 0;
}
}
@@ -1,259 +1,261 @@
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
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.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.Conditional;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.annotations.Reserved;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
@SWFVersion(from = 3)
public class SoundStreamHead2Tag extends Tag implements SoundStreamHeadTypeTag {
public static final int ID = 45;
public static final String NAME = "SoundStreamHead2";
@Reserved
@SWFType(value = BasicType.UB, count = 4)
public int reserved;
@SWFType(value = BasicType.UB, count = 2)
public int playBackSoundRate;
public boolean playBackSoundSize;
public boolean playBackSoundType;
@SWFType(value = BasicType.UB, count = 4)
public int streamSoundCompression;
@SWFType(value = BasicType.UB, count = 2)
public int streamSoundRate;
public boolean streamSoundSize;
public boolean streamSoundType;
@SWFType(BasicType.UI16)
public int streamSoundSampleCount;
@SWFType(BasicType.SI16)
@Conditional(value = "streamSoundCompression", options = {2})
public int latencySeek;
@Internal
private int virtualCharacterId = 0;
/**
* Constructor
*
* @param swf
*/
public SoundStreamHead2Tag(SWF swf) {
super(swf, ID, NAME, null);
}
/**
* Constructor
*
* @param sis
* @param data
* @throws IOException
*/
public SoundStreamHead2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException {
super(sis.getSwf(), ID, NAME, data);
readData(sis, data, 0, false, false, false);
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
reserved = (int) sis.readUB(4, "reserved");
playBackSoundRate = (int) sis.readUB(2, "playBackSoundRate");
playBackSoundSize = sis.readUB(1, "playBackSoundSize") == 1;
playBackSoundType = sis.readUB(1, "playBackSoundType") == 1;
streamSoundCompression = (int) sis.readUB(4, "streamSoundCompression");
streamSoundRate = (int) sis.readUB(2, "streamSoundRate");
streamSoundSize = sis.readUB(1, "streamSoundSize") == 1;
streamSoundType = sis.readUB(1, "streamSoundType") == 1;
streamSoundSampleCount = sis.readUI16("streamSoundSampleCount");
if (streamSoundCompression == 2) {
latencySeek = sis.readSI16("latencySeek");
}
}
/**
* Gets data bytes
*
* @param sos SWF output stream
* @throws java.io.IOException
*/
@Override
public void getData(SWFOutputStream sos) throws IOException {
sos.writeUB(4, reserved);
sos.writeUB(2, playBackSoundRate);
sos.writeUB(1, playBackSoundSize ? 1 : 0);
sos.writeUB(1, playBackSoundType ? 1 : 0);
sos.writeUB(4, streamSoundCompression);
sos.writeUB(2, streamSoundRate);
sos.writeUB(1, streamSoundSize ? 1 : 0);
sos.writeUB(1, streamSoundType ? 1 : 0);
sos.writeUI16(streamSoundSampleCount);
if (streamSoundCompression == 2) {
sos.writeSI16(latencySeek);
}
}
@Override
public int getCharacterId() {
return virtualCharacterId;
}
@Override
public void setCharacterId(int characterId) {
virtualCharacterId = characterId;
}
@Override
public SoundExportFormat getExportFormat() {
if (streamSoundCompression == SoundFormat.FORMAT_MP3) {
return SoundExportFormat.MP3;
}
if (streamSoundCompression == SoundFormat.FORMAT_ADPCM) {
return SoundExportFormat.WAV;
}
if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) {
return SoundExportFormat.WAV;
}
if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN) {
return SoundExportFormat.WAV;
}
if (streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER16KHZ || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER8KHZ) {
return SoundExportFormat.WAV;
}
return SoundExportFormat.FLV;
}
@Override
public long getSoundSampleCount() {
return streamSoundSampleCount;
}
@Override
public void setVirtualCharacterId(int ch) {
virtualCharacterId = ch;
}
@Override
public int getSoundFormatId() {
return streamSoundCompression;
}
@Override
public int getSoundRate() {
return streamSoundRate;
}
@Override
public boolean getSoundSize() {
return streamSoundSize;
}
@Override
public boolean getSoundType() {
return streamSoundType;
}
@Override
public List<SoundStreamBlockTag> getBlocks() {
Timeline timeline = swf.getTimeline();
List<SoundStreamBlockTag> ret = timeline.getSoundStreamBlocks(this);
return ret;
}
@Override
public boolean importSupported() {
return false;
}
@Override
public boolean setSound(InputStream is, int newSoundFormat) {
return false;
}
@Override
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
List<SoundStreamBlockTag> blocks = getBlocks();
for (SoundStreamBlockTag block : blocks) {
ByteArrayRange data = block.streamSoundData;
if (streamSoundCompression == SoundFormat.FORMAT_MP3) {
ret.add(data.getSubRange(4, data.getLength() - 4));
} else {
ret.add(data);
}
}
return ret;
}
@Override
public long getTotalSoundSampleCount() {
return getBlocks().size() * streamSoundSampleCount;
}
@Override
public SoundFormat getSoundFormat() {
final int[] rateMap = {5512, 11025, 22050, 44100};
return new SoundFormat(getSoundFormatId(), rateMap[getSoundRate()], getSoundType());
}
@Override
public String getCharacterExportFileName() {
String exportName = swf.getExportName(getCharacterId());
return getCharacterId() + (exportName != null ? "_" + exportName : "");
}
@Override
public void getTagInfo(TagInfo tagInfo) {
super.getTagInfo(tagInfo);
SoundFormat soundFormat = getSoundFormat();
tagInfo.addInfo("general", "codecName", soundFormat.getFormatName());
tagInfo.addInfo("general", "exportFormat", soundFormat.getNativeExportFormat());
tagInfo.addInfo("general", "samplingRate", soundFormat.samplingRate);
tagInfo.addInfo("general", "stereo", soundFormat.stereo);
tagInfo.addInfo("general", "sampleCount", streamSoundSampleCount);
}
}
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
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.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.Conditional;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.annotations.Reserved;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
@SWFVersion(from = 3)
public class SoundStreamHead2Tag extends Tag implements SoundStreamHeadTypeTag {
public static final int ID = 45;
public static final String NAME = "SoundStreamHead2";
@Reserved
@SWFType(value = BasicType.UB, count = 4)
public int reserved;
@SWFType(value = BasicType.UB, count = 2)
public int playBackSoundRate;
public boolean playBackSoundSize;
public boolean playBackSoundType;
@SWFType(value = BasicType.UB, count = 4)
public int streamSoundCompression;
@SWFType(value = BasicType.UB, count = 2)
public int streamSoundRate;
public boolean streamSoundSize;
public boolean streamSoundType;
@SWFType(BasicType.UI16)
public int streamSoundSampleCount;
@SWFType(BasicType.SI16)
@Conditional(value = "streamSoundCompression", options = {2})
public int latencySeek;
@Internal
private int virtualCharacterId = 0;
/**
* Constructor
*
* @param swf
*/
public SoundStreamHead2Tag(SWF swf) {
super(swf, ID, NAME, null);
}
/**
* Constructor
*
* @param sis
* @param data
* @throws IOException
*/
public SoundStreamHead2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException {
super(sis.getSwf(), ID, NAME, data);
readData(sis, data, 0, false, false, false);
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
reserved = (int) sis.readUB(4, "reserved");
playBackSoundRate = (int) sis.readUB(2, "playBackSoundRate");
playBackSoundSize = sis.readUB(1, "playBackSoundSize") == 1;
playBackSoundType = sis.readUB(1, "playBackSoundType") == 1;
streamSoundCompression = (int) sis.readUB(4, "streamSoundCompression");
streamSoundRate = (int) sis.readUB(2, "streamSoundRate");
streamSoundSize = sis.readUB(1, "streamSoundSize") == 1;
streamSoundType = sis.readUB(1, "streamSoundType") == 1;
streamSoundSampleCount = sis.readUI16("streamSoundSampleCount");
if (streamSoundCompression == 2) {
latencySeek = sis.readSI16("latencySeek");
}
}
/**
* Gets data bytes
*
* @param sos SWF output stream
* @throws java.io.IOException
*/
@Override
public void getData(SWFOutputStream sos) throws IOException {
sos.writeUB(4, reserved);
sos.writeUB(2, playBackSoundRate);
sos.writeUB(1, playBackSoundSize ? 1 : 0);
sos.writeUB(1, playBackSoundType ? 1 : 0);
sos.writeUB(4, streamSoundCompression);
sos.writeUB(2, streamSoundRate);
sos.writeUB(1, streamSoundSize ? 1 : 0);
sos.writeUB(1, streamSoundType ? 1 : 0);
sos.writeUI16(streamSoundSampleCount);
if (streamSoundCompression == 2) {
sos.writeSI16(latencySeek);
}
}
@Override
public int getCharacterId() {
return virtualCharacterId;
}
@Override
public void setCharacterId(int characterId) {
virtualCharacterId = characterId;
}
@Override
public SoundExportFormat getExportFormat() {
if (streamSoundCompression == SoundFormat.FORMAT_MP3) {
return SoundExportFormat.MP3;
}
if (streamSoundCompression == SoundFormat.FORMAT_ADPCM) {
return SoundExportFormat.WAV;
}
if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) {
return SoundExportFormat.WAV;
}
if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN) {
return SoundExportFormat.WAV;
}
if (streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER16KHZ || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER8KHZ) {
return SoundExportFormat.WAV;
}
return SoundExportFormat.FLV;
}
@Override
public long getSoundSampleCount() {
return streamSoundSampleCount;
}
@Override
public void setVirtualCharacterId(int ch) {
virtualCharacterId = ch;
}
@Override
public int getSoundFormatId() {
return streamSoundCompression;
}
@Override
public int getSoundRate() {
return streamSoundRate;
}
@Override
public boolean getSoundSize() {
return streamSoundSize;
}
@Override
public boolean getSoundType() {
return streamSoundType;
}
@Override
public List<SoundStreamBlockTag> getBlocks() {
Timeline timeline = swf.getTimeline();
List<SoundStreamBlockTag> ret = timeline.getSoundStreamBlocks(this);
return ret;
}
@Override
public boolean importSupported() {
return false;
}
@Override
public boolean setSound(InputStream is, int newSoundFormat) {
return false;
}
@Override
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
List<SoundStreamBlockTag> blocks = getBlocks();
if (blocks != null) {
for (SoundStreamBlockTag block : blocks) {
ByteArrayRange data = block.streamSoundData;
if (streamSoundCompression == SoundFormat.FORMAT_MP3) {
ret.add(data.getSubRange(4, data.getLength() - 4));
} else {
ret.add(data);
}
}
}
return ret;
}
@Override
public long getTotalSoundSampleCount() {
return getBlocks().size() * streamSoundSampleCount;
}
@Override
public SoundFormat getSoundFormat() {
final int[] rateMap = {5512, 11025, 22050, 44100};
return new SoundFormat(getSoundFormatId(), rateMap[getSoundRate()], getSoundType());
}
@Override
public String getCharacterExportFileName() {
String exportName = swf.getExportName(getCharacterId());
return getCharacterId() + (exportName != null ? "_" + exportName : "");
}
@Override
public void getTagInfo(TagInfo tagInfo) {
super.getTagInfo(tagInfo);
SoundFormat soundFormat = getSoundFormat();
tagInfo.addInfo("general", "codecName", soundFormat.getFormatName());
tagInfo.addInfo("general", "exportFormat", soundFormat.getNativeExportFormat());
tagInfo.addInfo("general", "samplingRate", soundFormat.samplingRate);
tagInfo.addInfo("general", "stereo", soundFormat.stereo);
tagInfo.addInfo("general", "sampleCount", streamSoundSampleCount);
}
}
@@ -1,62 +1,64 @@
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.helpers.ByteArrayRange;
import java.util.List;
/**
*
* @author JPEXS
*/
public abstract class FontInfoTag extends Tag implements CharacterIdTag {
@SWFType(BasicType.UI16)
public int fontID;
public FontInfoTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract List<Integer> getCodeTable();
public abstract void addCharacter(int index, int character);
@Override
public int getCharacterId() {
return fontID;
}
@Override
public void setCharacterId(int characterId) {
this.fontID = characterId;
}
public abstract String getFontName();
public abstract boolean getFontFlagsBold();
public abstract void setFontFlagsBold(boolean value);
public abstract boolean getFontFlagsItalic();
public abstract void setFontFlagsItalic(boolean value);
}
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.helpers.ByteArrayRange;
import java.util.List;
/**
*
* @author JPEXS
*/
public abstract class FontInfoTag extends Tag implements CharacterIdTag {
@SWFType(BasicType.UI16)
public int fontID;
public FontInfoTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract List<Integer> getCodeTable();
public abstract void addFontCharacter(int index, int character);
public abstract void removeFontCharacter(int index);
@Override
public int getCharacterId() {
return fontID;
}
@Override
public void setCharacterId(int characterId) {
this.fontID = characterId;
}
public abstract String getFontName();
public abstract boolean getFontFlagsBold();
public abstract void setFontFlagsBold(boolean value);
public abstract boolean getFontFlagsItalic();
public abstract void setFontFlagsItalic(boolean value);
}
@@ -1,411 +1,417 @@
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.helpers.FontHelper;
import com.jpexs.decompiler.flash.tags.DefineFontNameTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.SerializableImage;
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphVector;
import java.awt.geom.Area;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @author JPEXS
*/
public abstract class FontTag extends DrawableTag implements AloneTag {
public static final int PREVIEWSIZE = 500;
public FontTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract List<SHAPE> getGlyphShapeTable();
public abstract void addCharacter(char character, Font font);
public abstract void setAdvanceValues(Font font);
public abstract char glyphToChar(int glyphIndex);
public abstract int charToGlyph(char c);
public abstract double getGlyphAdvance(int glyphIndex);
public abstract int getGlyphKerningAdjustment(int glyphIndex, int nextGlyphIndex);
public abstract int getCharKerningAdjustment(char c1, char c2);
public abstract int getGlyphWidth(int glyphIndex);
public abstract String getFontNameIntag();
public abstract boolean isSmall();
public abstract boolean isBold();
public abstract boolean isItalic();
public abstract boolean isSmallEditable();
public abstract boolean isBoldEditable();
public abstract boolean isItalicEditable();
public abstract void setSmall(boolean value);
public abstract void setBold(boolean value);
public abstract void setItalic(boolean value);
public abstract double getDivider();
public abstract int getAscent();
public abstract int getDescent();
public abstract int getLeading();
public String getFontName() {
DefineFontNameTag fontNameTag = getFontNameTag();
if (fontNameTag == null) {
return getFontNameIntag();
}
return fontNameTag.fontName;
}
public String getFontCopyright() {
DefineFontNameTag fontNameTag = getFontNameTag();
if (fontNameTag == null) {
return "";
}
return fontNameTag.fontCopyright;
}
public static Map<String, Map<String, Font>> installedFontsByFamily;
public static Map<String, Font> installedFontsByName;
public static String defaultFontName;
static {
reload();
}
public int getFontId() {
return getCharacterId();
}
public boolean hasLayout() {
return false;
}
public boolean containsChar(char character) {
return charToGlyph(character) > -1;
}
public int getFontStyle() {
int fontStyle = 0;
if (isBold()) {
fontStyle |= Font.BOLD;
}
if (isItalic()) {
fontStyle |= Font.ITALIC;
}
return fontStyle;
}
public abstract int getCharacterCount();
public abstract String getCharacters();
@Override
public String getName() {
String nameAppend = "";
if (exportName != null) {
nameAppend = ": " + exportName;
}
if (className != null) {
nameAppend = ": " + className;
}
String fontName = getFontNameIntag();
if (fontName != null) {
nameAppend = ": " + fontName;
}
return tagName + " (" + getCharacterId() + nameAppend + ")";
}
@Override
public String getExportFileName() {
String result = super.getExportFileName();
String fontName = getFontNameIntag();
if (fontName != null) {
fontName = fontName.replace(" ", "_");
}
return result + (fontName != null ? "_" + fontName : "");
}
public String getSystemFontName() {
int fontId = getFontId();
String selectedFont = swf.sourceFontNamesMap.get(fontId);
if (selectedFont == null) {
SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(swf.getShortFileName());
String key = fontId + "_" + getFontNameIntag();
if (swfConf != null) {
selectedFont = swfConf.fontPairingMap.get(key);
}
}
if (selectedFont == null) {
selectedFont = Configuration.getFontToNameMap().get(getFontNameIntag());
}
if (selectedFont != null && FontTag.installedFontsByName.containsKey(selectedFont)) {
return selectedFont;
}
// findInstalledFontName always returns an available font name
return FontTag.findInstalledFontName(getFontName());
}
public Font getSystemFont() {
return FontTag.installedFontsByName.get(getSystemFontName());
}
protected void shiftGlyphIndices(int fontId, int startIndex) {
for (Tag t : swf.getTags()) {
List<TEXTRECORD> textRecords = null;
if (t instanceof StaticTextTag) {
textRecords = ((StaticTextTag) t).textRecords;
}
if (textRecords != null) {
int curFontId = 0;
for (TEXTRECORD tr : textRecords) {
if (tr.styleFlagsHasFont) {
curFontId = tr.fontId;
}
if (curFontId != fontId) {
continue;
}
for (GLYPHENTRY en : tr.glyphEntries) {
if (en == null) { //Currently edited
continue;
}
if (en.glyphIndex >= startIndex) {
en.glyphIndex++;
}
}
t.setModified(true);
}
}
}
}
public static float getSystemFontAdvance(String fontName, int fontStyle, int fontSize, Character character, Character nextCharacter) {
return getSystemFontAdvance(new Font(fontName, fontStyle, fontSize), character, nextCharacter);
}
public static float getSystemFontAdvance(Font aFont, Character character, Character nextCharacter) {
String chars = "" + character + (nextCharacter == null ? "" : nextCharacter);
GlyphVector gv = aFont.layoutGlyphVector(new FontRenderContext(aFont.getTransform(), true, true), chars.toCharArray(), 0, chars.length(), Font.LAYOUT_LEFT_TO_RIGHT);
GlyphMetrics gm = gv.getGlyphMetrics(0);
return gm.getAdvanceX();
}
public static void reload() {
installedFontsByFamily = FontHelper.getInstalledFonts();
installedFontsByName = new HashMap<>();
for (String fam : installedFontsByFamily.keySet()) {
for (String nam : installedFontsByFamily.get(fam).keySet()) {
installedFontsByName.put(nam, installedFontsByFamily.get(fam).get(nam));
}
}
if (installedFontsByFamily.containsKey("Times New Roman")) {
defaultFontName = "Times New Roman";
} else if (installedFontsByFamily.containsKey("Arial")) {
defaultFontName = "Arial";
} else {
defaultFontName = installedFontsByFamily.keySet().iterator().next();
}
}
public static String getFontNameWithFallback(String fontName) {
if (installedFontsByFamily.containsKey(fontName)) {
return fontName;
}
if (installedFontsByFamily.containsKey("Times New Roman")) {
return "Times New Roman";
}
if (installedFontsByFamily.containsKey("Arial")) {
return "Arial";
}
//First font
return installedFontsByFamily.keySet().iterator().next();
}
public static String isFontFamilyInstalled(String fontFamily) {
if (installedFontsByFamily.containsKey(fontFamily)) {
return fontFamily;
}
if (fontFamily.contains("_")) {
String beforeUnderscore = fontFamily.substring(0, fontFamily.indexOf('_'));
if (installedFontsByFamily.containsKey(beforeUnderscore)) {
return beforeUnderscore;
}
}
return null;
}
public static String findInstalledFontName(String fontName) {
if (installedFontsByName.containsKey(fontName)) {
return fontName;
}
if (fontName != null && fontName.contains("_")) {
String beforeUnderscore = fontName.substring(0, fontName.indexOf('_'));
if (installedFontsByName.containsKey(beforeUnderscore)) {
return beforeUnderscore;
}
}
return defaultFontName;
}
@Override
public int getUsedParameters() {
return PARAMETER_FRAME;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
RECT r = getRect();
return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()));
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
SHAPERECORD.shapeListToImage(swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) {
}
@Override
public void toHtmlCanvas(StringBuilder result, double unitDivisor) {
List<SHAPE> shapes = getGlyphShapeTable();
result.append("\tdefaultFill = textColor;\r\n");
result.append("\tswitch(ch){\r\n");
for (int i = 0; i < shapes.size(); i++) {
char c = glyphToChar(i);
String cs = "" + c;
cs = cs.replace("\\", "\\\\").replace("\"", "\\\"");
result.append("\t\tcase \"").append(cs).append("\":\r\n");
CanvasShapeExporter exporter = new CanvasShapeExporter(null, unitDivisor, swf, shapes.get(i), null, 0, 0);
exporter.export();
result.append("\t\t").append(exporter.getShapeData().replaceAll("\r\n", "\r\n\t\t"));
result.append("\tbreak;\r\n");
}
result.append("\t}\r\n");
}
@Override
public int getNumFrames() {
int frameCount = (getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1;
if (frameCount < 1) {
frameCount = 1;
}
return frameCount;
}
@Override
public boolean isSingleFrame() {
return true;
}
@Override
public RECT getRect() {
return getRect(null); // parameter not used
}
@Override
public RECT getRect(Set<BoundedTag> added) {
return new RECT(0, (int) (PREVIEWSIZE * SWF.unitDivisor), 0, (int) (PREVIEWSIZE * SWF.unitDivisor));
}
@Override
public String getCharacterExportFileName() {
return super.getCharacterExportFileName() + "_" + getFontNameIntag();
}
public DefineFontNameTag getFontNameTag() {
for (Tag t : swf.getTags()) {
if (t instanceof DefineFontNameTag) {
DefineFontNameTag dfn = (DefineFontNameTag) t;
if (dfn.fontId == getFontId()) {
return dfn;
}
}
}
return null;
}
public String getCopyright() {
DefineFontNameTag dfn = getFontNameTag();
if (dfn == null) {
return null;
}
return dfn.fontCopyright;
}
public RECT getGlyphBounds(int glyphIndex) {
return getGlyphShapeTable().get(glyphIndex).getBounds();
}
public FontTag toClassicFont() {
return this;
}
}
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.helpers.FontHelper;
import com.jpexs.decompiler.flash.tags.DefineFontNameTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.SerializableImage;
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphVector;
import java.awt.geom.Area;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @author JPEXS
*/
public abstract class FontTag extends DrawableTag implements AloneTag {
public static final int PREVIEWSIZE = 500;
public FontTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract List<SHAPE> getGlyphShapeTable();
public abstract void addCharacter(char character, Font font);
public abstract boolean removeCharacter(char character);
public abstract void setAdvanceValues(Font font);
public abstract char glyphToChar(int glyphIndex);
public abstract int charToGlyph(char c);
public abstract double getGlyphAdvance(int glyphIndex);
public abstract int getGlyphKerningAdjustment(int glyphIndex, int nextGlyphIndex);
public abstract int getCharKerningAdjustment(char c1, char c2);
public abstract int getGlyphWidth(int glyphIndex);
public abstract String getFontNameIntag();
public abstract boolean isSmall();
public abstract boolean isBold();
public abstract boolean isItalic();
public abstract boolean isSmallEditable();
public abstract boolean isBoldEditable();
public abstract boolean isItalicEditable();
public abstract void setSmall(boolean value);
public abstract void setBold(boolean value);
public abstract void setItalic(boolean value);
public abstract double getDivider();
public abstract int getAscent();
public abstract int getDescent();
public abstract int getLeading();
public String getFontName() {
DefineFontNameTag fontNameTag = getFontNameTag();
if (fontNameTag == null) {
return getFontNameIntag();
}
return fontNameTag.fontName;
}
public String getFontCopyright() {
DefineFontNameTag fontNameTag = getFontNameTag();
if (fontNameTag == null) {
return "";
}
return fontNameTag.fontCopyright;
}
public static Map<String, Map<String, Font>> installedFontsByFamily;
public static Map<String, Font> installedFontsByName;
public static String defaultFontName;
static {
reload();
}
public int getFontId() {
return getCharacterId();
}
public boolean hasLayout() {
return false;
}
public boolean containsChar(char character) {
return charToGlyph(character) > -1;
}
public int getFontStyle() {
int fontStyle = 0;
if (isBold()) {
fontStyle |= Font.BOLD;
}
if (isItalic()) {
fontStyle |= Font.ITALIC;
}
return fontStyle;
}
public abstract int getCharacterCount();
public abstract String getCharacters();
@Override
public String getName() {
String nameAppend = "";
if (exportName != null) {
nameAppend = ": " + exportName;
}
if (className != null) {
nameAppend = ": " + className;
}
String fontName = getFontNameIntag();
if (fontName != null) {
nameAppend = ": " + fontName;
}
return tagName + " (" + getCharacterId() + nameAppend + ")";
}
@Override
public String getExportFileName() {
String result = super.getExportFileName();
String fontName = getFontNameIntag();
if (fontName != null) {
fontName = fontName.replace(" ", "_");
}
return result + (fontName != null ? "_" + fontName : "");
}
public String getSystemFontName() {
int fontId = getFontId();
String selectedFont = swf.sourceFontNamesMap.get(fontId);
if (selectedFont == null) {
SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(swf.getShortFileName());
String key = fontId + "_" + getFontNameIntag();
if (swfConf != null) {
selectedFont = swfConf.fontPairingMap.get(key);
}
}
if (selectedFont == null) {
selectedFont = Configuration.getFontToNameMap().get(getFontNameIntag());
}
if (selectedFont != null && FontTag.installedFontsByName.containsKey(selectedFont)) {
return selectedFont;
}
// findInstalledFontName always returns an available font name
return FontTag.findInstalledFontName(getFontName());
}
public Font getSystemFont() {
return FontTag.installedFontsByName.get(getSystemFontName());
}
protected void shiftGlyphIndices(int fontId, int startIndex, boolean increment) {
for (Tag t : swf.getTags()) {
List<TEXTRECORD> textRecords = null;
if (t instanceof StaticTextTag) {
textRecords = ((StaticTextTag) t).textRecords;
}
if (textRecords != null) {
int curFontId = 0;
for (TEXTRECORD tr : textRecords) {
if (tr.styleFlagsHasFont) {
curFontId = tr.fontId;
}
if (curFontId != fontId) {
continue;
}
for (GLYPHENTRY en : tr.glyphEntries) {
if (en == null) { //Currently edited
continue;
}
if (en.glyphIndex >= startIndex) {
if (increment) {
en.glyphIndex++;
} else {
en.glyphIndex--;
}
}
}
t.setModified(true);
}
}
}
}
public static float getSystemFontAdvance(String fontName, int fontStyle, int fontSize, Character character, Character nextCharacter) {
return getSystemFontAdvance(new Font(fontName, fontStyle, fontSize), character, nextCharacter);
}
public static float getSystemFontAdvance(Font aFont, Character character, Character nextCharacter) {
String chars = "" + character + (nextCharacter == null ? "" : nextCharacter);
GlyphVector gv = aFont.layoutGlyphVector(new FontRenderContext(aFont.getTransform(), true, true), chars.toCharArray(), 0, chars.length(), Font.LAYOUT_LEFT_TO_RIGHT);
GlyphMetrics gm = gv.getGlyphMetrics(0);
return gm.getAdvanceX();
}
public static void reload() {
installedFontsByFamily = FontHelper.getInstalledFonts();
installedFontsByName = new HashMap<>();
for (String fam : installedFontsByFamily.keySet()) {
for (String nam : installedFontsByFamily.get(fam).keySet()) {
installedFontsByName.put(nam, installedFontsByFamily.get(fam).get(nam));
}
}
if (installedFontsByFamily.containsKey("Times New Roman")) {
defaultFontName = "Times New Roman";
} else if (installedFontsByFamily.containsKey("Arial")) {
defaultFontName = "Arial";
} else {
defaultFontName = installedFontsByFamily.keySet().iterator().next();
}
}
public static String getFontNameWithFallback(String fontName) {
if (installedFontsByFamily.containsKey(fontName)) {
return fontName;
}
if (installedFontsByFamily.containsKey("Times New Roman")) {
return "Times New Roman";
}
if (installedFontsByFamily.containsKey("Arial")) {
return "Arial";
}
//First font
return installedFontsByFamily.keySet().iterator().next();
}
public static String isFontFamilyInstalled(String fontFamily) {
if (installedFontsByFamily.containsKey(fontFamily)) {
return fontFamily;
}
if (fontFamily.contains("_")) {
String beforeUnderscore = fontFamily.substring(0, fontFamily.indexOf('_'));
if (installedFontsByFamily.containsKey(beforeUnderscore)) {
return beforeUnderscore;
}
}
return null;
}
public static String findInstalledFontName(String fontName) {
if (installedFontsByName.containsKey(fontName)) {
return fontName;
}
if (fontName != null && fontName.contains("_")) {
String beforeUnderscore = fontName.substring(0, fontName.indexOf('_'));
if (installedFontsByName.containsKey(beforeUnderscore)) {
return beforeUnderscore;
}
}
return defaultFontName;
}
@Override
public int getUsedParameters() {
return PARAMETER_FRAME;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
RECT r = getRect();
return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()));
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
SHAPERECORD.shapeListToImage(swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) {
}
@Override
public void toHtmlCanvas(StringBuilder result, double unitDivisor) {
List<SHAPE> shapes = getGlyphShapeTable();
result.append("\tdefaultFill = textColor;\r\n");
result.append("\tswitch(ch){\r\n");
for (int i = 0; i < shapes.size(); i++) {
char c = glyphToChar(i);
String cs = "" + c;
cs = cs.replace("\\", "\\\\").replace("\"", "\\\"");
result.append("\t\tcase \"").append(cs).append("\":\r\n");
CanvasShapeExporter exporter = new CanvasShapeExporter(null, unitDivisor, swf, shapes.get(i), null, 0, 0);
exporter.export();
result.append("\t\t").append(exporter.getShapeData().replaceAll("\r\n", "\r\n\t\t"));
result.append("\tbreak;\r\n");
}
result.append("\t}\r\n");
}
@Override
public int getNumFrames() {
int frameCount = (getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1;
if (frameCount < 1) {
frameCount = 1;
}
return frameCount;
}
@Override
public boolean isSingleFrame() {
return true;
}
@Override
public RECT getRect() {
return getRect(null); // parameter not used
}
@Override
public RECT getRect(Set<BoundedTag> added) {
return new RECT(0, (int) (PREVIEWSIZE * SWF.unitDivisor), 0, (int) (PREVIEWSIZE * SWF.unitDivisor));
}
@Override
public String getCharacterExportFileName() {
return super.getCharacterExportFileName() + "_" + getFontNameIntag();
}
public DefineFontNameTag getFontNameTag() {
for (Tag t : swf.getTags()) {
if (t instanceof DefineFontNameTag) {
DefineFontNameTag dfn = (DefineFontNameTag) t;
if (dfn.fontId == getFontId()) {
return dfn;
}
}
}
return null;
}
public String getCopyright() {
DefineFontNameTag dfn = getFontNameTag();
if (dfn == null) {
return null;
}
return dfn.fontCopyright;
}
public RECT getGlyphBounds(int glyphIndex) {
return getGlyphShapeTable().get(glyphIndex).getBounds();
}
public FontTag toClassicFont() {
return this;
}
}
@@ -179,7 +179,7 @@ public final class DefineCompactedFont extends FontTag {
}
if (!exists) {
shiftGlyphIndices(fontId, pos);
shiftGlyphIndices(fontId, pos, true);
}
Font fnt = cfont.deriveFont(fontStyle, Math.round(font.nominalSize * d));
@@ -198,6 +198,37 @@ public final class DefineCompactedFont extends FontTag {
getSwf().clearImageCache();
}
@Override
public boolean removeCharacter(char character) {
FontType font = fonts.get(0);
int code = (int) character;
int pos = -1;
for (int i = 0; i < font.glyphInfo.size(); i++) {
if (font.glyphInfo.get(i).glyphCode >= code) {
if (font.glyphInfo.get(i).glyphCode == code) {
pos = i;
break;
}
return false;
}
}
if (pos == -1) {
return false;
}
font.glyphInfo.remove(pos);
font.glyphs.remove(pos);
shapeCache.remove(pos);
shiftGlyphIndices(fontId, pos + 1, false);
setModified(true);
getSwf().clearImageCache();
return true;
}
@Override
public void setAdvanceValues(Font font) {
throw new UnsupportedOperationException("Setting the advance values for DefineCompactedFont is not supported.");