o-util-bdf-parser.1.1.1.source-code.parser.kt Maven / Gradle / Ivy
The newest version!
package top.e404.dbf
import java.io.BufferedReader
import java.io.File
object BdfParser {
/**
* 文件开始, `BDF`标准版本号, 如`2.1`
*/
const val START_FONT = "STARTFONT"
/**
* 文件结束
*/
const val END_FONT = "ENDFONT"
/**
* 注释
*/
const val COMMENT = "COMMENT"
/**
* 字体名
*/
const val FONT = "FONT"
/**
* 字形尺寸(以`PT`计) 分辨率x(以`DPI`计) 分辨率y(以`DPI`计)
*/
const val SIZE = "SIZE"
/**
* 字体包围盒宽度 字体包围盒高度 分辨率x 分辨率y, 单位像素
*/
const val FONT_BOUNDING_BOX = "FONTBOUNDINGBOX"
/**
* 整型值, 缺省值`0`, 三种可选值
*
* - `0` - 从左向右
* - `1` - 自上而下
* - `2` - 兼有
*
* 若该参数取1, 则`DWIDTH`和`SWIDTH`两关键字可选。
*/
const val METRICS_SET = "METRICSSET"
/**
* 可选属性
*/
const val START_PROPERTIES = "STARTPROPERTIES"
/**
* 字符数量
*/
const val CHARS = "CHARS"
/**
* 开始字符
*/
const val START_CHAR = "STARTCHAR"
/**
* 开始字符点阵
*/
const val BITMAP = "BITMAP "
/**
* 结束字符
*/
const val END_CHAR = "ENDCHAR"
/**
* 结束可选属性
*/
const val END_PROPERTIES = "ENDPROPERTIES"
fun parse(file: File): BdfFont {
return file.bufferedReader().use(::parse)
}
fun parse(reader: BufferedReader): BdfFont {
val header = parseHeader(reader)
val map = HashMap(header.count)
repeat(header.count) {
val bdfChar = parseFont(reader)
map[bdfChar.encoding] = bdfChar
}
return BdfFont(header, map)
}
fun parseHeader(reader: BufferedReader): BdfHeader {
val version = reader.readLine().removePrefix(START_FONT).trim()
val map = mutableMapOf()
var properties = mutableMapOf()
val count: Int
while (true) {
var line = reader.readLine()
while (line.startsWith(COMMENT)) line = reader.readLine()
// properties
if (line.startsWith(START_PROPERTIES, true)) {
val size = line.substringAfterLast(" ").toInt()
properties = HashMap(size)
repeat(size) {
var property = reader.readLine()
while (property.startsWith(COMMENT)) property = reader.readLine()
val index = property.indexOf(" ")
require(index != -1)
properties[property.substring(0, index)] = property.substring(index + 1)
}
require(reader.readLine() == END_PROPERTIES)
}
val index = line.indexOf(' ')
require(index != -1)
val key = line.substring(0, index)
if (key == CHARS) {
count = line.substring(index + 1).toInt()
break
}
map[key] = line.substring(index + 1)
}
return BdfHeader(
version = version,
font = map[FONT]!!,
size = BdfSize(map[SIZE]!!),
boundingBox = FontBoundingBox(map[FONT_BOUNDING_BOX]!!),
properties = properties,
count = count
)
}
fun parseFont(reader: BufferedReader): BdfChar {
val map = mutableMapOf()
val split = reader.readLine().split(" ")
require(split.size == 2)
require(split[0] == START_CHAR)
val unicode = split[1]
var line: String
while (true) {
line = reader.readLine()
if (line == BITMAP) break
val index = line.indexOf(' ')
require(index != -1) { "unexpected line: $line, unicode: $unicode" }
val key = line.substring(0, index)
map[key] = line.substring(index + 1)
}
val bbx = FontBoundingBox(map["BBX"]!!)
// bitmap
val lines = (1..bbx.h).map {
reader.readLine()
}
line = reader.readLine()
require(line == END_CHAR) { "unexpected line: $line, unicode: $unicode" }
return BdfChar(
unicode,
map["ENCODING"]!!.toInt(),
map["SWIDTH"]!!.split(" ").let { it[0].toInt() to it[1].toInt() },
map["DWIDTH"]!!.split(" ").let { it[0].toInt() to it[1].toInt() },
bbx,
BitMatrix(lines)
)
}
}
data class BdfFont(
val header: BdfHeader,
val chars: Map
) {
private companion object {
fun String.toUnicode(ascii: Boolean) = buildString {
[email protected] { append(it.toUnicode(ascii)) }
}
fun Char.toUnicode(ascii: Boolean): String {
if (code in 0..127 && !ascii) return toString()
return "\\u${Integer.toHexString(code).padStart(4, '0')}"
}
}
fun getBitmap(string: String) = chars[string[0].code]
fun getBitmaps(string: String) = string.toCharArray().map { chars[it.code] }
}
data class BdfHeader(
val version: String,
val font: String,
val size: BdfSize,
val boundingBox: FontBoundingBox,
val properties: Map,
val count: Int,
)
/**
* 尺寸数据
*
* @property size 字形尺寸(以PT计)
* @property x 分辨率x(以DPI计)
* @property y 分辨率y(以DPI计)
*/
data class BdfSize(
val size: Int,
val x: Int,
val y: Int
) {
companion object {
operator fun invoke(string: String): BdfSize {
val split = string.split(" ")
require(split.size == 3)
return BdfSize(split[0].toInt(), split[1].toInt(), split[2].toInt())
}
}
}
/**
* 字体包围盒宽度 字体包围盒高度 分辨率x 分辨率y, 单位像素
*
* @property w 字体边界宽度
* @property h 字体边界高度
* @property x 字体边界x
* @property y 字体边界y
*/
data class FontBoundingBox(
val w: Int,
val h: Int,
val x: Int,
val y: Int,
) {
companion object {
operator fun invoke(string: String): FontBoundingBox {
val split = string.split(" ")
require(split.size == 4)
return FontBoundingBox(split[0].toInt(), split[1].toInt(), split[2].toInt(), split[3].toInt())
}
}
}
class BdfChar(
val unicode: String,
val encoding: Int,
val sWidth: Pair,
val dWidth: Pair,
val bbx: FontBoundingBox,
val bitMatrix: BitMatrix
)
fun BitMatrix.printMatrix() {
for (y in 0 until height) {
for (x in 0 until width) {
print(if (get(x, y)) "⬛" else "⬜️")
}
println()
}
}
fun printByte(b: Int) = buildString {
for (i in 7 downTo 0) {
append(b shr i and 1)
}
}
fun main() {
val bdfFont = BdfParser.parse(File("F:\\D\\unifont-15.0.03.bdf"))
bdfFont.getBitmaps("张").forEach {
if (it == null) {
println(null)
return@forEach
}
it.bitMatrix.printMatrix()
println()
it.bitMatrix[0, 1] = false
it.bitMatrix[1, 1] = false
it.bitMatrix[2, 1] = false
it.bitMatrix.printMatrix()
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy