commit
21ff748eb2
@ -0,0 +1,483 @@ |
|||||||
|
package io.legado.app.model.analyzeRule |
||||||
|
|
||||||
|
import io.legado.app.help.JsExtensions |
||||||
|
import kotlin.experimental.and |
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes |
||||||
|
/** |
||||||
|
* 解析TTF字体 |
||||||
|
* @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> |
||||||
|
*/ |
||||||
|
class QueryTTF(var font: ByteArray) : JsExtensions { |
||||||
|
data class Index(var num: Int) |
||||||
|
|
||||||
|
class FileHeader { |
||||||
|
var majorVersion: UShort = 0u |
||||||
|
var minorVersion: UShort = 0u |
||||||
|
var numOfTables: UShort = 0u |
||||||
|
var searchRange: UShort = 0u |
||||||
|
var entrySelector: UShort = 0u |
||||||
|
var rangeShift: UShort = 0u |
||||||
|
} |
||||||
|
|
||||||
|
class TableDirectory { |
||||||
|
var tag: String = "" |
||||||
|
var checkSum: UInt = 0u |
||||||
|
var offset: UInt = 0u |
||||||
|
var length: UInt = 0u |
||||||
|
lateinit var data: ByteArray |
||||||
|
} |
||||||
|
|
||||||
|
class NameTable { |
||||||
|
var format: UShort = 0u |
||||||
|
var count: UShort = 0u |
||||||
|
var stringOffset: UShort = 0u |
||||||
|
var Records = ArrayList<NameRecord>() |
||||||
|
} |
||||||
|
|
||||||
|
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 { |
||||||
|
var majorVersion: UShort = 0u |
||||||
|
var minorVersion: UShort = 0u |
||||||
|
var fontRevision: UInt = 0u |
||||||
|
var checkSumAdjustment: UInt = 0u |
||||||
|
var magicNumber: UInt = 0u |
||||||
|
var flags: UShort = 0u |
||||||
|
var unitsPerEm: UShort = 0u |
||||||
|
var created: ULong = 0u |
||||||
|
var modified: ULong = 0u |
||||||
|
var xMin: Short = 0 |
||||||
|
var yMin: Short = 0 |
||||||
|
var xMax: Short = 0 |
||||||
|
var yMax: Short = 0 |
||||||
|
var macStyle: UShort = 0u |
||||||
|
var lowestRecPPEM: UShort = 0u |
||||||
|
var fontDirectionHint: Short = 0 |
||||||
|
var indexToLocFormat: Short = 0 |
||||||
|
var glyphDataFormat: Short = 0 |
||||||
|
} |
||||||
|
|
||||||
|
class MaxpTable { |
||||||
|
var majorVersion: UShort = 0u |
||||||
|
var minorVersion: UShort = 0u |
||||||
|
var numGlyphs: UShort = 0u // 字体中的字形数量 |
||||||
|
var maxPoints: UShort = 0u |
||||||
|
var maxContours: UShort = 0u |
||||||
|
var maxCompositePoints: UShort = 0u |
||||||
|
var maxCompositeContours: UShort = 0u |
||||||
|
var maxZones: UShort = 0u |
||||||
|
var maxTwilightPoints: UShort = 0u |
||||||
|
var maxStorage: UShort = 0u |
||||||
|
var maxFunctionDefs: UShort = 0u |
||||||
|
var maxInstructionDefs: UShort = 0u |
||||||
|
var maxStackElements: UShort = 0u |
||||||
|
var maxSizeOfInstructions: UShort = 0u |
||||||
|
var maxComponentElements: UShort = 0u |
||||||
|
var maxComponentDepth: UShort = 0u |
||||||
|
} |
||||||
|
|
||||||
|
class CmapTable { |
||||||
|
var version: UShort = 0u |
||||||
|
var numTables: UShort = 0u |
||||||
|
var records = ArrayList<EncodingRecord>() |
||||||
|
var tables = mutableMapOf<Int, Any>() |
||||||
|
} |
||||||
|
|
||||||
|
class EncodingRecord { |
||||||
|
var platformID: UShort = 0u |
||||||
|
var encodingID: UShort = 0u |
||||||
|
var offset: UInt = 0u |
||||||
|
} |
||||||
|
|
||||||
|
class Format0 { |
||||||
|
var format: UShort = 0u |
||||||
|
var length: UShort = 0u |
||||||
|
var language: UShort = 0u |
||||||
|
var glyphIdArray = ByteArray(256) |
||||||
|
} |
||||||
|
|
||||||
|
class Format4 { |
||||||
|
var format: UShort = 0u |
||||||
|
var length: UShort = 0u |
||||||
|
var language: UShort = 0u |
||||||
|
var segCountX2: UShort = 0u |
||||||
|
var searchRange: UShort = 0u |
||||||
|
var entrySelector: UShort = 0u |
||||||
|
var rangeShift: UShort = 0u |
||||||
|
lateinit var endCode: IntArray // UInt16[] |
||||||
|
var reservedPad: UShort = 0u |
||||||
|
lateinit var startCode: IntArray // UInt16[] |
||||||
|
lateinit var idDelta: ShortArray |
||||||
|
lateinit var idRangeOffset: IntArray // UInt16[] |
||||||
|
var glyphIdArray = ArrayList<UShort>() |
||||||
|
} |
||||||
|
|
||||||
|
class GlyfTable { |
||||||
|
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<Short> |
||||||
|
lateinit var yCoordinates: Array<Short> |
||||||
|
} |
||||||
|
|
||||||
|
var Header = FileHeader() |
||||||
|
|
||||||
|
var Tables = mutableMapOf<String, TableDirectory>() |
||||||
|
|
||||||
|
var name = NameTable() |
||||||
|
|
||||||
|
var head = HeadTable() |
||||||
|
|
||||||
|
var maxp = MaxpTable() |
||||||
|
|
||||||
|
var loca = ArrayList<UInt>() |
||||||
|
|
||||||
|
var cmap = CmapTable() |
||||||
|
|
||||||
|
var glyf = ArrayList<GlyfTable>() |
||||||
|
|
||||||
|
// 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 |
||||||
|
} |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// 索引变量 |
||||||
|
var index = 0 |
||||||
|
|
||||||
|
// 从索引index开始拷贝指定长度的数组 |
||||||
|
private fun ByteArray.copyOfIndex(length: Int): ByteArray { |
||||||
|
val fromIndex = index |
||||||
|
index += length |
||||||
|
return this.copyOfRange(fromIndex, fromIndex + length) |
||||||
|
} |
||||||
|
|
||||||
|
// 从索引fromIndex开始拷贝指定长度的数组 |
||||||
|
private fun ByteArray.copyOfIndex(fromIndex: Int, length: Int): ByteArray { |
||||||
|
return this.copyOfRange(fromIndex, fromIndex + length) |
||||||
|
} |
||||||
|
|
||||||
|
init { |
||||||
|
|
||||||
|
// 解析文件头 |
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
// 解析表 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) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 解析表 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() |
||||||
|
} |
||||||
|
|
||||||
|
// 解析表 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() |
||||||
|
} |
||||||
|
|
||||||
|
// 解析表 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 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 解析表 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()) |
||||||
|
} |
||||||
|
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 |
||||||
|
} |
||||||
|
} |
||||||
|
++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() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
// 获取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() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
/* |
||||||
|
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() |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
glyf.add(g) |
||||||
|
} else { |
||||||
|
// 复合字体暂不处理,以后用到再加 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取字体信息(默认获取字体名称) |
||||||
|
* @see <a href="https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids">获取详情</a> |
||||||
|
*/ |
||||||
|
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) |
||||||
|
} |
||||||
|
} |
||||||
|
return "error" |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取字体轮廓 (fontCode:十进制Unicode字符编码) |
||||||
|
*/ |
||||||
|
fun GetGlyf(fontCode: Int): ArrayList<Short> { |
||||||
|
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 |
||||||
|
} |
||||||
|
} |
||||||
|
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 |
||||||
|
} |
||||||
|
} 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() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
val glyph = ArrayList<Short>() |
||||||
|
if (fontIndex > -1) { |
||||||
|
// 将字体的轮廓坐标合并到一起返回,方便使用 |
||||||
|
glyph.addAll(glyf[fontIndex].xCoordinates) |
||||||
|
glyph.addAll(glyf[fontIndex].yCoordinates) |
||||||
|
} |
||||||
|
return glyph |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue