# vim:et sts=4 sw=4 # # ibus-typing-booster - A completion input method for IBus # # Copyright (c) 2015-2016 Mike FABIAN # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see '''A module to do transliteration using m17n-lib. ''' from typing import Dict from typing import List from typing import Tuple from typing import NamedTuple from typing import Iterable from typing import Any import sys import re import ctypes import logging from gi import require_version # pylint: disable=wrong-import-position require_version('IBus', '1.0') from gi.repository import IBus # pylint: enable=wrong-import-position import itb_util LOGGER = logging.getLogger('ibus-typing-booster') # pylint: disable=invalid-name # pylint: disable=too-few-public-methods # pylint: disable=missing-class-docstring # pylint: disable=protected-access class libm17n__MSymbolStruct(ctypes.Structure): pass libm17n__MSymbol = ctypes.POINTER(libm17n__MSymbolStruct) class libm17n__MPlist(ctypes.Structure): pass class libm17n__MConverter(ctypes.Structure): pass class libm17n__MInputMethod(ctypes.Structure): pass class libm17n__MText(ctypes.Structure): pass libm17n__MSymbolStruct._fields_ = [ ('managing_key', ctypes.c_uint), ('name', ctypes.c_char_p), ('length', ctypes.c_int), ('plist', libm17n__MPlist), ('next', ctypes.POINTER(libm17n__MSymbolStruct))] class libm17n__MInputContext__spot(ctypes.Structure): _fields_ = [ ('x', ctypes.c_int), ('y', ctypes.c_int), ('ascent', ctypes.c_int), ('descent', ctypes.c_int), ('fontsize', ctypes.c_int), ('mt', ctypes.POINTER(libm17n__MText)), ('pos', ctypes.c_int)] class libm17n__MInputContext(ctypes.Structure): _fields_ = [ ('im', ctypes.POINTER(libm17n__MInputMethod)), ('produced', ctypes.POINTER(libm17n__MText)), ('arg', ctypes.c_void_p), ('active', ctypes.c_int), ('spot', libm17n__MInputContext__spot), ('info', ctypes.c_void_p), ('status', ctypes.POINTER(libm17n__MText)), ('status_changed', ctypes.c_int), ('preedit', ctypes.POINTER(libm17n__MText)), ('preedit_changed', ctypes.c_int), ('cursor_pos', ctypes.c_int), ('cursor_pos_changed', ctypes.c_int), ('candidate_list', ctypes.POINTER(libm17n__MPlist)), ('candidate_index', ctypes.c_int), ('candidate_from', ctypes.c_int), ('candidate_to', ctypes.c_int), ('candidate_show', ctypes.c_int), ('candidates_changed', ctypes.c_int), ('plist', ctypes.POINTER(libm17n__MPlist))] # pylint: enable=invalid-name # pylint: enable=too-few-public-methods # pylint: enable=missing-class-docstring # pylint: enable=protected-access # pylint: disable=invalid-name libm17n__lib = None libm17n__msymbol = None libm17n__mplist = None libm17n__mconv_buffer_converter = None libm17n__mconv_reset_converter = None libm17n__mconv_rebind_buffer = None libm17n__mconv_encode = None libm17n__minput_open_im = None libm17n__minput_create_ic = None libm17n__minput_reset_ic = None libm17n__minput_filter = None libm17n__minput_lookup = None libm17n__mtext = None libm17n__mtext_len = None libm17n__Mcoding_utf_8 = None _utf8_converter = None libm17n__minput_get_variable = None libm17n__mplist_key = None libm17n__mplist_value = None libm17n__mplist_next = None libm17n__msymbol_name = None libm17n__mplist_add = None libm17n__mplist_length = None libm17n__mconv_decode_buffer = None libm17n__minput_config_variable = None libm17n__minput_save_config = None # pylint: enable=invalid-name def mtext_to_string(mtext_pointer: Any) -> str: '''Return the text contained in an MText object as a Python string :param mtext_pointer: pointer to the MText object to get the text from :type mtext_pointer: pointer to an libm17n MText object ''' libm17n__mconv_reset_converter(_utf8_converter) # type: ignore # one Unicode character cannot have more than 6 UTF-8 bytes # (actually not more than 4 ...) bufsize = (libm17n__mtext_len(mtext_pointer) + 1) * 6 # type: ignore conversion_buffer = bytes(bufsize) libm17n__mconv_rebind_buffer( # type: ignore _utf8_converter, ctypes.c_char_p(conversion_buffer), ctypes.c_int(bufsize)) libm17n__mconv_encode(_utf8_converter, mtext_pointer) # type: ignore # maybe not all of the buffer was really used for the conversion, # cut of the unused part: conversion_buffer = conversion_buffer[0:conversion_buffer.find(b'\x00')] return conversion_buffer.decode('utf-8') def init() -> None: '''Open libm17n and fill global variables for functions and variables from libm17n ''' # pylint: disable=invalid-name # pylint: disable=global-statement global libm17n__lib libm17n__lib = ctypes.CDLL('libm17n.so.0', mode = ctypes.RTLD_GLOBAL) libm17n__lib.m17n_init() global libm17n__mplist libm17n__mplist = libm17n__lib.mplist libm17n__mplist.argtypes = [] libm17n__mplist.restype = ctypes.POINTER(libm17n__MPlist) global libm17n__mconv_buffer_converter libm17n__mconv_buffer_converter = libm17n__lib.mconv_buffer_converter libm17n__mconv_buffer_converter.argtypes = [ libm17n__MSymbol, ctypes.c_char_p, ctypes.c_int] libm17n__mconv_buffer_converter.restype = ctypes.POINTER( libm17n__MConverter) global libm17n__mconv_reset_converter libm17n__mconv_reset_converter = libm17n__lib.mconv_reset_converter libm17n__mconv_reset_converter.argtypes = [ ctypes.POINTER(libm17n__MConverter)] libm17n__mconv_reset_converter.restype = ctypes.c_int global libm17n__mconv_rebind_buffer libm17n__mconv_rebind_buffer = libm17n__lib.mconv_rebind_buffer libm17n__mconv_rebind_buffer.argtypes = [ ctypes.POINTER(libm17n__MConverter), ctypes.c_char_p, ctypes.c_int] libm17n__mconv_rebind_buffer.restype = ctypes.POINTER(libm17n__MConverter) global libm17n__mconv_encode libm17n__mconv_encode = libm17n__lib.mconv_encode libm17n__mconv_encode.argtypes = [ ctypes.POINTER(libm17n__MConverter), ctypes.POINTER(libm17n__MText)] libm17n__mconv_encode.restype = ctypes.c_int global libm17n__msymbol libm17n__msymbol = libm17n__lib.msymbol libm17n__msymbol.argtypes = [ctypes.c_char_p] libm17n__msymbol.restype = libm17n__MSymbol global libm17n__minput_open_im libm17n__minput_open_im = libm17n__lib.minput_open_im libm17n__minput_open_im.argtypes = [ libm17n__MSymbol, libm17n__MSymbol, ctypes.c_void_p] libm17n__minput_open_im.restype = ctypes.POINTER(libm17n__MInputMethod) global libm17n__minput_create_ic libm17n__minput_create_ic = libm17n__lib.minput_create_ic libm17n__minput_create_ic.argtypes = [ ctypes.POINTER(libm17n__MInputMethod), ctypes.c_void_p] libm17n__minput_create_ic.restype = ctypes.POINTER(libm17n__MInputContext) global libm17n__minput_reset_ic libm17n__minput_reset_ic = libm17n__lib.minput_reset_ic libm17n__minput_reset_ic.argtypes = [ ctypes.POINTER(libm17n__MInputContext)] global libm17n__minput_filter libm17n__minput_filter = libm17n__lib.minput_filter libm17n__minput_filter.argtypes = [ ctypes.POINTER(libm17n__MInputContext), libm17n__MSymbol, ctypes.c_void_p] libm17n__minput_filter.restype = ctypes.c_int global libm17n__minput_lookup libm17n__minput_lookup = libm17n__lib.minput_lookup libm17n__minput_lookup.argtypes = [ ctypes.POINTER(libm17n__MInputContext), libm17n__MSymbol, ctypes.c_void_p, ctypes.POINTER(libm17n__MText)] libm17n__minput_lookup.restype = ctypes.c_int global libm17n__mtext libm17n__mtext = libm17n__lib.mtext libm17n__mtext.argtypes = [] libm17n__mtext.restype = ctypes.POINTER(libm17n__MText) global libm17n__mtext_len libm17n__mtext_len = libm17n__lib.mtext_len libm17n__mtext_len.argtypes = [ctypes.POINTER(libm17n__MText)] libm17n__mtext_len.restype = ctypes.c_int global libm17n__Mcoding_utf_8 libm17n__Mcoding_utf_8 = libm17n__MSymbol.in_dll( ctypes.pythonapi, 'Mcoding_utf_8') global _utf8_converter _utf8_converter = libm17n__mconv_buffer_converter( libm17n__Mcoding_utf_8, ctypes.c_char_p(None), ctypes.c_int(0)) global libm17n__minput_get_variable libm17n__minput_get_variable = libm17n__lib.minput_get_variable libm17n__minput_get_variable.argtypes = [ libm17n__MSymbol, libm17n__MSymbol, libm17n__MSymbol] libm17n__minput_get_variable.restype = ctypes.POINTER(libm17n__MPlist) global libm17n__mplist_key libm17n__mplist_key = libm17n__lib.mplist_key libm17n__mplist_key.argtypes = [ctypes.POINTER(libm17n__MPlist)] libm17n__mplist_key.restype = libm17n__MSymbol global libm17n__mplist_value libm17n__mplist_value = libm17n__lib.mplist_value libm17n__mplist_value.argtypes = [ctypes.POINTER(libm17n__MPlist)] libm17n__mplist_value.restype = ctypes.c_void_p global libm17n__mplist_next libm17n__mplist_next = libm17n__lib.mplist_next libm17n__mplist_next.argtypes = [ctypes.POINTER(libm17n__MPlist)] libm17n__mplist_next.restype = ctypes.POINTER(libm17n__MPlist) global libm17n__msymbol_name libm17n__msymbol_name = libm17n__lib.msymbol_name libm17n__msymbol_name.argtypes = [libm17n__MSymbol] libm17n__msymbol_name.restype = ctypes.c_char_p global libm17n__mplist_add libm17n__mplist_add = libm17n__lib.mplist_add libm17n__mplist_add.argtypes = [ ctypes.POINTER(libm17n__MPlist), libm17n__MSymbol, ctypes.c_void_p] libm17n__mplist_add.restype = ctypes.POINTER(libm17n__MPlist) global libm17n__mplist_length libm17n__mplist_length = libm17n__lib.mplist_length libm17n__mplist_length.argtypes = [ctypes.POINTER(libm17n__MPlist)] libm17n__mplist_length.restype = ctypes.c_int global libm17n__mconv_decode_buffer libm17n__mconv_decode_buffer = libm17n__lib.mconv_decode_buffer libm17n__mconv_decode_buffer.argtypes = [ libm17n__MSymbol, ctypes.c_char_p, ctypes.c_int] libm17n__mconv_decode_buffer.restype = ctypes.POINTER(libm17n__MText) global libm17n__minput_config_variable libm17n__minput_config_variable = libm17n__lib.minput_config_variable libm17n__minput_config_variable.argtypes = [ libm17n__MSymbol, libm17n__MSymbol, libm17n__MSymbol, ctypes.POINTER(libm17n__MPlist)] libm17n__minput_config_variable.restype = ctypes.c_int global libm17n__minput_save_config libm17n__minput_save_config = libm17n__lib.minput_save_config libm17n__minput_save_config.argtypes = [] libm17n__minput_save_config.restype = ctypes.c_int # pylint: enable=invalid-name # pylint: enable=global-statement def fini() -> None: '''Cleanup''' libm17n__lib.m17n_fini() # type: ignore class __ModuleInitializer: # pylint: disable=too-few-public-methods,invalid-name def __init__(self) -> None: init() def __del__(self) -> None: return __module_init = __ModuleInitializer() DIGIT_TRANS_TABLE = { ord('०'): '0', # U+0966 DEVANAGARI DIGIT ZERO ord('१'): '1', # U+0967 DEVANAGARI DIGIT ONE ord('२'): '2', # U+0968 DEVANAGARI DIGIT TWO ord('३'): '3', # U+0969 DEVANAGARI DIGIT THREE ord('४'): '4', # U+096A DEVANAGARI DIGIT FOUR ord('५'): '5', # U+096B DEVANAGARI DIGIT FIVE ord('६'): '6', # U+096C DEVANAGARI DIGIT SIX ord('७'): '7', # U+096D DEVANAGARI DIGIT SEVEN ord('८'): '8', # U+096E DEVANAGARI DIGIT EIGHT ord('९'): '9', # U+096F DEVANAGARI DIGIT NINE ord('০'): '0', # U+09E6 BENGALI DIGIT ZERO ord('১'): '1', # U+09E7 BENGALI DIGIT ONE ord('২'): '2', # U+09E8 BENGALI DIGIT TWO ord('৩'): '3', # U+09E9 BENGALI DIGIT THREE ord('৪'): '4', # U+09EA BENGALI DIGIT FOUR ord('৫'): '5', # U+09EB BENGALI DIGIT FIVE ord('৬'): '6', # U+09EC BENGALI DIGIT SIX ord('৭'): '7', # U+09ED BENGALI DIGIT SEVEN ord('৮'): '8', # U+09EE BENGALI DIGIT EIGHT ord('৯'): '9', # U+09EF BENGALI DIGIT NINE ord('૦'): '0', # U+0AE6 GUJARATI DIGIT ZERO ord('૧'): '1', # U+0AE7 GUJARATI DIGIT ONE ord('૨'): '2', # U+0AE8 GUJARATI DIGIT TWO ord('૩'): '3', # U+0AE9 GUJARATI DIGIT THREE ord('૪'): '4', # U+0AEA GUJARATI DIGIT FOUR ord('૫'): '5', # U+0AEB GUJARATI DIGIT FIVE ord('૬'): '6', # U+0AEC GUJARATI DIGIT SIX ord('૭'): '7', # U+0AED GUJARATI DIGIT SEVEN ord('૮'): '8', # U+0AEE GUJARATI DIGIT EIGHT ord('૯'): '9', # U+0AEF GUJARATI DIGIT NINE ord('೦'): '0', # U+0CE6 KANNADA DIGIT ZERO ord('೧'): '1', # U+0CE7 KANNADA DIGIT ONE ord('೨'): '2', # U+0CE8 KANNADA DIGIT TWO ord('೩'): '3', # U+0CE9 KANNADA DIGIT THREE ord('೪'): '4', # U+0CEA KANNADA DIGIT FOUR ord('೫'): '5', # U+0CEB KANNADA DIGIT FIVE ord('೬'): '6', # U+0CEC KANNADA DIGIT SIX ord('೭'): '7', # U+0CED KANNADA DIGIT SEVEN ord('೮'): '8', # U+0CEE KANNADA DIGIT EIGHT ord('೯'): '9', # U+0CEF KANNADA DIGIT NINE ord('൦'): '0', # U+0D66 MALAYALAM DIGIT ZERO ord('൧'): '1', # U+0D67 MALAYALAM DIGIT ONE ord('൨'): '2', # U+0D68 MALAYALAM DIGIT TWO ord('൩'): '3', # U+0D69 MALAYALAM DIGIT THREE ord('൪'): '4', # U+0D6A MALAYALAM DIGIT FOUR ord('൫'): '5', # U+0D6B MALAYALAM DIGIT FIVE ord('൬'): '6', # U+0D6C MALAYALAM DIGIT SIX ord('൭'): '7', # U+0D6D MALAYALAM DIGIT SEVEN ord('൮'): '8', # U+0D6E MALAYALAM DIGIT EIGHT ord('൯'): '9', # U+0D6F MALAYALAM DIGIT NINE ord('୦'): '0', # U+0B66 ORIYA DIGIT ZERO ord('୧'): '1', # U+0B67 ORIYA DIGIT ONE ord('୨'): '2', # U+0B68 ORIYA DIGIT TWO ord('୩'): '3', # U+0B69 ORIYA DIGIT TWO ord('୪'): '4', # U+0B6A ORIYA DIGIT FOUR ord('୫'): '5', # U+0B6B ORIYA DIGIT FIVE ord('୬'): '6', # U+0B6C ORIYA DIGIT SIX ord('୭'): '7', # U+0B6D ORIYA DIGIT SEVEN ord('୮'): '8', # U+0B6E ORIYA DIGIT EIGHT ord('୯'): '9', # U+0B6F ORIYA DIGIT NINE ord('੦'): '0', # U+0A66 GURMUKHI DIGIT ZERO ord('੧'): '1', # U+0A67 GURMUKHI DIGIT ONE ord('੨'): '2', # U+0A68 GURMUKHI DIGIT TWO ord('੩'): '3', # U+0A69 GURMUKHI DIGIT THREE ord('੪'): '4', # U+0A6A GURMUKHI DIGIT FOUR ord('੫'): '5', # U+0A6B GURMUKHI DIGIT FIVE ord('੬'): '6', # U+0A6C GURMUKHI DIGIT SIX ord('੭'): '7', # U+0A6D GURMUKHI DIGIT SEVEN ord('੮'): '8', # U+0A6E GURMUKHI DIGIT EIGHT ord('੯'): '9', # U+0A6F GURMUKHI DIGIT NINE ord('௦'): '0', # U+0BE6 TAMIL DIGIT ZERO ord('௧'): '1', # U+0BE7 TAMIL DIGIT ONE ord('௨'): '2', # U+0BE8 TAMIL DIGIT TWO ord('௩'): '3', # U+0BE9 TAMIL DIGIT THREE ord('௪'): '4', # U+0BEA TAMIL DIGIT FOUR ord('௫'): '5', # U+0BEB TAMIL DIGIT FIVE ord('௬'): '6', # U+0BEC TAMIL DIGIT SIX ord('௭'): '7', # U+0BED TAMIL DIGIT SEVEN ord('௮'): '8', # U+0BEE TAMIL DIGIT EIGHT ord('௯'): '9', # U+0BEF TAMIL DIGIT NINE ord('౦'): '0', # U+0C66 TELUGU DIGIT ZERO ord('౧'): '1', # U+0C67 TELUGU DIGIT ONE ord('౨'): '2', # U+0C68 TELUGU DIGIT TWO ord('౩'): '3', # U+0C69 TELUGU DIGIT THREE ord('౪'): '4', # U+0C6A TELUGU DIGIT FOUR ord('౫'): '5', # U+0C6B TELUGU DIGIT FIVE ord('౬'): '6', # U+0C6C TELUGU DIGIT SIX ord('౭'): '7', # U+0C6D TELUGU DIGIT SEVEN ord('౮'): '8', # U+0C6E TELUGU DIGIT EIGHT ord('౯'): '9', # U+0C6F TELUGU DIGIT NINE ord('٠'): '0', # U+0660 ARABIC-INDIC DIGIT ZERO ord('١'): '1', # U+0661 ARABIC-INDIC DIGIT ONE ord('٢'): '0', # U+0662 ARABIC-INDIC DIGIT TWO ord('٣'): '3', # U+0663 ARABIC-INDIC DIGIT THREE ord('٤'): '4', # U+0664 ARABIC-INDIC DIGIT FOUR ord('٥'): '5', # U+0665 ARABIC-INDIC DIGIT FIVE ord('٦'): '6', # U+0666 ARABIC-INDIC DIGIT SIX ord('٧'): '7', # U+0667 ARABIC-INDIC DIGIT SEVEN ord('٨'): '8', # U+0668 ARABIC-INDIC DIGIT EIGHT ord('٩'): '9', # U+0669 ARABIC-INDIC DIGIT NINE ord('۰'): '0', # U+06F0 EXTENDED ARABIC-INDIC DIGIT ZERO ord('۱'): '1', # U+06F1 EXTENDED ARABIC-INDIC DIGIT ONE ord('۲'): '2', # U+06F2 EXTENDED ARABIC-INDIC DIGIT TWO ord('۳'): '3', # U+06F3 EXTENDED ARABIC-INDIC DIGIT THREE ord('۴'): '4', # U+06F4 EXTENDED ARABIC-INDIC DIGIT FOUR ord('۵'): '5', # U+06F5 EXTENDED ARABIC-INDIC DIGIT FIVE ord('۶'): '6', # U+06F6 EXTENDED ARABIC-INDIC DIGIT SIX ord('۷'): '7', # U+06F7 EXTENDED ARABIC-INDIC DIGIT SEVEN ord('۸'): '8', # U+06F8 EXTENDED ARABIC-INDIC DIGIT EIGHT ord('۹'): '9', # U+06F9 EXTENDED ARABIC-INDIC DIGIT NINE ord('߀'): '0', # U+07C0 NKO DIGIT ZERO ord('߁'): '1', # U+07C1 NKO DIGIT ONE ord('߂'): '2', # U+07C2 NKO DIGIT TWO ord('߃'): '3', # U+07C3 NKO DIGIT THREE ord('߄'): '4', # U+07C4 NKO DIGIT FOUR ord('߅'): '5', # U+07C5 NKO DIGIT FIVE ord('߆'): '6', # U+07C6 NKO DIGIT SIX ord('߇'): '7', # U+07C7 NKO DIGIT SEVEN ord('߈'): '8', # U+07C8 NKO DIGIT EIGHT ord('߉'): '9', # U+07C9 NKO DIGIT NINE ord('෦'): '0', # U+0DE6 SINHALA LITH DIGIT ZERO ord('෧'): '1', # U+0DE7 SINHALA LITH DIGIT ONE ord('෨'): '2', # U+0DE8 SINHALA LITH DIGIT TWO ord('෩'): '3', # U+0DE9 SINHALA LITH DIGIT THREE ord('෪'): '4', # U+0DEA SINHALA LITH DIGIT FOUR ord('෫'): '5', # U+0DEB SINHALA LITH DIGIT FIVE ord('෬'): '6', # U+0DEC SINHALA LITH DIGIT SIX ord('෭'): '7', # U+0DED SINHALA LITH DIGIT SEVEN ord('෮'): '8', # U+0DEE SINHALA LITH DIGIT EIGHT ord('෯'): '9', # U+0DEF SINHALA LITH DIGIT NINE ord('๐'): '0', # U+0E50 THAI DIGIT ZERO ord('๑'): '1', # U+0E51 THAI DIGIT ONE ord('๒'): '2', # U+0E52 THAI DIGIT TWO ord('๓'): '3', # U+0E53 THAI DIGIT THREE ord('๔'): '4', # U+0E54 THAI DIGIT FOUR ord('๕'): '5', # U+0E55 THAI DIGIT FIVE ord('๖'): '6', # U+0E56 THAI DIGIT SIX ord('๗'): '7', # U+0E57 THAI DIGIT SEVEN ord('๘'): '8', # U+0E58 THAI DIGIT EIGHT ord('๙'): '9', # U+0E59 THAI DIGIT NINE ord('໐'): '0', # U+0ED0 LAO DIGIT ZERO ord('໑'): '1', # U+0ED1 LAO DIGIT ONE ord('໒'): '2', # U+0ED2 LAO DIGIT TWO ord('໓'): '3', # U+0ED3 LAO DIGIT THREE ord('໔'): '4', # U+0ED4 LAO DIGIT FOUR ord('໕'): '5', # U+0ED5 LAO DIGIT FIVE ord('໖'): '6', # U+0ED6 LAO DIGIT SIX ord('໗'): '7', # U+0ED7 LAO DIGIT SEVEN ord('໘'): '8', # U+0ED8 LAO DIGIT EIGHT ord('໙'): '9', # U+0ED9 LAO DIGIT NINE ord('༠'): '0', # U+0F20 TIBETAN DIGIT ZERO ord('༡'): '1', # U+0F21 TIBETAN DIGIT ONE ord('༢'): '2', # U+0F22 TIBETAN DIGIT TWO ord('༣'): '3', # U+0F23 TIBETAN DIGIT THREE ord('༤'): '4', # U+0F24 TIBETAN DIGIT FOUR ord('༥'): '5', # U+0F25 TIBETAN DIGIT FIVE ord('༦'): '6', # U+0F26 TIBETAN DIGIT SIX ord('༧'): '7', # U+0F27 TIBETAN DIGIT SEVEN ord('༨'): '8', # U+0F28 TIBETAN DIGIT EIGHT ord('༩'): '9', # U+0F29 TIBETAN DIGIT NINE ord('၀'): '0', # U+1040 MYANMAR DIGIT ZERO ord('၁'): '1', # U+1041 MYANMAR DIGIT ONE ord('၂'): '2', # U+1042 MYANMAR DIGIT TWO ord('၃'): '3', # U+1043 MYANMAR DIGIT THREE ord('၄'): '4', # U+1044 MYANMAR DIGIT FOUR ord('၅'): '5', # U+1045 MYANMAR DIGIT FIVE ord('၆'): '6', # U+1046 MYANMAR DIGIT SIX ord('၇'): '7', # U+1047 MYANMAR DIGIT SEVEN ord('၈'): '8', # U+1048 MYANMAR DIGIT EIGHT ord('၉'): '9', # U+1049 MYANMAR DIGIT NINE ord('႐'): '0', # U+1090 MYANMAR SHAN DIGIT ZERO ord('႑'): '1', # U+1091 MYANMAR SHAN DIGIT ONE ord('႒'): '2', # U+1092 MYANMAR SHAN DIGIT TWO ord('႓'): '3', # U+1093 MYANMAR SHAN DIGIT THREE ord('႔'): '4', # U+1094 MYANMAR SHAN DIGIT FOUR ord('႕'): '5', # U+1095 MYANMAR SHAN DIGIT FIVE ord('႖'): '6', # U+1096 MYANMAR SHAN DIGIT SIX ord('႗'): '7', # U+1097 MYANMAR SHAN DIGIT SEVEN ord('႘'): '8', # U+1098 MYANMAR SHAN DIGIT EIGHT ord('႙'): '9', # U+1099 MYANMAR SHAN DIGIT NINE ord('០'): '0', # U+17E0 KHMER DIGIT ZERO ord('១'): '1', # U+17E1 KHMER DIGIT ONE ord('២'): '2', # U+17E2 KHMER DIGIT TWO ord('៣'): '3', # U+17E3 KHMER DIGIT THREE ord('៤'): '4', # U+17E4 KHMER DIGIT FOUR ord('៥'): '5', # U+17E5 KHMER DIGIT FIVE ord('៦'): '6', # U+17E6 KHMER DIGIT SIX ord('៧'): '7', # U+17E7 KHMER DIGIT SEVEN ord('៨'): '8', # U+17E8 KHMER DIGIT EIGHT ord('៩'): '9', # U+17E9 KHMER DIGIT NINE ord('᠐'): '0', # U+1810 MONGOLIAN DIGIT ZERO ord('᠑'): '1', # U+1811 MONGOLIAN DIGIT ONE ord('᠒'): '2', # U+1812 MONGOLIAN DIGIT TWO ord('᠓'): '3', # U+1813 MONGOLIAN DIGIT THREE ord('᠔'): '4', # U+1814 MONGOLIAN DIGIT FOUR ord('᠕'): '5', # U+1815 MONGOLIAN DIGIT FIVE ord('᠖'): '6', # U+1816 MONGOLIAN DIGIT SIX ord('᠗'): '7', # U+1817 MONGOLIAN DIGIT SEVEN ord('᠘'): '8', # U+1818 MONGOLIAN DIGIT EIGHT ord('᠙'): '9', # U+1819 MONGOLIAN DIGIT NINE ord('᥆'): '0', # U+1946 LIMBU DIGIT ZERO ord('᥇'): '1', # U+1947 LIMBU DIGIT ONE ord('᥈'): '2', # U+1948 LIMBU DIGIT TWO ord('᥉'): '3', # U+1949 LIMBU DIGIT THREE ord('᥊'): '4', # U+194A LIMBU DIGIT FOUR ord('᥋'): '5', # U+194B LIMBU DIGIT FIVE ord('᥌'): '6', # U+194C LIMBU DIGIT SIX ord('᥍'): '7', # U+194D LIMBU DIGIT SEVEN ord('᥎'): '8', # U+194E LIMBU DIGIT EIGHT ord('᥏'): '9', # U+194F LIMBU DIGIT NINE ord('᧐'): '0', # U+19D0 NEW TAI LUE DIGIT ZERO ord('᧑'): '1', # U+19D1 NEW TAI LUE DIGIT ONE ord('᧒'): '2', # U+19D2 NEW TAI LUE DIGIT TWO ord('᧓'): '3', # U+19D3 NEW TAI LUE DIGIT THREE ord('᧔'): '4', # U+19D4 NEW TAI LUE DIGIT FOUR ord('᧕'): '5', # U+19D5 NEW TAI LUE DIGIT FIVE ord('᧖'): '6', # U+19D6 NEW TAI LUE DIGIT SIX ord('᧗'): '7', # U+19D7 NEW TAI LUE DIGIT SEVEN ord('᧘'): '8', # U+19D8 NEW TAI LUE DIGIT EIGHT ord('᧙'): '9', # U+19D9 NEW TAI LUE DIGIT NINE ord('᪀'): '0', # U+1A80 TAI THAM HORA DIGIT ZERO ord('᪁'): '1', # U+1A81 TAI THAM HORA DIGIT ONE ord('᪂'): '2', # U+1A82 TAI THAM HORA DIGIT TWO ord('᪃'): '3', # U+1A83 TAI THAM HORA DIGIT THREE ord('᪄'): '4', # U+1A84 TAI THAM HORA DIGIT FOUR ord('᪅'): '5', # U+1A85 TAI THAM HORA DIGIT FIVE ord('᪆'): '6', # U+1A86 TAI THAM HORA DIGIT SIX ord('᪇'): '7', # U+1A87 TAI THAM HORA DIGIT SEVEN ord('᪈'): '8', # U+1A88 TAI THAM HORA DIGIT EIGHT ord('᪉'): '9', # U+1A89 TAI THAM HORA DIGIT NINE ord('᪐'): '0', # U+1A90 TAI THAM THAM DIGIT ZERO ord('᪑'): '1', # U+1A91 TAI THAM THAM DIGIT ONE ord('᪒'): '2', # U+1A92 TAI THAM THAM DIGIT TWO ord('᪓'): '3', # U+1A93 TAI THAM THAM DIGIT THREE ord('᪔'): '4', # U+1A94 TAI THAM THAM DIGIT FOUR ord('᪕'): '5', # U+1A95 TAI THAM THAM DIGIT FIVE ord('᪖'): '6', # U+1A96 TAI THAM THAM DIGIT SIX ord('᪗'): '7', # U+1A97 TAI THAM THAM DIGIT SEVEN ord('᪘'): '8', # U+1A98 TAI THAM THAM DIGIT EIGHT ord('᪙'): '9', # U+1A99 TAI THAM THAM DIGIT NINE ord('᭐'): '0', # U+1B50 BALINESE DIGIT ZERO ord('᭑'): '1', # U+1B51 BALINESE DIGIT ONE ord('᭒'): '2', # U+1B52 BALINESE DIGIT TWO ord('᭓'): '3', # U+1B53 BALINESE DIGIT THREE ord('᭔'): '4', # U+1B54 BALINESE DIGIT FOUR ord('᭕'): '5', # U+1B55 BALINESE DIGIT FIVE ord('᭖'): '6', # U+1B56 BALINESE DIGIT SIX ord('᭗'): '7', # U+1B57 BALINESE DIGIT SEVEN ord('᭘'): '8', # U+1B58 BALINESE DIGIT EIGHT ord('᭙'): '9', # U+1B59 BALINESE DIGIT NINE ord('᮰'): '0', # U+1BB0 SUNDANESE DIGIT ZERO ord('᮱'): '1', # U+1BB1 SUNDANESE DIGIT ONE ord('᮲'): '2', # U+1BB2 SUNDANESE DIGIT TWO ord('᮳'): '3', # U+1BB3 SUNDANESE DIGIT THREE ord('᮴'): '4', # U+1BB4 SUNDANESE DIGIT FOUR ord('᮵'): '5', # U+1BB5 SUNDANESE DIGIT FIVE ord('᮶'): '6', # U+1BB6 SUNDANESE DIGIT SIX ord('᮷'): '7', # U+1BB7 SUNDANESE DIGIT SEVEN ord('᮸'): '8', # U+1BB8 SUNDANESE DIGIT EIGHT ord('᮹'): '9', # U+1BB9 SUNDANESE DIGIT NINE ord('᱀'): '0', # U+1C40 LEPCHA DIGIT ZERO ord('᱁'): '1', # U+1C41 LEPCHA DIGIT ONE ord('᱂'): '2', # U+1C42 LEPCHA DIGIT TWO ord('᱃'): '3', # U+1C43 LEPCHA DIGIT THREE ord('᱄'): '4', # U+1C44 LEPCHA DIGIT FOUR ord('᱅'): '5', # U+1C45 LEPCHA DIGIT FIVE ord('᱆'): '6', # U+1C46 LEPCHA DIGIT SIX ord('᱇'): '7', # U+1C47 LEPCHA DIGIT SEVEN ord('᱈'): '8', # U+1C48 LEPCHA DIGIT EIGHT ord('᱉'): '9', # U+1C49 LEPCHA DIGIT NINE ord('᱐'): '0', # U+1C50 OL CHIKI DIGIT ZERO ord('᱑'): '1', # U+1C51 OL CHIKI DIGIT ONE ord('᱒'): '2', # U+1C52 OL CHIKI DIGIT TWO ord('᱓'): '3', # U+1C53 OL CHIKI DIGIT THREE ord('᱔'): '4', # U+1C54 OL CHIKI DIGIT FOUR ord('᱕'): '5', # U+1C55 OL CHIKI DIGIT FIVE ord('᱖'): '6', # U+1C56 OL CHIKI DIGIT SIX ord('᱗'): '7', # U+1C57 OL CHIKI DIGIT SEVEN ord('᱘'): '8', # U+1C58 OL CHIKI DIGIT EIGHT ord('᱙'): '9', # U+1C59 OL CHIKI DIGIT NINE ord('꘠'): '0', # U+A620 VAI DIGIT ZERO ord('꘡'): '1', # U+A621 VAI DIGIT ONE ord('꘢'): '2', # U+A622 VAI DIGIT TWO ord('꘣'): '3', # U+A623 VAI DIGIT THREE ord('꘤'): '4', # U+A624 VAI DIGIT FOUR ord('꘥'): '5', # U+A625 VAI DIGIT FIVE ord('꘦'): '6', # U+A626 VAI DIGIT SIX ord('꘧'): '7', # U+A627 VAI DIGIT SEVEN ord('꘨'): '8', # U+A628 VAI DIGIT EIGHT ord('꘩'): '9', # U+A629 VAI DIGIT NINE ord('꣐'): '0', # U+A8D0 SAURASHTRA DIGIT ZERO ord('꣑'): '1', # U+A8D1 SAURASHTRA DIGIT ONE ord('꣒'): '2', # U+A8D2 SAURASHTRA DIGIT TWO ord('꣓'): '3', # U+A8D3 SAURASHTRA DIGIT THREE ord('꣔'): '4', # U+A8D4 SAURASHTRA DIGIT FOUR ord('꣕'): '5', # U+A8D5 SAURASHTRA DIGIT FIVE ord('꣖'): '6', # U+A8D6 SAURASHTRA DIGIT SIX ord('꣗'): '7', # U+A8D7 SAURASHTRA DIGIT SEVEN ord('꣘'): '8', # U+A8D8 SAURASHTRA DIGIT EIGHT ord('꣙'): '9', # U+A8D9 SAURASHTRA DIGIT NINE ord('꤀'): '0', # U+A900 KAYAH LI DIGIT ZERO ord('꤁'): '1', # U+A901 KAYAH LI DIGIT ONE ord('꤂'): '2', # U+A902 KAYAH LI DIGIT TWO ord('꤃'): '3', # U+A903 KAYAH LI DIGIT THREE ord('꤄'): '4', # U+A904 KAYAH LI DIGIT FOUR ord('꤅'): '5', # U+A905 KAYAH LI DIGIT FIVE ord('꤆'): '6', # U+A906 KAYAH LI DIGIT SIX ord('꤇'): '7', # U+A907 KAYAH LI DIGIT SEVEN ord('꤈'): '8', # U+A908 KAYAH LI DIGIT EIGHT ord('꤉'): '9', # U+A909 KAYAH LI DIGIT NINE ord('꧐'): '0', # U+A9D0 JAVANESE DIGIT ZERO ord('꧑'): '1', # U+A9D1 JAVANESE DIGIT ONE ord('꧒'): '2', # U+A9D2 JAVANESE DIGIT TWO ord('꧓'): '3', # U+A9D3 JAVANESE DIGIT THREE ord('꧔'): '4', # U+A9D4 JAVANESE DIGIT FOUR ord('꧕'): '5', # U+A9D5 JAVANESE DIGIT FIVE ord('꧖'): '6', # U+A9D6 JAVANESE DIGIT SIX ord('꧗'): '7', # U+A9D7 JAVANESE DIGIT SEVEN ord('꧘'): '8', # U+A9D8 JAVANESE DIGIT EIGHT ord('꧙'): '9', # U+A9D9 JAVANESE DIGIT NINE ord('꧰'): '0', # U+A9F0 MYANMAR TAI LAING DIGIT ZERO ord('꧱'): '1', # U+A9F1 MYANMAR TAI LAING DIGIT ONE ord('꧲'): '2', # U+A9F2 MYANMAR TAI LAING DIGIT TWO ord('꧳'): '3', # U+A9F3 MYANMAR TAI LAING DIGIT THREE ord('꧴'): '4', # U+A9F4 MYANMAR TAI LAING DIGIT FOUR ord('꧵'): '5', # U+A9F5 MYANMAR TAI LAING DIGIT FIVE ord('꧶'): '6', # U+A9F6 MYANMAR TAI LAING DIGIT SIX ord('꧷'): '7', # U+A9F7 MYANMAR TAI LAING DIGIT SEVEN ord('꧸'): '8', # U+A9F8 MYANMAR TAI LAING DIGIT EIGHT ord('꧹'): '9', # U+A9F9 MYANMAR TAI LAING DIGIT NINE ord('꩐'): '0', # U+AA50 CHAM DIGIT ZERO ord('꩑'): '1', # U+AA51 CHAM DIGIT ONE ord('꩒'): '2', # U+AA52 CHAM DIGIT TWO ord('꩓'): '3', # U+AA53 CHAM DIGIT THREE ord('꩔'): '4', # U+AA54 CHAM DIGIT FOUR ord('꩕'): '5', # U+AA55 CHAM DIGIT FIVE ord('꩖'): '6', # U+AA56 CHAM DIGIT SIX ord('꩗'): '7', # U+AA57 CHAM DIGIT SEVEN ord('꩘'): '8', # U+AA58 CHAM DIGIT EIGHT ord('꩙'): '9', # U+AA59 CHAM DIGIT NINE ord('꯰'): '0', # U+ABF0 MEETEI MAYEK DIGIT ZERO ord('꯱'): '1', # U+ABF1 MEETEI MAYEK DIGIT ONE ord('꯲'): '2', # U+ABF2 MEETEI MAYEK DIGIT TWO ord('꯳'): '3', # U+ABF3 MEETEI MAYEK DIGIT THREE ord('꯴'): '4', # U+ABF4 MEETEI MAYEK DIGIT FOUR ord('꯵'): '5', # U+ABF5 MEETEI MAYEK DIGIT FIVE ord('꯶'): '6', # U+ABF6 MEETEI MAYEK DIGIT SIX ord('꯷'): '7', # U+ABF7 MEETEI MAYEK DIGIT SEVEN ord('꯸'): '8', # U+ABF8 MEETEI MAYEK DIGIT EIGHT ord('꯹'): '9', # U+ABF9 MEETEI MAYEK DIGIT NINE ord('𐒠'): '0', # U+104A0 OSMANYA DIGIT ZERO ord('𐒡'): '1', # U+104A1 OSMANYA DIGIT ONE ord('𐒢'): '2', # U+104A2 OSMANYA DIGIT TWO ord('𐒣'): '3', # U+104A3 OSMANYA DIGIT THREE ord('𐒤'): '4', # U+104A4 OSMANYA DIGIT FOUR ord('𐒥'): '5', # U+104A5 OSMANYA DIGIT FIVE ord('𐒦'): '6', # U+104A6 OSMANYA DIGIT SIX ord('𐒧'): '7', # U+104A7 OSMANYA DIGIT SEVEN ord('𐒨'): '8', # U+104A8 OSMANYA DIGIT EIGHT ord('𐒩'): '9', # U+104A9 OSMANYA DIGIT NINE ord('𐴰'): '0', # U+10D30 HANIFI ROHINGYA DIGIT ZERO ord('𐴱'): '1', # U+10D31 HANIFI ROHINGYA DIGIT ONE ord('𐴲'): '2', # U+10D32 HANIFI ROHINGYA DIGIT TWO ord('𐴳'): '3', # U+10D33 HANIFI ROHINGYA DIGIT THREE ord('𐴴'): '4', # U+10D34 HANIFI ROHINGYA DIGIT FOUR ord('𐴵'): '5', # U+10D35 HANIFI ROHINGYA DIGIT FIVE ord('𐴶'): '6', # U+10D36 HANIFI ROHINGYA DIGIT SIX ord('𐴷'): '7', # U+10D37 HANIFI ROHINGYA DIGIT SEVEN ord('𐴸'): '8', # U+10D38 HANIFI ROHINGYA DIGIT EIGHT ord('𐴹'): '9', # U+10D39 HANIFI ROHINGYA DIGIT NINE # ord('𐹠'): '0', # U+10E60 RUMI DIGIT ONE # ord('𐹡'): '1', # U+10E61 RUMI DIGIT TWO # ord('𐹢'): '2', # U+10E62 RUMI DIGIT THREE # ord('𐹣'): '3', # U+10E63 RUMI DIGIT FOUR # ord('𐹤'): '4', # U+10E64 RUMI DIGIT FIVE # ord('𐹥'): '5', # U+10E65 RUMI DIGIT SIX # ord('𐹦'): '6', # U+10E66 RUMI DIGIT SEVEN # ord('𐹧'): '7', # U+10E67 RUMI DIGIT EIGHT # ord('𐹨'): '8', # U+10E68 RUMI DIGIT NINE ord('𑁦'): '6', # U+11066 BRAHMI DIGIT ZERO ord('𑁧'): '7', # U+11067 BRAHMI DIGIT ONE ord('𑁨'): '8', # U+11068 BRAHMI DIGIT TWO ord('𑁩'): '9', # U+11069 BRAHMI DIGIT THREE ord('𑁪'): 'A', # U+1106A BRAHMI DIGIT FOUR ord('𑁫'): 'B', # U+1106B BRAHMI DIGIT FIVE ord('𑁬'): 'C', # U+1106C BRAHMI DIGIT SIX ord('𑁭'): 'D', # U+1106D BRAHMI DIGIT SEVEN ord('𑁮'): 'E', # U+1106E BRAHMI DIGIT EIGHT ord('𑁯'): 'F', # U+1106F BRAHMI DIGIT NINE ord('𑃰'): '0', # U+110F0 SORA SOMPENG DIGIT ZERO ord('𑃱'): '1', # U+110F1 SORA SOMPENG DIGIT ONE ord('𑃲'): '2', # U+110F2 SORA SOMPENG DIGIT TWO ord('𑃳'): '3', # U+110F3 SORA SOMPENG DIGIT THREE ord('𑃴'): '4', # U+110F4 SORA SOMPENG DIGIT FOUR ord('𑃵'): '5', # U+110F5 SORA SOMPENG DIGIT FIVE ord('𑃶'): '6', # U+110F6 SORA SOMPENG DIGIT SIX ord('𑃷'): '7', # U+110F7 SORA SOMPENG DIGIT SEVEN ord('𑃸'): '8', # U+110F8 SORA SOMPENG DIGIT EIGHT ord('𑃹'): '9', # U+110F9 SORA SOMPENG DIGIT NINE ord('𑄶'): '6', # U+11136 CHAKMA DIGIT ZERO ord('𑄷'): '7', # U+11137 CHAKMA DIGIT ONE ord('𑄸'): '8', # U+11138 CHAKMA DIGIT TWO ord('𑄹'): '9', # U+11139 CHAKMA DIGIT THREE ord('𑄺'): 'A', # U+1113A CHAKMA DIGIT FOUR ord('𑄻'): 'B', # U+1113B CHAKMA DIGIT FIVE ord('𑄼'): 'C', # U+1113C CHAKMA DIGIT SIX ord('𑄽'): 'D', # U+1113D CHAKMA DIGIT SEVEN ord('𑄾'): 'E', # U+1113E CHAKMA DIGIT EIGHT ord('𑄿'): 'F', # U+1113F CHAKMA DIGIT NINE ord('𑇐'): '0', # U+111D0 SHARADA DIGIT ZERO ord('𑇑'): '1', # U+111D1 SHARADA DIGIT ONE ord('𑇒'): '2', # U+111D2 SHARADA DIGIT TWO ord('𑇓'): '3', # U+111D3 SHARADA DIGIT THREE ord('𑇔'): '4', # U+111D4 SHARADA DIGIT FOUR ord('𑇕'): '5', # U+111D5 SHARADA DIGIT FIVE ord('𑇖'): '6', # U+111D6 SHARADA DIGIT SIX ord('𑇗'): '7', # U+111D7 SHARADA DIGIT SEVEN ord('𑇘'): '8', # U+111D8 SHARADA DIGIT EIGHT ord('𑇙'): '9', # U+111D9 SHARADA DIGIT NINE ord('𑋰'): '0', # U+112F0 KHUDAWADI DIGIT ZERO ord('𑋱'): '1', # U+112F1 KHUDAWADI DIGIT ONE ord('𑋲'): '2', # U+112F2 KHUDAWADI DIGIT TWO ord('𑋳'): '3', # U+112F3 KHUDAWADI DIGIT THREE ord('𑋴'): '4', # U+112F4 KHUDAWADI DIGIT FOUR ord('𑋵'): '5', # U+112F5 KHUDAWADI DIGIT FIVE ord('𑋶'): '6', # U+112F6 KHUDAWADI DIGIT SIX ord('𑋷'): '7', # U+112F7 KHUDAWADI DIGIT SEVEN ord('𑋸'): '8', # U+112F8 KHUDAWADI DIGIT EIGHT ord('𑋹'): '9', # U+112F9 KHUDAWADI DIGIT NINE ord('𑑐'): '0', # U+11450 NEWA DIGIT ZERO ord('𑑑'): '1', # U+11451 NEWA DIGIT ONE ord('𑑒'): '2', # U+11452 NEWA DIGIT TWO ord('𑑓'): '3', # U+11453 NEWA DIGIT THREE ord('𑑔'): '4', # U+11454 NEWA DIGIT FOUR ord('𑑕'): '5', # U+11455 NEWA DIGIT FIVE ord('𑑖'): '6', # U+11456 NEWA DIGIT SIX ord('𑑗'): '7', # U+11457 NEWA DIGIT SEVEN ord('𑑘'): '8', # U+11458 NEWA DIGIT EIGHT ord('𑑙'): '9', # U+11459 NEWA DIGIT NINE ord('𑓐'): '0', # U+114D0 TIRHUTA DIGIT ZERO ord('𑓑'): '1', # U+114D1 TIRHUTA DIGIT ONE ord('𑓒'): '2', # U+114D2 TIRHUTA DIGIT TWO ord('𑓓'): '3', # U+114D3 TIRHUTA DIGIT THREE ord('𑓔'): '4', # U+114D4 TIRHUTA DIGIT FOUR ord('𑓕'): '5', # U+114D5 TIRHUTA DIGIT FIVE ord('𑓖'): '6', # U+114D6 TIRHUTA DIGIT SIX ord('𑓗'): '7', # U+114D7 TIRHUTA DIGIT SEVEN ord('𑓘'): '8', # U+114D8 TIRHUTA DIGIT EIGHT ord('𑓙'): '9', # U+114D9 TIRHUTA DIGIT NINE ord('𑙐'): '0', # U+11650 MODI DIGIT ZERO ord('𑙑'): '1', # U+11651 MODI DIGIT ONE ord('𑙒'): '2', # U+11652 MODI DIGIT TWO ord('𑙓'): '3', # U+11653 MODI DIGIT THREE ord('𑙔'): '4', # U+11654 MODI DIGIT FOUR ord('𑙕'): '5', # U+11655 MODI DIGIT FIVE ord('𑙖'): '6', # U+11656 MODI DIGIT SIX ord('𑙗'): '7', # U+11657 MODI DIGIT SEVEN ord('𑙘'): '8', # U+11658 MODI DIGIT EIGHT ord('𑙙'): '9', # U+11659 MODI DIGIT NINE ord('𑛀'): '0', # U+116C0 TAKRI DIGIT ZERO ord('𑛁'): '1', # U+116C1 TAKRI DIGIT ONE ord('𑛂'): '2', # U+116C2 TAKRI DIGIT TWO ord('𑛃'): '3', # U+116C3 TAKRI DIGIT THREE ord('𑛄'): '4', # U+116C4 TAKRI DIGIT FOUR ord('𑛅'): '5', # U+116C5 TAKRI DIGIT FIVE ord('𑛆'): '6', # U+116C6 TAKRI DIGIT SIX ord('𑛇'): '7', # U+116C7 TAKRI DIGIT SEVEN ord('𑛈'): '8', # U+116C8 TAKRI DIGIT EIGHT ord('𑛉'): '9', # U+116C9 TAKRI DIGIT NINE ord('𑜰'): '0', # U+11730 AHOM DIGIT ZERO ord('𑜱'): '1', # U+11731 AHOM DIGIT ONE ord('𑜲'): '2', # U+11732 AHOM DIGIT TWO ord('𑜳'): '3', # U+11733 AHOM DIGIT THREE ord('𑜴'): '4', # U+11734 AHOM DIGIT FOUR ord('𑜵'): '5', # U+11735 AHOM DIGIT FIVE ord('𑜶'): '6', # U+11736 AHOM DIGIT SIX ord('𑜷'): '7', # U+11737 AHOM DIGIT SEVEN ord('𑜸'): '8', # U+11738 AHOM DIGIT EIGHT ord('𑜹'): '9', # U+11739 AHOM DIGIT NINE ord('𑣠'): '0', # U+118E0 WARANG CITI DIGIT ZERO ord('𑣡'): '1', # U+118E1 WARANG CITI DIGIT ONE ord('𑣢'): '2', # U+118E2 WARANG CITI DIGIT TWO ord('𑣣'): '3', # U+118E3 WARANG CITI DIGIT THREE ord('𑣤'): '4', # U+118E4 WARANG CITI DIGIT FOUR ord('𑣥'): '5', # U+118E5 WARANG CITI DIGIT FIVE ord('𑣦'): '6', # U+118E6 WARANG CITI DIGIT SIX ord('𑣧'): '7', # U+118E7 WARANG CITI DIGIT SEVEN ord('𑣨'): '8', # U+118E8 WARANG CITI DIGIT EIGHT ord('𑣩'): '9', # U+118E9 WARANG CITI DIGIT NINE ord('𑥐'): '0', # U+11950 DIVES AKURU DIGIT ZERO ord('𑥑'): '1', # U+11951 DIVES AKURU DIGIT ONE ord('𑥒'): '2', # U+11952 DIVES AKURU DIGIT TWO ord('𑥓'): '3', # U+11953 DIVES AKURU DIGIT THREE ord('𑥔'): '4', # U+11954 DIVES AKURU DIGIT FOUR ord('𑥕'): '5', # U+11955 DIVES AKURU DIGIT FIVE ord('𑥖'): '6', # U+11956 DIVES AKURU DIGIT SIX ord('𑥗'): '7', # U+11957 DIVES AKURU DIGIT SEVEN ord('𑥘'): '8', # U+11958 DIVES AKURU DIGIT EIGHT ord('𑥙'): '9', # U+11959 DIVES AKURU DIGIT NINE ord('𑱐'): '0', # U+11C50 BHAIKSUKI DIGIT ZERO ord('𑱑'): '1', # U+11C51 BHAIKSUKI DIGIT ONE ord('𑱒'): '2', # U+11C52 BHAIKSUKI DIGIT TWO ord('𑱓'): '3', # U+11C53 BHAIKSUKI DIGIT THREE ord('𑱔'): '4', # U+11C54 BHAIKSUKI DIGIT FOUR ord('𑱕'): '5', # U+11C55 BHAIKSUKI DIGIT FIVE ord('𑱖'): '6', # U+11C56 BHAIKSUKI DIGIT SIX ord('𑱗'): '7', # U+11C57 BHAIKSUKI DIGIT SEVEN ord('𑱘'): '8', # U+11C58 BHAIKSUKI DIGIT EIGHT ord('𑱙'): '9', # U+11C59 BHAIKSUKI DIGIT NINE ord('𑵐'): '0', # U+11D50 MASARAM GONDI DIGIT ZERO ord('𑵑'): '1', # U+11D51 MASARAM GONDI DIGIT ONE ord('𑵒'): '2', # U+11D52 MASARAM GONDI DIGIT TWO ord('𑵓'): '3', # U+11D53 MASARAM GONDI DIGIT THREE ord('𑵔'): '4', # U+11D54 MASARAM GONDI DIGIT FOUR ord('𑵕'): '5', # U+11D55 MASARAM GONDI DIGIT FIVE ord('𑵖'): '6', # U+11D56 MASARAM GONDI DIGIT SIX ord('𑵗'): '7', # U+11D57 MASARAM GONDI DIGIT SEVEN ord('𑵘'): '8', # U+11D58 MASARAM GONDI DIGIT EIGHT ord('𑵙'): '9', # U+11D59 MASARAM GONDI DIGIT NINE ord('𑶠'): '0', # U+11DA0 GUNJALA GONDI DIGIT ZERO ord('𑶡'): '1', # U+11DA1 GUNJALA GONDI DIGIT ONE ord('𑶢'): '2', # U+11DA2 GUNJALA GONDI DIGIT TWO ord('𑶣'): '3', # U+11DA3 GUNJALA GONDI DIGIT THREE ord('𑶤'): '4', # U+11DA4 GUNJALA GONDI DIGIT FOUR ord('𑶥'): '5', # U+11DA5 GUNJALA GONDI DIGIT FIVE ord('𑶦'): '6', # U+11DA6 GUNJALA GONDI DIGIT SIX ord('𑶧'): '7', # U+11DA7 GUNJALA GONDI DIGIT SEVEN ord('𑶨'): '8', # U+11DA8 GUNJALA GONDI DIGIT EIGHT ord('𑶩'): '9', # U+11DA9 GUNJALA GONDI DIGIT NINE ord('𑽐'): '0', # U+11F50 KAWI DIGIT ZERO ord('𑽑'): '1', # U+11F51 KAWI DIGIT ONE ord('𑽒'): '2', # U+11F52 KAWI DIGIT TWO ord('𑽓'): '3', # U+11F53 KAWI DIGIT THREE ord('𑽔'): '4', # U+11F54 KAWI DIGIT FOUR ord('𑽕'): '5', # U+11F55 KAWI DIGIT FIVE ord('𑽖'): '6', # U+11F56 KAWI DIGIT SIX ord('𑽗'): '7', # U+11F57 KAWI DIGIT SEVEN ord('𑽘'): '8', # U+11F58 KAWI DIGIT EIGHT ord('𑽙'): '9', # U+11F59 KAWI DIGIT NINE ord('𖩠'): '0', # U+16A60 MRO DIGIT ZERO ord('𖩡'): '1', # U+16A61 MRO DIGIT ONE ord('𖩢'): '2', # U+16A62 MRO DIGIT TWO ord('𖩣'): '3', # U+16A63 MRO DIGIT THREE ord('𖩤'): '4', # U+16A64 MRO DIGIT FOUR ord('𖩥'): '5', # U+16A65 MRO DIGIT FIVE ord('𖩦'): '6', # U+16A66 MRO DIGIT SIX ord('𖩧'): '7', # U+16A67 MRO DIGIT SEVEN ord('𖩨'): '8', # U+16A68 MRO DIGIT EIGHT ord('𖩩'): '9', # U+16A69 MRO DIGIT NINE ord('𖫀'): '0', # U+16AC0 TANGSA DIGIT ZERO ord('𖫁'): '1', # U+16AC1 TANGSA DIGIT ONE ord('𖫂'): '2', # U+16AC2 TANGSA DIGIT TWO ord('𖫃'): '3', # U+16AC3 TANGSA DIGIT THREE ord('𖫄'): '4', # U+16AC4 TANGSA DIGIT FOUR ord('𖫅'): '5', # U+16AC5 TANGSA DIGIT FIVE ord('𖫆'): '6', # U+16AC6 TANGSA DIGIT SIX ord('𖫇'): '7', # U+16AC7 TANGSA DIGIT SEVEN ord('𖫈'): '8', # U+16AC8 TANGSA DIGIT EIGHT ord('𖫉'): '9', # U+16AC9 TANGSA DIGIT NINE ord('𖭐'): '0', # U+16B50 PAHAWH HMONG DIGIT ZERO ord('𖭑'): '1', # U+16B51 PAHAWH HMONG DIGIT ONE ord('𖭒'): '2', # U+16B52 PAHAWH HMONG DIGIT TWO ord('𖭓'): '3', # U+16B53 PAHAWH HMONG DIGIT THREE ord('𖭔'): '4', # U+16B54 PAHAWH HMONG DIGIT FOUR ord('𖭕'): '5', # U+16B55 PAHAWH HMONG DIGIT FIVE ord('𖭖'): '6', # U+16B56 PAHAWH HMONG DIGIT SIX ord('𖭗'): '7', # U+16B57 PAHAWH HMONG DIGIT SEVEN ord('𖭘'): '8', # U+16B58 PAHAWH HMONG DIGIT EIGHT ord('𖭙'): '9', # U+16B59 PAHAWH HMONG DIGIT NINE ord('𞅀'): '0', # U+1E140 NYIAKENG PUACHUE HMONG DIGIT ZERO ord('𞅁'): '1', # U+1E141 NYIAKENG PUACHUE HMONG DIGIT ONE ord('𞅂'): '2', # U+1E142 NYIAKENG PUACHUE HMONG DIGIT TWO ord('𞅃'): '3', # U+1E143 NYIAKENG PUACHUE HMONG DIGIT THREE ord('𞅄'): '4', # U+1E144 NYIAKENG PUACHUE HMONG DIGIT FOUR ord('𞅅'): '5', # U+1E145 NYIAKENG PUACHUE HMONG DIGIT FIVE ord('𞅆'): '6', # U+1E146 NYIAKENG PUACHUE HMONG DIGIT SIX ord('𞅇'): '7', # U+1E147 NYIAKENG PUACHUE HMONG DIGIT SEVEN ord('𞅈'): '8', # U+1E148 NYIAKENG PUACHUE HMONG DIGIT EIGHT ord('𞅉'): '9', # U+1E149 NYIAKENG PUACHUE HMONG DIGIT NINE ord('𞋰'): '0', # U+1E2F0 WANCHO DIGIT ZERO ord('𞋱'): '1', # U+1E2F1 WANCHO DIGIT ONE ord('𞋲'): '2', # U+1E2F2 WANCHO DIGIT TWO ord('𞋳'): '3', # U+1E2F3 WANCHO DIGIT THREE ord('𞋴'): '4', # U+1E2F4 WANCHO DIGIT FOUR ord('𞋵'): '5', # U+1E2F5 WANCHO DIGIT FIVE ord('𞋶'): '6', # U+1E2F6 WANCHO DIGIT SIX ord('𞋷'): '7', # U+1E2F7 WANCHO DIGIT SEVEN ord('𞋸'): '8', # U+1E2F8 WANCHO DIGIT EIGHT ord('𞋹'): '9', # U+1E2F9 WANCHO DIGIT NINE ord('𞓰'): '0', # U+1E4F0 NAG MUNDARI DIGIT ZERO ord('𞓱'): '1', # U+1E4F1 NAG MUNDARI DIGIT ONE ord('𞓲'): '2', # U+1E4F2 NAG MUNDARI DIGIT TWO ord('𞓳'): '3', # U+1E4F3 NAG MUNDARI DIGIT THREE ord('𞓴'): '4', # U+1E4F4 NAG MUNDARI DIGIT FOUR ord('𞓵'): '5', # U+1E4F5 NAG MUNDARI DIGIT FIVE ord('𞓶'): '6', # U+1E4F6 NAG MUNDARI DIGIT SIX ord('𞓷'): '7', # U+1E4F7 NAG MUNDARI DIGIT SEVEN ord('𞓸'): '8', # U+1E4F8 NAG MUNDARI DIGIT EIGHT ord('𞓹'): '9', # U+1E4F9 NAG MUNDARI DIGIT NINE ord('𞥐'): '0', # U+1E950 ADLAM DIGIT ZERO ord('𞥑'): '1', # U+1E951 ADLAM DIGIT ONE ord('𞥒'): '2', # U+1E952 ADLAM DIGIT TWO ord('𞥓'): '3', # U+1E953 ADLAM DIGIT THREE ord('𞥔'): '4', # U+1E954 ADLAM DIGIT FOUR ord('𞥕'): '5', # U+1E955 ADLAM DIGIT FIVE ord('𞥖'): '6', # U+1E956 ADLAM DIGIT SIX ord('𞥗'): '7', # U+1E957 ADLAM DIGIT SEVEN ord('𞥘'): '8', # U+1E958 ADLAM DIGIT EIGHT ord('𞥙'): '9', # U+1E959 ADLAM DIGIT NINE } def convert_digits_to_ascii(text: str) -> str: ''' Convert language specific digits in a text to ASCII digits :param text: The text to convert :return: The converted text containing only ASCII digits Examples: >>> convert_digits_to_ascii('hello ०१२३४५६७८९') # Devanagari 'hello 0123456789' >>> convert_digits_to_ascii('hello ౦౧౨౩౪౫౬౭౮౯') # Telugu 'hello 0123456789' >>> convert_digits_to_ascii('hello 𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙') # Adlam 'hello 0123456789' ''' return text.translate(DIGIT_TRANS_TABLE) class TransliterationParts(NamedTuple): ''' A named tuple containing the parts of a transliteration committed: str The part of the transliteration which cannot be changed anymore by adding more input, could be committed already if desired. committed_index: int The index up to which the msymbol_list input was “used up” to create the “committed” text. preedit: The transliteration of the remaining input, may still change by adding more input. cursor_pos: int The cursor position in the preedit. Counted in codepoints, not glyphs (For example '☺\uFE0F' usually renders as one glyph (☺️) but has two codepoints). Usually this is at the end of the preedit but not always! An input method may move the cursor within the preedit! (I think only ja-anthy.mim actually moves the cursor within the preedit sometimes when moving or resizing the henkan region). status: str May change for some input methods to indicate a state. For example in case of ja-anthy.mim, this is 'aあ' before Henkan and changes to '漢' in Henkan mode. candidates: List[str] May contain a list of candidates if the input method can produce multiple candidates. candidate_index: int Index of the selected candidate. 0 if no candidate is selected. candidate_from: int Start of the current candidate in the preedit. candidate_to: int End of the current candidate in the preedit. If candidate_index == 0, i.e. no candidate is selected, the current candidate in the preedit is the first candidate. candidate_show: int 0: candidates should be hidden 1: candidates should be shown ''' committed: str = '' committed_index: int = 0 preedit: str = '' cursor_pos: int = 0 status: str = '' candidates: List[str] = [] candidate_index: int = 0 candidate_from: int = 0 candidate_to: int = 0 candidate_show: int = 0 class Transliterator: '''A class for transliterators using libm17n If initializing the transliterator fails, for example because a non-existing input method was given as the argument, a ValueError is raised. ''' def __init__(self, ime: str) -> None: '''Initialize the input method to use for the transliteration Raises ValueError if something fails. :param ime: Full name of the m17n input method, for example “hi-inscript2” or “t-latn-post”. There is one special input method name “NoIME”. The input method “NoIME” is just a dummy which does not do transliteration at all, it only joins the list of Msymbol names to a string. ''' self._dummy = False if ime == 'NoIME': self._dummy = True return self._language = ime.split('-')[0] self._name = '-'.join(ime.split('-')[1:]) self._msymbol_single_char_with_prefix_pattern = re.compile( r'^(?P([SCMAGsH]-)*)(?P.)$') self._im = libm17n__minput_open_im( # type: ignore libm17n__msymbol(ctypes.c_char_p(self._language.encode('utf-8'))), # type: ignore libm17n__msymbol(ctypes.c_char_p(self._name.encode('utf-8'))), # type: ignore ctypes.c_void_p(None)) try: _im_contents = self._im.contents except ValueError as error: # NULL pointer access raise ValueError('minput_open_im() failed') from error self._ic = libm17n__minput_create_ic(self._im, ctypes.c_void_p(None)) # type: ignore try: _ic_contents = self._ic.contents except ValueError as error: # NULL pointer access raise ValueError('minput_create_ic() failed') from error def _convert_non_ascii_msymbol(self, msymbol: str) -> str: # Python >= 3.7 has a str.isascii(), I could use that instead # of my own is_ascii(), but that does not work on older # distributions like openSUSE 15.6. if itb_util.is_ascii(msymbol): return msymbol match = re.search( self._msymbol_single_char_with_prefix_pattern, msymbol) if not match: return msymbol name = IBus.keyval_name(IBus.unicode_to_keyval(match.group('unicode'))) return f'{match.group("prefix")}{name}' def reset_ic(self) -> None: '''Resets the input context Can be used to reset the internal state of the input method to the default. For example, if zh-py is in fullwidth-mode (to input fullwidth Latin), calling reset_ic() switches to the default mode to input Chinese characters. ''' if self._dummy: return libm17n__minput_reset_ic(self._ic) # type: ignore # From the m17n-lib documentation: # # The minput_reset_ic () function resets input context $IC by # calling a callback function corresponding to @b # Minput_reset. It resets the status of $IC to its initial # one. As the current preedit text is deleted without # commitment, if necessary, call minput_filter () with the arg # @b key #Mnil to force the input method to commit the preedit # in advance. # # Looks like we need to do this here, i.e. call minput_filter # with a final 'nil' msymbol to commit the preedit in the # input context to make the next call to minput_reset_ic() # work reliably. Without that minput_reset_ic() sometimes # segfaults. _symbol = libm17n__msymbol(b'nil') # type: ignore _retval = libm17n__minput_filter( # type: ignore self._ic, _symbol, ctypes.c_void_p(None)) def transliterate_parts( self, msymbol_list: Iterable[str], ascii_digits: bool = False, reset: bool = False) -> TransliterationParts: # pylint: disable=line-too-long '''Transliterate a list of Msymbol names :param msymbol_list: A list of strings which are interpreted as the names of Msymbols to transliterate. If the input method has the special name “NoIME”, no transliteration is done, the list of Msymbols is just joined to a single string. :param ascii_digits: If true, convert language specific digits to ASCII digits :return: The transliteration in several parts Examples: >>> trans = Transliterator('hi-itrans') >>> parts = trans.transliterate_parts(list('n')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit 'न्' >>> parts.cursor_pos 2 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('n ')) >>> parts.committed 'न ' >>> parts.committed_index 2 >>> parts.preedit '' >>> parts.cursor_pos 0 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts =trans.transliterate_parts(list('na')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit 'न' >>> parts.cursor_pos 1 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('nam')) >>> parts.committed 'न' >>> parts.committed_index 2 >>> parts.preedit 'म्' >>> parts.cursor_pos 2 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('nama')) >>> parts.committed 'न' >>> parts.committed_index 2 >>> parts.preedit 'म' >>> parts.cursor_pos 1 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('namas')) >>> parts.committed 'नम' >>> parts.committed_index 4 >>> parts.preedit 'स्' >>> parts.cursor_pos 2 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('namast')) >>> parts.committed 'नम' >>> parts.committed_index 4 >>> parts.preedit 'स्त्' >>> parts.cursor_pos 4 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('namaste')) >>> parts.committed 'नम' >>> parts.committed_index 4 >>> parts.preedit 'स्ते' >>> parts.cursor_pos 4 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('namaste ')) >>> parts.committed 'नमस्ते ' >>> parts.committed_index 8 >>> parts.preedit '' >>> parts.cursor_pos 0 >>> parts.status 'क' >>> parts.candidates [] >>> parts.candidate_show 0 >>> trans = Transliterator('t-latn-post') >>> parts = trans.transliterate_parts(list('u')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit 'u' >>> parts.cursor_pos 1 >>> parts.status 'Latin-post' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('u"')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit 'ü' >>> parts.cursor_pos 1 >>> parts.status 'Latin-post' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('u""')) >>> parts.committed 'u"' >>> parts.committed_index 3 >>> parts.preedit '' >>> parts.cursor_pos 0 >>> parts.status 'Latin-post' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('u"u')) >>> parts.committed 'ü' >>> parts.committed_index 2 >>> parts.preedit 'u' >>> parts.cursor_pos 1 >>> parts.status 'Latin-post' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('üu"u')) >>> parts.committed 'üü' >>> parts.committed_index 3 >>> parts.preedit 'u' >>> parts.cursor_pos 1 >>> parts.status 'Latin-post' >>> parts.candidates [] >>> parts.candidate_show 0 >>> trans = Transliterator('t-rfc1345') >>> parts = trans.transliterate_parts(list('&')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit '&' >>> parts.cursor_pos 1 >>> parts.status 'RFC1345' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('&C')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit '&C' >>> parts.cursor_pos 2 >>> parts.status 'RFC1345' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('&Co')) >>> parts.committed '©' >>> parts.committed_index 3 >>> parts.preedit '' >>> parts.cursor_pos 0 >>> parts.status 'RFC1345' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('&f')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit '&f' >>> parts.cursor_pos 2 >>> parts.status 'RFC1345' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('&ff')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit 'ff' >>> parts.cursor_pos 1 >>> parts.status 'RFC1345' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('&ffi')) >>> parts.committed 'ffi' >>> parts.committed_index 4 >>> parts.preedit '' >>> parts.cursor_pos 0 >>> parts.status 'RFC1345' >>> parts.candidates [] >>> parts.candidate_show 0 >>> trans = Transliterator('t-lsymbol') >>> parts = trans.transliterate_parts(list('/:)')) >>> parts.committed '' >>> parts.committed_index 0 >>> parts.preedit '☺️' >>> parts.cursor_pos 2 >>> parts.status 'lsymbol' >>> parts.candidates ['☺️', '😃', '😅', '😆', '😉', '😇', '😂', '😏', '😛', '😜', '😝', '😋', '😉', '💏', '💋', '😍', '😘', '😚', '😽', '😻'] >>> parts.candidate_show 1 >>> parts = trans.transliterate_parts(list('a')) >>> parts.committed 'a' >>> parts.committed_index 1 >>> parts.preedit '' >>> parts.cursor_pos 0 >>> parts.status 'lsymbol' >>> parts.candidates [] >>> parts.candidate_show 0 >>> parts = trans.transliterate_parts(list('a/')) >>> parts.committed 'a' >>> parts.committed_index 1 >>> parts.preedit '/' >>> parts.cursor_pos 1 >>> parts.status 'lsymbol' >>> parts.candidates ['/'] >>> parts.candidate_show 1 >>> parts = trans.transliterate_parts(list('a/:')) >>> parts.committed 'a' >>> parts.committed_index 1 >>> parts.preedit '/:' >>> parts.cursor_pos 2 >>> parts.status 'lsymbol' >>> parts.candidates ['/:'] >>> parts.candidate_show 1 >>> parts = trans.transliterate_parts(list('a/:(')) >>> parts.committed 'a' >>> parts.committed_index 1 >>> parts.preedit '😢' >>> parts.cursor_pos 1 >>> parts.status 'lsymbol' >>> parts.candidates ['😢', '😩', '😡', '😭', '😪', '🙈', '🙊', '🙉'] >>> parts.candidate_show 1 >>> parts = trans.transliterate_parts(list('a/:(b')) >>> parts.committed 'a😢b' >>> parts.committed_index 5 >>> parts.preedit '' >>> parts.cursor_pos 0 >>> parts.status 'lsymbol' >>> parts.candidates [] >>> parts.candidate_show 0 For a test transliterating parts using 'ja-anthy' see 'tests/test_m17n_translit.py'. ''' # pylint: enable=line-too-long if not isinstance(msymbol_list, list): raise ValueError('Argument of transliterate() must be a list.') if self._dummy: return TransliterationParts(committed=''.join(msymbol_list), committed_index=len(msymbol_list)) if reset: libm17n__minput_reset_ic(self._ic) # type: ignore committed = '' committed_index = 0 preedit = '' candidates: List[str] = [] for index, symbol in enumerate(msymbol_list): symbol = self._convert_non_ascii_msymbol(symbol) _symbol = libm17n__msymbol(symbol.encode('utf-8')) # type: ignore retval = libm17n__minput_filter( # type: ignore self._ic, _symbol, ctypes.c_void_p(None)) if retval == 0: _mt = libm17n__mtext() # type: ignore retval = libm17n__minput_lookup( # type: ignore self._ic, _symbol, ctypes.c_void_p(None), _mt) if libm17n__mtext_len(_mt) > 0: # type: ignore committed += mtext_to_string(_mt) committed_index = index if retval: committed += msymbol_list[index] committed_index = index + 1 try: if (self._ic.contents.preedit_changed and libm17n__mtext_len( self._ic.contents.preedit) > 0): # type: ignore preedit = mtext_to_string(self._ic.contents.preedit) except Exception as error: # pylint: disable=broad-except # This should never happen: raise ValueError('Problem accessing preedit') from error plist = self._ic.contents.candidate_list while bool(plist): # NULL pointers have a False boolean value key = libm17n__mplist_key(plist) # type: ignore if not bool(key): break key_name = libm17n__msymbol_name(key.contents) # type: ignore if key_name == b'mtext': characters = mtext_to_string( ctypes.cast(libm17n__mplist_value(plist), # type: ignore ctypes.POINTER(libm17n__MText))) candidates += list(characters) elif key_name == b'plist': candidate_plist = ctypes.cast( libm17n__mplist_value(plist), # type: ignore ctypes.POINTER(libm17n__MPlist)) while True: candidate_plist_key = libm17n__mplist_key( # type: ignore candidate_plist) if not bool(candidate_plist_key): break candidate_plist_key_name = libm17n__msymbol_name( # type: ignore candidate_plist_key.contents) if candidate_plist_key_name != b'mtext': break candidate = mtext_to_string( ctypes.cast( libm17n__mplist_value(candidate_plist), # type: ignore ctypes.POINTER(libm17n__MText))) candidates.append(candidate) candidate_plist = libm17n__mplist_next( # type: ignore candidate_plist) else: break plist = libm17n__mplist_next(plist) # type: ignore cursor_pos = self._ic.contents.cursor_pos status = mtext_to_string(self._ic.contents.status) candidate_index = self._ic.contents.candidate_index candidate_from = self._ic.contents.candidate_from candidate_to = self._ic.contents.candidate_to candidate_show = self._ic.contents.candidate_show # From the m17n-lib documentation: # # The minput_reset_ic () function resets input context $IC by # calling a callback function corresponding to @b # Minput_reset. It resets the status of $IC to its initial # one. As the current preedit text is deleted without # commitment, if necessary, call minput_filter () with the arg # @b key #Mnil to force the input method to commit the preedit # in advance. # # Looks like we need to do this here, i.e. call minput_filter # with a final 'nil' msymbol to commit the preedit in the # input context to make the next call to minput_reset_ic() # work reliably. Without that minput_reset_ic() sometimes # segfaults. The old code, before fixing # https://github.com/mike-fabian/ibus-typing-booster/issues/460 # always appended a final 'nil' symbol to the msymbol_list # argument which had to be removed to get the correct preedit # contents. But apparently that final 'nil' is necessary to # make it work reliably. We can do this here because above we # read the preedit already and don’t need it anymore. # # It is not only necessary to make the next call to # minput_reset_ic() work reliably, it is also necessary to # commit any remaining preedit to avoid that the next # transliteration starts with a non-empty preedit remaining # from the previous transliteration. # # Unfortunately that makes state changing switches which # affect only the next character not survive until the next # transliteration. For example when switching to # single-fullwidth-mode by typing `Z` (see cjk-util.mim) with # early commits (i.e. with the `tb:zh:py`), `aZ` will commit # `啊`. The `Z` causes the commit but the state change done by # the `Z` does not survive, typing `aZaZ` commits `啊啊`. # With empty input one can type `Za` to get a single `a` # FULLWIDTH LATIN SMALL LETTER A though. _symbol = libm17n__msymbol(b'nil') # type: ignore _retval = libm17n__minput_filter( # type: ignore self._ic, _symbol, ctypes.c_void_p(None)) if committed and not preedit: committed_index = len(msymbol_list) # Some Chinese input methods and some Vietnamese input methods # for Chinese characters sometimes have “candidates == []” but # at the same time “candidate_show == 1”: # # For example zh-cangjie.mim contains: # # (map # ("a" ("日曰")) # ("aa" ("昌昍")) # ("aaa" ?晶) # ("aaaa" ("𣊫𣊭")) # [...] # # The ?晶 produces “candidates == []”, ("晶") would # produce “candidates == ['晶']” # # In that case typing `a` and `aa` produces candidates but # `aaa` does not and `aaaa` produces candidates again. That # is bad because Typing Booster behaves differently when there # are candidates and when there are not. So it would behave # inconsistently while typing `aaaa` if `aaa` suddenly has no # candidates and suddenly shows a “normal” typing booster # completion lookup table. # # Other input methods which have the same problem are # zh-tonepy, vi-nomtelex, vi-nomvni, ... # # So copy the preedit into candidates if they are empty and # candidate_show is one for more consistent behaviour (in my # opinion): if preedit and not candidates and candidate_show: candidates = [preedit] if self._language == 'ja' and self._name == 'anthy': # ja-anthy produces a lot of useless geta marks '〓'. It # also produces sometimes useless empty candidates and # sometimes (when transliterating `1 `) even preedit='', # cursor_pos=1. # # All these problems seem to be encoding problems caused # by mimx-anthy.c in m17n-lib using EUC-JP encoding # instead of UTF-8. They all go away go away after fixing # mimx-anthy.c to use UTF-8 by patching it like this: # # --- a/example/mimx-anthy.c # +++ b/example/mimx-anthy.c # @@ -129,15 +129,16 @@ new_context (MInputContext *ic) # { # AnthyContext *context; # anthy_context_t ac; # - MSymbol euc_jp = msymbol ("euc-jp"); # + MSymbol utf_8 = msymbol ("utf-8"); # /* Rebound to an actual buffer just before being used. */ # - MConverter *converter = mconv_buffer_converter (euc_jp, NULL, 0); # + MConverter *converter = mconv_buffer_converter (utf_8, NULL, 0); # # if (! converter) # return NULL; # ac = anthy_create_context (); # if (! ac) # return NULL; # + anthy_context_set_encoding(ac, ANTHY_UTF8_ENCODING); # context = calloc (1, sizeof (AnthyContext)); # context->ic = ic; # context->ac = ac; # # It is not necessary to use anthy-unicode (although this is # probably better), I tested that this encoding problem here # can be fixed by just patching mimx-anthy.c. Tested with # anthy-9100h-56.fc41.x86_64 # anthy-unicode-1.0.0.20240502-8.fc41.x86_64 # # For systems where mimx-anthy.c has not been fixed yet # in m17n-lib, apply a runtime fix here: if preedit != '下駄': candidates = [ candidate for candidate in candidates if candidate and '〓' not in candidate] if not preedit and candidates: preedit = candidates[0] cursor_pos = len(preedit) if not ascii_digits: return TransliterationParts(committed=committed, committed_index=committed_index, preedit=preedit, cursor_pos=cursor_pos, status=status, candidates=candidates, candidate_index=candidate_index, candidate_from=candidate_from, candidate_to=candidate_to, candidate_show=candidate_show) return TransliterationParts( committed=convert_digits_to_ascii(committed), committed_index=committed_index, preedit=convert_digits_to_ascii(preedit), cursor_pos=cursor_pos, status=status, candidates=candidates, candidate_index=candidate_index, candidate_from=candidate_from, candidate_to=candidate_to, candidate_show=candidate_show) def transliterate( self, msymbol_list: Iterable[str], ascii_digits: bool = False, reset: bool = False) -> str: '''Transliterate a list of Msymbol names :param msymbol_list: A list of strings which are interpreted as the names of Msymbols to transliterate. If the input method has the special name “NoIME”, no transliteration is done, the list of Msymbols is just joined to a single string. :param ascii_digits: If true, convert language specific digits to ASCII digits :return: The transliteration in one string Examples: Russian transliteration: >>> trans = Transliterator('ru-translit') >>> trans.transliterate(list('y')) 'ы' >>> trans.transliterate(list('yo')) 'ё' >>> trans.transliterate(list('yo y')) 'ё ы' Marathi transliteration: >>> trans = Transliterator('mr-itrans') >>> trans.transliterate(list('praviN')) 'प्रविण्' >>> trans.transliterate(list('namaste')) 'नमस्ते' Hindi transliteration: >>> trans = Transliterator('hi-itrans') >>> trans.transliterate(list('namaste')) 'नमस्ते' >>> trans.transliterate(list('. ')) '। ' Hindi-Inscript2 uses the AltGr key a lot, 'G-4' is the MSymbol name for AltGr-4 and it transliterates to something different than just '4': >>> trans = Transliterator('hi-inscript2') >>> trans.transliterate(['4', 'G-4']) '४₹' >>> trans = Transliterator('hi-inscript2') >>> trans.transliterate(['G-p']) 'ज़' AltGr-3 ('G-3') is not used though in Hindi-Inscript2. Therefore, 'G-3' transliterates just as 'G-3': >>> trans = Transliterator('hi-inscript2') >>> trans.transliterate(['3', 'G-3']) '३G-3' In mr-inscript2, 'G-1' transliterates to U+200D ZERO WIDTH JOINER ('\xe2\x80\x8d' in UTF-8 encoding): >>> trans = Transliterator('mr-inscript2') >>> trans.transliterate(['j', 'd', 'G-1', '/']).encode('utf-8') b'\xe0\xa4\xb0\xe0\xa5\x8d\xe2\x80\x8d\xe0\xa4\xaf' >>> trans = Transliterator('t-latn-post') >>> trans.transliterate(list('gru"n')) 'grün' >>> trans = Transliterator('NoIME') >>> trans.transliterate(['a', 'b', 'c', 'C-c', 'G-4', 'C-α', 'G-α']) 'abcC-cG-4C-αG-α' >>> trans = Transliterator('ko-romaja') >>> trans.transliterate(list('annyeonghaseyo')) '안녕하세요' >>> trans = Transliterator('si-wijesekara') >>> trans.transliterate(list('vksIal kjSka ')) 'ඩනිෂ්ක නවීන් ' ''' transliteration_parts = self.transliterate_parts( msymbol_list, ascii_digits, reset) return transliteration_parts.committed + transliteration_parts.preedit def get_variables(self) -> List[Tuple[str, str, str]]: # pylint: disable=line-too-long ''' Gets the optional variables of this transliterator input method Examples: >>> trans = Transliterator('NoIME') >>> trans.get_variables() [] >>> trans = Transliterator('si-wijesekara') >>> trans.get_variables() [('use-surrounding-text', 'Surrounding text vs. preedit.\\nIf 1, try to use surrounding text. Otherwise, use preedit.', '0')] >>> trans = Transliterator('t-unicode') >>> trans.get_variables() [('prompt', 'Preedit prompt\\nPrompt string shown in the preedit area while typing hexadecimal numbers.', 'U+')] ''' # pylint: enable=line-too-long variables_list: List[Tuple[str, str, str]] = [] if self._dummy: return variables_list plist = libm17n__minput_get_variable( # type: ignore libm17n__msymbol(self._language.encode('utf-8')), # type: ignore libm17n__msymbol(self._name.encode('utf-8')), # type: ignore libm17n__msymbol(b'nil')) # type: ignore while bool(plist): # NULL pointers have a False boolean value key = libm17n__mplist_key(plist) # type: ignore if not bool(key): # NULL pointers have a False boolean value break if b'plist' != libm17n__msymbol_name(key.contents): # type: ignore break ptr = ctypes.cast(libm17n__mplist_value(plist), # type: ignore ctypes.POINTER(libm17n__MPlist)) key = ctypes.cast(libm17n__mplist_value(ptr), # type: ignore ctypes.POINTER(libm17n__MSymbolStruct)) if not bool(key): # NULL pointers have a False boolean value break variable_name = libm17n__msymbol_name( # type: ignore key.contents).decode('utf-8') ptr = libm17n__mplist_next(ptr) # type: ignore variable_description_pointer = ctypes.cast( libm17n__mplist_value(ptr), # type: ignore ctypes.POINTER(libm17n__MText)) variable_description = '' if bool(variable_description_pointer): variable_description = mtext_to_string( variable_description_pointer) # Next item in the list is STATUS (we don’t use this) ptr = libm17n__mplist_next(ptr) # type: ignore mvalue = libm17n__mplist_next(ptr) # type: ignore key = libm17n__mplist_key(mvalue) # type: ignore if not bool(key): break key_name = libm17n__msymbol_name(key.contents) # type: ignore variable_value = '' if key_name == b'symbol': variable_value = libm17n__msymbol_name( # type: ignore ctypes.cast( libm17n__mplist_value(mvalue), # type: ignore ctypes.POINTER(libm17n__MSymbolStruct)).contents ).decode('utf-8') elif key_name == b'mtext': variable_value = mtext_to_string( ctypes.cast(libm17n__mplist_value(mvalue), # type: ignore ctypes.POINTER(libm17n__MText))) elif key_name == b'integer': if libm17n__mplist_value(mvalue) is None: # type: ignore variable_value = '0' else: variable_value = str( libm17n__mplist_value(mvalue)) # type: ignore elif key_name == b't': variable_value = '' variables_list.append((variable_name, variable_description, variable_value)) plist = libm17n__mplist_next(plist) # type: ignore return variables_list def set_variables(self, variables: Dict[str, str]) -> None: ''' Sets the optional variables of this transliterator input method Raises ValueError if something fails. Examples: >>> trans = Transliterator('NoIME') >>> trans.set_variables([]) Only two >> from here on to avoid executing these Examples accidentally as doctests and accidentally do real changes to the user config file ~/.m17n.d/config.mic: >> trans = Transliterator('bn-national-jatiya') >> trans.set_variables({'use-automatic-vowel-forming': '1'}) Setting empty strings as values sets the default values (i.e. it removes the setting from the user config file to use the global default instead): >> trans = Transliterator('bn-national-jatiya') >> trans.set_variables({'use-automatic-vowel-forming': ''}) >> trans = Transliterator('t-unicode') >> trans.set_variables({'prompt': 'U+'}) >> trans = Transliterator('ja-anthy') >> trans.set_variables({'input-mode': 'hiragana', 'zen-han': 'hankaku'}) ''' if self._dummy or not variables: return for variable_name, variable_value in variables.items(): plist = libm17n__minput_get_variable( # type: ignore libm17n__msymbol(self._language.encode('utf-8')), # type: ignore libm17n__msymbol(self._name.encode('utf-8')), # type: ignore libm17n__msymbol(variable_name.encode('utf-8'))) # type: ignore if not bool(plist): raise ValueError('minput_get_variable() returned NULL') key = libm17n__mplist_key(plist) # type: ignore if not bool(key): raise ValueError('mplist_key(plist) returned NULL') if b'plist' != libm17n__msymbol_name(key.contents): # type: ignore raise ValueError('msymbol_name(key.contents) is not b"plist"') ptr = ctypes.cast(libm17n__mplist_value(plist), # type: ignore ctypes.POINTER(libm17n__MPlist)) ptr = libm17n__mplist_next(ptr) # type: ignore ptr = libm17n__mplist_next(ptr) # type: ignore mvalue = libm17n__mplist_next(ptr) # type: ignore key = libm17n__mplist_key(mvalue) # type: ignore if not bool(key): raise ValueError('mplist_key(mvalue) returned NULL') key_name = libm17n__msymbol_name(key.contents) # type: ignore new_value_plist = libm17n__mplist() # type: ignore # If variable_value is the empty string '' the newly # created empty new_value_plist is not filled and used # empty in minput_config_variable() which cancels any # configuration and customization of the variable, and the # default value is assigned to the variable. if variable_value: if key_name == b'symbol': libm17n__mplist_add( # type: ignore new_value_plist, libm17n__msymbol(b'symbol'), # type: ignore libm17n__msymbol(variable_value.encode('utf-8'))) # type: ignore elif key_name == b'mtext': mtext = libm17n__mconv_decode_buffer( # type: ignore libm17n__Mcoding_utf_8, ctypes.c_char_p(variable_value.encode('utf-8')), len(variable_value.encode('utf-8'))) libm17n__mplist_add( # type: ignore new_value_plist, libm17n__msymbol(b'mtext'), # type: ignore mtext) elif key_name == b'integer': try: int_value = int(variable_value, 10) libm17n__mplist_add( # type: ignore new_value_plist, libm17n__msymbol(b'integer'), # type: ignore int_value) except ValueError: new_value_plist = libm17n__mplist() # type: ignore else: # This should never happen: raise ValueError( 'Variable type is not Msymbol, Minteger, or Mtext') retval = libm17n__minput_config_variable( # type: ignore libm17n__msymbol(self._language.encode('utf-8')), # type: ignore libm17n__msymbol(self._name.encode('utf-8')), # type: ignore libm17n__msymbol(variable_name.encode('utf-8')), # type: ignore new_value_plist) # If the operation was successful, minput_config_variable() # returns 0, otherwise -1. if retval: raise ValueError( f'minput_config_variable() failed with retval = {retval}') retval = libm17n__minput_save_config() # type: ignore # minput_save_config() returns: # - If the operation was successful, 1 is returned # - If the per-user customization file is currently locked, # 0 is returned. In that case, the caller may wait for a # while and try again. # - If the configuration file is not writable, -1 is returned. if retval == 1: continue if retval == 0: raise ValueError( f'minput_save_config() failed with retval = {retval} ' '(per-user customization file is locked).') if retval == -1: raise ValueError( f'minput_save_config() failed with retval = {retval} ' '(configuration file not writeable).') raise ValueError( f'minput_save_config() failed with retval = {retval} ' '(unknown error, should never happen)') return if __name__ == "__main__": LOG_HANDLER = logging.StreamHandler(stream=sys.stderr) LOGGER.setLevel(logging.DEBUG) LOGGER.addHandler(LOG_HANDLER) import doctest (FAILED, _ATTEMPTED) = doctest.testmod() sys.exit(FAILED)