aboutsummaryrefslogtreecommitdiffstats
path: root/text/unicode.py
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-28 12:48:45 +0100
committereunhhu <[email protected]>2026-09-28 12:48:45 +0100
commit16d84b2530e154795a825fbf2b7e028e2f209b71 (patch)
tree4e90cc9b2b64a01460031941983d73acc7d883c5 /text/unicode.py
parent482d3c93255277f5251008638bc59bfab0eb4b9e (diff)
downloadvega-16d84b2530e154795a825fbf2b7e028e2f209b71.tar.gz
vega-16d84b2530e154795a825fbf2b7e028e2f209b71.zip
Generate the Unicode properties shaping needs
Joining types, general categories and scripts (with their ISO 15924 codes) as two-stage tables, canonical decompositions one step at a time, and Extended_Pictographic, from the UCD by text/unicode.py.
Diffstat (limited to 'text/unicode.py')
-rw-r--r--text/unicode.py103
1 files changed, 101 insertions, 2 deletions
diff --git a/text/unicode.py b/text/unicode.py
index e4710db..b0cd768 100644
--- a/text/unicode.py
+++ b/text/unicode.py
@@ -111,13 +111,47 @@ def normalization():
excluded.update(range(first, last + 1))
decompositions = {code: full(code) for code in single}
compositions = sorted((d[0], d[1], code) for code, d in single.items() if len(d) == 2 and code not in excluded)
- return ccc, decompositions, compositions
+ return ccc, decompositions, compositions, single
+
+
+JOINING = ['U', 'D', 'R', 'L', 'C', 'T']
+CATEGORIES = ['Cn', 'Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Mn', 'Mc', 'Me', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Ps', 'Pe', 'Pi', 'Pf',
+ 'Po', 'Sm', 'Sc', 'Sk', 'So', 'Zs', 'Zl', 'Zp', 'Cc', 'Cf', 'Cs', 'Co']
+
+
+def scripts():
+ """Each code point's Script (Unknown by default), and the scripts' names in order."""
+ names = ['Unknown', 'Common', 'Inherited']
+ values = [0] * (LAST + 1)
+ for first, last, fields, missing in ranges('Scripts.txt'):
+ if missing:
+ continue
+ if fields[0] not in names:
+ names.append(fields[0])
+ values[first:last + 1] = [names.index(fields[0])] * (last - first + 1)
+ return values, names
+
+
+def script_codes(names):
+ """Each script's ISO 15924 code (Latn for Latin), in the order of names."""
+ codes = {}
+ for line in (UCD / 'PropertyValueAliases.txt').read_text(encoding='utf-8').splitlines():
+ fields = [f.strip() for f in line.split('#', 1)[0].split(';')]
+ if len(fields) >= 3 and fields[0] == 'sc':
+ codes[fields[2]] = fields[1]
+ return [codes[name] for name in names]
def main():
bidi = derived('extracted/DerivedBidiClass.txt', BIDI, LONG)
+ joining = derived('extracted/DerivedJoiningType.txt', JOINING, {'Non_Joining': 'U'})
+ category = derived('extracted/DerivedGeneralCategory.txt', CATEGORIES)
+ script, script_names = scripts()
+ for index, name in ((0, 'Unknown'), (1, 'Common'), (2, 'Inherited'), (3, 'Latin'), (4, 'Greek'), (5, 'Cyrillic'),
+ (7, 'Hebrew'), (8, 'Arabic'), (26, 'Hangul')):
+ assert script_names[index] == name, f'text.h names {name} {index}: now {script_names.index(name)}'
table, size = two_stage('bidi', bidi)
- ccc, decompositions, compositions = normalization()
+ ccc, decompositions, compositions, single = normalization()
ccc_table, ccc_size = two_stage('ccc', ccc)
brackets = []
for first, _, fields, missing in ranges('BidiBrackets.txt'):
@@ -174,6 +208,56 @@ uint32_t vega_mirror(uint32_t code) {
print(f'/* Canonical_Combining_Class ({ccc_size} bytes). */')
print(ccc_table)
print()
+ for name, values, kind, fallback in (('joining', joining, 'VegaJoining', 'VEGA_JOINING_U'),
+ ('category', category, 'VegaCategory', 'VEGA_CN'),
+ ('script', script, 'VegaScript', 'VEGA_SCRIPT_UNKNOWN')):
+ table, size = two_stage(name, values)
+ print()
+ print(f'/* {name} ({size} bytes). */')
+ print(table)
+ print()
+ print(f'{kind} vega_{name}(uint32_t code) {{')
+ print(f' return code > 0x10FFFF ? {fallback} : ({kind}){name}_values[{name}_blocks[code >> 8] * 256u + (code & 255)];')
+ print('}')
+ print()
+ print(f'static const char *const script_names[{len(script_names)}] = {{')
+ for i in range(0, len(script_names), 6):
+ print(' ' + ', '.join(f'"{n}"' for n in script_names[i:i + 6]) + ',')
+ print('};')
+ print()
+ print('const char *vega_script_name(VegaScript script) {')
+ print(f' return (unsigned)script < {len(script_names)} ? script_names[script] : "Unknown";')
+ print('}')
+ codes = script_codes(script_names)
+ print()
+ print('/* Their ISO 15924 codes, as tags (the first letter in the high byte). */')
+ print(f'static const uint32_t script_codes[{len(codes)}] = {{')
+ for i in range(0, len(codes), 8):
+ print(' ' + ', '.join('0x%08X' % int.from_bytes(c.encode(), 'big') for c in codes[i:i + 8]) + ',')
+ print('};')
+ print()
+ print('uint32_t vega_script_code(VegaScript script) {')
+ print(f' return script_codes[(unsigned)script < {len(codes)} ? script : 0];')
+ print('}')
+ pictographic = [(first, last) for first, last, fields, missing in ranges('emoji/emoji-data.txt')
+ if not missing and fields[0] == 'Extended_Pictographic']
+ print()
+ print('/* Extended_Pictographic (emoji-data.txt), as sorted ranges. */')
+ print(f'static const uint32_t pictographic[{len(pictographic)}][2] = {{')
+ for i in range(0, len(pictographic), 6):
+ print(' ' + ', '.join('{0x%04X, 0x%04X}' % r for r in pictographic[i:i + 6]) + ',')
+ print('};')
+ print('''
+bool vega_extended_pictographic(uint32_t code) {
+ size_t low = 0, high = sizeof(pictographic) / sizeof(*pictographic);
+ while (low < high) {
+ size_t middle = (low + high) / 2;
+ if (pictographic[middle][1] < code) low = middle + 1;
+ else high = middle;
+ }
+ return low < sizeof(pictographic) / sizeof(*pictographic) && pictographic[low][0] <= code;
+}''')
+ print()
print('uint8_t vega_combining_class(uint32_t code) {')
print(' return code > 0x10FFFF ? 0 : ccc_values[ccc_blocks[code >> 8] * 256u + (code & 255)];')
print('}')
@@ -199,6 +283,21 @@ uint32_t vega_mirror(uint32_t code) {
print(' return decompositions[row][2];')
print('}')
print()
+ print('/* Canonical decomposition mappings, one step (UnicodeData.txt): code, first, second (0 for one). */')
+ print(f'static const uint32_t steps[{len(single)}][3] = {{')
+ for code in sorted(single):
+ parts = single[code] + [0]
+ print(f' {{0x{code:04X}, 0x{parts[0]:04X}, 0x{parts[1]:04X}}},')
+ print('};')
+ print()
+ print('bool vega_decomposition_step(uint32_t code, uint32_t *first, uint32_t *second) {')
+ print(' long row = find(&steps[0][0], sizeof(steps) / sizeof(*steps), 3, code);')
+ print(' if (row < 0) return false;')
+ print(' *first = steps[row][1];')
+ print(' *second = steps[row][2];')
+ print(' return true;')
+ print('}')
+ print()
print('/* The primary composites: first, second, composite, by first then second. */')
print(f'static const uint32_t compositions[{len(compositions)}][3] = {{')
for first, second, code in compositions: