btex using xml wip

This commit is contained in:
minjaesong
2023-10-28 16:51:23 +09:00
parent 76d6579ce9
commit 991a16e6a7
6 changed files with 518 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
<btex cover="hardcover" inner="standard" papersize="standard">
<cover>
<title>The Way to Mastery of Lorem Ipsum<br />Or, How To Write and Publish a Book</title>
<author>Terran Publishing</author>
<edition>Test Edition</edition>
</cover>
<toc><tableofcontents /></toc>
<manuscript>
<chapter>What Is a Book</chapter>
<p>This example book is designed to give you the exampe of the Book Language.</p>
<section>What Really Is a Book</section>
<p>A book is a collection of texts printed in a special way that allows them to be read easily, with enumerable pages and insertion of other helpful resources, such as illustrations and hyperlinks.</p>
<newpage />
<fullpagebox>
<span colour="grey">
<p>this page is intentionally left blank</p>
</span>
</fullpagebox>
<chapter>Writing Book Using Pen and Papers</chapter>
<p>If you open a book on a writing table, you will be welcomed with a toolbar used to put other book elements, such as chapters, sections.</p>
<chapter>Writing Book Using Typewriter</chapter>
<p>Typewriters can only write single style of font, therefore chapters and sections are not available.</p>
<chapter>Writing Book using Computer</chapter>
<p>Writing book using a computer requires a use of the Book Typesetting Engine Extended, or <BTeX /></p>
<section>Full Control of the Shape</section>
<p>With <BTeX /> you can fully control how your publishing would look like, from a pile of papers that look like they have been typed out using typewriter, a pile of papers but a fully-featured printouts that have illustrations in it, to a fully-featured hardcover book.</p>
<p>This style is controlled using the <code>cover</code> attribute on the root tag, with following values: <code>typewriter</code>, <code>printout</code>, <code>hardcover</code></p>
<p>Typewriter and Printout are considered not bound and readers will only see one page at a time, while Hardcover is considered bound and two pages are presented to the readers.</p>
</manuscript>
</btex>

View File

@@ -0,0 +1,60 @@
\usestyle{examination}
\begin{blocklut}
\0\{basegame:0}
\1\{basegame:32}
\100\{wall@basegame:32}
\end{blocklut}
\begin{examination}
\section{Introduction}
\begin{tiles}{5x4}
\terr{
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0
}
\wall{
0,1,1,1,0,
1,1,1,1,1,
1,1,1,1,1,
0,1,1,1,0
}
\end{tiles}
This is the first page of the Examination.
Examination is a way to instruct the player certain gameplay concepts.
The system is popularised by the Minecraft Mod called ``Create'', of which it was called Pondering.
Terrarum borrows the concept and implements it using its own language.
\section{Language}
\begin{tiles}{5x4}
\terr{
0,1,1,1,0,
1,0,1,0,1,
1,0,1,0,1,
0,1,1,1,0
}
\wall{
0,1,1,1,0,
1,1,1,1,1,
1,1,1,1,1,
0,1,1,1,0
}
\end{tiles}
Examination relies on the script written in \BTeX.
\BTeX\ is a \TeX-inspired language to write texts to be displayed onto the game, one example being a book.
Examination is a special format of the "book" that operates on the same book system.
\end{examination}

View File

@@ -0,0 +1,57 @@
<btex def="examination">
<blocklut>
<pair key="0" value="basegame:0" />
<pair key="1" value="basegame:32" />
</blocklut>
<manuscript>
<section>Introduction</section>
<tiles w="5" h="4">
<terr>
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0
</terr>
<wall>
0,1,1,1,0,
1,1,1,1,1,
1,1,1,1,1,
0,1,1,1,0
</wall>
</tiles>
<p>This is the first page of the Examination.</p>
<p>Examination is a way to instruct the player certain gameplay concepts.</p>
<p>The system is popularised by the Minecraft Mod called “Create”, of which it was called Pondering.</p>
<p>Terrarum borrows the concept and implements it using its own language.</p>
<section>Language</section>
<tiles w="5" h="4">
<terr>
0,1,1,1,0,
1,0,1,0,1,
1,0,1,0,1,
0,1,1,1,0
</terr>
<wall>
0,1,1,1,0,
1,1,1,1,1,
1,1,1,1,1,
0,1,1,1,0
</wall>
</tiles>
<p>Examination relies on the script written in <BTeX />.</p>
<p><BTeX /> is a XML-like markup language to write texts to be displayed onto the game, one example being a book.</p>
<p>Examination is a special format of the “book” that operates on the same book system.</p>
</manuscript>
</btex>

