mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
btex: test--rectangle around hyperlinks
This commit is contained in:
BIN
lib/TerrarumSansBitmap.jar
LFS
BIN
lib/TerrarumSansBitmap.jar
LFS
Binary file not shown.
@@ -7,6 +7,8 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import com.badlogic.gdx.utils.Disposable
|
||||
import net.torvald.btex.BTeXDocViewer
|
||||
import net.torvald.terrarum.btex.BTeXDocument.Companion.HREF_UNDERLINE
|
||||
import net.torvald.terrarum.btex.BTeXDocument.Companion.UNDERLINE_Y
|
||||
import net.torvald.terrarum.ceilToInt
|
||||
import net.torvald.terrarum.concurrent.ThreadExecutor
|
||||
import net.torvald.terrarum.imagefont.TinyAlphNum
|
||||
@@ -62,8 +64,11 @@ class BTeXDocument : Disposable {
|
||||
val DEFAULT_PAGE_BACK = Color(0xe0dfdb_ff.toInt())
|
||||
// val DEFAULT_PAGE_FORE = Color(0x0a0706_ff)
|
||||
val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff)
|
||||
val HREF_UNDERLINE = Color(0x0033BBff)
|
||||
val ccPagenum = TerrarumSansBitmap.toColorCode(0xf333)
|
||||
|
||||
const val UNDERLINE_Y = 22
|
||||
|
||||
private fun String.escape() = this.replace("\"", "\\\"")
|
||||
|
||||
private fun newTempFile(name: String) = FileHandle.tempFile(name)
|
||||
@@ -143,12 +148,12 @@ class BTeXDocument : Disposable {
|
||||
@Transient private val fontNum = TinyAlphNum
|
||||
|
||||
fun addNewPage(back: Color = DEFAULT_PAGE_BACK) {
|
||||
pages.add(BTeXPage(back, pageDimensionWidth, pageDimensionHeight))
|
||||
pages.add(BTeXPage(this, back, pageDimensionWidth, pageDimensionHeight))
|
||||
linesPrintedOnPage.add(0)
|
||||
}
|
||||
|
||||
fun addNewPageAt(index: Int, back: Color = DEFAULT_PAGE_BACK) {
|
||||
pages.add(index, BTeXPage(back, pageDimensionWidth, pageDimensionHeight))
|
||||
pages.add(index, BTeXPage(this, back, pageDimensionWidth, pageDimensionHeight))
|
||||
linesPrintedOnPage.add(index, 0)
|
||||
}
|
||||
|
||||
@@ -332,12 +337,16 @@ class BTeXDocument : Disposable {
|
||||
}
|
||||
|
||||
data class BTeXClickable(
|
||||
val posX: Int, val posY: Int, val width: Int, val height: Int,
|
||||
var posX: Int, var posY: Int, val width: Int, val height: Int,
|
||||
val onClick: (BTeXDocViewer) -> Unit,
|
||||
// val onHover: () -> Unit = {}
|
||||
)
|
||||
) {
|
||||
var deltaX = 0
|
||||
var deltaY = 0
|
||||
}
|
||||
|
||||
class BTeXPage(
|
||||
val doc: BTeXDocument,
|
||||
val back: Color,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
@@ -360,8 +369,11 @@ class BTeXPage(
|
||||
drawCalls.sortBy { if (it.text != null) 16 else 0 }
|
||||
}
|
||||
|
||||
// paint background
|
||||
batch.color = back.cpy().also { it.a = 0.93f }
|
||||
Toolkit.fillArea(batch, x, y, width, height)
|
||||
|
||||
// print texts
|
||||
batch.color = Color.WHITE
|
||||
drawCalls.forEach {
|
||||
it.draw(batch, x + marginH, y + marginV)
|
||||
@@ -387,10 +399,18 @@ class BTeXPage(
|
||||
fun renderToPixmap(pixmap: Pixmap, x: Int, y: Int, marginH: Int, marginV: Int) {
|
||||
drawX = x; drawY = y
|
||||
drawCalls.sortedBy { if (it.text != null) 16 else 0 }.let { drawCalls ->
|
||||
// paint background
|
||||
val backCol = back.cpy().also { it.a = 0.93f }
|
||||
pixmap.setColor(backCol)
|
||||
pixmap.fill()
|
||||
|
||||
// debug underlines on clickableElements
|
||||
clickableElements.forEach {
|
||||
pixmap.setColor(HREF_UNDERLINE)
|
||||
pixmap.drawRectangle(it.posX + doc.pageMarginH, it.posY + doc.pageMarginV, it.width, doc.lineHeightInPx)
|
||||
}
|
||||
|
||||
// print texts
|
||||
pixmap.setColor(Color.WHITE)
|
||||
drawCalls.forEach {
|
||||
it.drawToPixmap(pixmap, x + marginH, y + marginV)
|
||||
|
||||
@@ -12,11 +12,8 @@ import com.jme3.math.FastMath.DEG_TO_RAD
|
||||
import net.torvald.colourutil.OKLch
|
||||
import net.torvald.colourutil.tosRGB
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.btex.BTeXBatchDrawCall
|
||||
import net.torvald.terrarum.btex.BTeXDocument
|
||||
import net.torvald.terrarum.btex.*
|
||||
import net.torvald.terrarum.btex.BTeXDocument.Companion.DEFAULT_ORNAMENTS_COL
|
||||
import net.torvald.terrarum.btex.BTeXDrawCall
|
||||
import net.torvald.terrarum.btex.TypesetDrawCall
|
||||
import net.torvald.terrarum.gameitems.ItemID
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
@@ -31,6 +28,7 @@ import org.xml.sax.Attributes
|
||||
import org.xml.sax.InputSource
|
||||
import org.xml.sax.SAXParseException
|
||||
import org.xml.sax.helpers.DefaultHandler
|
||||
import java.awt.SystemColor.text
|
||||
import java.io.*
|
||||
import java.net.URL
|
||||
import javax.xml.parsers.SAXParserFactory
|
||||
@@ -343,7 +341,7 @@ object BTeXParser {
|
||||
private val CODEMODE_END = "${TerrarumSansBitmap.charsetOverrideDefault}$ccDefault${spacingBlockToString(CODE_TAG_MARGIN)}"
|
||||
|
||||
private val HREF_BEGIN = "$ccHref"
|
||||
private val HREF_END = "$ccDefault"
|
||||
private val HREF_END = "${OBJ.codepointToString()}${OBJ_END.codepointToString()}$ccDefault"
|
||||
|
||||
private val BUCKS_BEGIN = "$ccBucks"
|
||||
private val BUCKS_END = "$ccDefault"
|
||||
@@ -355,6 +353,10 @@ object BTeXParser {
|
||||
|
||||
private val SPAN_END = "$ccDefault${TerrarumSansBitmap.charsetOverrideDefault}"
|
||||
|
||||
private fun CharArray.toSurrogatedString(): String = if (this.size == 1) "${this[0]}" else "${this[0]}${this[1]}"
|
||||
private fun Int.codepointToString() = Character.toChars(this).toSurrogatedString()
|
||||
|
||||
|
||||
private fun Color.toCC(): String {
|
||||
return TerrarumSansBitmap.toColorCode(
|
||||
this.r.times(15f).roundToInt(),
|
||||
@@ -1121,58 +1123,57 @@ object BTeXParser {
|
||||
width = doc.textWidth - 2*MARGIN_PARBOX_H,
|
||||
height = doc.pageLines - 1,
|
||||
align = currentAlign
|
||||
).forEachIndexed { index, it ->
|
||||
it.posX += MARGIN_PARBOX_H
|
||||
it.deltaX += MARGIN_PARBOX_H
|
||||
|
||||
it.posY += doc.lineHeightInPx / 2
|
||||
it.deltaY += doc.lineHeightInPx / 2
|
||||
).let {
|
||||
it.moveObjectsAround(MARGIN_PARBOX_H, doc.lineHeightInPx / 2)
|
||||
|
||||
// add boxes
|
||||
it.extraDrawFun = { batch, x, y ->
|
||||
val width = doc.textWidth - 2 * MARGIN_PARBOX_H.toFloat()
|
||||
val height = it.lineCount * doc.lineHeightInPx.toFloat()
|
||||
it.first.forEach {
|
||||
it.extraDrawFun = { batch, x, y ->
|
||||
val width = doc.textWidth - 2 * MARGIN_PARBOX_H.toFloat()
|
||||
val height = it.lineCount * doc.lineHeightInPx.toFloat()
|
||||
|
||||
if (height > 0) {
|
||||
val oldcol = batch.color.cpy()
|
||||
batch.color = Color(0xccccccff.toInt())
|
||||
Toolkit.fillArea(
|
||||
batch,
|
||||
x - MARGIN_PARBOX_H,
|
||||
y - MARGIN_PARBOX_V,
|
||||
width + 2 * MARGIN_PARBOX_H,
|
||||
height + 2 * MARGIN_PARBOX_V
|
||||
)
|
||||
batch.color = Color(0x999999ff.toInt())
|
||||
Toolkit.drawBoxBorder(
|
||||
batch,
|
||||
x - MARGIN_PARBOX_H,
|
||||
y - MARGIN_PARBOX_V,
|
||||
width + 2 * MARGIN_PARBOX_H,
|
||||
height + 2 * MARGIN_PARBOX_V
|
||||
)
|
||||
batch.color = oldcol
|
||||
if (height > 0) {
|
||||
val oldcol = batch.color.cpy()
|
||||
batch.color = Color(0xccccccff.toInt())
|
||||
Toolkit.fillArea(
|
||||
batch,
|
||||
x - MARGIN_PARBOX_H,
|
||||
y - MARGIN_PARBOX_V,
|
||||
width + 2 * MARGIN_PARBOX_H,
|
||||
height + 2 * MARGIN_PARBOX_V
|
||||
)
|
||||
batch.color = Color(0x999999ff.toInt())
|
||||
Toolkit.drawBoxBorder(
|
||||
batch,
|
||||
x - MARGIN_PARBOX_H,
|
||||
y - MARGIN_PARBOX_V,
|
||||
width + 2 * MARGIN_PARBOX_H,
|
||||
height + 2 * MARGIN_PARBOX_V
|
||||
)
|
||||
batch.color = oldcol
|
||||
}
|
||||
}
|
||||
}
|
||||
it.extraPixmapDrawFun = { pixmap, x, y ->
|
||||
val width = doc.textWidth - 2 * MARGIN_PARBOX_H
|
||||
val height = it.lineCount * doc.lineHeightInPx
|
||||
it.extraPixmapDrawFun = { pixmap, x, y ->
|
||||
val width = doc.textWidth - 2 * MARGIN_PARBOX_H
|
||||
val height = it.lineCount * doc.lineHeightInPx
|
||||
|
||||
if (height > 0) {
|
||||
pixmap.setColor(Color(0xccccccff.toInt()))
|
||||
pixmap.fillRectangle(
|
||||
x - MARGIN_PARBOX_H,
|
||||
y - MARGIN_PARBOX_V,
|
||||
width + 2 * MARGIN_PARBOX_H,
|
||||
height + 2 * MARGIN_PARBOX_V
|
||||
)
|
||||
pixmap.setColor(Color(0x999999ff.toInt()))
|
||||
Toolkit.drawBoxBorderToPixmap(pixmap,
|
||||
x - MARGIN_PARBOX_H,
|
||||
y - MARGIN_PARBOX_V,
|
||||
width + 2 * MARGIN_PARBOX_H,
|
||||
height + 2 * MARGIN_PARBOX_V
|
||||
)
|
||||
if (height > 0) {
|
||||
pixmap.setColor(Color(0xccccccff.toInt()))
|
||||
pixmap.fillRectangle(
|
||||
x - MARGIN_PARBOX_H,
|
||||
y - MARGIN_PARBOX_V,
|
||||
width + 2 * MARGIN_PARBOX_H,
|
||||
height + 2 * MARGIN_PARBOX_V
|
||||
)
|
||||
pixmap.setColor(Color(0x999999ff.toInt()))
|
||||
Toolkit.drawBoxBorderToPixmap(
|
||||
pixmap,
|
||||
x - MARGIN_PARBOX_H,
|
||||
y - MARGIN_PARBOX_V,
|
||||
width + 2 * MARGIN_PARBOX_H,
|
||||
height + 2 * MARGIN_PARBOX_V
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1220,20 +1221,14 @@ object BTeXParser {
|
||||
width = doc.textWidth - 2*MARGIN_PARBOX_H,
|
||||
height = doc.pageLines - 1,
|
||||
align = currentAlign
|
||||
).forEachIndexed { index, it ->
|
||||
it.posX += MARGIN_PARBOX_H
|
||||
it.deltaX += MARGIN_PARBOX_H
|
||||
|
||||
it.posY += doc.lineHeightInPx / 2
|
||||
it.deltaY += doc.lineHeightInPx / 2
|
||||
}
|
||||
).moveObjectsAround(MARGIN_PARBOX_H, doc.lineHeightInPx / 2)
|
||||
|
||||
clearParBuffer()
|
||||
}
|
||||
|
||||
@CloseTag
|
||||
fun closeElemANONBREAK(handler: BTeXHandler, doc: BTeXDocument, uri: String, siblingIndex: Int) {
|
||||
typesetParagraphs("${ccDefault}――――――――――――", handler, align = "left").also {it.first().let {
|
||||
typesetParagraphs("${ccDefault}――――――――――――", handler, align = "left").also {it.first.first().let {
|
||||
it.posX += (doc.textWidth - it.width) / 2
|
||||
} }
|
||||
clearParBuffer()
|
||||
@@ -1435,7 +1430,7 @@ object BTeXParser {
|
||||
|
||||
private fun typesetBookTitle(thePar: String, handler: BTeXHandler) {
|
||||
val label = "\n${TerrarumSansBitmap.toColorCode(15, 15, 15)}" + thePar
|
||||
typesetParagraphs(getTitleFont(), label, handler, doc.textWidth - 2*MARGIN_TITLE_TEXTS, align = "left").also {
|
||||
typesetParagraphs(getTitleFont(), label, handler, doc.textWidth - 2*MARGIN_TITLE_TEXTS, align = "left").first.also {
|
||||
val addedLines = it.sumOf { it.lineCount }
|
||||
doc.linesPrintedOnPage[doc.currentPage] += addedLines
|
||||
|
||||
@@ -1453,7 +1448,7 @@ object BTeXParser {
|
||||
doc.textWidth - 2*MARGIN_TITLE_TEXTS,
|
||||
align = "left"
|
||||
).also {
|
||||
it.last().extraDrawFun = { batch, x, y ->
|
||||
it.first.last().extraDrawFun = { batch, x, y ->
|
||||
val px = x
|
||||
val py = y + doc.lineHeightInPx - 1
|
||||
val pw = doc.textWidth - 2f * MARGIN_TITLE_TEXTS
|
||||
@@ -1462,7 +1457,7 @@ object BTeXParser {
|
||||
batch.color = Color.WHITE
|
||||
Toolkit.fillArea(batch, px, py, pw, 1f)
|
||||
}
|
||||
it.last().extraPixmapDrawFun = { pixmap, x, y ->
|
||||
it.first.last().extraPixmapDrawFun = { pixmap, x, y ->
|
||||
val px = x
|
||||
val py = y + doc.lineHeightInPx - 1
|
||||
val pw = doc.textWidth - 2 * MARGIN_TITLE_TEXTS
|
||||
@@ -1472,9 +1467,7 @@ object BTeXParser {
|
||||
pixmap.fillRectangle(px, py, pw, 1)
|
||||
}
|
||||
|
||||
it.forEach {
|
||||
it.posX += MARGIN_TITLE_TEXTS
|
||||
}
|
||||
it.moveObjectsAround(MARGIN_TITLE_TEXTS, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1485,11 +1478,7 @@ object BTeXParser {
|
||||
handler,
|
||||
doc.textWidth - 2*MARGIN_TITLE_TEXTS,
|
||||
align = "left"
|
||||
).also {
|
||||
it.forEach {
|
||||
it.posX += MARGIN_TITLE_TEXTS
|
||||
}
|
||||
}
|
||||
).moveObjectsAround(MARGIN_TITLE_TEXTS, 0)
|
||||
}
|
||||
|
||||
private fun typesetPartHeading(num: String, thePar: String, handler: BTeXHandler, indent: Int = PAR_INDENTATION, width: Int = doc.textWidth) {
|
||||
@@ -1545,12 +1534,11 @@ object BTeXParser {
|
||||
val header = if (num == null) thePar else "$num${spacingBlockToString(9)}$thePar"
|
||||
typesetParagraphs("\n$ccDefault$header", handler, width - indent, align = "left").also {
|
||||
// add indents and adjust text y pos
|
||||
it.forEach {
|
||||
it.posX += indent
|
||||
it.posY -= doc.lineHeightInPx / 2
|
||||
}
|
||||
it.moveObjectsAround(indent, -doc.lineHeightInPx / 2)
|
||||
|
||||
|
||||
// add ornamental column on the left
|
||||
it.forEach {
|
||||
it.first.forEach {
|
||||
it.extraDrawFun = { batch, x, y ->
|
||||
val oldCol = batch.color.cpy()
|
||||
batch.color = DEFAULT_ORNAMENTS_COL.cpy().also { it.a *= bodyTextShadowAlpha }
|
||||
@@ -1592,10 +1580,7 @@ object BTeXParser {
|
||||
private fun typesetSectionHeading(num: String, thePar: String, handler: BTeXHandler, indent: Int = 16, width: Int = doc.textWidth) {
|
||||
typesetParagraphs("\n$ccDefault$num${spacingBlockToString(9)}$thePar", handler, width - indent, align = "left").also {
|
||||
// add indents and adjust text y pos
|
||||
it.forEach {
|
||||
it.posX += indent
|
||||
it.posY -= doc.lineHeightInPx / 2
|
||||
}
|
||||
it.moveObjectsAround(indent, -doc.lineHeightInPx / 2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1606,7 +1591,7 @@ object BTeXParser {
|
||||
height: Int = doc.pageLines,
|
||||
startingPage: Int = doc.currentPage,
|
||||
align: String
|
||||
): List<BTeXDrawCall> {
|
||||
): Pair<List<BTeXDrawCall>, List<BTeXClickable>> {
|
||||
return typesetParagraphs(getFont(), thePar, handler, width, height, startingPage, align)
|
||||
}
|
||||
|
||||
@@ -1618,7 +1603,7 @@ object BTeXParser {
|
||||
height: Int = doc.pageLines,
|
||||
startingPage: Int = doc.currentPage,
|
||||
align: String
|
||||
): List<BTeXDrawCall> {
|
||||
): Pair<List<BTeXDrawCall>, List<BTeXClickable>> {
|
||||
val strat = when (align) {
|
||||
"left" -> TypesettingStrategy.RAGGED_RIGHT
|
||||
"right" -> TypesettingStrategy.RAGGED_LEFT
|
||||
@@ -1630,6 +1615,7 @@ object BTeXParser {
|
||||
var pageNum = startingPage
|
||||
|
||||
val drawCalls = ArrayList<BTeXDrawCall>()
|
||||
val clickables = ArrayList<BTeXClickable>()
|
||||
|
||||
var remainder = height - doc.linesPrintedOnPage.last()
|
||||
var slugHeight = slugs.height
|
||||
@@ -1653,18 +1639,57 @@ object BTeXParser {
|
||||
doc.appendDrawCall(doc.pages[pageNum], it); drawCalls.add(it)
|
||||
}
|
||||
}
|
||||
// >>> HREF code here!! <<<
|
||||
val hrefs = parseAndGetHref(textDrawCalls[0], font, handler, posYline, slugs, subset.first, subset.second)
|
||||
hrefs.forEach {
|
||||
var objectIsSplit = false
|
||||
hrefs.forEach { (hrefObj, objSeq) ->
|
||||
// search for:
|
||||
// ..... [OBJ:RSETNFAOON]word setaf
|
||||
// get width of "word"
|
||||
val searchStr = slugs.typesettedSlugs.subList(subset.first, subset.first + subset.second)
|
||||
printdbg("HREF searchStr: ${searchStr.joinToString { it.toReadable() }}")
|
||||
// val clickable = BTeXClickable(it.x, it.y, undefined, doc.lineHeightInPx) { viewer ->
|
||||
// viewer.gotoIndex(it.hrefTarget)
|
||||
// }
|
||||
// doc.appendClickable(doc.pages[pageNum], it)
|
||||
val searchStrs = slugs.typesettedSlugs.subList(subset.first, subset.first + subset.second)
|
||||
searchStrs.forEach { str ->
|
||||
printdbg("1HREF searchStr: ${str.toReadable()}")
|
||||
printdbg("1HREF object: ${objSeq.toReadable()} (id=${hrefDict[objSeq.toReadable()]})")
|
||||
|
||||
val indexOfSequence = str.indexOfSequence(objSeq)
|
||||
|
||||
val theIndex = if (objectIsSplit) -(objSeq.size + 4) else indexOfSequence
|
||||
|
||||
theIndex?.let { index -> // we never know which line the object appears
|
||||
val wordOffset = index + objSeq.size + 4 // must be index of starting NUL
|
||||
// target word is on the current line
|
||||
if (wordOffset < str.size) {
|
||||
var wordEnd = wordOffset + 1 // will be right on the ending NUL
|
||||
// find ending OBJ
|
||||
while (!(wordEnd >= str.size || str[wordEnd] == OBJ)) {
|
||||
wordEnd++
|
||||
}
|
||||
val substr = CodepointSequence(str.subList(wordOffset + 1, wordEnd))
|
||||
|
||||
printdbg("1HREF word: ${substr.toReadable()}")
|
||||
printdbg("1HREF hrefObj: ${hrefObj}")
|
||||
|
||||
val hrefX = if (objectIsSplit) 0 else hrefObj.x
|
||||
var hrefY = hrefObj.y; if (objectIsSplit) hrefY += doc.lineHeightInPx
|
||||
|
||||
val clickable = BTeXClickable(hrefX, hrefY, getFont().getWidth(substr), doc.lineHeightInPx) { viewer ->
|
||||
viewer.gotoIndex(hrefObj.hrefTarget)
|
||||
}
|
||||
doc.appendClickable(doc.pages[pageNum], clickable); clickables.add(clickable)
|
||||
|
||||
objectIsSplit = false
|
||||
}
|
||||
// target word is on the next line (probably)
|
||||
else {
|
||||
printdbg("1HREF object was cut off by the linebreak")
|
||||
objectIsSplit = true
|
||||
}
|
||||
}
|
||||
|
||||
printdbg(" ")
|
||||
}
|
||||
}
|
||||
// >>> HREF code ends here!! <<<
|
||||
|
||||
linesOut += remainder
|
||||
slugHeight -= remainder
|
||||
@@ -1687,9 +1712,53 @@ object BTeXParser {
|
||||
}
|
||||
// >>> HREF code here!! <<<
|
||||
val hrefs = parseAndGetHref(textDrawCalls[0], font, handler, posYline, slugs, subset.first, subset.second)
|
||||
hrefs.forEach {
|
||||
val searchStr = slugs.typesettedSlugs.subList(subset.first, subset.first + subset.second)
|
||||
printdbg("HREF searchStr: ${searchStr.joinToString { it.toReadable() }}")
|
||||
var objectIsSplit = false
|
||||
hrefs.forEach { (hrefObj, objSeq) ->
|
||||
// search for:
|
||||
// ..... [OBJ:RSETNFAOON]word setaf
|
||||
// get width of "word"
|
||||
val searchStrs = slugs.typesettedSlugs.subList(subset.first, subset.first + subset.second)
|
||||
searchStrs.forEach { str ->
|
||||
printdbg("2HREF searchStr: ${str.toReadable()}")
|
||||
printdbg("2HREF object: ${objSeq.toReadable()} (id=${hrefDict[objSeq.toReadable()]})")
|
||||
|
||||
val indexOfSequence = str.indexOfSequence(objSeq)
|
||||
|
||||
val theIndex = if (objectIsSplit) -(objSeq.size + 4) else indexOfSequence
|
||||
|
||||
theIndex?.let { index -> // we never know which line the object appears
|
||||
val wordOffset = index + objSeq.size + 4 // must be index of starting NUL
|
||||
// target word is on the current line
|
||||
if (wordOffset < str.size) {
|
||||
var wordEnd = wordOffset + 1 // will be right on the ending NUL
|
||||
// find ending OBJ
|
||||
while (!(wordEnd >= str.size || str[wordEnd] == OBJ)) {
|
||||
wordEnd++
|
||||
}
|
||||
val substr = CodepointSequence(str.subList(wordOffset + 1, wordEnd))
|
||||
|
||||
printdbg("2HREF word: ${substr.toReadable()}")
|
||||
printdbg("2HREF hrefObj: ${hrefObj}")
|
||||
|
||||
val hrefX = if (objectIsSplit) 0 else hrefObj.x
|
||||
var hrefY = hrefObj.y; if (objectIsSplit) hrefY += doc.lineHeightInPx
|
||||
|
||||
val clickable = BTeXClickable(hrefX, hrefY, getFont().getWidth(substr), doc.lineHeightInPx) { viewer ->
|
||||
viewer.gotoIndex(hrefObj.hrefTarget)
|
||||
}
|
||||
doc.appendClickable(doc.pages[pageNum], clickable); clickables.add(clickable)
|
||||
|
||||
objectIsSplit = false
|
||||
}
|
||||
// target word is on the next line (probably)
|
||||
else {
|
||||
printdbg("2HREF object was cut off by the linebreak")
|
||||
objectIsSplit = true
|
||||
}
|
||||
}
|
||||
|
||||
printdbg(" ")
|
||||
}
|
||||
}
|
||||
// >>> HREF code ends here!! <<<
|
||||
|
||||
@@ -1704,7 +1773,7 @@ object BTeXParser {
|
||||
// if typesetting the paragraph leaves the first line of new page empty, move the "row cursor" back up
|
||||
if (doc.linesPrintedOnPage[pageNum] == 1 && doc.pages[pageNum].isEmpty()) doc.linesPrintedOnPage[pageNum] = 0 // '\n' adds empty draw call to the page, which makes isEmpty() to return false
|
||||
|
||||
return drawCalls
|
||||
return drawCalls to clickables
|
||||
}
|
||||
|
||||
private fun textToDrawCall(handler: BTeXHandler, posYline: Int, slugs: MovableType, lineStart: Int, lineCount: Int): List<BTeXDrawCall> {
|
||||
@@ -1743,7 +1812,7 @@ object BTeXParser {
|
||||
c += 1
|
||||
}
|
||||
|
||||
if (!idbuf.startsWith("HREF@")) {
|
||||
if (idbuf.isNotBlank() && !idbuf.startsWith("HREF@")) {
|
||||
out.add(BTeXDrawCall(
|
||||
doc, x, y, currentTheme,
|
||||
cmd = objDict[idbuf.toString()]?.invoke(textDrawCall)
|
||||
@@ -1765,8 +1834,8 @@ object BTeXParser {
|
||||
slugs: MovableType,
|
||||
lineStart: Int,
|
||||
lineCount: Int
|
||||
): List<_HrefObject> {
|
||||
val out = ArrayList<_HrefObject>()
|
||||
): List<Pair<_HrefObject, CodepointSequence>> {
|
||||
val out = ArrayList<Pair<_HrefObject, CodepointSequence>>()
|
||||
|
||||
slugs.typesettedSlugs.subList(lineStart, lineStart + lineCount).forEachIndexed { lineNumCnt, line ->
|
||||
line.mapIndexed { i, c -> i to c }.filter { it.second == OBJ }.map { it.first }.forEach { xIndex ->
|
||||
@@ -1775,18 +1844,20 @@ object BTeXParser {
|
||||
|
||||
// get OBJ id
|
||||
val idbuf = StringBuilder()
|
||||
val cpseq = CodepointSequence()
|
||||
|
||||
var c = xIndex + 1
|
||||
while (true) {
|
||||
val codepoint = line[c]
|
||||
if (codepoint == 0xFFF9F) break
|
||||
idbuf.append(codepointToObjIdChar(codepoint))
|
||||
cpseq.add(codepoint)
|
||||
c += 1
|
||||
}
|
||||
|
||||
if (idbuf.startsWith("HREF@")) {
|
||||
if (idbuf.isNotBlank() && idbuf.startsWith("HREF@")) {
|
||||
val id = hrefDict[idbuf.toString()]!!
|
||||
out.add(_HrefObject(x, y, id))
|
||||
out.add(_HrefObject(x, y, id) to cpseq)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1802,13 +1873,9 @@ object BTeXParser {
|
||||
val dotPosEnd = typeWidth - pageNumWidth - dotGap*1.5f
|
||||
|
||||
typesetParagraphs("$ccDefault$heading$name", handler, typeWidth - pageNumWidth - dotGap, startingPage = pageToWrite ?: doc.currentPage, align = "justify").let {
|
||||
it.forEach {
|
||||
it.posX += indentation
|
||||
it.moveObjectsAround(indentation, 0)
|
||||
|
||||
// println("pos: (${it.posX}, ${it.posY})\tTOC: $name")
|
||||
}
|
||||
|
||||
it.last { it.text != null }.let { call ->
|
||||
it.first.last { it.text != null }.let { call ->
|
||||
call.extraDrawFun = { batch, x, y ->
|
||||
val oldCol = batch.color.cpy()
|
||||
|
||||
@@ -1916,10 +1983,12 @@ object BTeXParser {
|
||||
val ccItemName = TerrarumSansBitmap.toColorCode(0xf03b)
|
||||
val ccTargetName = TerrarumSansBitmap.toColorCode(0xf170)
|
||||
|
||||
private const val NUL = 0
|
||||
private const val ZWSP = 0x200B
|
||||
private const val SHY = 0xAD
|
||||
private const val NBSP = 0xA0
|
||||
private const val OBJ = 0xFFFC
|
||||
private const val OBJ_END = 0xFFF9F
|
||||
private const val SPACING_BLOCK_ONE = 0xFFFD0
|
||||
private const val SPACING_BLOCK_SIXTEEN = 0xFFFDF
|
||||
|
||||
@@ -1966,7 +2035,10 @@ object BTeXParser {
|
||||
|
||||
idstr.add(0xFFF9F)
|
||||
|
||||
return "\uFFFC" + idstr.toUTF8Bytes().toString(Charsets.UTF_8) + spacingBlockToString(width)
|
||||
if (width != 0)
|
||||
return "\uFFFC" + idstr.toUTF8Bytes().toString(Charsets.UTF_8) + spacingBlockToString(width)
|
||||
else
|
||||
return "\uFFFC" + idstr.toUTF8Bytes().toString(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
fun Int.toRomanNum(): String = when (this) {
|
||||
@@ -2004,7 +2076,46 @@ object BTeXParser {
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeRandomObjName() = (0 until 12).joinToString("") { "${hashStrMap.random()}" }
|
||||
private fun makeRandomObjName() = (0 until 16).joinToString("") { "${hashStrMap.random()}" }
|
||||
|
||||
// KMP algorithm
|
||||
internal fun CodepointSequence.indexOfSequence(pattern: CodepointSequence): Int? {
|
||||
if (pattern.isEmpty())
|
||||
throw IllegalArgumentException("Pattern is empty")
|
||||
if (this.isEmpty())
|
||||
throw IllegalArgumentException("Pattern is empty")
|
||||
|
||||
// next[i] stores the index of the next best partial match
|
||||
val next = IntArray(pattern.size + 1)
|
||||
for (i in 1 until pattern.size) {
|
||||
var j = next[i]
|
||||
|
||||
while (j > 0 && this[j] != this[i]) {
|
||||
j = next[j]
|
||||
}
|
||||
|
||||
if (j > 0 || this[j] == this[i]) {
|
||||
next[i + 1] = j + 1
|
||||
}
|
||||
}
|
||||
|
||||
var i = 0
|
||||
var j = 0
|
||||
while (i < this.size) {
|
||||
if (j < pattern.size && this[i] == pattern[j]) {
|
||||
if (++j == pattern.size) {
|
||||
return i - j + 1
|
||||
}
|
||||
}
|
||||
else if (j > 0) {
|
||||
j = next[j]
|
||||
i-- // since i will be incremented in the next iteration
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2013,6 +2124,20 @@ object BTeXParser {
|
||||
private annotation class CloseTag
|
||||
}
|
||||
|
||||
private fun Pair<List<BTeXDrawCall>, List<BTeXClickable>>.moveObjectsAround(x: Int, y: Int) {
|
||||
this.first.forEachIndexed { index, it ->
|
||||
it.posX += x
|
||||
it.deltaX += x
|
||||
it.posY += y
|
||||
it.deltaY += y
|
||||
}
|
||||
this.second.forEachIndexed { index, it ->
|
||||
it.posX += x
|
||||
it.deltaX += x
|
||||
it.posY += y
|
||||
it.deltaY += y
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BTeXParsingException(s: String) : RuntimeException(s) {
|
||||
|
||||
Reference in New Issue
Block a user