191 lines
5.0 KiB
Python
Executable File
191 lines
5.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
""" The FONTCHARACTER Python module is used for manipulating the source
|
|
and binary formats. See <../../formats/SOURCE.md> for more information.
|
|
"""
|
|
|
|
import os, sys, string, yaml
|
|
|
|
# Sainitize for the `id` field
|
|
def sanitize_id(s):
|
|
return ''.join(ch for ch in s \
|
|
if ch in string.ascii_letters + string.digits)
|
|
|
|
class Reference:
|
|
""" The FONTCHARACTER source object.
|
|
Loads the characters and corrects the data. """
|
|
|
|
def __init__(self, path, sets_only=False):
|
|
# Load sets
|
|
self.__load_sets(yaml.load(open(\
|
|
os.path.join(path, 'sets.yml')).read()))
|
|
if sets_only: return
|
|
|
|
# Load categories
|
|
self.categories = {}
|
|
for c in yaml.load(open(os.path.join(path, 'categories.yml')).read()):
|
|
self.__explore_category(c, '', '', '')
|
|
|
|
# Load all of the YAML files
|
|
self.__load_characters(yaml.load(open(\
|
|
os.path.join(path, 'characters.yml')).read()))
|
|
|
|
# Gather leaders [TODO: `leading` field?]
|
|
for st in self.sets.keys():
|
|
lead = []
|
|
for code in self.sets[st]['characters'].keys():
|
|
lead += [code >> 8]
|
|
self.sets[st]['leading'] = set(lead)
|
|
|
|
def __explore_category(self, c, id, prefix, suffix):
|
|
""" Explore a category. """
|
|
|
|
# Iterate on things
|
|
id += c['id']
|
|
try: prefix = c['prefix'] + ' ' + prefix
|
|
except: True
|
|
try: suffix = suffix + ' ' + c['suffix']
|
|
except: True
|
|
|
|
# Add current (sub)category
|
|
self.categories[id] = {'prefix': prefix, 'suffix': suffix}
|
|
|
|
# Explore subcategories
|
|
if c.get('sub'):
|
|
for s in c['sub']:
|
|
self.__explore_category(s, id + '/', prefix, suffix)
|
|
|
|
def __load_sets(self, raw):
|
|
""" Explore sets. """
|
|
|
|
self.default_set = ''
|
|
self.sets = {}
|
|
|
|
# Initialize kids
|
|
kids = {}
|
|
|
|
# Read raw entries
|
|
for s in raw:
|
|
self.sets[s['id']] = {
|
|
'description': s['description'],
|
|
'characters': {},
|
|
'parent': s.get('parent'),
|
|
'kids': []}
|
|
if s.get('default'): self.default_set = s['id']
|
|
if s.get('parent'):
|
|
if not kids.get(s['parent']): kids[s['parent']] = []
|
|
kids[s['parent']] += [s['id']]
|
|
|
|
# Add kids to real elements
|
|
for parent, k in kids.items():
|
|
self.sets[parent]['kids'] += kids[parent]
|
|
|
|
def __inherit_character(self, id, code, inherit, pr):
|
|
""" Inherit character.
|
|
|
|
`id`: id of the set, code: code of the character,
|
|
`inherit`: the set to inherit it from,
|
|
`pr`: priority (starting from 0, the more the further). """
|
|
|
|
if not self.sets[id]['characters'].get(code) \
|
|
or self.sets[id]['characters'][code]['_pr'] > pr:
|
|
self.sets[id]['characters'][code] = {'inherit': inherit, '_pr': pr}
|
|
for k in self.sets[id]['kids']:
|
|
self.__inherit_character(k, code, inherit, pr + 1)
|
|
|
|
def __load_characters(self, raw):
|
|
""" Load characters. """
|
|
|
|
# Main loop
|
|
for c in raw:
|
|
# Get the complete name
|
|
n = c['name']
|
|
if c.get('category'):
|
|
ct = self.categories[c['category']]
|
|
n = ct['prefix'] + n + ct['suffix']
|
|
|
|
# Get the character set, and the priority
|
|
try: st = c['set']
|
|
except: st = self.default_set
|
|
|
|
# Make the character
|
|
code = c['code']
|
|
char = {'name': n, '_pr': 0}
|
|
|
|
# Check the multi thingy
|
|
m = c.get('multi')
|
|
if type(m) == list and m:
|
|
char['multi'] = \
|
|
list(map(lambda x:int(x, 16) if type(x) == str else x, m))
|
|
elif type(m) == int:
|
|
char['multi'] = [m]
|
|
|
|
# Check the unicode thingy
|
|
u = c.get('unicode')
|
|
if type(u) == list and u:
|
|
char['unicode'] = \
|
|
list(map(lambda x:int(x, 16) if type(x) == str else x, u))
|
|
elif type(u) == int:
|
|
char['unicode'] = [u]
|
|
|
|
# Check the ascii thingy
|
|
if c.get('ascii'):
|
|
char['ascii'] = c['ascii']
|
|
elif char.get('unicode') \
|
|
and all(0x00 <= c <= 0xFF for c in char['unicode']):
|
|
char['ascii'] = ''.join(map(chr,char['unicode']))
|
|
|
|
# Check the id thingy
|
|
if c.get('id'):
|
|
char['id'] = c['id']
|
|
elif char.get('ascii'):
|
|
char['id'] = sanitize_id(char['ascii'])
|
|
if not char.get('id') and not char.get('multi'):
|
|
char['id'] = sanitize_id(char['name'])
|
|
|
|
# Add it to the set
|
|
self.sets[st]['characters'][code] = char
|
|
for k in self.sets[st]['kids']:
|
|
self.__inherit_character(k, code, st, 1)
|
|
|
|
# Get ascii/unicode equivalents
|
|
for id, st in self.sets.items():
|
|
for code in st['characters'].keys():
|
|
self.__deduce_character_id(id, code)
|
|
|
|
def __deduce_character_id(self, id, code):
|
|
""" Calculate a multi-character's ID. """
|
|
|
|
char = self.sets[id]['characters'][code]
|
|
if char['_pr'] > 0 or char.get('id'): return
|
|
m = ""
|
|
if not char.get('multi'): m = sanitize_id(char.get('name'))
|
|
else:
|
|
for num, c in map(lambda x:(x, self.sets[id]['characters'][x]), \
|
|
char['multi']):
|
|
parent = id
|
|
if c['_pr'] > 0:
|
|
parent = c['inherit']
|
|
c = self.sets[parent]['characters'][num]
|
|
if c.get('multi'):
|
|
self.__deduce_character_id(parent, num)
|
|
m += c['id']
|
|
char['id'] = m
|
|
|
|
def list(self):
|
|
""" Get the list of sets. """
|
|
|
|
l = list(self.sets.keys())
|
|
l.remove(self.default_set)
|
|
return [self.default_set] + l
|
|
|
|
def get(self, id = None):
|
|
""" Get a set. """
|
|
|
|
if type(id) != str:
|
|
id = self.default_set
|
|
st = self.sets[id]
|
|
st['id'] = id
|
|
return st
|
|
|
|
# End of file.
|