diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index d160b9b..3a2729c 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -7,15 +7,18 @@
-
-
+
+
+
+
+
@@ -32,20 +35,20 @@
-
+
-
-
+
+
-
+
-
-
+
+
@@ -53,7 +56,7 @@
-
+
@@ -66,8 +69,8 @@
-
-
+
+
@@ -78,8 +81,8 @@
-
-
+
+
@@ -87,8 +90,8 @@
-
-
+
+
@@ -98,7 +101,7 @@
-
+
@@ -106,8 +109,8 @@
-
-
+
+
@@ -116,8 +119,8 @@
-
-
+
+
@@ -126,11 +129,11 @@
-
+
-
+
@@ -215,13 +218,13 @@
-
-
-
-
-
+
+
+
+
+
@@ -514,18 +517,18 @@
-
+
+
-
+
-
@@ -839,57 +842,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -914,11 +866,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -926,10 +899,40 @@
-
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dd33fdf..ff1b8ce 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -48,7 +48,7 @@ Green-tinted area (should be 10 px tall) contains the tags. Tags are defined as
1 -+ 8 | (if this is zero, floorOf(width/2) will be used instead)
0 -+ 0 Align 1 Align 0 Align 1 Align before
1 -+ 0 left 0 right 1 centre 1 the glyph
- 0 == Write on top of prev chars (e.g. diacritics)
+ 1 == write-on-top, usually it's diatritics but some are not (e.g. devanagari vowel sign O)
1 == 0 Stack 1 Stack 0 Before 1 Up &
(MSB) 0 == 0 up 0 down 1 &After 1 Down (e.g. U+0C48)
```
diff --git a/FontTestGDX/lib/TerrarumSansBitmap.jar b/FontTestGDX/lib/TerrarumSansBitmap.jar
index c15e6d4..d8a30a6 100644
Binary files a/FontTestGDX/lib/TerrarumSansBitmap.jar and b/FontTestGDX/lib/TerrarumSansBitmap.jar differ
diff --git a/FontTestGDX/src/FontTestGDX.kt b/FontTestGDX/src/FontTestGDX.kt
index 3027ace..c055cdf 100644
--- a/FontTestGDX/src/FontTestGDX.kt
+++ b/FontTestGDX/src/FontTestGDX.kt
@@ -23,8 +23,8 @@ class FontTestGDX : Game() {
lateinit var camera: OrthographicCamera
- private val demotextName = "testtext.txt"
- private val outimageName = "testing.png"
+ private val demotextName = "demotext.txt"
+ private val outimageName = "demo.png"
override fun create() {
font = GameFontBase("./assets", flipY = false, errorOnUnknownChar = true) // must test for two flipY cases
@@ -256,7 +256,7 @@ class FontTestGDX : Game() {
lateinit var appConfig: LwjglApplicationConfiguration
const val TEXW = 874
-const val TEXH = 2060
+const val TEXH = 2400
fun main(args: Array) {
appConfig = LwjglApplicationConfiguration()
diff --git a/assets/ascii_variable.tga b/assets/ascii_variable.tga
index 84563e3..24a6bff 100644
Binary files a/assets/ascii_variable.tga and b/assets/ascii_variable.tga differ
diff --git a/assets/devanagari_bengali_variable.tga b/assets/devanagari_bengali_variable.tga
index 5ec8689..9c993d5 100644
Binary files a/assets/devanagari_bengali_variable.tga and b/assets/devanagari_bengali_variable.tga differ
diff --git a/assets/unipunct_variable.tga b/assets/unipunct_variable.tga
index 9be4fd9..c3ab5f8 100644
Binary files a/assets/unipunct_variable.tga and b/assets/unipunct_variable.tga differ
diff --git a/demo.PNG b/demo.PNG
index 0164c90..a9b715a 100644
Binary files a/demo.PNG and b/demo.PNG differ
diff --git a/demotext.txt b/demotext.txt
index 37bdffc..f2c8ad6 100644
--- a/demotext.txt
+++ b/demotext.txt
@@ -26,6 +26,7 @@ How multilingual? Real multilingual!
գրիչս վայր դրի, վեր կացա և պատրաստվում էի, որ քնեմ, երբ հանկարծ դռանս զանգակը հնչեց
ՄՇԱԿԻՉ ԿԱՄ ԿԵՆՏՐՈՆԱԿԱՆ ՄՇԱԿԻՉ ՀԱՆԳՈՒՅՑԸ ՀԱՆԴԻՍԱՆՈՒՄ Է ՀԱՄԱԿԱՐԳՉԻ ՍԱՐՔԱՎՈՐՈՒՄՆԵՐԻՑ
Zəfər, jaketini də papağını da götür, bu axşam hava çox soyuq olacaq
+ আমি কাঁচ খেতে পারি, তাতে আমার কোনো ক্ষতি হয় না।
Под южно дърво, цъфтящо в синьо, бягаше малко пухкаво зайче
ᎠᏍᎦᏯᎡᎦᎢᎾᎨᎢᎣᏍᏓᎤᎩᏍᏗᎥᎴᏓᎯᎲᎢᏔᎵᏕᎦᏟᏗᏖᎸᎳᏗᏗᎧᎵᎢᏘᎴᎩ ᏙᏱᏗᏜᏫᏗᏣᏚᎦᏫᏛᏄᏓᎦᏝᏃᎠᎾᏗᎭᏞᎦᎯᎦᏘᏓᏠᎨᏏᏕᏡᎬᏢᏓᏥᏩᏝᎡᎢᎪᎢ
ᎠᎦᏂᏗᎮᎢᎫᎩᎬᏩᎴᎢᎠᏆᏅᏛᎫᏊᎾᎥᎠᏁᏙᎲᏐᏈᎵᎤᎩᎸᏓᏭᎷᏤᎢᏏᏉᏯᏌᏊ ᎤᏂᏋᎢᏡᎬᎢᎰᏩᎬᏤᎵᏍᏗᏱᎩᎱᎱᎤᎩᎴᎢᏦᎢᎠᏂᏧᏣᏨᎦᏥᎪᎥᏌᏊᎤᎶᏒᎢᎢᏡᎬᎢ
@@ -93,7 +94,7 @@ How multilingual? Real multilingual!
Unicode References:
Basic Latin Latin-1 Latin Extension A Latin Extionsion B IPA Extension Greek Cyrillic
- Cyrillic Supplement Armenian Devanagari Thai Georgian Runic Cherokee Georgian Extended
+ Cyrillic Supplement Armenian Devanagari Bengali Thai Georgian Runic Cherokee Georgian Extended
General Punctuations CJK Symbols Kana Kana Phonetic Extension CJK Unihan Extension A CJK Unihan
Hangul Syllables Fullwidth Forms Kana Supplement
diff --git a/src/net/torvald/terrarumsansbitmap/GlyphProps.kt b/src/net/torvald/terrarumsansbitmap/GlyphProps.kt
index b3ccd96..1930dad 100644
--- a/src/net/torvald/terrarumsansbitmap/GlyphProps.kt
+++ b/src/net/torvald/terrarumsansbitmap/GlyphProps.kt
@@ -9,7 +9,8 @@ data class GlyphProps(
val alignWhere: Int,
val alignXPos: Int,
val rtl: Boolean = false,
- val stackWhere: Int = 0
+ val stackWhere: Int = 0,
+ var extInfo: IntArray? = null
) {
companion object {
const val ALIGN_LEFT = 0
@@ -24,6 +25,8 @@ data class GlyphProps(
const val DIA_OVERLAY = 1
const val DIA_JOINER = 2
+
+ private fun Boolean.toInt() = if (this) 1 else 0
}
constructor(width: Int, tags: Int) : this(
@@ -36,4 +39,28 @@ data class GlyphProps(
)
fun isOverlay() = writeOnTop && alignXPos == 1
+
+ override fun hashCode(): Int {
+ val tags = rtl.toInt() or alignXPos.shl(1) or alignWhere.shl(5) or
+ writeOnTop.toInt().shl(7) or stackWhere.shl(8)
+
+ var hash = -2128831034
+
+ extInfo?.forEach {
+ hash = hash xor it
+ hash = hash * 16777619
+ }
+
+ hash = hash xor tags
+ hash = hash * 167677619
+
+ return hash
+ }
+
+ override fun equals(other: Any?): Boolean {
+ // comparing hash because I'm lazy
+ return other is GlyphProps && this.hashCode() == other.hashCode()
+ }
+
+ fun requiredExtInfoCount() = if (stackWhere == STACK_BEFORE_N_AFTER) 2 else 0
}
\ No newline at end of file
diff --git a/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt b/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt
index 55cfc26..be50b2d 100644
--- a/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt
+++ b/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt
@@ -31,7 +31,6 @@ import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.*
import net.torvald.terrarumsansbitmap.GlyphProps
import java.io.BufferedOutputStream
-import java.io.File
import java.io.FileOutputStream
import java.util.zip.GZIPInputStream
@@ -522,7 +521,9 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
posXbuffer[nonDiacriticCounter] + W_VAR_INIT + alignmentOffset + interchar
else
posXbuffer[nonDiacriticCounter] + itsProp.width + alignmentOffset + interchar
+
nonDiacriticCounter = charIndex
+
stackUpwardCounter = 0
stackDownwardCounter = 0
}
@@ -536,46 +537,53 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
else {
// set X pos according to alignment information
posXbuffer[charIndex] = when (thisProp.alignWhere) {
- GlyphProps.ALIGN_LEFT -> posXbuffer[nonDiacriticCounter]
+ GlyphProps.ALIGN_LEFT, GlyphProps.ALIGN_BEFORE -> posXbuffer[nonDiacriticCounter]
GlyphProps.ALIGN_RIGHT -> {
posXbuffer[nonDiacriticCounter] - (W_VAR_INIT - itsProp.width)
}
GlyphProps.ALIGN_CENTRE -> {
val alignXPos = if (itsProp.alignXPos == 0) itsProp.width.div(2) else itsProp.alignXPos
- posXbuffer[nonDiacriticCounter] + alignXPos - (W_VAR_INIT - 1).div(2)
+ if (itsProp.alignWhere == GlyphProps.ALIGN_RIGHT) {
+ posXbuffer[nonDiacriticCounter] + alignXPos + (itsProp.width + 1).div(2)
+ }
+ else {
+ posXbuffer[nonDiacriticCounter] + alignXPos - (W_VAR_INIT - 1).div(2)
+ }
}
else -> throw InternalError("Unsupported alignment: ${thisProp.alignWhere}")
}
// set Y pos according to diacritics position
- when (thisProp.stackWhere) {
- GlyphProps.STACK_DOWN -> {
- posYbuffer[charIndex] = H_DIACRITICS * stackDownwardCounter
- stackDownwardCounter++
- }
- GlyphProps.STACK_UP -> {
- posYbuffer[charIndex] = -H_DIACRITICS * stackUpwardCounter
-
- // shift down on lowercase if applicable
- if (getSheetType(thisChar) in autoShiftDownOnLowercase &&
- lastNonDiacriticChar.isLowHeight()) {
- if (thisProp.alignXPos == GlyphProps.DIA_OVERLAY)
- posYbuffer[charIndex] += H_OVERLAY_LOWERCASE_SHIFTDOWN
- else
- posYbuffer[charIndex] += H_STACKUP_LOWERCASE_SHIFTDOWN
+ if (thisProp.alignWhere == GlyphProps.ALIGN_CENTRE) {
+ when (thisProp.stackWhere) {
+ GlyphProps.STACK_DOWN -> {
+ posYbuffer[charIndex] = H_DIACRITICS * stackDownwardCounter
+ stackDownwardCounter++
}
+ GlyphProps.STACK_UP -> {
+ posYbuffer[charIndex] = -H_DIACRITICS * stackUpwardCounter
- stackUpwardCounter++
- }
- GlyphProps.STACK_UP_N_DOWN -> {
- posYbuffer[charIndex] = H_DIACRITICS * stackDownwardCounter
- stackDownwardCounter++
- posYbuffer[charIndex] = -H_DIACRITICS * stackUpwardCounter
- stackUpwardCounter++
- }
+ // shift down on lowercase if applicable
+ if (getSheetType(thisChar) in autoShiftDownOnLowercase &&
+ lastNonDiacriticChar.isLowHeight()) {
+ if (thisProp.alignXPos == GlyphProps.DIA_OVERLAY)
+ posYbuffer[charIndex] += H_OVERLAY_LOWERCASE_SHIFTDOWN
+ else
+ posYbuffer[charIndex] += H_STACKUP_LOWERCASE_SHIFTDOWN
+ }
+
+ stackUpwardCounter++
+ }
+ GlyphProps.STACK_UP_N_DOWN -> {
+ posYbuffer[charIndex] = H_DIACRITICS * stackDownwardCounter
+ stackDownwardCounter++
+ posYbuffer[charIndex] = -H_DIACRITICS * stackUpwardCounter
+ stackUpwardCounter++
+ }
// for BEFORE_N_AFTER, do nothing in here
+ }
}
}
}
@@ -972,6 +980,25 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
glyphWidth = -glyphWidth*/
glyphProps[code] = GlyphProps(width, tags)
+
+ // extra info
+ val extCount = glyphProps[code]?.requiredExtInfoCount() ?: 0
+ if (extCount > 0) {
+
+ glyphProps[code]?.extInfo = IntArray(extCount)
+
+ for (x in 0 until extCount) {
+ var info = 0
+ for (y in 0..18) {
+ // if ALPHA is not zero, assume it's 1
+ if (pixmap.getPixel(cellX + x, cellY + y).and(0xFF) != 0) {
+ info = info or (1 shl y)
+ }
+ }
+
+ glyphProps[code]!!.extInfo!![x] = info
+ }
+ }
}
}
@@ -998,7 +1025,9 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
glyphProps[0x1D79] = GlyphProps(9, 0)
- glyphProps[0xFFFD] = nullProp
+ // U+007F is DEL originally, but this font stores bitmap of Replacement Character (U+FFFD)
+ // to this position. String replacer will replace U+FFFD into U+007F.
+ glyphProps[0x7F] = GlyphProps(15, 0)
}
private val glyphLayout = GlyphLayout()
@@ -1021,15 +1050,34 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
if (i < this.lastIndex && c.isHighSurrogate()) {
val cNext = this[i + 1]
- if (!cNext.isLowSurrogate())
- throw IllegalArgumentException("Malformed UTF-16 String: High surrogate must be paired with low surrogate")
+ if (!cNext.isLowSurrogate()) {
+ // replace with Unicode replacement char
+ seq.add(0xFFFD)
+ }
+ else {
+ val H = c
+ val L = cNext
- val H = c
- val L = cNext
+ seq.add(Character.toCodePoint(H, L))
- seq.add(Character.toCodePoint(H, L))
-
- i++ // skip next char (guaranteed to be Low Surrogate)
+ i++ // skip next char (guaranteed to be Low Surrogate)
+ }
+ }
+ // rearrange {letter, before-and-after diacritics} as {letter, before-diacritics, after-diacritics}
+ // {letter, before-diacritics} part will be dealt with swapping code below
+ // DOES NOT WORK if said diacritics has codepoint > 0xFFFF
+ else if (i < this.lastIndex && this[i + 1].toInt() <= 0xFFFF &&
+ glyphProps[this[i + 1].toInt()]?.stackWhere == GlyphProps.STACK_BEFORE_N_AFTER) {
+ val diacriticsProp = glyphProps[this[i + 1].toInt()]!!
+ seq.add(c.toInt())
+ seq.add(diacriticsProp.extInfo!![0])
+ seq.add(diacriticsProp.extInfo!![1])
+ i++
+ }
+ // U+007F is DEL originally, but this font stores bitmap of Replacement Character (U+FFFD)
+ // to this position. This line will replace U+FFFD into U+007F.
+ else if (c == '�') {
+ seq.add(0x7F)
}
else {
seq.add(c.toInt())
@@ -1038,10 +1086,10 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
i++
}
-
// swap position of {letter, diacritics that comes before the letter}
i = 1
while (i <= seq.lastIndex) {
+
if ((glyphProps[seq[i]] ?: nullProp).alignWhere == GlyphProps.ALIGN_BEFORE) {
val t = seq[i - 1]
seq[i - 1] = seq[i]
@@ -1051,7 +1099,6 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
i++
}
-
return seq
}
diff --git a/testing.PNG b/testing.PNG
index 18a33b7..841c0c0 100644
Binary files a/testing.PNG and b/testing.PNG differ
diff --git a/testtext.txt b/testtext.txt
index a080c9e..a6d3739 100644
--- a/testtext.txt
+++ b/testtext.txt
@@ -1,22 +1,5 @@
-O̸
-o̸
-
-Received Pronunciation IPA: /ˌɪntəˈnæʃənəl/, [ˌɪntəˈnæʃənəɫ]
-General American IPA: /ˌɪntɚˈnæʃənəl/, [ˌɪntɚˈnæʃənəɫ], [ˌɪɾ̃ɚˈnæʃənəɫ]
-Rhymes: -ɛntəl (wtf wiktionary ??)
-
-ˈkʰomɐ gɛts ɐ ˈkʰjuɚ wɛl çiəz ə ˈstʌɹi fɔ ˈju ˈsɐɾə ˈpɛɾi wɔz ə bɛtʰəˈna˞li ˈnʌɚs hu hæd bin ˈwʌ˞kɪŋ deɪli æt æn
-ˈɔʊl̴də d͡zʉ in ə dɪˈzʌɚtɪdə dɪsˈtɹʷɪkt ɔv zə tʰ ˈtʰɛɹɪtəɹi soʊ ʃi wʌz ˈvɛɹi ˈhæpi s tʉ stat ə njʉ d͡ʒɔb æt ə
-ˈsʌbʌb˺ ˈpɹaɪbɛt pɹaktis in noʊsə ˈskweɚ niə zə ˈdjʉk ˈstoʊi ˈtaʊɚ ðæt ˈeɾiə wəz mʌt͡ʃ ˈniɾə fɔ hɐ ænd mɔə
-tʉ laɪk˺ hɐ ˈlaɪkiŋgə ˈibn̩ so ɔ̃ ha fa˞st ˈmɔnɪŋ ʃi fɛlt͡s s t͡stɹɛst ʃi eɪt ə bɔl̴ ɔb˺ ˈpɔɹʷɪd͡ʒ t͡ʃɛkt hɐ˞sɛlf ɪn ðə
-ˈmiɹəɚ ænd wɑʃt hɐ˞ ɸeɪs ɪn ə ˈhʌ˞li zɛn ʃi pʊt ɑn ə pɹeɪn bə ˈjɛloʊ dɹɛs ænd ə ɸʊlɪɸʊlis ˈd͡ʒækɛt pikt ap
-hɐ˞ kʰit ænd ˈhɛdɪdə fɔ ˈwʌ˞kʰ
-
-ˈkʊmɐ gɛts ə ˈkjɚ wɛl̴ ˈɸ͜hi˞ɹɪzə ˈstʌɾi fɔ ʔəju saɹəʔ ˈpɛɹi wɑzə bə ˈbɛtənəɹi bɛt˺ ˈbɛtəˌɹɪnəɹi ənʌs f f hu havə bin
-ˈw̰ʌ˞kɪŋ deɹi ʔat˺ ʔan ʔat an ol̴d͜zʉ in zə diˈzɑɹɛd d ˈdistɹɪkt ɔb ðəʔ ˈtɛɹɪtəɹʷi soʊ çi wʌz ˈbɛɹɪ ˈhɑpi tʉ stɑ˞t˺
-njʉ ə ˈnjʉ d͡ʒoʊb̥ ɐʔ to s̩ˈpɚb pɹaɪvɛt ˈpɹaktɪs ɪn nɔsˈkwɛɚ niə ðə ˈdʌk sɹit˺ t ˈtɔʌ˞ zat˺ ˈeɹiə wɑz mat͡ʃ ˈniɹɪ ˈniɹə fɔ
-hʌ̥ɕʉ̥ fɔ hɚ ændə mɔ̰ mɔə tu ˈħə ˈɹaɪkɪŋ ˈivn̩ zoʊ ɔn ɸ hɐ fɐst mɔɹɪŋ ʃi fɛlt ˈsɹɛɾɛ ɛ̰ ʃi eɪt˺ ʌ boʊl̴ ɔb ˈpɹis æ̃ ˈt͡ʃɛkʰt
-ˈhɑsɛlf ɪnə ˈmiɹə and wɔʃt hɐ fʷeɪs ɪn ˈhʌ˞ɹi ʌ ˈðɛn si pʊt ɔn ðə pɹeɪnə ˈjɛɹoʊ ˈdɹɛsɪz an fɹis ˈd͡ʒækɛt n̩ pik ʌpt
-ˈhɑ kɪt an ˈhɛdɪd fɔ ə fɔ wɔk fɔ ˈwʌ˞kʰ æ̰̃
-
-acegijmnopqrsuvwxyzɱɳʙɾɽʒʂʐʋɹɻɥɟɡɢʛȵɲŋɴʀɕʑçʝxɣχʁʜʍɰʟɨʉɯuʊøɘɵɤəɛœɜɞʌɔæɐɶɑɒɚɝɩɪʅʈʏʞ
\ No newline at end of file
+কঁা (incorrect order, ??? rendering)
+কাঁ (correct order, ??? rendering)
+हैहैहै
+সিওল কোরিয়া রাজধানী
+सियोल कोरिया की राजधानी है
\ No newline at end of file