View File

@@ -0,0 +1,93 @@
package net.torvald.terrarum.btex
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.BitmapFont
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
/**
* Created by minjaesong on 2023-10-28.
*/
class BTeXDocument {
var context = "tome" // tome (cover=hardcover), sheets (cover=typewriter or cover=printout), examination (def=examination)
var font = "default" // default or typewriter
var inner = "standard"
var papersize = "standard"
var pageWidth = 420
var pageHeight = 25 * 24
companion object {
val DEFAULT_PAGE_BACK = Color(0xe1e1d7ff.toInt())
val DEFAULT_PAGE_FORE = Color(0x131311ff)
}
private val pages = ArrayList<BTeXPage>()
fun addNewPage(back: Color = DEFAULT_PAGE_BACK) {
pages.add(BTeXPage(back, pageWidth, pageHeight))
}
fun appendDrawCall(drawCall: BTeXDrawCall) {
pages.last().appendDrawCall(drawCall)
}
fun render(batch: SpriteBatch, page: Int, x: Int, y: Int) {
pages[page].render(batch, x, y)
}
}
class BTeXPage(
val back: Color,
val width: Int,
val height: Int,
) {
private val drawCalls = ArrayList<BTeXDrawCall>()
fun appendDrawCall(drawCall: BTeXDrawCall) {
drawCalls.add(drawCall)
}
fun render(batch: SpriteBatch, x: Int, y: Int) {
batch.color = back
Toolkit.fillArea(batch, x, y, width, height)
drawCalls.forEach {
it.draw(batch, x, y)
}
}
}
class BTeXDrawCall(
val posX: Int,
val posY: Int,
val theme: String,
val colour: Color,
val font: BitmapFont,
val text: String? = null,
val texture: TextureRegion? = null,
) {
fun draw(batch: SpriteBatch, x: Int, y: Int) {
val px = (posX + x).toFloat()
val py = (posY + y).toFloat()
if (theme == "code") {
// todo draw code background
}
batch.color = colour
if (text != null && texture == null) {
font.draw(batch, text, px, py)
}
else if (text == null && texture != null) {
batch.draw(texture, px, py)
}
else throw Error("Text and Texture are both non-null")
}
}

View File

@@ -0,0 +1,159 @@
package net.torvald.btex
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.helpers.DefaultHandler
import java.io.*
import java.util.*
import javax.xml.parsers.SAXParserFactory
/**
* Created by minjaesong on 2023-10-28.
*/
object BTeXParser {
operator fun invoke(file: FileHandle) = invoke(file.file())
operator fun invoke(file: File): BTeXDocument {
val doc = BTeXDocument()
val parser = SAXParserFactory.newDefaultInstance().newSAXParser()
val stream = FileInputStream(file)
parser.parse(stream, BTeXHandler(doc))
return doc
}
private class BTeXHandler(val doc: BTeXDocument) : DefaultHandler() {
private val DEFAULT_FONTCOL = Color(0x222222ff)
private val LINE_HEIGHT = 24
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 val blockLut = HashMap<String, ItemID>()
private val tagStack = ArrayList<String>() // index zero should be "btex"
private var spanColour: String? = null
private var typeX = 0
private var typeY = 0
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")
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)
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
}
}
"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
}
}
}
}
override fun endElement(uri: String, localName: String, qName: String) {
tagStack.removeLast()
when (qName) {
"span" -> {
spanColour = null
}
}
}
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)
}
private fun advanceCursorPre(w: Int, h: Int) {
if (typeX + w > pageWidth) {
typeY += LINE_HEIGHT
typeX = 0
}
if (typeY + h > pageHeight) {
typeX = 0
typeY = 0
doc.addNewPage()
}
}
private fun advanceCursorPost(w: Int, h: Int) {
typeX += w
typeY += h
}
private fun getFont() = when (cover) {
"typewriter" -> TODO()
else -> App.fontGame
}
private fun getSpanColour(): Color = spanColourMap.getOrDefault(spanColour, DEFAULT_FONTCOL)
private val spanColourMap = hashMapOf(
"grey" to Color.LIGHT_GRAY
)
private val pageWidthMap = hashMapOf(
"standard" to 420
)
private val pageHeightMap = hashMapOf(
"standard" to 25
)
}
}
class BTeXParsingException(s: String) : RuntimeException(s) {
}

