11 Commits

Author SHA1 Message Date
minjaesong
39603d897b more fixes 2026-03-08 03:12:40 +09:00
minjaesong
2b1f9a866f more fixes 2026-03-08 02:38:03 +09:00
minjaesong
af1d720ec2 anchor fixes 2026-03-08 01:44:17 +09:00
minjaesong
8a52fcfb91 tfw part of your code was full of hacks 2026-03-08 00:32:42 +09:00
minjaesong
b82dbecc30 keming machine dot removal directive for OTF 2026-03-07 22:47:01 +09:00
minjaesong
2008bbf6dd keming machine dot removal directive 2026-03-07 22:41:24 +09:00
minjaesong
163e3d7b3e Arrow and Number Forms unicode block 2026-03-07 22:13:22 +09:00
minjaesong
f26998b641 Cyrillic Ext-C 2026-03-07 21:34:40 +09:00
minjaesong
4121648dfc minor fixes (2) 2026-03-07 21:08:44 +09:00
minjaesong
30de279a08 minor fixes 2026-03-07 21:03:10 +09:00
minjaesong
956599b83f Full Cyrillic, CyrlExtA/B support (enables church slavonic) 2026-03-07 20:55:47 +09:00
36 changed files with 257 additions and 116 deletions

Binary file not shown.

View File

@@ -46,8 +46,8 @@ Rightmost vertical column (should be 20 px tall) contains the tags. Tags are def
W |= Width of the character W |= Width of the character
W | W |
W -' W -'
m --Is this character lowheight?
K -, K -,
K |
K |= Tags used by the "Keming Machine" K |= Tags used by the "Keming Machine"
K -' K -'
Q ---Compiler Directive (see below) Q ---Compiler Directive (see below)
@@ -77,29 +77,32 @@ Up&Down:
<MSB,Red> SXXXXXXX SYYYYYYY 00000000 <LSB,Blue> <MSB,Red> SXXXXXXX SYYYYYYY 00000000 <LSB,Blue>
Each X and Y numbers are Signed 8-Bit Integer. Each X and Y numbers are TWO'S COMPLEMENT Signed 8-Bit Integer.
X-positive: nudges towards left X-positive: nudges towards left
Y-positive: nudges towards up Y-positive: nudges towards down
#### Diacritics Anchor Point Encoding #### Diacritics Anchor Point Encoding
4 Pixels are further divided as follows: 4 Pixels are further divided as follows:
| LSB | | Red | Green | Blue | | LSB | | Red | Green | Blue |
| ------------ | ------------ | ------------ | ------------ | ------------ | | ------------ | ------------ | ------------ | ----------- | ------------ |
| Y | Anchor point Y for: | undefined | undefined | undefined | | Y | Anchor point Y for: | undefined | undefined | undefined |
| X | Anchor point X for: | undefined | undefined | undefined | | X | Anchor point X for: | undefined | undefined | undefined |
| Y | Anchor point Y for: | (unused) | (unused) | (unused) | | Y | Anchor point Y for: | Type-0 | Type-1 | Type-2 |
| X | Anchor point X for: | Type-0 | Type-1 | Type-2 | | X | Anchor point X for: | Type-0 | Type-1 | Type-2 |
| **MSB** | | | | | | **MSB** | | | | |
<MSB,Red> 1Y1Y1Y1Y 1Y2Y2Y2Y 1Y3Y3Y3Y <LSB,Blue> <MSB,Red> 1Y1Y1Y1Y 2Y2Y2Y2Y 3Y3Y3Y3Y <LSB,Blue>
<MSB,Red> 1X1X1X1X 1X2X2X2X 1X3X3X3X <LSB,Blue> <MSB,Red> 1X1X1X1X 2X2X2X2X 3X3X3X3X <LSB,Blue>
where Red is first, Green is second, Blue is the third diacritics. where Red is first, Green is second, Blue is the third diacritics.
MSB for each word must be set so that the pixel would appear brighter on the image editor.
(the font program will only read low 7 bits for each RGB channel) Each X and Y numbers are SIGN AND MAGNITUDE 8-Bit Integer.
X-positive: nudges towards left
Y-positive: nudges towards down
#### Diacritics Type Bit Encoding #### Diacritics Type Bit Encoding

Binary file not shown.

View File

@@ -370,10 +370,9 @@ def build_font(assets_dir, output_path, no_bitmap=False, no_features=False):
x_offset = 0 x_offset = 0
x_offset -= g.props.nudge_x * SCALE x_offset -= g.props.nudge_x * SCALE
# For STACK_DOWN marks (below-base diacritics), negative nudge_y # For marks (write_on_top >= 0), positive nudge_y means shift UP
# means "shift content down to below baseline". The sign convention # in the bitmap engine (opposite to non-marks where positive = down).
# is opposite to non-marks where positive nudge_y means shift down. if g.props.write_on_top >= 0:
if g.props.stack_where == SC.STACK_DOWN and g.props.write_on_top >= 0:
y_offset = g.props.nudge_y * SCALE y_offset = g.props.nudge_y * SCALE
else: else:
y_offset = -g.props.nudge_y * SCALE y_offset = -g.props.nudge_y * SCALE

View File

@@ -38,6 +38,7 @@ class GlyphProps:
has_kern_data: bool = False has_kern_data: bool = False
is_kern_y_type: bool = False is_kern_y_type: bool = False
kerning_mask: int = 255 kerning_mask: int = 255
dot_removal: Optional[int] = None # codepoint to replace with when followed by a STACK_UP mark
directive_opcode: int = 0 directive_opcode: int = 0
directive_arg1: int = 0 directive_arg1: int = 0
directive_arg2: int = 0 directive_arg2: int = 0
@@ -131,7 +132,8 @@ def parse_variable_sheet(image, sheet_index, cell_w, cell_h, cols, is_xy_swapped
# Kerning data # Kerning data
kerning_bit1 = _tagify(image.get_pixel(code_start_x, code_start_y + 6)) kerning_bit1 = _tagify(image.get_pixel(code_start_x, code_start_y + 6))
# kerning_bit2 and kerning_bit3 are reserved kerning_bit2 = _tagify(image.get_pixel(code_start_x, code_start_y + 7))
dot_removal = None if kerning_bit2 == 0 else (kerning_bit2 >> 8)
is_kern_y_type = (kerning_bit1 & 0x80000000) != 0 is_kern_y_type = (kerning_bit1 & 0x80000000) != 0
kerning_mask = (kerning_bit1 >> 8) & 0xFFFFFF kerning_mask = (kerning_bit1 >> 8) & 0xFFFFFF
has_kern_data = (kerning_bit1 & 0xFF) != 0 has_kern_data = (kerning_bit1 & 0xFF) != 0
@@ -188,7 +190,7 @@ def parse_variable_sheet(image, sheet_index, cell_w, cell_h, cols, is_xy_swapped
align_where=align_where, write_on_top=write_on_top, align_where=align_where, write_on_top=write_on_top,
stack_where=stack_where, ext_info=ext_info, stack_where=stack_where, ext_info=ext_info,
has_kern_data=has_kern_data, is_kern_y_type=is_kern_y_type, has_kern_data=has_kern_data, is_kern_y_type=is_kern_y_type,
kerning_mask=kerning_mask, kerning_mask=kerning_mask, dot_removal=dot_removal,
directive_opcode=directive_opcode, directive_arg1=directive_arg1, directive_opcode=directive_opcode, directive_arg1=directive_arg1,
directive_arg2=directive_arg2, directive_arg2=directive_arg2,
) )

