summaryrefslogtreecommitdiffstats
path: root/engine
diff options
context:
space:
mode:
authorHuang Peng <shawn.p.huang@gmail.com>2008-06-11 18:01:32 +0800
committerHuang Peng <shawn.p.huang@gmail.com>2008-06-11 18:01:32 +0800
commit924e244fff3a48eea3aa742bfbe0885c62f93d71 (patch)
tree8d72f8af2cb2b73bcba1d389ae60ee591a28f6d4 /engine
parente5cdfb21fde5c4f7028ec5ceb6ed44aac769feb3 (diff)
downloadibus-924e244fff3a48eea3aa742bfbe0885c62f93d71.tar.gz
ibus-924e244fff3a48eea3aa742bfbe0885c62f93d71.tar.xz
ibus-924e244fff3a48eea3aa742bfbe0885c62f93d71.zip
WIP anthy engine.
Diffstat (limited to 'engine')
-rw-r--r--engine/anthy/Makefile4
-rw-r--r--engine/anthy/engine.py257
-rw-r--r--engine/anthy/factory.py33
-rw-r--r--engine/anthy/tables.py331
4 files changed, 624 insertions, 1 deletions
diff --git a/engine/anthy/Makefile b/engine/anthy/Makefile
index 36dbf74..70d0428 100644
--- a/engine/anthy/Makefile
+++ b/engine/anthy/Makefile
@@ -1,8 +1,10 @@
-all: _anthy.so
+all: _anthy.so test
anthy.py anthy_wrap.c: anthy.i
swig -python -I/usr/include $<
_anthy.so: anthy_wrap.c
$(CC) -shared -o $@ $^ -fPIC `python-config --cflags --libs` `pkg-config anthy --cflags --libs`
+test:
+ PYTHONPATH=../.. python main.py
clean:
$(RM) anthy.py anthy_wrap.c _anthy.so
diff --git a/engine/anthy/engine.py b/engine/anthy/engine.py
new file mode 100644
index 0000000..faf14d6
--- /dev/null
+++ b/engine/anthy/engine.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python
+import gobject
+import gtk
+import pango
+import dbus
+import ibus
+import anthy
+from tables import *
+from ibus import keysyms
+from ibus import interface
+
+class Engine (interface.IEngine):
+ def __init__ (self, dbusconn, object_path):
+ interface.IEngine.__init__ (self, dbusconn, object_path)
+ self._dbusconn = dbusconn
+
+ # create anthy context
+ self._context = anthy.anthy_context ()
+ self._context._set_encoding (2)
+
+ self._lookup_table = ibus.LookupTable ()
+ self._prop_list = ibus.PropList ()
+
+ # use reset to init values
+ self._reset ()
+
+ # reset values of engine
+ def _reset (self):
+ self._input_chars = u""
+ self._convert_chars = u""
+ self._cursor_pos = 0
+ self._need_update = False
+ self._convert_begined = False
+ self._segments = []
+ self._lookup_table.clean ()
+
+ # begine convert
+ def _begin_convert (self):
+ if self._convert_begined:
+ return
+ self._convert_begined = True
+
+ self._context.set_string (self._input_chars.encode ("utf-8"))
+ conv_stat = anthy.anthy_conv_stat ()
+ self._context.get_stat (conv_stat)
+
+ for i in xrange (0, conv_stat.nr_segment):
+ buf = " " * 100
+ l = self._context.get_segment (i, 0, buf, 100)
+ text = unicode (buf[:l], "utf-8")
+ self._segments.append ((0, text))
+
+ self._cursor_pos = 0
+
+ self._fill_lookup_table ()
+
+ def _fill_lookup_table ():
+ # get segment stat
+ seg_stat = anthy.anthy_segment_stat ()
+ self._context.get_segment_stat (self._current_pos, seg_stat)
+
+ # fill lookup_table
+ self._lookup_table.clean ()
+ for i in xrange (0, seg_stat.nr_candidate):
+ buf = " " * 100
+ l = self._context.get_segment (self._cursor_pos, i, buf, 100)
+ candidate = unicode (buf[:l], "utf-8")
+ self._lookup_table.append_candidate (candidate)
+
+ def _process_key_event (self, keyval, is_press, state):
+ # ignore key release events
+ if not is_press:
+ return False
+
+ if self._input_chars:
+ if keyval == keysyms.Return:
+ self._commit_string (self._input_chars)
+ return True
+ elif keyval == keysyms.Escape:
+ self._reset ()
+ return True
+ elif keyval == keysyms.BackSpace:
+ self._input_chars = self._input_chars[:self._cursor_pos - 1] + self._input_chars [self._cursor_pos:]
+ self._invalidate ()
+ return True
+ elif keyval == keysyms.space:
+ if not self._convert_begined:
+ self._begine_convert ()
+ self._invalidate ()
+ else:
+ self._cursor_down ()
+ return True
+ elif keyval >= keysyms._1 and keyval <= keysyms._9:
+ index = keyval - keysyms._1
+ candidates = self._lookup_table.get_canidates_in_current_page ()
+ if index >= len (candidates):
+ return False
+ candidate = candidates[index][0]
+ self._commit_string (candidate)
+ return True
+ elif keyval == keysyms.Page_Up or keyval == keysyms.KP_Page_Up:
+ if self._lookup_table.page_up ():
+ self._update_lookup_table ()
+ return True
+ elif keyval == keysyms.Up:
+ self._cursor_up ()
+ return True
+ elif keyval == keysyms.Down:
+ self._cursor_down ()
+ return True
+ elif keyval == keysyms.Left or keyval == keysyms.Right:
+ return True
+ elif keyval == keysyms.Page_Down or keyval == keysyms.KP_Page_Down:
+ if self._lookup_table.page_down ():
+ self._update_lookup_table ()
+ return True
+ if keyval in xrange (keysyms.a, keysyms.z + 1) or \
+ keyval in xrange (keysyms.A, keysyms.Z + 1):
+ if state & (keysyms.CONTROL_MASK | keysyms.ALT_MASK) == 0:
+ self._input_chars += unichr (keyval)
+ self._cursor_pos += 1
+ self._invalidate ()
+ return True
+ else:
+ if keyval < 128 and self._input_chars:
+ self._commit_string (self._input_chars)
+
+ return False
+
+ def _invalidate (self):
+ if self._need_update:
+ return
+ self._need_update = True
+ gobject.idle_add (self._update, priority = gobject.PRIORITY_LOW)
+
+ def _cursor_up (self):
+ # only process cursor down in convert mode
+ if not self._convert_begined:
+ return False
+
+ if not self._lookup_table.cursor_up ():
+ return False
+
+ candidate = self._lookup_table.get_current_candidate ()[0]
+ index = self._lookup_table.get_cursor_pos ()
+ self._segments[self._cursor_pos] = index, candidate
+ self._invalidate ()
+ return True
+
+ def _cursor_down (self):
+ # only process cursor down in convert mode
+ if not self._convert_begined:
+ return False
+
+ if not self._lookup_table.cursor_down ():
+ return False
+
+ candidate = self._lookup_table.get_current_candidate ()[0]
+ index = self._lookup_table.get_cursor_pos ()
+ self._segments[self._cursor_pos] = index, candidate
+ self._invalidate ()
+ return True
+
+ def _commit_string (self, text):
+ self._reset ()
+ self.CommitString (text)
+ self._update ()
+
+ def _update_input_chars (self):
+ begin, end = max (self._cursor_pos - 4, 0), self._cursor_pos
+
+ for i in range (begin, end):
+ text = self._input_chars[i:end]
+ romja = romaji_typing_rule.get (text, None)
+ if romja != None:
+ self._input_chars = u"".join ((self._input_chars[:i], romja, self._input_chars[end:]))
+ self._cursor_pos -= len(text)
+ self._cursor_pos += len(romja)
+
+ attrs = ibus.AttrList ()
+ attrs.append (ibus.AttributeUnderline (pango.UNDERLINE_SINGLE, 0, len (self._input_chars.encode ("utf-8"))))
+
+ self.UpdatePreedit (dbus.String (self._input_chars),
+ attrs.to_dbus_value (),
+ dbus.Int32 (self._cursor_pos),
+ len (self._input_chars) > 0)
+
+ def _update_convert_chars (self):
+ self._convert_chars = u""
+ buf = " " * 100
+ pos = 0
+ i = 0
+ for seg_index, text in self._segments:
+ self._convert_chars += text
+ if i < self._cursor_pos:
+ pos += len (text)
+ i += 1
+
+ attrs = ibus.AttrList ()
+ attrs.append (ibus.AttributeUnderline (pango.UNDERLINE_SINGLE, 0, len (self._convert_chars.encode ("utf-8"))))
+
+ self.UpdatePreedit (dbus.String (self._convert_chars),
+ attrs.to_dbus_value (),
+ dbus.Int32 (pos),
+ True)
+
+ def _update (self):
+ self._need_update = False
+ if self._convert_begined:
+ self._update_input_chars ()
+ else:
+ self._update_convert_chars ()
+
+ # methods for dbus rpc
+ def ProcessKeyEvent (self, keyval, is_press, state):
+ try:
+ return self._process_key_event (keyval, is_press, state)
+ except Exception, e:
+ print e
+ return False
+
+ def FocusIn (self):
+ self.RegisterProperties (self._prop_list.to_dbus_value ())
+ print "FocusIn"
+
+ def FocusOut (self):
+ print "FocusOut"
+
+ def SetCursorLocation (self, x, y, w, h):
+ pass
+
+ def Reset (self):
+ print "Reset"
+
+ def PageUp (self):
+ print "PageUp"
+
+ def PageDown (self):
+ print "PageDown"
+
+ def CursorUp (self):
+ self._cursor_up ()
+
+ def CursorDown (self):
+ self._cursor_down ()
+
+ def SetEnable (self, enable):
+ self._enable = enable
+ if self._enable:
+ self.RegisterProperties (self._prop_list.to_dbus_value ())
+
+ def PropertyActivate (self, prop_name):
+ print "PropertyActivate (%s)" % prop_name
+
+ def Destroy (self):
+ print "Destroy"
+
diff --git a/engine/anthy/factory.py b/engine/anthy/factory.py
new file mode 100644
index 0000000..62f697c
--- /dev/null
+++ b/engine/anthy/factory.py
@@ -0,0 +1,33 @@
+from ibus import interface
+import engine
+
+FACTORY_PATH = "/com/redhat/IBus/engines/Anthy/Factory"
+ENGINE_PATH = "/com/redhat/IBus/engines/Anthy/Engine/%d"
+
+class DemoEngineFactory (interface.IEngineFactory):
+ NAME = "AnthyEngine"
+ LANG = "ja"
+ ICON = ""
+ AUTHORS = "Huang Peng <shawn.p.huang@gmail.com>"
+ CREDITS = "GPLv2"
+
+ def __init__ (self, dbusconn):
+ interface.IEngineFactory.__init__ (self, dbusconn, object_path = FACTORY_PATH)
+ self._dbusconn = dbusconn
+ self._max_engine_id = 1
+
+ def GetInfo (self):
+ return [
+ self.NAME,
+ self.LANG,
+ self.ICON,
+ self.AUTHORS,
+ self.CREDITS
+ ]
+
+ def CreateEngine (self):
+ engine_path = ENGINE_PATH % self._max_engine_id
+ self._max_engine_id += 1
+ return engine.Engine (self._dbusconn, engine_path)
+
+
diff --git a/engine/anthy/tables.py b/engine/anthy/tables.py
new file mode 100644
index 0000000..e01a464
--- /dev/null
+++ b/engine/anthy/tables.py
@@ -0,0 +1,331 @@
+# -*- encoding: utf-8 -*-
+
+# string, result, cont
+romaji_typing_rule = {
+ u"-" : u"ー",
+ u"a" : u"あ",
+ u"i" : u"い",
+ u"u" : u"う",
+ u"e" : u"え",
+ u"o" : u"お",
+ u"xa" : u"ぁ",
+ u"xi" : u"ぃ",
+ u"xu" : u"ぅ",
+ u"xe" : u"ぇ",
+ u"xo" : u"ぉ",
+ u"la" : u"ぁ",
+ u"li" : u"ぃ",
+ u"lu" : u"ぅ",
+ u"le" : u"ぇ",
+ u"lo" : u"ぉ",
+ u"wi" : u"うぃ",
+ u"we" : u"うぇ",
+ u"wha" : u"うぁ",
+ u"whi" : u"うぃ",
+ u"whe" : u"うぇ",
+ u"who" : u"うぉ",
+ u"va" : u"ヴぁ",
+ u"vi" : u"ヴぃ",
+ u"vu" : u"ヴ",
+ u"ve" : u"ヴぇ",
+ u"vo" : u"ヴぉ",
+ u"ka" : u"か",
+ u"ki" : u"き",
+ u"ku" : u"く",
+ u"ke" : u"け",
+ u"ko" : u"こ",
+ u"ga" : u"が",
+ u"gi" : u"ぎ",
+ u"gu" : u"ぐ",
+ u"ge" : u"げ",
+ u"go" : u"ご",
+ u"kya" : u"きゃ",
+ u"kyi" : u"きぃ",
+ u"kyu" : u"きゅ",
+ u"kye" : u"きぇ",
+ u"kyo" : u"きょ",
+ u"gya" : u"ぎゃ",
+ u"gyi" : u"ぎぃ",
+ u"gyu" : u"ぎゅ",
+ u"gye" : u"ぎぇ",
+ u"gyo" : u"ぎょ",
+ u"sa" : u"さ",
+ u"si" : u"し",
+ u"su" : u"す",
+ u"se" : u"せ",
+ u"so" : u"そ",
+ u"za" : u"ざ",
+ u"zi" : u"じ",
+ u"zu" : u"ず",
+ u"ze" : u"ぜ",
+ u"zo" : u"ぞ",
+ u"sya" : u"しゃ",
+ u"syi" : u"しぃ",
+ u"syu" : u"しゅ",
+ u"sye" : u"しぇ",
+ u"syo" : u"しょ",
+ u"sha" : u"しゃ",
+ u"shi" : u"し",
+ u"shu" : u"しゅ",
+ u"she" : u"しぇ",
+ u"sho" : u"しょ",
+ u"zya" : u"じゃ",
+ u"zyi" : u"じぃ",
+ u"zyu" : u"じゅ",
+ u"zye" : u"じぇ",
+ u"zyo" : u"じょ",
+ u"ja" : u"じゃ",
+ u"jya" : u"じゃ",
+ u"ji" : u"じ",
+ u"jyi" : u"じぃ",
+ u"ju" : u"じゅ",
+ u"jyu" : u"じゅ",
+ u"je" : u"じぇ",
+ u"jye" : u"じぇ",
+ u"jo" : u"じょ",
+ u"jyo" : u"じょ",
+ u"ta" : u"た",
+ u"ti" : u"ち",
+ u"tu" : u"つ",
+ u"tsu" : u"つ",
+ u"te" : u"て",
+ u"to" : u"と",
+ u"da" : u"だ",
+ u"di" : u"ぢ",
+ u"du" : u"づ",
+ u"de" : u"で",
+ u"do" : u"ど",
+ u"xtu" : u"っ",
+ u"xtsu" : u"っ",
+ u"ltu" : u"っ",
+ u"ltsu" : u"っ",
+ u"tya" : u"ちゃ",
+ u"tyi" : u"ちぃ",
+ u"tyu" : u"ちゅ",
+ u"tye" : u"ちぇ",
+ u"tyo" : u"ちょ",
+ u"cha" : u"ちゃ",
+ u"chi" : u"ち",
+ u"chu" : u"ちゅ",
+ u"che" : u"ちぇ",
+ u"cho" : u"ちょ",
+ u"dya" : u"ぢゃ",
+ u"dyi" : u"ぢぃ",
+ u"dyu" : u"ぢゅ",
+ u"dye" : u"ぢぇ",
+ u"dyo" : u"ぢょ",
+ u"tha" : u"てゃ",
+ u"thi" : u"てぃ",
+ u"thu" : u"てゅ",
+ u"the" : u"てぇ",
+ u"tho" : u"てょ",
+ u"dha" : u"でゃ",
+ u"dhi" : u"でぃ",
+ u"dhu" : u"でゅ",
+ u"dhe" : u"でぇ",
+ u"dho" : u"でょ",
+ u"na" : u"な",
+ u"ni" : u"に",
+ u"nu" : u"ぬ",
+ u"ne" : u"ね",
+ u"no" : u"の",
+ u"nya" : u"にゃ",
+ u"nyi" : u"にぃ",
+ u"nyu" : u"にゅ",
+ u"nye" : u"にぇ",
+ u"nyo" : u"にょ",
+ u"ha" : u"は",
+ u"hi" : u"ひ",
+ u"hu" : u"ふ",
+ u"fu" : u"ふ",
+ u"he" : u"へ",
+ u"ho" : u"ほ",
+ u"ba" : u"ば",
+ u"bi" : u"び",
+ u"bu" : u"ぶ",
+ u"be" : u"べ",
+ u"bo" : u"ぼ",
+ u"pa" : u"ぱ",
+ u"pi" : u"ぴ",
+ u"pu" : u"ぷ",
+ u"pe" : u"ぺ",
+ u"po" : u"ぽ",
+ u"hya" : u"ひゃ",
+ u"hyi" : u"ひぃ",
+ u"hyu" : u"ひゅ",
+ u"hye" : u"ひぇ",
+ u"hyo" : u"ひょ",
+ u"bya" : u"びゃ",
+ u"byi" : u"びぃ",
+ u"byu" : u"びゅ",
+ u"bye" : u"びぇ",
+ u"byo" : u"びょ",
+ u"pya" : u"ぴゃ",
+ u"pyi" : u"ぴぃ",
+ u"pyu" : u"ぴゅ",
+ u"pye" : u"ぴぇ",
+ u"pyo" : u"ぴょ",
+ u"fa" : u"ふぁ",
+ u"fi" : u"ふぃ",
+ u"fu" : u"ふ",
+ u"fe" : u"ふぇ",
+ u"fo" : u"ふぉ",
+ u"ma" : u"ま",
+ u"mi" : u"み",
+ u"mu" : u"む",
+ u"me" : u"め",
+ u"mo" : u"も",
+ u"mya" : u"みゃ",
+ u"myi" : u"みぃ",
+ u"myu" : u"みゅ",
+ u"mye" : u"みぇ",
+ u"myo" : u"みょ",
+ u"lya" : u"ゃ",
+ u"xya" : u"ゃ",
+ u"ya" : u"や",
+ u"lyu" : u"ゅ",
+ u"xyu" : u"ゅ",
+ u"yu" : u"ゆ",
+ u"lyo" : u"ょ",
+ u"xyo" : u"ょ",
+ u"yo" : u"よ",
+ u"ra" : u"ら",
+ u"ri" : u"り",
+ u"ru" : u"る",
+ u"re" : u"れ",
+ u"ro" : u"ろ",
+ u"rya" : u"りゃ",
+ u"ryi" : u"りぃ",
+ u"ryu" : u"りゅ",
+ u"rye" : u"りぇ",
+ u"ryo" : u"りょ",
+ u"xwa" : u"ゎ",
+ u"wa" : u"わ",
+ u"wo" : u"を",
+# u"n'" : u"ん",
+ u"nn" : u"ん",
+ u"wyi" : u"ゐ",
+ u"wye" : u"ゑ",
+}
+
+#hiragana, katakana, half_katakana
+hiragana_katakana_table = {
+ u"あ" : (u"ア", u"ア"),
+ u"い" : (u"イ", u"イ"),
+ u"う" : (u"ウ", u"ウ"),
+ u"え" : (u"エ", u"エ"),
+ u"お" : (u"オ", u"オ"),
+ u"か" : (u"カ", u"カ"),
+ u"き" : (u"キ", u"キ"),
+ u"く" : (u"ク", u"ク"),
+ u"け" : (u"ケ", u"ケ"),
+ u"こ" : (u"コ", u"コ"),
+ u"が" : (u"ガ", u"ガ"),
+ u"ぎ" : (u"ギ", u"ギ"),
+ u"ぐ" : (u"グ", u"グ"),
+ u"げ" : (u"ゲ", u"ゲ"),
+ u"ご" : (u"ゴ", u"ゴ"),
+ u"さ" : (u"サ", u"サ"),
+ u"し" : (u"シ", u"シ"),
+ u"す" : (u"ス", u"ス"),
+ u"せ" : (u"セ", u"セ"),
+ u"そ" : (u"ソ", u"ソ"),
+ u"ざ" : (u"ザ", u"ザ"),
+ u"じ" : (u"ジ", u"ジ"),
+ u"ず" : (u"ズ", u"ズ"),
+ u"ぜ" : (u"ゼ", u"ゼ"),
+ u"ぞ" : (u"ゾ", u"ゾ"),
+ u"た" : (u"タ", u"タ"),
+ u"ち" : (u"チ", u"チ"),
+ u"つ" : (u"ツ", u"ツ"),
+ u"て" : (u"テ", u"テ"),
+ u"と" : (u"ト", u"ト"),
+ u"だ" : (u"ダ", u"ダ"),
+ u"ぢ" : (u"ヂ", u"ヂ"),
+ u"づ" : (u"ヅ", u"ヅ"),
+ u"で" : (u"デ", u"デ"),
+ u"ど" : (u"ド", u"ド"),
+ u"な" : (u"ナ", u"ナ"),
+ u"に" : (u"ニ", u"ニ"),
+ u"ぬ" : (u"ヌ", u"ヌ"),
+ u"ね" : (u"ネ", u"ネ"),
+ u"の" : (u"ノ", u"ノ"),
+ u"は" : (u"ハ", u"ハ"),
+ u"ひ" : (u"ヒ", u"ヒ"),
+ u"ふ" : (u"フ", u"フ"),
+ u"へ" : (u"ヘ", u"ヘ"),
+ u"ほ" : (u"ホ", u"ホ"),
+ u"ば" : (u"バ", u"バ"),
+ u"び" : (u"ビ", u"ビ"),
+ u"ぶ" : (u"ブ", u"ブ"),
+ u"べ" : (u"ベ", u"ベ"),
+ u"ぼ" : (u"ボ", u"ボ"),
+ u"ぱ" : (u"パ", u"パ"),
+ u"ぴ" : (u"ピ", u"ピ"),
+ u"ぷ" : (u"プ", u"プ"),
+ u"ぺ" : (u"ペ", u"ペ"),
+ u"ぽ" : (u"ポ", u"ポ"),
+ u"ま" : (u"マ", u"マ"),
+ u"み" : (u"ミ", u"ミ"),
+ u"む" : (u"ム", u"ム"),
+ u"め" : (u"メ", u"メ"),
+ u"も" : (u"モ", u"モ"),
+ u"や" : (u"ヤ", u"ヤ"),
+ u"ゆ" : (u"ユ", u"ユ"),
+ u"よ" : (u"ヨ", u"ヨ"),
+ u"ら" : (u"ラ", u"ラ"),
+ u"り" : (u"リ", u"リ"),
+ u"る" : (u"ル", u"ル"),
+ u"れ" : (u"レ", u"レ"),
+ u"ろ" : (u"ロ", u"ロ"),
+ u"わ" : (u"ワ", u"ワ"),
+ u"を" : (u"ヲ", u"ヲ"),
+ u"ん" : (u"ン", u"ン"),
+ u"ぁ" : (u"ァ", u"ァ"),
+ u"ぃ" : (u"ィ", u"ィ"),
+ u"ぅ" : (u"ゥ", u"ゥ"),
+ u"ぇ" : (u"ェ", u"ェ"),
+ u"ぉ" : (u"ォ", u"ォ"),
+ u"っ" : (u"ッ", u"ッ"),
+ u"ゃ" : (u"ャ", u"ャ"),
+ u"ゅ" : (u"ュ", u"ュ"),
+ u"ょ" : (u"ョ", u"ョ"),
+ u"ヵ" : (u"ヵ", u"カ"),
+ u"ヶ" : (u"ヶ", u"ケ"),
+ u"ゎ" : (u"ヮ", u"ワ"),
+ u"ゐ" : (u"ヰ", u"ィ"),
+ u"ゑ" : (u"ヱ", u"ェ"),
+ u"ヴ" : (u"ヴ", u"ヴ"),
+ u"ー" : (u"ー", u"ー"),
+ u"、" : (u"、", u"、"),
+ u"。" : (u"。", u"。"),
+ u"!" : (u"!", u"!"),
+ u"”" : (u"”", u"\""),
+ u"#" : (u"#", u"#"),
+ u"$" : (u"$", u"$"),
+ u"%" : (u"%", u"%"),
+ u"&" : (u"&", u"&"),
+ u"’" : (u"’", u"'"),
+ u"(" : (u"(", u""),
+ u")" : (u")", u")"),
+ u"〜" : (u"〜", u"~"),
+ u"=" : (u"=", u"="),
+ u"^" : (u"^", u"u"),
+ u"\" : (u"\", u"\\"),
+ u"|" : (u"|", u"|"),
+ u"‘" : (u"‘", u"`"),
+ u"@" : (u"@", u"@"),
+ u"{" : (u"{", u""),
+ u"「" : (u"「", u"「"),
+ u"+" : (u"+", u"+"),
+ u";" : (u";", u";"),
+ u"*" : (u"*", u"*"),
+ u":" : (u":", u" : u"),
+ u"}" : (u"}", u")"),
+ u"」" : (u"」", u"」"),
+ u"<" : (u"<", u"<"),
+ u">" : (u">", u">"),
+ u"?" : (u"?", u"?"),
+ u"/" : (u"/", u"/"),
+ u"_" : (u"_", u"_"),
+}