27 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
minjaesong
6bc365fc57 maths ops 2026-03-21 22:15:39 +09:00
minjaesong
69f868c3e8 fix for github issue #15 2026-03-20 22:57:24 +09:00
minjaesong
a1147c8611 update to Unicode 18 2026-03-20 20:12:04 +09:00
minjaesong
4bd8febfdf fix: some old hangul getting wrong component 2026-03-20 18:48:50 +09:00
minjaesong
04fbddb200 OTF hangul base addr remap 2026-03-20 18:30:57 +09:00
minjaesong
fb935ab28f cyrillic ext d 2026-03-19 16:18:21 +09:00
minjaesong
6c65cfc0a1 demo text update 2026-03-18 00:44:22 +09:00
minjaesong
c5c2ae4022 full support of CJK Unified Ideographs and ExtA 2026-03-16 22:33:44 +09:00
minjaesong
2279873222 wenquanyi update 2026-03-16 16:09:16 +09:00
minjaesong
4dbcbb7df5 working on Han char sheet 2026-03-15 22:11:24 +09:00
minjaesong
ad4a044ace ascii fractions to use char decompo 2026-03-15 01:18:43 +09:00
minjaesong
9ca8117b5f coptic 2026-03-15 01:00:23 +09:00
minjaesong
76f223aee8 ogham 2026-03-14 19:32:15 +09:00
minjaesong
662dc5b093 glyph texture atlas (2) 2026-03-14 16:06:31 +09:00
minjaesong
f4e1db5846 glyph texture atlas 2026-03-14 14:24:23 +09:00
minjaesong
1c7471ccf3 added missing glyphs in Greek and Coptic block 2026-03-14 11:14:18 +09:00
minjaesong
5fca96a861 fix: number forms using wrong slash 2026-03-13 21:17:25 +09:00
minjaesong
539a2c9f46 autokem: more filtering 2026-03-13 20:08:56 +09:00
CuriousTorvald
d57707b210 Add funding options for GitHub and PayPal
Updated funding options to include GitHub Sponsors and a PayPal link.
2026-03-13 19:16:55 +09:00
minjaesong
7c4ab9d4be reshaping Tironian et 2026-03-13 19:14:58 +09:00
74 changed files with 678 additions and 232 deletions

View File

@@ -0,0 +1,30 @@
{
"permissions": {
"allow": [
"WebFetch(domain:github.com)",
"WebSearch",
"Bash(head:*)",
"WebFetch(domain:gitlab.com)",
"Bash(java:*)",
"Bash(ls:*)",
"Bash(jar tf:*)",
"Bash(chmod +x:*)",
"WebFetch(domain:fontforge.org)",
"WebFetch(domain:fonttools.readthedocs.io)",
"Bash(grep:*)",
"Bash(tail:*)",
"Bash(python3:*)",
"Bash(make:*)",
"Bash(git stash:*)",
"Bash(./autokem*)",
"Bash(cmp:*)",
"Bash(wc:*)",
"Bash(pip3:*)",
"Bash(pip install:*)",
"Bash(.venv/bin/python3:*)",
"Bash(.venv/bin/python:*)",
"Bash(find:*)",
"Skill(update-config)"
]
}
}

View File

@@ -0,0 +1,93 @@
---
name: add-unicode-block
description: Add a new Unicode script/block to the Terrarum Sans Bitmap font engine.
---
# Add Unicode Block
## Required inputs
The user must supply:
- **Script name** — human-readable name used in constant/function names (e.g. `Ogham`, `LatinExtE`)
- **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. Extra directions can be given after Unicode range.
## Step 1 — Determine the next sheet index
Read the sheet index constants from both files to find the current highest index (excluding `SHEET_UNKNOWN = 254`):
- Kotlin: `src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt` — grep for `internal const val SHEET_`
- Python: `OTFbuild/sheet_config.py` — grep for `^SHEET_`
The new index = highest existing index + 1.
## Step 2 — Derive identifiers
From the script name, derive:
- **Kotlin constant**: `SHEET_<UPPER_SNAKE>_VARW` (e.g. `SHEET_OGHAM_VARW`)
- **Kotlin indexY function**: `<camelCase>IndexY` (e.g. `oghamIndexY`)
- **Python constant**: same as Kotlin constant
- **Range start hex**: the lower bound codepoint as a `0x`-prefixed Kotlin/Python literal
## Step 3 — Edit both files
Make all 6 edits. Read each section before editing.
### Kotlin: `src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt`
**a) Sheet index constant** — find the block of `internal const val SHEET_*` constants (just before `SHEET_UNKNOWN = 254`) and append:
```kotlin
internal const val SHEET_<NAME>_VARW = <INDEX>
```
**b) fileList entry** — find `internal val fileList` array and append before the closing `)`:
```kotlin
"<tga_filename>",
```
**c) codeRange entry** — find `internal val codeRange` array and append before the closing `)`:
```kotlin
0x<START>..<0x<END>, // SHEET_<NAME>_VARW
```
Use `+` to combine non-contiguous ranges if needed.
**d) getSheetwisePosition when-branch** — find the `when` block that dispatches to indexY functions (just before `else -> ch / 16`) and append:
```kotlin
SHEET_<NAME>_VARW -> <camelCase>IndexY(ch)
```
**e) indexY function** — find the block of private `*IndexY` functions near the bottom of the companion object and append:
```kotlin
private fun <camelCase>IndexY(c: CodePoint) = (c - 0x<START>) / 16
```
### Python: `OTFbuild/sheet_config.py`
**f) Sheet index constant** — find the block of `SHEET_* = <n>` constants (just before `SHEET_UNKNOWN = 254`) and append:
```python
SHEET_<NAME>_VARW = <INDEX>
```
**g) FILE_LIST entry** — find `FILE_LIST = [` array and append before the closing `]`:
```python
"<tga_filename>",
```
**h) CODE_RANGE entry** — find `CODE_RANGE = [` array and append before the closing `]`:
```python
list(range(0x<START>, 0x<END+1>)), # <INDEX>: <ScriptName>
```
**i) index_y lambda** — find the dict in `get_index_y(sheet_index, c)` (just before `SHEET_HANGUL: lambda: 0`) and append:
```python
SHEET_<NAME>_VARW: lambda: (c - 0x<START>) // 16,
```
## Step 4 — Verify
After all edits, confirm:
1. The Kotlin constant, fileList, codeRange, when-branch, and indexY function are all present and consistent.
2. The Python constant, FILE_LIST, CODE_RANGE, and index_y lambda are all present and consistent.
3. The indices in both files match.
4. The range end in `CODE_RANGE` is `end + 1` (Python `range` is exclusive).

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: [curioustorvald]
custom: ["https://paypal.me/curioustorvald"]

View File

@@ -2,7 +2,7 @@
#include "tga.h" #include "tga.h"
#include "nn.h" #include "nn.h"
#include "safetensor.h" #include "safetensor.h"
#include "unicode_lm.h" #include "unicode_filter.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>

Binary file not shown.

View File

@@ -2,7 +2,7 @@
#include "tga.h" #include "tga.h"
#include "nn.h" #include "nn.h"
#include "safetensor.h" #include "safetensor.h"
#include "unicode_lm.h" #include "unicode_filter.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -78,8 +78,8 @@ static int collect_from_sheet(const char *path, int is_xyswap, int start_code,
} }
if (width == 0) continue; if (width == 0) continue;
/* Skip modifier letters (superscripts/subscripts) */ /* Skip modifier letters, symbols, punctuation */
if (start_code >= 0 && is_modifier_letter(start_code + index)) if (start_code >= 0 && is_excluded_from_training(start_code + index))
continue; continue;
/* Read kerning data pixel at Y+6 */ /* Read kerning data pixel at Y+6 */

View File

@@ -127,11 +127,12 @@ def collect_from_sheet(path, is_xyswap, code_range=None):
if width == 0: if width == 0:
continue continue
# Skip modifier letters (superscripts/subscripts) # Skip modifier letters, symbols, punctuation
if code_range is not None and index < len(code_range): if code_range is not None and index < len(code_range):
cp = code_range[index] cp = code_range[index]
try: try:
if unicodedata.category(chr(cp)) == 'Lm': cat = unicodedata.category(chr(cp))
if cat == 'Lm' or cat[0] in ('S', 'P'):
skipped_lm += 1 skipped_lm += 1
continue continue
except (ValueError, OverflowError): except (ValueError, OverflowError):
@@ -194,14 +195,14 @@ def collect_all_samples(assets_dir):
inputs, labels, skipped_lm = collect_from_sheet(path, is_xyswap, code_range) inputs, labels, skipped_lm = collect_from_sheet(path, is_xyswap, code_range)
total_skipped_lm += skipped_lm total_skipped_lm += skipped_lm
if inputs: if inputs:
suffix = f" (skipped {skipped_lm} Lm)" if skipped_lm else "" suffix = f" (skipped {skipped_lm})" if skipped_lm else ""
print(f" {name}: {len(inputs)} samples{suffix}") print(f" {name}: {len(inputs)} samples{suffix}")
all_inputs.extend(inputs) all_inputs.extend(inputs)
all_labels.extend(labels) all_labels.extend(labels)
file_count += 1 file_count += 1
if total_skipped_lm: if total_skipped_lm:
print(f" Total modifier letters filtered: {total_skipped_lm}") print(f" Filtered (Lm/S/P): {total_skipped_lm}")
return np.array(all_inputs), np.array(all_labels, dtype=np.float32), file_count return np.array(all_inputs), np.array(all_labels, dtype=np.float32), file_count