View File

@@ -70,6 +70,11 @@ languagesystem sund dflt;
if ccmp_code: if ccmp_code:
parts.append(ccmp_code) parts.append(ccmp_code)
# ccmp: dot removal (e.g. i→ı, j→ȷ when followed by STACK_UP marks)
dot_removal_code = _generate_dot_removal(glyphs, has)
if dot_removal_code:
parts.append(dot_removal_code)
# Hangul jamo GSUB assembly # Hangul jamo GSUB assembly
hangul_code = _generate_hangul_gsub(glyphs, has, jamo_data) hangul_code = _generate_hangul_gsub(glyphs, has, jamo_data)
if hangul_code: if hangul_code:
@@ -162,6 +167,54 @@ def _generate_ccmp(replacewith_subs, has):
return '\n'.join(lines) return '\n'.join(lines)
def _generate_dot_removal(glyphs, has):
"""Generate ccmp contextual substitution for dot removal.
When a base glyph tagged with dot_removal (kerning bit 2, pixel Y+7) is
followed by a STACK_UP mark, substitute the base with its dotless form.
Matches the Kotlin engine's dotRemoval logic.
"""
# Collect all STACK_UP marks
stack_up_marks = []
for cp, g in glyphs.items():
if g.props.write_on_top >= 0 and g.props.stack_where == SC.STACK_UP and has(cp):
stack_up_marks.append(cp)
if not stack_up_marks:
return ""
# Collect all base glyphs with dot_removal
dot_removal_subs = []
for cp, g in glyphs.items():
if g.props.dot_removal is not None and has(cp) and has(g.props.dot_removal):
dot_removal_subs.append((cp, g.props.dot_removal))
if not dot_removal_subs:
return ""
lines = []
# Define the STACK_UP marks class
mark_names = ' '.join(glyph_name(cp) for cp in sorted(stack_up_marks))
lines.append(f"@stackUpMarks = [{mark_names}];")
lines.append("")
# Single substitution lookup for the replacements
lines.append("lookup DotRemoval {")
for src_cp, dst_cp in sorted(dot_removal_subs):
lines.append(f" sub {glyph_name(src_cp)} by {glyph_name(dst_cp)};")
lines.append("} DotRemoval;")
lines.append("")
# Contextual rules in ccmp
lines.append("feature ccmp {")
for src_cp, _ in sorted(dot_removal_subs):
lines.append(f" sub {glyph_name(src_cp)}' lookup DotRemoval @stackUpMarks;")
lines.append("} ccmp;")
return '\n'.join(lines)
def _generate_hangul_gsub(glyphs, has, jamo_data): def _generate_hangul_gsub(glyphs, has, jamo_data):
""" """
Generate Hangul jamo GSUB lookups for syllable assembly. Generate Hangul jamo GSUB lookups for syllable assembly.
@@ -1750,7 +1803,7 @@ def _generate_mark(glyphs, has):
mark_groups = {} # (mark_type, align, is_dia, stack_cat) -> [(cp, g), ...] mark_groups = {} # (mark_type, align, is_dia, stack_cat) -> [(cp, g), ...]
for cp, g in marks.items(): for cp, g in marks.items():
is_dia = (0x0300 <= cp <= 0x036F) is_dia = True # all marks (write_on_top >= 0) are diacritics; Kotlin applies lowheight shiftdown unconditionally
sc = _stack_cat(g.props.stack_where) sc = _stack_cat(g.props.stack_where)
key = (g.props.write_on_top, g.props.align_where, is_dia, sc) key = (g.props.write_on_top, g.props.align_where, is_dia, sc)
mark_groups.setdefault(key, []).append((cp, g)) mark_groups.setdefault(key, []).append((cp, g))
@@ -1846,7 +1899,9 @@ def _generate_mark(glyphs, has):
# Lowheight adjustment for combining diacritical marks: # Lowheight adjustment for combining diacritical marks:
# shift base anchor Y down so diacritics sit closer to # shift base anchor Y down so diacritics sit closer to
# the shorter base glyph. # the shorter base glyph.
if is_dia and g.props.is_low_height: # Only applies to 'up' marks (and overlay), not 'dn',
# matching Kotlin which only adjusts in STACK_UP/STACK_UP_N_DOWN.
if is_dia and g.props.is_low_height and scat != 'dn':
if mark_type == 2: # overlay if mark_type == 2: # overlay
ay -= SC.H_OVERLAY_LOWERCASE_SHIFTDOWN * SC.SCALE ay -= SC.H_OVERLAY_LOWERCASE_SHIFTDOWN * SC.SCALE
else: # above (type 0) else: # above (type 0)
@@ -1878,12 +1933,13 @@ def _generate_mark(glyphs, has):
lines.append(f"lookup {mkmk_name} {{") lines.append(f"lookup {mkmk_name} {{")
if scat == 'up': if scat == 'up':
m2y = SC.ASCENT + SC.H_DIACRITICS * SC.SCALE m2y_base = SC.ASCENT + SC.H_DIACRITICS * SC.SCALE
else: # 'dn' else: # 'dn'
m2y = SC.ASCENT - SC.H_DIACRITICS * SC.SCALE m2y_base = SC.ASCENT - SC.H_DIACRITICS * SC.SCALE
for cp, g in mark_list: for cp, g in mark_list:
mx = mark_anchors.get(cp, 0) mx = mark_anchors.get(cp, 0)
m2y = m2y_base
lines.append( lines.append(
f" pos mark {glyph_name(cp)}" f" pos mark {glyph_name(cp)}"
f" <anchor {mx} {m2y}> mark {class_name};" f" <anchor {mx} {m2y}> mark {class_name};"
@@ -1893,6 +1949,46 @@ def _generate_mark(glyphs, has):
lines.append("") lines.append("")
mkmk_lookup_names.append(mkmk_name) mkmk_lookup_names.append(mkmk_name)
# --- Nudge-Y gap correction for MarkToMark ---
# Without cascade, the gap between consecutive 'up' marks is
# H_DIACRITICS + nudge_y_2 - nudge_y_1
# which is less than H_DIACRITICS when nudge_y_1 > nudge_y_2
# (e.g. Cyrillic uni2DED nudge=2 followed by uni0487 nudge=0).
# Add contextual positioning to compensate: shift mark2 up by
# (nudge_y_1 - nudge_y_2) * SCALE for each such pair.
# This keeps Thai correct (same nudge on both marks → no correction)
# while fixing Cyrillic (different nudge → correction applied).
nudge_groups = {} # nudge_y -> [glyph_name, ...]
for (mark_type, align, is_dia, scat), mark_list in sorted(mark_groups.items()):
if scat != 'up':
continue
for cp, g in mark_list:
ny = g.props.nudge_y
nudge_groups.setdefault(ny, []).append(glyph_name(cp))
distinct_nudges = sorted(nudge_groups.keys())
correction_pairs = []
for n1 in distinct_nudges:
for n2 in distinct_nudges:
if n1 > n2:
correction_pairs.append((n1, n2, (n1 - n2) * SC.SCALE))
if correction_pairs:
for ny, glyphs in sorted(nudge_groups.items()):
lines.append(f"@up_nudge_{ny} = [{' '.join(sorted(glyphs))}];")
lines.append("")
mkmk_corr_name = "mkmk_nudge_correct"
lines.append(f"lookup {mkmk_corr_name} {{")
for n1, n2, val in correction_pairs:
lines.append(
f" pos @up_nudge_{n1} <0 0 0 0>"
f" @up_nudge_{n2} <0 {val} 0 0>;"
)
lines.append(f"}} {mkmk_corr_name};")
lines.append("")
mkmk_lookup_names.append(mkmk_corr_name)
# Register MarkToBase lookups under mark. # Register MarkToBase lookups under mark.
# dev2 is excluded: HarfBuzz/DirectWrite use abvm for Devanagari marks. # dev2 is excluded: HarfBuzz/DirectWrite use abvm for Devanagari marks.
# deva is INCLUDED: CoreText's old-Indic shaper may need mark/mkmk # deva is INCLUDED: CoreText's old-Indic shaper may need mark/mkmk

View File

@@ -72,6 +72,9 @@ SHEET_ALPHABETIC_PRESENTATION_FORMS = 38
SHEET_HENTAIGANA_VARW = 39 SHEET_HENTAIGANA_VARW = 39
SHEET_CONTROL_PICTURES_VARW = 40 SHEET_CONTROL_PICTURES_VARW = 40
SHEET_LEGACY_COMPUTING_VARW = 41 SHEET_LEGACY_COMPUTING_VARW = 41
SHEET_CYRILIC_EXTB_VARW = 42
SHEET_CYRILIC_EXTA_VARW = 43
SHEET_CYRILIC_EXTC_VARW = 44
SHEET_UNKNOWN = 254 SHEET_UNKNOWN = 254
@@ -118,6 +121,9 @@ FILE_LIST = [
"hentaigana_variable.tga", "hentaigana_variable.tga",
"control_pictures_variable.tga", "control_pictures_variable.tga",
"symbols_for_legacy_computing_variable.tga", "symbols_for_legacy_computing_variable.tga",
"cyrilic_extB_variable.tga",
"cyrilic_extA_variable.tga",
"cyrilic_extC_variable.tga",
] ]
CODE_RANGE = [ CODE_RANGE = [
@@ -151,7 +157,7 @@ CODE_RANGE = [
list(range(0xA720, 0xA800)), # 27: Latin Ext D list(range(0xA720, 0xA800)), # 27: Latin Ext D
list(range(0x20A0, 0x20D0)), # 28: Currencies list(range(0x20A0, 0x20D0)), # 28: Currencies
list(range(0xFFE00, 0xFFFA0)), # 29: Internal list(range(0xFFE00, 0xFFFA0)), # 29: Internal
list(range(0x2100, 0x2150)), # 30: Letterlike list(range(0x2100, 0x2200)), # 30: Letterlike
list(range(0x1F100, 0x1F200)), # 31: Enclosed Alphanum Supl list(range(0x1F100, 0x1F200)), # 31: Enclosed Alphanum Supl
list(range(0x0B80, 0x0C00)) + list(range(0xF00C0, 0xF0100)), # 32: Tamil list(range(0x0B80, 0x0C00)) + list(range(0xF00C0, 0xF0100)), # 32: Tamil
list(range(0x980, 0xA00)), # 33: Bengali list(range(0x980, 0xA00)), # 33: Bengali
@@ -163,6 +169,9 @@ CODE_RANGE = [
list(range(0x1B000, 0x1B170)), # 39: Hentaigana list(range(0x1B000, 0x1B170)), # 39: Hentaigana
list(range(0x2400, 0x2440)), # 40: Control Pictures list(range(0x2400, 0x2440)), # 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(0x2DE0, 0x2E00)), # 43: Cyrillic Ext A
list(range(0x1C80, 0x1C8F)), # 43: Cyrillic Ext C
] ]
CODE_RANGE_HANGUL_COMPAT = range(0x3130, 0x3190) CODE_RANGE_HANGUL_COMPAT = range(0x3130, 0x3190)
@@ -539,5 +548,8 @@ def index_y(sheet_index, c):
SHEET_HENTAIGANA_VARW: lambda: (c - 0x1B000) // 16, SHEET_HENTAIGANA_VARW: lambda: (c - 0x1B000) // 16,
SHEET_CONTROL_PICTURES_VARW: lambda: (c - 0x2400) // 16, SHEET_CONTROL_PICTURES_VARW: lambda: (c - 0x2400) // 16,
SHEET_LEGACY_COMPUTING_VARW: lambda: (c - 0x1FB00) // 16, SHEET_LEGACY_COMPUTING_VARW: lambda: (c - 0x1FB00) // 16,
SHEET_CYRILIC_EXTB_VARW: lambda: (c - 0xA640) // 16,
SHEET_CYRILIC_EXTA_VARW: lambda: (c - 0x2DE0) // 16,
SHEET_CYRILIC_EXTC_VARW: lambda: (c - 0x1C80) // 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: 168 KiB

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -25,6 +25,7 @@ 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􀀀 􏻬PACK MY BOX WITH FIVE DOZEN LIQUOR JUGS􀀀
@@ -104,6 +105,10 @@ How multilingual? Real multilingual!
􎳌‣ Full support for Archaic Kana/Hentaigana􀀀 􎳌‣ Full support for Archaic Kana/Hentaigana􀀀
􏻬серафими мн̑оꙮ҆читїи̑, ꙗ҆́кѡ ѻ҆́гнь, ѡ҆крꙋжа́хꙋ прⷭ҇то́лъ Бж҃їй, и҆ всѧ̀ землѧ̀ и҆спо́лнисѧ свѣ́та􀀀
􎳌‣ Fan of Church Slavonic? Weve got you!􀀀
􏃯Supported Unicode Blocks:􀀀 􏃯Supported Unicode Blocks:􀀀
Basic Latin Basic Latin
@@ -111,6 +116,7 @@ How multilingual? Real multilingual!
Latin Extended Additional Latin Extended Additional
Latin Extended-A/B/C/D Latin Extended-A/B/C/D
Armenian Armenian
Arrows
Bengali􏿆ᶠⁱ􀀀 Bengali􏿆ᶠⁱ􀀀
Braille Patterns Braille Patterns
Cherokee􏿆⁷􀀀 Cherokee􏿆⁷􀀀
@@ -120,8 +126,9 @@ How multilingual? Real multilingual!
Combining Diacritical Marks Combining Diacritical Marks
Control Pictures Control Pictures
Currency Symbols Currency Symbols
Cyrillic􏿆ᴭ􀀀 Cyrillic
Cyrillic Supplement􏿆ᴭ􀀀 Cyrillic Supplement
Cyrillic Extended-A/B/C
Devanagari Devanagari
Enclosed Alphanumeric Supplement Enclosed Alphanumeric Supplement
General Punctuations General Punctuations
@@ -140,6 +147,7 @@ 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
Phonetic Extensions Phonetic Extensions
@@ -153,7 +161,7 @@ How multilingual? Real multilingual!
Tamil Tamil
Thai Thai
􏿆ᴭ􀀀 No support for archæic letters  􏿆ᴱ􀀀 No support for Coptic 􏿆ᴱ􀀀 No support for Coptic
􏿆ᶠⁱ􀀀 No support for ligatures  􏿆ჼ􀀀 Mkhedruli only 􏿆ᶠⁱ􀀀 No support for ligatures  􏿆ჼ􀀀 Mkhedruli only
􏿆⁶􀀀 􏿆⁷􀀀 􏿆⁹􀀀 􏿆¹²·¹􀀀 Up to the specified Unicode version 􏿆⁶􀀀 􏿆⁷􀀀 􏿆⁹􀀀 􏿆¹²·¹􀀀 Up to the specified Unicode version

View File

@@ -1,7 +1,7 @@
--- Pixel 0 --- Pixel 0
- Lowheight bit - Lowheight bit
- encoding: has pixel - it's low height - encoding: has pixel - it's low height
- used by the diacritics system to quickly look up if the character is low height without parsing the Pixel 1 - bit must be set if above-diacritics should be lowered (e.g. lowercase b, which has 'A' shape bit but considered lowheight)
### Legends ### Legends
# #
@@ -106,3 +106,5 @@ dot removal for diacritics:
- encoding: - encoding:
- <MSB> RRRRRRRR GGGGGGGG BBBBBBBB <LSB> - <MSB> RRRRRRRR GGGGGGGG BBBBBBBB <LSB>
--- Pixel 3
Unused for now.

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.

View File

@@ -1,9 +1,13 @@
package net.torvald.terrarumsansbitmap package net.torvald.terrarumsansbitmap
import net.torvald.terrarumsansbitmap.gdx.CodePoint
/** /**
* Created by minjaesong on 2021-11-25. * Created by minjaesong on 2021-11-25.
*/ */
data class DiacriticsAnchor(val type: Int, val x: Int, val y: Int, val xUsed: Boolean, val yUsed: Boolean) data class DiacriticsAnchor(val type: Int, val x: Int, val y: Int) {
val isZero = (x == 0 && y == 0)
}
/** /**
* Created by minjaesong on 2018-08-07. * Created by minjaesong on 2018-08-07.
*/ */
@@ -15,7 +19,7 @@ data class GlyphProps(
val nudgeX: Int = 0, val nudgeX: Int = 0,
val nudgeY: Int = 0, val nudgeY: Int = 0,
val diacriticsAnchors: Array<DiacriticsAnchor> = Array(6) { DiacriticsAnchor(it, 0, 0, false, false) }, val diacriticsAnchors: Array<DiacriticsAnchor> = Array(6) { DiacriticsAnchor(it, 0, 0) },
val alignWhere: Int = 0, // ALIGN_LEFT..ALIGN_BEFORE val alignWhere: Int = 0, // ALIGN_LEFT..ALIGN_BEFORE
@@ -29,6 +33,8 @@ data class GlyphProps(
val isKernYtype: Boolean = false, val isKernYtype: Boolean = false,
val kerningMask: Int = 255, val kerningMask: Int = 255,
val dotRemoval: CodePoint? = null,
val directiveOpcode: Int = 0, // 8-bits wide val directiveOpcode: Int = 0, // 8-bits wide
val directiveArg1: Int = 0, // 8-bits wide val directiveArg1: Int = 0, // 8-bits wide
val directiveArg2: Int = 0, // 8-bits wide val directiveArg2: Int = 0, // 8-bits wide
@@ -95,10 +101,6 @@ data class GlyphProps(
diacriticsAnchors.forEach { diacriticsAnchors.forEach {
hash = hash xor it.type hash = hash xor it.type
hash = hash * 16777619 hash = hash * 16777619
hash = hash xor (it.x or (if (it.xUsed) 128 else 0))
hash = hash * 16777619
hash = hash xor (it.y or (if (it.yUsed) 128 else 0))
hash = hash * 16777619
} }
hash = hash xor tags hash = hash xor tags

View File

@@ -888,6 +888,9 @@ class TerrarumSansBitmap(
SHEET_HENTAIGANA_VARW -> hentaiganaIndexY(ch) SHEET_HENTAIGANA_VARW -> hentaiganaIndexY(ch)
SHEET_CONTROL_PICTURES_VARW -> controlPicturesIndexY(ch) SHEET_CONTROL_PICTURES_VARW -> controlPicturesIndexY(ch)
SHEET_LEGACY_COMPUTING_VARW -> legacyComputingIndexY(ch) SHEET_LEGACY_COMPUTING_VARW -> legacyComputingIndexY(ch)
SHEET_CYRILIC_EXTB_VARW -> cyrilicExtBIndexY(ch)
SHEET_CYRILIC_EXTA_VARW -> cyrilicExtAIndexY(ch)
SHEET_CYRILIC_EXTC_VARW -> cyrilicExtCIndexY(ch)
else -> ch / 16 else -> ch / 16
} }
@@ -918,9 +921,9 @@ class TerrarumSansBitmap(
val isLowHeight = (pixmap.getPixel(codeStartX, codeStartY + 5).and(255) != 0) val isLowHeight = (pixmap.getPixel(codeStartX, codeStartY + 5).and(255) != 0)
// Keming machine parameters // Keming machine parameters
val kerningBit1 = pixmap.getPixel(codeStartX, codeStartY + 6).tagify() val kerningBit1 = pixmap.getPixel(codeStartX, codeStartY + 6).tagify() // glyph shape
val kerningBit2 = pixmap.getPixel(codeStartX, codeStartY + 7).tagify() val kerningBit2 = pixmap.getPixel(codeStartX, codeStartY + 7).tagify() // dot removal
val kerningBit3 = pixmap.getPixel(codeStartX, codeStartY + 8).tagify() val kerningBit3 = pixmap.getPixel(codeStartX, codeStartY + 8).tagify() // unused
var isKernYtype = ((kerningBit1 and 0x80000000.toInt()) != 0) var isKernYtype = ((kerningBit1 and 0x80000000.toInt()) != 0)
var kerningMask = kerningBit1.ushr(8).and(0xFFFFFF) var kerningMask = kerningBit1.ushr(8).and(0xFFFFFF)
val hasKernData = kerningBit1 and 255 != 0//(kerningBit1 and 255 != 0 && kerningMask != 0xFFFF) val hasKernData = kerningBit1 and 255 != 0//(kerningBit1 and 255 != 0 && kerningMask != 0xFFFF)
@@ -944,12 +947,12 @@ class TerrarumSansBitmap(
val shift = (3 - (it % 3)) * 8 val shift = (3 - (it % 3)) * 8
val yPixel = pixmap.getPixel(codeStartX, codeStartY + yPos).tagify() val yPixel = pixmap.getPixel(codeStartX, codeStartY + yPos).tagify()
val xPixel = pixmap.getPixel(codeStartX, codeStartY + yPos + 1).tagify() val xPixel = pixmap.getPixel(codeStartX, codeStartY + yPos + 1).tagify()
val yUsed = (yPixel ushr shift) and 128 != 0 val ySgn = ((yPixel ushr shift) and 128).let { if (it == 0) -1 else 1 }
val xUsed = (xPixel ushr shift) and 128 != 0 val xSgn = ((xPixel ushr shift) and 128).let { if (it == 0) -1 else 1 }
val y = if (yUsed) (yPixel ushr shift) and 127 else 0 val y = ((yPixel ushr shift) and 127) * ySgn
val x = if (xUsed) (xPixel ushr shift) and 127 else 0 val x = ((xPixel ushr shift) and 127) * xSgn
DiacriticsAnchor(it, x, y, xUsed, yUsed) DiacriticsAnchor(it, x, y)
}.toTypedArray() }.toTypedArray()
val alignWhere = (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 15).and(255) != 0).toInt() shl y) } val alignWhere = (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 15).and(255) != 0).toInt() shl y) }
@@ -968,7 +971,9 @@ class TerrarumSansBitmap(
GlyphProps.STACK_DONT GlyphProps.STACK_DONT
else (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 18).and(255) != 0).toInt() shl y) } else (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 18).and(255) != 0).toInt() shl y) }
glyphProps[code] = GlyphProps(width, isLowHeight, nudgeX, nudgeY, diacriticsAnchors, alignWhere, writeOnTop, stackWhere, IntArray(15), hasKernData, isKernYtype, kerningMask, directiveOpcode, directiveArg1, directiveArg2) val dotRemoval = if (kerningBit2 == 0) null else kerningBit2.ushr(8)
glyphProps[code] = GlyphProps(width, isLowHeight, nudgeX, nudgeY, diacriticsAnchors, alignWhere, writeOnTop, stackWhere, IntArray(15), hasKernData, isKernYtype, kerningMask, dotRemoval, directiveOpcode, directiveArg1, directiveArg2)
// extra info // extra info
val extCount = glyphProps[code]?.requiredExtInfoCount() ?: 0 val extCount = glyphProps[code]?.requiredExtInfoCount() ?: 0
@@ -1015,17 +1020,6 @@ class TerrarumSansBitmap(
(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) }
// manually add width of one orphan insular letter
// WARNING: glyphs in 0xA770..0xA778 has invalid data, further care is required
glyphProps[0x1D79] = GlyphProps(9)
// 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)
} }
private fun Int.halveWidth() = this / 2 + 1 private fun Int.halveWidth() = this / 2 + 1
@@ -1120,6 +1114,7 @@ class TerrarumSansBitmap(
var nonDiacriticCounter = 0 // index of last instance of non-diacritic char var nonDiacriticCounter = 0 // index of last instance of non-diacritic char
var stackUpwardCounter = 0 // TODO separate stack counter for centre- and right aligned var stackUpwardCounter = 0 // TODO separate stack counter for centre- and right aligned
var stackDownwardCounter = 0 var stackDownwardCounter = 0
var nudgeUpHighWater = 0 // tracks max nudgeY seen in current stack, so subsequent marks with lower nudge still clear the previous one
val HALF_VAR_INIT = W_VAR_INIT.minus(1).div(2) val HALF_VAR_INIT = W_VAR_INIT.minus(1).div(2)
@@ -1198,6 +1193,7 @@ class TerrarumSansBitmap(
stackUpwardCounter = 0 stackUpwardCounter = 0
stackDownwardCounter = 0 stackDownwardCounter = 0
nudgeUpHighWater = 0
} }
// FIXME HACK: using 0th diacritics' X-anchor pos as a type selector // FIXME HACK: using 0th diacritics' X-anchor pos as a type selector
/*else if (thisProp.writeOnTop && thisProp.diacriticsAnchors[0].x == GlyphProps.DIA_JOINER) { /*else if (thisProp.writeOnTop && thisProp.diacriticsAnchors[0].x == GlyphProps.DIA_JOINER) {
@@ -1217,30 +1213,32 @@ class TerrarumSansBitmap(
// set X pos according to alignment information // set X pos according to alignment information
posXbuffer[charIndex] = -thisProp.nudgeX + posXbuffer[charIndex] = -thisProp.nudgeX +
when (thisProp.alignWhere) { when (thisProp.alignWhere) {
GlyphProps.ALIGN_LEFT, GlyphProps.ALIGN_BEFORE -> posXbuffer[nonDiacriticCounter] GlyphProps.ALIGN_LEFT, GlyphProps.ALIGN_BEFORE -> {
val anchorPointX = if (itsProp.diacriticsAnchors[diacriticsType].isZero) itsProp.width else itsProp.diacriticsAnchors[diacriticsType].x
posXbuffer[nonDiacriticCounter] + anchorPointX
}
GlyphProps.ALIGN_RIGHT -> { GlyphProps.ALIGN_RIGHT -> {
// println("thisprop alignright $kerning, $extraWidth") // println("thisprop alignright $kerning, $extraWidth")
val anchorPoint = val anchorPointX = if (itsProp.diacriticsAnchors[diacriticsType].isZero) itsProp.width else itsProp.diacriticsAnchors[diacriticsType].x
if (!itsProp.diacriticsAnchors[diacriticsType].xUsed) itsProp.width else itsProp.diacriticsAnchors[diacriticsType].x
extraWidth += thisProp.width extraWidth += thisProp.width
posXbuffer[nonDiacriticCounter] + anchorPoint - W_VAR_INIT + kerning + extraWidth posXbuffer[nonDiacriticCounter] + anchorPointX - W_VAR_INIT + kerning + extraWidth
} }
GlyphProps.ALIGN_CENTRE -> { GlyphProps.ALIGN_CENTRE -> {
val anchorPoint = val anchorPointX = if (itsProp.diacriticsAnchors[diacriticsType].isZero) itsProp.width.div(2) else itsProp.diacriticsAnchors[diacriticsType].x
if (!itsProp.diacriticsAnchors[diacriticsType].xUsed) itsProp.width.div(2) else itsProp.diacriticsAnchors[diacriticsType].x
if (itsProp.alignWhere == GlyphProps.ALIGN_RIGHT) { if (itsProp.alignWhere == GlyphProps.ALIGN_RIGHT) {
if (thisChar in 0x900..0x902) if (thisChar in 0x900..0x902)
posXbuffer[nonDiacriticCounter] + anchorPoint + (itsProp.width - 1).div(2) posXbuffer[nonDiacriticCounter] + anchorPointX + (itsProp.width - 1).div(2)
else else
posXbuffer[nonDiacriticCounter] + anchorPoint + (itsProp.width + 1).div(2) posXbuffer[nonDiacriticCounter] + anchorPointX + (itsProp.width + 1).div(2)
} else { } else {
if (thisChar in 0x900..0x902) if (thisChar in 0x900..0x902)
posXbuffer[nonDiacriticCounter] + anchorPoint - (W_VAR_INIT + 1) / 2 posXbuffer[nonDiacriticCounter] + anchorPointX - (W_VAR_INIT + 1) / 2
else else
posXbuffer[nonDiacriticCounter] + anchorPoint - HALF_VAR_INIT posXbuffer[nonDiacriticCounter] + anchorPointX - HALF_VAR_INIT
} }
} }
else -> throw InternalError("Unsupported alignment: ${thisProp.alignWhere}") else -> throw InternalError("Unsupported alignment: ${thisProp.alignWhere}")
@@ -1277,14 +1275,14 @@ class TerrarumSansBitmap(
// set Y pos according to diacritics position // set Y pos according to diacritics position
when (thisProp.stackWhere) { when (thisProp.stackWhere) {
GlyphProps.STACK_DOWN -> { GlyphProps.STACK_DOWN -> {
posYbuffer[charIndex] = (-thisProp.nudgeY + H_DIACRITICS * stackDownwardCounter) * flipY.toSign() posYbuffer[charIndex] = (-thisProp.nudgeY + H_DIACRITICS * stackDownwardCounter) * flipY.toSign() - thisProp.nudgeY
stackDownwardCounter++ stackDownwardCounter++
} }
GlyphProps.STACK_UP -> { GlyphProps.STACK_UP -> {
posYbuffer[charIndex] = -thisProp.nudgeY + (-H_DIACRITICS * stackUpwardCounter + -thisProp.nudgeY) * flipY.toSign() val effectiveNudge = maxOf(thisProp.nudgeY, nudgeUpHighWater)
posYbuffer[charIndex] = -effectiveNudge + (-H_DIACRITICS * stackUpwardCounter + -effectiveNudge) * flipY.toSign() + effectiveNudge
// shift down on lowercase if applicable // shift down on lowercase if applicable
if (getSheetType(thisChar) in autoShiftDownOnLowercase && if (lastNonDiacriticChar.isLowHeight()) {
lastNonDiacriticChar.isLowHeight()) {
//dbgprn("AAARRRRHHHH for character ${thisChar.toHex()}") //dbgprn("AAARRRRHHHH for character ${thisChar.toHex()}")
//dbgprn("lastNonDiacriticChar: ${lastNonDiacriticChar.toHex()}") //dbgprn("lastNonDiacriticChar: ${lastNonDiacriticChar.toHex()}")
//dbgprn("cond: ${thisProp.alignXPos == GlyphProps.DIA_OVERLAY}, charIndex: $charIndex") //dbgprn("cond: ${thisProp.alignXPos == GlyphProps.DIA_OVERLAY}, charIndex: $charIndex")
@@ -1294,6 +1292,7 @@ class TerrarumSansBitmap(
posYbuffer[charIndex] += H_STACKUP_LOWERCASE_SHIFTDOWN * flipY.toSign() // if minus-assign doesn't work, try plus-assign posYbuffer[charIndex] += H_STACKUP_LOWERCASE_SHIFTDOWN * flipY.toSign() // if minus-assign doesn't work, try plus-assign
} }
nudgeUpHighWater = effectiveNudge
stackUpwardCounter++ stackUpwardCounter++
// dbgprn("lastNonDiacriticChar: ${lastNonDiacriticChar.charInfo()}; stack counter: $stackUpwardCounter") // dbgprn("lastNonDiacriticChar: ${lastNonDiacriticChar.charInfo()}; stack counter: $stackUpwardCounter")
@@ -1302,22 +1301,25 @@ class TerrarumSansBitmap(
posYbuffer[charIndex] = (-thisProp.nudgeY + H_DIACRITICS * stackDownwardCounter) * flipY.toSign() posYbuffer[charIndex] = (-thisProp.nudgeY + H_DIACRITICS * stackDownwardCounter) * flipY.toSign()
stackDownwardCounter++ stackDownwardCounter++
val effectiveNudge = maxOf(thisProp.nudgeY, nudgeUpHighWater)
posYbuffer[charIndex] = (-thisProp.nudgeY + -H_DIACRITICS * stackUpwardCounter) * flipY.toSign() posYbuffer[charIndex] = (-effectiveNudge + -H_DIACRITICS * stackUpwardCounter) * flipY.toSign()
// shift down on lowercase if applicable // shift down on lowercase if applicable
if (getSheetType(thisChar) in autoShiftDownOnLowercase && if (lastNonDiacriticChar.isLowHeight()) {
lastNonDiacriticChar.isLowHeight()) {
if (diacriticsType == GlyphProps.DIA_OVERLAY) if (diacriticsType == GlyphProps.DIA_OVERLAY)
posYbuffer[charIndex] += H_OVERLAY_LOWERCASE_SHIFTDOWN * flipY.toSign() // if minus-assign doesn't work, try plus-assign posYbuffer[charIndex] += H_OVERLAY_LOWERCASE_SHIFTDOWN * flipY.toSign() // if minus-assign doesn't work, try plus-assign
else else
posYbuffer[charIndex] += H_STACKUP_LOWERCASE_SHIFTDOWN * flipY.toSign() // if minus-assign doesn't work, try plus-assign posYbuffer[charIndex] += H_STACKUP_LOWERCASE_SHIFTDOWN * flipY.toSign() // if minus-assign doesn't work, try plus-assign
} }
nudgeUpHighWater = effectiveNudge
stackUpwardCounter++ stackUpwardCounter++
} }
// for BEFORE_N_AFTER, do nothing in here // for BEFORE_N_AFTER, do nothing in here
} }
// nudge Y pos according to anchor position
posYbuffer[charIndex] -= itsProp.diacriticsAnchors[diacriticsType].y
// Don't reset extraWidth here! // Don't reset extraWidth here!
} }
} }
@@ -1327,7 +1329,7 @@ class TerrarumSansBitmap(
if (str.isNotEmpty()) { if (str.isNotEmpty()) {
val lastCharProp = glyphProps[str.last()] val lastCharProp = glyphProps[str.last()]
val penultCharProp = glyphProps[str[nonDiacriticCounter]] ?: val penultCharProp = glyphProps[str[nonDiacriticCounter]] ?:
(if (errorOnUnknownChar) throw throw InternalError("No GlyphProps for char '${str[nonDiacriticCounter]}' " + (if (errorOnUnknownChar) throw InternalError("No GlyphProps for char '${str[nonDiacriticCounter]}' " +
"(${str[nonDiacriticCounter].charInfo()})") else nullProp) "(${str[nonDiacriticCounter].charInfo()})") else nullProp)
posXbuffer[posXbuffer.lastIndex] = posXbuffer[posXbuffer.lastIndex - 1] + // DON'T add 1 to house the shadow, it totally breaks stuffs posXbuffer[posXbuffer.lastIndex] = posXbuffer[posXbuffer.lastIndex - 1] + // DON'T add 1 to house the shadow, it totally breaks stuffs
if (lastCharProp != null && lastCharProp.writeOnTop >= 0) { if (lastCharProp != null && lastCharProp.writeOnTop >= 0) {
@@ -1523,8 +1525,8 @@ class TerrarumSansBitmap(
} }
// for lowercase i and j, if cNext is a diacritic that goes on top, remove the dots // for lowercase i and j, if cNext is a diacritic that goes on top, remove the dots
else if (diacriticDotRemoval.containsKey(c) && (glyphProps[cNext]?.writeOnTop ?: -1) >= 0 && glyphProps[cNext]?.stackWhere == GlyphProps.STACK_UP) { else if (glyphProps[c]!!.dotRemoval != null && (glyphProps[cNext]?.writeOnTop ?: -1) >= 0 && glyphProps[cNext]?.stackWhere == GlyphProps.STACK_UP) {
seq.add(diacriticDotRemoval[c]!!) seq.add(glyphProps[c]!!.dotRemoval!!)
} }
// BEGIN of tamil subsystem implementation // BEGIN of tamil subsystem implementation
@@ -2604,6 +2606,9 @@ class TerrarumSansBitmap(
internal const val SHEET_HENTAIGANA_VARW = 39 internal const val SHEET_HENTAIGANA_VARW = 39
internal const val SHEET_CONTROL_PICTURES_VARW = 40 internal const val SHEET_CONTROL_PICTURES_VARW = 40
internal const val SHEET_LEGACY_COMPUTING_VARW = 41 internal const val SHEET_LEGACY_COMPUTING_VARW = 41
internal const val SHEET_CYRILIC_EXTB_VARW = 42
internal const val SHEET_CYRILIC_EXTA_VARW = 43
internal const val SHEET_CYRILIC_EXTC_VARW = 44
internal const val SHEET_UNKNOWN = 254 internal const val SHEET_UNKNOWN = 254
@@ -2625,10 +2630,6 @@ class TerrarumSansBitmap(
const val MOVABLE_BLOCK_1 = 0xFFFF0 const val MOVABLE_BLOCK_1 = 0xFFFF0
private val autoShiftDownOnLowercase = arrayOf(
SHEET_DIACRITICAL_MARKS_VARW
)
private val fileList = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!! private val fileList = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!!
"ascii_variable.tga", "ascii_variable.tga",
"hangul_johab.tga", "hangul_johab.tga",
@@ -2672,6 +2673,9 @@ class TerrarumSansBitmap(
"hentaigana_variable.tga", "hentaigana_variable.tga",
"control_pictures_variable.tga", "control_pictures_variable.tga",
"symbols_for_legacy_computing_variable.tga", "symbols_for_legacy_computing_variable.tga",
"cyrilic_extB_variable.tga",
"cyrilic_extA_variable.tga",
"cyrilic_extC_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
@@ -2704,7 +2708,7 @@ class TerrarumSansBitmap(
0xA720..0xA7FF, // SHEET_EXTD_VARW 0xA720..0xA7FF, // SHEET_EXTD_VARW
0x20A0..0x20CF, // SHEET_CURRENCIES_VARW 0x20A0..0x20CF, // SHEET_CURRENCIES_VARW
0xFFE00..0xFFF9F, // SHEET_INTERNAL_VARW 0xFFE00..0xFFF9F, // SHEET_INTERNAL_VARW
0x2100..0x214F, // SHEET_LETTERLIKE_MATHS_VARW 0x2100..0x21FF, // SHEET_LETTERLIKE_MATHS_VARW
0x1F100..0x1F1FF, // SHEET_ENCLOSED_ALPHNUM_SUPL_VARW 0x1F100..0x1F1FF, // SHEET_ENCLOSED_ALPHNUM_SUPL_VARW
(0x0B80..0x0BFF) + (0xF00C0..0xF00FF), // SHEET_TAMIL_VARW (0x0B80..0x0BFF) + (0xF00C0..0xF00FF), // SHEET_TAMIL_VARW
0x980..0x9FF, // SHEET_BENGALI_VARW 0x980..0x9FF, // SHEET_BENGALI_VARW
@@ -2716,6 +2720,9 @@ class TerrarumSansBitmap(
0x1B000..0x1B16F, // SHEET_HENTAIGANA_VARW 0x1B000..0x1B16F, // SHEET_HENTAIGANA_VARW
0x2400..0x243F, // SHEET_CONTROL_PICTURES_VARW 0x2400..0x243F, // SHEET_CONTROL_PICTURES_VARW
0x1FB00..0x1FBFF, // SHEET_LEGACY_COMPUTING_VARW 0x1FB00..0x1FBFF, // SHEET_LEGACY_COMPUTING_VARW
0xA640..0xA69F, // SHEET_CYRILIC_EXTB_VARW
0x2DE0..0x2DFF, // SHEET_CYRILIC_EXTA_VARW
0x1C80..0x1C8F, // SHEET_CYRILIC_EXTC_VARW
) )
private val codeRangeHangulCompat = 0x3130..0x318F private val codeRangeHangulCompat = 0x3130..0x318F
@@ -2733,11 +2740,6 @@ class TerrarumSansBitmap(
0x20..0x7F, 0x20..0x7F,
) )
private val diacriticDotRemoval = hashMapOf(
'i'.toInt() to 0x131,
'j'.toInt() to 0x237
)
internal fun Int.charInfo() = "U+${this.toString(16).padStart(4, '0').toUpperCase()}: ${Character.getName(this)}" internal fun Int.charInfo() = "U+${this.toString(16).padStart(4, '0').toUpperCase()}: ${Character.getName(this)}"
const val NQSP = 0x2000 const val NQSP = 0x2000
@@ -3066,6 +3068,9 @@ class TerrarumSansBitmap(
private fun hentaiganaIndexY(c: CodePoint) = (c - 0x1B000) / 16 private fun hentaiganaIndexY(c: CodePoint) = (c - 0x1B000) / 16
private fun controlPicturesIndexY(c: CodePoint) = (c - 0x2400) / 16 private fun controlPicturesIndexY(c: CodePoint) = (c - 0x2400) / 16
private fun legacyComputingIndexY(c: CodePoint) = (c - 0x1FB00) / 16 private fun legacyComputingIndexY(c: CodePoint) = (c - 0x1FB00) / 16
private fun cyrilicExtBIndexY(c: CodePoint) = (c - 0xA640) / 16
private fun cyrilicExtAIndexY(c: CodePoint) = (c - 0x2DE0) / 16
private fun cyrilicExtCIndexY(c: CodePoint) = (c - 0x1C80) / 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()

View File

@@ -229,16 +229,16 @@ class TerrarumTypewriterBitmap(
val nudgeY = nudgingBits.ushr(16).toByte().toInt() // signed 8-bit int val nudgeY = nudgingBits.ushr(16).toByte().toInt() // signed 8-bit int
val diacriticsAnchors = (0..5).map { val diacriticsAnchors = (0..5).map {
val yPos = 11 + (it / 3) * 2 val yPos = 13 - (it / 3) * 2
val shift = (3 - (it % 3)) * 8 val shift = (3 - (it % 3)) * 8
val yPixel = pixmap.getPixel(codeStartX, codeStartY + yPos).tagify() val yPixel = pixmap.getPixel(codeStartX, codeStartY + yPos).tagify()
val xPixel = pixmap.getPixel(codeStartX, codeStartY + yPos + 1).tagify() val xPixel = pixmap.getPixel(codeStartX, codeStartY + yPos + 1).tagify()
val y = (yPixel ushr shift) and 127 val ySgn = ((yPixel ushr shift) and 128).let { if (it == 0) -1 else 1 }
val x = (xPixel ushr shift) and 127 val xSgn = ((xPixel ushr shift) and 128).let { if (it == 0) -1 else 1 }
val yUsed = (yPixel ushr shift) >= 128 val y = ((yPixel ushr shift) and 127) * ySgn
val xUsed = (yPixel ushr shift) >= 128 val x = ((xPixel ushr shift) and 127) * xSgn
DiacriticsAnchor(it, x, y, xUsed, yUsed) DiacriticsAnchor(it, x, y)
}.toTypedArray() }.toTypedArray()
val alignWhere = (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 15).and(255) != 0).toInt() shl y) } val alignWhere = (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 15).and(255) != 0).toInt() shl y) }

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.