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