mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-06 08:38:30 +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.graphics.g2d.TextureRegion
|
||||||
import com.badlogic.gdx.utils.Disposable
|
import com.badlogic.gdx.utils.Disposable
|
||||||
import net.torvald.btex.BTeXDocViewer
|
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.ceilToInt
|
||||||
import net.torvald.terrarum.concurrent.ThreadExecutor
|
import net.torvald.terrarum.concurrent.ThreadExecutor
|
||||||
import net.torvald.terrarum.imagefont.TinyAlphNum
|
import net.torvald.terrarum.imagefont.TinyAlphNum
|
||||||
@@ -62,8 +64,11 @@ class BTeXDocument : Disposable {
|
|||||||
val DEFAULT_PAGE_BACK = Color(0xe0dfdb_ff.toInt())
|
val DEFAULT_PAGE_BACK = Color(0xe0dfdb_ff.toInt())
|
||||||
// val DEFAULT_PAGE_FORE = Color(0x0a0706_ff)
|
// val DEFAULT_PAGE_FORE = Color(0x0a0706_ff)
|
||||||
val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff)
|
val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff)
|
||||||
|
val HREF_UNDERLINE = Color(0x0033BBff)
|
||||||
val ccPagenum = TerrarumSansBitmap.toColorCode(0xf333)
|
val ccPagenum = TerrarumSansBitmap.toColorCode(0xf333)
|
||||||
|
|
||||||
|
const val UNDERLINE_Y = 22
|
||||||
|
|
||||||
private fun String.escape() = this.replace("\"", "\\\"")
|
private fun String.escape() = this.replace("\"", "\\\"")
|
||||||
|
|
||||||
private fun newTempFile(name: String) = FileHandle.tempFile(name)
|
private fun newTempFile(name: String) = FileHandle.tempFile(name)
|
||||||
@@ -143,12 +148,12 @@ class BTeXDocument : Disposable {
|
|||||||
@Transient private val fontNum = TinyAlphNum
|
@Transient private val fontNum = TinyAlphNum
|
||||||
|
|
||||||
fun addNewPage(back: Color = DEFAULT_PAGE_BACK) {
|
fun addNewPage(back: Color = DEFAULT_PAGE_BACK) {
|
||||||
pages.add(BTeXPage(back, pageDimensionWidth, pageDimensionHeight))
|
pages.add(BTeXPage(this, back, pageDimensionWidth, pageDimensionHeight))
|
||||||
linesPrintedOnPage.add(0)
|
linesPrintedOnPage.add(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addNewPageAt(index: Int, back: Color = DEFAULT_PAGE_BACK) {
|
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)
|
linesPrintedOnPage.add(index, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,12 +337,16 @@ class BTeXDocument : Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class BTeXClickable(
|
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 onClick: (BTeXDocViewer) -> Unit,
|
||||||
// val onHover: () -> Unit = {}
|
// val onHover: () -> Unit = {}
|
||||||
)
|
) {
|
||||||
|
var deltaX = 0
|
||||||
|
var deltaY = 0
|
||||||
|
}
|
||||||
|
|
||||||
class BTeXPage(
|
class BTeXPage(
|
||||||
|
val doc: BTeXDocument,
|
||||||
val back: Color,
|
val back: Color,
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
@@ -360,8 +369,11 @@ class BTeXPage(
|
|||||||
drawCalls.sortBy { if (it.text != null) 16 else 0 }
|
drawCalls.sortBy { if (it.text != null) 16 else 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paint background
|
||||||
batch.color = back.cpy().also { it.a = 0.93f }
|
batch.color = back.cpy().also { it.a = 0.93f }
|
||||||
Toolkit.fillArea(batch, x, y, width, height)
|
Toolkit.fillArea(batch, x, y, width, height)
|
||||||
|
|
||||||
|
// print texts
|
||||||
batch.color = Color.WHITE
|
batch.color = Color.WHITE
|
||||||
drawCalls.forEach {
|
drawCalls.forEach {
|
||||||
it.draw(batch, x + marginH, y + marginV)
|
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) {
|
fun renderToPixmap(pixmap: Pixmap, x: Int, y: Int, marginH: Int, marginV: Int) {
|
||||||
drawX = x; drawY = y
|
drawX = x; drawY = y
|
||||||
drawCalls.sortedBy { if (it.text != null) 16 else 0 }.let { drawCalls ->
|
drawCalls.sortedBy { if (it.text != null) 16 else 0 }.let { drawCalls ->
|
||||||
|
// paint background
|
||||||
val backCol = back.cpy().also { it.a = 0.93f }
|
val backCol = back.cpy().also { it.a = 0.93f }
|
||||||
pixmap.setColor(backCol)
|
pixmap.setColor(backCol)
|
||||||
pixmap.fill()
|
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)
|
pixmap.setColor(Color.WHITE)
|
||||||
drawCalls.forEach {
|
drawCalls.forEach {
|
||||||
it.drawToPixmap(pixmap, x + marginH, y + marginV)
|
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.OKLch
|
||||||
import net.torvald.colourutil.tosRGB
|
import net.torvald.colourutil.tosRGB
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.*
|
||||||
import net.torvald.terrarum.btex.BTeXBatchDrawCall
|
import net.torvald.terrarum.btex.*
|
||||||
import net.torvald.terrarum.btex.BTeXDocument
|
|
||||||
import net.torvald.terrarum.btex.BTeXDocument.Companion.DEFAULT_ORNAMENTS_COL
|
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.gameitems.ItemID
|
||||||
import net.torvald.terrarum.langpack.Lang
|
import net.torvald.terrarum.langpack.Lang
|
||||||
import net.torvald.terrarum.ui.Toolkit
|
import net.torvald.terrarum.ui.Toolkit
|
||||||
@@ -31,6 +28,7 @@ import org.xml.sax.Attributes
|
|||||||
import org.xml.sax.InputSource
|
import org.xml.sax.InputSource
|
||||||
import org.xml.sax.SAXParseException
|
import org.xml.sax.SAXParseException
|
||||||
import org.xml.sax.helpers.DefaultHandler
|
import org.xml.sax.helpers.DefaultHandler
|
||||||
|
import java.awt.SystemColor.text
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.xml.parsers.SAXParserFactory
|
import javax.xml.parsers.SAXParserFactory
|
||||||
@@ -343,7 +341,7 @@ object BTeXParser {
|
|||||||
private val CODEMODE_END = "${TerrarumSansBitmap.charsetOverrideDefault}$ccDefault${spacingBlockToString(CODE_TAG_MARGIN)}"
|
private val CODEMODE_END = "${TerrarumSansBitmap.charsetOverrideDefault}$ccDefault${spacingBlockToString(CODE_TAG_MARGIN)}"
|
||||||
|
|
||||||
private val HREF_BEGIN = "$ccHref"
|
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_BEGIN = "$ccBucks"
|
||||||
private val BUCKS_END = "$ccDefault"
|
private val BUCKS_END = "$ccDefault"
|
||||||
@@ -355,6 +353,10 @@ object BTeXParser {
|
|||||||
|
|
||||||
private val SPAN_END = "$ccDefault${TerrarumSansBitmap.charsetOverrideDefault}"
|
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 {
|
private fun Color.toCC(): String {
|
||||||
return TerrarumSansBitmap.toColorCode(
|
return TerrarumSansBitmap.toColorCode(
|
||||||
this.r.times(15f).roundToInt(),
|
this.r.times(15f).roundToInt(),
|
||||||
@@ -1121,58 +1123,57 @@ object BTeXParser {
|
|||||||
width = doc.textWidth - 2*MARGIN_PARBOX_H,
|
width = doc.textWidth - 2*MARGIN_PARBOX_H,
|
||||||
height = doc.pageLines - 1,
|
height = doc.pageLines - 1,
|
||||||
align = currentAlign
|
align = currentAlign
|
||||||
).forEachIndexed { index, it ->
|
).let {
|
||||||
it.posX += MARGIN_PARBOX_H
|
it.moveObjectsAround(MARGIN_PARBOX_H, doc.lineHeightInPx / 2)
|
||||||
it.deltaX += MARGIN_PARBOX_H
|
|
||||||
|
|
||||||
it.posY += doc.lineHeightInPx / 2
|
|
||||||
it.deltaY += doc.lineHeightInPx / 2
|
|
||||||
|
|
||||||
// add boxes
|
// add boxes
|
||||||
it.extraDrawFun = { batch, x, y ->
|
it.first.forEach {
|
||||||
val width = doc.textWidth - 2 * MARGIN_PARBOX_H.toFloat()
|
it.extraDrawFun = { batch, x, y ->
|
||||||
val height = it.lineCount * doc.lineHeightInPx.toFloat()
|
val width = doc.textWidth - 2 * MARGIN_PARBOX_H.toFloat()
|
||||||
|
val height = it.lineCount * doc.lineHeightInPx.toFloat()
|
||||||
|
|
||||||
if (height > 0) {
|
if (height > 0) {
|
||||||
val oldcol = batch.color.cpy()
|
val oldcol = batch.color.cpy()
|
||||||
batch.color = Color(0xccccccff.toInt())
|
batch.color = Color(0xccccccff.toInt())
|
||||||
Toolkit.fillArea(
|
Toolkit.fillArea(
|
||||||
batch,
|
batch,
|
||||||
x - MARGIN_PARBOX_H,
|
x - MARGIN_PARBOX_H,
|
||||||
y - MARGIN_PARBOX_V,
|
y - MARGIN_PARBOX_V,
|
||||||
width + 2 * MARGIN_PARBOX_H,
|
width + 2 * MARGIN_PARBOX_H,
|
||||||
height + 2 * MARGIN_PARBOX_V
|
height + 2 * MARGIN_PARBOX_V
|
||||||
)
|
)
|
||||||
batch.color = Color(0x999999ff.toInt())
|
batch.color = Color(0x999999ff.toInt())
|
||||||
Toolkit.drawBoxBorder(
|
Toolkit.drawBoxBorder(
|
||||||
batch,
|
batch,
|
||||||
x - MARGIN_PARBOX_H,
|
x - MARGIN_PARBOX_H,
|
||||||
y - MARGIN_PARBOX_V,
|
y - MARGIN_PARBOX_V,
|
||||||
width + 2 * MARGIN_PARBOX_H,
|
width + 2 * MARGIN_PARBOX_H,
|
||||||
height + 2 * MARGIN_PARBOX_V
|
height + 2 * MARGIN_PARBOX_V
|
||||||
)
|
)
|
||||||
batch.color = oldcol
|
batch.color = oldcol
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
it.extraPixmapDrawFun = { pixmap, x, y ->
|
||||||
it.extraPixmapDrawFun = { pixmap, x, y ->
|
val width = doc.textWidth - 2 * MARGIN_PARBOX_H
|
||||||
val width = doc.textWidth - 2 * MARGIN_PARBOX_H
|
val height = it.lineCount * doc.lineHeightInPx
|
||||||
val height = it.lineCount * doc.lineHeightInPx
|
|
||||||
|
|
||||||
if (height > 0) {
|
if (height > 0) {
|
||||||
pixmap.setColor(Color(0xccccccff.toInt()))
|
pixmap.setColor(Color(0xccccccff.toInt()))
|
||||||
pixmap.fillRectangle(
|
pixmap.fillRectangle(
|
||||||
x - MARGIN_PARBOX_H,
|
x - MARGIN_PARBOX_H,
|
||||||
y - MARGIN_PARBOX_V,
|
y - MARGIN_PARBOX_V,
|
||||||
width + 2 * MARGIN_PARBOX_H,
|
width + 2 * MARGIN_PARBOX_H,
|
||||||
height + 2 * MARGIN_PARBOX_V
|
height + 2 * MARGIN_PARBOX_V
|
||||||
)
|
)
|
||||||
pixmap.setColor(Color(0x999999ff.toInt()))
|
pixmap.setColor(Color(0x999999ff.toInt()))
|
||||||
Toolkit.drawBoxBorderToPixmap(pixmap,
|
Toolkit.drawBoxBorderToPixmap(
|
||||||
x - MARGIN_PARBOX_H,
|
pixmap,
|
||||||
y - MARGIN_PARBOX_V,
|
x - MARGIN_PARBOX_H,
|
||||||
width + 2 * MARGIN_PARBOX_H,
|
y - MARGIN_PARBOX_V,
|
||||||
height + 2 * 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,
|
width = doc.textWidth - 2*MARGIN_PARBOX_H,
|
||||||
height = doc.pageLines - 1,
|
height = doc.pageLines - 1,
|
||||||
align = currentAlign
|
align = currentAlign
|
||||||
).forEachIndexed { index, it ->
|
).moveObjectsAround(MARGIN_PARBOX_H, doc.lineHeightInPx / 2)
|
||||||
it.posX += MARGIN_PARBOX_H
|
|
||||||
it.deltaX += MARGIN_PARBOX_H
|
|
||||||
|
|
||||||
it.posY += doc.lineHeightInPx / 2
|
|
||||||
it.deltaY += doc.lineHeightInPx / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
clearParBuffer()
|
clearParBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@CloseTag
|
@CloseTag
|
||||||
fun closeElemANONBREAK(handler: BTeXHandler, doc: BTeXDocument, uri: String, siblingIndex: Int) {
|
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
|
it.posX += (doc.textWidth - it.width) / 2
|
||||||
} }
|
} }
|
||||||
clearParBuffer()
|
clearParBuffer()
|
||||||
@@ -1435,7 +1430,7 @@ object BTeXParser {
|
|||||||
|
|
||||||
private fun typesetBookTitle(thePar: String, handler: BTeXHandler) {
|
private fun typesetBookTitle(thePar: String, handler: BTeXHandler) {
|
||||||
val label = "\n${TerrarumSansBitmap.toColorCode(15, 15, 15)}" + thePar
|
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 }
|
val addedLines = it.sumOf { it.lineCount }
|
||||||
doc.linesPrintedOnPage[doc.currentPage] += addedLines
|
doc.linesPrintedOnPage[doc.currentPage] += addedLines
|
||||||
|
|
||||||
@@ -1453,7 +1448,7 @@ object BTeXParser {
|
|||||||
doc.textWidth - 2*MARGIN_TITLE_TEXTS,
|
doc.textWidth - 2*MARGIN_TITLE_TEXTS,
|
||||||
align = "left"
|
align = "left"
|
||||||
).also {
|
).also {
|
||||||
it.last().extraDrawFun = { batch, x, y ->
|
it.first.last().extraDrawFun = { batch, x, y ->
|
||||||
val px = x
|
val px = x
|
||||||
val py = y + doc.lineHeightInPx - 1
|
val py = y + doc.lineHeightInPx - 1
|
||||||
val pw = doc.textWidth - 2f * MARGIN_TITLE_TEXTS
|
val pw = doc.textWidth - 2f * MARGIN_TITLE_TEXTS
|
||||||
@@ -1462,7 +1457,7 @@ object BTeXParser {
|
|||||||
batch.color = Color.WHITE
|
batch.color = Color.WHITE
|
||||||
Toolkit.fillArea(batch, px, py, pw, 1f)
|
Toolkit.fillArea(batch, px, py, pw, 1f)
|
||||||
}
|
}
|
||||||
it.last().extraPixmapDrawFun = { pixmap, x, y ->
|
it.first.last().extraPixmapDrawFun = { pixmap, x, y ->
|
||||||
val px = x
|
val px = x
|
||||||
val py = y + doc.lineHeightInPx - 1
|
val py = y + doc.lineHeightInPx - 1
|
||||||
val pw = doc.textWidth - 2 * MARGIN_TITLE_TEXTS
|
val pw = doc.textWidth - 2 * MARGIN_TITLE_TEXTS
|
||||||
@@ -1472,9 +1467,7 @@ object BTeXParser {
|
|||||||
pixmap.fillRectangle(px, py, pw, 1)
|
pixmap.fillRectangle(px, py, pw, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
it.forEach {
|
it.moveObjectsAround(MARGIN_TITLE_TEXTS, 0)
|
||||||
it.posX += MARGIN_TITLE_TEXTS
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1485,11 +1478,7 @@ object BTeXParser {
|
|||||||
handler,
|
handler,
|
||||||
doc.textWidth - 2*MARGIN_TITLE_TEXTS,
|
doc.textWidth - 2*MARGIN_TITLE_TEXTS,
|
||||||
align = "left"
|
align = "left"
|
||||||
).also {
|
).moveObjectsAround(MARGIN_TITLE_TEXTS, 0)
|
||||||
it.forEach {
|
|
||||||
it.posX += MARGIN_TITLE_TEXTS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun typesetPartHeading(num: String, thePar: String, handler: BTeXHandler, indent: Int = PAR_INDENTATION, width: Int = doc.textWidth) {
|
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"
|
val header = if (num == null) thePar else "$num${spacingBlockToString(9)}$thePar"
|
||||||
typesetParagraphs("\n$ccDefault$header", handler, width - indent, align = "left").also {
|
typesetParagraphs("\n$ccDefault$header", handler, width - indent, align = "left").also {
|
||||||
// add indents and adjust text y pos
|
// add indents and adjust text y pos
|
||||||
it.forEach {
|
it.moveObjectsAround(indent, -doc.lineHeightInPx / 2)
|
||||||
it.posX += indent
|
|
||||||
it.posY -= doc.lineHeightInPx / 2
|
|
||||||
}
|
|
||||||
// add ornamental column on the left
|
// add ornamental column on the left
|
||||||
it.forEach {
|
it.first.forEach {
|
||||||
it.extraDrawFun = { batch, x, y ->
|
it.extraDrawFun = { batch, x, y ->
|
||||||
val oldCol = batch.color.cpy()
|
val oldCol = batch.color.cpy()
|
||||||
batch.color = DEFAULT_ORNAMENTS_COL.cpy().also { it.a *= bodyTextShadowAlpha }
|
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) {
|
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 {
|
typesetParagraphs("\n$ccDefault$num${spacingBlockToString(9)}$thePar", handler, width - indent, align = "left").also {
|
||||||
// add indents and adjust text y pos
|
// add indents and adjust text y pos
|
||||||
it.forEach {
|
it.moveObjectsAround(indent, -doc.lineHeightInPx / 2)
|
||||||
it.posX += indent
|
|
||||||
it.posY -= doc.lineHeightInPx / 2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1606,7 +1591,7 @@ object BTeXParser {
|
|||||||
height: Int = doc.pageLines,
|
height: Int = doc.pageLines,
|
||||||
startingPage: Int = doc.currentPage,
|
startingPage: Int = doc.currentPage,
|
||||||
align: String
|
align: String
|
||||||
): List<BTeXDrawCall> {
|
): Pair<List<BTeXDrawCall>, List<BTeXClickable>> {
|
||||||
return typesetParagraphs(getFont(), thePar, handler, width, height, startingPage, align)
|
return typesetParagraphs(getFont(), thePar, handler, width, height, startingPage, align)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1618,7 +1603,7 @@ object BTeXParser {
|
|||||||
height: Int = doc.pageLines,
|
height: Int = doc.pageLines,
|
||||||
startingPage: Int = doc.currentPage,
|
startingPage: Int = doc.currentPage,
|
||||||
align: String
|
align: String
|
||||||
): List<BTeXDrawCall> {
|
): Pair<List<BTeXDrawCall>, List<BTeXClickable>> {
|
||||||
val strat = when (align) {
|
val strat = when (align) {
|
||||||
"left" -> TypesettingStrategy.RAGGED_RIGHT
|
"left" -> TypesettingStrategy.RAGGED_RIGHT
|
||||||
"right" -> TypesettingStrategy.RAGGED_LEFT
|
"right" -> TypesettingStrategy.RAGGED_LEFT
|
||||||
@@ -1630,6 +1615,7 @@ object BTeXParser {
|
|||||||
var pageNum = startingPage
|
var pageNum = startingPage
|
||||||
|
|
||||||
val drawCalls = ArrayList<BTeXDrawCall>()
|
val drawCalls = ArrayList<BTeXDrawCall>()
|
||||||
|
val clickables = ArrayList<BTeXClickable>()
|
||||||
|
|
||||||
var remainder = height - doc.linesPrintedOnPage.last()
|
var remainder = height - doc.linesPrintedOnPage.last()
|
||||||
var slugHeight = slugs.height
|
var slugHeight = slugs.height
|
||||||
@@ -1653,18 +1639,57 @@ object BTeXParser {
|
|||||||
doc.appendDrawCall(doc.pages[pageNum], it); drawCalls.add(it)
|
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)
|
val hrefs = parseAndGetHref(textDrawCalls[0], font, handler, posYline, slugs, subset.first, subset.second)
|
||||||
hrefs.forEach {
|
var objectIsSplit = false
|
||||||
|
hrefs.forEach { (hrefObj, objSeq) ->
|
||||||
// search for:
|
// search for:
|
||||||
// ..... [OBJ:RSETNFAOON]word setaf
|
// ..... [OBJ:RSETNFAOON]word setaf
|
||||||
// get width of "word"
|
// get width of "word"
|
||||||
val searchStr = slugs.typesettedSlugs.subList(subset.first, subset.first + subset.second)
|
val searchStrs = slugs.typesettedSlugs.subList(subset.first, subset.first + subset.second)
|
||||||
printdbg("HREF searchStr: ${searchStr.joinToString { it.toReadable() }}")
|
searchStrs.forEach { str ->
|
||||||
// val clickable = BTeXClickable(it.x, it.y, undefined, doc.lineHeightInPx) { viewer ->
|
printdbg("1HREF searchStr: ${str.toReadable()}")
|
||||||
// viewer.gotoIndex(it.hrefTarget)
|
printdbg("1HREF object: ${objSeq.toReadable()} (id=${hrefDict[objSeq.toReadable()]})")
|
||||||
// }
|
|
||||||
// doc.appendClickable(doc.pages[pageNum], it)
|
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
|
linesOut += remainder
|
||||||
slugHeight -= remainder
|
slugHeight -= remainder
|
||||||
@@ -1687,9 +1712,53 @@ object BTeXParser {
|
|||||||
}
|
}
|
||||||
// >>> HREF code here!! <<<
|
// >>> HREF code here!! <<<
|
||||||
val hrefs = parseAndGetHref(textDrawCalls[0], font, handler, posYline, slugs, subset.first, subset.second)
|
val hrefs = parseAndGetHref(textDrawCalls[0], font, handler, posYline, slugs, subset.first, subset.second)
|
||||||
hrefs.forEach {
|
var objectIsSplit = false
|
||||||
val searchStr = slugs.typesettedSlugs.subList(subset.first, subset.first + subset.second)
|
hrefs.forEach { (hrefObj, objSeq) ->
|
||||||
printdbg("HREF searchStr: ${searchStr.joinToString { it.toReadable() }}")
|
// 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!! <<<
|
// >>> 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 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
|
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> {
|
private fun textToDrawCall(handler: BTeXHandler, posYline: Int, slugs: MovableType, lineStart: Int, lineCount: Int): List<BTeXDrawCall> {
|
||||||
@@ -1743,7 +1812,7 @@ object BTeXParser {
|
|||||||
c += 1
|
c += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!idbuf.startsWith("HREF@")) {
|
if (idbuf.isNotBlank() && !idbuf.startsWith("HREF@")) {
|
||||||
out.add(BTeXDrawCall(
|
out.add(BTeXDrawCall(
|
||||||
doc, x, y, currentTheme,
|
doc, x, y, currentTheme,
|
||||||
cmd = objDict[idbuf.toString()]?.invoke(textDrawCall)
|
cmd = objDict[idbuf.toString()]?.invoke(textDrawCall)
|
||||||
@@ -1765,8 +1834,8 @@ object BTeXParser {
|
|||||||
slugs: MovableType,
|
slugs: MovableType,
|
||||||
lineStart: Int,
|
lineStart: Int,
|
||||||
lineCount: Int
|
lineCount: Int
|
||||||
): List<_HrefObject> {
|
): List<Pair<_HrefObject, CodepointSequence>> {
|
||||||
val out = ArrayList<_HrefObject>()
|
val out = ArrayList<Pair<_HrefObject, CodepointSequence>>()
|
||||||
|
|
||||||
slugs.typesettedSlugs.subList(lineStart, lineStart + lineCount).forEachIndexed { lineNumCnt, line ->
|
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 ->
|
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
|
// get OBJ id
|
||||||
val idbuf = StringBuilder()
|
val idbuf = StringBuilder()
|
||||||
|
val cpseq = CodepointSequence()
|
||||||
|
|
||||||
var c = xIndex + 1
|
var c = xIndex + 1
|
||||||
while (true) {
|
while (true) {
|
||||||
val codepoint = line[c]
|
val codepoint = line[c]
|
||||||
if (codepoint == 0xFFF9F) break
|
if (codepoint == 0xFFF9F) break
|
||||||
idbuf.append(codepointToObjIdChar(codepoint))
|
idbuf.append(codepointToObjIdChar(codepoint))
|
||||||
|
cpseq.add(codepoint)
|
||||||
c += 1
|
c += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idbuf.startsWith("HREF@")) {
|
if (idbuf.isNotBlank() && idbuf.startsWith("HREF@")) {
|
||||||
val id = hrefDict[idbuf.toString()]!!
|
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
|
val dotPosEnd = typeWidth - pageNumWidth - dotGap*1.5f
|
||||||
|
|
||||||
typesetParagraphs("$ccDefault$heading$name", handler, typeWidth - pageNumWidth - dotGap, startingPage = pageToWrite ?: doc.currentPage, align = "justify").let {
|
typesetParagraphs("$ccDefault$heading$name", handler, typeWidth - pageNumWidth - dotGap, startingPage = pageToWrite ?: doc.currentPage, align = "justify").let {
|
||||||
it.forEach {
|
it.moveObjectsAround(indentation, 0)
|
||||||
it.posX += indentation
|
|
||||||
|
|
||||||
// println("pos: (${it.posX}, ${it.posY})\tTOC: $name")
|
it.first.last { it.text != null }.let { call ->
|
||||||
}
|
|
||||||
|
|
||||||
it.last { it.text != null }.let { call ->
|
|
||||||
call.extraDrawFun = { batch, x, y ->
|
call.extraDrawFun = { batch, x, y ->
|
||||||
val oldCol = batch.color.cpy()
|
val oldCol = batch.color.cpy()
|
||||||
|
|
||||||
@@ -1916,10 +1983,12 @@ object BTeXParser {
|
|||||||
val ccItemName = TerrarumSansBitmap.toColorCode(0xf03b)
|
val ccItemName = TerrarumSansBitmap.toColorCode(0xf03b)
|
||||||
val ccTargetName = TerrarumSansBitmap.toColorCode(0xf170)
|
val ccTargetName = TerrarumSansBitmap.toColorCode(0xf170)
|
||||||
|
|
||||||
|
private const val NUL = 0
|
||||||
private const val ZWSP = 0x200B
|
private const val ZWSP = 0x200B
|
||||||
private const val SHY = 0xAD
|
private const val SHY = 0xAD
|
||||||
private const val NBSP = 0xA0
|
private const val NBSP = 0xA0
|
||||||
private const val OBJ = 0xFFFC
|
private const val OBJ = 0xFFFC
|
||||||
|
private const val OBJ_END = 0xFFF9F
|
||||||
private const val SPACING_BLOCK_ONE = 0xFFFD0
|
private const val SPACING_BLOCK_ONE = 0xFFFD0
|
||||||
private const val SPACING_BLOCK_SIXTEEN = 0xFFFDF
|
private const val SPACING_BLOCK_SIXTEEN = 0xFFFDF
|
||||||
|
|
||||||
@@ -1966,7 +2035,10 @@ object BTeXParser {
|
|||||||
|
|
||||||
idstr.add(0xFFF9F)
|
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) {
|
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 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) {
|
class BTeXParsingException(s: String) : RuntimeException(s) {
|
||||||
|
|||||||
Reference in New Issue
Block a user