Converts Keyboard Layout Files to TUXEDO Computers SVG keyboard layout.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
7.0 KiB

#!/usr/bin/env python3
# ./klf2tux.py <tuxedo svg template> <klf file> <language code>
# e.g. ./klf2tux.py infinitybook-tpl.svg azerty.klf fr
# '-> This command will output SVG to stdout.
import json
import sys
import re
# Hide same letters on the same key
HIDE_DUPLICATE=True
# Hide the AltGr and AltGr+Shift letters from all keys
NOALTKEYS=False
# If NOALTKEYS and ALTKEYS_FOR_NUM are both set to True,
# the AltGr letters will still show, exclusively for
# the numbers row.
# No effect if NOALTKEYS is False
ALTKEYS_FOR_NUM=False
# Handle keysym synonyms not covered by klfc tool
XKB_SYNONYMS = {
'circumflex': '^',
'diaeresis': '¨',
'tilde': '~',
'pound': '£',
'pilcrow': '',
'multiplication': '×',
'oslash': 'Ø',
'dead_caron': '^',
'dead_doubleacute': '~',
'dead_doubleacute': '~',
'dead_ogonek': ',',
'AltR': 'Alt Gr',
'Alt_R': 'Alt Gr',
'AltGr_R': 'Alt Gr',
'AltL': 'Alt Gr',
'Alt_L': 'Alt Gr',
'AltGr_L': 'Alt Gr',
'Shift_R': 'Shift',
'Shift_L': 'Shift',
'Control_L': 'Ctrl',
'Control_R': 'Ctrl',
'SCtrl': 'Ctrl',
'acute': '´',
'doubleacute': '˝',
'grave': '`',
'caron': 'ˇ',
'stroke': '/',
'breve': '˘',
'abovedot': '˙',
'currency': '¤',
'horn': ' ̛',
'macron': '¯',
'hook': ' ̉',
'abovering': '˚',
'ogonek': '˛',
'belowdot': '.',
'greek': 'µ',
'cedilla': '¸',
'belowcomma': ',',
'belowmacron': 'ˍ',
}
if len(sys.argv) != 4:
print("usage: ./klf2tux.py <tux svg template> <klf file> <language code>")
print("e.g. ./klf2tux.py infinitybook-tpl.svg azerty.klf fr")
sys.exit(1)
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
# defining user specified language
lang = sys.argv[3]
# opening translations file
with open("translations.json", 'r') as json_tr:
tr = json.load(json_tr)
# return the translation from a specified key
# fallbacks to English if no translations available
def loc(key):
try:
return tr[key][lang]
except:
eprint("WARN: Translation missing for key {}, falling back to English".format(key))
try:
return tr[key]["en"]
except:
eprint("ERROR: Key {} not found in translations file".format(key))
# takes the svg template and the tpl key we want to replace.
# return the same svg template, with the replaced values
class template(str):
# replace a %KEY string with its translation
def translate(self, key):
# exception for "Esc" key:
# in the template we have %ESC1 and %ESC2
# %ESC1 -> short Esc text (e.g. "Esc")
# %ESC2 -> long Esc text (e.g. "Échap")
if key == "ESC":
if len(loc(key)) > 3:
tpl = template(self.replace("%"+key+"2", loc(key), 1))
tpl = template(tpl.replace("%"+key+"1", '', 1))
else:
tpl = template(self.replace("%"+key+"1", loc(key), 1))
tpl = template(tpl.replace("%"+key+"2", '', 1))
return tpl
else:
return template(self.replace("%"+key, loc(key), 1))
# replaces the %KEY in template with a key from the klf file
# tplkey example : { "pos": "2", "letters": [ "é", "2", "~" ] }
def placekeygroup(self, pos, letters):
# If there's only one letter on the key, put it at the top-left
if len(letters) == 1:
return template(self.placekey(pos, None, 'N') \
.placekey(pos, None, 'A') \
.placekey(pos, None, 'G') \
.replace('%'+pos.upper()+'U', esc(letters[0]), 1))
newtpl = self.placekey(pos, letters, 'U')
# only write lowercase to keyboard if the letter is different
# unless HIDE_DUPLICATE is false
if HIDE_DUPLICATE == False or esc(letters[1]).upper() != esc(letters[0]).upper():
newtpl = newtpl.placekey(pos, letters, 'N')
else:
newtpl = newtpl.placekey(pos, None, 'N')
# early return if the user does not want the altgr and altgr+shift keys
# print altgr numbers anyway if ALTKEYS_FOR_NUM is specified
if NOALTKEYS == True and (ALTKEYS_FOR_NUM == True and not is_numrow(pos)) \
or ALTKEYS_FOR_NUM == False:
return newtpl.placekey(pos, None, 'A').placekey(pos, None, 'G')
try:
# write the letter if the top-left key is different
# unless HIDE_DUPLICATE is true
if HIDE_DUPLICATE == False or esc(letters[0]).upper() != esc(letters[2]).upper():
newtpl = newtpl.placekey(pos, letters, 'A')
else:
raise
except:
newtpl = newtpl.placekey(pos, None, 'A')
# Second NOALTKEYS check, this time we quit for real
if NOALTKEYS == True:
return newtpl.placekey(pos, None, 'G')
try:
if HIDE_DUPLICATE == False or (esc(letters[1]).upper() != esc(letters[3]).upper()
and esc(letters[2]).upper() != esc(letters[3]).upper()):
newtpl = newtpl.placekey(pos, letters, 'G')
else:
raise
except:
newtpl = newtpl.placekey(pos, None, 'G')
return newtpl
# 0 = Regular
# 1 = Shift
# 2 = Alt Gr
# 3 = Alt Gr + Shift
# 4 = Empty
def placekey(self, pos, letters, keytype):
if keytype == 'N':
keynum = 0
elif keytype == 'U':
keynum = 1
elif keytype == 'A':
keynum = 2
else:
keynum = 3
try:
return template(self.replace('%'+pos.upper()+keytype, esc(letters[keynum]), 1))
except:
return template(self.replace('%'+pos.upper()+keytype, '', 1))
# escape characters & < > in svg
# also catch the keysym aliases klfc failed to catch
def esc(letter):
if letter == '&':
new_letter = "&amp;"
elif letter == '<':
new_letter = "&lt;"
elif letter == '>':
new_letter = "&gt;"
elif len(letter) > 1:
if letter in XKB_SYNONYMS:
new_letter = XKB_SYNONYMS[letter]
else:
eprint("WARN: Letter {} is unknown.".format(letter))
new_letter = ''
else:
new_letter = letter
return new_letter
# check if `pos` is in the num row
# (for the ALTKEYS_FOR_NUM parameter)
def is_numrow(pos):
return (pos == '+' or pos == '-' \
or (pos >= '0' and pos <= '9'))
# opening KLF file
with open(sys.argv[2], 'r') as json_klf:
klf = json.load(json_klf)
# opening template file
with open(sys.argv[1], 'r') as svg_tpl:
tpl = template(svg_tpl.read())
# translate keys
for trstr in tr.keys():
tpl = tpl.translate(trstr)
# Write KLF values in the svg
for key in klf["keys"]:
# eprint(key["pos"])
tpl = tpl.placekeygroup(key["pos"], key["letters"])
# remove missing keys and print a warning
empty_keys = re.findall(">%.{1,6}<", tpl)
for e in empty_keys:
e_clean = e.replace('>', '').replace('<', '')
eprint("WARN: Empty key found: {}".format(e_clean))
tpl = re.sub(">%.{1,6}<", "><", tpl)
print(tpl)