7 Commits

Author SHA1 Message Date
minjaesong
af334ad20b Enclosed alphanumerics (issue #18) 2026-05-17 17:21:39 +09:00
minjaesong
55ad8ee943 minor fixes 2026-05-06 22:13:27 +09:00
minjaesong
e9fcf6bbce Wenquanyi error fix 2026-04-26 00:38:58 +09:00
minjaesong
45d5b758e3 emoji shiftdown as they should 2026-04-14 23:14:38 +09:00
minjaesong
d3ae868723 emoji wip 2026-03-28 17:36:01 +09:00
minjaesong
372ae9b354 support for Miscellaneous Technical 2026-03-27 21:16:06 +09:00
minjaesong
45027be83c Miscellaneous Technical wip 2026-03-27 01:31:19 +09:00
22 changed files with 86 additions and 22 deletions

View File

@@ -11,7 +11,7 @@ The user must supply:
- **TGA filename** — the sprite sheet filename without path (e.g. `ogham_variable.tga`)
- **Unicode range** — start and end codepoints inclusive (e.g. `U+1680..U+169F`)
If any of these are missing, ask for them before proceeding.
If any of these are missing, ask for them before proceeding. Extra directions can be given after Unicode range.
## Step 1 — Determine the next sheet index

View File

@@ -332,6 +332,7 @@ def build_font(assets_dir, output_path, no_bitmap=False, no_features=False):
charstrings[".notdef"] = pen.getCharString()
_unihan_cps = set(SC.CODE_RANGE[SC.SHEET_UNIHAN])
_emoji1_cps = set(SC.CODE_RANGE[SC.SHEET_EMOJI1])
_base_offsets = {} # glyph_name -> (x_offset, y_offset) for COLR layers
traced_count = 0
@@ -382,6 +383,10 @@ def build_font(assets_dir, output_path, no_bitmap=False, no_features=False):
if cp in _unihan_cps:
y_offset -= ((SC.H - SC.H_UNIHAN) // 2) * SCALE
# Emoji1 glyphs are 16px tall in a 20px cell; same 2px top/bottom padding.
if cp in _emoji1_cps:
y_offset -= ((SC.H - SC.H_EMOJI1) // 2) * SCALE
# Hangul jungseong/jongseong PUA variants (rows 15-18) have zero
# advance and overlay the preceding choseong. Shift their outlines
# left by one syllable cell width so they render at the same position.

View File

@@ -6,8 +6,10 @@ Ported from TerrarumSansBitmap.kt companion object and SheetConfig.kt.
# Font metrics
H = 20
H_UNIHAN = 16
H_EMOJI1 = 16
W_HANGUL_BASE = 13
W_UNIHAN = 16
W_EMOJI1 = 17
W_LATIN_WIDE = 9
W_VAR_INIT = 15
W_WIDEVAR_INIT = 31
@@ -82,6 +84,8 @@ SHEET_OGHAM_VARW = 48
SHEET_COPTIC_VARW = 49
SHEET_CYRILIC_EXTD_VARW = 50
SHEET_MATHS1_VARW = 51
SHEET_EMOJI1 = 52
SHEET_ENCLOSED_ALPHNUM_VARW = 53
SHEET_UNKNOWN = 254
@@ -138,6 +142,8 @@ FILE_LIST = [
"coptic_variable.tga",
"cyrilic_extD_variable.tga",
"maths1_extrawide_variable.tga",
"emoji1.tga",
"enclosed_alphanumeric_variable.tga",
]
CODE_RANGE = [
@@ -192,7 +198,9 @@ CODE_RANGE = [
list(range(0x1680, 0x16A0)), # 48: Ogham
list(range(0x2C80, 0x2D00)), # 49: Coptic
list(range(0x1E030, 0x1E090)), # 50: Cyrillic Ext D
list(range(0x2200, 0x2300)), # 51: Maths1
list(range(0x2200, 0x2400)), # 51: Maths1
list(range(0x1F600, 0x1F650)), # 52: Emoji1
list(range(0x2460, 0x2500)), # 53: Enclosed Alphanum
]
CODE_RANGE_HANGUL_COMPAT = range(0x3130, 0x3190)
@@ -274,6 +282,8 @@ def get_cell_width(sheet_index):
return W_VAR_INIT + HGAP_VAR # 16
if sheet_index == SHEET_UNIHAN:
return W_UNIHAN
if sheet_index == SHEET_EMOJI1:
return W_EMOJI1
if sheet_index == SHEET_HANGUL:
return W_HANGUL_BASE
if sheet_index == SHEET_CUSTOM_SYM:
@@ -286,6 +296,8 @@ def get_cell_width(sheet_index):
def get_cell_height(sheet_index):
if sheet_index == SHEET_UNIHAN:
return H_UNIHAN
if sheet_index == SHEET_EMOJI1:
return H_EMOJI1
if sheet_index == SHEET_CUSTOM_SYM:
return SIZE_CUSTOM_SYM
return H
@@ -579,5 +591,7 @@ def index_y(sheet_index, c):
SHEET_COPTIC_VARW: lambda: (c - 0x2C80) // 16,
SHEET_CYRILIC_EXTD_VARW: lambda: (c - 0x1E030) // 16,
SHEET_MATHS1_VARW: lambda: (c - 0x2200) // 16,
SHEET_EMOJI1: lambda: (c - 0x1F600) // 16,
SHEET_ENCLOSED_ALPHNUM_VARW: lambda: (c - 0x2460) // 16,
SHEET_HANGUL: lambda: 0,
}.get(sheet_index, lambda: c // 16)()

BIN
demo.PNG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -132,6 +132,7 @@ How multilingual? Real multilingual!
Cyrillic Supplement
Cyrillic Extended-A/B/C/D
Devanagari
Enclosed Alphanumerics
Enclosed Alphanumeric Supplement
General Punctuations
Georgian􏿆ჼ􀀀
@@ -152,6 +153,7 @@ How multilingual? Real multilingual!
Small Kana Extension
Letterlike Symbols
Mathematical Operators
Miscellaneous Technical
Number Forms
Ogham
Optical Character Recognition

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/assets/emoji1.tga LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -328,6 +328,7 @@ class TerrarumSansBitmap(
init {
atlas = GlyphAtlas(4096, 4096)
var unihanPixmap: Pixmap? = null
var emoji1Pixmap: Pixmap? = null
// first we create pixmap to read pixels, then pack into atlas
fileList.forEachIndexed { index, it ->
@@ -373,6 +374,10 @@ class TerrarumSansBitmap(
// defer wenquanyi packing to after all other sheets
unihanPixmap = pixmap
}
else if (index == SHEET_EMOJI1) {
// defer emoji1 packing to after all other sheets
emoji1Pixmap = pixmap
}
else {
val texRegPack = if (isExtraWide)
PixmapRegionPack(pixmap, W_WIDEVAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
@@ -418,6 +423,14 @@ class TerrarumSansBitmap(
it.dispose()
}
// pack emoji1 as a contiguous blit (fixed 17x16 cells, 2px top/bottom padding)
emoji1Pixmap?.let {
val cols = it.width / W_EMOJI1
val rows = it.height / H_EMOJI1
atlas.blitSheet(SHEET_EMOJI1, it, W_EMOJI1, H_EMOJI1, cols, rows)
it.dispose()
}
// make sure null char is actually null (draws nothing and has zero width)
atlas.getRegion(SHEET_ASCII_VARW, 0, 0)?.let { atlas.clearRegion(it) }
glyphProps[0] = GlyphProps(0)
@@ -446,6 +459,7 @@ class TerrarumSansBitmap(
}
private val offsetUnihan = (H - H_UNIHAN) / 2
private val offsetEmoji1 = (H - H_EMOJI1) / 2
private val offsetCustomSym = (H - SIZE_CUSTOM_SYM) / 2
private var flagFirstRun = true
@@ -617,6 +631,8 @@ class TerrarumSansBitmap(
val posY = posmap.y[index].flipY() +
if (sheetID == SHEET_UNIHAN) // evil exceptions
offsetUnihan
else if (sheetID == SHEET_EMOJI1)
offsetEmoji1
else if (sheetID == SHEET_CUSTOM_SYM)
offsetCustomSym
else 0
@@ -728,6 +744,8 @@ class TerrarumSansBitmap(
val posY = posmap.y[index].flipY() +
if (sheetID == SHEET_UNIHAN) // evil exceptions
offsetUnihan
else if (sheetID == SHEET_EMOJI1)
offsetEmoji1
else if (sheetID == SHEET_CUSTOM_SYM)
offsetCustomSym
else 0
@@ -885,6 +903,8 @@ class TerrarumSansBitmap(
SHEET_COPTIC_VARW -> copticIndexY(ch)
SHEET_CYRILIC_EXTD_VARW -> cyrilicExtDIndexY(ch)
SHEET_MATHS1_VARW -> maths1IndexY(ch)
SHEET_EMOJI1 -> emoji1IndexY(ch)
SHEET_ENCLOSED_ALPHNUM_VARW -> enclosedAlphnumIndexY(ch)
else -> ch / 16
}
@@ -1011,6 +1031,7 @@ class TerrarumSansBitmap(
codeRangeHangulCompat.forEach { glyphProps[it] = GlyphProps(W_HANGUL_BASE) }
codeRange[SHEET_RUNIC].forEach { glyphProps[it] = GlyphProps(9) }
codeRange[SHEET_UNIHAN].forEach { glyphProps[it] = GlyphProps(W_UNIHAN) }
codeRange[SHEET_EMOJI1].forEach { glyphProps[it] = GlyphProps(W_EMOJI1) }
(0xD800..0xDFFF).forEach { glyphProps[it] = GlyphProps(0) }
(0x100000..0x10FFFF).forEach { glyphProps[it] = GlyphProps(0) }
(0xFFFA0..0xFFFFF).forEach { glyphProps[it] = GlyphProps(0) }
@@ -2560,6 +2581,8 @@ class TerrarumSansBitmap(
internal const val H = 20
internal const val H_UNIHAN = 16
internal const val W_EMOJI1 = 17
internal const val H_EMOJI1 = 16
internal const val H_DIACRITICS = 3
@@ -2620,6 +2643,8 @@ class TerrarumSansBitmap(
internal const val SHEET_COPTIC_VARW = 49
internal const val SHEET_CYRILIC_EXTD_VARW = 50
internal const val SHEET_MATHS1_VARW = 51
internal const val SHEET_EMOJI1 = 52
internal const val SHEET_ENCLOSED_ALPHNUM_VARW = 53
internal const val SHEET_UNKNOWN = 254
@@ -2694,6 +2719,8 @@ class TerrarumSansBitmap(
"coptic_variable.tga",
"cyrilic_extD_variable.tga",
"maths1_extrawide_variable.tga",
"emoji1.tga",
"enclosed_alphanumeric_variable.tga",
)
internal val codeRange = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!!
0..0xFF, // SHEET_ASCII_VARW
@@ -2747,7 +2774,9 @@ class TerrarumSansBitmap(
0x1680..0x169F, // SHEET_OGHAM_VARW
0x2C80..0x2CFF, // SHEET_COPTIC_VARW
0x1E030..0x1E08F, // SHEET_CYRILIC_EXTD_VARW
0x2200..0x22FF, // SHEET_MATHS1_VARW
0x2200..0x23FF, // SHEET_MATHS1_VARW
0x1F600..0x1F64F, // SHEET_EMOJI1
0x2460..0x24FF, // SHEET_ENCLOSED_ALPHNUM_VARW
)
private val codeRangeHangulCompat = 0x3130..0x318F
@@ -3110,6 +3139,8 @@ class TerrarumSansBitmap(
private fun copticIndexY(c: CodePoint) = (c - 0x2C80) / 16
private fun cyrilicExtDIndexY(c: CodePoint) = (c - 0x1E030) / 16
private fun maths1IndexY(c: CodePoint) = (c - 0x2200) / 16
private fun emoji1IndexY(c: CodePoint) = (c - 0x1F600) / 16
private fun enclosedAlphnumIndexY(c: CodePoint) = (c - 0x2460) / 16
val charsetOverrideDefault = Character.toChars(CHARSET_OVERRIDE_DEFAULT).toSurrogatedString()
val charsetOverrideBulgarian = Character.toChars(CHARSET_OVERRIDE_BG_BG).toSurrogatedString()

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
work_files/emoji1.kra LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.