我的任务是解析一个ttf文件以获得字形轮廓信息。我写了下面的代码来读取glyf表,但我就是不知道它出了什么问题。理论上,这应该是正确的。唯一有点奇怪的是,我用来从标志中提取一个位的每一行都有不同的结果,也许我只是没有从标志中读取正确的字节,但我尝试了太多,到那时我真的应该选择正确的字节了。microsoft文档是否仍然是最新的,或者glyf表是否在某种程度上发生了更改?如果有人想知道我犯了什么错误:实际上是几个不同的错误:
首先,如果我没有插入一行来检查数组中的当前位置是否为0,并且在解析坐标时设置了“复制最后一个坐标”的标志,这意味着当前x/y坐标应该与最后一个坐标相同,那么我会得到一个数组越界异常,即使这是阵列中的第一个坐标。
如果我检查它是否是数组中的第一个,如果是,则将值设置为0,然后将所有点绘制到屏幕上,其中任何点都绝对没有顺序或模式。
最后一个问题是,数组大小似乎有问题,因为其中一些数组太长,它们也会读取其他数组的一部分,而另一些数组太短,无法读取整个glyf数据
这是我的代码:
private static class GlyfTable {
public Glyf[] glyfs;
public GlyfTable(ByteFile file, TableDirectory directory, LocaTable loca, MaxpTable maxp) {
file.setPosition(directory.tableRecords.get("glyf").offset);
glyfs = new Glyf[maxp.numGlyphs];
for(int i = 0; i < maxp.numGlyphs; i++) {
file.setPosition(directory.tableRecords.get("glyf").offset + loca.offsets[i]);
glyfs[i] = new Glyf();
glyfs[i].numberOfContours = file.getInt16();
glyfs[i].xMin = file.getInt16();
glyfs[i].yMin = file.getInt16();
glyfs[i].xMax = file.getInt16();
glyfs[i].yMax = file.getInt16();
if(glyfs[i].numberOfContours >= 0) {
glyfs[i].endPtsOfContours = new int[glyfs[i].numberOfContours];
for(int g = 0; g < glyfs[i].numberOfContours; g++) {
glyfs[i].endPtsOfContours[g] = file.getUint16();
}
glyfs[i].instructionLength = file.getUint16();
glyfs[i].instructions = new int[glyfs[i].instructionLength];
for(int c = 0; c < glyfs[i].instructionLength; c++) {
glyfs[i].instructions[c] = file.getUint8();
}
int pointCount = glyfs[i].endPtsOfContours[glyfs[i].endPtsOfContours.length - 1] + 1;
glyfs[i].flags = new byte[pointCount];
for(int f = 0; f < pointCount; f++) {
byte flag = file.getByte();
glyfs[i].flags[f] = flag;
if((flag & (1 << 3)) != 0) {
int repeat = file.getUint8();
for(int r = 0; r < repeat; r++) {
f++;
glyfs[i].flags[f] = flag;
}
}
}
glyfs[i].xCoordinates = new int[pointCount];
for(int c = 0; c < pointCount; c++) {
if((glyfs[i].flags[c] & (1 << 1)) != 0) {
if((glyfs[i].flags[c] & (1 << 4)) != 0) {
glyfs[i].xCoordinates[c] = file.getUint8();
}
else {
glyfs[i].xCoordinates[c] = file.getUint8() * -1;
}
}
else {
if((glyfs[i].flags[c] & (1 << 4)) != 0) {
if(c > 0) {
glyfs[i].xCoordinates[c] = glyfs[i].xCoordinates[c - 1];
}
else {
glyfs[i].xCoordinates[c] = 0;
}
}
else {
glyfs[i].xCoordinates[c] = file.getInt16();
}
}
}
glyfs[i].yCoordinates = new int[pointCount];
for(int c = 0; c < pointCount; c++) {
if((glyfs[i].flags[c] & (1 << 2)) != 0) {
if((glyfs[i].flags[c] & (1 << 5)) != 0) {
glyfs[i].yCoordinates[c] = file.getUint8();
}
else {
glyfs[i].yCoordinates[c] = file.getUint8() * -1;
}
}
else {
if((glyfs[i].flags[c] & (1 << 5)) != 0) {
if(c == 0) {
glyfs[i].yCoordinates[c] = 0;
}
else {
glyfs[i].yCoordinates[c] = glyfs[i].yCoordinates[c - 1];
}
}
else {
glyfs[i].yCoordinates[c] = file.getInt16();
}
}
}
}
}
}
static class Glyf {
public int numberOfContours;
public int xMin;
public int yMin;
public int xMax;
public int yMax;
public int[] endPtsOfContours;
int instructionLength;
int[] instructions;
byte[] flags;
int[] xCoordinates;
int[] yCoordinates;
}
}
}
编辑:这是类的全部代码
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.LinkedHashMap;
public class Font_MK2 {
public static void main(String[] args) throws IOException {
TTFFile ttf = new TTFFile("path to font");
}
private static class TTFFile {
ByteFile file;
private TableDirectory directory;
private CmapTable cmap;
private MaxpTable maxp;
private LocaTable loca;
private HeadTable head;
private GlyfTable glyf;
public TTFFile(String path) throws IOException {
file = new ByteFile(path);
directory = new TableDirectory(file);
cmap = new CmapTable(file,directory);
maxp = new MaxpTable(file,directory);
head = new HeadTable(file,directory);
loca = new LocaTable(file, directory, maxp, head);
glyf = new GlyfTable(file, directory, loca, maxp);
}
private static class TableDirectory {
public long sfntVersion;
public int numTables;
public int searchRange;
public int entrySelector;
public int rangeShift;
public LinkedHashMap<String,TableRecord> tableRecords;
public TableDirectory(ByteFile file) {
sfntVersion = file.getUint32();
numTables = file.getUint16();
searchRange = file.getUint16();
entrySelector = file.getUint16();
rangeShift = file.getUint16();
tableRecords = new LinkedHashMap<>();
for(int i = 0; i < numTables; i++) {
String tag = file.getTag();
tableRecords.put(tag,new TableRecord(tag,
file.getUint32(),
file.getOffset32(),
file.getUint32()));
}
}
private static class TableRecord {
public TableRecord(String tag, long check, long off, long len) {
tableTag = tag;checksum = check; offset = off; length = len;
}
public String tableTag;
public long checksum;
public long offset;
public long length;
}
}
private static class CmapTable {
public int version;
public int numTables;
public encodingRecord[] encodingRecords;
public int[] CharacterCodes;
public CmapTable(ByteFile file, TableDirectory directory) {
file.setPosition(directory.tableRecords.get("cmap").offset);
version = file.getUint16();
numTables = file.getUint16();
encodingRecords = new encodingRecord[numTables];
long OffsetToUse = 0;
long encodingToUse = 0;
for(int i = 0; i < numTables; i++) {
encodingRecords[i] = new encodingRecord(file.getUint16(), file.getUint16(), file.getOffset32());
if(encodingRecords[i].platformID == 0 && encodingRecords[i].encodingID == 3) {
OffsetToUse = encodingRecords[i].subtableOffset;
encodingToUse = encodingRecords[i].encodingID;
}
}
if(encodingToUse != 3) {
try {
throw new Exception("Encoding not recognized");
} catch (Exception e) {
e.printStackTrace();
}
}
file.setPosition(directory.tableRecords.get("head").offset + OffsetToUse);
if(file.getUint16() == 0) {
int subtableLength = file.getUint16();
int language = file.getUint16();
CharacterCodes = new int[subtableLength];
for(int i = 0; i < subtableLength; i++) {
CharacterCodes[i] = file.getUint8();
}
}
else {
try {
throw new Exception("Format not recognized");
} catch (Exception e) {
e.printStackTrace();
}
}
}
static class encodingRecord {
public int platformID;
public int encodingID;
public long subtableOffset;
public encodingRecord(int platformID, int encodingID, long subtableOffset) {
this.platformID = platformID; this.encodingID = encodingID; this.subtableOffset = subtableOffset;
}
}
}
private static class MaxpTable{
public int version;
public int numGlyphs;
public int maxPoints;
public int maxContours;
public int maxCompositePoints;
public int maxCompositeContours;
public int maxZones;
public int maxTwilightPoints;
public int maxStorage;
public int maxFunctionDefs;
public int maxInstructionDefs;
public int maxStackElements;
public int maxSizeOfInstructions;
public int maxComponentElements;
public int maxComponentDepth;
public MaxpTable(ByteFile file, TableDirectory directory) {
file.setPosition(directory.tableRecords.get("maxp").offset);
version = file.getVersion()[0];
if(version == 1) {
numGlyphs = file.getUint16();
maxPoints = file.getUint16();
maxContours = file.getUint16();
maxCompositePoints = file.getUint16();
maxCompositeContours = file.getUint16();
maxZones = file.getUint16();
maxTwilightPoints = file.getUint16();
maxStorage = file.getUint16();
maxFunctionDefs = file.getUint16();
maxInstructionDefs = file.getUint16();
maxStackElements = file.getUint16();
maxSizeOfInstructions = file.getUint16();
maxComponentElements = file.getUint16();
maxComponentDepth = file.getUint16();
}
else {
try {
throw new Exception("unknown Version");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static class HeadTable {
public int majorVersion;
public int minorVersion;
public float fontRevision;
public long checksumAdjustment;
public long magicNumber;
public int flags;
public int unitsPerEm;
public long created;
public long modified;
public int xMin;
public int yMin;
public int xMax;
public int yMax;
public int macStyle;
public int lowestRecPPEM;
public int fontDirectionHint;
public int indexToLocFormat;
public int glyphDataFormat;
public HeadTable(ByteFile file, TableDirectory directory) {
file.setPosition(directory.tableRecords.get("head").offset);
majorVersion = file.getUint16();
minorVersion = file.getUint16();
fontRevision = file.getFixed();
checksumAdjustment = file.getUint32();
magicNumber = file.getUint32();
flags = file.getUint16();
unitsPerEm = file.getUint16();
created = file.getDate();
modified = file.getDate();
xMin = file.getInt16();
yMin = file.getInt16();
xMax = file.getInt16();
yMax = file.getInt16();
macStyle = file.getUint16();
lowestRecPPEM = file.getUint16();
fontDirectionHint = file.getInt16();
indexToLocFormat = file.getInt16();
glyphDataFormat = file.getInt16();
}
}
private static class LocaTable {
long[] offsets;
public LocaTable(ByteFile file, TableDirectory directory, MaxpTable maxp, HeadTable head) {
file.setPosition(directory.tableRecords.get("loca").offset);
offsets = new long[maxp.numGlyphs + 1];
if(head.indexToLocFormat == 0) {
for(int i = 0; i < maxp.numGlyphs + 1; i++) {
offsets[i] = file.getOffset16() * 2l;
}
}
else if(head.indexToLocFormat == 1) {
for(int i = 0; i < maxp.numGlyphs + 1; i++) {
offsets[i] = file.getOffset32();
}
}
}
}
private static class GlyfTable {
public Glyf[] glyfs;
public GlyfTable(ByteFile file, TableDirectory directory, LocaTable loca, MaxpTable maxp) {
file.setPosition(directory.tableRecords.get("glyf").offset);
glyfs = new Glyf[maxp.numGlyphs];
for(int i = 0; i < maxp.numGlyphs; i++) {
file.setPosition(directory.tableRecords.get("glyf").offset + loca.offsets[i]);
glyfs[i] = new Glyf();
glyfs[i].numberOfContours = file.getInt16();
glyfs[i].xMin = file.getInt16();
glyfs[i].yMin = file.getInt16();
glyfs[i].xMax = file.getInt16();
glyfs[i].yMax = file.getInt16();
if(glyfs[i].numberOfContours >= 0) {
glyfs[i].endPtsOfContours = new int[glyfs[i].numberOfContours];
for(int g = 0; g < glyfs[i].numberOfContours; g++) {
glyfs[i].endPtsOfContours[g] = file.getUint16();
}
glyfs[i].instructionLength = file.getUint16();
glyfs[i].instructions = new int[glyfs[i].instructionLength];
for(int c = 0; c < glyfs[i].instructionLength; c++) {
glyfs[i].instructions[c] = file.getUint8();
}
int pointCount = glyfs[i].endPtsOfContours[glyfs[i].endPtsOfContours.length - 1] + 1;
glyfs[i].flags = new byte[pointCount];
for(int f = 0; f < pointCount; f++) {
byte flag = file.getByte();
glyfs[i].flags[f] = flag;
if((flag & (1 << 3)) != 0) {
int repeat = file.getUint8();
for(int r = 0; r < repeat; r++) {
f++;
glyfs[i].flags[f] = flag;
}
}
}
glyfs[i].xCoordinates = new int[pointCount];
for(int c = 0; c < pointCount; c++) {
if((glyfs[i].flags[c] & (1 << 1)) != 0) {
if((glyfs[i].flags[c] & (1 << 4)) != 0) {
glyfs[i].xCoordinates[c] = file.getUint8();
}
else {
glyfs[i].xCoordinates[c] = file.getUint8() * -1;
}
}
else {
if((glyfs[i].flags[c] & (1 << 4)) != 0) {
if(c > 0) {
glyfs[i].xCoordinates[c] = glyfs[i].xCoordinates[c - 1];
}
else {
glyfs[i].xCoordinates[c] = 0;
}
}
else {
glyfs[i].xCoordinates[c] = file.getInt16();
}
}
}
glyfs[i].yCoordinates = new int[pointCount];
for(int c = 0; c < pointCount; c++) {
if((glyfs[i].flags[c] & (1 << 2)) != 0) {
if((glyfs[i].flags[c] & (1 << 5)) != 0) {
glyfs[i].yCoordinates[c] = file.getUint8();
}
else {
glyfs[i].yCoordinates[c] = file.getUint8() * -1;
}
}
else {
if((glyfs[i].flags[c] & (1 << 5)) != 0) {
if(c == 0) {
glyfs[i].yCoordinates[c] = 0;
}
else {
glyfs[i].yCoordinates[c] = glyfs[i].yCoordinates[c - 1];
}
}
else {
glyfs[i].yCoordinates[c] = file.getInt16();
}
}
}
}
}
}
public String toBitString(final byte val) {
return String.format("%8s", Integer.toBinaryString(val & 0xFF))
.replace(' ', '0');
}
static class Glyf {
public int numberOfContours;
public int xMin;
public int yMin;
public int xMax;
public int yMax;
public int[] endPtsOfContours;
int instructionLength;
int[] instructions;
byte[] flags;
int[] xCoordinates;
int[] yCoordinates;
}
}
}
private static class ByteFile {
private byte[] file;
private long position;
public ByteFile(String path) throws IOException {
file = Files.readAllBytes(new File(path).toPath());
}
byte getByte() {
return file[(int)position++];
}
public int getUint8() {
byte[] b = new byte[] {0,0,0,getByte()};
long l = 0;
l |= b[0] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[3] & 0xFF;
return (int)l;
}
public int getInt8() {
return getByte();
}
public boolean[] getBit8() {
String s1 = String.format("%8s", Integer.toBinaryString(getByte() & 0xFF)).replace(' ', '0');
return new boolean[] {s1.charAt(0) == '1',s1.charAt(1) == '1',s1.charAt(2) == '1',s1.charAt(3) == '1',s1.charAt(4) == '1',s1.charAt(5) == '1',s1.charAt(6) == '1',s1.charAt(7) == '1'};
}
public int getUint16() {
byte[] b = new byte[] {0,0,getByte(),getByte()};
long l = 0;
l |= b[0] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[3] & 0xFF;
return (int)l;
}
public int getInt16() {
byte[] b = new byte[] {0,0,getByte(),getByte()};
long l = 0;
l |= b[0] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[3] & 0xFF;
return l;
}
public long getUint24() {
byte[] b = new byte[] {0,getByte(),getByte(),getByte()};
long l = 0;
l |= b[0] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[3] & 0xFF;
return (int)l;
}
public long getUint32() {
byte[] b = new byte[] {getByte(),getByte(),getByte(),getByte()};
long l = 0;
l |= b[0] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[3] & 0xFF;
return l;
}
public int getInt32() {
return (getByte() << 24) | (getByte() << 16) | (getByte() << 8) | getByte();
}
public float getFixed() {
int s = getInt32();
return Float.parseFloat((s << 16) + "." + ((0 << 16)));
}
public int getFword() {
return getInt16();
}
public int getUFWord() {
return getUint16();
}
public float getF2DOT14() {
int r = getInt16();
return Float.parseFloat((0 << 14 | r) + "." + (r << 14) + "f");
}
public long getDate() {
return (getUint32() << 32) | getUint32();
}
public String getTag() {
return new String(new char[] {(char)getByte(), (char)getByte(), (char)getByte(), (char)getByte()});
}
public int getOffset16() {
return getUint16();
}
public long getOffset32() {
return getUint32();
}
public int[] getVersion() {
return new int[] {getInt16(), getInt16()};
}
public long getPosition() {
return position;
}
public void setPosition(long Position) {
position = Position;
}
}
}
暂无答案!
目前还没有任何答案,快来回答吧!