diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt b/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt index b6de0a9aa..9b6a8ec52 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt @@ -1,6 +1,7 @@ package io.legado.app.model.analyzeRule import io.legado.app.help.JsExtensions +import java.nio.charset.Charset import kotlin.experimental.and @ExperimentalUnsignedTypes @@ -9,10 +10,8 @@ import kotlin.experimental.and * @see 获取详情 * @see 基于Javascript的TTF解析器 */ -class QueryTTF(var font: ByteArray) : JsExtensions { - data class Index(var num: Int) - - class FileHeader { +class QueryTTF(var Font: Array) : JsExtensions { + private class Header { var majorVersion: UShort = 0u var minorVersion: UShort = 0u var numOfTables: UShort = 0u @@ -21,32 +20,30 @@ class QueryTTF(var font: ByteArray) : JsExtensions { var rangeShift: UShort = 0u } - class TableDirectory { + private class Directory { var tag: String = "" var checkSum: UInt = 0u var offset: UInt = 0u var length: UInt = 0u - lateinit var data: ByteArray } - class NameTable { + private class NameLayout { var format: UShort = 0u var count: UShort = 0u var stringOffset: UShort = 0u - var Records = ArrayList() + var records = ArrayList() } - class NameRecord { + private class NameRecord { var platformID: UShort = 0u var encodingID: UShort = 0u var languageID: UShort = 0u var nameID: UShort = 0u var length: UShort = 0u var offset: UShort = 0u - lateinit var nameBuffer: ByteArray } - class HeadTable { + private class HeadLayout { var majorVersion: UShort = 0u var minorVersion: UShort = 0u var fontRevision: UInt = 0u @@ -67,7 +64,7 @@ class QueryTTF(var font: ByteArray) : JsExtensions { var glyphDataFormat: Short = 0 } - class MaxpTable { + private class MaxpLayout { var majorVersion: UShort = 0u var minorVersion: UShort = 0u var numGlyphs: UShort = 0u // 字体中的字形数量 @@ -86,27 +83,27 @@ class QueryTTF(var font: ByteArray) : JsExtensions { var maxComponentDepth: UShort = 0u } - class CmapTable { + private class CmapLayout { var version: UShort = 0u var numTables: UShort = 0u - var records = ArrayList() - var tables = mutableMapOf() + var records = ArrayList() + var tables = mutableMapOf() } - class EncodingRecord { + private class CmapRecord { var platformID: UShort = 0u var encodingID: UShort = 0u var offset: UInt = 0u } - class Format0 { + private class CmapFormat0 { var format: UShort = 0u var length: UShort = 0u var language: UShort = 0u - var glyphIdArray = ByteArray(256) + var glyphIdArray = ArrayList() } - class Format4 { + private class CmapFormat4 { var format: UShort = 0u var length: UShort = 0u var language: UShort = 0u @@ -114,377 +111,493 @@ class QueryTTF(var font: ByteArray) : JsExtensions { var searchRange: UShort = 0u var entrySelector: UShort = 0u var rangeShift: UShort = 0u - lateinit var endCode: IntArray // UInt16[] + lateinit var endCode: ArrayList // UInt16[] var reservedPad: UShort = 0u - lateinit var startCode: IntArray // UInt16[] - lateinit var idDelta: ShortArray - lateinit var idRangeOffset: IntArray // UInt16[] + lateinit var startCode: ArrayList // UInt16[] + lateinit var idDelta: ArrayList + lateinit var idRangeOffset: ArrayList // UInt16[] + var glyphIdArray = ArrayList() + } + + private class CmapFormat6 { + var format: UShort = 0u + var length: UShort = 0u + var language: UShort = 0u + var firstCode: UShort = 0u + var entryCount: UShort = 0u var glyphIdArray = ArrayList() } - class GlyfTable { + private class CmapFormat12 { + var format: UShort = 0u + var reserved: UShort = 0u + var length: UInt = 0u + var language: UInt = 0u + var numGroups: UInt = 0u + lateinit var groups: ArrayList> + } + + private class GlyfLayout { var numberOfContours: Short = 0 var xMin: Short = 0 var yMin: Short = 0 var xMax: Short = 0 var yMax: Short = 0 - lateinit var endPtsOfContours: IntArray // UInt16[] - var instructionLength: Int = 0 // UInt16 - lateinit var instructions: ByteArray - lateinit var flags: ByteArray - lateinit var xCoordinates: Array - lateinit var yCoordinates: Array + lateinit var endPtsOfContours: ArrayList // UInt16[] + var instructionLength: UShort = 0u // UInt16 + lateinit var instructions: ArrayList + lateinit var flags: ArrayList + lateinit var xCoordinates: ArrayList + lateinit var yCoordinates: ArrayList } - var Header = FileHeader() + private class ByteArrayReader(var Buffer: Array, var Index: Int) { + fun ReadUIntX(len: Long): ULong { + var result: ULong = 0u; + for (i in 0 until len) { + result.shl(8); + result = result or Buffer[Index++].toULong() + } + return result; + } - var Tables = mutableMapOf() + fun ReadUInt64(): ULong { + return ReadUIntX(8) + } - var name = NameTable() + fun ReadInt64(): Long { + return ReadUIntX(8).toLong() + } - var head = HeadTable() + fun ReadUInt32(): UInt { + return ReadUIntX(4).toUInt() + } - var maxp = MaxpTable() + fun ReadInt32(): Int { + return ReadUIntX(4).toInt() + } - var loca = ArrayList() + fun ReadUInt16(): UShort { + return ReadUIntX(2).toUShort() + } - var cmap = CmapTable() + fun ReadInt16(): Short { + return ReadUIntX(2).toShort() + } - var glyf = ArrayList() + fun ReadStrings(len: Int, charset: Charset): String { + if (len <= 0) return "" + val result = ByteArray(len) + for (i in 0 until len) { + result[i] = Buffer[Index++] + } + return result.toString(charset) + } - // ByteArray转Long整数 - private fun byteArrToUintx(buff: ByteArray): Long { - var result: Long = 0 - var n: Int = 0 - var i: Int = buff.size - while (i > 0) { - result = result or buff[--i].toLong().shl(n) - n += 8 + fun GetByte(): Byte { + return Buffer[Index++] } - return result - } - // 索引变量 - var index = 0 + fun GetBytes(len: Int): ArrayList { + if (len <= 0) return ArrayList(0) + val result = ArrayList(len) + for (i in 0 until len) { + result[i] = Buffer[Index++] + } + return result; + } - // 从索引index开始拷贝指定长度的数组 - private fun ByteArray.copyOfIndex(length: Int): ByteArray { - val fromIndex = index - index += length - return this.copyOfRange(fromIndex, fromIndex + length) - } + fun GetUInt16Array(len: Int): ArrayList { + if (len <= 0) return ArrayList(0) + val result = ArrayList(len) + for (i in 0 until len) { + result[i] = ReadUInt16() + } + return result; + } - // 从索引fromIndex开始拷贝指定长度的数组 - private fun ByteArray.copyOfIndex(fromIndex: Int, length: Int): ByteArray { - return this.copyOfRange(fromIndex, fromIndex + length) + fun GetInt16Array(len: Int): ArrayList { + if (len <= 0) return ArrayList(0) + val result = ArrayList(len) + for (i in 0 until len) { + result[i] = ReadInt16() + } + return result; + } } - init { + private var FontReader: ByteArrayReader + private var fileHeader = Header() + private var directorys = ArrayList() + private var name = NameLayout() + private var head = HeadLayout() + private var maxp = MaxpLayout() + private var loca = ArrayList() + private var cmap = CmapLayout() + private var glyf = ArrayList() + private var unicodeMap = mutableMapOf>() - // 解析文件头 - Header = FileHeader() - // 跳过不需要的数据条目 - index = 4 - Header.numOfTables = byteArrToUintx(font.copyOfIndex(2)).toUShort() - - // 获取数据表 - index = 12 - for (i in 0 until Header.numOfTables.toInt()) { - val table = TableDirectory() - table.tag = font.copyOfIndex(4).toString(Charsets.US_ASCII) - table.checkSum = byteArrToUintx(font.copyOfIndex(4)).toUInt() - table.offset = byteArrToUintx(font.copyOfIndex(4)).toUInt() - table.length = byteArrToUintx(font.copyOfIndex(4)).toUInt() - table.data = font.copyOfIndex(table.offset.toInt(), table.length.toInt()) - Tables[table.tag] = table + init { + FontReader = ByteArrayReader(Font, 0) + // 获取文件头 + fileHeader.majorVersion = FontReader.ReadUInt16() + fileHeader.minorVersion = FontReader.ReadUInt16(); + fileHeader.numOfTables = FontReader.ReadUInt16(); + fileHeader.searchRange = FontReader.ReadUInt16(); + fileHeader.entrySelector = FontReader.ReadUInt16(); + fileHeader.rangeShift = FontReader.ReadUInt16(); + // 获取目录 + for (i in 0 until fileHeader.numOfTables.toInt()) { + val tag = FontReader.ReadStrings(4, Charsets.US_ASCII) + val t = Directory(); + t.tag = tag + t.checkSum = FontReader.ReadUInt32() + t.offset = FontReader.ReadUInt32() + t.length = FontReader.ReadUInt32() + directorys.add(t) } - // 解析表 name (字体信息,包含版权、名称、作者等...) - run { - val data = Tables["name"]!!.data - index = 0 - name.format = byteArrToUintx(data.copyOfIndex(2)).toUShort() - name.count = byteArrToUintx(data.copyOfIndex(2)).toUShort() - name.stringOffset = byteArrToUintx(data.copyOfIndex(2)).toUShort() - - for (i in 0 until name.count.toInt()) { - val record = NameRecord() - record.platformID = byteArrToUintx(data.copyOfIndex(2)).toUShort() - record.encodingID = byteArrToUintx(data.copyOfIndex(2)).toUShort() - record.languageID = byteArrToUintx(data.copyOfIndex(2)).toUShort() - record.nameID = byteArrToUintx(data.copyOfIndex(2)).toUShort() - record.length = byteArrToUintx(data.copyOfIndex(2)).toUShort() - record.offset = byteArrToUintx(data.copyOfIndex(2)).toUShort() - record.nameBuffer = data.copyOfIndex((name.stringOffset + record.offset).toInt(), record.length.toInt()) - name.Records.add(record) + for (Temp in directorys) { + if (Temp.tag == "name") { + FontReader.Index = Temp.offset.toInt(); + name.format = FontReader.ReadUInt16(); + name.count = FontReader.ReadUInt16(); + name.stringOffset = FontReader.ReadUInt16(); + + for (i in 0 until name.count.toInt()) { + val record = 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) - run { - val data = Tables["head"]!!.data - index = 0 - head.majorVersion = byteArrToUintx(data.copyOfIndex(2)).toUShort() - head.minorVersion = byteArrToUintx(data.copyOfIndex(2)).toUShort() - head.fontRevision = byteArrToUintx(data.copyOfIndex(4)).toUInt() - head.checkSumAdjustment = byteArrToUintx(data.copyOfIndex(4)).toUInt() - head.magicNumber = byteArrToUintx(data.copyOfIndex(4)).toUInt() - head.flags = byteArrToUintx(data.copyOfIndex(2)).toUShort() - head.unitsPerEm = byteArrToUintx(data.copyOfIndex(2)).toUShort() - head.created = byteArrToUintx(data.copyOfIndex(8)).toULong() - head.modified = byteArrToUintx(data.copyOfIndex(8)).toULong() - head.xMin = byteArrToUintx(data.copyOfIndex(2)).toShort() - head.yMin = byteArrToUintx(data.copyOfIndex(2)).toShort() - head.xMax = byteArrToUintx(data.copyOfIndex(2)).toShort() - head.yMax = byteArrToUintx(data.copyOfIndex(2)).toShort() - head.macStyle = byteArrToUintx(data.copyOfIndex(2)).toUShort() - head.lowestRecPPEM = byteArrToUintx(data.copyOfIndex(2)).toUShort() - head.fontDirectionHint = byteArrToUintx(data.copyOfIndex(2)).toShort() - head.indexToLocFormat = byteArrToUintx(data.copyOfIndex(2)).toShort() - head.glyphDataFormat = byteArrToUintx(data.copyOfIndex(2)).toShort() + for (Temp in directorys) { + if (Temp.tag == "head") { + FontReader.Index = Temp.offset.toInt(); + 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) - run { - val data = Tables["maxp"]!!.data - index = 0 - index = 0 - maxp.majorVersion = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.minorVersion = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.numGlyphs = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxPoints = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxContours = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxCompositePoints = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxCompositeContours = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxZones = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxTwilightPoints = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxStorage = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxFunctionDefs = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxInstructionDefs = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxStackElements = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxSizeOfInstructions = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxComponentElements = byteArrToUintx(data.copyOfIndex(2)).toUShort() - maxp.maxComponentDepth = byteArrToUintx(data.copyOfIndex(2)).toUShort() + for (Temp in directorys) { + if (Temp.tag == "maxp") { + FontReader.Index = Temp.offset.toInt(); + 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 (轮廓数据偏移地址表) - run { - val data = Tables["maxp"]!!.data - val offset = if (head.indexToLocFormat.equals(0)) 2 else 4 - index = 0 - while (index < data.size) { - loca.add((byteArrToUintx(data.copyOfIndex(offset)) * (if (offset == 4) 1 else 2)).toUInt()) - index += offset + for (Temp in directorys) { + if (Temp.tag == "loca") { + FontReader.Index = Temp.offset.toInt() + val offset: UInt = if (head.indexToLocFormat.toInt() == 0) 2u else 4u + var i: UInt = 0u + while (i < Temp.length) { + loca.add(if (offset == 2u) FontReader.ReadUInt16().toUInt().shl(1) else FontReader.ReadUInt32()) + i += offset + } } } - // 解析表 cmap (Unicode编码轮廓索引对照表) - run { - val data = Tables["cmap"]!!.data - index = 0 - cmap.version = byteArrToUintx(data.copyOfIndex(2)).toUShort() - cmap.numTables = byteArrToUintx(data.copyOfIndex(2)).toUShort() - - for (i in 0 until cmap.numTables.toInt()) { - val record = EncodingRecord() - record.platformID = byteArrToUintx(data.copyOfIndex(2)).toUShort() - record.encodingID = byteArrToUintx(data.copyOfIndex(2)).toUShort() - record.offset = byteArrToUintx(data.copyOfIndex(4)).toUInt() - cmap.records.add(record) - val tmpIndex = index; // 缓存索引 - - index = record.offset.toInt() - val fmt = byteArrToUintx(data.copyOfIndex(2)).toUShort() - val len = byteArrToUintx(data.copyOfIndex(2)).toUShort() - val lang = byteArrToUintx(data.copyOfIndex(2)).toUShort() - if (fmt.equals(0)) { - val ft = Format0() - ft.format = fmt - ft.length = len - ft.language = lang - ft.glyphIdArray = data.copyOfIndex(index, len.toInt() - 6) - cmap.tables[fmt.toInt()] = ft - } else if (fmt.equals(4)) { - val ft = Format4() - ft.format = fmt - ft.length = len - ft.language = lang - ft.segCountX2 = byteArrToUintx(data.copyOfIndex(2)).toUShort() - val segCount = ft.segCountX2.toInt() / 2 - ft.searchRange = byteArrToUintx(data.copyOfIndex(2)).toUShort() - ft.entrySelector = byteArrToUintx(data.copyOfIndex(2)).toUShort() - ft.rangeShift = byteArrToUintx(data.copyOfIndex(2)).toUShort() - ft.endCode = IntArray(segCount) - for (n in 0 until segCount) { - ft.endCode[n] = byteArrToUintx(data.copyOfIndex(2)).toInt() - } - ft.reservedPad = byteArrToUintx(data.copyOfIndex(2)).toUShort() - ft.startCode = IntArray(segCount) - for (n in 0 until segCount) { - ft.startCode[n] = byteArrToUintx(data.copyOfIndex(2)).toInt() - } - ft.idDelta = ShortArray(segCount) - for (n in 0 until segCount) { - ft.idDelta[n] = byteArrToUintx(data.copyOfIndex(2)).toShort() - } - ft.idRangeOffset = IntArray(segCount) - for (n in 0 until segCount) { - ft.idRangeOffset[n] = byteArrToUintx(data.copyOfIndex(2)).toInt() - } - while (index < len.toInt()) { - ft.glyphIdArray.add(byteArrToUintx(data.copyOfIndex(2)).toUShort()) + for (Temp in directorys) { + if (Temp.tag == "cmap") { + FontReader.Index = Temp.offset.toInt() + cmap.version = FontReader.ReadUInt16() + cmap.numTables = FontReader.ReadUInt16() + + for (i in 0 until cmap.numTables.toInt()) { + val record = CmapRecord() + record.platformID = FontReader.ReadUInt16() + record.encodingID = FontReader.ReadUInt16() + record.offset = FontReader.ReadUInt32() + cmap.records.add(record) + } + for (i in 0 until cmap.numTables.toInt()) { + val fmtOffset = cmap.records[i].offset; + FontReader.Index = (Temp.offset + fmtOffset).toInt(); + val EndIndex = FontReader.Index; + + val format = FontReader.ReadUInt16(); + + if (cmap.tables.contains(fmtOffset.toInt())) continue; + when { + format.equals(0) -> { + val fmt = CmapFormat0() + fmt.format = format + fmt.length = FontReader.ReadUInt16() + fmt.language = FontReader.ReadUInt16() + fmt.glyphIdArray = FontReader.GetBytes(fmt.length.toInt() - 6) + cmap.tables[fmtOffset] = fmt + } + format.equals(4) -> { + val fmt = CmapFormat4() + fmt.format = format + fmt.length = FontReader.ReadUInt16() + fmt.language = FontReader.ReadUInt16() + fmt.segCountX2 = FontReader.ReadUInt16() + val segCount = fmt.segCountX2.toInt() / 2 + fmt.searchRange = FontReader.ReadUInt16() + fmt.entrySelector = FontReader.ReadUInt16() + fmt.rangeShift = FontReader.ReadUInt16() + fmt.endCode = FontReader.GetUInt16Array(segCount) + fmt.reservedPad = FontReader.ReadUInt16() + fmt.startCode = FontReader.GetUInt16Array(segCount) + fmt.idDelta = FontReader.GetInt16Array(segCount) + fmt.idRangeOffset = FontReader.GetUInt16Array(segCount) + fmt.glyphIdArray = FontReader.GetUInt16Array((fmt.length.toInt() - (FontReader.Index - EndIndex)) / 2) + cmap.tables[fmtOffset] = fmt + } + format.equals(6) -> { + val fmt = CmapFormat6() + fmt.format = format + fmt.length = FontReader.ReadUInt16() + fmt.language = FontReader.ReadUInt16() + fmt.firstCode = FontReader.ReadUInt16() + fmt.entryCount = FontReader.ReadUInt16() + fmt.glyphIdArray = FontReader.GetUInt16Array(fmt.entryCount.toInt()) + cmap.tables[fmtOffset] = fmt + } + format.equals(12) -> { + val fmt = CmapFormat12() + fmt.format = format + fmt.reserved = FontReader.ReadUInt16(); + fmt.length = FontReader.ReadUInt32(); + fmt.language = FontReader.ReadUInt32(); + fmt.numGroups = FontReader.ReadUInt32(); + for (n in 0 until fmt.numGroups.toLong()) { + fmt.groups.add(Triple(FontReader.ReadUInt32(), FontReader.ReadUInt32(), FontReader.ReadUInt32())) + } + cmap.tables[fmtOffset] = fmt + } } - cmap.tables[fmt.toInt()] = ft } - index = tmpIndex; // 读取缓存的索引 } } - // 解析表 glyf (字体轮廓数据表) - run { - val data = Tables["glyf"]!!.data - for (i in 0 until maxp.numGlyphs.toInt()) { - index = loca[i].toInt() - val numberOfContours = byteArrToUintx(data.copyOfIndex(2)).toShort() - val xMin = byteArrToUintx(data.copyOfIndex(2)).toShort() - val yMin = byteArrToUintx(data.copyOfIndex(2)).toShort() - val xMax = byteArrToUintx(data.copyOfIndex(2)).toShort() - val yMax = byteArrToUintx(data.copyOfIndex(2)).toShort() - if (numberOfContours > 0) { - val g = GlyfTable() - g.numberOfContours = numberOfContours - g.xMin = xMin - g.yMin = yMin - g.xMax = xMax - g.yMax = yMax - g.endPtsOfContours = IntArray(numberOfContours.toInt()) - for (n in 0 until numberOfContours) { - g.endPtsOfContours[n] = byteArrToUintx(data.copyOfIndex(2)).toInt(); // 这里数据源为UShort - } - g.instructionLength = byteArrToUintx(data.copyOfIndex(2)).toInt(); // 这里数据源为UShort - g.instructions = data.copyOfIndex(g.instructionLength) - val flagLength = g.endPtsOfContours.last() + 1 - g.flags = ByteArray(flagLength) - var n = 0 - while (n < flagLength) { - val flag = data.copyOfIndex(1).first() - g.flags[n] = flag - if (!(flag and 0x08).equals(0)) { - var j = data.copyOfIndex(1).first() - while (j > 0) { - --j - g.flags[++n] = flag + for (Temp in directorys) { + if (Temp.tag == "glyf") { + FontReader.Index = Temp.offset.toInt() + for (i in 0 until maxp.numGlyphs.toInt()) { + FontReader.Index = (Temp.offset + loca[i]).toInt() + + val g = GlyfLayout() + g.numberOfContours = FontReader.ReadInt16() + g.xMin = FontReader.ReadInt16() + g.yMin = FontReader.ReadInt16() + g.xMax = FontReader.ReadInt16() + g.yMax = FontReader.ReadInt16() + if (g.numberOfContours > 0) { + g.endPtsOfContours = FontReader.GetUInt16Array(g.numberOfContours.toInt()) + g.instructionLength = FontReader.ReadUInt16() + g.instructions = FontReader.GetBytes(g.instructionLength.toInt()) + val flagLength = g.endPtsOfContours.last().toInt() + 1 + // 获取轮廓点描述标志 + g.flags = ArrayList(flagLength) + var n = 0 + while (n < flagLength) { + g.flags[n] = FontReader.GetByte(); + if ((g.flags[n].and(0x08)).toInt() != 0x00) { + for (m in FontReader.GetByte() downTo 1) { + g.flags[++n] = g.flags[n - 1] + } } + ++n } - ++n - } - // 获取x轴相对坐标 - g.xCoordinates = Array(flagLength) { 0 } - for (n in 0 until flagLength) { - val xByte = !(g.flags[n] and 0x02).equals(0) - val xSame = !(g.flags[n] and 0x10).equals(0) - if (xByte) { - g.xCoordinates[n] = ((if (xSame) 1 else -1) * data.copyOfIndex(1).first()).toShort() - } else { - if (xSame) g.xCoordinates[n] = 0 - else { - g.xCoordinates[n] = byteArrToUintx(data.copyOfIndex(2)).toShort() + // 获取轮廓点描述x轴相对值 + g.xCoordinates = ArrayList(flagLength) + for (m in 0 until flagLength) { + val same = if ((g.flags[m].and(0x10)).toInt() != 0) 1 else -1 + if ((g.flags[m].and(0x02)).toInt() != 0) { + g.xCoordinates[m] = (same * FontReader.GetByte()).toShort() + } else { + g.xCoordinates[m] = if (same == 1) 0 else FontReader.ReadInt16() } } - } - // 获取y轴相对坐标 - g.yCoordinates = Array(flagLength) { 0 } - for (n in 0 until flagLength) { - val yByte = !(g.flags[n] and 0x04).equals(0) - val ySame = !(g.flags[n] and 0x20).equals(0) - if (yByte) { - g.yCoordinates[n] = ((if (ySame) 1 else -1) * data.copyOfIndex(1).first()).toShort() - } else { - if (ySame) g.yCoordinates[n] = 0 - else { - g.yCoordinates[n] = byteArrToUintx(data.copyOfIndex(2)).toShort() + // 获取轮廓点描述y轴相对值 + g.yCoordinates = ArrayList(flagLength) + for (m in 0 until flagLength) { + val same = if ((g.flags[m].and(0x20)).toInt() != 0) 1 else -1 + if ((g.flags[m].and(0x04)).toInt() != 0) { + g.yCoordinates[n] = (same * FontReader.GetByte()).toShort() + } else { + g.yCoordinates[n] = if (same == 1) 0 else FontReader.ReadInt16() } } - } - /* - PS:因为不需要绘制字体,转换就省了 - // 相对坐标转绝对坐标 - for (n in 0 until flagLength) { - g.xCoordinates[n] = (g.xCoordinates[n] + g.xCoordinates[n - 1]).toShort() - g.yCoordinates[n] = (g.yCoordinates[n] + g.yCoordinates[n - 1]).toShort() - } - */ + /* + // 相对坐标转绝对坐标 (因不绘制字体,这里用不上) + for(m in 1 until flagLength) + { + g.xCoordinates[m] = (g.xCoordinates[m] + g.xCoordinates[m - 1]).toShort(); + g.yCoordinates[m] = (g.yCoordinates[m] + g.yCoordinates[m - 1]).toShort(); + } + */ - glyf.add(g) - } else { - // 复合字体暂不处理,以后用到再加 + glyf.add(g); + } else { + // 复合字体暂未使用 + } } } } + + // 建立Unicode&Glyf映射表 + for (i in 0..130000) { + val gid = GetGlyfIndex(i).toInt() + if (gid == 0) continue; + if (unicodeMap.containsKey(gid)) continue + val thisGlyf = ArrayList() + thisGlyf.addAll(glyf[gid].xCoordinates) + thisGlyf.addAll(glyf[gid].yCoordinates) + unicodeMap[i] = thisGlyf; + } + } + /** - * 获取字体信息(默认获取字体名称) - * @see 获取详情 + * 获取字体信息 (默认获取字体名称,索引位1) */ - fun GetName(nameID: Int = 1): String { - for (record in name.Records) { - if (!record.nameID.equals(nameID)) continue - if (record.platformID.equals(1) && record.encodingID.equals(0)) { - return record.nameBuffer.toString(Charsets.UTF_8) - } else if (record.platformID.equals(3) && record.encodingID.equals(1)) { - return record.nameBuffer.toString(Charsets.UTF_16BE) - } + fun GetNameById(nameId: Int = 1): String { + for (Temp in directorys) { + if (Temp.tag != "name") continue; + FontReader.Index = Temp.offset.toInt(); + break; + } + for (record in name.records) { + if (record.nameID.toInt() != nameId) continue; + FontReader.Index = FontReader.Index + (name.stringOffset + record.offset).toInt(); + return FontReader.ReadStrings(record.length.toInt(), if (record.platformID.toInt() == 1) Charsets.UTF_8 else Charsets.UTF_16BE); } - return "error" + return "error"; } + var pps = arrayListOf>(Pair(3u, 10u), Pair(0u, 4u), Pair(3u, 1u), Pair(1u, 0u), Pair(0u, 3u), Pair(0u, 1u)) /** - * 获取字体轮廓 (fontCode:十进制Unicode字符编码) - */ - fun GetGlyf(fontCode: Int): ArrayList { - var fontIndex = -1; // 轮廓索引 - val fmt4 = cmap.tables.getOrDefault(4, null) - if (fmt4 != null) { - val fmt = (fmt4 as Format4) - val endCode = fmt.endCode - for (i in endCode.indices) { - if (endCode[i] == fontCode) { - fontIndex = i - break + * 使用Unicode值查找轮廓索引 + */ + private fun GetGlyfIndex(code: Int): Long { + var fmtKey: UInt = 0u; + for (record in cmap.records) { + for (item in pps) { + if ((item.first == record.platformID) && (item.second == record.encodingID)) { + fmtKey = record.offset; + break; } } - if (fontIndex == -1) fontIndex = 0 - else if (fontCode < fmt.startCode[fontIndex]) fontIndex = 0 - else { - if (fmt.idRangeOffset[fontIndex] != 0) { - fontIndex = - fmt.glyphIdArray[fontCode - fmt.startCode[fontIndex] + fmt.idRangeOffset[fontIndex].ushr(1) - (fmt.idRangeOffset.size - fontIndex)].toInt() - } else fontIndex = fontCode + fmt.idDelta[fontIndex] - fontIndex = fontIndex and 0xFFFF + if (fmtKey > 0u) break; + } + if (fmtKey == 0u) return 0; + + var glyfID: Long = 0; + if (cmap.tables[fmtKey] is CmapFormat0) { + val tab = cmap.tables[fmtKey] as CmapFormat0 + if (code >= tab.glyphIdArray.size) glyfID = 0; + else glyfID = tab.glyphIdArray[code].toLong(); + } else if (cmap.tables[fmtKey] is CmapFormat4) { + val tab = cmap.tables[fmtKey] as CmapFormat4 + if (code > tab.endCode.last().toInt()) return 0; + // 二分法查找数值索引 + var start = 0 + var middle: Int + var end = tab.endCode.size - 1; + while (start + 1 != end) { + middle = (start + end) / 2; + if (tab.endCode[middle] <= code.toUInt()) start = middle; + else end = middle; + } + if (tab.endCode[start] < code.toUInt()) ++start; + if (code.toUInt() < tab.startCode[start]) return 0; + glyfID = if (tab.idRangeOffset[start].toInt() != 0) { + tab.glyphIdArray[code - tab.startCode[start].toInt() + (tab.idRangeOffset[start].toInt() / 2) - (tab.idRangeOffset.size - start)].toLong(); + } else (code + tab.idDelta[start]).toLong(); + glyfID = glyfID.and(0xFFFF) + } else if (cmap.tables[fmtKey] is CmapFormat6) { + val tab = cmap.tables[fmtKey] as CmapFormat6 + val index = code - tab.firstCode.toInt(); + glyfID = if (index < 0 || index >= tab.glyphIdArray.size) 0 else tab.glyphIdArray[index].toLong() + } else if (cmap.tables[fmtKey] is CmapFormat12) { + val tab = (cmap.tables[fmtKey] as CmapFormat12); + if (code > tab.groups.last().second.toInt()) return 0; + // 二分法查找数值索引 + var start = 0 + var middle: Int + var end = tab.groups.size - 1; + while (start + 1 != end) { + middle = (start + end) / 2; + if (tab.groups[middle].first.toInt() <= code) start = middle; + else end = middle; } - } else { - val fmt0 = cmap.tables.getOrDefault(0, null) - if (fmt0 != null) { - val fmt = (fmt0 as Format0) - val glyphs = fmt.glyphIdArray - fontIndex = if (fontCode > glyphs.size) 0 else glyphs[fontCode].toInt() + if (tab.groups[start].first.toInt() <= code && code <= tab.groups[start].second.toInt()) { + glyfID = (tab.groups[start].third.toInt() + code - tab.groups[start].first.toInt()).toLong(); } } + return glyfID + } - val glyph = ArrayList() - if (fontIndex > -1) { - // 将字体的轮廓坐标合并到一起返回,方便使用 - glyph.addAll(glyf[fontIndex].xCoordinates) - glyph.addAll(glyf[fontIndex].yCoordinates) + /** + * 使用轮廓数据获取Unicode值 + */ + public fun GetCodeByGlyf(inputGlyf:List): Int { + var unicodeVal = 0; + if(inputGlyf.isEmpty()) return 0; + for(g in unicodeMap) + { + if (inputGlyf.size != g.value.size) continue; + var isFound = true; + for(i in inputGlyf.indices) + { + if (inputGlyf[i] != g.value[i]) { + isFound = false; + break; + } + } + if (isFound) unicodeVal = g.key; } - return glyph + return unicodeVal; } - /*** - * 获取字体轮廓 (fontCode:单个String字符) + /** + * 使用Unicode值获取轮廓数据 */ - fun GetGlyf(fontCode: String): ArrayList { - return GetGlyf(fontCode[0].toInt()) + public fun GetGlyfByCode(code:Int): ArrayList + { + if(code <= 0) return ArrayList() + return unicodeMap.getOrDefault(code, ArrayList()) } } \ No newline at end of file