0 表示短偏移 (Offset16),1 表示长偏移 (Offset32)。
+ */
+ public short indexToLocFormat;
+ /**
+ * int16
+ */
public short glyphDataFormat;
}
+ /**
+ * Maximum Profile
+ */
private static class MaxpLayout {
- public int majorVersion;
- public int minorVersion;
- public int numGlyphs; // 字体中的字形数量
+ /**
+ * uint32 高16位表示整数,低16位表示小数
+ */
+ public int version;
+ /**
+ * uint16 字体中的字形数量
+ */
+ public int numGlyphs;
+ /**
+ * uint16 非复合字形中包含的最大点数。点是构成字形轮廓的基本单位。
+ */
public int maxPoints;
+ /**
+ * uint16 非复合字形中包含的最大轮廓数。轮廓是由一系列点连接形成的封闭曲线。
+ */
public int maxContours;
+ /**
+ * uint16 复合字形中包含的最大点数。复合字形是由多个简单字形组合而成的。
+ */
public int maxCompositePoints;
+ /**
+ * uint16 复合字形中包含的最大轮廓数。
+ */
public int maxCompositeContours;
+ /**
+ * uint16
+ */
public int maxZones;
+ /**
+ * uint16
+ */
public int maxTwilightPoints;
+ /**
+ * uint16
+ */
public int maxStorage;
+ /**
+ * uint16
+ */
public int maxFunctionDefs;
+ /**
+ * uint16
+ */
public int maxInstructionDefs;
+ /**
+ * uint16
+ */
public int maxStackElements;
+ /**
+ * uint16
+ */
public int maxSizeOfInstructions;
+ /**
+ * uint16 任何复合字形在“顶层”引用的最大组件数。
+ */
public int maxComponentElements;
+ /**
+ * uint16 递归的最大层数;简单组件为1。
+ */
public int maxComponentDepth;
}
+ /**
+ * 字符到字形索引映射表
+ */
private static class CmapLayout {
+ /**
+ * uint16
+ */
public int version;
+ /**
+ * uint16 后面的编码表的数量
+ */
public int numTables;
- public List 0、Unicode
+ * 1、Macintosh
+ * 2、ISO
+ * 3、Windows
+ * 4、Custom
+ */
+ public int platformID;
+ /**
+ * uint16 Platform-specific encoding ID.
+ * platform ID = 3
+ * 0、Symbol
+ * 1、Unicode BMP
+ * 2、ShiftJIS
+ * 3、PRC
+ * 4、Big5
+ * 5、Wansung
+ * 6、Johab
+ * 7、Reserved
+ * 8、Reserved
+ * 9、Reserved
+ * 10、Unicode full repertoire
+ */
+ public int encodingID;
+ /**
+ * uint32 从 cmap 表开头到子表的字节偏移量
+ */
+ public int offset;
}
private static class CmapFormat {
+ /**
+ * uint16
+ * cmapFormat 子表的格式类型
+ */
public int format;
+ /**
+ * uint16
+ * 这个 Format 表的长度(以字节为单位)
+ */
public int length;
+ /**
+ * uint16
+ * 仅 platformID=1 时有效
+ */
public int language;
- public byte[] glyphIdArray;
- }
-
- private static class CmapFormat4 extends CmapFormat {
+ /**
+ * uint16[256]
+ * 仅 Format=2
+ * 将高字节映射到 subHeaders 的数组:值为 subHeader 索引x8
+ */
+ public int[] subHeaderKeys;
+ /**
+ * uint16[]
+ * 仅 Format=2
+ * subHeader 子标头的可变长度数组
+ * 其结构为 uint16[][4]{ {uint16,uint16,int16,uint16}, ... }
+ */
+ public int[] subHeaders;
+ /**
+ * uint16 segCount x2
+ * 仅 Format=4
+ * seg段计数乘以 2。这是因为每个段用两个字节表示,所以这个值是实际段数的两倍。
+ */
public int segCountX2;
+ /**
+ * uint16
+ * 仅 Format=4
+ * 小于或等于段数的最大二次幂,再乘以 2。这是为二分查找优化搜索过程。
+ */
public int searchRange;
+ /**
+ * uint16
+ * 仅 Format=4
+ * 等于 log2(searchRange/2),这是最大二次幂的对数。
+ */
public int entrySelector;
+ /**
+ * uint16
+ * 仅 Format=4
+ * segCount * 2 - searchRange 用于调整搜索范围的偏移。
+ */
public int rangeShift;
+ /**
+ * uint16[segCount]
+ * 仅 Format=4
+ * 每个段的结束字符码,最后一个是 0xFFFF,表示 Unicode 范围的结束。
+ */
public int[] endCode;
+ /**
+ * uint16
+ * 仅 Format=4
+ * 固定设置为 0,用于填充保留位以保持数据对齐。
+ */
public int reservedPad;
+ /**
+ * uint16[segCount]
+ * 仅 Format=4
+ * 每个段的起始字符码。
+ */
public int[] startCode;
- public short[] idDelta;
- public int[] idRangeOffset;
- public int[] glyphIdArray;
- }
-
- private static class CmapFormat6 extends CmapFormat {
+ /**
+ * int16[segCount]
+ * 仅 Format=4
+ * 用于计算字形索引的偏移值。该值被加到从 startCode 到 endCode 的所有字符码上,得到相应的字形索引。
+ */
+ public int[] idDelta;
+ /**
+ * uint16[segCount]
+ * 仅 Format=4
+ * 偏移到 glyphIdArray 中的起始位置,如果没有额外的字形索引映射,则为 0。
+ */
+ public int[] idRangeOffsets;
+ /**
+ * uint16
+ * 仅 Format=6
+ * 子范围的第一个字符代码。这是连续字符代码范围的起始点。
+ */
public int firstCode;
+ /**
+ * uint16
+ * 仅 Format=6
+ * 子范围中字符代码的数量。这表示从 firstCode 开始,连续多少个字符代码被包含
+ */
public int entryCount;
+ /**
+ * 字形索引数组
+ * Format=0 为 bye[256]数组
+ * Format>0 为 uint16[] 数组
+ * Format>12 为 uint32[] 数组
+ * @url Microsoft cmap文档
+ */
public int[] glyphIdArray;
}
- private static class CmapFormat12 extends CmapFormat {
- public int reserved;
- public int length;
- public int language;
- public int numGroups;
- public List bit0: 该点位于曲线上
+ * bit1: < 1:xCoordinate为uint8 >
+ * bit2: < 1:yCoordinate为uint8 >
+ * bit3: < 1:下一个uint8为此条目之后插入的附加逻辑标志条目的数量 >
+ * bit4: < bit1=1时表示符号[1.正,0.负]; bit1=0时[1.x坐标重复一次,0.x坐标读为int16] >
+ * bit5: < bit2=1时表示符号[1.正,0.负]; bit2=0时[1.y坐标重复一次,0.y坐标读为int16] >
+ * bit6: 字形描述中的轮廓可能会重叠
+ * bit7: 保留位,无意义
+ */
+ int[] flags;
+ /**
+ * uint8[] when(flags&0x02==0x02)
+ * int16[] when(flags&0x12==0x00)
+ */
+ int[] xCoordinates;
+ /**
+ * uint8[] when(flags&0x04==0x02)
+ * int16[] when(flags&0x24==0x00)
+ */
+ int[] yCoordinates;
+ }
+
+ /**
+ * 复合字形数据表
+ */
+ private static class GlyphTableComponent {
+ /**
+ * uint16
+ * bit0: < 1:argument是16bit,0:argument是8bit >
+ * bit1: < 1:argument是有符号值,0:argument是无符号值 >
+ * bit3: 该组件有一个缩放比例,否则比例为1.0
+ * bit5: 表示在此字形之后还有字形
+ */
+ int flags;
+ /**
+ * uint16
+ */
+ int glyphIndex;
+ /**
+ * x-offset
+ * uint8 when flags&0x03==0
+ * int8 when flags&0x03==1
+ * uint16 when flags&0x03==2
+ * int16 when flags&0x03==3
+ */
+ int argument1;
+ /**
+ * y-offset
+ * uint8 when flags&0x03==0
+ * int8 when flags&0x03==1
+ * uint16 when flags&0x03==2
+ * int16 when flags&0x03==3
+ */
+ int argument2;
+ /**
+ * uint16
+ * 值类型为 F2DOT14 的组件缩放X比例值
+ */
+ float xScale;
+ /**
+ * uint16
+ * 值类型为 F2DOT14 的2x2变换矩阵01值
+ */
+ float scale01;
+ /**
+ * uint16
+ * 值类型为 F2DOT14 的2x2变换矩阵10值
+ */
+ float scale10;
+ /**
+ * uint16
+ * 值类型为 F2DOT14 的组件缩放Y比例值
+ */
+ float yScale;
}
private static class BufferReader {
@@ -168,7 +520,6 @@ public class QueryTTF {
return byteBuffer.position();
}
-
public long ReadUInt64() {
return byteBuffer.getLong();
}
@@ -198,39 +549,52 @@ public class QueryTTF {
}
public byte[] ReadByteArray(int len) {
- if (len < 0) return null;
+ assert len >= 0;
byte[] result = new byte[len];
byteBuffer.get(result);
return result;
}
- public short[] ReadInt16Array(int len) {
- if (len < 0) return null;
- var result = new short[len];
+ public int[] ReadUInt8Array(int len) {
+ assert len >= 0;
+ var result = new int[len];
+ for (int i = 0; i < len; ++i) result[i] = byteBuffer.get() & 0xFF;
+ return result;
+ }
+
+ public int[] ReadInt16Array(int len) {
+ assert len >= 0;
+ var result = new int[len];
for (int i = 0; i < len; ++i) result[i] = byteBuffer.getShort();
return result;
}
public int[] ReadUInt16Array(int len) {
- if (len < 0) return null;
+ assert len >= 0;
var result = new int[len];
for (int i = 0; i < len; ++i) result[i] = byteBuffer.getShort() & 0xFFFF;
return result;
}
+
+ public int[] ReadInt32Array(int len) {
+ assert len >= 0;
+ var result = new int[len];
+ for (int i = 0; i < len; ++i) result[i] = byteBuffer.getInt();
+ return result;
+ }
}
private final Header fileHeader = new Header();
- private final Map 根据定义,索引零指向“丢失的字符”。
+ * loca.length = maxp.numGlyphs + 1;
+ */
+ private int[] loca;
+
+ private void readLocaTable(byte[] buffer) {
+ var dataTable = directorys.get("loca");
+ assert dataTable != null;
+ var reader = new BufferReader(buffer, dataTable.offset);
+ if (head.indexToLocFormat == 0) {
+ loca = reader.ReadUInt16Array(dataTable.length / 2);
+ } else {
+ loca = reader.ReadInt32Array(dataTable.length / 4);
+ }
+ }
+
private void readCmapTable(byte[] buffer) {
- var dataTable = Objects.requireNonNull(directorys.get("cmap"));
+ var dataTable = directorys.get("cmap");
+ assert dataTable != null;
var reader = new BufferReader(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.platformSpecificID = 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();
+ for (var formatTable : Cmap.records) {
+ int fmtOffset = formatTable.offset;
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);
- }
- }
- }
+ reader.position(dataTable.offset + fmtOffset);
- private void readLocaTable(byte[] buffer) {
- var dataTable = Objects.requireNonNull(directorys.get("loca"));
- var reader = new BufferReader(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());
+ CmapFormat f = new CmapFormat();
+ f.format = reader.ReadUInt16();
+ f.length = reader.ReadUInt16();
+ f.language = reader.ReadUInt16();
+ switch (f.format) {
+ case 0: {
+ f.glyphIdArray = reader.ReadUInt8Array(f.length - 6);
+ // 记录 unicode->glyphId 映射表
+ int unicodeInclusive = 0;
+ int unicodeExclusive = f.glyphIdArray.length;
+ for (; unicodeInclusive < unicodeExclusive; unicodeInclusive++) {
+ unicodeToGlyphId.put(unicodeInclusive, f.glyphIdArray[unicodeInclusive]);
+ }
+ break;
+ }
+ case 4: {
+ f.segCountX2 = reader.ReadUInt16();
+ int segCount = f.segCountX2 / 2;
+ 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.idRangeOffsets = reader.ReadUInt16Array(segCount);
+ // 一个包含字形索引的数组,其长度是任意的,取决于映射的复杂性和字体中的字符数量。
+ int glyphIdArrayLength = (f.length - 16 - (segCount * 8)) / 2;
+ f.glyphIdArray = reader.ReadUInt16Array(glyphIdArrayLength);
+
+ // 记录 unicode->glyphId 映射表
+ for (int segmentIndex = 0; segmentIndex < segCount; segmentIndex++) {
+ int unicodeInclusive = f.startCode[segmentIndex];
+ int unicodeExclusive = f.endCode[segmentIndex];
+ int idDelta = f.idDelta[segmentIndex];
+ int idRangeOffset = f.idRangeOffsets[segmentIndex];
+ for (int unicode = unicodeInclusive; unicode <= unicodeExclusive; unicode++) {
+ if (idRangeOffset == 0) {
+ unicodeToGlyphId.put(unicode, (unicode + idDelta) & 0xFFFF);
+ } else {
+ int gIndex = (idRangeOffset / 2) + unicode - unicodeInclusive + segmentIndex;
+ unicodeToGlyphId.put(unicode, gIndex < glyphIdArrayLength ? f.glyphIdArray[gIndex] + idDelta : 0);
+ }
+ }
+ }
+ break;
+ }
+ case 6: {
+ f.firstCode = reader.ReadUInt16();
+ f.entryCount = reader.ReadUInt16();
+ // 范围内字符代码的字形索引值数组。
+ f.glyphIdArray = reader.ReadUInt16Array(f.entryCount);
+
+ // 记录 unicode->glyphId 映射表
+ int unicodeIndex = f.firstCode;
+ int unicodeCount = f.entryCount;
+ for (int gIndex = 0; gIndex < unicodeCount; gIndex++) {
+ unicodeToGlyphId.put(unicodeIndex, f.glyphIdArray[gIndex]);
+ unicodeIndex++;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ Cmap.tables.put(fmtOffset, f);
}
}
private void readMaxpTable(byte[] buffer) {
- var dataTable = Objects.requireNonNull(directorys.get("maxp"));
+ var dataTable = directorys.get("maxp");
+ assert dataTable != null;
var reader = new BufferReader(buffer, dataTable.offset);
- maxp.majorVersion = reader.ReadUInt16();
- maxp.minorVersion = reader.ReadUInt16();
+ maxp.version = reader.ReadUInt32();
maxp.numGlyphs = reader.ReadUInt16();
maxp.maxPoints = reader.ReadUInt16();
maxp.maxContours = reader.ReadUInt16();
@@ -370,80 +762,163 @@ public class QueryTTF {
maxp.maxComponentDepth = reader.ReadUInt16();
}
- private void readGlyfTable(byte[] buffer) {
- var dataTable = Objects.requireNonNull(directorys.get("glyf"));
- int glyfCount = maxp.numGlyphs;
- glyf = new String[glyfCount];
+ /**
+ * 字形轮廓表 数组
+ */
+ private GlyfLayout[] glyfArray;
- int coreCount = Runtime.getRuntime().availableProcessors();
- ExecutorService executor = Executors.newFixedThreadPool(coreCount);
- int sliceSize = (coreCount < glyfCount) ? (glyfCount / coreCount) : glyfCount;
- for (int blockOffset = 0; blockOffset < glyfCount; blockOffset += sliceSize) {
- final int inclusive = blockOffset;
- final int exclusive = Math.min(blockOffset + sliceSize, glyfCount);
- executor.submit(() -> {
- var reader = new BufferReader(buffer, 0);
- for (int index = inclusive; index < exclusive; index++) {
- int offset = dataTable.offset + loca.get(index);
- int glyfLength = (index + 1 < glyfCount ? loca.get(index + 1) : dataTable.offset + dataTable.length) - loca.get(index);
- reader.position(offset);
- 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];
- }
- }
+ private void readGlyfTable(byte[] buffer) {
+ var dataTable = directorys.get("glyf");
+ assert dataTable != null;
+ int glyfCount = maxp.numGlyphs;
+ glyfArray = new GlyfLayout[glyfCount + 1]; // 创建容器时,多创建一个作为保留区
+
+ var reader = new BufferReader(buffer, 0);
+ for (int index = 1; index <= glyfCount; index++) {
+ if (loca[index - 1] == loca[index]) continue; // 当前loca与下一个loca相同,表示这个字形不存在
+ int offset = dataTable.offset + loca[index - 1];
+ // 读GlyphHeaders
+ var glyph = new GlyfLayout();
+ reader.position(offset);
+ glyph.numberOfContours = reader.ReadInt16();
+ glyph.xMin = reader.ReadInt16();
+ glyph.yMin = reader.ReadInt16();
+ glyph.xMax = reader.ReadInt16();
+ glyph.yMax = reader.ReadInt16();
+
+ // 读Glyph轮廓数据
+ if (glyph.numberOfContours > 0) {
+ // 简单轮廓
+ glyph.glyphSimple = new GlyphTableBySimple();
+ glyph.glyphSimple.endPtsOfContours = reader.ReadUInt16Array(glyph.numberOfContours);
+ glyph.glyphSimple.instructionLength = reader.ReadUInt16();
+ glyph.glyphSimple.instructions = reader.ReadUInt8Array(glyph.glyphSimple.instructionLength);
+ int flagLength = glyph.glyphSimple.endPtsOfContours[glyph.glyphSimple.endPtsOfContours.length - 1] + 1;
+ // 获取轮廓点描述标志
+ glyph.glyphSimple.flags = new int[flagLength];
+ for (int n = 0; n < flagLength; ++n) {
+ var glyphSimpleFlag = reader.ReadUInt8();
+ glyph.glyphSimple.flags[n] = glyphSimpleFlag;
+ if ((glyphSimpleFlag & 0x08) == 0x08) {
+ for (int m = reader.ReadUInt8(); m > 0; --m) {
+ glyph.glyphSimple.flags[++n] = glyphSimpleFlag;
}
- // 获取轮廓点描述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 {
- // TODO: 处理复合字形
- glyph = reader.ReadByteArray(glyfLength - 2);
}
- glyf[index] = getHexFromBytes(glyph);
}
- });
+ // 获取轮廓点描述x轴相对值
+ glyph.glyphSimple.xCoordinates = new int[flagLength];
+ for (int n = 0; n < flagLength; ++n) {
+ switch (glyph.glyphSimple.flags[n] & 0x12) {
+ case 0x02:
+ glyph.glyphSimple.xCoordinates[n] = -1 * reader.ReadUInt8();
+ break;
+ case 0x12:
+ glyph.glyphSimple.xCoordinates[n] = reader.ReadUInt8();
+ break;
+ case 0x10:
+ glyph.glyphSimple.xCoordinates[n] = 0; // 点位数据重复上一次数据,那么相对数据变化量就是0
+ break;
+ case 0x00:
+ glyph.glyphSimple.xCoordinates[n] = reader.ReadInt16();
+ break;
+ }
+ }
+ // 获取轮廓点描述y轴相对值
+ glyph.glyphSimple.yCoordinates = new int[flagLength];
+ for (int n = 0; n < flagLength; ++n) {
+ switch (glyph.glyphSimple.flags[n] & 0x24) {
+ case 0x04:
+ glyph.glyphSimple.yCoordinates[n] = -1 * reader.ReadUInt8();
+ break;
+ case 0x24:
+ glyph.glyphSimple.yCoordinates[n] = reader.ReadUInt8();
+ break;
+ case 0x20:
+ glyph.glyphSimple.yCoordinates[n] = 0; // 点位数据重复上一次数据,那么相对数据变化量就是0
+ break;
+ case 0x00:
+ glyph.glyphSimple.yCoordinates[n] = reader.ReadInt16();
+ break;
+ }
+ }
+ } else {
+ // 复合轮廓
+ glyph.glyphComponent = new LinkedList<>();
+ while (true) {
+ var glyphTableComponent = new GlyphTableComponent();
+ glyphTableComponent.flags = reader.ReadUInt16();
+ glyphTableComponent.glyphIndex = reader.ReadUInt16();
+ switch (glyphTableComponent.flags & 0b11) {
+ case 0b00:
+ glyphTableComponent.argument1 = reader.ReadUInt8();
+ glyphTableComponent.argument2 = reader.ReadUInt8();
+ break;
+ case 0b10:
+ glyphTableComponent.argument1 = reader.ReadInt8();
+ glyphTableComponent.argument2 = reader.ReadInt8();
+ break;
+ case 0b01:
+ glyphTableComponent.argument1 = reader.ReadUInt16();
+ glyphTableComponent.argument2 = reader.ReadUInt16();
+ break;
+ case 0b11:
+ glyphTableComponent.argument1 = reader.ReadInt16();
+ glyphTableComponent.argument2 = reader.ReadInt16();
+ break;
+ }
+ switch (glyphTableComponent.flags & 0b11001000) {
+ case 0b00001000:
+ // 有单一比例
+ glyphTableComponent.yScale = glyphTableComponent.xScale = ((float) reader.ReadUInt16()) / 16384.0f;
+ break;
+ case 0b01000000:
+ // 有X和Y的独立比例
+ glyphTableComponent.xScale = ((float) reader.ReadUInt16()) / 16384.0f;
+ glyphTableComponent.yScale = ((float) reader.ReadUInt16()) / 16384.0f;
+ break;
+ case 0b10000000:
+ // 有2x2变换矩阵
+ glyphTableComponent.xScale = ((float) reader.ReadUInt16()) / 16384.0f;
+ glyphTableComponent.scale01 = ((float) reader.ReadUInt16()) / 16384.0f;
+ glyphTableComponent.scale10 = ((float) reader.ReadUInt16()) / 16384.0f;
+ glyphTableComponent.yScale = ((float) reader.ReadUInt16()) / 16384.0f;
+ break;
+ }
+ glyph.glyphComponent.add(glyphTableComponent);
+ if ((glyphTableComponent.flags & 0x20) == 0) break;
+ }
+ }
+ glyfArray[index] = glyph; // 根据文档 glyfId=0 作为保留区使用,这里赋值从索引1开始
}
- executor.shutdown();
- try {
- boolean b = executor.awaitTermination(60, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- Log.e("queryTTF", "glyf表解析出错: " + e);
+ }
+
+ /**
+ * 使用轮廓索引值获取轮廓数据
+ *
+ * @param glyfId 轮廓索引
+ * @return 轮廓数据
+ */
+ public String getGlyfById(int glyfId) {
+ var glyph = glyfArray[glyfId];
+ if (glyph == null) return null; // 过滤不存在的字体轮廓
+ String glyphString;
+ if (glyph.numberOfContours >= 0) {
+ // 简单字形
+ int dataCount = glyph.glyphSimple.flags.length;
+ String[] coordinateArray = new String[dataCount];
+ for (int i = 0; i < dataCount; i++) {
+ coordinateArray[i] = glyph.glyphSimple.xCoordinates[i] + "," + glyph.glyphSimple.yCoordinates[i];
+ }
+ glyphString = String.join("|", coordinateArray);
+ } else {
+ // 复合字形
+ LinkedList