diff --git a/assets/mods/basegame/books/btex.xml b/assets/mods/basegame/books/btex.xml
index 18989fcdf..bc18f1809 100644
--- a/assets/mods/basegame/books/btex.xml
+++ b/assets/mods/basegame/books/btex.xml
@@ -169,12 +169,12 @@
<img src="http(s) or file URL" height="8"/>
<img fromgame="basegame:gui/small.png" height="4"/>
<img gameitem="basegame:33" height="1"/>
+ --><img fromgame="basegame:gui/small.png" height="4"/>
The height attribute specifies the height of the image in the number of lines,
- rather than pixels, the width is calculated automatically. Image width wider than the text width will cause an error.
+ rather than pixels, the width is calculated automatically; image width wider than the text width
+ will cause an error. The tag optionally takes caption attribute which prints a text below the image.
Supported image formats: JPEG, PNG, BMP or TGA
@@ -304,7 +304,9 @@
system is there because the book is being printed in the background on separate threads
(yes, they are multi-threaded!) to not interfere with the normal gameplay, or else the players will
encounter the freezing every time the book is being printed, and this would be a huge minus towards
- the gameplay experience. If the process exits without any error, the mailing system will be
+ the gameplay experience.
+
+ If the process exits without any error, the mailing system will be
notified and will send the mail containing finished books to the player; if the process exits
with errors, the mail containing details of the errors will be sent instead.
diff --git a/assets/mods/basegame/books/btex_ko.xml b/assets/mods/basegame/books/btex_ko.xml
index 967be8ba4..a1dff909e 100644
--- a/assets/mods/basegame/books/btex_ko.xml
+++ b/assets/mods/basegame/books/btex_ko.xml
@@ -142,7 +142,7 @@
left — 문단을 좌측정렬함
right — 문단을 우측정렬함
center — 문단을 중앙정렬함
- justify — 문단을 양끝정렬함. 기본값
+ justify — 문단을 양쪽정렬함. 기본값
p, span, callout 태그는 추가로 class="code" 속성을 지원한다. 이 속성이 적용된 문구는 코드꼴(code font)을 사용하여 인자된다.
@@ -162,11 +162,10 @@
<img src="http(s)나 file URL" height="8"/>
<img fromgame="basegame:gui/small.png" height="4"/>
<img gameitem="basegame:33" height="1"/>
+ --><img fromgame="basegame:gui/small.png" height="4"/>
- height 속성은 이미지의 높이를 픽셀 단위가 아닌 줄의 개수로 지정하어야 한다. 이미지의 너비는 자동으로 계산된다. 텍스트의 너비보다 넓다면 오류가 발생한다.
+ height 속성은 이미지의 높이를 픽셀 단위가 아닌 줄의 개수로 지정하어야 한다. 이미지의 너비는 자동으로 계산되며, 텍스트의 너비보다 넓다면 오류가 발생한다. 이미지 하단에 문구를 출력하려면 caption 속성을 사용할 수 있다.
지원하는 이미지 포맷: JPEG·PNG·BMP·TGA
@@ -268,8 +267,15 @@
-->[koKR] 원고가 담긴 홀로테이프를 출판사로 우편을 통해 보내야 책이 인쇄됩니다.
+ 일반인 출입금지
+ 왜 책이 인쇄될 때까지 기다려야 하나요? 인쇄기를 직접 사용할 수는 없나요?
+ 엔진은 빠른 물건이 아니다. 책을 하나 조판하려면 최소 몇 초가 필요하다. “대기 시스템”은 별도의 스레드 풀에서 (멀티스레딩으로 작동함) 책이 인쇄되도록 하여 게임플레이를 방해하지 않도록 도입되었다. 이것이 없다면 책이 한번 인쇄될 때마다 게임이 ‘응답 없음’ 상태가 될 것이고, 이는 인게임 경험에 매우 부정적으로 작용한다.
+
+ 인쇄 프로세스가 오류 없이 종료되었다면, 우편 시스템을 통해 플레이어에게 완성된 책을 배송한다. 오류가 있다면 오류 정보를 담은 우편이 대신 배송된다.
+
+ 상기와 같은 이유로 “인쇄기”는 플레이어에게 직접적으로 제공되지 않고, “출판사”와 우편을 통해 간접적으로 이용하는 형태로 제공된다.
diff --git a/btexdoc.dtd b/btexdoc.dtd
index 5ff3ee26b..21f946d33 100644
--- a/btexdoc.dtd
+++ b/btexdoc.dtd
@@ -48,6 +48,7 @@
"src CDATA #IMPLIED
fromgame CDATA #IMPLIED
gameitem CDATA #IMPLIED
+ caption %Text; #IMPLIED
height %Number; #REQUIRED">
FileOutputStream(tempFile.file()).use { fileOutputStream ->
val dataBuffer = ByteArray(1024)
@@ -954,39 +960,47 @@ object BTeXParser {
}
Pixmap(tempFile).also { App.disposables.add(it) }
}
-
- val imgWidth = (imgHeight.toFloat() / inputPixmap.height * inputPixmap.width).roundToInt()
-
- if (imgWidth > doc.textWidth)
- throw RuntimeException("Image width ($imgWidth) is larger than the text width (${doc.textWidth})")
-
- val drawCallObj = { parentText: BTeXDrawCall -> object : BTeXBatchDrawCall(imgWidth, (heightInLines - 1).coerceAtLeast(0), parentText) {
- private lateinit var inputTexture: Texture
- override fun draw(doc: BTeXDocument, batch: SpriteBatch, x: Float, y: Float, font: TerrarumSansBitmap?) {
- if (!::inputTexture.isInitialized) {
- inputTexture = Texture(inputPixmap).also { App.disposables.add(it) }
- }
- val destX = (x + (doc.pageDimensionWidth - imgWidth) / 2)
- val destY = y + 1
- batch.draw(inputTexture, destX, destY, imgWidth.toFloat(), imgHeight.toFloat())
- }
- override fun drawToPixmap(doc: BTeXDocument, pixmap: Pixmap, x: Int, y: Int, font: TerrarumSansBitmap?) {
- val destX = x
- val destY = y + 1
- pixmap.drawPixmap(inputPixmap, 0, 0, inputPixmap.width, inputPixmap.height, destX, destY, imgWidth, imgHeight)
- }
- } }
-
- objDict[btexObjName] = { text: BTeXDrawCall -> drawCallObj(text) }
- objWidthDict[btexObjName] = imgWidth
-
- typesetParagraphs(objectMarkerWithWidth(btexObjName, imgWidth), handler, align = "center")
}
- catch (e: IOException) { }
- catch (e: GdxRuntimeException) { }
- finally {
- tempFile.delete()
+ else if (fromgame != null) {
+ val moduleName = fromgame.substringBefore(':')
+ val modulePath = fromgame.substringAfter(':')
+ Pixmap(ModMgr.getGdxFile(moduleName, modulePath))
}
+ else throw InternalError()
+
+ val imgWidth = (imgHeight.toFloat() / inputPixmap.height * inputPixmap.width).roundToInt()
+
+ if (imgWidth > doc.textWidth)
+ throw RuntimeException("Image width ($imgWidth) is larger than the text width (${doc.textWidth})")
+
+ val drawCallObj = { parentText: BTeXDrawCall -> object : BTeXBatchDrawCall(imgWidth, (heightInLines - 1).coerceAtLeast(0), parentText) {
+ private lateinit var inputTexture: Texture
+ override fun draw(doc: BTeXDocument, batch: SpriteBatch, x: Float, y: Float, font: TerrarumSansBitmap?) {
+ if (!::inputTexture.isInitialized) {
+ inputTexture = Texture(inputPixmap).also { App.disposables.add(it) }
+ }
+ val destX = (x + (doc.pageDimensionWidth - imgWidth) / 2)
+ val destY = y + 1
+ batch.draw(inputTexture, destX, destY, imgWidth.toFloat(), imgHeight.toFloat())
+ }
+ override fun drawToPixmap(doc: BTeXDocument, pixmap: Pixmap, x: Int, y: Int, font: TerrarumSansBitmap?) {
+ val destX = x
+ val destY = y + 1
+ pixmap.drawPixmap(inputPixmap, 0, 0, inputPixmap.width, inputPixmap.height, destX, destY, imgWidth, imgHeight)
+ }
+ } }
+
+ objDict[btexObjName] = { text: BTeXDrawCall -> drawCallObj(text) }
+ objWidthDict[btexObjName] = imgWidth
+
+ typesetParagraphs(objectMarkerWithWidth(btexObjName, imgWidth), handler, align = "center")
+ if (caption != null)
+ typesetParagraphs("$ccDefault$caption", handler, align = "center")
+ }
+ catch (e: IOException) { }
+ catch (e: GdxRuntimeException) { }
+ finally {
+ tempFile.delete()
}
}
@@ -1697,7 +1711,7 @@ object BTeXParser {
val dotGap = 10
val dotPosEnd = typeWidth - pageNumWidth - dotGap*1.5f
- typesetParagraphs("$ccDefault$heading$name", handler, typeWidth, 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.posX += indentation
diff --git a/src/net/torvald/terrarum/tests/BTeXTest.kt b/src/net/torvald/terrarum/tests/BTeXTest.kt
index 4bdfdb6a3..5534b533f 100644
--- a/src/net/torvald/terrarum/tests/BTeXTest.kt
+++ b/src/net/torvald/terrarum/tests/BTeXTest.kt
@@ -27,11 +27,11 @@ import kotlin.system.measureTimeMillis
class BTeXTest : ApplicationAdapter() {
// val filePath = "btex.xml"
-// val filePath = "btex_ko.xml"
- val filePath = "test.xml"
+ val filePath = "btex_ko.xml"
+// val filePath = "test.xml"
// val filePath = "literature/en/daniel_defoe_robinson_crusoe.xml"
// val filePath = "literature/ruRU/anton_chekhov_palata_no_6.xml"
-// val filePath = "literature/koKR/yisang_nalgae.btxbook"
+// val filePath = "literature/koKR/yisang_nalgae.xml"
private lateinit var document: BTeXDocument