btex: <a> boxing better detection of hyphenation

This commit is contained in:
minjaesong
2024-05-18 15:21:44 +09:00
parent 6857ab266b
commit 82627a8ab4
3 changed files with 93 additions and 32 deletions

View File

@@ -343,6 +343,24 @@ data class BTeXClickable(
) { ) {
var deltaX = 0 var deltaX = 0
var deltaY = 0 var deltaY = 0
fun debugDrawHitboxToPixmap(pixmap: Pixmap, doc: BTeXDocument) {
pixmap.drawRectangle(
posX - HBPADH + doc.pageMarginH,
posY - HBPADV + doc.pageMarginV,
width + 2 * HBPADH,
doc.lineHeightInPx + 2 * HBPADV
)
}
fun pointInHitbox(doc: BTeXDocument, x: Int, y: Int) =
(x in posX - HBPADH + doc.pageMarginH until posX - HBPADH + doc.pageMarginH + width + 2 * HBPADH &&
y in posY - HBPADV + doc.pageMarginV until posY - HBPADV + doc.pageMarginV + doc.lineHeightInPx + 2 * HBPADV)
companion object {
private const val HBPADH = 0
private const val HBPADV = 1
}
} }
class BTeXPage( class BTeXPage(
@@ -407,7 +425,7 @@ class BTeXPage(
// debug underlines on clickableElements // debug underlines on clickableElements
clickableElements.forEach { clickableElements.forEach {
pixmap.setColor(HREF_UNDERLINE) pixmap.setColor(HREF_UNDERLINE)
pixmap.drawRectangle(it.posX + doc.pageMarginH, it.posY + doc.pageMarginV, it.width, doc.lineHeightInPx) it.debugDrawHitboxToPixmap(pixmap, doc)
} }
// print texts // print texts

View File

@@ -1653,10 +1653,10 @@ object BTeXParser {
val indexOfSequence = str.indexOfSequence(objSeq) val indexOfSequence = str.indexOfSequence(objSeq)
val theIndex = if (objectIsSplit) -(objSeq.size + 4) else indexOfSequence val theIndex = if (objectIsSplit) -(objSeq.size) else indexOfSequence
theIndex?.let { index -> // we never know which line the object appears theIndex?.let { index -> // we never know which line the object appears
val wordOffset = index + objSeq.size + 4 // must be index of starting NUL val wordOffset = index + objSeq.size // must be index of starting NUL
// target word is on the current line // target word is on the current line
if (wordOffset < str.size) { if (wordOffset < str.size) {
var wordEnd = wordOffset + 1 // will be right on the ending NUL var wordEnd = wordOffset + 1 // will be right on the ending NUL
@@ -1724,10 +1724,10 @@ object BTeXParser {
val indexOfSequence = str.indexOfSequence(objSeq) val indexOfSequence = str.indexOfSequence(objSeq)
val theIndex = if (objectIsSplit) -(objSeq.size + 4) else indexOfSequence val theIndex = if (objectIsSplit) -(objSeq.size) else indexOfSequence
theIndex?.let { index -> // we never know which line the object appears theIndex?.let { index -> // we never know which line the object appears
val wordOffset = index + objSeq.size + 4 // must be index of starting NUL val wordOffset = index + objSeq.size // must be index of starting NUL
// target word is on the current line // target word is on the current line
if (wordOffset < str.size) { if (wordOffset < str.size) {
var wordEnd = wordOffset + 1 // will be right on the ending NUL var wordEnd = wordOffset + 1 // will be right on the ending NUL
@@ -1735,6 +1735,10 @@ object BTeXParser {
while (!(wordEnd >= str.size || str[wordEnd] == OBJ)) { while (!(wordEnd >= str.size || str[wordEnd] == OBJ)) {
wordEnd++ wordEnd++
} }
// if searching finished without finding OBJ, mark it
val objectIsSplit2 = (wordEnd >= str.size)
// retrieve the actual word
val substr = CodepointSequence(str.subList(wordOffset + 1, wordEnd)) val substr = CodepointSequence(str.subList(wordOffset + 1, wordEnd))
printdbg("2HREF word: ${substr.toReadable()}") printdbg("2HREF word: ${substr.toReadable()}")
@@ -1748,7 +1752,7 @@ object BTeXParser {
} }
doc.appendClickable(doc.pages[pageNum], clickable); clickables.add(clickable) doc.appendClickable(doc.pages[pageNum], clickable); clickables.add(clickable)
objectIsSplit = false objectIsSplit = objectIsSplit2
} }
// target word is on the next line (probably) // target word is on the next line (probably)
else { else {
@@ -2083,7 +2087,11 @@ object BTeXParser {
if (pattern.isEmpty()) if (pattern.isEmpty())
throw IllegalArgumentException("Pattern is empty") throw IllegalArgumentException("Pattern is empty")
if (this.isEmpty()) if (this.isEmpty())
throw IllegalArgumentException("Pattern is empty") throw IllegalArgumentException("String is empty")
// pattern cannot exist because the string is shorter than the pattern
if (this.size < pattern.size)
return null
// next[i] stores the index of the next best partial match // next[i] stores the index of the next best partial match
val next = IntArray(pattern.size + 1) val next = IntArray(pattern.size + 1)

View File

@@ -8,17 +8,20 @@ import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.OrthographicCamera import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.ShaderProgram import com.badlogic.gdx.graphics.glutils.ShaderProgram
import net.torvald.btex.BTeXDocViewer import net.torvald.btex.BTeXDocViewer
import net.torvald.btex.BTeXParser import net.torvald.btex.BTeXParser
import net.torvald.terrarum.* import net.torvald.terrarum.FlippingSpriteBatch
import net.torvald.terrarum.btex.BTeXDocument import net.torvald.terrarum.btex.BTeXDocument
import net.torvald.terrarum.gdxClearAndEnableBlend
import net.torvald.terrarum.imagefont.TinyAlphNum import net.torvald.terrarum.imagefont.TinyAlphNum
import net.torvald.terrarum.inUse
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.ui.Toolkit
import net.torvald.unicode.EMDASH import net.torvald.unicode.EMDASH
import java.io.File import java.util.concurrent.atomic.AtomicReference
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@@ -27,8 +30,8 @@ import kotlin.system.measureTimeMillis
*/ */
class BTeXTest : ApplicationAdapter() { class BTeXTest : ApplicationAdapter() {
// val filePath = "btex.xml" val filePath = "btex.xml"
val filePath = "btex_ko.xml" // val filePath = "btex_ko.xml"
// val filePath = "test.xml" // val filePath = "test.xml"
// val filePath = "literature/en/daniel_defoe_robinson_crusoe.xml" // val filePath = "literature/en/daniel_defoe_robinson_crusoe.xml"
// val filePath = "literature/ruRU/anton_chekhov_palata_no_6.xml" // val filePath = "literature/ruRU/anton_chekhov_palata_no_6.xml"
@@ -47,6 +50,10 @@ class BTeXTest : ApplicationAdapter() {
"bucks" to "121687" "bucks" to "121687"
) )
private val errorInfo = AtomicReference<Throwable?>().also {
it.set(null)
}
override fun create() { override fun create() {
Lang Lang
TinyAlphNum TinyAlphNum
@@ -65,25 +72,31 @@ class BTeXTest : ApplicationAdapter() {
if (!isBookFinalised) { if (!isBookFinalised) {
Thread { Thread {
measureTimeMillis { try {
val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath"), varMap) measureTimeMillis {
document = f.first val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath"), varMap)
documentHandler = f.second document = f.first
}.also { documentHandler = f.second
println("Time spent on typesetting [ms]: $it") }.also {
} println("Time spent on typesetting [ms]: $it")
}
measureTimeMillis { measureTimeMillis {
document.finalise(true) document.finalise(true)
}.also { }.also {
println("Time spent on finalising [ms]: $it") println("Time spent on finalising [ms]: $it")
} }
/*measureTimeMillis { /*measureTimeMillis {
document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btxbook")}")) document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btxbook")}"))
}.also { }.also {
println("Time spent on serialisation [ms]: $it") println("Time spent on serialisation [ms]: $it")
}*/ }*/
}
catch (e: Throwable) {
errorInfo.set(e)
e.printStackTrace()
}
}.start() }.start()
} }
else { else {
@@ -100,6 +113,30 @@ class BTeXTest : ApplicationAdapter() {
private val drawY = 24 private val drawY = 24
private fun drawLoadingMsg(batch: SpriteBatch, stage: String) {
val e = errorInfo.get()
if (e != null) {
val st = e.message!!.split('\n').let {
val idx = it.indexOfFirst { it.startsWith("Caused by: ") }
it.subList(idx, it.size)
}
val th = 14 * st.size
val tw = st.maxOf { it.length } * 7
val tx = (1280 - tw) / 2
val ty = (720 - th) / 2
batch.color = Color.CORAL
st.forEachIndexed { i, s ->
TinyAlphNum.draw(batch, s, tx.toFloat(), ty + 14f*i)
}
}
else {
batch.color = Color.WHITE
Toolkit.drawTextCentered(batch, TinyAlphNum, stage, 1280, 0, 354)
}
}
override fun render() { override fun render() {
Gdx.graphics.setTitle("BTeXTest $EMDASH F: ${Gdx.graphics.framesPerSecond}") Gdx.graphics.setTitle("BTeXTest $EMDASH F: ${Gdx.graphics.framesPerSecond}")
@@ -127,8 +164,7 @@ class BTeXTest : ApplicationAdapter() {
} }
else { else {
batch.inUse { batch.inUse {
batch.color = Color.WHITE drawLoadingMsg(batch, "Rendering...")
Toolkit.drawTextCentered(batch, TinyAlphNum, "Rendering...", 1280, 0, 354)
} }
} }
@@ -145,8 +181,7 @@ class BTeXTest : ApplicationAdapter() {
} }
else { else {
batch.inUse { batch.inUse {
batch.color = Color.WHITE drawLoadingMsg(batch, "Typesetting...")
Toolkit.drawTextCentered(batch, TinyAlphNum, "Typesetting...", 1280, 0, 354)
} }
} }
} }