btex stuffs

This commit is contained in:
minjaesong
2024-04-24 01:13:46 +09:00
parent e2a87d0e14
commit 76dd9a98e4
18 changed files with 602 additions and 182 deletions

View File

@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.App
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarumsansbitmap.MovableType
/**
* Created by minjaesong on 2023-10-28.
@@ -17,7 +18,9 @@ class BTeXDocument {
var papersize = "standard"
var pageWidth = 420
var pageHeight = 25 * 24
var lineHeight = 24
var pageLines = 25
var pageHeight = pageLines * lineHeight
companion object {
val DEFAULT_PAGE_BACK = Color(0xe1e1d7ff.toInt())
@@ -61,13 +64,18 @@ class BTeXPage(
}
}
data class MovableTypeDrawCall(val movableType: MovableType, val rowStart: Int, val rowEnd: Int) {
fun draw(batch: SpriteBatch, x: Float, y: Float) {
movableType.draw(batch, x, y, rowStart, rowEnd)
}
}
class BTeXDrawCall(
val posX: Int,
val posY: Int,
val theme: String,
val colour: Color,
val font: BitmapFont,
val text: String? = null,
val text: MovableTypeDrawCall? = null,
val texture: TextureRegion? = null,
) {
@@ -82,7 +90,7 @@ class BTeXDrawCall(
batch.color = colour
if (text != null && texture == null) {
font.draw(batch, text, px, py)
text.draw(batch, px, py)
}
else if (text == null && texture != null) {
batch.draw(texture, px, py)

View File

@@ -4,13 +4,16 @@ import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Color
import net.torvald.terrarum.App
import net.torvald.terrarum.btex.BTeXDocument
import net.torvald.terrarum.btex.BTeXDrawCall
import net.torvald.terrarum.gameitems.ItemID
import org.xml.sax.Attributes
import org.xml.sax.InputSource
import org.xml.sax.helpers.DefaultHandler
import java.io.*
import java.util.*
import javax.xml.parsers.SAXParserFactory
import kotlin.reflect.KFunction
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.full.findAnnotation
/**
* Created by minjaesong on 2023-10-28.
@@ -27,18 +30,27 @@ object BTeXParser {
return doc
}
private class BTeXHandler(val doc: BTeXDocument) : DefaultHandler() {
operator fun invoke(string: String): BTeXDocument {
val doc = BTeXDocument()
val parser = SAXParserFactory.newDefaultInstance().newSAXParser()
parser.parse(InputSource(StringReader(string)), BTeXHandler(doc))
return doc
}
internal class BTeXHandler(val doc: BTeXDocument) : DefaultHandler() {
private val DEFAULT_FONTCOL = Color(0x222222ff)
private val LINE_HEIGHT = 24
private val LINE_HEIGHT = doc.lineHeight
private var cover = ""
private var inner = ""
private var papersize = ""
private var def = ""
private var pageWidth = 420
private var pageLines = 25
private var pageHeight = 25 * LINE_HEIGHT
private var btexOpened = false
private var pageWidth = doc.pageWidth
private var pageLines = doc.pageLines
private var pageHeight = doc.pageHeight
private val blockLut = HashMap<String, ItemID>()
@@ -51,66 +63,93 @@ object BTeXParser {
private var typeX = 0
private var typeY = 0
private val elemOpeners: HashMap<String, KFunction<*>> = HashMap()
private val elemClosers: HashMap<String, KFunction<*>> = HashMap()
init {
BTeXHandler::class.declaredFunctions.filter { it.findAnnotation<OpenTag>() != null }.forEach {
println("Tag opener: ${it.name}")
elemOpeners[it.name] = it
}
BTeXHandler::class.declaredFunctions.filter { it.findAnnotation<CloseTag>() != null }.forEach {
println("Tag closer: ${it.name}")
elemClosers[it.name] = it
}
}
private fun printdbg(message: String?) {
val CSI = "\u001B[32m"
val timeNow = System.currentTimeMillis()
val ss = (timeNow / 1000) % 60
val mm = (timeNow / 60000) % 60
val hh = (timeNow / 3600000) % 24
val ms = timeNow % 1000
val out = this.javaClass.getSimpleName()
val prompt = CSI + String.format("%02d:%02d:%02d.%03d [%s]%s ", hh, mm, ss, ms, out, App.csi0)
if (message == null) {
println(prompt + "null")
}
else {
val indentation = " ".repeat(out.length + 16)
val msgLines: Array<String> = message.toString().split("\\n".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
for (i in msgLines.indices) {
println((if (i == 0) prompt else indentation) + msgLines[i])
}
}
}
override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) {
val tag = qName; if (tagStack.isEmpty() && tag != "btex") throw BTeXParsingException("Document is not btex")
val tag = qName; if (tagStack.isEmpty() && tag.lowercase() != "btexdoc") throw BTeXParsingException("Document is not BTeX")
tagStack.add(tag)
val attribs = HashMap<String, String>().also {
it.putAll((0 until attributes.length).map { attributes.getQName(it) to attributes.getValue(it) })
}
val mode = tagStack.getOrNull(1)
val theTag = tag.uppercase()
when (tag) {
"btex" -> {
if (attribs.containsKey("def"))
def = attribs["def"]!!
else {
cover = attribs["cover"] ?: "printout"
inner = attribs["inner"] ?: "standard"
papersize = attribs["papersize"] ?: "standard"
pageWidth = pageWidthMap[papersize]!!
pageLines = pageHeightMap[papersize]!!
pageHeight = pageLines * LINE_HEIGHT
doc.pageWidth = pageWidth
doc.pageHeight = pageHeight
elemOpeners["processElem$theTag"].let {
if (it == null)
System.err.println("Unknown tag: $theTag")
else {
try {
it.call(this, this, doc, theTag, uri, attribs)
}
catch (e: Throwable) {
throw BTeXParsingException(e.stackTraceToString())
}
}
"pair" -> {
if (tagStack.size == 3 && mode == "blocklut") {
blockLut[attribs["key"]!!] = attribs["value"]!!
}
else {
throw BTeXParsingException("<pair> used outside of <blocklut>")
}
}
"span" -> {
attribs["span"]?.let {
spanColour = it
}
}
}
// printdbg("Start element \t($tag)")
}
override fun endElement(uri: String, localName: String, qName: String) {
tagStack.removeLast()
val popped = tagStack.removeLast()
when (qName) {
"span" -> {
spanColour = null
val theTag = qName.uppercase()
elemClosers["closeElem$theTag"].let {
try {
it?.call(this, this, doc, theTag, uri)
}
catch (e: Throwable) {
throw BTeXParsingException(e.stackTraceToString())
}
}
// printdbg(" End element \t($popped)")
}
override fun characters(ch: CharArray, start: Int, length: Int) {
val str = String(ch.sliceArray(start until start+length)).replace('\n',' ').replace(Regex(" +"), " ").trim()
val font = getFont()
advanceCursorPre(font.getWidth(str), 0)
doc.appendDrawCall(BTeXDrawCall(typeX, typeY, inner, getSpanColour(), font, str))
advanceCursorPost(font.getWidth(str), 0)
val str =
String(ch.sliceArray(start until start + length)).replace('\n', ' ').replace(Regex(" +"), " ")//.trim()
if (str.isNotBlank()) {
printdbg("Characters \t\"$str\"")
}
}
private fun advanceCursorPre(w: Int, h: Int) {
@@ -149,8 +188,99 @@ object BTeXParser {
private val pageHeightMap = hashMapOf(
"standard" to 25
)
@OpenTag // reflective access is impossible with 'private'
fun processElemBTEXDOC(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
if (handler.btexOpened) {
throw BTeXParsingException("BTEXDOC tag has already opened")
}
if (attribs.containsKey("def"))
handler.def = attribs["def"]!!
else {
handler.cover = attribs["cover"] ?: "printout"
handler.inner = attribs["inner"] ?: "standard"
handler.papersize = attribs["papersize"] ?: "standard"
//change the "default values" of the document
handler.pageWidth = pageWidthMap[papersize]!!
handler.pageLines = pageHeightMap[papersize]!!
handler.pageHeight = pageLines * LINE_HEIGHT
doc.pageWidth = pageWidth
doc.pageHeight = pageHeight
}
handler.btexOpened = true
printdbg("BTeX document: def=${handler.def}, cover=${handler.cover}, inner=${handler.inner}, papersize=${handler.papersize}, dim=${handler.pageWidth}x${handler.pageHeight} (${handler.pageLines} lines)")
}
@OpenTag // reflective access is impossible with 'private'
fun processElemPAIR(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
if (tagStack.size == 3 && tagStack.getOrNull(1) == "blocklut") {
blockLut[attribs["key"]!!] = attribs["value"]!!
}
else {
throw BTeXParsingException("<pair> used outside of <blocklut>")
}
}
@OpenTag // reflective access is impossible with 'private'
fun processElemSPAN(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
attribs["span"]?.let {
spanColour = it
}
}
@OpenTag // reflective access is impossible with 'private'
fun processElemTABLEOFCONTENTS(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
// TODO add post-parsing hook to the handler
}
@OpenTag // reflective access is impossible with 'private'
fun processElemBR(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
}
@OpenTag // reflective access is impossible with 'private'
fun processElemNEWPAGE(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
}
@OpenTag // reflective access is impossible with 'private'
fun processElemP(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
}
@CloseTag // reflective access is impossible with 'private'
fun closeElemP(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String) {
}
@OpenTag // reflective access is impossible with 'private'
fun processElemARST(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
}
@CloseTag // reflective access is impossible with 'private'
fun closeElemSPAN(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String) {
spanColour = null
}
}
private annotation class OpenTag
private annotation class CloseTag
}