实现TTF字体的(Unicode<->轮廓)双向查询功能

pull/487/head
Antecer 4 years ago
parent 1ccbab9e5f
commit 6ead673471
  1. 721
      app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt

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