diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 41f2737..d8f0866 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -10,7 +10,8 @@
-
+
+
@@ -31,8 +32,26 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -49,22 +68,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -85,8 +90,8 @@
-
-
+
+
@@ -94,8 +99,8 @@
-
-
+
+
@@ -127,10 +132,6 @@
- getG
- getWidt
- batch.color =
- f
fun getWidth
fun dispo
xHei
@@ -157,9 +158,15 @@
GlyphProps.LE
buildWidthTable
lastNonDiacriticChar
+ c.toInt()
+ ArrayList<Int>
+ har.toInt()
+ println
.141
+ c
+ har
@@ -514,37 +521,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -840,25 +816,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -899,17 +856,57 @@
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FontTestGDX/demotext.txt b/FontTestGDX/demotext.txt
index 3ae791f..61155e1 100644
--- a/FontTestGDX/demotext.txt
+++ b/FontTestGDX/demotext.txt
@@ -37,7 +37,8 @@ How multilingual? Real multilingual!
სწრაფი ყავისფერი მელა გადაახტა ზარმაც ძაღლს
Kæmi ný öxi hér, ykist þjófum nú bæði víl og ádrepa
Ċuaiġ bé ṁórṡáċ le dlúṫspád fíorḟinn trí hata mo ḋea-ṗorcáin ḃig
- とりなくこゑす ゆめさませ みよあけわたる ひんかしを そらいろはえて おきつへに ほふねむれゐぬ もやのうち
+ あめつちほしそら やまかはみねたに くもきりむろこけ ひといぬうへすゑ ゆわさるおふせよ えの𛀁をなれゐて
+ トリナクコヱス ユメサマセ ミヨアケワタル ヒンカシヲ ソライロハエテ オキツヘニ ホフネムレヰヌ モヤノウチ
田居に出で 菜摘むわれをぞ 君召すと 求食り追ひゆく 山城の 打酔へる子ら 藻葉干せよ え舟繋けぬ
정 참판 양반댁 규수 큰 교자 타고 혼례 치른 날 하얬다 도럄직한 퀡봹퉪헰
Četri psihi faķīri vēlu vakarā zāģēja guļbūvei durvis, fonā šņācot mežam
diff --git a/FontTestGDX/lib/TerrarumSansBitmap.jar b/FontTestGDX/lib/TerrarumSansBitmap.jar
index 9cb2b25..5dc7bc5 100644
Binary files a/FontTestGDX/lib/TerrarumSansBitmap.jar and b/FontTestGDX/lib/TerrarumSansBitmap.jar differ
diff --git a/assets/ascii_variable.tga b/assets/ascii_variable.tga
index 7908e1f..2c2cd6f 100644
Binary files a/assets/ascii_variable.tga and b/assets/ascii_variable.tga differ
diff --git a/assets/kana.tga b/assets/kana.tga
index ae840bf..79adc10 100644
Binary files a/assets/kana.tga and b/assets/kana.tga differ
diff --git a/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt b/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt
index 03b0708..5ccafe1 100644
--- a/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt
+++ b/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt
@@ -35,6 +35,8 @@ import java.io.File
import java.io.FileOutputStream
import java.util.zip.GZIPInputStream
+typealias CodepointSequence = ArrayList
+
/**
* LibGDX port of Terrarum Sans Bitmap implementation
*
@@ -109,89 +111,88 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
9
}
- private fun isHangul(c: Char) = c.toInt() in codeRange[SHEET_HANGUL]
- private fun isAscii(c: Char) = c.toInt() in codeRange[SHEET_ASCII_VARW]
- private fun isRunic(c: Char) = c.toInt() in codeRange[SHEET_RUNIC]
- private fun isExtA(c: Char) = c.toInt() in codeRange[SHEET_EXTA_VARW]
- private fun isExtB(c: Char) = c.toInt() in codeRange[SHEET_EXTB_VARW]
- private fun isKana(c: Char) = c.toInt() in codeRange[SHEET_KANA]
- private fun isCJKPunct(c: Char) = c.toInt() in codeRange[SHEET_CJK_PUNCT]
- private fun isUniHan(c: Char) = c.toInt() in codeRange[SHEET_UNIHAN]
- private fun isCyrilic(c: Char) = c.toInt() in codeRange[SHEET_CYRILIC_VARW]
- private fun isFullwidthUni(c: Char) = c.toInt() in codeRange[SHEET_FW_UNI]
- private fun isUniPunct(c: Char) = c.toInt() in codeRange[SHEET_UNI_PUNCT_VARW]
- private fun isGreek(c: Char) = c.toInt() in codeRange[SHEET_GREEK_VARW]
- private fun isThai(c: Char) = c.toInt() in codeRange[SHEET_THAI_VARW]
- /*private fun isDiacritics(c: Char) = c.toInt() in 0xE34..0xE3A
- || c.toInt() in 0xE47..0xE4E
- || c.toInt() == 0xE31*/
- private fun isCustomSym(c: Char) = c.toInt() in codeRange[SHEET_CUSTOM_SYM]
- private fun isArmenian(c: Char) = c.toInt() in codeRange[SHEET_HAYEREN_VARW]
- private fun isKartvelian(c: Char) = c.toInt() in codeRange[SHEET_KARTULI_VARW]
- private fun isIPA(c: Char) = c.toInt() in codeRange[SHEET_IPA_VARW]
- private fun isColourCodeHigh(c: Char) = c.toInt() in 0xDC00..0xDFFF // only works with JVM (which uses UTF-16 internally)
- private fun isColourCodeLow(c: Char) = c.toInt() in 0xDBC0..0xDBFF // only works with JVM (which uses UTF-16 internally)
- private fun isLatinExtAdd(c: Char) = c.toInt() in 0x1E00..0x1EFF
- private fun isCharsetOverrideHigh(c: Char) = c.toInt() in 0xDFF8..0xDFFF // only works with JVM (which uses UTF-16 internally)
- private fun isCharsetOverrideLow(c: Char) = c.toInt() == 0xDBBF // only works with JVM (which uses UTF-16 internally)
- private fun isBulgarian(c: Char) = c.toInt() in 0x400..0x45F
- private fun isCherokee(c: Char) = c.toInt() in codeRange[SHEET_TSALAGI_VARW]
+ private fun isHangul(c: Int) = c in codeRange[SHEET_HANGUL]
+ private fun isAscii(c: Int) = c in codeRange[SHEET_ASCII_VARW]
+ private fun isRunic(c: Int) = c in codeRange[SHEET_RUNIC]
+ private fun isExtA(c: Int) = c in codeRange[SHEET_EXTA_VARW]
+ private fun isExtB(c: Int) = c in codeRange[SHEET_EXTB_VARW]
+ private fun isKana(c: Int) = c in codeRange[SHEET_KANA]
+ private fun isCJKPunct(c: Int) = c in codeRange[SHEET_CJK_PUNCT]
+ private fun isUniHan(c: Int) = c in codeRange[SHEET_UNIHAN]
+ private fun isCyrilic(c: Int) = c in codeRange[SHEET_CYRILIC_VARW]
+ private fun isFullwidthUni(c: Int) = c in codeRange[SHEET_FW_UNI]
+ private fun isUniPunct(c: Int) = c in codeRange[SHEET_UNI_PUNCT_VARW]
+ private fun isGreek(c: Int) = c in codeRange[SHEET_GREEK_VARW]
+ private fun isThai(c: Int) = c in codeRange[SHEET_THAI_VARW]
+ /*private fun isDiacritics(c: Int) = c in 0xE34..0xE3A
+ || c in 0xE47..0xE4E
+ || c == 0xE31*/
+ private fun isCustomSym(c: Int) = c in codeRange[SHEET_CUSTOM_SYM]
+ private fun isArmenian(c: Int) = c in codeRange[SHEET_HAYEREN_VARW]
+ private fun isKartvelian(c: Int) = c in codeRange[SHEET_KARTULI_VARW]
+ private fun isIPA(c: Int) = c in codeRange[SHEET_IPA_VARW]
+ private fun isLatinExtAdd(c: Int) = c in 0x1E00..0x1EFF
+ private fun isBulgarian(c: Int) = c in 0x400..0x45F
+ private fun isColourCode(c: Int) = c in 0x100000..0x10FFFF
+ private fun isCharsetOverride(c: Int) = c in 0xFFFF8..0xFFFFF
+ private fun isCherokee(c: Int) = c in codeRange[SHEET_TSALAGI_VARW]
- private fun extAindexX(c: Char) = (c.toInt() - 0x100) % 16
- private fun extAindexY(c: Char) = (c.toInt() - 0x100) / 16
+ private fun extAindexX(c: Int) = (c - 0x100) % 16
+ private fun extAindexY(c: Int) = (c - 0x100) / 16
- private fun extBindexX(c: Char) = (c.toInt() - 0x180) % 16
- private fun extBindexY(c: Char) = (c.toInt() - 0x180) / 16
+ private fun extBindexX(c: Int) = (c - 0x180) % 16
+ private fun extBindexY(c: Int) = (c - 0x180) / 16
- private fun runicIndexX(c: Char) = (c.toInt() - 0x16A0) % 16
- private fun runicIndexY(c: Char) = (c.toInt() - 0x16A0) / 16
+ private fun runicIndexX(c: Int) = (c - 0x16A0) % 16
+ private fun runicIndexY(c: Int) = (c - 0x16A0) / 16
- private fun kanaIndexX(c: Char) = (c.toInt() - 0x3040) % 16
- private fun kanaIndexY(c: Char) = (c.toInt() - 0x3040) / 16
+ private fun kanaIndexX(c: Int) = (c - 0x3040) % 16
+ private fun kanaIndexY(c: Int) =
+ if (c in 0x31F0..0x31FF) 12
+ else if (c in 0x31F0..0x31FF) 13
+ else (c - 0x3040) / 16
- private fun cjkPunctIndexX(c: Char) = (c.toInt() - 0x3000) % 16
- private fun cjkPunctIndexY(c: Char) = (c.toInt() - 0x3000) / 16
+ private fun cjkPunctIndexX(c: Int) = (c - 0x3000) % 16
+ private fun cjkPunctIndexY(c: Int) = (c - 0x3000) / 16
- private fun cyrilicIndexX(c: Char) = (c.toInt() - 0x400) % 16
- private fun cyrilicIndexY(c: Char) = (c.toInt() - 0x400) / 16
+ private fun cyrilicIndexX(c: Int) = (c - 0x400) % 16
+ private fun cyrilicIndexY(c: Int) = (c - 0x400) / 16
- private fun fullwidthUniIndexX(c: Char) = (c.toInt() - 0xFF00) % 16
- private fun fullwidthUniIndexY(c: Char) = (c.toInt() - 0xFF00) / 16
+ private fun fullwidthUniIndexX(c: Int) = (c - 0xFF00) % 16
+ private fun fullwidthUniIndexY(c: Int) = (c - 0xFF00) / 16
- private fun uniPunctIndexX(c: Char) = (c.toInt() - 0x2000) % 16
- private fun uniPunctIndexY(c: Char) = (c.toInt() - 0x2000) / 16
+ private fun uniPunctIndexX(c: Int) = (c - 0x2000) % 16
+ private fun uniPunctIndexY(c: Int) = (c - 0x2000) / 16
- private fun unihanIndexX(c: Char) = (c.toInt() - 0x3400) % 256
- private fun unihanIndexY(c: Char) = (c.toInt() - 0x3400) / 256
+ private fun unihanIndexX(c: Int) = (c - 0x3400) % 256
+ private fun unihanIndexY(c: Int) = (c - 0x3400) / 256
- private fun greekIndexX(c: Char) = (c.toInt() - 0x370) % 16
- private fun greekIndexY(c: Char) = (c.toInt() - 0x370) / 16
+ private fun greekIndexX(c: Int) = (c - 0x370) % 16
+ private fun greekIndexY(c: Int) = (c - 0x370) / 16
- private fun thaiIndexX(c: Char) = (c.toInt() - 0xE00) % 16
- private fun thaiIndexY(c: Char) = (c.toInt() - 0xE00) / 16
+ private fun thaiIndexX(c: Int) = (c - 0xE00) % 16
+ private fun thaiIndexY(c: Int) = (c - 0xE00) / 16
- private fun symbolIndexX(c: Char) = (c.toInt() - 0xE000) % 16
- private fun symbolIndexY(c: Char) = (c.toInt() - 0xE000) / 16
+ private fun symbolIndexX(c: Int) = (c - 0xE000) % 16
+ private fun symbolIndexY(c: Int) = (c - 0xE000) / 16
- private fun armenianIndexX(c: Char) = (c.toInt() - 0x530) % 16
- private fun armenianIndexY(c: Char) = (c.toInt() - 0x530) / 16
+ private fun armenianIndexX(c: Int) = (c - 0x530) % 16
+ private fun armenianIndexY(c: Int) = (c - 0x530) / 16
- private fun kartvelianIndexX(c: Char) = (c.toInt() - 0x10D0) % 16
- private fun kartvelianIndexY(c: Char) = (c.toInt() - 0x10D0) / 16
+ private fun kartvelianIndexX(c: Int) = (c - 0x10D0) % 16
+ private fun kartvelianIndexY(c: Int) = (c - 0x10D0) / 16
- private fun ipaIndexX(c: Char) = (c.toInt() - 0x250) % 16
- private fun ipaIndexY(c: Char) = (c.toInt() - 0x250) / 16
+ private fun ipaIndexX(c: Int) = (c - 0x250) % 16
+ private fun ipaIndexY(c: Int) = (c - 0x250) / 16
- private fun latinExtAddX(c: Char) = (c.toInt() - 0x1E00) % 16
- private fun latinExtAddY(c: Char) = (c.toInt() - 0x1E00) / 16
+ private fun latinExtAddX(c: Int) = (c - 0x1E00) % 16
+ private fun latinExtAddY(c: Int) = (c - 0x1E00) / 16
- private fun cherokeeIndexX(c: Char) = (c.toInt() - 0x13A0) % 16
- private fun cherokeeIndexY(c: Char) = (c.toInt() - 0x13A0) / 16
-
- private fun getColour(charHigh: Char, charLow: Char): Color { // input: 0x10ARGB, out: RGBA8888
- val codePoint = Character.toCodePoint(charHigh, charLow)
+ private fun cherokeeIndexX(c: Int) = (c - 0x13A0) % 16
+ private fun cherokeeIndexY(c: Int) = (c - 0x13A0) / 16
+ private fun getColour(codePoint: Int): Color { // input: 0x10ARGB, out: RGBA8888
if (colourBuffer.containsKey(codePoint))
return colourBuffer[codePoint]!!
@@ -390,7 +391,7 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
private val offsetUnihan = (H - H_UNIHAN) / 2
private val offsetCustomSym = (H - SIZE_CUSTOM_SYM) / 2
- private var textBuffer: CharSequence = ""
+ private var textBuffer = CodepointSequence(256)
private var posXbuffer = intArrayOf() // absolute posX of glyphs from print-origin
private var posYbuffer = intArrayOf() // absolute posY of glyphs from print-origin
private var glyphWidthBuffer = intArrayOf() // width of each glyph
@@ -398,6 +399,7 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
private lateinit var originalColour: Color
override fun draw(batch: Batch, str: CharSequence, x: Float, y: Float): GlyphLayout? {
+ val str = str.toCodePoints()
fun Int.flipY() = this * if (flipY) 1 else -1
@@ -413,8 +415,8 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
glyphWidthBuffer = widths
- posXbuffer = IntArray(str.length, { 0 })
- posYbuffer = IntArray(str.length, { 0 })
+ posXbuffer = IntArray(str.size, { 0 })
+ posYbuffer = IntArray(str.size, { 0 })
var nonDiacriticCounter = 0 // index of last instance of non-diacritic char
@@ -425,12 +427,12 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
// nonDiacriticCounter allows multiple diacritics
val thisChar = textBuffer[charIndex]
- if (glyphProps[thisChar.toInt()] == null) {
- throw InternalError("No props for char '$thisChar' (${thisChar.toInt().toString(16)})")
+ if (glyphProps[thisChar] == null) {
+ throw InternalError("No props for char '$thisChar' (${thisChar.toString(16)})")
}
- val thisProp = glyphProps[thisChar.toInt()]!!
+ val thisProp = glyphProps[thisChar]!!
val lastNonDiacriticChar = textBuffer[nonDiacriticCounter]
- val itsProp = glyphProps[lastNonDiacriticChar.toInt()]!!
+ val itsProp = glyphProps[lastNonDiacriticChar]!!
//println("char: $thisChar; properties: $thisProp")
@@ -497,35 +499,22 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
//println("[TerrarumSansBitmap] sprite: $sheetID:${sheetX}x${sheetY}")
- if (isColourCodeLow(c)) {
- val cchigh = c
- val cclow = textBuffer[index + 1]
-
- if (Character.toCodePoint(cchigh, cclow) == 0x100000) {
+ if (isColourCode(c)) {
+ if (c == 0x100000) {
mainCol = originalColour
shadowCol = mainCol.cpy().mul(0.5f,0.5f,0.5f,1f)
}
else {
- mainCol = getColour(cchigh, cclow)
+ mainCol = getColour(c)
shadowCol = mainCol.cpy().mul(0.5f, 0.5f, 0.5f, 1f)
}
-
- index += 1
}
- else if (isCharsetOverrideLow(c)) {
- val cchigh = c
- val cclow = textBuffer[index + 1]
-
- charsetOverride = Character.toCodePoint(cchigh, cclow) - CHARSET_OVERRIDE_NULL
-
- index += 1
- }
- else if (isCharsetOverrideHigh(c) || isColourCodeHigh(c)) {
- /* do nothing and advance */
+ else if (isCharsetOverride(c)) {
+ charsetOverride = c - CHARSET_OVERRIDE_NULL
}
else if (sheetID == SHEET_HANGUL) {
val hangulSheet = sheets[SHEET_HANGUL]
- val hIndex = c.toInt() - 0xAC00
+ val hIndex = c - 0xAC00
val indexCho = getHanChosung(hIndex)
val indexJung = getHanJungseong(hIndex)
@@ -648,23 +637,23 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
sheets.forEach { it.dispose() }
}
- private fun getWidthOfCharSeq(s: CharSequence): IntArray {
- val len = IntArray(s.length)
+ private fun getWidthOfCharSeq(s: CodepointSequence): IntArray {
+ val len = IntArray(s.size)
for (i in 0..s.lastIndex) {
val chr = s[i]
val ctype = getSheetType(s[i])
if (variableWidthSheets.contains(ctype)) {
- if (!glyphProps.containsKey(chr.toInt())) {
+ if (!glyphProps.containsKey(chr)) {
System.err.println("[TerrarumSansBitmap] no width data for glyph number ${Integer.toHexString(chr.toInt()).toUpperCase()}")
len[i] = W_LATIN_WIDE
}
- val prop = glyphProps[chr.toInt()]!!
+ val prop = glyphProps[chr]!!
//println("${chr.toInt()} -> $prop")
len[i] = prop.width * (if (prop.writeOnTop) -1 else 1)
}
- else if (isColourCodeHigh(chr) || isColourCodeLow(chr) || isCharsetOverrideHigh(chr) || isCharsetOverrideLow(chr))
+ else if (isColourCode(chr) || isCharsetOverride(chr))
len[i] = 0
else if (ctype == SHEET_CJK_PUNCT)
len[i] = W_ASIAN_PUNCT
@@ -686,7 +675,7 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
return len
}
- private fun getSheetType(c: Char): Int {
+ private fun getSheetType(c: Int): Int {
if (charsetOverride == 1 && isBulgarian(c))
return SHEET_BULGARIAN_VARW
else if (charsetOverride == 2 && isBulgarian(c))
@@ -735,7 +724,7 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
// fallback
}
- private fun getSheetwisePosition(ch: Char): IntArray {
+ private fun getSheetwisePosition(ch: Int): IntArray {
val sheetX: Int; val sheetY: Int
when (getSheetType(ch)) {
SHEET_UNIHAN -> {
@@ -811,8 +800,8 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
sheetY = cherokeeIndexY(ch)
}
else -> {
- sheetX = ch.toInt() % 16
- sheetY = ch.toInt() / 16
+ sheetX = ch % 16
+ sheetY = ch / 16
}
}
@@ -871,15 +860,84 @@ class GameFontBase(fontDir: String, val noShadow: Boolean = false, val flipY: Bo
this.codeRange[SHEET_RUNIC].forEach { glyphProps[it] = GlyphProps(9, 0) }
this.codeRange[SHEET_UNIHAN].forEach { glyphProps[it] = GlyphProps(W_UNIHAN, 0) }
(0xD800..0xDFFF).forEach { glyphProps[it] = GlyphProps(0, 0) }
+ (0x100000..0x10FFFF).forEach { glyphProps[it] = GlyphProps(0, 0) }
+ (0xFFFF8..0xFFFFF).forEach { glyphProps[it] = GlyphProps(0, 0) }
}
private val glyphLayout = GlyphLayout()
fun getWidth(text: String): Int {
- return getWidthOfCharSeq(text).sum()
+ return getWidthOfCharSeq(text.toCodePoints()).sum()
}
+ /** UTF-16 to ArrayList of Int. UTF-16 is because of Java */
+ private fun CharSequence.toCodePoints(): CodepointSequence {
+ val seq = ArrayList()
+
+ var i = 0
+ while (i < this.length) {
+ val c = this[i]
+
+ 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")
+
+ val H = c
+ val L = cNext
+
+ seq.add(Character.toCodePoint(H, L))
+
+ i++ // skip next char (guaranteed to be Low Surrogate)
+ }
+ else {
+ seq.add(c.toInt())
+ }
+
+ i++
+ }
+
+ return seq
+ }
+
+ /** As CharSequence is just an Interface, copy-pasting the code would be the fastest way */
+ private fun String.toCodePoints(): CodepointSequence {
+ val seq = ArrayList()
+
+ var i = 0
+ while (i < this.length) {
+ val c = this[i]
+
+ if (i < this.lastIndex) {
+ if (c.isHighSurrogate()) {
+ val cNext = this[i + 1]
+
+ if (!cNext.isLowSurrogate())
+ throw IllegalArgumentException("Malformed UTF-16 String: High surrogate must be paired with low surrogate")
+
+ val H = c
+ val L = cNext
+
+ seq.add(Character.toCodePoint(H, L))
+
+ i++ // skip next char (guaranteed to be Low Surrogate)
+ }
+ }
+ else {
+ seq.add(c.toInt())
+ }
+ }
+
+ return seq
+ }
+
+ /** High surrogate comes before the low. */
+ private fun Char.isHighSurrogate() = (this.toInt() in 0xD800..0xDBFF)
+ /** CodePoint = 0x10000 + (H - 0xD800) * 0x400 + (L - 0xDC00) */
+ private fun Char.isLowSurrogate() = (this.toInt() in 0xDC00..0xDFFF)
+
var interchar = 0
var scale = 1