Antecer 2 лет назад
Родитель
Сommit
3562a13bac
1 измененных файлов с 309 добавлено и 261 удалено
  1. 309 261
      app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java

+ 309 - 261
app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java

@@ -1,17 +1,21 @@
 package io.legado.app.model.analyzeRule;
 
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.commons.lang3.tuple.Triple;
+import android.util.Log;
 
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.HashMap;
+import java.util.Map;
+import java.nio.charset.StandardCharsets;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
-@SuppressWarnings({"FieldCanBeLocal", "unused"})
+@SuppressWarnings({"FieldCanBeLocal", "StatementWithEmptyBody", "unused"})
 public class QueryTTF {
     private static class Header {
         public int majorVersion;
@@ -129,7 +133,7 @@ public class QueryTTF {
         public int length;
         public int language;
         public int numGroups;
-        public List<Triple<Integer, Integer, Integer>> groups;
+        public List<int[]> groups;
     }
 
     private static class GlyfLayout {
@@ -147,103 +151,313 @@ public class QueryTTF {
     }
 
     private static class ByteArrayReader {
-        public int index;
-        public byte[] buffer;
+        private final byte[] buffer;
+        public final ByteBuffer byteBuffer;
 
         public ByteArrayReader(byte[] buffer, int index) {
             this.buffer = buffer;
-            this.index = index;
+            this.byteBuffer = ByteBuffer.wrap(buffer);
+            this.byteBuffer.order(ByteOrder.BIG_ENDIAN); // 设置为大端模式
+            this.byteBuffer.position(index); // 设置起始索引
         }
 
-        public long ReadUIntX(long len) {
-            long result = 0;
-            for (long i = 0; i < len; ++i) {
-                result <<= 8;
-                result |= buffer[index++] & 0xFF;
-            }
-            return result;
+        public void position(int index) {
+            byteBuffer.position(index); // 设置起始索引
+        }
+
+        public int position() {
+            return byteBuffer.position();
         }
 
+
         public long ReadUInt64() {
-            return ReadUIntX(8);
+            return byteBuffer.getLong();
         }
 
         public int ReadUInt32() {
-            return (int) ReadUIntX(4);
+            return byteBuffer.getInt();
+        }
+
+        public int ReadInt32() {
+            return byteBuffer.getInt();
         }
 
         public int ReadUInt16() {
-            return (int) ReadUIntX(2);
+            return byteBuffer.getShort() & 0xFFFF;
         }
 
         public short ReadInt16() {
-            return (short) ReadUIntX(2);
+            return byteBuffer.getShort();
         }
 
         public short ReadUInt8() {
-            return (short) ReadUIntX(1);
-        }
-
-
-        public String ReadStrings(int len, Charset charset) {
-            byte[] result = len > 0 ? new byte[len] : null;
-            for (int i = 0; i < len; ++i) result[i] = buffer[index++];
-            return new String(result, charset);
+            return (short) (byteBuffer.get() & 0xFF);
         }
 
-        public byte GetByte() {
-            return buffer[index++];
+        public byte ReadInt8() {
+            return byteBuffer.get();
         }
 
-        public byte[] GetBytes(int len) {
-            byte[] result = len > 0 ? new byte[len] : null;
-            for (int i = 0; i < len; ++i) result[i] = buffer[index++];
+        public byte[] ReadByteArray(int len) {
+            if (len < 0) throw new IllegalArgumentException("Length must not be negative");
+            byte[] result = new byte[len];
+            byteBuffer.get(result);
             return result;
         }
 
-        public int[] GetUInt16Array(int len) {
-            int[] result = len > 0 ? new int[len] : null;
-            for (int i = 0; i < len; ++i) result[i] = ReadUInt16();
+        public short[] ReadInt16Array(int len) {
+            if (len < 0) throw new IllegalArgumentException("Length must not be negative");
+            var result = new short[len];
+            for (int i = 0; i < len; ++i) result[i] = byteBuffer.getShort();
             return result;
         }
 
-        public short[] GetInt16Array(int len) {
-            short[] result = len > 0 ? new short[len] : null;
-            for (int i = 0; i < len; ++i) result[i] = ReadInt16();
+        public int[] ReadUInt16Array(int len) {
+            if (len < 0) throw new IllegalArgumentException("Length must not be negative");
+            var result = new int[len];
+            for (int i = 0; i < len; ++i) result[i] = byteBuffer.getShort() & 0xFFFF;
             return result;
         }
     }
 
-    private final ByteArrayReader fontReader;
     private final Header fileHeader = new Header();
-    private final List<Directory> directorys = new LinkedList<>();
+    private final Map<String, Directory> directorys = new HashMap<>();
     private final NameLayout name = new NameLayout();
     private final HeadLayout head = new HeadLayout();
     private final MaxpLayout maxp = new MaxpLayout();
     private final List<Integer> loca = new LinkedList<>();
     private final CmapLayout Cmap = new CmapLayout();
-    private final List<String> glyf = new LinkedList<>();
-    @SuppressWarnings("unchecked")
-    private final Pair<Integer, Integer>[] pps = new Pair[]{
-            Pair.of(3, 10),
-            Pair.of(0, 4),
-            Pair.of(3, 1),
-            Pair.of(1, 0),
-            Pair.of(0, 3),
-            Pair.of(0, 1)
+    private final ConcurrentHashMap<Integer, String> glyf = new ConcurrentHashMap<>();
+    private final int[][] pps = new int[][]{
+            {3, 10},
+            {0, 4},
+            {3, 1},
+            {1, 0},
+            {0, 3},
+            {0, 1}
     };
 
     public final Map<Integer, String> unicodeToGlyph = new HashMap<>();
     public final Map<String, Integer> glyphToUnicode = new HashMap<>();
     public final Map<Integer, Integer> unicodeToGlyphIndex = new HashMap<>();
 
+    private void readNameTable(byte[] buffer) {
+        var dataTable = Objects.requireNonNull(directorys.get("name"));
+        var reader = new ByteArrayReader(buffer, dataTable.offset);
+        name.format = reader.ReadUInt16();
+        name.count = reader.ReadUInt16();
+        name.stringOffset = reader.ReadUInt16();
+        for (int i = 0; i < name.count; ++i) {
+            NameRecord record = new NameRecord();
+            record.platformID = reader.ReadUInt16();
+            record.encodingID = reader.ReadUInt16();
+            record.languageID = reader.ReadUInt16();
+            record.nameID = reader.ReadUInt16();
+            record.length = reader.ReadUInt16();
+            record.offset = reader.ReadUInt16();
+            name.records.add(record);
+        }
+    }
+
+    private void readHeadTable(byte[] buffer) {
+        var dataTable = Objects.requireNonNull(directorys.get("head"));
+        var reader = new ByteArrayReader(buffer, dataTable.offset);
+        head.majorVersion = reader.ReadUInt16();
+        head.minorVersion = reader.ReadUInt16();
+        head.fontRevision = reader.ReadUInt32();
+        head.checkSumAdjustment = reader.ReadUInt32();
+        head.magicNumber = reader.ReadUInt32();
+        head.flags = reader.ReadUInt16();
+        head.unitsPerEm = reader.ReadUInt16();
+        head.created = reader.ReadUInt64();
+        head.modified = reader.ReadUInt64();
+        head.xMin = reader.ReadInt16();
+        head.yMin = reader.ReadInt16();
+        head.xMax = reader.ReadInt16();
+        head.yMax = reader.ReadInt16();
+        head.macStyle = reader.ReadUInt16();
+        head.lowestRecPPEM = reader.ReadUInt16();
+        head.fontDirectionHint = reader.ReadInt16();
+        head.indexToLocFormat = reader.ReadInt16();
+        head.glyphDataFormat = reader.ReadInt16();
+    }
+
+    private void readCmapTable(byte[] buffer) {
+        var dataTable = Objects.requireNonNull(directorys.get("cmap"));
+        var reader = new ByteArrayReader(buffer, dataTable.offset);
+        Cmap.version = reader.ReadUInt16();
+        Cmap.numTables = reader.ReadUInt16();
+        for (int i = 0; i < Cmap.numTables; ++i) {
+            CmapRecord record = new CmapRecord();
+            record.platformID = reader.ReadUInt16();
+            record.encodingID = reader.ReadUInt16();
+            record.offset = reader.ReadUInt32();
+            Cmap.records.add(record);
+        }
+        for (int i = 0; i < Cmap.numTables; ++i) {
+            int fmtOffset = Cmap.records.get(i).offset;
+            reader.position(dataTable.offset + fmtOffset);
+            int EndIndex = reader.position();
+
+            int format = reader.ReadUInt16();
+            if (Cmap.tables.containsKey(fmtOffset)) continue;
+            if (format == 0) {
+                CmapFormat f = new CmapFormat();
+                f.format = format;
+                f.length = reader.ReadUInt16();
+                f.language = reader.ReadUInt16();
+                f.glyphIdArray = reader.ReadByteArray(f.length - 6);
+                Cmap.tables.put(fmtOffset, f);
+            } else if (format == 4) {
+                CmapFormat4 f = new CmapFormat4();
+                f.format = format;
+                f.length = reader.ReadUInt16();
+                f.language = reader.ReadUInt16();
+                f.segCountX2 = reader.ReadUInt16();
+                int segCount = f.segCountX2 >> 1;
+                f.searchRange = reader.ReadUInt16();
+                f.entrySelector = reader.ReadUInt16();
+                f.rangeShift = reader.ReadUInt16();
+                f.endCode = reader.ReadUInt16Array(segCount);
+                f.reservedPad = reader.ReadUInt16();
+                f.startCode = reader.ReadUInt16Array(segCount);
+                f.idDelta = reader.ReadInt16Array(segCount);
+                f.idRangeOffset = reader.ReadUInt16Array(segCount);
+                f.glyphIdArray = reader.ReadUInt16Array((EndIndex + f.length - reader.position()) >> 1);
+                Cmap.tables.put(fmtOffset, f);
+            } else if (format == 6) {
+                CmapFormat6 f = new CmapFormat6();
+                f.format = format;
+                f.length = reader.ReadUInt16();
+                f.language = reader.ReadUInt16();
+                f.firstCode = reader.ReadUInt16();
+                f.entryCount = reader.ReadUInt16();
+                f.glyphIdArray = reader.ReadUInt16Array(f.entryCount);
+                Cmap.tables.put(fmtOffset, f);
+            } else if (format == 12) {
+                CmapFormat12 f = new CmapFormat12();
+                f.format = format;
+                f.reserved = reader.ReadUInt16();
+                f.length = reader.ReadUInt32();
+                f.language = reader.ReadUInt32();
+                f.numGroups = reader.ReadUInt32();
+                f.groups = new LinkedList<>();
+                for (int n = 0; n < f.numGroups; ++n) {
+                    f.groups.add(new int[]{reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32()});
+                }
+                Cmap.tables.put(fmtOffset, f);
+            }
+        }
+    }
+
+    private void readLocaTable(byte[] buffer) {
+        var dataTable = Objects.requireNonNull(directorys.get("loca"));
+        var reader = new ByteArrayReader(buffer, dataTable.offset);
+        var locaTableSize = dataTable.length;
+        if (head.indexToLocFormat == 0) {
+            for (long i = 0; i < locaTableSize; i += 2) loca.add(reader.ReadUInt16());
+        } else {
+            for (long i = 0; i < locaTableSize; i += 4) loca.add(reader.ReadUInt32());
+        }
+    }
+
+    private void readMaxpTable(byte[] buffer) {
+        var dataTable = Objects.requireNonNull(directorys.get("maxp"));
+        var reader = new ByteArrayReader(buffer, dataTable.offset);
+        maxp.majorVersion = reader.ReadUInt16();
+        maxp.minorVersion = reader.ReadUInt16();
+        maxp.numGlyphs = reader.ReadUInt16();
+        maxp.maxPoints = reader.ReadUInt16();
+        maxp.maxContours = reader.ReadUInt16();
+        maxp.maxCompositePoints = reader.ReadUInt16();
+        maxp.maxCompositeContours = reader.ReadUInt16();
+        maxp.maxZones = reader.ReadUInt16();
+        maxp.maxTwilightPoints = reader.ReadUInt16();
+        maxp.maxStorage = reader.ReadUInt16();
+        maxp.maxFunctionDefs = reader.ReadUInt16();
+        maxp.maxInstructionDefs = reader.ReadUInt16();
+        maxp.maxStackElements = reader.ReadUInt16();
+        maxp.maxSizeOfInstructions = reader.ReadUInt16();
+        maxp.maxComponentElements = reader.ReadUInt16();
+        maxp.maxComponentDepth = reader.ReadUInt16();
+    }
+
+    private void readGlyfTable(byte[] buffer) {
+        var dataTable = Objects.requireNonNull(directorys.get("glyf"));
+        int glyfCount = maxp.numGlyphs;
+
+        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+        for (int i = 0; i < glyfCount; i++) {
+            final int index = i;
+            executor.submit(() -> {
+                var reader = new ByteArrayReader(buffer, dataTable.offset + loca.get(index));
+                int glyfNextIndex = index + 1 < glyfCount ? (dataTable.offset + loca.get(index + 1)) : (dataTable.offset + dataTable.length);
+                byte[] glyph;
+                short numberOfContours = reader.ReadInt16();
+                if (numberOfContours > 0) {
+                    short g_xMin = reader.ReadInt16();
+                    short g_yMin = reader.ReadInt16();
+                    short g_xMax = reader.ReadInt16();
+                    short g_yMax = reader.ReadInt16();
+                    int[] endPtsOfContours = reader.ReadUInt16Array(numberOfContours);
+                    int instructionLength = reader.ReadUInt16();
+                    var instructions = reader.ReadByteArray(instructionLength);
+                    int flagLength = endPtsOfContours[endPtsOfContours.length - 1] + 1;
+                    // 获取轮廓点描述标志
+                    var flags = new byte[flagLength];
+                    for (int n = 0; n < flagLength; ++n) {
+                        flags[n] = reader.ReadInt8();
+                        if ((flags[n] & 0x08) != 0x00) {
+                            for (int m = reader.ReadUInt8(); m > 0; --m) {
+                                flags[++n] = flags[n - 1];
+                            }
+                        }
+                    }
+                    // 获取轮廓点描述x,y相对值
+                    ByteBuffer xyCoordinatesBuffer = ByteBuffer.allocate(flagLength * 4);
+                    // 获取轮廓点描述x轴相对值
+                    for (int n = 0; n < flagLength; ++n) {
+                        short same = (short) ((flags[n] & 0x10) != 0 ? 1 : -1);
+                        if ((flags[n] & 0x02) != 0) {
+                            xyCoordinatesBuffer.putShort((short) (same * reader.ReadUInt8()));
+                        } else {
+                            xyCoordinatesBuffer.putShort(same == 1 ? (short) 0 : reader.ReadInt16());
+                        }
+                    }
+                    // 获取轮廓点描述y轴相对值
+                    for (int n = 0; n < flagLength; ++n) {
+                        short same = (short) ((flags[n] & 0x20) != 0 ? 1 : -1);
+                        if ((flags[n] & 0x04) != 0) {
+                            xyCoordinatesBuffer.putShort((short) (same * reader.ReadUInt8()));
+                        } else {
+                            xyCoordinatesBuffer.putShort(same == 1 ? (short) 0 : reader.ReadInt16());
+                        }
+                    }
+                    // 保存轮廓点描述x,y相对值ByteArray
+                    glyph = xyCoordinatesBuffer.array();
+                } else {
+                    // 复合字体未做详细处理
+                    glyph = reader.ReadByteArray(glyfNextIndex - (reader.position() - 2));
+                }
+                glyf.put(index, getHexFromBytes(glyph));
+            });
+        }
+        executor.shutdown();
+        try {
+            boolean b = executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            Log.e("queryTTF", "glyf表解析出错: " + e);
+        }
+    }
+
     /**
      * 构造函数
      *
      * @param buffer 传入TTF字体二进制数组
      */
     public QueryTTF(byte[] buffer) {
-        fontReader = new ByteArrayReader(buffer, 0);
+        var fontReader = new ByteArrayReader(buffer, 0);
+        Log.i("[queryTTF]", "读文件头");
         // 获取文件头
         fileHeader.majorVersion = fontReader.ReadUInt16();
         fileHeader.minorVersion = fontReader.ReadUInt16();
@@ -254,187 +468,29 @@ public class QueryTTF {
         // 获取目录
         for (int i = 0; i < fileHeader.numOfTables; ++i) {
             Directory d = new Directory();
-            d.tag = fontReader.ReadStrings(4, StandardCharsets.US_ASCII);
+            d.tag = new String(fontReader.ReadByteArray(4), StandardCharsets.US_ASCII);
             d.checkSum = fontReader.ReadUInt32();
             d.offset = fontReader.ReadUInt32();
             d.length = fontReader.ReadUInt32();
-            directorys.add(d);
-        }
-        // 解析表 name (字体信息,包含版权、名称、作者等...)
-        for (Directory Temp : directorys) {
-            if (Temp.tag.equals("name")) {
-                fontReader.index = Temp.offset;
-                name.format = fontReader.ReadUInt16();
-                name.count = fontReader.ReadUInt16();
-                name.stringOffset = fontReader.ReadUInt16();
-                for (int i = 0; i < name.count; ++i) {
-                    NameRecord record = new NameRecord();
-                    record.platformID = fontReader.ReadUInt16();
-                    record.encodingID = fontReader.ReadUInt16();
-                    record.languageID = fontReader.ReadUInt16();
-                    record.nameID = fontReader.ReadUInt16();
-                    record.length = fontReader.ReadUInt16();
-                    record.offset = fontReader.ReadUInt16();
-                    name.records.add(record);
-                }
-            }
-        }
-        // 解析表 head (获取 head.indexToLocFormat)
-        for (Directory Temp : directorys) {
-            if (Temp.tag.equals("head")) {
-                fontReader.index = Temp.offset;
-                head.majorVersion = fontReader.ReadUInt16();
-                head.minorVersion = fontReader.ReadUInt16();
-                head.fontRevision = fontReader.ReadUInt32();
-                head.checkSumAdjustment = fontReader.ReadUInt32();
-                head.magicNumber = fontReader.ReadUInt32();
-                head.flags = fontReader.ReadUInt16();
-                head.unitsPerEm = fontReader.ReadUInt16();
-                head.created = fontReader.ReadUInt64();
-                head.modified = fontReader.ReadUInt64();
-                head.xMin = fontReader.ReadInt16();
-                head.yMin = fontReader.ReadInt16();
-                head.xMax = fontReader.ReadInt16();
-                head.yMax = fontReader.ReadInt16();
-                head.macStyle = fontReader.ReadUInt16();
-                head.lowestRecPPEM = fontReader.ReadUInt16();
-                head.fontDirectionHint = fontReader.ReadInt16();
-                head.indexToLocFormat = fontReader.ReadInt16();
-                head.glyphDataFormat = fontReader.ReadInt16();
-            }
-        }
-        // 解析表 maxp (获取 maxp.numGlyphs)
-        for (Directory Temp : directorys) {
-            if (Temp.tag.equals("maxp")) {
-                fontReader.index = Temp.offset;
-                maxp.majorVersion = fontReader.ReadUInt16();
-                maxp.minorVersion = fontReader.ReadUInt16();
-                maxp.numGlyphs = fontReader.ReadUInt16();
-                maxp.maxPoints = fontReader.ReadUInt16();
-                maxp.maxContours = fontReader.ReadUInt16();
-                maxp.maxCompositePoints = fontReader.ReadUInt16();
-                maxp.maxCompositeContours = fontReader.ReadUInt16();
-                maxp.maxZones = fontReader.ReadUInt16();
-                maxp.maxTwilightPoints = fontReader.ReadUInt16();
-                maxp.maxStorage = fontReader.ReadUInt16();
-                maxp.maxFunctionDefs = fontReader.ReadUInt16();
-                maxp.maxInstructionDefs = fontReader.ReadUInt16();
-                maxp.maxStackElements = fontReader.ReadUInt16();
-                maxp.maxSizeOfInstructions = fontReader.ReadUInt16();
-                maxp.maxComponentElements = fontReader.ReadUInt16();
-                maxp.maxComponentDepth = fontReader.ReadUInt16();
-            }
-        }
-        // 解析表 loca (轮廓数据偏移地址表)
-        for (Directory Temp : directorys) {
-            if (Temp.tag.equals("loca")) {
-                fontReader.index = Temp.offset;
-                int offset = head.indexToLocFormat == 0 ? 2 : 4;
-                for (long i = 0; i < Temp.length; i += offset) {
-                    loca.add(offset == 2 ? fontReader.ReadUInt16() << 1 : fontReader.ReadUInt32());
-                }
-            }
-        }
-        // 解析表 cmap (Unicode编码轮廓索引对照表)
-        for (Directory Temp : directorys) {
-            if (Temp.tag.equals("cmap")) {
-                fontReader.index = Temp.offset;
-                Cmap.version = fontReader.ReadUInt16();
-                Cmap.numTables = fontReader.ReadUInt16();
-
-                for (int i = 0; i < Cmap.numTables; ++i) {
-                    CmapRecord record = new CmapRecord();
-                    record.platformID = fontReader.ReadUInt16();
-                    record.encodingID = fontReader.ReadUInt16();
-                    record.offset = fontReader.ReadUInt32();
-                    Cmap.records.add(record);
-                }
-                for (int i = 0; i < Cmap.numTables; ++i) {
-                    int fmtOffset = Cmap.records.get(i).offset;
-                    fontReader.index = Temp.offset + fmtOffset;
-                    int EndIndex = fontReader.index;
-
-                    int format = fontReader.ReadUInt16();
-                    if (Cmap.tables.containsKey(fmtOffset)) continue;
-                    if (format == 0) {
-                        CmapFormat f = new CmapFormat();
-                        f.format = format;
-                        f.length = fontReader.ReadUInt16();
-                        f.language = fontReader.ReadUInt16();
-                        f.glyphIdArray = fontReader.GetBytes(f.length - 6);
-                        Cmap.tables.put(fmtOffset, f);
-                    } else if (format == 4) {
-                        CmapFormat4 f = new CmapFormat4();
-                        f.format = format;
-                        f.length = fontReader.ReadUInt16();
-                        f.language = fontReader.ReadUInt16();
-                        f.segCountX2 = fontReader.ReadUInt16();
-                        int segCount = f.segCountX2 >> 1;
-                        f.searchRange = fontReader.ReadUInt16();
-                        f.entrySelector = fontReader.ReadUInt16();
-                        f.rangeShift = fontReader.ReadUInt16();
-                        f.endCode = fontReader.GetUInt16Array(segCount);
-                        f.reservedPad = fontReader.ReadUInt16();
-                        f.startCode = fontReader.GetUInt16Array(segCount);
-                        f.idDelta = fontReader.GetInt16Array(segCount);
-                        f.idRangeOffset = fontReader.GetUInt16Array(segCount);
-                        f.glyphIdArray = fontReader.GetUInt16Array((EndIndex + f.length - fontReader.index) >> 1);
-                        Cmap.tables.put(fmtOffset, f);
-                    } else if (format == 6) {
-                        CmapFormat6 f = new CmapFormat6();
-                        f.format = format;
-                        f.length = fontReader.ReadUInt16();
-                        f.language = fontReader.ReadUInt16();
-                        f.firstCode = fontReader.ReadUInt16();
-                        f.entryCount = fontReader.ReadUInt16();
-                        f.glyphIdArray = fontReader.GetUInt16Array(f.entryCount);
-                        Cmap.tables.put(fmtOffset, f);
-                    } else if (format == 12) {
-                        CmapFormat12 f = new CmapFormat12();
-                        f.format = format;
-                        f.reserved = fontReader.ReadUInt16();
-                        f.length = fontReader.ReadUInt32();
-                        f.language = fontReader.ReadUInt32();
-                        f.numGroups = fontReader.ReadUInt32();
-                        f.groups = new ArrayList<>(f.numGroups);
-                        for (int n = 0; n < f.numGroups; ++n) {
-                            f.groups.add(Triple.of(fontReader.ReadUInt32(), fontReader.ReadUInt32(), fontReader.ReadUInt32()));
-                        }
-                        Cmap.tables.put(fmtOffset, f);
-                    }
-                }
-            }
-        }
-        // 读取表 glyf (字体轮廓数据表)
-        for (Directory Temp : directorys) {
-            if (Temp.tag.equals("glyf")) {
-                int glyfCount = maxp.numGlyphs;
-                for (int i = 0; i < glyfCount; ) {
-                    fontReader.index = Temp.offset + loca.get(i);
-                    ++i;
-                    int glyfNextIndex = i < glyfCount ? loca.get(i) : Temp.length;
-
-                    byte[] glyph;
-                    short numberOfContours = fontReader.ReadInt16();
-                    if (numberOfContours > 0) {
-                        short g_xMin = fontReader.ReadInt16();
-                        short g_yMin = fontReader.ReadInt16();
-                        short g_xMax = fontReader.ReadInt16();
-                        short g_yMax = fontReader.ReadInt16();
-                        int[] endPtsOfContours = fontReader.GetUInt16Array(numberOfContours);
-                        glyph = fontReader.GetBytes(glyfNextIndex - (fontReader.index - Temp.offset));
-                    } else {
-                        glyph = fontReader.GetBytes(glyfNextIndex - (fontReader.index - 2));
-                    }
-                    glyf.add(getHexFromBytes(glyph));
-                }
-            }
+            directorys.put(d.tag, d);
         }
 
-        // 建立Unicode&Glyph双向表
+        Log.i("[queryTTF]", "解析表 name"); // 字体信息,包含版权、名称、作者等...
+        readNameTable(buffer);
+        Log.i("[queryTTF]", "解析表 head"); // 获取 head.indexToLocFormat
+        readHeadTable(buffer);
+        Log.i("[queryTTF]", "解析表 cmap"); // Unicode编码->轮廓索引 对照表
+        readCmapTable(buffer);
+        Log.i("[queryTTF]", "解析表 loca"); // 轮廓数据偏移地址表
+        readLocaTable(buffer);
+        Log.i("[queryTTF]", "解析表 maxp"); // 获取 maxp.numGlyphs 字体轮廓数量
+        readMaxpTable(buffer);
+        Log.i("[queryTTF]", "解析表 glyf"); // 字体轮廓数据表,需要解析loca,maxp表后计算
+        readGlyfTable(buffer);
+
+        Log.i("[queryTTF]", "创建Unicode&Glyph映射表");
         for (int key = 0; key < 130000; ++key) {
-//            if (key == 0xFF) key = 0x3400;
-            int gid = queryGlyfIndex(key);
+            Integer gid = queryGlyfIndex(key);
             if (gid >= glyf.size()) continue;
             unicodeToGlyphIndex.put(key, gid);
             var val = glyf.get(gid);
@@ -442,27 +498,24 @@ public class QueryTTF {
             if (glyphToUnicode.containsKey(val)) continue;
             glyphToUnicode.put(val, key);
         }
+        Log.i("[queryTTF]", "字体处理完成");
     }
 
-    /**
-     * 获取字体信息 (1=字体名称)
-     *
-     * @param nameId 传入十进制字体信息索引
-     * @return 返回查询结果字符串
-     */
-    public String getNameById(int nameId) {
-        for (Directory Temp : directorys) {
-            if (!Temp.tag.equals("name")) continue;
-            fontReader.index = Temp.offset;
-            break;
-        }
-        for (NameRecord record : name.records) {
-            if (record.nameID != nameId) continue;
-            fontReader.index += name.stringOffset + record.offset;
-            return fontReader.ReadStrings(record.length, record.platformID == 1 ? StandardCharsets.UTF_8 : StandardCharsets.UTF_16BE);
-        }
-        return "error";
-    }
+//    /**
+//     * 获取字体信息 (1=字体名称)
+//     *
+//     * @param nameId 传入十进制字体信息索引
+//     * @return 返回查询结果字符串
+//     */
+//    public String getNameById(int nameId) {
+//        fontReader.index = Objects.requireNonNull(directorys.get("name")).offset;
+//        for (NameRecord record : name.records) {
+//            if (record.nameID != nameId) continue;
+//            fontReader.index += name.stringOffset + record.offset;
+//            return fontReader.ReadStrings(record.length, record.platformID == 1 ? StandardCharsets.UTF_8 : StandardCharsets.UTF_16BE);
+//        }
+//        return "error";
+//    }
 
     /**
      * 使用Unicode值查找轮廓索引
@@ -472,10 +525,11 @@ public class QueryTTF {
      */
     public int queryGlyfIndex(int unicode) {
         if (unicode == 0) return 0;
+
         int fmtKey = 0;
-        for (Pair<Integer, Integer> item : pps) {
+        for (var item : pps) {
             for (CmapRecord record : Cmap.records) {
-                if ((item.getLeft() == record.platformID) && (item.getRight() == record.encodingID)) {
+                if ((item[0] == record.platformID) && (item[1] == record.encodingID)) {
                     fmtKey = record.offset;
                     break;
                 }
@@ -512,10 +566,10 @@ public class QueryTTF {
             if (0 <= index && index < tab.glyphIdArray.length) glyfID = tab.glyphIdArray[index];
         } else if (fmt == 12) {
             CmapFormat12 tab = (CmapFormat12) table;
-            if (unicode > tab.groups.get(tab.numGroups - 1).getMiddle()) return 0;
+            if (unicode > tab.groups.get(tab.numGroups - 1)[1]) return 0;
             for (int i = 0; i < tab.numGroups; i++) {
-                if (tab.groups.get(i).getLeft() <= unicode && unicode <= tab.groups.get(i).getMiddle()) {
-                    glyfID = tab.groups.get(i).getRight() + unicode - tab.groups.get(i).getLeft();
+                if (tab.groups.get(i)[0] <= unicode && unicode <= tab.groups.get(i)[1]) {
+                    glyfID = tab.groups.get(i)[2] + unicode - tab.groups.get(i)[0];
                     break;
                 }
             }
@@ -564,14 +618,8 @@ public class QueryTTF {
      */
     public String getHexFromBytes(byte[] glyph) {
         if (glyph == null) return "";
-        StringBuilder sb = new StringBuilder();
-        for (byte b : glyph) {
-            String hex = Integer.toHexString(b);
-            if (hex.length() == 1) {
-                sb.append("0");//当16进制为个位数时,在前面补0
-            }
-            sb.append(hex);//将16进制加入字符串
-        }
-        return sb.toString().toUpperCase();
+        StringBuilder hexString = new StringBuilder();
+        for (byte b : glyph) hexString.append(String.format("%02X", b));
+        return hexString.toString();
     }
 }