View File

@@ -0,0 +1,100 @@
package net.torvald.terrarum.tests
import org.xml.sax.Attributes
import org.xml.sax.HandlerBase
import org.xml.sax.helpers.DefaultHandler
import java.io.ByteArrayInputStream
import java.io.InputStream
import javax.xml.parsers.SAXParserFactory
/**
* Created by minjaesong on 2023-10-28.
*/
fun main() {
val csiR = "\u001B[31m"
val csiG = "\u001B[32m"
val csi0 = "\u001B[m"
val tex = """<btex cover="hardcover" inner="standard" papersize="standard">
<cover>
<title>The Way to Mastery of Lorem Ipsum<br />Or, How To Write and Publish a Book</title>
<author>Terran Publishing</author>
<edition>Test Edition</edition>
</cover>
<toc><tableofcontents /></toc>
<manuscript>
<chapter>What Is a Book</chapter>
<p>This example book is designed to give you the exampe of the Book Language.</p>
<section>What Really Is a Book</section>
<p>A book is a collection of texts printed in a special way that allows them to be read easily, with
enumerable pages and insertion of other helpful resources, such as illustrations and hyperlinks.</p>
<newpage />
<fullpagebox>
<span colour="grey">
<p>this page is intentionally left blank</p>
</span>
</fullpagebox>
<chapter>Writing Book Using Pen and Papers</chapter>
<p>If you open a book on a writing table, you will be welcomed with a toolbar used to put other book
elements, such as chapters, sections.</p>
<chapter>Writing Book Using Typewriter</chapter>
<p>Typewriters can only write single style of font, therefore chapters and sections are not available.</p>
<chapter>Writing Book using Computer</chapter>
<p>Writing book using a computer requires a use of the Book Typesetting Engine Extended, or <BTeX /></p>
<section>Full Control of the Shape</section>
<p>With <BTeX /> you can fully control how your publishing would look like, from a pile of papers that
look like they have been typed out using typewriter, a pile of papers but a fully-featured printouts that
have illustrations in it, to a fully-featured hardcover book.</p>
<p>This style is controlled using the <code>cover</code> attribute on the root tag, with following
values: <code>typewriter</code>, <code>printout</code>, <code>hardcover</code></p>
<p>Typewriter and Printout are considered not bound and readers will only see one page at a time,
while Hardcover is considered bound and two pages are presented to the readers.</p>
</manuscript>
</btex>
"""
val parser = SAXParserFactory.newDefaultInstance().newSAXParser()
val stream: InputStream = ByteArrayInputStream(tex.encodeToByteArray())
val hb = object : DefaultHandler() {
override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) {
println(" $csiG$qName$csi0 ${(0 until attributes.length).map { "${attributes.getQName(it)}=${attributes.getValue(it)}" }}")
}
override fun endElement(uri: String, localName: String, qName: String) {
println("$csiG/$qName$csi0")
}
override fun characters(ch: CharArray, start: Int, length: Int) {
val str = String(ch.sliceArray(start until start+length)).replace('\n',' ').replace(Regex(" +"), " ").trim()
if (str.isNotBlank()) {
println("$str|")
}
}
}
parser.parse(stream, hb)
}