View File

@@ -1,18 +1,21 @@
#ifndef UNICODE_LM_H #ifndef UNICODE_FILTER_H
#define UNICODE_LM_H #define UNICODE_FILTER_H
#include <string.h> #include <string.h>
/* /*
* Unicode category Lm (Letter, modifier) range checks. * Unicode category filters for training/apply.
* Generated from Python unicodedata (Unicode 16.0). * Generated from Python unicodedata (Unicode 16.0).
* *
* is_modifier_letter(cp) true for all Lm codepoints * is_modifier_letter(cp) category Lm
* is_subscript_modifier(cp) true for Lm codepoints with <sub> decomposition * is_subscript_modifier(cp) Lm with <sub> decomposition
* is_symbol_or_punctuation(cp) categories S* or P*
* is_excluded_from_training(cp) Lm or S* or P*
*/ */
/* ---- Lm (modifier letter) ---- */
static inline int is_modifier_letter(int cp) { static inline int is_modifier_letter(int cp) {
/* 71 contiguous ranges covering all 397 Lm codepoints */
if (cp >= 0x02B0 && cp <= 0x02C1) return 1; if (cp >= 0x02B0 && cp <= 0x02C1) return 1;
if (cp >= 0x02C6 && cp <= 0x02D1) return 1; if (cp >= 0x02C6 && cp <= 0x02D1) return 1;
if (cp >= 0x02E0 && cp <= 0x02E4) return 1; if (cp >= 0x02E0 && cp <= 0x02E4) return 1;
@@ -88,20 +91,67 @@ static inline int is_modifier_letter(int cp) {
} }
static inline int is_subscript_modifier(int cp) { static inline int is_subscript_modifier(int cp) {
/* 49 Lm codepoints with <sub> decomposition */ if (cp >= 0x1D62 && cp <= 0x1D6A) return 1;
if (cp >= 0x1D62 && cp <= 0x1D6A) return 1; /* 9 */ if (cp >= 0x2090 && cp <= 0x209C) return 1;
if (cp >= 0x2090 && cp <= 0x209C) return 1; /* 13 */ if (cp == 0x2C7C) return 1;
if (cp == 0x2C7C) return 1; /* 1 */ if (cp >= 0x1E051 && cp <= 0x1E06A) return 1;
if (cp >= 0x1E051 && cp <= 0x1E06A) return 1; /* 26 */
return 0; return 0;
} }
/* /* ---- S* (Symbol) and P* (Punctuation) ---- */
* Map sheet filename to first codepoint of its (contiguous) code range.
* Returns -1 if unknown. For non-contiguous sheets (e.g. Devanagari), /* Table of {start, end} ranges for S/P codepoints in font sheets */
* returns the start of the first sub-range; cells beyond it won't static const int sp_ranges[][2] = {
* collide with Lm codepoints in practice. {0x00021, 0x0002F}, {0x0003A, 0x00040}, {0x0005B, 0x00060},
*/ {0x0007B, 0x0007E}, {0x000A1, 0x000A9}, {0x000AB, 0x000AC},
{0x000AE, 0x000B1}, {0x000B4, 0x000B4}, {0x000B6, 0x000B8},
{0x000BB, 0x000BB}, {0x000BF, 0x000BF}, {0x000D7, 0x000D7},
{0x000F7, 0x000F7}, {0x002C2, 0x002C5}, {0x002D2, 0x002DF},
{0x002E5, 0x002EB}, {0x002ED, 0x002ED}, {0x002EF, 0x002FF},
{0x00375, 0x00375}, {0x0037E, 0x0037E}, {0x00384, 0x00385},
{0x00387, 0x00387}, {0x00482, 0x00482}, {0x0055A, 0x0055F},
{0x00589, 0x0058A}, {0x0058D, 0x0058F}, {0x00964, 0x00965},
{0x00970, 0x00970}, {0x009F2, 0x009F3}, {0x009FA, 0x009FB},
{0x009FD, 0x009FD}, {0x00BF3, 0x00BFA}, {0x00E3F, 0x00E3F},
{0x00E4F, 0x00E4F}, {0x00E5A, 0x00E5B}, {0x010FB, 0x010FB},
{0x016EB, 0x016ED}, {0x01CC0, 0x01CC7}, {0x01FBD, 0x01FBD},
{0x01FBF, 0x01FC1}, {0x01FCD, 0x01FCF}, {0x01FDD, 0x01FDF},
{0x01FED, 0x01FEF}, {0x01FFD, 0x01FFE}, {0x02010, 0x02027},
{0x02030, 0x0205E}, {0x0207A, 0x0207E}, {0x0208A, 0x0208E},
{0x020A0, 0x020C0}, {0x02100, 0x02101}, {0x02103, 0x02106},
{0x02108, 0x02109}, {0x02114, 0x02114}, {0x02116, 0x02118},
{0x0211E, 0x02123}, {0x02125, 0x02125}, {0x02127, 0x02127},
{0x02129, 0x02129}, {0x0212E, 0x0212E}, {0x0213A, 0x0213B},
{0x02140, 0x02144}, {0x0214A, 0x0214D}, {0x0214F, 0x0214F},
{0x0218A, 0x0218B}, {0x02190, 0x021FF}, {0x02400, 0x02426},
{0x02800, 0x028FF}, {0x03001, 0x03004}, {0x03008, 0x03020},
{0x03030, 0x03030}, {0x03036, 0x03037}, {0x0303D, 0x0303F},
{0x0309B, 0x0309C}, {0x030A0, 0x030A0}, {0x030FB, 0x030FB},
{0x04DC0, 0x04DFF}, {0x0A673, 0x0A673}, {0x0A67E, 0x0A67E},
{0x0A720, 0x0A721}, {0x0A789, 0x0A78A}, {0x0AB5B, 0x0AB5B},
{0x0AB6A, 0x0AB6B}, {0x0FF01, 0x0FF0F}, {0x0FF1A, 0x0FF20},
{0x0FF3B, 0x0FF40}, {0x0FF5B, 0x0FF65}, {0x0FFE0, 0x0FFE6},
{0x0FFE8, 0x0FFEE}, {0x0FFFC, 0x0FFFD}, {0x1F10D, 0x1F1AD},
{0x1F1E6, 0x1F1FF}, {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA},
};
static inline int is_symbol_or_punctuation(int cp) {
int n = (int)(sizeof(sp_ranges) / sizeof(sp_ranges[0]));
for (int i = 0; i < n; i++) {
if (cp >= sp_ranges[i][0] && cp <= sp_ranges[i][1])
return 1;
}
return 0;
}
/* ---- Combined filter for training exclusion ---- */
static inline int is_excluded_from_training(int cp) {
return is_modifier_letter(cp) || is_symbol_or_punctuation(cp);
}
/* ---- Sheet filename → start codepoint ---- */
static int sheet_start_code(const char *basename) { static int sheet_start_code(const char *basename) {
if (strstr(basename, "ascii_variable")) return 0x00; if (strstr(basename, "ascii_variable")) return 0x00;
if (strstr(basename, "latinExtA_variable")) return 0x100; if (strstr(basename, "latinExtA_variable")) return 0x100;
@@ -138,4 +188,4 @@ static int sheet_start_code(const char *basename) {
return -1; return -1;
} }
#endif /* UNICODE_LM_H */ #endif /* UNICODE_FILTER_H */

View File

@@ -28,7 +28,7 @@ from keming_machine import generate_kerning_pairs
from opentype_features import generate_features, glyph_name from opentype_features import generate_features, glyph_name
import sheet_config as SC import sheet_config as SC
FONT_VERSION = "1.15" FONT_VERSION = "1.16"
# Codepoints that get cmap entries (user-visible) # Codepoints that get cmap entries (user-visible)
# PUA forms used internally by GSUB get glyphs but NO cmap entries # PUA forms used internally by GSUB get glyphs but NO cmap entries
@@ -332,6 +332,7 @@ def build_font(assets_dir, output_path, no_bitmap=False, no_features=False):
charstrings[".notdef"] = pen.getCharString() charstrings[".notdef"] = pen.getCharString()
_unihan_cps = set(SC.CODE_RANGE[SC.SHEET_UNIHAN]) _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 _base_offsets = {} # glyph_name -> (x_offset, y_offset) for COLR layers
traced_count = 0 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: if cp in _unihan_cps:
y_offset -= ((SC.H - SC.H_UNIHAN) // 2) * SCALE 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 # Hangul jungseong/jongseong PUA variants (rows 15-18) have zero
# advance and overlay the preceding choseong. Shift their outlines # advance and overlay the preceding choseong. Shift their outlines
# left by one syllable cell width so they render at the same position. # left by one syllable cell width so they render at the same position.

View File

@@ -16,8 +16,8 @@ import sheet_config as SC
# PUA range for Hangul jamo variant storage. # PUA range for Hangul jamo variant storage.
# We need space for: max_col * max_row variants. # We need space for: max_col * max_row variants.
# Using 0xF0600-0xF1E7F # Using 0x100000-0x10187F
HANGUL_PUA_BASE = 0xF0600 HANGUL_PUA_BASE = 0x100000
def _compose_bitmaps(a, b, w, h): def _compose_bitmaps(a, b, w, h):

View File

@@ -273,7 +273,7 @@ def _generate_hangul_gsub(glyphs, has, jamo_data):
continue continue
for f in [0, 1]: for f in [0, 1]:
try: try:
row_ng = SC.get_han_initial_row(1, idx, f) row_ng = SC.get_han_initial_row(2, idx, f)
except (ValueError, KeyError): except (ValueError, KeyError):
continue continue
jung_groups_general.setdefault((row_ng, f), []).append(jcp) jung_groups_general.setdefault((row_ng, f), []).append(jcp)

View File

@@ -6,8 +6,10 @@ Ported from TerrarumSansBitmap.kt companion object and SheetConfig.kt.
# Font metrics # Font metrics
H = 20 H = 20
H_UNIHAN = 16 H_UNIHAN = 16
H_EMOJI1 = 16
W_HANGUL_BASE = 13 W_HANGUL_BASE = 13
W_UNIHAN = 16 W_UNIHAN = 16
W_EMOJI1 = 17
W_LATIN_WIDE = 9 W_LATIN_WIDE = 9
W_VAR_INIT = 15 W_VAR_INIT = 15
W_WIDEVAR_INIT = 31 W_WIDEVAR_INIT = 31
@@ -78,6 +80,12 @@ SHEET_CYRILIC_EXTC_VARW = 44
SHEET_LATIN_EXTE_VARW = 45 SHEET_LATIN_EXTE_VARW = 45
SHEET_LATIN_EXTF_VARW = 46 SHEET_LATIN_EXTF_VARW = 46
SHEET_LATIN_EXTG_VARW = 47 SHEET_LATIN_EXTG_VARW = 47
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 SHEET_UNKNOWN = 254
@@ -130,6 +138,12 @@ FILE_LIST = [
"latinExtE_variable.tga", "latinExtE_variable.tga",
"latinExtF_variable.tga", "latinExtF_variable.tga",
"latinExtG_variable.tga", "latinExtG_variable.tga",
"ogham_variable.tga",
"coptic_variable.tga",
"cyrilic_extD_variable.tga",
"maths1_extrawide_variable.tga",
"emoji1.tga",
"enclosed_alphanumeric_variable.tga",
] ]
CODE_RANGE = [ CODE_RANGE = [
@@ -143,7 +157,7 @@ CODE_RANGE = [
list(range(0x400, 0x530)), # 7: Cyrillic list(range(0x400, 0x530)), # 7: Cyrillic
list(range(0xFF00, 0x10000)), # 8: Halfwidth/Fullwidth list(range(0xFF00, 0x10000)), # 8: Halfwidth/Fullwidth
list(range(0x2000, 0x20A0)), # 9: Uni Punct list(range(0x2000, 0x20A0)), # 9: Uni Punct
list(range(0x370, 0x3CF)), # 10: Greek list(range(0x370, 0x400)), # 10: Greek
list(range(0xE00, 0xE60)), # 11: Thai list(range(0xE00, 0xE60)), # 11: Thai
list(range(0x530, 0x590)), # 12: Armenian list(range(0x530, 0x590)), # 12: Armenian
list(range(0x10D0, 0x1100)), # 13: Georgian list(range(0x10D0, 0x1100)), # 13: Georgian
@@ -173,7 +187,7 @@ CODE_RANGE = [
list(range(0xF0520, 0xF0580)), # 37: Codestyle ASCII list(range(0xF0520, 0xF0580)), # 37: Codestyle ASCII
list(range(0xFB00, 0xFB18)), # 38: Alphabetic Presentation list(range(0xFB00, 0xFB18)), # 38: Alphabetic Presentation
list(range(0x1B000, 0x1B170)), # 39: Hentaigana list(range(0x1B000, 0x1B170)), # 39: Hentaigana
list(range(0x2400, 0x2440)), # 40: Control Pictures list(range(0x2400, 0x2450)), # 40: Control Pictures
list(range(0x1FB00, 0x1FC00)), # 41: Legacy Computing list(range(0x1FB00, 0x1FC00)), # 41: Legacy Computing
list(range(0xA640, 0xA6A0)), # 42: Cyrillic Ext B list(range(0xA640, 0xA6A0)), # 42: Cyrillic Ext B
list(range(0x2DE0, 0x2E00)), # 43: Cyrillic Ext A list(range(0x2DE0, 0x2E00)), # 43: Cyrillic Ext A
@@ -181,6 +195,12 @@ CODE_RANGE = [
list(range(0xAB30, 0xAB70)), # 45: Latin Ext E list(range(0xAB30, 0xAB70)), # 45: Latin Ext E
list(range(0x10780, 0x107C0)), # 46: Latin Ext F list(range(0x10780, 0x107C0)), # 46: Latin Ext F
list(range(0x1DF00, 0x1E000)), # 47: Latin Ext G list(range(0x1DF00, 0x1E000)), # 47: Latin Ext G
list(range(0x1680, 0x16A0)), # 48: Ogham
list(range(0x2C80, 0x2D00)), # 49: Coptic
list(range(0x1E030, 0x1E090)), # 50: Cyrillic Ext D
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) CODE_RANGE_HANGUL_COMPAT = range(0x3130, 0x3190)
@@ -262,6 +282,8 @@ def get_cell_width(sheet_index):
return W_VAR_INIT + HGAP_VAR # 16 return W_VAR_INIT + HGAP_VAR # 16
if sheet_index == SHEET_UNIHAN: if sheet_index == SHEET_UNIHAN:
return W_UNIHAN return W_UNIHAN
if sheet_index == SHEET_EMOJI1:
return W_EMOJI1
if sheet_index == SHEET_HANGUL: if sheet_index == SHEET_HANGUL:
return W_HANGUL_BASE return W_HANGUL_BASE
if sheet_index == SHEET_CUSTOM_SYM: if sheet_index == SHEET_CUSTOM_SYM:
@@ -274,6 +296,8 @@ def get_cell_width(sheet_index):
def get_cell_height(sheet_index): def get_cell_height(sheet_index):
if sheet_index == SHEET_UNIHAN: if sheet_index == SHEET_UNIHAN:
return H_UNIHAN return H_UNIHAN
if sheet_index == SHEET_EMOJI1:
return H_EMOJI1
if sheet_index == SHEET_CUSTOM_SYM: if sheet_index == SHEET_CUSTOM_SYM:
return SIZE_CUSTOM_SYM return SIZE_CUSTOM_SYM
return H return H
@@ -563,5 +587,11 @@ def index_y(sheet_index, c):
SHEET_LATIN_EXTE_VARW: lambda: (c - 0xAB30) // 16, SHEET_LATIN_EXTE_VARW: lambda: (c - 0xAB30) // 16,
SHEET_LATIN_EXTF_VARW: lambda: (c - 0x10780) // 16, SHEET_LATIN_EXTF_VARW: lambda: (c - 0x10780) // 16,
SHEET_LATIN_EXTG_VARW: lambda: (c - 0x1DF00) // 16, SHEET_LATIN_EXTG_VARW: lambda: (c - 0x1DF00) // 16,
SHEET_OGHAM_VARW: lambda: (c - 0x1680) // 16,
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, SHEET_HANGUL: lambda: 0,
}.get(sheet_index, lambda: c // 16)() }.get(sheet_index, lambda: c // 16)()

Binary file not shown.

BIN
demo.PNG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -26,21 +26,22 @@ How multilingual? Real multilingual!
􏻬󿿁Под южно дърво, цъфтящо в синьо, бягаше малко пухкаво зайче󿿀􀀀 􏻬󿿁Под южно дърво, цъфтящо в синьо, бягаше малко пухкаво зайче󿿀􀀀
􏻬ᎠᏍᎦᏯᎡᎦᎢᎾᎨᎢᎣᏍᏓᎤᎩᏍᏗᎥᎴᏓᎯᎲᎢᏔᎵᏕᎦᏟᏗᏖᎸᎳᏗᏗᎧᎵᎢᏘᎴᎩ ᏙᏱᏗᏜᏫᏗᏣᏚᎦᏫᏛᏄᏓᎦᏝᏃᎠᎾᏗᎭᏞᎦᎯᎦᏘᏓᏠᎨᏏᏕᏡᎬᏢᏓᏥᏩᏝᎡᎢᎪᎢ ᎠᎦᏂᏗᎮᎢᎫᎩᎬᏩᎴᎢᎠᏆᏅᏛᎫᏊᎾᎥᎠᏁᏙᎲᏐᏈᎵᎤᎩᎸᏓᏭᎷᏤᎢᏏᏉᏯᏌᏊ ᎤᏂᏋᎢᏡᎬᎢᎰᏩᎬᏤᎵᏍᏗᏱᎩᎱᎱᎤᎩᎴᎢᏦᎢᎠᏂᏧᏣᏨᎦᏥᎪᎥᏌᏊᎤᎶᏒᎢᎢᏡᎬᎢ ᎹᎦᎺᎵᏥᎻᎼᏏᎽᏗᏩᏂᎦᏘᎾᎿᎠᏁᎬᎢᏅᎩᎾᏂᎡᎢᏌᎶᎵᏎᎷᎠᏑᏍᏗᏪᎩ ᎠᎴ ᏬᏗᏲᏭᎾᏓᏍᏓᏴᏁᎢᎤᎦᏅᏮᏰᎵᏳᏂᎨᎢ􀀀 􏻬ᎠᏍᎦᏯᎡᎦᎢᎾᎨᎢᎣᏍᏓᎤᎩᏍᏗᎥᎴᏓᎯᎲᎢᏔᎵᏕᎦᏟᏗᏖᎸᎳᏗᏗᎧᎵᎢᏘᎴᎩ ᏙᏱᏗᏜᏫᏗᏣᏚᎦᏫᏛᏄᏓᎦᏝᏃᎠᎾᏗᎭᏞᎦᎯᎦᏘᏓᏠᎨᏏᏕᏡᎬᏢᏓᏥᏩᏝᎡᎢᎪᎢ ᎠᎦᏂᏗᎮᎢᎫᎩᎬᏩᎴᎢᎠᏆᏅᏛᎫᏊᎾᎥᎠᏁᏙᎲᏐᏈᎵᎤᎩᎸᏓᏭᎷᏤᎢᏏᏉᏯᏌᏊ ᎤᏂᏋᎢᏡᎬᎢᎰᏩᎬᏤᎵᏍᏗᏱᎩᎱᎱᎤᎩᎴᎢᏦᎢᎠᏂᏧᏣᏨᎦᏥᎪᎥᏌᏊᎤᎶᏒᎢᎢᏡᎬᎢ ᎹᎦᎺᎵᏥᎻᎼᏏᎽᏗᏩᏂᎦᏘᎾᎿᎠᏁᎬᎢᏅᎩᎾᏂᎡᎢᏌᎶᎵᏎᎷᎠᏑᏍᏗᏪᎩ ᎠᎴ ᏬᏗᏲᏭᎾᏓᏍᏓᏴᏁᎢᎤᎦᏅᏮᏰᎵᏳᏂᎨᎢ􀀀
􏻬Ѳеѡфа́нъ и҆ Алеѯі́й, ѕѣлѡ̀ возлюби́вше ѱалти́рь, воспѣ́ша при свѣ́тѣ ѕвѣ́здъ, помазꙋ́юще сщ҃е́нное мѵ́ро; серафими мн̑оꙮ҆читїи̑, ꙗ҆́кѡ ѻ҆́гнь, ѡ҆крꙋжа́хꙋ прⷭ҇то́лъ Бж҃їй, и҆ всѧ̀ землѧ̀ и҆спо́лнисѧ свѣ́та, ꙗ҆́кѡ ѕмі́й попра́нъ є҆́сть􀀀 􏻬Ѳеѡфа́нъ и҆ Алеѯі́й, ѕѣлѡ̀ возлюби́вше ѱалти́рь, воспѣ́ша при свѣ́тѣ ѕвѣ́здъ, помазꙋ́юще сщ҃е́нное мѵ́ро; серафими мн̑оꙮ҆читїи̑, ꙗ҆́кѡ ѻ҆́гнь, ѡ҆крꙋжа́хꙋ прⷭ҇то́лъ Бж҃їй, и҆ всѧ̀ землѧ̀ и҆спо́лнисѧ свѣ́та, ꙗ҆́кѡ ѕмі́й попра́нъ є҆́сть􀀀
􏻬ⲡⲓⲝⲉⲛⲟⲥ ⲅⲁⲣ ⲁϥϫⲉⲙ ⲟⲩⲫⲱⲥ ϧⲉⲛ ⲡⲓⲍⲏⲗⲟⲥ ⲛⲧⲉ ϯⲯⲩⲭⲏ· ⲁϥϣⲱⲡⲓ ⲇⲉ ⲕⲁⲧⲁ ⲡⲓⲑⲉⲗⲏⲙⲁ· ⲁϥϭⲓ ⲛϩⲱⲃ ⲛⲓⲃⲉⲛ ⲟⲩⲟϩ ⲁϥϯⲙⲟⲧ􀀀
􏻬Příliš žluťoučký kůň úpěl ďábelské ódy􀀀 􏻬Příliš žluťoučký kůň úpěl ďábelské ódy􀀀
􏻬Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Walther spillede på xylofon􀀀 􏻬Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Walther spillede på xylofon􀀀
􏻬PACK MY BOX WITH FIVE DOZEN LIQUOR JUGS􀀀 􏻬Sphinx of black quartz, judge my vow􀀀
􏻬hƿæt ƿe ᵹardena inᵹear ꝺaᵹum þeoꝺ cynninᵹa þꞃym ᵹeꝼꞃumon􀀀 􏻬hƿæt ƿe ᵹardena inᵹear ꝺaᵹum þeoꝺ cynninᵹa þꞃym ᵹeꝼꞃumon􀀀
􏻬Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich GROẞEN GROẞE􀀀 􏻬Victor jagt zwölf Boxkämpfer quer über den GROẞEN Sylter Deich􀀀
􏻬ζαφείρι δέξου πάγκαλο, βαθῶν ψυχῆς τὸ σῆμα􀀀 􏻬ζαφείρι δέξου πάγκαλο, βαθῶν ψυχῆς τὸ σῆμα􀀀
􏻬ΔΙΑΦΥΛΆΞΤΕ ΓΕΝΙΚΆ ΤΗ ΖΩΉ ΣΑΣ ΑΠΌ ΒΑΘΕΙΆ ΨΥΧΙΚΆ ΤΡΑΎΜΑΤΑ􀀀
􏻬სწრაფი ყავისფერი მელა გადაახტა ზარმაც ძაღლს ᲘᲜᲢᲔᲚ ᲞᲔᲜᲢᲘᲣᲛᲘ ᲛᲘᲙᲠᲝᲞᲠᲝᲪᲔᲡᲝᲠᲘ􀀀 􏻬სწრაფი ყავისფერი მელა გადაახტა ზარმაც ძაღლს ᲘᲜᲢᲔᲚ ᲞᲔᲜᲢᲘᲣᲛᲘ ᲛᲘᲙᲠᲝᲞᲠᲝᲪᲔᲡᲝᲠᲘ􀀀
􏻬ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम अयोध्या के महाराज दशरथ के􀀀 􏻬ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम अयोध्या के महाराज दशरथ के􀀀
􏻬Kæmi ný öxi hér, ykist þjófum nú bæði víl og ádrepa􀀀 􏻬Kæmi ný öxi hér, ykist þjófum nú bæði víl og ádrepa􀀀
􏻬Ċuaiġ bé ṁórṡáċ le dlúṫspád fíoꝛḟinn trí hata mo ḋea-ṗoꝛcáin ḃig􀀀 􏻬Scríoḃ Fergus ⁊ a ṁáṫaır dán le peann úr􀀀
􏻬あめつちほしそら やまかはみねたに くもきりむろこけ ひといぬうへすゑ ゆわさるおふせよ えの𛀁をなれゐて􀀀 􏻬あめつちほしそら やまかはみねたに くもきりむろこけ ひといぬうへすゑ ゆわさるおふせよ えの𛀁をなれゐて􀀀
􏻬トリナクコヱス ユメサマセ ミヨアケワタル ヒンカシヲ ソライロハエテ オキツヘニ ホフネムレヰヌ モヤノウチ􀀀 􏻬トリナクコヱス ユメサマセ ミヨアケワタル ヒンカシヲ ソライロハエテ オキツヘニ ホフネムレヰヌ モヤノウチ􀀀
􏻬田居に出で 菜摘むわれをぞ 君召すと 求食り追ひゆく 山城の 打酔へる子ら 藻葉干せよ え舟繋けぬ􀀀 􏻬田居に出で 菜摘むわれをぞ 君召すと 求食り追ひゆく 山城の 打酔へる子ら 藻葉干せよ え舟繋けぬ􀀀
􏻬정 참판 양반댁 규수 큰 교자 타고 혼례 치른 날  찦차를 타고 온 펲시맨과 쑛다리 똠방각하􀀀 􏻬콩고물과 우유가 들어간 빙수는 차게 먹어야 특별한 맛이 잘 표현된다 유쾌했던 땃쥐 토끼풀 쫓기 바쁨􀀀
􏻬찦차를 타고 온 펲시맨과 쑛다리 똠방각하 왜날뷁􀀀
􏻬쾅 ᄒᆞ는 소리 헨 “아이구 베락 털어져ᇝ인가?” 영 걷어진 쥥은 몰르곡 경헨 ᄇᆞᆰ도록 ᄌᆞᆷ ᄒᆞᆫᄌᆞᆷ들 안 잣수다􀀀 􏻬쾅 ᄒᆞ는 소리 헨 “아이구 베락 털어져ᇝ인가?” 영 걷어진 쥥은 몰르곡 경헨 ᄇᆞᆰ도록 ᄌᆞᆷ ᄒᆞᆫᄌᆞᆷ들 안 잣수다􀀀
􏻬Četri psihi faķīri vēlu vakarā zāģēja guļbūvei durvis, fonā šņācot mežam􀀀 􏻬Četri psihi faķīri vēlu vakarā zāģēja guļbūvei durvis, fonā šņācot mežam􀀀
􏻬Įlinkdama fechtuotojo špaga sublykčiojusi pragręžė apvalų arbūzą􀀀 􏻬Įlinkdama fechtuotojo špaga sublykčiojusi pragręžė apvalų arbūzą􀀀
@@ -58,7 +59,7 @@ How multilingual? Real multilingual!
􏻬Pijamalı hasta yağız şoföre çabucak güvendi􀀀 􏻬Pijamalı hasta yağız şoföre çabucak güvendi􀀀
􏻬Жебракують філософи при ґанку церкви в Гадячі, ще й шатро їхнє п’яне знаємо􀀀 􏻬Жебракують філософи при ґанку церкви в Гадячі, ще й шатро їхнє п’яне знаємо􀀀
􏻬Do bạch kim rất quý nên sẽ dùng để lắp vô xương􀀀 􏻬Do bạch kim rất quý nên sẽ dùng để lắp vô xương􀀀
􏻬日堀油告観観藤村抄海評業庁経賃室弁市。太撮収改売週法所何都慣次現。価紙一無三洋日話転手治稿載末替付致治。􀀀 􏻬日堀油告観観藤村抄海評業庁経賃室弁市。太撮収改売週法所何都慣次現。􀀀
􏻬[pʰnɣɬɥi.m͡ŋχɫʍɨnaɸ.cθʊɫɯ.ɹɨɫʏ͡ɛx.ɯ͡ɣaxɲaɣɫ.ɸtʰɑɣɴ]􀀀 􏻬[pʰnɣɬɥi.m͡ŋχɫʍɨnaɸ.cθʊɫɯ.ɹɨɫʏ͡ɛx.ɯ͡ɣaxɲaɣɫ.ɸtʰɑɣɴ]􀀀
􏻬⠑⠥⠊⠵⠀⠟⠫⠒⠵⠀⠓⠗⠎⠉⠂⠀⠠⠊⠗⠘⠍⠓⠎⠀⠨⠣⠩⠐⠥⠍⠑⠱⠀⠈⠪⠀⠨⠷⠎⠢⠈⠧⠀⠈⠏⠒⠐⠕⠝⠀⠕⠌⠎⠀⠊⠿⠊⠪⠶⠚⠊􀀀 􏻬⠑⠥⠊⠵⠀⠟⠫⠒⠵⠀⠓⠗⠎⠉⠂⠀⠠⠊⠗⠘⠍⠓⠎⠀⠨⠣⠩⠐⠥⠍⠑⠱⠀⠈⠪⠀⠨⠷⠎⠢⠈⠧⠀⠈⠏⠒⠐⠕⠝⠀⠕⠌⠎⠀⠊⠿⠊⠪⠶⠚⠊􀀀
@@ -97,7 +98,7 @@ How multilingual? Real multilingual!
􎳌‣ Unicode fractions, also known as super/subscripts􀀀 􎳌‣ Unicode fractions, also known as super/subscripts􀀀
􏻬ᄀᆞᄅᆞᇝ ᄀᆞᅀᅢ 자거늘 밀므리 사ᄋᆞ리로ᄃᆡ 나거ᅀᅡ ᄌᆞᄆᆞ니ᅌᅵ다 셤 안해 자시ᇙ 제 한비 사ᄋᆞ리로ᄃᆡ 뷔어ᅀᅡ ᄌᆞᄆᆞ니ᅌᅵ다􀀀 􏻬ᄀᆞᄅᆞᇝ ᄀᆞᅀᅢ 자거늘 밀므리 사ᄋᆞ리로ᄃᆡ 나거ᅀᅡ ᄌᆞᄆᆞ니ᅌᅵ다 셤 안해 자시ᇙ 제 한비 사ᄋᆞ리로ᄃᆡ 뷔어ᅀᅡ ᄌᆞᄆᆞ니ᅌᅵ다􀀀
􏻬쾅 ᄒᆞ는 소리 헨 “아이구, 베락 털어져ᇝ인가?” 영 걷어진 쥥은 몰르곡 경헨 나왕 보고들랑 영헤연 ᄇᆞᆰ도록 ᄌᆞᆷ ᄒᆞᆫᄌᆞᆷ들 안 잣수다 이 시간 동네 사람들.􀀀 􏻬쾅 ᄒᆞ는 소리 헨 “아이구, 베락 털어져ᇝ인가?” 영 걷어진 쥥은 몰르곡 경헨 나왕 보고들랑 영헤연 ᄇᆞᆰ도록 ᄌᆞᆷ ᄒᆞᆫᄌᆞᆷ들 안 잣수다􀀀
􎳌‣ Full support for Old Korean/Jeju dialect orthography􀀀 􎳌‣ Full support for Old Korean/Jeju dialect orthography􀀀
@@ -121,20 +122,22 @@ How multilingual? Real multilingual!
Braille Patterns Braille Patterns
Cherokee􏿆ᴬ􀀀 Cherokee􏿆ᴬ􀀀
CJK Symbols and Punctuation CJK Symbols and Punctuation
CJK Unified Ideographs􏿆⁶􀀀 CJK Unified Ideographs
CJK Unified Ideographs Extension A􏿆¹²·¹􀀀 CJK Unified Ideographs Extension A
Combining Diacritical Marks Combining Diacritical Marks
Control Pictures Control Pictures
Coptic
Currency Symbols Currency Symbols
Cyrillic Cyrillic
Cyrillic Supplement Cyrillic Supplement
Cyrillic Extended-A/B/C Cyrillic Extended-A/B/C/D
Devanagari Devanagari
Enclosed Alphanumerics
Enclosed Alphanumeric Supplement Enclosed Alphanumeric Supplement
General Punctuations General Punctuations
Georgian􏿆ჼ􀀀 Georgian􏿆ჼ􀀀
Georgian Extended Georgian Extended
Greek and Coptic􏿆ᴱ􀀀 Greek and Coptic
Greek Extended Greek Extended
Halfwidth and Fullwidth Forms Halfwidth and Fullwidth Forms
Hangul Compatibility Jamo Hangul Compatibility Jamo
@@ -147,9 +150,13 @@ How multilingual? Real multilingual!
Katakana Phonetic Extensions Katakana Phonetic Extensions
Kana Supplement Kana Supplement
Kana Extended-A Kana Extended-A
Number Forms
Small Kana Extension Small Kana Extension
Letterlike Symbols Letterlike Symbols
Mathematical Operators
Miscellaneous Technical
Number Forms
Ogham
Optical Character Recognition
Phonetic Extensions Phonetic Extensions
Phonetic Extensions Supplement Phonetic Extensions Supplement
Runic Runic
@@ -160,9 +167,9 @@ How multilingual? Real multilingual!
Symbols for Legacy Computing Symbols for Legacy Computing
Tamil Tamil
Thai Thai
Yijing Hexagram Symbols
􏿆ᴱ􀀀 No support for Coptic    􏿆ᴬ􀀀 Uppercase only 􏿆ᶠⁱ􀀀 No support for ligatures
􏿆ᶠⁱ􀀀 No support for ligatures 􏿆ჼ􀀀 Mkhedruli only 􏿆ᴬ􀀀 Uppercase only 􏿆ჼ􀀀 Mkhedruli only
􏿆⁶􀀀 􏿆¹²·¹􀀀 Up to the specified Unicode version
GitHubs issue page is open! You can report any 􏽕errors􀀀, or leave 􏽕suggestions􀀀. You can help this font to be more versatile. (for more languages, more frameworks) 􏽕Clone􀀀 this repo, make changes, and make a 􏽕pull request􀀀! I appreciate any and all supports. GitHubs issue page is open! You can report any 􏽕errors􀀀, or leave 􏽕suggestions􀀀. You can help this font to be more versatile. (for more languages, more frameworks) 􏽕Clone􀀀 this repo, make changes, and make a 􏽕pull request􀀀! I appreciate any and all supports.

View File

@@ -579,15 +579,15 @@ const EXAMPLES = [
{ zones: 'BDFGH', wye: false, chars: '\u027A', desc: 'BDFGH' }, { zones: 'BDFGH', wye: false, chars: '\u027A', desc: 'BDFGH' },
{ zones: 'BG', wye: true, chars: '/', desc: 'BG(Y)' }, { zones: 'BG', wye: true, chars: '/', desc: 'BG(Y)' },
{ zones: 'CD', wye: false, chars: '\u10B5', desc: 'CD' }, { zones: 'CD', wye: false, chars: '\u10B5', desc: 'CD' },
{ zones: 'CDEF', wye: true, chars: '\u03A6', desc: 'CDEF(Y)' }, { zones: 'CDEF', wye: true, chars: '\u03A6,v', desc: 'CDEF(Y)' },
{ zones: 'CDEFGH', wye: false, chars: 'a,c,e', desc: 'CDEFGH' }, { zones: 'CDEFGH', wye: false, chars: 'a,e', desc: 'CDEFGH' },
{ zones: 'CDEFGHJK', wye: false, chars: 'g', desc: 'CDEFGHJK' }, { zones: 'CDEFGHJK', wye: false, chars: 'g', desc: 'CDEFGHJK' },
{ zones: 'CDEFGHK', wye: false, chars: '\u019E', desc: 'CDEFGHK' }, { zones: 'CDEFGHK', wye: false, chars: '\u019E', desc: 'CDEFGHK' },
{ zones: 'CDEFGH', wye: true, chars: 'A', desc: 'CDEFGH(Y)' },
{ zones: 'CDEGH', wye: false, chars: 'c', desc: 'CDEGH' },
{ zones: 'AB', wye: true, chars: 'Y', desc: 'AB(Y)' }, { zones: 'AB', wye: true, chars: 'Y', desc: 'AB(Y)' },
{ zones: 'ABCD', wye: true, chars: 'V', desc: 'ABCD(Y)' }, { zones: 'ABCD', wye: true, chars: 'V', desc: 'ABCD(Y)' },
{ zones: 'CDEF', wye: true, chars: 'v', desc: 'CDEF(Y)' },
{ zones: 'EFGH', wye: true, chars: '\u028C', desc: 'EFGH(Y)' }, { zones: 'EFGH', wye: true, chars: '\u028C', desc: 'EFGH(Y)' },
{ zones: 'CDEFGH', wye: true, chars: 'A', desc: 'CDEFGH(Y)' },
]; ];
function buildExamples() { function buildExamples() {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/assets/coptic_variable.tga LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/assets/ogham_variable.tga LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,134 @@
package net.torvald.terrarumsansbitmap.gdx
import com.badlogic.gdx.graphics.Pixmap
data class AtlasRegion(
val atlasX: Int,
val atlasY: Int,
val width: Int,
val height: Int,
val offsetX: Int = 0,
val offsetY: Int = 0
)
class GlyphAtlas(val atlasWidth: Int, val atlasHeight: Int) {
val pixmap = Pixmap(atlasWidth, atlasHeight, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.SourceOver }
private val regions = HashMap<Long, AtlasRegion>()
private var cursorX = 0
private var cursorY = 0
private var shelfHeight = 0
private val pendingCells = ArrayList<PendingCell>()
private class PendingCell(
val sheetID: Int,
val cellX: Int,
val cellY: Int,
val cropped: Pixmap,
val offsetX: Int,
val offsetY: Int
)
private fun atlasKey(sheetID: Int, cellX: Int, cellY: Int): Long =
sheetID.toLong().shl(32) or cellX.toLong().shl(16) or cellY.toLong()
/** Scans the cell for its non-transparent bounding box, crops, and queues for deferred packing. */
fun queueCell(sheetID: Int, cellX: Int, cellY: Int, cellPixmap: Pixmap) {
var minX = cellPixmap.width
var minY = cellPixmap.height
var maxX = -1
var maxY = -1
for (y in 0 until cellPixmap.height) {
for (x in 0 until cellPixmap.width) {
if (cellPixmap.getPixel(x, y) and 0xFF != 0) {
if (x < minX) minX = x
if (y < minY) minY = y
if (x > maxX) maxX = x
if (y > maxY) maxY = y
}
}
}
if (maxX < 0) return // entirely transparent, skip
val cropW = maxX - minX + 1
val cropH = maxY - minY + 1
val cropped = Pixmap(cropW, cropH, Pixmap.Format.RGBA8888)
cropped.drawPixmap(cellPixmap, 0, 0, minX, minY, cropW, cropH)
pendingCells.add(PendingCell(sheetID, cellX, cellY, cropped, minX, minY))
}
/** Sorts queued cells by height desc then width desc, and packs into shelves. */
fun packAllQueued() {
pendingCells.sortWith(
compareByDescending<PendingCell> { it.cropped.height }
.thenByDescending { it.cropped.width }
)
for (cell in pendingCells) {
val w = cell.cropped.width
val h = cell.cropped.height
// start new shelf if cell doesn't fit horizontally
if (cursorX + w > atlasWidth) {
cursorX = 0
cursorY += shelfHeight
shelfHeight = 0
}
pixmap.drawPixmap(cell.cropped, cursorX, cursorY)
regions[atlasKey(cell.sheetID, cell.cellX, cell.cellY)] =
AtlasRegion(cursorX, cursorY, w, h, cell.offsetX, cell.offsetY)
cursorX += w
if (h > shelfHeight) shelfHeight = h
cell.cropped.dispose()
}
pendingCells.clear()
}
fun blitSheet(sheetID: Int, sheetPixmap: Pixmap, cellW: Int, cellH: Int, cols: Int, rows: Int) {
if (cursorX > 0) {
cursorX = 0
cursorY += shelfHeight
shelfHeight = 0
}
val baseY = cursorY
pixmap.drawPixmap(sheetPixmap, 0, baseY)
for (cy in 0 until rows) {
for (cx in 0 until cols) {
regions[atlasKey(sheetID, cx, cy)] = AtlasRegion(cx * cellW, baseY + cy * cellH, cellW, cellH)
}
}
cursorY = baseY + sheetPixmap.height
cursorX = 0
shelfHeight = 0
}
fun getRegion(sheetID: Int, cellX: Int, cellY: Int): AtlasRegion? =
regions[atlasKey(sheetID, cellX, cellY)]
fun clearRegion(region: AtlasRegion) {
for (y in 0 until region.height) {
for (x in 0 until region.width) {
pixmap.drawPixel(region.atlasX + x, region.atlasY + y, 0)
}
}
}
fun dispose() {
pixmap.dispose()
}
}

View File

@@ -318,7 +318,7 @@ class TerrarumSansBitmap(
/** Props of all printable Unicode points. */ /** Props of all printable Unicode points. */
private val glyphProps = HashMap<CodePoint, GlyphProps>() private val glyphProps = HashMap<CodePoint, GlyphProps>()
private val textReplaces = HashMap<CodePoint, CodePoint>() private val textReplaces = HashMap<CodePoint, CodePoint>()
private val sheets: Array<PixmapRegionPack> private lateinit var atlas: GlyphAtlas
// private var charsetOverride = 0 // private var charsetOverride = 0
@@ -326,9 +326,11 @@ class TerrarumSansBitmap(
// private val tempFiles = ArrayList<String>() // private val tempFiles = ArrayList<String>()
init { init {
val sheetsPack = ArrayList<PixmapRegionPack>() atlas = GlyphAtlas(4096, 4096)
var unihanPixmap: Pixmap? = null
var emoji1Pixmap: Pixmap? = null
// first we create pixmap to read pixels, then make texture using pixmap // first we create pixmap to read pixels, then pack into atlas
fileList.forEachIndexed { index, it -> fileList.forEachIndexed { index, it ->
val isVariable = it.endsWith("_variable.tga") val isVariable = it.endsWith("_variable.tga")
val isXYSwapped = it.contains("xyswap", true) val isXYSwapped = it.contains("xyswap", true)
@@ -346,32 +348,6 @@ class TerrarumSansBitmap(
else else
dbgprn("loading texture [STATIC] $it") dbgprn("loading texture [STATIC] $it")
// unpack gz if applicable
/*if (it.endsWith(".gz")) {
val tmpFilePath = tempDir + "/tmp_${it.dropLast(7)}.tga"
try {
val gzi = GZIPInputStream(Gdx.files.classpath(fontParentDir + it).read(8192))
val wholeFile = gzi.readBytes()
gzi.close()
val fos = BufferedOutputStream(FileOutputStream(tmpFilePath))
fos.write(wholeFile)
fos.flush()
fos.close()
pixmap = Pixmap(Gdx.files.absolute(tmpFilePath))
// tempFiles.add(tmpFilePath)
}
catch (e: GdxRuntimeException) {
//e.printStackTrace()
dbgprn("said texture not found, skipping...")
pixmap = Pixmap(1, 1, Pixmap.Format.RGBA8888)
}
//File(tmpFileName).delete()
}
else {*/
try { try {
pixmap = Pixmap(Gdx.files.classpath("assets/$it")) pixmap = Pixmap(Gdx.files.classpath("assets/$it"))
} }
@@ -387,7 +363,6 @@ class TerrarumSansBitmap(
System.exit(1) System.exit(1)
} }
} }
//}
if (isVariable) buildWidthTable(pixmap, codeRange[index], if (isExtraWide) 32 else 16) if (isVariable) buildWidthTable(pixmap, codeRange[index], if (isExtraWide) 32 else 16)
buildWidthTableFixed() buildWidthTableFixed()
@@ -395,40 +370,75 @@ class TerrarumSansBitmap(
setupDynamicTextReplacer() setupDynamicTextReplacer()
/*if (!noShadow) { if (index == SHEET_UNIHAN) {
makeShadowForSheet(pixmap) // 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)
else if (isVariable)
PixmapRegionPack(pixmap, W_VAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
else if (index == SHEET_HANGUL)
PixmapRegionPack(pixmap, W_HANGUL_BASE, H)
else if (index == SHEET_CUSTOM_SYM)
PixmapRegionPack(pixmap, SIZE_CUSTOM_SYM, SIZE_CUSTOM_SYM)
else if (index == SHEET_RUNIC)
PixmapRegionPack(pixmap, W_LATIN_WIDE, H)
else throw IllegalArgumentException("Unknown sheet index: $index")
// this code causes initial deva chars to be skipped from rendering
// val illegalCells = HashSet<Long>()
// for (code in codeRange[index]) {
// if (glyphProps[code]?.isIllegal == true) {
// val pos = getSheetwisePosition(0, code)
// illegalCells.add(pos[0].toLong().shl(16) or pos[1].toLong())
// }
// }
//
for (cy in 0 until texRegPack.verticalCount) {
for (cx in 0 until texRegPack.horizontalCount) {
// if (cx.toLong().shl(16) or cy.toLong() in illegalCells) continue
atlas.queueCell(index, cx, cy, texRegPack.get(cx, cy))
}
}
//val texture = Texture(pixmap) texRegPack.dispose()
val texRegPack = if (isExtraWide) pixmap.dispose()
PixmapRegionPack(pixmap, W_WIDEVAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped) }
else if (isVariable)
PixmapRegionPack(pixmap, W_VAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
else if (index == SHEET_UNIHAN)
PixmapRegionPack(pixmap, W_UNIHAN, H_UNIHAN) // the only exception that is height is 16
// below they all have height of 20 'H'
else if (index == SHEET_HANGUL)
PixmapRegionPack(pixmap, W_HANGUL_BASE, H)
else if (index == SHEET_CUSTOM_SYM)
PixmapRegionPack(pixmap, SIZE_CUSTOM_SYM, SIZE_CUSTOM_SYM) // TODO variable
else if (index == SHEET_RUNIC)
PixmapRegionPack(pixmap, W_LATIN_WIDE, H)
else throw IllegalArgumentException("Unknown sheet index: $index")
//texRegPack.texture.setFilter(minFilter, magFilter)
sheetsPack.add(texRegPack)
pixmap.dispose() // you are terminated
} }
sheets = sheetsPack.toTypedArray() // sort and pack all queued cells (tight-cropped, sorted by height then width)
atlas.packAllQueued()
// pack wenquanyi (SHEET_UNIHAN) last as a contiguous blit
unihanPixmap?.let {
val cols = it.width / W_UNIHAN
val rows = it.height / H_UNIHAN
atlas.blitSheet(SHEET_UNIHAN, it, W_UNIHAN, H_UNIHAN, cols, rows)
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) // make sure null char is actually null (draws nothing and has zero width)
sheets[SHEET_ASCII_VARW].regions[0].setColor(0) atlas.getRegion(SHEET_ASCII_VARW, 0, 0)?.let { atlas.clearRegion(it) }
sheets[SHEET_ASCII_VARW].regions[0].fill()
glyphProps[0] = GlyphProps(0) glyphProps[0] = GlyphProps(0)
if (debug) {
com.badlogic.gdx.graphics.PixmapIO.writePNG(Gdx.files.absolute("$tempDir/glyph_atlas_dump.png"), atlas.pixmap)
dbgprn("atlas dumped to $tempDir/glyph_atlas_dump.png")
}
} }
override fun getLineHeight(): Float = LINE_HEIGHT.toFloat() * scale override fun getLineHeight(): Float = LINE_HEIGHT.toFloat() * scale
@@ -449,6 +459,7 @@ class TerrarumSansBitmap(
} }
private val offsetUnihan = (H - H_UNIHAN) / 2 private val offsetUnihan = (H - H_UNIHAN) / 2
private val offsetEmoji1 = (H - H_EMOJI1) / 2
private val offsetCustomSym = (H - SIZE_CUSTOM_SYM) / 2 private val offsetCustomSym = (H - SIZE_CUSTOM_SYM) / 2
private var flagFirstRun = true private var flagFirstRun = true
@@ -582,6 +593,9 @@ class TerrarumSansBitmap(
renderCol = getColour(c) renderCol = getColour(c)
} }
} }
else if (isNoDrawChar(c) || glyphProps[c]?.isIllegal == true) {
// whitespace/control/internal/invalid — no visible glyph, just advance position
}
else if (sheetID == SHEET_HANGUL) { else if (sheetID == SHEET_HANGUL) {
// Flookahead for {I, P, F} // Flookahead for {I, P, F}
@@ -599,39 +613,33 @@ class TerrarumSansBitmap(
val (indexCho, indexJung, indexJong) = indices val (indexCho, indexJung, indexJong) = indices
val (choRow, jungRow, jongRow) = rows val (choRow, jungRow, jongRow) = rows
val hangulSheet = sheets[SHEET_HANGUL] atlas.getRegion(SHEET_HANGUL, indexCho, choRow)?.let {
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
}
atlas.getRegion(SHEET_HANGUL, indexJung, jungRow)?.let {
val choTex = hangulSheet.get(indexCho, choRow) linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
val jungTex = hangulSheet.get(indexJung, jungRow) }
val jongTex = hangulSheet.get(indexJong, jongRow) atlas.getRegion(SHEET_HANGUL, indexJong, jongRow)?.let {
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
linotypePixmap.drawPixmap(choTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol) }
linotypePixmap.drawPixmap(jungTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
linotypePixmap.drawPixmap(jongTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
index += hangulLength - 1 index += hangulLength - 1
} }
else { else {
try { val posY = posmap.y[index].flipY() +
val posY = posmap.y[index].flipY() + if (sheetID == SHEET_UNIHAN) // evil exceptions
if (sheetID == SHEET_UNIHAN) // evil exceptions offsetUnihan
offsetUnihan else if (sheetID == SHEET_EMOJI1)
else if (sheetID == SHEET_CUSTOM_SYM) offsetEmoji1
offsetCustomSym else if (sheetID == SHEET_CUSTOM_SYM)
else 0 offsetCustomSym
else 0
val posX = posmap.x[index] val posX = posmap.x[index]
val texture = sheets[sheetID].get(sheetX, sheetY) atlas.getRegion(sheetID, sheetX, sheetY)?.let {
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
linotypePixmap.drawPixmap(texture, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
}
catch (noSuchGlyph: ArrayIndexOutOfBoundsException) {
} }
} }
@@ -698,6 +706,9 @@ class TerrarumSansBitmap(
renderCol = getColour(c) renderCol = getColour(c)
} }
} }
else if (isNoDrawChar(c) || glyphProps[c]?.isIllegal == true) {
// whitespace/control/internal/invalid — no visible glyph, just advance position
}
else if (sheetID == SHEET_HANGUL) { else if (sheetID == SHEET_HANGUL) {
// Flookahead for {I, P, F} // Flookahead for {I, P, F}
@@ -715,39 +726,33 @@ class TerrarumSansBitmap(
val (indexCho, indexJung, indexJong) = indices val (indexCho, indexJung, indexJong) = indices
val (choRow, jungRow, jongRow) = rows val (choRow, jungRow, jongRow) = rows
val hangulSheet = sheets[SHEET_HANGUL] atlas.getRegion(SHEET_HANGUL, indexCho, choRow)?.let {
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
}
atlas.getRegion(SHEET_HANGUL, indexJung, jungRow)?.let {
val choTex = hangulSheet.get(indexCho, choRow) linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
val jungTex = hangulSheet.get(indexJung, jungRow) }
val jongTex = hangulSheet.get(indexJong, jongRow) atlas.getRegion(SHEET_HANGUL, indexJong, jongRow)?.let {
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
linotypePixmap.drawPixmap(choTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol) }
linotypePixmap.drawPixmap(jungTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
linotypePixmap.drawPixmap(jongTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
index += hangulLength - 1 index += hangulLength - 1
} }
else { else {
try { val posY = posmap.y[index].flipY() +
val posY = posmap.y[index].flipY() + if (sheetID == SHEET_UNIHAN) // evil exceptions
if (sheetID == SHEET_UNIHAN) // evil exceptions offsetUnihan
offsetUnihan else if (sheetID == SHEET_EMOJI1)
else if (sheetID == SHEET_CUSTOM_SYM) offsetEmoji1
offsetCustomSym else if (sheetID == SHEET_CUSTOM_SYM)
else 0 offsetCustomSym
else 0
val posX = posmap.x[index] val posX = posmap.x[index]
val texture = sheets[sheetID].get(sheetX, sheetY) atlas.getRegion(sheetID, sheetX, sheetY)?.let {
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
linotypePixmap.drawPixmap(texture, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
}
catch (noSuchGlyph: ArrayIndexOutOfBoundsException) {
} }
} }
@@ -826,7 +831,7 @@ class TerrarumSansBitmap(
override fun dispose() { override fun dispose() {
super.dispose() super.dispose()
textCache.values.forEach { it.dispose() } textCache.values.forEach { it.dispose() }
sheets.forEach { it.dispose() } atlas.dispose()
} }
fun getSheetType(c: CodePoint): Int { fun getSheetType(c: CodePoint): Int {
@@ -894,6 +899,12 @@ class TerrarumSansBitmap(
SHEET_LATIN_EXTE_VARW -> latinExtEIndexY(ch) SHEET_LATIN_EXTE_VARW -> latinExtEIndexY(ch)
SHEET_LATIN_EXTF_VARW -> latinExtFIndexY(ch) SHEET_LATIN_EXTF_VARW -> latinExtFIndexY(ch)
SHEET_LATIN_EXTG_VARW -> latinExtGIndexY(ch) SHEET_LATIN_EXTG_VARW -> latinExtGIndexY(ch)
SHEET_OGHAM_VARW -> oghamIndexY(ch)
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 else -> ch / 16
} }
@@ -1020,6 +1031,7 @@ class TerrarumSansBitmap(
codeRangeHangulCompat.forEach { glyphProps[it] = GlyphProps(W_HANGUL_BASE) } codeRangeHangulCompat.forEach { glyphProps[it] = GlyphProps(W_HANGUL_BASE) }
codeRange[SHEET_RUNIC].forEach { glyphProps[it] = GlyphProps(9) } codeRange[SHEET_RUNIC].forEach { glyphProps[it] = GlyphProps(9) }
codeRange[SHEET_UNIHAN].forEach { glyphProps[it] = GlyphProps(W_UNIHAN) } 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) } (0xD800..0xDFFF).forEach { glyphProps[it] = GlyphProps(0) }
(0x100000..0x10FFFF).forEach { glyphProps[it] = GlyphProps(0) } (0x100000..0x10FFFF).forEach { glyphProps[it] = GlyphProps(0) }
(0xFFFA0..0xFFFFF).forEach { glyphProps[it] = GlyphProps(0) } (0xFFFA0..0xFFFFF).forEach { glyphProps[it] = GlyphProps(0) }
@@ -2163,6 +2175,16 @@ class TerrarumSansBitmap(
} }
} }
private fun Pixmap.drawFromAtlas(atlas: Pixmap, region: AtlasRegion, xPos: Int, yPos: Int, col: Int) {
for (y in 0 until region.height) {
for (x in 0 until region.width) {
val pixel = atlas.getPixel(region.atlasX + x, region.atlasY + y)
val newPixel = pixel colorTimes col
this.drawPixel(xPos + x + region.offsetX, yPos + y + region.offsetY, newPixel)
}
}
}
private fun Color.toRGBA8888() = private fun Color.toRGBA8888() =
(this.r * 255f).toInt().shl(24) or (this.r * 255f).toInt().shl(24) or
(this.g * 255f).toInt().shl(16) or (this.g * 255f).toInt().shl(16) or
@@ -2559,6 +2581,8 @@ class TerrarumSansBitmap(
internal const val H = 20 internal const val H = 20
internal const val H_UNIHAN = 16 internal const val H_UNIHAN = 16
internal const val W_EMOJI1 = 17
internal const val H_EMOJI1 = 16
internal const val H_DIACRITICS = 3 internal const val H_DIACRITICS = 3
@@ -2615,6 +2639,12 @@ class TerrarumSansBitmap(
internal const val SHEET_LATIN_EXTE_VARW = 45 internal const val SHEET_LATIN_EXTE_VARW = 45
internal const val SHEET_LATIN_EXTF_VARW = 46 internal const val SHEET_LATIN_EXTF_VARW = 46
internal const val SHEET_LATIN_EXTG_VARW = 47 internal const val SHEET_LATIN_EXTG_VARW = 47
internal const val SHEET_OGHAM_VARW = 48
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 internal const val SHEET_UNKNOWN = 254
@@ -2685,6 +2715,12 @@ class TerrarumSansBitmap(
"latinExtE_variable.tga", "latinExtE_variable.tga",
"latinExtF_variable.tga", "latinExtF_variable.tga",
"latinExtG_variable.tga", "latinExtG_variable.tga",
"ogham_variable.tga",
"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!! internal val codeRange = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!!
0..0xFF, // SHEET_ASCII_VARW 0..0xFF, // SHEET_ASCII_VARW
@@ -2697,7 +2733,7 @@ class TerrarumSansBitmap(
0x400..0x52F, // SHEET_CYRILIC_VARW 0x400..0x52F, // SHEET_CYRILIC_VARW
0xFF00..0xFFFF, // SHEET_HALFWIDTH_FULLWIDTH_VARW 0xFF00..0xFFFF, // SHEET_HALFWIDTH_FULLWIDTH_VARW
0x2000..0x209F, // SHEET_UNI_PUNCT_VARW 0x2000..0x209F, // SHEET_UNI_PUNCT_VARW
0x370..0x3CE, // SHEET_GREEK_VARW 0x370..0x3FF, // SHEET_GREEK_VARW
0xE00..0xE5F, // SHEET_THAI_VARW 0xE00..0xE5F, // SHEET_THAI_VARW
0x530..0x58F, // SHEET_HAYEREN_VARW 0x530..0x58F, // SHEET_HAYEREN_VARW
0x10D0..0x10FF, // SHEET_KARTULI_VARW 0x10D0..0x10FF, // SHEET_KARTULI_VARW
@@ -2727,7 +2763,7 @@ class TerrarumSansBitmap(
0xF0520..0xF057F, // SHEET_CODESTYLE_ASCII_VARW 0xF0520..0xF057F, // SHEET_CODESTYLE_ASCII_VARW
0xFB00..0xFB17, // SHEET_ALPHABETIC_PRESENTATION_FORMS 0xFB00..0xFB17, // SHEET_ALPHABETIC_PRESENTATION_FORMS
0x1B000..0x1B16F, // SHEET_HENTAIGANA_VARW 0x1B000..0x1B16F, // SHEET_HENTAIGANA_VARW
0x2400..0x243F, // SHEET_CONTROL_PICTURES_VARW 0x2400..0x244F, // SHEET_CONTROL_PICTURES_VARW
0x1FB00..0x1FBFF, // SHEET_LEGACY_COMPUTING_VARW 0x1FB00..0x1FBFF, // SHEET_LEGACY_COMPUTING_VARW
0xA640..0xA69F, // SHEET_CYRILIC_EXTB_VARW 0xA640..0xA69F, // SHEET_CYRILIC_EXTB_VARW
0x2DE0..0x2DFF, // SHEET_CYRILIC_EXTA_VARW 0x2DE0..0x2DFF, // SHEET_CYRILIC_EXTA_VARW
@@ -2735,6 +2771,12 @@ class TerrarumSansBitmap(
0xAB30..0xAB6F, // SHEET_LATIN_EXTE_VARW 0xAB30..0xAB6F, // SHEET_LATIN_EXTE_VARW
0x10780..0x107BF, // SHEET_LATIN_EXTF_VARW 0x10780..0x107BF, // SHEET_LATIN_EXTF_VARW
0x1DF00..0x1DFFF, // SHEET_LATIN_EXTG_VARW 0x1DF00..0x1DFFF, // SHEET_LATIN_EXTG_VARW
0x1680..0x169F, // SHEET_OGHAM_VARW
0x2C80..0x2CFF, // SHEET_COPTIC_VARW
0x1E030..0x1E08F, // SHEET_CYRILIC_EXTD_VARW
0x2200..0x23FF, // SHEET_MATHS1_VARW
0x1F600..0x1F64F, // SHEET_EMOJI1
0x2460..0x24FF, // SHEET_ENCLOSED_ALPHNUM_VARW
) )
private val codeRangeHangulCompat = 0x3130..0x318F private val codeRangeHangulCompat = 0x3130..0x318F
@@ -3030,6 +3072,13 @@ class TerrarumSansBitmap(
private fun isBulgarian(c: CodePoint) = c in 0xF0000..0xF005F private fun isBulgarian(c: CodePoint) = c in 0xF0000..0xF005F
private fun isSerbian(c: CodePoint) = c in 0xF0060..0xF00BF private fun isSerbian(c: CodePoint) = c in 0xF0060..0xF00BF
fun isColourCode(c: CodePoint) = c == 0x100000 || c in 0x10F000..0x10FFFF fun isColourCode(c: CodePoint) = c == 0x100000 || c in 0x10F000..0x10FFFF
private fun isNoDrawChar(c: CodePoint): Boolean =
c <= 0x20 || c == NBSP || c == SHY || c == OBJ ||
c in 0x2000..0x200D ||
c in 0xD800..0xDFFF ||
c in 0xF800..0xF8FF ||
c in 0xFFF70..0xFFF9F ||
c >= 0xFFFA0
private fun isCharsetOverride(c: CodePoint) = c in 0xFFFC0..0xFFFCF private fun isCharsetOverride(c: CodePoint) = c in 0xFFFC0..0xFFFCF
private fun isDevanagari(c: CodePoint) = c in codeRange[SHEET_DEVANAGARI_VARW] private fun isDevanagari(c: CodePoint) = c in codeRange[SHEET_DEVANAGARI_VARW]
private fun isHangulCompat(c: CodePoint) = c in codeRangeHangulCompat private fun isHangulCompat(c: CodePoint) = c in codeRangeHangulCompat
@@ -3086,6 +3135,12 @@ class TerrarumSansBitmap(
private fun latinExtEIndexY(c: CodePoint) = (c - 0xAB30) / 16 private fun latinExtEIndexY(c: CodePoint) = (c - 0xAB30) / 16
private fun latinExtFIndexY(c: CodePoint) = (c - 0x10780) / 16 private fun latinExtFIndexY(c: CodePoint) = (c - 0x10780) / 16
private fun latinExtGIndexY(c: CodePoint) = (c - 0x1DF00) / 16 private fun latinExtGIndexY(c: CodePoint) = (c - 0x1DF00) / 16
private fun oghamIndexY(c: CodePoint) = (c - 0x1680) / 16
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 charsetOverrideDefault = Character.toChars(CHARSET_OVERRIDE_DEFAULT).toSurrogatedString()
val charsetOverrideBulgarian = Character.toChars(CHARSET_OVERRIDE_BG_BG).toSurrogatedString() val charsetOverrideBulgarian = Character.toChars(CHARSET_OVERRIDE_BG_BG).toSurrogatedString()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
work_files/coptic_variable.kra LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
work_files/ogham_variable.kra LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
work_files/wenquanyi_addendum.kra LFS Normal file

Binary file not shown.