无法分析ttf glyf表

ncecgwcz  于 2021-08-25  发布在  Java
关注(0)|答案(0)|浏览(217)

我的任务是解析一个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;
    }
}

}

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题