diff --git a/src/net/torvald/btex/BTeXDocument.kt b/src/net/torvald/btex/BTeXDocument.kt index 2091aa000..634ad439d 100644 --- a/src/net/torvald/btex/BTeXDocument.kt +++ b/src/net/torvald/btex/BTeXDocument.kt @@ -1,10 +1,8 @@ package net.torvald.terrarum.btex import com.badlogic.gdx.Gdx -import com.badlogic.gdx.graphics.Color -import com.badlogic.gdx.graphics.OrthographicCamera -import com.badlogic.gdx.graphics.Pixmap -import com.badlogic.gdx.graphics.PixmapIO +import com.badlogic.gdx.files.FileHandle +import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.glutils.FrameBuffer @@ -16,9 +14,11 @@ import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.Clustf import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClustfileOutputStream import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.ui.Toolkit +import net.torvald.terrarum.utils.JsonFetcher import net.torvald.terrarumsansbitmap.MovableType import net.torvald.terrarumsansbitmap.gdx.CodepointSequence import java.io.File +import java.io.RandomAccessFile import java.util.zip.Deflater /** @@ -36,7 +36,7 @@ class BTeXDocument : Disposable { var theEdition = "" var textWidth = 480 - var lineHeightInPx = 24 + val lineHeightInPx = 24 var pageLines = 24 val textHeight: Int get() = pageLines * lineHeightInPx @@ -58,6 +58,60 @@ class BTeXDocument : Disposable { val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff) private fun String.escape() = this.replace("\"", "\\\"") + + fun fromFile(fileHandle: FileHandle) = fromFile(fileHandle.file()) + + fun fromFile(file: File): BTeXDocument { + val doc = BTeXDocument() + + val ra = RandomAccessFile(file, "r") + val DOM = ClusteredFormatDOM(ra) + + // get meta file + val meta = Clustfile(DOM, "/bibliography.json") + if (!meta.exists()) throw IllegalStateException("No bibliography.json found on the archive") + val metaReader = meta.readBytes().toString(Common.CHARSET).reader() + val metaJson = JsonFetcher.readFromJsonString(metaReader) + + doc.theTitle = metaJson["title"].asString() + doc.theSubtitle = metaJson["subtitle"].asString() + doc.theAuthor = metaJson["author"].asString() + doc.theEdition = metaJson["edition"].asString() + val pageCount = metaJson["pages"].asInt() + doc.context = metaJson["context"].asString() + doc.font = metaJson["font"].asString() + doc.inner = metaJson["inner"].asString() + doc.papersize = metaJson["papersize"].asString() + doc.fromArchive = true + doc.pageTextures = ArrayList() + + + println("Title: ${doc.theTitle}") + println("Pages: $pageCount") + + for (page in 0 until pageCount) { + Clustfile(DOM, "/${page}.png").also { + if (!it.exists()) throw IllegalStateException("No file '${page}.png' on the archive") + + val tempFile = Gdx.files.external("./.btex-import.png") // must create new file descriptor for every page, or else every page will share a single file descriptor which cause problems + it.exportFileTo(tempFile.file()) + val texture = TextureRegion(Texture(tempFile)) + doc.pageTextures.add(texture) + + if (page == 0) { + doc.textWidth = texture.regionWidth - 2 * doc.pageMarginH + doc.pageLines = (texture.regionHeight - 2 * doc.pageMarginH) / doc.lineHeightInPx + + println("Page dimension: (${texture.regionWidth}x${texture.regionHeight}) (${doc.pageDimensionWidth}x${doc.pageDimensionHeight})") + } + tempFile.delete() // deleting also affects file descriptor juggling + } + } + + ra.close() + + return doc + } } internal val pages = ArrayList() @@ -72,7 +126,7 @@ class BTeXDocument : Disposable { get() = pages[currentPage] val pageIndices: IntRange - get() = pages.indices + get() = if (fromArchive) pageTextures.indices else pages.indices internal val linesPrintedOnPage = ArrayList() @@ -92,6 +146,7 @@ class BTeXDocument : Disposable { * Must be called on a thread with GL context! */ fun finalise() { + if (fromArchive) throw IllegalStateException("Document is loaded from the archive and thus cannot be finalised") if (isFinalised) throw IllegalStateException("Page is already been finalised") pageTextures = ArrayList() @@ -100,7 +155,7 @@ class BTeXDocument : Disposable { val camera = OrthographicCamera(pageDimensionWidth.toFloat(), pageDimensionHeight.toFloat()) val batch = FlippingSpriteBatch() - pages.forEach { page -> + pages.forEachIndexed { pageNum, page -> val fbo = FrameBuffer(Pixmap.Format.RGBA8888, pageDimensionWidth, pageDimensionHeight, false) fbo.inAction(null, null) { @@ -113,6 +168,7 @@ class BTeXDocument : Disposable { blendNormalStraightAlpha(batch) batch.inUse { page.render(0f, batch, 0, 0, pageMarginH, pageMarginV) + printPageNumber(batch, pageNum, 0, 0) } } @@ -129,6 +185,9 @@ class BTeXDocument : Disposable { pageTextures.forEach { it.texture.dispose() } pageFrameBuffers.forEach { it.dispose() } } + else if (fromArchive) { + pageTextures.forEach { it.texture.dispose() } + } } fun serialise(archiveFile: File) { @@ -136,7 +195,6 @@ class BTeXDocument : Disposable { val diskFile = ClusteredFormatDOM.createNewArchive(archiveFile, Common.CHARSET, "", 0x7FFFF) val DOM = ClusteredFormatDOM(diskFile) - val tempFile = Gdx.files.external("./.btex-export.png") val json = """ { @@ -164,18 +222,20 @@ class BTeXDocument : Disposable { fbo.inAction(null, null) { val pixmap = Pixmap.createFromFrameBuffer(0, 0, fbo.width, fbo.height) + val tempFile = Gdx.files.external("./.btex-export.png") PixmapIO.writePNG(tempFile, pixmap, Deflater.BEST_COMPRESSION, false) val outstream = ClustfileOutputStream(file) outstream.write(tempFile.readBytes()) outstream.flush(); outstream.close() + tempFile.delete() } } DOM.changeDiskCapacity(diskFile.length().div(4096f).ceilToInt()) - tempFile.delete() } var isFinalised = false; private set + var fromArchive = false; private set /** * Appends draw call to the list. The draw call must be prepared manually so that they would not overflow. @@ -199,13 +259,16 @@ class BTeXDocument : Disposable { fun render(frameDelta: Float, batch: SpriteBatch, page: Int, x: Int, y: Int) { batch.color = Color.WHITE - if (!isFinalised) - pages[page].render(frameDelta, batch, x, y, pageMarginH, pageMarginV) - else + if (isFinalised || fromArchive) batch.draw(pageTextures[page], x.toFloat(), y.toFloat()) + else { + pages[page].render(frameDelta, batch, x, y, pageMarginH, pageMarginV) + printPageNumber(batch, page, x, y) + } + } - // paint page number - val num = "${page+1}" + private fun printPageNumber(batch: SpriteBatch, page: Int, x: Int, y: Int) { + val num = "${page + 1}" val numW = TinyAlphNum.getWidth(num) val numX = if (context == "tome") { if (page % 2 == 1) @@ -216,7 +279,7 @@ class BTeXDocument : Disposable { else { x + (pageDimensionWidth - numW) / 2 } - val numY = y + pageDimensionHeight - 2*pageMarginV - 4 + val numY = y + pageDimensionHeight - 2 * pageMarginV - 4 if (page == 0 && context != "tome" || page in tocPageStart until endOfPageStart) { batch.color = DEFAULT_ORNAMENTS_COL diff --git a/src/net/torvald/terrarum/tests/BTeXTest.kt b/src/net/torvald/terrarum/tests/BTeXTest.kt index 42b8a3416..6062331d1 100644 --- a/src/net/torvald/terrarum/tests/BTeXTest.kt +++ b/src/net/torvald/terrarum/tests/BTeXTest.kt @@ -25,8 +25,8 @@ import kotlin.system.measureTimeMillis */ class BTeXTest : ApplicationAdapter() { - val filePath = "btex.xml" -// val filePath = "literature/en/daniel_defoe_robinson_crusoe.xml" +// val filePath = "btex.btexbin" + val filePath = "literature/en/daniel_defoe_robinson_crusoe.btexbin" // val filePath = "literature/ruRU/anton_chekhov_palata_no_6.xml" // val filePath = "literature/koKR/yisang_nalgae.xml" @@ -47,25 +47,36 @@ class BTeXTest : ApplicationAdapter() { bg = TextureRegion(Texture(Gdx.files.internal("test_assets/real_bg_with_guides.png"))) - measureTimeMillis { - val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath")) - document = f.first - documentHandler = f.second - }.also { - println("Time spent on typesetting [ms]: $it") - } + val isBookFinalised = filePath.endsWith(".btexbin") - measureTimeMillis { - document.finalise() - documentHandler.dispose() - }.also { - println("Time spent on finalising [ms]: $it") - } + if (!isBookFinalised) { + measureTimeMillis { + val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath")) + document = f.first + documentHandler = f.second + }.also { + println("Time spent on typesetting [ms]: $it") + } - measureTimeMillis { - document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btexbin")}")) - }.also { - println("Time spent on serialisation [ms]: $it") + measureTimeMillis { + document.finalise() + documentHandler.dispose() + }.also { + println("Time spent on finalising [ms]: $it") + } + + measureTimeMillis { + document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btexbin")}")) + }.also { + println("Time spent on serialisation [ms]: $it") + } + } + else { + measureTimeMillis { + document = BTeXDocument.fromFile(Gdx.files.internal("./assets/mods/basegame/books/$filePath")) + }.also { + println("Time spent on loading [ms]: $it") + } } }