summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChoe Hwanjin <choe.hwanjin@gmail.com>2016-03-04 18:10:29 +0900
committerChoe Hwanjin <choe.hwanjin@gmail.com>2016-03-05 11:18:58 +0900
commitacf25460c6ec11259ce5044c1005a4d620c9ae4c (patch)
tree9eecdd84792a6a0bde584db177325b0eaa461014
parentbf4981327a5d5985b573a2e594c31b455ec59ba6 (diff)
downloadlibhangul-acf25460c6ec11259ce5044c1005a4d620c9ae4c.tar.gz
xml로 작성된 외부의 한글 자판을 로딩하는 기능 구현
한글 자판 파일의 확장성을 고려하여 자판 파일은 xml로 구성하도록 한다. xml 파서는 expat을 사용한다. xml 번역을 위해서 intltool을 사용하고 autopoint대신 intltool로 gettext을 초기화 한다. 한글 자판 관리는 HangulKeyboardList를 통해서 한다. 한글 자판을 로딩하고 언로딩하기 위해서 hangul_init()/hangul_fini()와 같은 함수를 추가로 도입한다. HangulKeyboard는 HangulCombination과 세트로 관리하는 편이 합리적인 것 같다.
-rw-r--r--.gitignore1
-rwxr-xr-xautogen.sh2
-rw-r--r--configure.ac10
-rw-r--r--data/Makefile.am2
-rw-r--r--data/keyboards/Makefile.am58
-rw-r--r--data/keyboards/hangul-combination-default.xml40
-rw-r--r--data/keyboards/hangul-combination-full.xml356
-rw-r--r--data/keyboards/hangul-keyboard-2.xml.template105
-rw-r--r--data/keyboards/hangul-keyboard-2y.xml.template105
-rw-r--r--data/keyboards/hangul-keyboard-32.xml.template105
-rw-r--r--data/keyboards/hangul-keyboard-39.xml.template105
-rw-r--r--data/keyboards/hangul-keyboard-3f.xml.template105
-rw-r--r--data/keyboards/hangul-keyboard-3s.xml.template105
-rw-r--r--data/keyboards/hangul-keyboard-3y.xml.template105
-rw-r--r--data/keyboards/hangul-keyboard-ahn.xml.template186
-rw-r--r--data/keyboards/hangul-keyboard-ro.xml.template151
-rw-r--r--hangul/Makefile.am11
-rw-r--r--hangul/hangul.h29
-rw-r--r--hangul/hangulinputcontext.c375
-rw-r--r--hangul/hangulinternals.h31
-rw-r--r--hangul/hangulkeyboard.c950
-rw-r--r--po/POTFILES.in2
-rw-r--r--test/hangul.c4
-rw-r--r--test/test.c12
-rw-r--r--tools/hangul.c12
25 files changed, 2603 insertions, 364 deletions
diff --git a/.gitignore b/.gitignore
index 7cb2394..7816a9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ config.sub
configure
data/hanja/Makefile
data/hanja/Makefile.in
+data/keyboards/hangul-keyboard-*.xml
depcomp
doc/Doxyfile
doc/hangulkeyboards.dox
diff --git a/autogen.sh b/autogen.sh
index 635edf6..97617b2 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -8,7 +8,7 @@ if test -z "$libtoolize"; then
libtoolize=libtoolize
fi
-autopoint --force
+intltoolize --copy --force
$libtoolize --automake --copy --force
aclocal $ACLOCAL_AMFLAGS
autoheader
diff --git a/configure.ac b/configure.ac
index cde5bad..95e89c7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,6 +16,8 @@ AC_SUBST(LIBHANGUL_CURRENT)
AC_SUBST(LIBHANGUL_REVISION)
AC_SUBST(LIBHANGUL_AGE)
+IT_PROG_INTLTOOL
+
# Checks for programs.
AC_PROG_CC
AM_PROG_CC_C_O
@@ -48,12 +50,13 @@ AC_CHECK_FUNCS([nl_langinfo])
GETTEXT_PACKAGE="$PACKAGE"
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", gettext package name)
-AM_GNU_GETTEXT([external])
-AM_GNU_GETTEXT_VERSION(0.18)
AM_ICONV
-# Checks for unit test framework
+# Checks for expat
PKG_PROG_PKG_CONFIG
+PKG_CHECK_MODULES(EXPAT, [expat])
+
+# Checks for unit test framework
if test -n "$PKG_CONFIG"; then
PKG_CHECK_EXISTS(check, [ PKG_CHECK_MODULES([CHECK], [check]) ])
fi
@@ -62,6 +65,7 @@ AC_CONFIG_FILES([
Makefile
data/Makefile
data/hanja/Makefile
+data/keyboards/Makefile
doc/Doxyfile
hangul/Makefile
libhangul.pc
diff --git a/data/Makefile.am b/data/Makefile.am
index b60de42..6f8340e 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1 +1 @@
-SUBDIRS = hanja
+SUBDIRS = hanja keyboards
diff --git a/data/keyboards/Makefile.am b/data/keyboards/Makefile.am
new file mode 100644
index 0000000..12b5459
--- /dev/null
+++ b/data/keyboards/Makefile.am
@@ -0,0 +1,58 @@
+
+keyboardsdir = $(pkgdatadir)/keyboards
+keyboards_DATA = \
+ hangul-keyboard-2.xml \
+ hangul-keyboard-2y.xml \
+ hangul-keyboard-39.xml \
+ hangul-keyboard-3f.xml \
+ hangul-keyboard-32.xml \
+ hangul-keyboard-3s.xml \
+ hangul-keyboard-3y.xml \
+ hangul-keyboard-ro.xml \
+ hangul-keyboard-ahn.xml \
+ hangul-combination-default.xml \
+ hangul-combination-full.xml \
+ $(NULL)
+
+EXTRA_DIST = \
+ hangul-keyboard-2.xml.template \
+ hangul-keyboard-2y.xml.template \
+ hangul-keyboard-39.xml.template \
+ hangul-keyboard-3f.xml.template \
+ hangul-keyboard-32.xml.template \
+ hangul-keyboard-3s.xml.template \
+ hangul-keyboard-3y.xml.template \
+ hangul-keyboard-ro.xml.template \
+ hangul-keyboard-ahn.xml.template \
+ hangul-combination-default.xml \
+ hangul-combination-full.xml \
+ $(NULL)
+
+# intltool로 xml 파일을 번역하면 주석이 모두 사라지고 attr의 순서도
+# 재정렬된다. 이를 방지하고자 name 부분만 별도의 name.xml로 만들어
+# 번역한후 template과 name.xml을 병합하여 키보드 xml 파일을 생성한다.
+hangul-keyboard-%.xml: hangul-keyboard-%.name.xml hangul-keyboard-%.xml.template
+ sed -i -e '1 D' -e 's/^<name/ <name/' $<
+ sed \
+ -e '/<_name>/r $<' \
+ -e '/<_name>/a\ ' \
+ -e '/<_name>/D' \
+ $(srcdir)/$@.template > $@
+
+hangul-keyboard-%.name.xml.in: hangul-keyboard-%.xml.template
+ grep "xml version=" $< > $@
+ grep "<_name>" $< >> $@
+
+CLEANFILES = \
+ hangul-keyboard-2.xml \
+ hangul-keyboard-2y.xml \
+ hangul-keyboard-39.xml \
+ hangul-keyboard-3f.xml \
+ hangul-keyboard-32.xml \
+ hangul-keyboard-3s.xml \
+ hangul-keyboard-3y.xml \
+ hangul-keyboard-ro.xml \
+ hangul-keyboard-ahn.xml \
+ $(NULL)
+
+@INTLTOOL_XML_RULE@
diff --git a/data/keyboards/hangul-combination-default.xml b/data/keyboards/hangul-combination-default.xml
new file mode 100644
index 0000000..53eb30d
--- /dev/null
+++ b/data/keyboards/hangul-combination-default.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<combination id="0">
+ <item first="0x1100" second="0x1100" result="0x1101"/> <!-- ᄀ + ᄀ → ᄁ -->
+ <item first="0x1100" second="0x1109" result="0x11aa"/> <!-- ᄀ + ᄉ → ᆪ -->
+ <item first="0x1102" second="0x110c" result="0x11ac"/> <!-- ᄂ + ᄌ → ᆬ -->
+ <item first="0x1102" second="0x1112" result="0x11ad"/> <!-- ᄂ + ᄒ → ᆭ -->
+ <item first="0x1103" second="0x1103" result="0x1104"/> <!-- ᄃ + ᄃ → ᄄ -->
+ <item first="0x1105" second="0x1100" result="0x11b0"/> <!-- ᄅ + ᄀ → ᆰ -->
+ <item first="0x1105" second="0x1106" result="0x11b1"/> <!-- ᄅ + ᄆ → ᆱ -->
+ <item first="0x1105" second="0x1107" result="0x11b2"/> <!-- ᄅ + ᄇ → ᆲ -->
+ <item first="0x1105" second="0x1109" result="0x11b3"/> <!-- ᄅ + ᄉ → ᆳ -->
+ <item first="0x1105" second="0x1110" result="0x11b4"/> <!-- ᄅ + ᄐ → ᆴ -->
+ <item first="0x1105" second="0x1111" result="0x11b5"/> <!-- ᄅ + ᄑ → ᆵ -->
+ <item first="0x1105" second="0x1112" result="0x11b6"/> <!-- ᄅ + ᄒ → ᆶ -->
+ <item first="0x1107" second="0x1107" result="0x1108"/> <!-- ᄇ + ᄇ → ᄈ -->
+ <item first="0x1107" second="0x1109" result="0x11b9"/> <!-- ᄇ + ᄉ → ᆹ -->
+ <item first="0x1109" second="0x1109" result="0x110a"/> <!-- ᄉ + ᄉ → ᄊ -->
+ <item first="0x110c" second="0x110c" result="0x110d"/> <!-- ᄌ + ᄌ → ᄍ -->
+ <item first="0x1169" second="0x1161" result="0x116a"/> <!-- ᅩ + ᅡ → ᅪ -->
+ <item first="0x1169" second="0x1162" result="0x116b"/> <!-- ᅩ + ᅢ → ᅫ -->
+ <item first="0x1169" second="0x1175" result="0x116c"/> <!-- ᅩ + ᅵ → ᅬ -->
+ <item first="0x116e" second="0x1165" result="0x116f"/> <!-- ᅮ + ᅥ → ᅯ -->
+ <item first="0x116e" second="0x1166" result="0x1170"/> <!-- ᅮ + ᅦ → ᅰ -->
+ <item first="0x116e" second="0x1175" result="0x1171"/> <!-- ᅮ + ᅵ → ᅱ -->
+ <item first="0x1173" second="0x1175" result="0x1174"/> <!-- ᅳ + ᅵ → ᅴ -->
+ <item first="0x11a8" second="0x11a8" result="0x11a9"/> <!-- ᆨ + ᆨ → ᆩ -->
+ <item first="0x11a8" second="0x11ba" result="0x11aa"/> <!-- ᆨ + ᆺ → ᆪ -->
+ <item first="0x11ab" second="0x11bd" result="0x11ac"/> <!-- ᆫ + ᆽ → ᆬ -->
+ <item first="0x11ab" second="0x11c2" result="0x11ad"/> <!-- ᆫ + ᇂ → ᆭ -->
+ <item first="0x11af" second="0x11a8" result="0x11b0"/> <!-- ᆯ + ᆨ → ᆰ -->
+ <item first="0x11af" second="0x11b7" result="0x11b1"/> <!-- ᆯ + ᆷ → ᆱ -->
+ <item first="0x11af" second="0x11b8" result="0x11b2"/> <!-- ᆯ + ᆸ → ᆲ -->
+ <item first="0x11af" second="0x11ba" result="0x11b3"/> <!-- ᆯ + ᆺ → ᆳ -->
+ <item first="0x11af" second="0x11c0" result="0x11b4"/> <!-- ᆯ + ᇀ → ᆴ -->
+ <item first="0x11af" second="0x11c1" result="0x11b5"/> <!-- ᆯ + ᇁ → ᆵ -->
+ <item first="0x11af" second="0x11c2" result="0x11b6"/> <!-- ᆯ + ᇂ → ᆶ -->
+ <item first="0x11b8" second="0x11ba" result="0x11b9"/> <!-- ᆸ + ᆺ → ᆹ -->
+ <item first="0x11ba" second="0x11ba" result="0x11bb"/> <!-- ᆺ + ᆺ → ᆻ -->
+</combination>
diff --git a/data/keyboards/hangul-combination-full.xml b/data/keyboards/hangul-combination-full.xml
new file mode 100644
index 0000000..318d7ec
--- /dev/null
+++ b/data/keyboards/hangul-combination-full.xml
@@ -0,0 +1,356 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<combination id="0">
+ <item first="0x1100" second="0x1100" result="0x1101"/> <!-- ᄀ + ᄀ → ᄁ -->
+ <item first="0x1100" second="0x1103" result="0x115a"/> <!-- ᄀ + ᄃ → ᅚ -->
+ <item first="0x1102" second="0x1100" result="0x1113"/> <!-- ᄂ + ᄀ → ᄓ -->
+ <item first="0x1102" second="0x1102" result="0x1114"/> <!-- ᄂ + ᄂ → ᄔ -->
+ <item first="0x1102" second="0x1103" result="0x1115"/> <!-- ᄂ + ᄃ → ᄕ -->
+ <item first="0x1102" second="0x1107" result="0x1116"/> <!-- ᄂ + ᄇ → ᄖ -->
+ <item first="0x1102" second="0x1109" result="0x115b"/> <!-- ᄂ + ᄉ → ᅛ -->
+ <item first="0x1102" second="0x110c" result="0x115c"/> <!-- ᄂ + ᄌ → ᅜ -->
+ <item first="0x1102" second="0x1112" result="0x115d"/> <!-- ᄂ + ᄒ → ᅝ -->
+ <item first="0x1103" second="0x1100" result="0x1117"/> <!-- ᄃ + ᄀ → ᄗ -->
+ <item first="0x1103" second="0x1103" result="0x1104"/> <!-- ᄃ + ᄃ → ᄄ -->
+ <item first="0x1103" second="0x1105" result="0x115e"/> <!-- ᄃ + ᄅ → ᅞ -->
+ <item first="0x1103" second="0x1106" result="0xa960"/> <!-- ᄃ + ᄆ → ꥠ -->
+ <item first="0x1103" second="0x1107" result="0xa961"/> <!-- ᄃ + ᄇ → ꥡ -->
+ <item first="0x1103" second="0x1109" result="0xa962"/> <!-- ᄃ + ᄉ → ꥢ -->
+ <item first="0x1103" second="0x110c" result="0xa963"/> <!-- ᄃ + ᄌ → ꥣ -->
+ <item first="0x1105" second="0x1100" result="0xa964"/> <!-- ᄅ + ᄀ → ꥤ -->
+ <item first="0x1105" second="0x1101" result="0xa965"/> <!-- ᄅ + ᄁ → ꥥ -->
+ <item first="0x1105" second="0x1102" result="0x1118"/> <!-- ᄅ + ᄂ → ᄘ -->
+ <item first="0x1105" second="0x1103" result="0xa966"/> <!-- ᄅ + ᄃ → ꥦ -->
+ <item first="0x1105" second="0x1104" result="0xa967"/> <!-- ᄅ + ᄄ → ꥧ -->
+ <item first="0x1105" second="0x1105" result="0x1119"/> <!-- ᄅ + ᄅ → ᄙ -->
+ <item first="0x1105" second="0x1106" result="0xa968"/> <!-- ᄅ + ᄆ → ꥨ -->
+ <item first="0x1105" second="0x1107" result="0xa969"/> <!-- ᄅ + ᄇ → ꥩ -->
+ <item first="0x1105" second="0x1108" result="0xa96a"/> <!-- ᄅ + ᄈ → ꥪ -->
+ <item first="0x1105" second="0x1109" result="0xa96c"/> <!-- ᄅ + ᄉ → ꥬ -->
+ <item first="0x1105" second="0x110b" result="0x111b"/> <!-- ᄅ + ᄋ → ᄛ -->
+ <item first="0x1105" second="0x110c" result="0xa96d"/> <!-- ᄅ + ᄌ → ꥭ -->
+ <item first="0x1105" second="0x110f" result="0xa96e"/> <!-- ᄅ + ᄏ → ꥮ -->
+ <item first="0x1105" second="0x1112" result="0x111a"/> <!-- ᄅ + ᄒ → ᄚ -->
+ <item first="0x1105" second="0x112b" result="0xa96b"/> <!-- ᄅ + ᄫ → ꥫ -->
+ <item first="0x1106" second="0x1100" result="0xa96f"/> <!-- ᄆ + ᄀ → ꥯ -->
+ <item first="0x1106" second="0x1103" result="0xa970"/> <!-- ᄆ + ᄃ → ꥰ -->
+ <item first="0x1106" second="0x1107" result="0x111c"/> <!-- ᄆ + ᄇ → ᄜ -->
+ <item first="0x1106" second="0x1109" result="0xa971"/> <!-- ᄆ + ᄉ → ꥱ -->
+ <item first="0x1106" second="0x110b" result="0x111d"/> <!-- ᄆ + ᄋ → ᄝ -->
+ <item first="0x1107" second="0x1100" result="0x111e"/> <!-- ᄇ + ᄀ → ᄞ -->
+ <item first="0x1107" second="0x1102" result="0x111f"/> <!-- ᄇ + ᄂ → ᄟ -->
+ <item first="0x1107" second="0x1103" result="0x1120"/> <!-- ᄇ + ᄃ → ᄠ -->
+ <item first="0x1107" second="0x1107" result="0x1108"/> <!-- ᄇ + ᄇ → ᄈ -->
+ <item first="0x1107" second="0x1109" result="0x1121"/> <!-- ᄇ + ᄉ → ᄡ -->
+ <item first="0x1107" second="0x110a" result="0x1125"/> <!-- ᄇ + ᄊ → ᄥ -->
+ <item first="0x1107" second="0x110b" result="0x112b"/> <!-- ᄇ + ᄋ → ᄫ -->
+ <item first="0x1107" second="0x110c" result="0x1127"/> <!-- ᄇ + ᄌ → ᄧ -->
+ <item first="0x1107" second="0x110e" result="0x1128"/> <!-- ᄇ + ᄎ → ᄨ -->
+ <item first="0x1107" second="0x110f" result="0xa973"/> <!-- ᄇ + ᄏ → ꥳ -->
+ <item first="0x1107" second="0x1110" result="0x1129"/> <!-- ᄇ + ᄐ → ᄩ -->
+ <item first="0x1107" second="0x1111" result="0x112a"/> <!-- ᄇ + ᄑ → ᄪ -->
+ <item first="0x1107" second="0x1112" result="0xa974"/> <!-- ᄇ + ᄒ → ꥴ -->
+ <item first="0x1107" second="0x112b" result="0x112c"/> <!-- ᄇ + ᄫ → ᄬ -->
+ <item first="0x1107" second="0x112d" result="0x1122"/> <!-- ᄇ + ᄭ → ᄢ -->
+ <item first="0x1107" second="0x112f" result="0x1123"/> <!-- ᄇ + ᄯ → ᄣ -->
+ <item first="0x1107" second="0x1132" result="0x1124"/> <!-- ᄇ + ᄲ → ᄤ -->
+ <item first="0x1107" second="0x1136" result="0x1126"/> <!-- ᄇ + ᄶ → ᄦ -->
+ <item first="0x1107" second="0x1139" result="0xa972"/> <!-- ᄇ + ᄹ → ꥲ -->
+ <item first="0x1108" second="0x110b" result="0x112c"/> <!-- ᄈ + ᄋ → ᄬ -->
+ <item first="0x1109" second="0x1100" result="0x112d"/> <!-- ᄉ + ᄀ → ᄭ -->
+ <item first="0x1109" second="0x1102" result="0x112e"/> <!-- ᄉ + ᄂ → ᄮ -->
+ <item first="0x1109" second="0x1103" result="0x112f"/> <!-- ᄉ + ᄃ → ᄯ -->
+ <item first="0x1109" second="0x1105" result="0x1130"/> <!-- ᄉ + ᄅ → ᄰ -->
+ <item first="0x1109" second="0x1106" result="0x1131"/> <!-- ᄉ + ᄆ → ᄱ -->
+ <item first="0x1109" second="0x1107" result="0x1132"/> <!-- ᄉ + ᄇ → ᄲ -->
+ <item first="0x1109" second="0x1109" result="0x110a"/> <!-- ᄉ + ᄉ → ᄊ -->
+ <item first="0x1109" second="0x110a" result="0x1134"/> <!-- ᄉ + ᄊ → ᄴ -->
+ <item first="0x1109" second="0x110b" result="0x1135"/> <!-- ᄉ + ᄋ → ᄵ -->
+ <item first="0x1109" second="0x110c" result="0x1136"/> <!-- ᄉ + ᄌ → ᄶ -->
+ <item first="0x1109" second="0x110e" result="0x1137"/> <!-- ᄉ + ᄎ → ᄷ -->
+ <item first="0x1109" second="0x110f" result="0x1138"/> <!-- ᄉ + ᄏ → ᄸ -->
+ <item first="0x1109" second="0x1110" result="0x1139"/> <!-- ᄉ + ᄐ → ᄹ -->
+ <item first="0x1109" second="0x1111" result="0x113a"/> <!-- ᄉ + ᄑ → ᄺ -->
+ <item first="0x1109" second="0x1112" result="0x113b"/> <!-- ᄉ + ᄒ → ᄻ -->
+ <item first="0x1109" second="0x111e" result="0x1133"/> <!-- ᄉ + ᄞ → ᄳ -->
+ <item first="0x1109" second="0x1132" result="0xa975"/> <!-- ᄉ + ᄲ → ꥵ -->
+ <item first="0x110a" second="0x1107" result="0xa975"/> <!-- ᄊ + ᄇ → ꥵ -->
+ <item first="0x110a" second="0x1109" result="0x1134"/> <!-- ᄊ + ᄉ → ᄴ -->
+ <item first="0x110b" second="0x1100" result="0x1141"/> <!-- ᄋ + ᄀ → ᅁ -->
+ <item first="0x110b" second="0x1103" result="0x1142"/> <!-- ᄋ + ᄃ → ᅂ -->
+ <item first="0x110b" second="0x1105" result="0xa976"/> <!-- ᄋ + ᄅ → ꥶ -->
+ <item first="0x110b" second="0x1106" result="0x1143"/> <!-- ᄋ + ᄆ → ᅃ -->
+ <item first="0x110b" second="0x1107" result="0x1144"/> <!-- ᄋ + ᄇ → ᅄ -->
+ <item first="0x110b" second="0x1109" result="0x1145"/> <!-- ᄋ + ᄉ → ᅅ -->
+ <item first="0x110b" second="0x110b" result="0x1147"/> <!-- ᄋ + ᄋ → ᅇ -->
+ <item first="0x110b" second="0x110c" result="0x1148"/> <!-- ᄋ + ᄌ → ᅈ -->
+ <item first="0x110b" second="0x110e" result="0x1149"/> <!-- ᄋ + ᄎ → ᅉ -->
+ <item first="0x110b" second="0x1110" result="0x114a"/> <!-- ᄋ + ᄐ → ᅊ -->
+ <item first="0x110b" second="0x1111" result="0x114b"/> <!-- ᄋ + ᄑ → ᅋ -->
+ <item first="0x110b" second="0x1112" result="0xa977"/> <!-- ᄋ + ᄒ → ꥷ -->
+ <item first="0x110b" second="0x1140" result="0x1146"/> <!-- ᄋ + ᅀ → ᅆ -->
+ <item first="0x110c" second="0x110b" result="0x114d"/> <!-- ᄌ + ᄋ → ᅍ -->
+ <item first="0x110c" second="0x110c" result="0x110d"/> <!-- ᄌ + ᄌ → ᄍ -->
+ <item first="0x110d" second="0x1112" result="0xa978"/> <!-- ᄍ + ᄒ → ꥸ -->
+ <item first="0x110e" second="0x110f" result="0x1152"/> <!-- ᄎ + ᄏ → ᅒ -->
+ <item first="0x110e" second="0x1112" result="0x1153"/> <!-- ᄎ + ᄒ → ᅓ -->
+ <item first="0x1110" second="0x1110" result="0xa979"/> <!-- ᄐ + ᄐ → ꥹ -->
+ <item first="0x1111" second="0x1107" result="0x1156"/> <!-- ᄑ + ᄇ → ᅖ -->
+ <item first="0x1111" second="0x110b" result="0x1157"/> <!-- ᄑ + ᄋ → ᅗ -->
+ <item first="0x1111" second="0x1112" result="0xa97a"/> <!-- ᄑ + ᄒ → ꥺ -->
+ <item first="0x1112" second="0x1109" result="0xa97b"/> <!-- ᄒ + ᄉ → ꥻ -->
+ <item first="0x1112" second="0x1112" result="0x1158"/> <!-- ᄒ + ᄒ → ᅘ -->
+ <item first="0x1121" second="0x1100" result="0x1122"/> <!-- ᄡ + ᄀ → ᄢ -->
+ <item first="0x1121" second="0x1103" result="0x1123"/> <!-- ᄡ + ᄃ → ᄣ -->
+ <item first="0x1121" second="0x1107" result="0x1124"/> <!-- ᄡ + ᄇ → ᄤ -->
+ <item first="0x1121" second="0x1109" result="0x1125"/> <!-- ᄡ + ᄉ → ᄥ -->
+ <item first="0x1121" second="0x110c" result="0x1126"/> <!-- ᄡ + ᄌ → ᄦ -->
+ <item first="0x1121" second="0x1110" result="0xa972"/> <!-- ᄡ + ᄐ → ꥲ -->
+ <item first="0x1132" second="0x1100" result="0x1133"/> <!-- ᄲ + ᄀ → ᄳ -->
+ <item first="0x113c" second="0x113c" result="0x113d"/> <!-- ᄼ + ᄼ → ᄽ -->
+ <item first="0x113e" second="0x113e" result="0x113f"/> <!-- ᄾ + ᄾ → ᄿ -->
+ <item first="0x114e" second="0x114e" result="0x114f"/> <!-- ᅎ + ᅎ → ᅏ -->
+ <item first="0x1150" second="0x1150" result="0x1151"/> <!-- ᅐ + ᅐ → ᅑ -->
+ <item first="0x1159" second="0x1159" result="0xa97c"/> <!-- ᅙ + ᅙ → ꥼ -->
+ <item first="0x1161" second="0x1161" result="0x119e"/> <!-- ᅡ + ᅡ → ᆞ -->
+ <item first="0x1161" second="0x1169" result="0x1176"/> <!-- ᅡ + ᅩ → ᅶ -->
+ <item first="0x1161" second="0x116e" result="0x1177"/> <!-- ᅡ + ᅮ → ᅷ -->
+ <item first="0x1161" second="0x1173" result="0x11a3"/> <!-- ᅡ + ᅳ → ᆣ -->
+ <item first="0x1161" second="0x1175" result="0x1162"/> <!-- ᅡ + ᅵ → ᅢ -->
+ <item first="0x1163" second="0x1169" result="0x1178"/> <!-- ᅣ + ᅩ → ᅸ -->
+ <item first="0x1163" second="0x116d" result="0x1179"/> <!-- ᅣ + ᅭ → ᅹ -->
+ <item first="0x1163" second="0x116e" result="0x11a4"/> <!-- ᅣ + ᅮ → ᆤ -->
+ <item first="0x1163" second="0x1175" result="0x1164"/> <!-- ᅣ + ᅵ → ᅤ -->
+ <item first="0x1165" second="0x1169" result="0x117a"/> <!-- ᅥ + ᅩ → ᅺ -->
+ <item first="0x1165" second="0x116e" result="0x117b"/> <!-- ᅥ + ᅮ → ᅻ -->
+ <item first="0x1165" second="0x1173" result="0x117c"/> <!-- ᅥ + ᅳ → ᅼ -->
+ <item first="0x1165" second="0x1175" result="0x1166"/> <!-- ᅥ + ᅵ → ᅦ -->
+ <item first="0x1167" second="0x1163" result="0x11a5"/> <!-- ᅧ + ᅣ → ᆥ -->
+ <item first="0x1167" second="0x1169" result="0x117d"/> <!-- ᅧ + ᅩ → ᅽ -->
+ <item first="0x1167" second="0x116e" result="0x117e"/> <!-- ᅧ + ᅮ → ᅾ -->
+ <item first="0x1167" second="0x1175" result="0x1168"/> <!-- ᅧ + ᅵ → ᅨ -->
+ <item first="0x1169" second="0x1161" result="0x116a"/> <!-- ᅩ + ᅡ → ᅪ -->
+ <item first="0x1169" second="0x1162" result="0x116b"/> <!-- ᅩ + ᅢ → ᅫ -->
+ <item first="0x1169" second="0x1163" result="0x11a6"/> <!-- ᅩ + ᅣ → ᆦ -->
+ <item first="0x1169" second="0x1164" result="0x11a7"/> <!-- ᅩ + ᅤ → ᆧ -->
+ <item first="0x1169" second="0x1165" result="0x117f"/> <!-- ᅩ + ᅥ → ᅿ -->
+ <item first="0x1169" second="0x1166" result="0x1180"/> <!-- ᅩ + ᅦ → ᆀ -->
+ <item first="0x1169" second="0x1167" result="0xd7b0"/> <!-- ᅩ + ᅧ → ힰ -->
+ <item first="0x1169" second="0x1168" result="0x1181"/> <!-- ᅩ + ᅨ → ᆁ -->
+ <item first="0x1169" second="0x1169" result="0x1182"/> <!-- ᅩ + ᅩ → ᆂ -->
+ <item first="0x1169" second="0x116e" result="0x1183"/> <!-- ᅩ + ᅮ → ᆃ -->
+ <item first="0x1169" second="0x1175" result="0x116c"/> <!-- ᅩ + ᅵ → ᅬ -->
+ <item first="0x116a" second="0x1175" result="0x116b"/> <!-- ᅪ + ᅵ → ᅫ -->
+ <item first="0x116d" second="0x1161" result="0xd7b2"/> <!-- ᅭ + ᅡ → ힲ -->
+ <item first="0x116d" second="0x1162" result="0xd7b3"/> <!-- ᅭ + ᅢ → ힳ -->
+ <item first="0x116d" second="0x1163" result="0x1184"/> <!-- ᅭ + ᅣ → ᆄ -->
+ <item first="0x116d" second="0x1164" result="0x1185"/> <!-- ᅭ + ᅤ → ᆅ -->
+ <item first="0x116d" second="0x1165" result="0xd7b4"/> <!-- ᅭ + ᅥ → ힴ -->
+ <item first="0x116d" second="0x1167" result="0x1186"/> <!-- ᅭ + ᅧ → ᆆ -->
+ <item first="0x116d" second="0x1169" result="0x1187"/> <!-- ᅭ + ᅩ → ᆇ -->
+ <item first="0x116d" second="0x1175" result="0x1188"/> <!-- ᅭ + ᅵ → ᆈ -->
+ <item first="0x116e" second="0x1161" result="0x1189"/> <!-- ᅮ + ᅡ → ᆉ -->
+ <item first="0x116e" second="0x1162" result="0x118a"/> <!-- ᅮ + ᅢ → ᆊ -->
+ <item first="0x116e" second="0x1165" result="0x116f"/> <!-- ᅮ + ᅥ → ᅯ -->
+ <item first="0x116e" second="0x1166" result="0x1170"/> <!-- ᅮ + ᅦ → ᅰ -->
+ <item first="0x116e" second="0x1167" result="0xd7b5"/> <!-- ᅮ + ᅧ → ힵ -->
+ <item first="0x116e" second="0x1168" result="0x118c"/> <!-- ᅮ + ᅨ → ᆌ -->
+ <item first="0x116e" second="0x116e" result="0x118d"/> <!-- ᅮ + ᅮ → ᆍ -->
+ <item first="0x116e" second="0x1175" result="0x1171"/> <!-- ᅮ + ᅵ → ᅱ -->
+ <item first="0x116e" second="0x117c" result="0x118b"/> <!-- ᅮ + ᅼ → ᆋ -->
+ <item first="0x116e" second="0xd7c4" result="0xd7b6"/> <!-- ᅮ + ퟄ → ힶ -->
+ <item first="0x116f" second="0x1173" result="0x118b"/> <!-- ᅯ + ᅳ → ᆋ -->
+ <item first="0x116f" second="0x1175" result="0x1170"/> <!-- ᅯ + ᅵ → ᅰ -->
+ <item first="0x1171" second="0x1175" result="0xd7b6"/> <!-- ᅱ + ᅵ → ힶ -->
+ <item first="0x1172" second="0x1161" result="0x118e"/> <!-- ᅲ + ᅡ → ᆎ -->
+ <item first="0x1172" second="0x1162" result="0xd7b7"/> <!-- ᅲ + ᅢ → ힷ -->
+ <item first="0x1172" second="0x1165" result="0x118f"/> <!-- ᅲ + ᅥ → ᆏ -->
+ <item first="0x1172" second="0x1166" result="0x1190"/> <!-- ᅲ + ᅦ → ᆐ -->
+ <item first="0x1172" second="0x1167" result="0x1191"/> <!-- ᅲ + ᅧ → ᆑ -->
+ <item first="0x1172" second="0x1168" result="0x1192"/> <!-- ᅲ + ᅨ → ᆒ -->
+ <item first="0x1172" second="0x1169" result="0xd7b8"/> <!-- ᅲ + ᅩ → ힸ -->
+ <item first="0x1172" second="0x116e" result="0x1193"/> <!-- ᅲ + ᅮ → ᆓ -->
+ <item first="0x1172" second="0x1175" result="0x1194"/> <!-- ᅲ + ᅵ → ᆔ -->
+ <item first="0x1173" second="0x1161" result="0xd7b9"/> <!-- ᅳ + ᅡ → ힹ -->
+ <item first="0x1173" second="0x1165" result="0xd7ba"/> <!-- ᅳ + ᅥ → ힺ -->
+ <item first="0x1173" second="0x1166" result="0xd7bb"/> <!-- ᅳ + ᅦ → ힻ -->
+ <item first="0x1173" second="0x1169" result="0xd7bc"/> <!-- ᅳ + ᅩ → ힼ -->
+ <item first="0x1173" second="0x116e" result="0x1195"/> <!-- ᅳ + ᅮ → ᆕ -->
+ <item first="0x1173" second="0x1173" result="0x1196"/> <!-- ᅳ + ᅳ → ᆖ -->
+ <item first="0x1173" second="0x1175" result="0x1174"/> <!-- ᅳ + ᅵ → ᅴ -->
+ <item first="0x1174" second="0x116e" result="0x1197"/> <!-- ᅴ + ᅮ → ᆗ -->
+ <item first="0x1175" second="0x1161" result="0x1198"/> <!-- ᅵ + ᅡ → ᆘ -->
+ <item first="0x1175" second="0x1163" result="0x1199"/> <!-- ᅵ + ᅣ → ᆙ -->
+ <item first="0x1175" second="0x1164" result="0xd7be"/> <!-- ᅵ + ᅤ → ힾ -->
+ <item first="0x1175" second="0x1167" result="0xd7bf"/> <!-- ᅵ + ᅧ → ힿ -->
+ <item first="0x1175" second="0x1168" result="0xd7c0"/> <!-- ᅵ + ᅨ → ퟀ -->
+ <item first="0x1175" second="0x1169" result="0x119a"/> <!-- ᅵ + ᅩ → ᆚ -->
+ <item first="0x1175" second="0x116d" result="0xd7c2"/> <!-- ᅵ + ᅭ → ퟂ -->
+ <item first="0x1175" second="0x116e" result="0x119b"/> <!-- ᅵ + ᅮ → ᆛ -->
+ <item first="0x1175" second="0x1172" result="0xd7c3"/> <!-- ᅵ + ᅲ → ퟃ -->
+ <item first="0x1175" second="0x1173" result="0x119c"/> <!-- ᅵ + ᅳ → ᆜ -->
+ <item first="0x1175" second="0x1175" result="0xd7c4"/> <!-- ᅵ + ᅵ → ퟄ -->
+ <item first="0x1175" second="0x1178" result="0xd7bd"/> <!-- ᅵ + ᅸ → ힽ -->
+ <item first="0x1175" second="0x119e" result="0x119d"/> <!-- ᅵ + ᆞ → ᆝ -->
+ <item first="0x1182" second="0x1175" result="0xd7b1"/> <!-- ᆂ + ᅵ → ힱ -->
+ <item first="0x1199" second="0x1169" result="0xd7bd"/> <!-- ᆙ + ᅩ → ힽ -->
+ <item first="0x119a" second="0x1175" result="0xd7c1"/> <!-- ᆚ + ᅵ → ퟁ -->
+ <item first="0x119e" second="0x1161" result="0xd7c5"/> <!-- ᆞ + ᅡ → ퟅ -->
+ <item first="0x119e" second="0x1165" result="0x119f"/> <!-- ᆞ + ᅥ → ᆟ -->
+ <item first="0x119e" second="0x1166" result="0xd7c6"/> <!-- ᆞ + ᅦ → ퟆ -->
+ <item first="0x119e" second="0x116e" result="0x11a0"/> <!-- ᆞ + ᅮ → ᆠ -->
+ <item first="0x119e" second="0x1175" result="0x11a1"/> <!-- ᆞ + ᅵ → ᆡ -->
+ <item first="0x119e" second="0x119e" result="0x11a2"/> <!-- ᆞ + ᆞ → ᆢ -->
+ <item first="0x11a8" second="0x11a8" result="0x11a9"/> <!-- ᆨ + ᆨ → ᆩ -->
+ <item first="0x11a8" second="0x11ab" result="0x11fa"/> <!-- ᆨ + ᆫ → ᇺ -->
+ <item first="0x11a8" second="0x11af" result="0x11c3"/> <!-- ᆨ + ᆯ → ᇃ -->
+ <item first="0x11a8" second="0x11b8" result="0x11fb"/> <!-- ᆨ + ᆸ → ᇻ -->
+ <item first="0x11a8" second="0x11ba" result="0x11aa"/> <!-- ᆨ + ᆺ → ᆪ -->
+ <item first="0x11a8" second="0x11be" result="0x11fc"/> <!-- ᆨ + ᆾ → ᇼ -->
+ <item first="0x11a8" second="0x11bf" result="0x11fd"/> <!-- ᆨ + ᆿ → ᇽ -->
+ <item first="0x11a8" second="0x11c2" result="0x11fe"/> <!-- ᆨ + ᇂ → ᇾ -->
+ <item first="0x11a8" second="0x11e7" result="0x11c4"/> <!-- ᆨ + ᇧ → ᇄ -->
+ <item first="0x11aa" second="0x11a8" result="0x11c4"/> <!-- ᆪ + ᆨ → ᇄ -->
+ <item first="0x11ab" second="0x11a8" result="0x11c5"/> <!-- ᆫ + ᆨ → ᇅ -->
+ <item first="0x11ab" second="0x11ab" result="0x11ff"/> <!-- ᆫ + ᆫ → ᇿ -->
+ <item first="0x11ab" second="0x11ae" result="0x11c6"/> <!-- ᆫ + ᆮ → ᇆ -->
+ <item first="0x11ab" second="0x11af" result="0xd7cb"/> <!-- ᆫ + ᆯ → ퟋ -->
+ <item first="0x11ab" second="0x11ba" result="0x11c7"/> <!-- ᆫ + ᆺ → ᇇ -->
+ <item first="0x11ab" second="0x11bd" result="0x11ac"/> <!-- ᆫ + ᆽ → ᆬ -->
+ <item first="0x11ab" second="0x11be" result="0xd7cc"/> <!-- ᆫ + ᆾ → ퟌ -->
+ <item first="0x11ab" second="0x11c0" result="0x11c9"/> <!-- ᆫ + ᇀ → ᇉ -->
+ <item first="0x11ab" second="0x11c2" result="0x11ad"/> <!-- ᆫ + ᇂ → ᆭ -->
+ <item first="0x11ab" second="0x11eb" result="0x11c8"/> <!-- ᆫ + ᇫ → ᇈ -->
+ <item first="0x11ae" second="0x11a8" result="0x11ca"/> <!-- ᆮ + ᆨ → ᇊ -->
+ <item first="0x11ae" second="0x11ae" result="0xd7cd"/> <!-- ᆮ + ᆮ → ퟍ -->
+ <item first="0x11ae" second="0x11af" result="0x11cb"/> <!-- ᆮ + ᆯ → ᇋ -->
+ <item first="0x11ae" second="0x11b8" result="0xd7cf"/> <!-- ᆮ + ᆸ → ퟏ -->
+ <item first="0x11ae" second="0x11ba" result="0xd7d0"/> <!-- ᆮ + ᆺ → ퟐ -->
+ <item first="0x11ae" second="0x11bd" result="0xd7d2"/> <!-- ᆮ + ᆽ → ퟒ -->
+ <item first="0x11ae" second="0x11be" result="0xd7d3"/> <!-- ᆮ + ᆾ → ퟓ -->
+ <item first="0x11ae" second="0x11c0" result="0xd7d4"/> <!-- ᆮ + ᇀ → ퟔ -->
+ <item first="0x11ae" second="0x11e7" result="0xd7d1"/> <!-- ᆮ + ᇧ → ퟑ -->
+ <item first="0x11ae" second="0xd7cf" result="0xd7ce"/> <!-- ᆮ + ퟏ → ퟎ -->
+ <item first="0x11af" second="0x11a8" result="0x11b0"/> <!-- ᆯ + ᆨ → ᆰ -->
+ <item first="0x11af" second="0x11a9" result="0xd7d5"/> <!-- ᆯ + ᆩ → ퟕ -->
+ <item first="0x11af" second="0x11aa" result="0x11cc"/> <!-- ᆯ + ᆪ → ᇌ -->
+ <item first="0x11af" second="0x11ab" result="0x11cd"/> <!-- ᆯ + ᆫ → ᇍ -->
+ <item first="0x11af" second="0x11ae" result="0x11ce"/> <!-- ᆯ + ᆮ → ᇎ -->
+ <item first="0x11af" second="0x11af" result="0x11d0"/> <!-- ᆯ + ᆯ → ᇐ -->
+ <item first="0x11af" second="0x11b7" result="0x11b1"/> <!-- ᆯ + ᆷ → ᆱ -->
+ <item first="0x11af" second="0x11b8" result="0x11b2"/> <!-- ᆯ + ᆸ → ᆲ -->
+ <item first="0x11af" second="0x11b9" result="0x11d3"/> <!-- ᆯ + ᆹ → ᇓ -->
+ <item first="0x11af" second="0x11ba" result="0x11b3"/> <!-- ᆯ + ᆺ → ᆳ -->
+ <item first="0x11af" second="0x11bb" result="0x11d6"/> <!-- ᆯ + ᆻ → ᇖ -->
+ <item first="0x11af" second="0x11bc" result="0xd7dd"/> <!-- ᆯ + ᆼ → ퟝ -->
+ <item first="0x11af" second="0x11bf" result="0x11d8"/> <!-- ᆯ + ᆿ → ᇘ -->
+ <item first="0x11af" second="0x11c0" result="0x11b4"/> <!-- ᆯ + ᇀ → ᆴ -->
+ <item first="0x11af" second="0x11c1" result="0x11b5"/> <!-- ᆯ + ᇁ → ᆵ -->
+ <item first="0x11af" second="0x11c2" result="0x11b6"/> <!-- ᆯ + ᇂ → ᆶ -->
+ <item first="0x11af" second="0x11d8" result="0xd7d7"/> <!-- ᆯ + ᇘ → ퟗ -->
+ <item first="0x11af" second="0x11da" result="0x11d1"/> <!-- ᆯ + ᇚ → ᇑ -->
+ <item first="0x11af" second="0x11dd" result="0x11d2"/> <!-- ᆯ + ᇝ → ᇒ -->
+ <item first="0x11af" second="0x11e1" result="0xd7d8"/> <!-- ᆯ + ᇡ → ퟘ -->
+ <item first="0x11af" second="0x11e4" result="0xd7da"/> <!-- ᆯ + ᇤ → ퟚ -->
+ <item first="0x11af" second="0x11e5" result="0x11d4"/> <!-- ᆯ + ᇥ → ᇔ -->
+ <item first="0x11af" second="0x11e6" result="0x11d5"/> <!-- ᆯ + ᇦ → ᇕ -->
+ <item first="0x11af" second="0x11eb" result="0x11d7"/> <!-- ᆯ + ᇫ → ᇗ -->
+ <item first="0x11af" second="0x11f0" result="0xd7db"/> <!-- ᆯ + ᇰ → ퟛ -->
+ <item first="0x11af" second="0x11f9" result="0x11d9"/> <!-- ᆯ + ᇹ → ᇙ -->
+ <item first="0x11af" second="0x11fe" result="0xd7d6"/> <!-- ᆯ + ᇾ → ퟖ -->
+ <item first="0x11af" second="0xd7e3" result="0xd7d9"/> <!-- ᆯ + ퟣ → ퟙ -->
+ <item first="0x11b0" second="0x11a8" result="0xd7d5"/> <!-- ᆰ + ᆨ → ퟕ -->
+ <item first="0x11b0" second="0x11ba" result="0x11cc"/> <!-- ᆰ + ᆺ → ᇌ -->
+ <item first="0x11b0" second="0x11c2" result="0xd7d6"/> <!-- ᆰ + ᇂ → ퟖ -->
+ <item first="0x11b1" second="0x11a8" result="0x11d1"/> <!-- ᆱ + ᆨ → ᇑ -->
+ <item first="0x11b1" second="0x11ba" result="0x11d2"/> <!-- ᆱ + ᆺ → ᇒ -->
+ <item first="0x11b1" second="0x11c2" result="0xd7d8"/> <!-- ᆱ + ᇂ → ퟘ -->
+ <item first="0x11b2" second="0x11ae" result="0xd7d9"/> <!-- ᆲ + ᆮ → ퟙ -->
+ <item first="0x11b2" second="0x11ba" result="0x11d3"/> <!-- ᆲ + ᆺ → ᇓ -->
+ <item first="0x11b2" second="0x11bc" result="0x11d5"/> <!-- ᆲ + ᆼ → ᇕ -->
+ <item first="0x11b2" second="0x11c1" result="0xd7da"/> <!-- ᆲ + ᇁ → ퟚ -->
+ <item first="0x11b2" second="0x11c2" result="0x11d4"/> <!-- ᆲ + ᇂ → ᇔ -->
+ <item first="0x11b3" second="0x11ba" result="0x11d6"/> <!-- ᆳ + ᆺ → ᇖ -->
+ <item first="0x11b7" second="0x11a8" result="0x11da"/> <!-- ᆷ + ᆨ → ᇚ -->
+ <item first="0x11b7" second="0x11ab" result="0xd7de"/> <!-- ᆷ + ᆫ → ퟞ -->
+ <item first="0x11b7" second="0x11af" result="0x11db"/> <!-- ᆷ + ᆯ → ᇛ -->
+ <item first="0x11b7" second="0x11b7" result="0xd7e0"/> <!-- ᆷ + ᆷ → ퟠ -->
+ <item first="0x11b7" second="0x11b8" result="0x11dc"/> <!-- ᆷ + ᆸ → ᇜ -->
+ <item first="0x11b7" second="0x11b9" result="0xd7e1"/> <!-- ᆷ + ᆹ → ퟡ -->
+ <item first="0x11b7" second="0x11ba" result="0x11dd"/> <!-- ᆷ + ᆺ → ᇝ -->
+ <item first="0x11b7" second="0x11bb" result="0x11de"/> <!-- ᆷ + ᆻ → ᇞ -->
+ <item first="0x11b7" second="0x11bc" result="0x11e2"/> <!-- ᆷ + ᆼ → ᇢ -->
+ <item first="0x11b7" second="0x11bd" result="0xd7e2"/> <!-- ᆷ + ᆽ → ퟢ -->
+ <item first="0x11b7" second="0x11be" result="0x11e0"/> <!-- ᆷ + ᆾ → ᇠ -->
+ <item first="0x11b7" second="0x11c2" result="0x11e1"/> <!-- ᆷ + ᇂ → ᇡ -->
+ <item first="0x11b7" second="0x11eb" result="0x11df"/> <!-- ᆷ + ᇫ → ᇟ -->
+ <item first="0x11b7" second="0x11ff" result="0xd7df"/> <!-- ᆷ + ᇿ → ퟟ -->
+ <item first="0x11b8" second="0x11ae" result="0xd7e3"/> <!-- ᆸ + ᆮ → ퟣ -->
+ <item first="0x11b8" second="0x11af" result="0x11e3"/> <!-- ᆸ + ᆯ → ᇣ -->
+ <item first="0x11b8" second="0x11b5" result="0xd7e4"/> <!-- ᆸ + ᆵ → ퟤ -->
+ <item first="0x11b8" second="0x11b7" result="0xd7e5"/> <!-- ᆸ + ᆷ → ퟥ -->
+ <item first="0x11b8" second="0x11b8" result="0xd7e6"/> <!-- ᆸ + ᆸ → ퟦ -->
+ <item first="0x11b8" second="0x11ba" result="0x11b9"/> <!-- ᆸ + ᆺ → ᆹ -->
+ <item first="0x11b8" second="0x11bc" result="0x11e6"/> <!-- ᆸ + ᆼ → ᇦ -->
+ <item first="0x11b8" second="0x11bd" result="0xd7e8"/> <!-- ᆸ + ᆽ → ퟨ -->
+ <item first="0x11b8" second="0x11be" result="0xd7e9"/> <!-- ᆸ + ᆾ → ퟩ -->
+ <item first="0x11b8" second="0x11c1" result="0x11e4"/> <!-- ᆸ + ᇁ → ᇤ -->
+ <item first="0x11b8" second="0x11c2" result="0x11e5"/> <!-- ᆸ + ᇂ → ᇥ -->
+ <item first="0x11b8" second="0x11e8" result="0xd7e7"/> <!-- ᆸ + ᇨ → ퟧ -->
+ <item first="0x11b9" second="0x11ae" result="0xd7e7"/> <!-- ᆹ + ᆮ → ퟧ -->
+ <item first="0x11ba" second="0x11a8" result="0x11e7"/> <!-- ᆺ + ᆨ → ᇧ -->
+ <item first="0x11ba" second="0x11ae" result="0x11e8"/> <!-- ᆺ + ᆮ → ᇨ -->
+ <item first="0x11ba" second="0x11af" result="0x11e9"/> <!-- ᆺ + ᆯ → ᇩ -->
+ <item first="0x11ba" second="0x11b7" result="0xd7ea"/> <!-- ᆺ + ᆷ → ퟪ -->
+ <item first="0x11ba" second="0x11b8" result="0x11ea"/> <!-- ᆺ + ᆸ → ᇪ -->
+ <item first="0x11ba" second="0x11ba" result="0x11bb"/> <!-- ᆺ + ᆺ → ᆻ -->
+ <item first="0x11ba" second="0x11bd" result="0xd7ef"/> <!-- ᆺ + ᆽ → ퟯ -->
+ <item first="0x11ba" second="0x11be" result="0xd7f0"/> <!-- ᆺ + ᆾ → ퟰ -->
+ <item first="0x11ba" second="0x11c0" result="0xd7f1"/> <!-- ᆺ + ᇀ → ퟱ -->
+ <item first="0x11ba" second="0x11c2" result="0xd7f2"/> <!-- ᆺ + ᇂ → ퟲ -->
+ <item first="0x11ba" second="0x11e6" result="0xd7eb"/> <!-- ᆺ + ᇦ → ퟫ -->
+ <item first="0x11ba" second="0x11e7" result="0xd7ec"/> <!-- ᆺ + ᇧ → ퟬ -->
+ <item first="0x11ba" second="0x11e8" result="0xd7ed"/> <!-- ᆺ + ᇨ → ퟭ -->
+ <item first="0x11ba" second="0x11eb" result="0xd7ee"/> <!-- ᆺ + ᇫ → ퟮ -->
+ <item first="0x11bb" second="0x11a8" result="0xd7ec"/> <!-- ᆻ + ᆨ → ퟬ -->
+ <item first="0x11bb" second="0x11ae" result="0xd7ed"/> <!-- ᆻ + ᆮ → ퟭ -->
+ <item first="0x11bd" second="0x11b8" result="0xd7f7"/> <!-- ᆽ + ᆸ → ퟷ -->
+ <item first="0x11bd" second="0x11bd" result="0xd7f9"/> <!-- ᆽ + ᆽ → ퟹ -->
+ <item first="0x11bd" second="0xd7e6" result="0xd7f8"/> <!-- ᆽ + ퟦ → ퟸ -->
+ <item first="0x11c1" second="0x11b8" result="0x11f3"/> <!-- ᇁ + ᆸ → ᇳ -->
+ <item first="0x11c1" second="0x11ba" result="0xd7fa"/> <!-- ᇁ + ᆺ → ퟺ -->
+ <item first="0x11c1" second="0x11bc" result="0x11f4"/> <!-- ᇁ + ᆼ → ᇴ -->
+ <item first="0x11c1" second="0x11c0" result="0xd7fb"/> <!-- ᇁ + ᇀ → ퟻ -->
+ <item first="0x11c2" second="0x11ab" result="0x11f5"/> <!-- ᇂ + ᆫ → ᇵ -->
+ <item first="0x11c2" second="0x11af" result="0x11f6"/> <!-- ᇂ + ᆯ → ᇶ -->
+ <item first="0x11c2" second="0x11b7" result="0x11f7"/> <!-- ᇂ + ᆷ → ᇷ -->
+ <item first="0x11c2" second="0x11b8" result="0x11f8"/> <!-- ᇂ + ᆸ → ᇸ -->
+ <item first="0x11ce" second="0x11c2" result="0x11cf"/> <!-- ᇎ + ᇂ → ᇏ -->
+ <item first="0x11d0" second="0x11bf" result="0xd7d7"/> <!-- ᇐ + ᆿ → ퟗ -->
+ <item first="0x11d9" second="0x11c2" result="0xd7dc"/> <!-- ᇙ + ᇂ → ퟜ -->
+ <item first="0x11dc" second="0x11ba" result="0xd7e1"/> <!-- ᇜ + ᆺ → ퟡ -->
+ <item first="0x11dd" second="0x11ba" result="0x11de"/> <!-- ᇝ + ᆺ → ᇞ -->
+ <item first="0x11e3" second="0x11c1" result="0xd7e4"/> <!-- ᇣ + ᇁ → ퟤ -->
+ <item first="0x11ea" second="0x11bc" result="0xd7eb"/> <!-- ᇪ + ᆼ → ퟫ -->
+ <item first="0x11eb" second="0x11b8" result="0xd7f3"/> <!-- ᇫ + ᆸ → ퟳ -->
+ <item first="0x11eb" second="0x11e6" result="0xd7f4"/> <!-- ᇫ + ᇦ → ퟴ -->
+ <item first="0x11ec" second="0x11a8" result="0x11ed"/> <!-- ᇬ + ᆨ → ᇭ -->
+ <item first="0x11f0" second="0x11a8" result="0x11ec"/> <!-- ᇰ + ᆨ → ᇬ -->
+ <item first="0x11f0" second="0x11a9" result="0x11ed"/> <!-- ᇰ + ᆩ → ᇭ -->
+ <item first="0x11f0" second="0x11b7" result="0xd7f5"/> <!-- ᇰ + ᆷ → ퟵ -->
+ <item first="0x11f0" second="0x11ba" result="0x11f1"/> <!-- ᇰ + ᆺ → ᇱ -->
+ <item first="0x11f0" second="0x11bf" result="0x11ef"/> <!-- ᇰ + ᆿ → ᇯ -->
+ <item first="0x11f0" second="0x11c2" result="0xd7f6"/> <!-- ᇰ + ᇂ → ퟶ -->
+ <item first="0x11f0" second="0x11eb" result="0x11f2"/> <!-- ᇰ + ᇫ → ᇲ -->
+ <item first="0x11f0" second="0x11f0" result="0x11ee"/> <!-- ᇰ + ᇰ → ᇮ -->
+ <item first="0xa964" second="0x1100" result="0xa965"/> <!-- ꥤ + ᄀ → ꥥ -->
+ <item first="0xa966" second="0x1103" result="0xa967"/> <!-- ꥦ + ᄃ → ꥧ -->
+ <item first="0xa969" second="0x1107" result="0xa96a"/> <!-- ꥩ + ᄇ → ꥪ -->
+ <item first="0xa969" second="0x110b" result="0xa96b"/> <!-- ꥩ + ᄋ → ꥫ -->
+ <item first="0xd7c5" second="0x1161" result="0x11a2"/> <!-- ퟅ + ᅡ → ᆢ -->
+ <item first="0xd7cd" second="0x11b8" result="0xd7ce"/> <!-- ퟍ + ᆸ → ퟎ -->
+ <item first="0xd7d0" second="0x11a8" result="0xd7d1"/> <!-- ퟐ + ᆨ → ퟑ -->
+ <item first="0xd7de" second="0x11ab" result="0xd7df"/> <!-- ퟞ + ᆫ → ퟟ -->
+ <item first="0xd7f3" second="0x11bc" result="0xd7f4"/> <!-- ퟳ + ᆼ → ퟴ -->
+ <item first="0xd7f7" second="0x11b8" result="0xd7f8"/> <!-- ퟷ + ᆸ → ퟸ -->
+</combination>
diff --git a/data/keyboards/hangul-keyboard-2.xml.template b/data/keyboards/hangul-keyboard-2.xml.template
new file mode 100644
index 0000000..7704d08
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-2.xml.template
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="2" type="jamo">
+
+ <_name>Dubeolsik</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x0021"/> <!-- ! → ! -->
+ <item key="0x22" value="0x0022"/> <!-- " → " -->
+ <item key="0x23" value="0x0023"/> <!-- # → # -->
+ <item key="0x24" value="0x0024"/> <!-- $ → $ -->
+ <item key="0x25" value="0x0025"/> <!-- % → % -->
+ <item key="0x26" value="0x0026"/> <!-- & → & -->
+ <item key="0x27" value="0x0027"/> <!-- ' → ' -->
+ <item key="0x28" value="0x0028"/> <!-- ( → ( -->
+ <item key="0x29" value="0x0029"/> <!-- ) → ) -->
+ <item key="0x2a" value="0x002a"/> <!-- * → * -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x002c"/> <!-- , → , -->
+ <item key="0x2d" value="0x002d"/> <!-- - → - -->
+ <item key="0x2e" value="0x002e"/> <!-- . → . -->
+ <item key="0x2f" value="0x002f"/> <!-- / → / -->
+ <item key="0x30" value="0x0030"/> <!-- 0 → 0 -->
+ <item key="0x31" value="0x0031"/> <!-- 1 → 1 -->
+ <item key="0x32" value="0x0032"/> <!-- 2 → 2 -->
+ <item key="0x33" value="0x0033"/> <!-- 3 → 3 -->
+ <item key="0x34" value="0x0034"/> <!-- 4 → 4 -->
+ <item key="0x35" value="0x0035"/> <!-- 5 → 5 -->
+ <item key="0x36" value="0x0036"/> <!-- 6 → 6 -->
+ <item key="0x37" value="0x0037"/> <!-- 7 → 7 -->
+ <item key="0x38" value="0x0038"/> <!-- 8 → 8 -->
+ <item key="0x39" value="0x0039"/> <!-- 9 → 9 -->
+ <item key="0x3a" value="0x003a"/> <!-- : → : -->
+ <item key="0x3b" value="0x003b"/> <!-- ; → ; -->
+ <item key="0x3c" value="0x003c"/> <!-- < → < -->
+ <item key="0x3d" value="0x003d"/> <!-- = → = -->
+ <item key="0x3e" value="0x003e"/> <!-- > → > -->
+ <item key="0x3f" value="0x003f"/> <!-- ? → ? -->
+ <item key="0x40" value="0x0040"/> <!-- @ → @ -->
+ <item key="0x41" value="0x1106"/> <!-- A → ᄆᅠ -->
+ <item key="0x42" value="0x1172"/> <!-- B → ᅟᅲ -->
+ <item key="0x43" value="0x110e"/> <!-- C → ᄎᅠ -->
+ <item key="0x44" value="0x110b"/> <!-- D → ᄋᅠ -->
+ <item key="0x45" value="0x1104"/> <!-- E → ᄄᅠ -->
+ <item key="0x46" value="0x1105"/> <!-- F → ᄅᅠ -->
+ <item key="0x47" value="0x1112"/> <!-- G → ᄒᅠ -->
+ <item key="0x48" value="0x1169"/> <!-- H → ᅟᅩ -->
+ <item key="0x49" value="0x1163"/> <!-- I → ᅟᅣ -->
+ <item key="0x4a" value="0x1165"/> <!-- J → ᅟᅥ -->
+ <item key="0x4b" value="0x1161"/> <!-- K → ᅟᅡ -->
+ <item key="0x4c" value="0x1175"/> <!-- L → ᅟᅵ -->
+ <item key="0x4d" value="0x1173"/> <!-- M → ᅟᅳ -->
+ <item key="0x4e" value="0x116e"/> <!-- N → ᅟᅮ -->
+ <item key="0x4f" value="0x1164"/> <!-- O → ᅟᅤ -->
+ <item key="0x50" value="0x1168"/> <!-- P → ᅟᅨ -->
+ <item key="0x51" value="0x1108"/> <!-- Q → ᄈᅠ -->
+ <item key="0x52" value="0x1101"/> <!-- R → ᄁᅠ -->
+ <item key="0x53" value="0x1102"/> <!-- S → ᄂᅠ -->
+ <item key="0x54" value="0x110a"/> <!-- T → ᄊᅠ -->
+ <item key="0x55" value="0x1167"/> <!-- U → ᅟᅧ -->
+ <item key="0x56" value="0x1111"/> <!-- V → ᄑᅠ -->
+ <item key="0x57" value="0x110d"/> <!-- W → ᄍᅠ -->
+ <item key="0x58" value="0x1110"/> <!-- X → ᄐᅠ -->
+ <item key="0x59" value="0x116d"/> <!-- Y → ᅟᅭ -->
+ <item key="0x5a" value="0x110f"/> <!-- Z → ᄏᅠ -->
+ <item key="0x5b" value="0x005b"/> <!-- [ → [ -->
+ <item key="0x5c" value="0x005c"/> <!-- \ → \ -->
+ <item key="0x5d" value="0x005d"/> <!-- ] → ] -->
+ <item key="0x5e" value="0x005e"/> <!-- ^ → ^ -->
+ <item key="0x5f" value="0x005f"/> <!-- _ → _ -->
+ <item key="0x60" value="0x0060"/> <!-- ` → ` -->
+ <item key="0x61" value="0x1106"/> <!-- a → ᄆᅠ -->
+ <item key="0x62" value="0x1172"/> <!-- b → ᅟᅲ -->
+ <item key="0x63" value="0x110e"/> <!-- c → ᄎᅠ -->
+ <item key="0x64" value="0x110b"/> <!-- d → ᄋᅠ -->
+ <item key="0x65" value="0x1103"/> <!-- e → ᄃᅠ -->
+ <item key="0x66" value="0x1105"/> <!-- f → ᄅᅠ -->
+ <item key="0x67" value="0x1112"/> <!-- g → ᄒᅠ -->
+ <item key="0x68" value="0x1169"/> <!-- h → ᅟᅩ -->
+ <item key="0x69" value="0x1163"/> <!-- i → ᅟᅣ -->
+ <item key="0x6a" value="0x1165"/> <!-- j → ᅟᅥ -->
+ <item key="0x6b" value="0x1161"/> <!-- k → ᅟᅡ -->
+ <item key="0x6c" value="0x1175"/> <!-- l → ᅟᅵ -->
+ <item key="0x6d" value="0x1173"/> <!-- m → ᅟᅳ -->
+ <item key="0x6e" value="0x116e"/> <!-- n → ᅟᅮ -->
+ <item key="0x6f" value="0x1162"/> <!-- o → ᅟᅢ -->
+ <item key="0x70" value="0x1166"/> <!-- p → ᅟᅦ -->
+ <item key="0x71" value="0x1107"/> <!-- q → ᄇᅠ -->
+ <item key="0x72" value="0x1100"/> <!-- r → ᄀᅠ -->
+ <item key="0x73" value="0x1102"/> <!-- s → ᄂᅠ -->
+ <item key="0x74" value="0x1109"/> <!-- t → ᄉᅠ -->
+ <item key="0x75" value="0x1167"/> <!-- u → ᅟᅧ -->
+ <item key="0x76" value="0x1111"/> <!-- v → ᄑᅠ -->
+ <item key="0x77" value="0x110c"/> <!-- w → ᄌᅠ -->
+ <item key="0x78" value="0x1110"/> <!-- x → ᄐᅠ -->
+ <item key="0x79" value="0x116d"/> <!-- y → ᅟᅭ -->
+ <item key="0x7a" value="0x110f"/> <!-- z → ᄏᅠ -->
+ <item key="0x7b" value="0x007b"/> <!-- { → { -->
+ <item key="0x7c" value="0x007c"/> <!-- | → | -->
+ <item key="0x7d" value="0x007d"/> <!-- } → } -->
+ <item key="0x7e" value="0x007e"/> <!-- ~ → ~ -->
+ </map>
+
+ <include file="hangul-combination-default.xml"/>
+
+</hangul-keyboard>
diff --git a/data/keyboards/hangul-keyboard-2y.xml.template b/data/keyboards/hangul-keyboard-2y.xml.template
new file mode 100644
index 0000000..6ff75e7
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-2y.xml.template
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="2y" type="jamo-yet">
+
+ <_name>Dubeolsik Yetgeul</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x0021"/> <!-- ! → ! -->
+ <item key="0x22" value="0x0022"/> <!-- " → " -->
+ <item key="0x23" value="0x0023"/> <!-- # → # -->
+ <item key="0x24" value="0x0024"/> <!-- $ → $ -->
+ <item key="0x25" value="0x0025"/> <!-- % → % -->
+ <item key="0x26" value="0x0026"/> <!-- & → & -->
+ <item key="0x27" value="0x0027"/> <!-- ' → ' -->
+ <item key="0x28" value="0x0028"/> <!-- ( → ( -->
+ <item key="0x29" value="0x0029"/> <!-- ) → ) -->
+ <item key="0x2a" value="0x002a"/> <!-- * → * -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x002c"/> <!-- , → , -->
+ <item key="0x2d" value="0x002d"/> <!-- - → - -->
+ <item key="0x2e" value="0x002e"/> <!-- . → . -->
+ <item key="0x2f" value="0x002f"/> <!-- / → / -->
+ <item key="0x30" value="0x0030"/> <!-- 0 → 0 -->
+ <item key="0x31" value="0x0031"/> <!-- 1 → 1 -->
+ <item key="0x32" value="0x0032"/> <!-- 2 → 2 -->
+ <item key="0x33" value="0x0033"/> <!-- 3 → 3 -->
+ <item key="0x34" value="0x0034"/> <!-- 4 → 4 -->
+ <item key="0x35" value="0x0035"/> <!-- 5 → 5 -->
+ <item key="0x36" value="0x0036"/> <!-- 6 → 6 -->
+ <item key="0x37" value="0x0037"/> <!-- 7 → 7 -->
+ <item key="0x38" value="0x0038"/> <!-- 8 → 8 -->
+ <item key="0x39" value="0x0039"/> <!-- 9 → 9 -->
+ <item key="0x3a" value="0x003a"/> <!-- : → : -->
+ <item key="0x3b" value="0x003b"/> <!-- ; → ; -->
+ <item key="0x3c" value="0x003c"/> <!-- < → < -->
+ <item key="0x3d" value="0x003d"/> <!-- = → = -->
+ <item key="0x3e" value="0x003e"/> <!-- > → > -->
+ <item key="0x3f" value="0x003f"/> <!-- ? → ? -->
+ <item key="0x40" value="0x0040"/> <!-- @ → @ -->
+ <item key="0x41" value="0x1140"/> <!-- A → ᅀᅠ -->
+ <item key="0x42" value="0x1154"/> <!-- B → ᅔᅠ -->
+ <item key="0x43" value="0x114e"/> <!-- C → ᅎᅠ -->
+ <item key="0x44" value="0x114c"/> <!-- D → ᅌᅠ -->
+ <item key="0x45" value="0x1104"/> <!-- E → ᄄᅠ -->
+ <item key="0x46" value="0x111a"/> <!-- F → ᄚᅠ -->
+ <item key="0x47" value="0x1159"/> <!-- G → ᅙᅠ -->
+ <item key="0x48" value="0x1183"/> <!-- H → ᅟᆃ -->
+ <item key="0x49" value="0x1163"/> <!-- I → ᅟᅣ -->
+ <item key="0x4a" value="0x1165"/> <!-- J → ᅟᅥ -->
+ <item key="0x4b" value="0x119e"/> <!-- K → ᅟᆞ -->
+ <item key="0x4c" value="0x1194"/> <!-- L → ᅟᆔ -->
+ <item key="0x4d" value="0x1173"/> <!-- M → ᅟᅳ -->
+ <item key="0x4e" value="0x1155"/> <!-- N → ᅕᅠ -->
+ <item key="0x4f" value="0x1164"/> <!-- O → ᅟᅤ -->
+ <item key="0x50" value="0x1168"/> <!-- P → ᅟᅨ -->
+ <item key="0x51" value="0x1108"/> <!-- Q → ᄈᅠ -->
+ <item key="0x52" value="0x1101"/> <!-- R → ᄁᅠ -->
+ <item key="0x53" value="0x115d"/> <!-- S → ᅝᅠ -->
+ <item key="0x54" value="0x110a"/> <!-- T → ᄊᅠ -->
+ <item key="0x55" value="0x1167"/> <!-- U → ᅟᅧ -->
+ <item key="0x56" value="0x1150"/> <!-- V → ᅐᅠ -->
+ <item key="0x57" value="0x110d"/> <!-- W → ᄍᅠ -->
+ <item key="0x58" value="0x113e"/> <!-- X → ᄾᅠ -->
+ <item key="0x59" value="0x116d"/> <!-- Y → ᅟᅭ -->
+ <item key="0x5a" value="0x113c"/> <!-- Z → ᄼᅠ -->
+ <item key="0x5b" value="0x005b"/> <!-- [ → [ -->
+ <item key="0x5c" value="0x005c"/> <!-- \ → \ -->
+ <item key="0x5d" value="0x005d"/> <!-- ] → ] -->
+ <item key="0x5e" value="0x005e"/> <!-- ^ → ^ -->
+ <item key="0x5f" value="0x005f"/> <!-- _ → _ -->
+ <item key="0x60" value="0x0060"/> <!-- ` → ` -->
+ <item key="0x61" value="0x1106"/> <!-- a → ᄆᅠ -->
+ <item key="0x62" value="0x1172"/> <!-- b → ᅟᅲ -->
+ <item key="0x63" value="0x110e"/> <!-- c → ᄎᅠ -->
+ <item key="0x64" value="0x110b"/> <!-- d → ᄋᅠ -->
+ <item key="0x65" value="0x1103"/> <!-- e → ᄃᅠ -->
+ <item key="0x66" value="0x1105"/> <!-- f → ᄅᅠ -->
+ <item key="0x67" value="0x1112"/> <!-- g → ᄒᅠ -->
+ <item key="0x68" value="0x1169"/> <!-- h → ᅟᅩ -->
+ <item key="0x69" value="0x1163"/> <!-- i → ᅟᅣ -->
+ <item key="0x6a" value="0x1165"/> <!-- j → ᅟᅥ -->
+ <item key="0x6b" value="0x1161"/> <!-- k → ᅟᅡ -->
+ <item key="0x6c" value="0x1175"/> <!-- l → ᅟᅵ -->
+ <item key="0x6d" value="0x1173"/> <!-- m → ᅟᅳ -->
+ <item key="0x6e" value="0x116e"/> <!-- n → ᅟᅮ -->
+ <item key="0x6f" value="0x1162"/> <!-- o → ᅟᅢ -->
+ <item key="0x70" value="0x1166"/> <!-- p → ᅟᅦ -->
+ <item key="0x71" value="0x1107"/> <!-- q → ᄇᅠ -->
+ <item key="0x72" value="0x1100"/> <!-- r → ᄀᅠ -->
+ <item key="0x73" value="0x1102"/> <!-- s → ᄂᅠ -->
+ <item key="0x74" value="0x1109"/> <!-- t → ᄉᅠ -->
+ <item key="0x75" value="0x1167"/> <!-- u → ᅟᅧ -->
+ <item key="0x76" value="0x1111"/> <!-- v → ᄑᅠ -->
+ <item key="0x77" value="0x110c"/> <!-- w → ᄌᅠ -->
+ <item key="0x78" value="0x1110"/> <!-- x → ᄐᅠ -->
+ <item key="0x79" value="0x116d"/> <!-- y → ᅟᅭ -->
+ <item key="0x7a" value="0x110f"/> <!-- z → ᄏᅠ -->
+ <item key="0x7b" value="0x007b"/> <!-- { → { -->
+ <item key="0x7c" value="0x007c"/> <!-- | → | -->
+ <item key="0x7d" value="0x007d"/> <!-- } → } -->
+ <item key="0x7e" value="0x007e"/> <!-- ~ → ~ -->
+ </map>
+
+ <include file="hangul-combination-full.xml"/>
+
+</hangul-keyboard>
diff --git a/data/keyboards/hangul-keyboard-32.xml.template b/data/keyboards/hangul-keyboard-32.xml.template
new file mode 100644
index 0000000..5920069
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-32.xml.template
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="32" type="jaso">
+
+ <_name>Sebeolsik Dubeol Layout</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x0021"/> <!-- ! → ! -->
+ <item key="0x22" value="0x0022"/> <!-- " → " -->
+ <item key="0x23" value="0x0023"/> <!-- # → # -->
+ <item key="0x24" value="0x0024"/> <!-- $ → $ -->
+ <item key="0x25" value="0x0025"/> <!-- % → % -->
+ <item key="0x26" value="0x0026"/> <!-- & → & -->
+ <item key="0x27" value="0x0027"/> <!-- ' → ' -->
+ <item key="0x28" value="0x0028"/> <!-- ( → ( -->
+ <item key="0x29" value="0x0029"/> <!-- ) → ) -->
+ <item key="0x2a" value="0x002a"/> <!-- * → * -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x002c"/> <!-- , → , -->
+ <item key="0x2d" value="0x002d"/> <!-- - → - -->
+ <item key="0x2e" value="0x002e"/> <!-- . → . -->
+ <item key="0x2f" value="0x002f"/> <!-- / → / -->
+ <item key="0x30" value="0x0030"/> <!-- 0 → 0 -->
+ <item key="0x31" value="0x0031"/> <!-- 1 → 1 -->
+ <item key="0x32" value="0x0032"/> <!-- 2 → 2 -->
+ <item key="0x33" value="0x0033"/> <!-- 3 → 3 -->
+ <item key="0x34" value="0x0034"/> <!-- 4 → 4 -->
+ <item key="0x35" value="0x0035"/> <!-- 5 → 5 -->
+ <item key="0x36" value="0x0036"/> <!-- 6 → 6 -->
+ <item key="0x37" value="0x0037"/> <!-- 7 → 7 -->
+ <item key="0x38" value="0x0038"/> <!-- 8 → 8 -->
+ <item key="0x39" value="0x0039"/> <!-- 9 → 9 -->
+ <item key="0x3a" value="0x003a"/> <!-- : → : -->
+ <item key="0x3b" value="0x003b"/> <!-- ; → ; -->
+ <item key="0x3c" value="0x003c"/> <!-- < → < -->
+ <item key="0x3d" value="0x003d"/> <!-- = → = -->
+ <item key="0x3e" value="0x003e"/> <!-- > → > -->
+ <item key="0x3f" value="0x003f"/> <!-- ? → ? -->
+ <item key="0x40" value="0x0040"/> <!-- @ → @ -->
+ <item key="0x41" value="0x11b7"/> <!-- A → ᅟᅠᆷ -->
+ <item key="0x42" value="0x1172"/> <!-- B → ᅟᅲ -->
+ <item key="0x43" value="0x11be"/> <!-- C → ᅟᅠᆾ -->
+ <item key="0x44" value="0x11bc"/> <!-- D → ᅟᅠᆼ -->
+ <item key="0x45" value="0x11ae"/> <!-- E → ᅟᅠᆮ -->
+ <item key="0x46" value="0x11af"/> <!-- F → ᅟᅠᆯ -->
+ <item key="0x47" value="0x11c2"/> <!-- G → ᅟᅠᇂ -->
+ <item key="0x48" value="0x1169"/> <!-- H → ᅟᅩ -->
+ <item key="0x49" value="0x1163"/> <!-- I → ᅟᅣ -->
+ <item key="0x4a" value="0x1165"/> <!-- J → ᅟᅥ -->
+ <item key="0x4b" value="0x1161"/> <!-- K → ᅟᅡ -->
+ <item key="0x4c" value="0x1175"/> <!-- L → ᅟᅵ -->
+ <item key="0x4d" value="0x1173"/> <!-- M → ᅟᅳ -->
+ <item key="0x4e" value="0x116e"/> <!-- N → ᅟᅮ -->
+ <item key="0x4f" value="0x1164"/> <!-- O → ᅟᅤ -->
+ <item key="0x50" value="0x1168"/> <!-- P → ᅟᅨ -->
+ <item key="0x51" value="0x11b8"/> <!-- Q → ᅟᅠᆸ -->
+ <item key="0x52" value="0x11a8"/> <!-- R → ᅟᅠᆨ -->
+ <item key="0x53" value="0x11ab"/> <!-- S → ᅟᅠᆫ -->
+ <item key="0x54" value="0x11ba"/> <!-- T → ᅟᅠᆺ -->
+ <item key="0x55" value="0x1167"/> <!-- U → ᅟᅧ -->
+ <item key="0x56" value="0x11c1"/> <!-- V → ᅟᅠᇁ -->
+ <item key="0x57" value="0x11bd"/> <!-- W → ᅟᅠᆽ -->
+ <item key="0x58" value="0x11c0"/> <!-- X → ᅟᅠᇀ -->
+ <item key="0x59" value="0x116d"/> <!-- Y → ᅟᅭ -->
+ <item key="0x5a" value="0x11bf"/> <!-- Z → ᅟᅠᆿ -->
+ <item key="0x5b" value="0x005b"/> <!-- [ → [ -->
+ <item key="0x5c" value="0x005c"/> <!-- \ → \ -->
+ <item key="0x5d" value="0x005d"/> <!-- ] → ] -->
+ <item key="0x5e" value="0x005e"/> <!-- ^ → ^ -->
+ <item key="0x5f" value="0x005f"/> <!-- _ → _ -->
+ <item key="0x60" value="0x0060"/> <!-- ` → ` -->
+ <item key="0x61" value="0x1106"/> <!-- a → ᄆᅠ -->
+ <item key="0x62" value="0x1172"/> <!-- b → ᅟᅲ -->
+ <item key="0x63" value="0x110e"/> <!-- c → ᄎᅠ -->
+ <item key="0x64" value="0x110b"/> <!-- d → ᄋᅠ -->
+ <item key="0x65" value="0x1103"/> <!-- e → ᄃᅠ -->
+ <item key="0x66" value="0x1105"/> <!-- f → ᄅᅠ -->
+ <item key="0x67" value="0x1112"/> <!-- g → ᄒᅠ -->
+ <item key="0x68" value="0x1169"/> <!-- h → ᅟᅩ -->
+ <item key="0x69" value="0x1163"/> <!-- i → ᅟᅣ -->
+ <item key="0x6a" value="0x1165"/> <!-- j → ᅟᅥ -->
+ <item key="0x6b" value="0x1161"/> <!-- k → ᅟᅡ -->
+ <item key="0x6c" value="0x1175"/> <!-- l → ᅟᅵ -->
+ <item key="0x6d" value="0x1173"/> <!-- m → ᅟᅳ -->
+ <item key="0x6e" value="0x116e"/> <!-- n → ᅟᅮ -->
+ <item key="0x6f" value="0x1162"/> <!-- o → ᅟᅢ -->
+ <item key="0x70" value="0x1166"/> <!-- p → ᅟᅦ -->
+ <item key="0x71" value="0x1107"/> <!-- q → ᄇᅠ -->
+ <item key="0x72" value="0x1100"/> <!-- r → ᄀᅠ -->
+ <item key="0x73" value="0x1102"/> <!-- s → ᄂᅠ -->
+ <item key="0x74" value="0x1109"/> <!-- t → ᄉᅠ -->
+ <item key="0x75" value="0x1167"/> <!-- u → ᅟᅧ -->
+ <item key="0x76" value="0x1111"/> <!-- v → ᄑᅠ -->
+ <item key="0x77" value="0x110c"/> <!-- w → ᄌᅠ -->
+ <item key="0x78" value="0x1110"/> <!-- x → ᄐᅠ -->
+ <item key="0x79" value="0x116d"/> <!-- y → ᅟᅭ -->
+ <item key="0x7a" value="0x110f"/> <!-- z → ᄏᅠ -->
+ <item key="0x7b" value="0x007b"/> <!-- { → { -->
+ <item key="0x7c" value="0x007c"/> <!-- | → | -->
+ <item key="0x7d" value="0x007d"/> <!-- } → } -->
+ <item key="0x7e" value="0x203b"/> <!-- ~ → ※ -->
+ </map>
+
+ <include file="hangul-combination-default.xml"/>
+
+</hangul-keyboard>
diff --git a/data/keyboards/hangul-keyboard-39.xml.template b/data/keyboards/hangul-keyboard-39.xml.template
new file mode 100644
index 0000000..78cfb50
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-39.xml.template
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="39" type="jaso">
+
+ <_name>Sebeolsik 390</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x11bd"/> <!-- ! → ᅟᅠᆽ -->
+ <item key="0x22" value="0x0022"/> <!-- " → " -->
+ <item key="0x23" value="0x0023"/> <!-- # → # -->
+ <item key="0x24" value="0x0024"/> <!-- $ → $ -->
+ <item key="0x25" value="0x0025"/> <!-- % → % -->
+ <item key="0x26" value="0x0026"/> <!-- & → & -->
+ <item key="0x27" value="0x1110"/> <!-- ' → ᄐᅠ -->
+ <item key="0x28" value="0x0028"/> <!-- ( → ( -->
+ <item key="0x29" value="0x0029"/> <!-- ) → ) -->
+ <item key="0x2a" value="0x002a"/> <!-- * → * -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x002c"/> <!-- , → , -->
+ <item key="0x2d" value="0x002d"/> <!-- - → - -->
+ <item key="0x2e" value="0x002e"/> <!-- . → . -->
+ <item key="0x2f" value="0x1169"/> <!-- / → ᅟᅩ -->
+ <item key="0x30" value="0x110f"/> <!-- 0 → ᄏᅠ -->
+ <item key="0x31" value="0x11c2"/> <!-- 1 → ᅟᅠᇂ -->
+ <item key="0x32" value="0x11bb"/> <!-- 2 → ᅟᅠᆻ -->
+ <item key="0x33" value="0x11b8"/> <!-- 3 → ᅟᅠᆸ -->
+ <item key="0x34" value="0x116d"/> <!-- 4 → ᅟᅭ -->
+ <item key="0x35" value="0x1172"/> <!-- 5 → ᅟᅲ -->
+ <item key="0x36" value="0x1163"/> <!-- 6 → ᅟᅣ -->
+ <item key="0x37" value="0x1168"/> <!-- 7 → ᅟᅨ -->
+ <item key="0x38" value="0x1174"/> <!-- 8 → ᅟᅴ -->
+ <item key="0x39" value="0x116e"/> <!-- 9 → ᅟᅮ -->
+ <item key="0x3a" value="0x003a"/> <!-- : → : -->
+ <item key="0x3b" value="0x1107"/> <!-- ; → ᄇᅠ -->
+ <item key="0x3c" value="0x0032"/> <!-- < → 2 -->
+ <item key="0x3d" value="0x003d"/> <!-- = → = -->
+ <item key="0x3e" value="0x0033"/> <!-- > → 3 -->
+ <item key="0x3f" value="0x003f"/> <!-- ? → ? -->
+ <item key="0x40" value="0x0040"/> <!-- @ → @ -->
+ <item key="0x41" value="0x11ae"/> <!-- A → ᅟᅠᆮ -->
+ <item key="0x42" value="0x0021"/> <!-- B → ! -->
+ <item key="0x43" value="0x11b1"/> <!-- C → ᅟᅠᆱ -->
+ <item key="0x44" value="0x11b0"/> <!-- D → ᅟᅠᆰ -->
+ <item key="0x45" value="0x11bf"/> <!-- E → ᅟᅠᆿ -->
+ <item key="0x46" value="0x11a9"/> <!-- F → ᅟᅠᆩ -->
+ <item key="0x47" value="0x002f"/> <!-- G → / -->
+ <item key="0x48" value="0x0027"/> <!-- H → ' -->
+ <item key="0x49" value="0x0038"/> <!-- I → 8 -->
+ <item key="0x4a" value="0x0034"/> <!-- J → 4 -->
+ <item key="0x4b" value="0x0035"/> <!-- K → 5 -->
+ <item key="0x4c" value="0x0036"/> <!-- L → 6 -->
+ <item key="0x4d" value="0x0031"/> <!-- M → 1 -->
+ <item key="0x4e" value="0x0030"/> <!-- N → 0 -->
+ <item key="0x4f" value="0x0039"/> <!-- O → 9 -->
+ <item key="0x50" value="0x003e"/> <!-- P → > -->
+ <item key="0x51" value="0x11c1"/> <!-- Q → ᅟᅠᇁ -->
+ <item key="0x52" value="0x1164"/> <!-- R → ᅟᅤ -->
+ <item key="0x53" value="0x11ad"/> <!-- S → ᅟᅠᆭ -->
+ <item key="0x54" value="0x003b"/> <!-- T → ; -->
+ <item key="0x55" value="0x0037"/> <!-- U → 7 -->
+ <item key="0x56" value="0x11b6"/> <!-- V → ᅟᅠᆶ -->
+ <item key="0x57" value="0x11c0"/> <!-- W → ᅟᅠᇀ -->
+ <item key="0x58" value="0x11b9"/> <!-- X → ᅟᅠᆹ -->
+ <item key="0x59" value="0x003c"/> <!-- Y → < -->
+ <item key="0x5a" value="0x11be"/> <!-- Z → ᅟᅠᆾ -->
+ <item key="0x5b" value="0x005b"/> <!-- [ → [ -->
+ <item key="0x5c" value="0x005c"/> <!-- \ → \ -->
+ <item key="0x5d" value="0x005d"/> <!-- ] → ] -->
+ <item key="0x5e" value="0x005e"/> <!-- ^ → ^ -->
+ <item key="0x5f" value="0x005f"/> <!-- _ → _ -->
+ <item key="0x60" value="0x0060"/> <!-- ` → ` -->
+ <item key="0x61" value="0x11bc"/> <!-- a → ᅟᅠᆼ -->
+ <item key="0x62" value="0x116e"/> <!-- b → ᅟᅮ -->
+ <item key="0x63" value="0x1166"/> <!-- c → ᅟᅦ -->
+ <item key="0x64" value="0x1175"/> <!-- d → ᅟᅵ -->
+ <item key="0x65" value="0x1167"/> <!-- e → ᅟᅧ -->
+ <item key="0x66" value="0x1161"/> <!-- f → ᅟᅡ -->
+ <item key="0x67" value="0x1173"/> <!-- g → ᅟᅳ -->
+ <item key="0x68" value="0x1102"/> <!-- h → ᄂᅠ -->
+ <item key="0x69" value="0x1106"/> <!-- i → ᄆᅠ -->
+ <item key="0x6a" value="0x110b"/> <!-- j → ᄋᅠ -->
+ <item key="0x6b" value="0x1100"/> <!-- k → ᄀᅠ -->
+ <item key="0x6c" value="0x110c"/> <!-- l → ᄌᅠ -->
+ <item key="0x6d" value="0x1112"/> <!-- m → ᄒᅠ -->
+ <item key="0x6e" value="0x1109"/> <!-- n → ᄉᅠ -->
+ <item key="0x6f" value="0x110e"/> <!-- o → ᄎᅠ -->
+ <item key="0x70" value="0x1111"/> <!-- p → ᄑᅠ -->
+ <item key="0x71" value="0x11ba"/> <!-- q → ᅟᅠᆺ -->
+ <item key="0x72" value="0x1162"/> <!-- r → ᅟᅢ -->
+ <item key="0x73" value="0x11ab"/> <!-- s → ᅟᅠᆫ -->
+ <item key="0x74" value="0x1165"/> <!-- t → ᅟᅥ -->
+ <item key="0x75" value="0x1103"/> <!-- u → ᄃᅠ -->
+ <item key="0x76" value="0x1169"/> <!-- v → ᅟᅩ -->
+ <item key="0x77" value="0x11af"/> <!-- w → ᅟᅠᆯ -->
+ <item key="0x78" value="0x11a8"/> <!-- x → ᅟᅠᆨ -->
+ <item key="0x79" value="0x1105"/> <!-- y → ᄅᅠ -->
+ <item key="0x7a" value="0x11b7"/> <!-- z → ᅟᅠᆷ -->
+ <item key="0x7b" value="0x007b"/> <!-- { → { -->
+ <item key="0x7c" value="0x007c"/> <!-- | → | -->
+ <item key="0x7d" value="0x007d"/> <!-- } → } -->
+ <item key="0x7e" value="0x007e"/> <!-- ~ → ~ -->
+ </map>
+
+ <include file="hangul-combination-default.xml"/>
+
+</hangul-keyboard>
diff --git a/data/keyboards/hangul-keyboard-3f.xml.template b/data/keyboards/hangul-keyboard-3f.xml.template
new file mode 100644
index 0000000..c563df9
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-3f.xml.template
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="3f" type="jaso">
+
+ <_name>Sebeolsik Final</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x11a9"/> <!-- ! → ᅟᅠᆩ -->
+ <item key="0x22" value="0x00b7"/> <!-- " → · -->
+ <item key="0x23" value="0x11bd"/> <!-- # → ᅟᅠᆽ -->
+ <item key="0x24" value="0x11b5"/> <!-- $ → ᅟᅠᆵ -->
+ <item key="0x25" value="0x11b4"/> <!-- % → ᅟᅠᆴ -->
+ <item key="0x26" value="0x201c"/> <!-- & → “ -->
+ <item key="0x27" value="0x1110"/> <!-- ' → ᄐᅠ -->
+ <item key="0x28" value="0x0027"/> <!-- ( → ' -->
+ <item key="0x29" value="0x007e"/> <!-- ) → ~ -->
+ <item key="0x2a" value="0x201d"/> <!-- * → ” -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x002c"/> <!-- , → , -->
+ <item key="0x2d" value="0x0029"/> <!-- - → ) -->
+ <item key="0x2e" value="0x002e"/> <!-- . → . -->
+ <item key="0x2f" value="0x1169"/> <!-- / → ᅟᅩ -->
+ <item key="0x30" value="0x110f"/> <!-- 0 → ᄏᅠ -->
+ <item key="0x31" value="0x11c2"/> <!-- 1 → ᅟᅠᇂ -->
+ <item key="0x32" value="0x11bb"/> <!-- 2 → ᅟᅠᆻ -->
+ <item key="0x33" value="0x11b8"/> <!-- 3 → ᅟᅠᆸ -->
+ <item key="0x34" value="0x116d"/> <!-- 4 → ᅟᅭ -->
+ <item key="0x35" value="0x1172"/> <!-- 5 → ᅟᅲ -->
+ <item key="0x36" value="0x1163"/> <!-- 6 → ᅟᅣ -->
+ <item key="0x37" value="0x1168"/> <!-- 7 → ᅟᅨ -->
+ <item key="0x38" value="0x1174"/> <!-- 8 → ᅟᅴ -->
+ <item key="0x39" value="0x116e"/> <!-- 9 → ᅟᅮ -->
+ <item key="0x3a" value="0x0034"/> <!-- : → 4 -->
+ <item key="0x3b" value="0x1107"/> <!-- ; → ᄇᅠ -->
+ <item key="0x3c" value="0x002c"/> <!-- < → , -->
+ <item key="0x3d" value="0x003e"/> <!-- = → > -->
+ <item key="0x3e" value="0x002e"/> <!-- > → . -->
+ <item key="0x3f" value="0x0021"/> <!-- ? → ! -->
+ <item key="0x40" value="0x11b0"/> <!-- @ → ᅟᅠᆰ -->
+ <item key="0x41" value="0x11ae"/> <!-- A → ᅟᅠᆮ -->
+ <item key="0x42" value="0x003f"/> <!-- B → ? -->
+ <item key="0x43" value="0x11bf"/> <!-- C → ᅟᅠᆿ -->
+ <item key="0x44" value="0x11b2"/> <!-- D → ᅟᅠᆲ -->
+ <item key="0x45" value="0x11ac"/> <!-- E → ᅟᅠᆬ -->
+ <item key="0x46" value="0x11b1"/> <!-- F → ᅟᅠᆱ -->
+ <item key="0x47" value="0x1164"/> <!-- G → ᅟᅤ -->
+ <item key="0x48" value="0x0030"/> <!-- H → 0 -->
+ <item key="0x49" value="0x0037"/> <!-- I → 7 -->
+ <item key="0x4a" value="0x0031"/> <!-- J → 1 -->
+ <item key="0x4b" value="0x0032"/> <!-- K → 2 -->
+ <item key="0x4c" value="0x0033"/> <!-- L → 3 -->
+ <item key="0x4d" value="0x0022"/> <!-- M → " -->
+ <item key="0x4e" value="0x002d"/> <!-- N → - -->
+ <item key="0x4f" value="0x0038"/> <!-- O → 8 -->
+ <item key="0x50" value="0x0039"/> <!-- P → 9 -->
+ <item key="0x51" value="0x11c1"/> <!-- Q → ᅟᅠᇁ -->
+ <item key="0x52" value="0x11b6"/> <!-- R → ᅟᅠᆶ -->
+ <item key="0x53" value="0x11ad"/> <!-- S → ᅟᅠᆭ -->
+ <item key="0x54" value="0x11b3"/> <!-- T → ᅟᅠᆳ -->
+ <item key="0x55" value="0x0036"/> <!-- U → 6 -->
+ <item key="0x56" value="0x11aa"/> <!-- V → ᅟᅠᆪ -->
+ <item key="0x57" value="0x11c0"/> <!-- W → ᅟᅠᇀ -->
+ <item key="0x58" value="0x11b9"/> <!-- X → ᅟᅠᆹ -->
+ <item key="0x59" value="0x0035"/> <!-- Y → 5 -->
+ <item key="0x5a" value="0x11be"/> <!-- Z → ᅟᅠᆾ -->
+ <item key="0x5b" value="0x0028"/> <!-- [ → ( -->
+ <item key="0x5c" value="0x003a"/> <!-- \ → : -->
+ <item key="0x5d" value="0x003c"/> <!-- ] → < -->
+ <item key="0x5e" value="0x003d"/> <!-- ^ → = -->
+ <item key="0x5f" value="0x003b"/> <!-- _ → ; -->
+ <item key="0x60" value="0x002a"/> <!-- ` → * -->
+ <item key="0x61" value="0x11bc"/> <!-- a → ᅟᅠᆼ -->
+ <item key="0x62" value="0x116e"/> <!-- b → ᅟᅮ -->
+ <item key="0x63" value="0x1166"/> <!-- c → ᅟᅦ -->
+ <item key="0x64" value="0x1175"/> <!-- d → ᅟᅵ -->
+ <item key="0x65" value="0x1167"/> <!-- e → ᅟᅧ -->
+ <item key="0x66" value="0x1161"/> <!-- f → ᅟᅡ -->
+ <item key="0x67" value="0x1173"/> <!-- g → ᅟᅳ -->
+ <item key="0x68" value="0x1102"/> <!-- h → ᄂᅠ -->
+ <item key="0x69" value="0x1106"/> <!-- i → ᄆᅠ -->
+ <item key="0x6a" value="0x110b"/> <!-- j → ᄋᅠ -->
+ <item key="0x6b" value="0x1100"/> <!-- k → ᄀᅠ -->
+ <item key="0x6c" value="0x110c"/> <!-- l → ᄌᅠ -->
+ <item key="0x6d" value="0x1112"/> <!-- m → ᄒᅠ -->
+ <item key="0x6e" value="0x1109"/> <!-- n → ᄉᅠ -->
+ <item key="0x6f" value="0x110e"/> <!-- o → ᄎᅠ -->
+ <item key="0x70" value="0x1111"/> <!-- p → ᄑᅠ -->
+ <item key="0x71" value="0x11ba"/> <!-- q → ᅟᅠᆺ -->
+ <item key="0x72" value="0x1162"/> <!-- r → ᅟᅢ -->
+ <item key="0x73" value="0x11ab"/> <!-- s → ᅟᅠᆫ -->
+ <item key="0x74" value="0x1165"/> <!-- t → ᅟᅥ -->
+ <item key="0x75" value="0x1103"/> <!-- u → ᄃᅠ -->
+ <item key="0x76" value="0x1169"/> <!-- v → ᅟᅩ -->
+ <item key="0x77" value="0x11af"/> <!-- w → ᅟᅠᆯ -->
+ <item key="0x78" value="0x11a8"/> <!-- x → ᅟᅠᆨ -->
+ <item key="0x79" value="0x1105"/> <!-- y → ᄅᅠ -->
+ <item key="0x7a" value="0x11b7"/> <!-- z → ᅟᅠᆷ -->
+ <item key="0x7b" value="0x0025"/> <!-- { → % -->
+ <item key="0x7c" value="0x005c"/> <!-- | → \ -->
+ <item key="0x7d" value="0x002f"/> <!-- } → / -->
+ <item key="0x7e" value="0x203b"/> <!-- ~ → ※ -->
+ </map>
+
+ <include file="hangul-combination-default.xml"/>
+
+</hangul-keyboard>
diff --git a/data/keyboards/hangul-keyboard-3s.xml.template b/data/keyboards/hangul-keyboard-3s.xml.template
new file mode 100644
index 0000000..5a95490
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-3s.xml.template
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="3s" type="jaso">
+
+ <_name>Sebeolsik Noshift</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x0021"/> <!-- ! → ! -->
+ <item key="0x22" value="0x0022"/> <!-- " → " -->
+ <item key="0x23" value="0x0023"/> <!-- # → # -->
+ <item key="0x24" value="0x0024"/> <!-- $ → $ -->
+ <item key="0x25" value="0x0025"/> <!-- % → % -->
+ <item key="0x26" value="0x0026"/> <!-- & → & -->
+ <item key="0x27" value="0x1110"/> <!-- ' → ᄐᅠ -->
+ <item key="0x28" value="0x0028"/> <!-- ( → ( -->
+ <item key="0x29" value="0x0029"/> <!-- ) → ) -->
+ <item key="0x2a" value="0x002a"/> <!-- * → * -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x002c"/> <!-- , → , -->
+ <item key="0x2d" value="0x11bd"/> <!-- - → ᅟᅠᆽ -->
+ <item key="0x2e" value="0x002e"/> <!-- . → . -->
+ <item key="0x2f" value="0x11ae"/> <!-- / → ᅟᅠᆮ -->
+ <item key="0x30" value="0x1164"/> <!-- 0 → ᅟᅤ -->
+ <item key="0x31" value="0x11c2"/> <!-- 1 → ᅟᅠᇂ -->
+ <item key="0x32" value="0x11bb"/> <!-- 2 → ᅟᅠᆻ -->
+ <item key="0x33" value="0x11b8"/> <!-- 3 → ᅟᅠᆸ -->
+ <item key="0x34" value="0x116d"/> <!-- 4 → ᅟᅭ -->
+ <item key="0x35" value="0x1172"/> <!-- 5 → ᅟᅲ -->
+ <item key="0x36" value="0x1163"/> <!-- 6 → ᅟᅣ -->
+ <item key="0x37" value="0x1168"/> <!-- 7 → ᅟᅨ -->
+ <item key="0x38" value="0x1174"/> <!-- 8 → ᅟᅴ -->
+ <item key="0x39" value="0x110f"/> <!-- 9 → ᄏᅠ -->
+ <item key="0x3a" value="0x003a"/> <!-- : → : -->
+ <item key="0x3b" value="0x1107"/> <!-- ; → ᄇᅠ -->
+ <item key="0x3c" value="0x0032"/> <!-- < → 2 -->
+ <item key="0x3d" value="0x11be"/> <!-- = → ᅟᅠᆾ -->
+ <item key="0x3e" value="0x0033"/> <!-- > → 3 -->
+ <item key="0x3f" value="0x003f"/> <!-- ? → ? -->
+ <item key="0x40" value="0x0040"/> <!-- @ → @ -->
+ <item key="0x41" value="0x11bc"/> <!-- A → ᅟᅠᆼ -->
+ <item key="0x42" value="0x0021"/> <!-- B → ! -->
+ <item key="0x43" value="0x005c"/> <!-- C → \ -->
+ <item key="0x44" value="0x005d"/> <!-- D → ] -->
+ <item key="0x45" value="0x1167"/> <!-- E → ᅟᅧ -->
+ <item key="0x46" value="0x1161"/> <!-- F → ᅟᅡ -->
+ <item key="0x47" value="0x002f"/> <!-- G → / -->
+ <item key="0x48" value="0x0027"/> <!-- H → ' -->
+ <item key="0x49" value="0x0038"/> <!-- I → 8 -->
+ <item key="0x4a" value="0x0034"/> <!-- J → 4 -->
+ <item key="0x4b" value="0x0035"/> <!-- K → 5 -->
+ <item key="0x4c" value="0x0036"/> <!-- L → 6 -->
+ <item key="0x4d" value="0x0031"/> <!-- M → 1 -->
+ <item key="0x4e" value="0x0030"/> <!-- N → 0 -->
+ <item key="0x4f" value="0x0039"/> <!-- O → 9 -->
+ <item key="0x50" value="0x003e"/> <!-- P → > -->
+ <item key="0x51" value="0x11ba"/> <!-- Q → ᅟᅠᆺ -->
+ <item key="0x52" value="0x1162"/> <!-- R → ᅟᅢ -->
+ <item key="0x53" value="0x005b"/> <!-- S → [ -->
+ <item key="0x54" value="0x003b"/> <!-- T → ; -->
+ <item key="0x55" value="0x0037"/> <!-- U → 7 -->
+ <item key="0x56" value="0x1169"/> <!-- V → ᅟᅩ -->
+ <item key="0x57" value="0x11af"/> <!-- W → ᅟᅠᆯ -->
+ <item key="0x58" value="0x003d"/> <!-- X → = -->
+ <item key="0x59" value="0x003c"/> <!-- Y → < -->
+ <item key="0x5a" value="0x002d"/> <!-- Z → - -->
+ <item key="0x5b" value="0x11c0"/> <!-- [ → ᅟᅠᇀ -->
+ <item key="0x5c" value="0x11bf"/> <!-- \ → ᅟᅠᆿ -->
+ <item key="0x5d" value="0x11c1"/> <!-- ] → ᅟᅠᇁ -->
+ <item key="0x5e" value="0x005e"/> <!-- ^ → ^ -->
+ <item key="0x5f" value="0x005f"/> <!-- _ → _ -->
+ <item key="0x60" value="0x0060"/> <!-- ` → ` -->
+ <item key="0x61" value="0x11bc"/> <!-- a → ᅟᅠᆼ -->
+ <item key="0x62" value="0x116e"/> <!-- b → ᅟᅮ -->
+ <item key="0x63" value="0x1166"/> <!-- c → ᅟᅦ -->
+ <item key="0x64" value="0x1175"/> <!-- d → ᅟᅵ -->
+ <item key="0x65" value="0x1167"/> <!-- e → ᅟᅧ -->
+ <item key="0x66" value="0x1161"/> <!-- f → ᅟᅡ -->
+ <item key="0x67" value="0x1173"/> <!-- g → ᅟᅳ -->
+ <item key="0x68" value="0x1102"/> <!-- h → ᄂᅠ -->
+ <item key="0x69" value="0x1106"/> <!-- i → ᄆᅠ -->
+ <item key="0x6a" value="0x110b"/> <!-- j → ᄋᅠ -->
+ <item key="0x6b" value="0x1100"/> <!-- k → ᄀᅠ -->
+ <item key="0x6c" value="0x110c"/> <!-- l → ᄌᅠ -->
+ <item key="0x6d" value="0x1112"/> <!-- m → ᄒᅠ -->
+ <item key="0x6e" value="0x1109"/> <!-- n → ᄉᅠ -->
+ <item key="0x6f" value="0x110e"/> <!-- o → ᄎᅠ -->
+ <item key="0x70" value="0x1111"/> <!-- p → ᄑᅠ -->
+ <item key="0x71" value="0x11ba"/> <!-- q → ᅟᅠᆺ -->
+ <item key="0x72" value="0x1162"/> <!-- r → ᅟᅢ -->
+ <item key="0x73" value="0x11ab"/> <!-- s → ᅟᅠᆫ -->
+ <item key="0x74" value="0x1165"/> <!-- t → ᅟᅥ -->
+ <item key="0x75" value="0x1103"/> <!-- u → ᄃᅠ -->
+ <item key="0x76" value="0x1169"/> <!-- v → ᅟᅩ -->
+ <item key="0x77" value="0x11af"/> <!-- w → ᅟᅠᆯ -->
+ <item key="0x78" value="0x11a8"/> <!-- x → ᅟᅠᆨ -->
+ <item key="0x79" value="0x1105"/> <!-- y → ᄅᅠ -->
+ <item key="0x7a" value="0x11b7"/> <!-- z → ᅟᅠᆷ -->
+ <item key="0x7b" value="0x007b"/> <!-- { → { -->
+ <item key="0x7c" value="0x007c"/> <!-- | → | -->
+ <item key="0x7d" value="0x007d"/> <!-- } → } -->
+ <item key="0x7e" value="0x007e"/> <!-- ~ → ~ -->
+ </map>
+
+ <include file="hangul-combination-default.xml"/>
+
+</hangul-keyboard>
diff --git a/data/keyboards/hangul-keyboard-3y.xml.template b/data/keyboards/hangul-keyboard-3y.xml.template
new file mode 100644
index 0000000..67f0ae9
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-3y.xml.template
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="3y" type="jaso-yet">
+
+ <_name>Sebeolsik Yetgeul</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x11bd"/> <!-- ! → ᅟᅠᆽ -->
+ <item key="0x22" value="0x0022"/> <!-- " → " -->
+ <item key="0x23" value="0x0023"/> <!-- # → # -->
+ <item key="0x24" value="0x0024"/> <!-- $ → $ -->
+ <item key="0x25" value="0x0025"/> <!-- % → % -->
+ <item key="0x26" value="0x0026"/> <!-- & → & -->
+ <item key="0x27" value="0x1110"/> <!-- ' → ᄐᅠ -->
+ <item key="0x28" value="0x0028"/> <!-- ( → ( -->
+ <item key="0x29" value="0x0029"/> <!-- ) → ) -->
+ <item key="0x2a" value="0x002a"/> <!-- * → * -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x002c"/> <!-- , → , -->
+ <item key="0x2d" value="0x002d"/> <!-- - → - -->
+ <item key="0x2e" value="0x002e"/> <!-- . → . -->
+ <item key="0x2f" value="0x1169"/> <!-- / → ᅟᅩ -->
+ <item key="0x30" value="0x110f"/> <!-- 0 → ᄏᅠ -->
+ <item key="0x31" value="0x11c2"/> <!-- 1 → ᅟᅠᇂ -->
+ <item key="0x32" value="0x11bb"/> <!-- 2 → ᅟᅠᆻ -->
+ <item key="0x33" value="0x11b8"/> <!-- 3 → ᅟᅠᆸ -->
+ <item key="0x34" value="0x116d"/> <!-- 4 → ᅟᅭ -->
+ <item key="0x35" value="0x1172"/> <!-- 5 → ᅟᅲ -->
+ <item key="0x36" value="0x1163"/> <!-- 6 → ᅟᅣ -->
+ <item key="0x37" value="0x1168"/> <!-- 7 → ᅟᅨ -->
+ <item key="0x38" value="0x1174"/> <!-- 8 → ᅟᅴ -->
+ <item key="0x39" value="0x116e"/> <!-- 9 → ᅟᅮ -->
+ <item key="0x3a" value="0x003a"/> <!-- : → : -->
+ <item key="0x3b" value="0x1107"/> <!-- ; → ᄇᅠ -->
+ <item key="0x3c" value="0x113c"/> <!-- < → ᄼᅠ -->
+ <item key="0x3d" value="0x003d"/> <!-- = → = -->
+ <item key="0x3e" value="0x113e"/> <!-- > → ᄾᅠ -->
+ <item key="0x3f" value="0x003f"/> <!-- ? → ? -->
+ <item key="0x40" value="0x11eb"/> <!-- @ → ᅟᅠᇫ -->
+ <item key="0x41" value="0x11ae"/> <!-- A → ᅟᅠᆮ -->
+ <item key="0x42" value="0x0021"/> <!-- B → ! -->
+ <item key="0x43" value="0x11b1"/> <!-- C → ᅟᅠᆱ -->
+ <item key="0x44" value="0x11b0"/> <!-- D → ᅟᅠᆰ -->
+ <item key="0x45" value="0x11bf"/> <!-- E → ᅟᅠᆿ -->
+ <item key="0x46" value="0x11a9"/> <!-- F → ᅟᅠᆩ -->
+ <item key="0x47" value="0x119e"/> <!-- G → ᅟᆞ -->
+ <item key="0x48" value="0x0027"/> <!-- H → ' -->
+ <item key="0x49" value="0x1154"/> <!-- I → ᅔᅠ -->
+ <item key="0x4a" value="0x114c"/> <!-- J → ᅌᅠ -->
+ <item key="0x4b" value="0x114e"/> <!-- K → ᅎᅠ -->
+ <item key="0x4c" value="0x1150"/> <!-- L → ᅐᅠ -->
+ <item key="0x4d" value="0x1159"/> <!-- M → ᅙᅠ -->
+ <item key="0x4e" value="0x1140"/> <!-- N → ᅀᅠ -->
+ <item key="0x4f" value="0x1155"/> <!-- O → ᅕᅠ -->
+ <item key="0x50" value="0x003e"/> <!-- P → > -->
+ <item key="0x51" value="0x11c1"/> <!-- Q → ᅟᅠᇁ -->
+ <item key="0x52" value="0x1164"/> <!-- R → ᅟᅤ -->
+ <item key="0x53" value="0x11ad"/> <!-- S → ᅟᅠᆭ -->
+ <item key="0x54" value="0x003b"/> <!-- T → ; -->
+ <item key="0x55" value="0x00b7"/> <!-- U → · -->
+ <item key="0x56" value="0x11b6"/> <!-- V → ᅟᅠᆶ -->
+ <item key="0x57" value="0x11c0"/> <!-- W → ᅟᅠᇀ -->
+ <item key="0x58" value="0x11b9"/> <!-- X → ᅟᅠᆹ -->
+ <item key="0x59" value="0x003c"/> <!-- Y → < -->
+ <item key="0x5a" value="0x11be"/> <!-- Z → ᅟᅠᆾ -->
+ <item key="0x5b" value="0x005b"/> <!-- [ → [ -->
+ <item key="0x5c" value="0x005c"/> <!-- \ → \ -->
+ <item key="0x5d" value="0x005d"/> <!-- ] → ] -->
+ <item key="0x5e" value="0x114c"/> <!-- ^ → ᅌᅠ -->
+ <item key="0x5f" value="0x005f"/> <!-- _ → _ -->
+ <item key="0x60" value="0x11f9"/> <!-- ` → ᅟᅠᇹ -->
+ <item key="0x61" value="0x11bc"/> <!-- a → ᅟᅠᆼ -->
+ <item key="0x62" value="0x116e"/> <!-- b → ᅟᅮ -->
+ <item key="0x63" value="0x1166"/> <!-- c → ᅟᅦ -->
+ <item key="0x64" value="0x1175"/> <!-- d → ᅟᅵ -->
+ <item key="0x65" value="0x1167"/> <!-- e → ᅟᅧ -->
+ <item key="0x66" value="0x1161"/> <!-- f → ᅟᅡ -->
+ <item key="0x67" value="0x1173"/> <!-- g → ᅟᅳ -->
+ <item key="0x68" value="0x1102"/> <!-- h → ᄂᅠ -->
+ <item key="0x69" value="0x1106"/> <!-- i → ᄆᅠ -->
+ <item key="0x6a" value="0x110b"/> <!-- j → ᄋᅠ -->
+ <item key="0x6b" value="0x1100"/> <!-- k → ᄀᅠ -->
+ <item key="0x6c" value="0x110c"/> <!-- l → ᄌᅠ -->
+ <item key="0x6d" value="0x1112"/> <!-- m → ᄒᅠ -->
+ <item key="0x6e" value="0x1109"/> <!-- n → ᄉᅠ -->
+ <item key="0x6f" value="0x110e"/> <!-- o → ᄎᅠ -->
+ <item key="0x70" value="0x1111"/> <!-- p → ᄑᅠ -->
+ <item key="0x71" value="0x11ba"/> <!-- q → ᅟᅠᆺ -->
+ <item key="0x72" value="0x1162"/> <!-- r → ᅟᅢ -->
+ <item key="0x73" value="0x11ab"/> <!-- s → ᅟᅠᆫ -->
+ <item key="0x74" value="0x1165"/> <!-- t → ᅟᅥ -->
+ <item key="0x75" value="0x1103"/> <!-- u → ᄃᅠ -->
+ <item key="0x76" value="0x1169"/> <!-- v → ᅟᅩ -->
+ <item key="0x77" value="0x11af"/> <!-- w → ᅟᅠᆯ -->
+ <item key="0x78" value="0x11a8"/> <!-- x → ᅟᅠᆨ -->
+ <item key="0x79" value="0x1105"/> <!-- y → ᄅᅠ -->
+ <item key="0x7a" value="0x11b7"/> <!-- z → ᅟᅠᆷ -->
+ <item key="0x7b" value="0x007b"/> <!-- { → { -->
+ <item key="0x7c" value="0x007c"/> <!-- | → | -->
+ <item key="0x7d" value="0x007d"/> <!-- } → } -->
+ <item key="0x7e" value="0x11f0"/> <!-- ~ → ᅟᅠᇰ -->
+ </map>
+
+ <include file="hangul-combination-full.xml"/>
+
+</hangul-keyboard>
diff --git a/data/keyboards/hangul-keyboard-ahn.xml.template b/data/keyboards/hangul-keyboard-ahn.xml.template
new file mode 100644
index 0000000..0fa4d3c
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-ahn.xml.template
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="ahn" type="jaso">
+
+ <_name>Ahnmatae</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x0021"/> <!-- ! → ! -->
+ <item key="0x22" value="0x0022"/> <!-- " → " -->
+ <item key="0x23" value="0x0023"/> <!-- # → # -->
+ <item key="0x24" value="0x0024"/> <!-- $ → $ -->
+ <item key="0x25" value="0x0025"/> <!-- % → % -->
+ <item key="0x26" value="0x0026"/> <!-- & → & -->
+ <item key="0x27" value="0x002e"/> <!-- ' → . -->
+ <item key="0x28" value="0x0028"/> <!-- ( → ( -->
+ <item key="0x29" value="0x0029"/> <!-- ) → ) -->
+ <item key="0x2a" value="0x002a"/> <!-- * → * -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x11b7"/> <!-- , → ᅟᅠᆷ -->
+ <item key="0x2d" value="0x002d"/> <!-- - → - -->
+ <item key="0x2e" value="0x11af"/> <!-- . → ᅟᅠᆯ -->
+ <item key="0x2f" value="0x11c2"/> <!-- / → ᅟᅠᇂ -->
+ <item key="0x30" value="0x0030"/> <!-- 0 → 0 -->
+ <item key="0x31" value="0x0031"/> <!-- 1 → 1 -->
+ <item key="0x32" value="0x0032"/> <!-- 2 → 2 -->
+ <item key="0x33" value="0x0033"/> <!-- 3 → 3 -->
+ <item key="0x34" value="0x0034"/> <!-- 4 → 4 -->
+ <item key="0x35" value="0x0035"/> <!-- 5 → 5 -->
+ <item key="0x36" value="0x0036"/> <!-- 6 → 6 -->
+ <item key="0x37" value="0x0037"/> <!-- 7 → 7 -->
+ <item key="0x38" value="0x0038"/> <!-- 8 → 8 -->
+ <item key="0x39" value="0x0039"/> <!-- 9 → 9 -->
+ <item key="0x3a" value="0x003a"/> <!-- : → : -->
+ <item key="0x3b" value="0x116e"/> <!-- ; → ᅟᅮ -->
+ <item key="0x3c" value="0x003c"/> <!-- < → < -->
+ <item key="0x3d" value="0x003d"/> <!-- = → = -->
+ <item key="0x3e" value="0x003e"/> <!-- > → > -->
+ <item key="0x3f" value="0x11f9"/> <!-- ? → ᅟᅠᇹ -->
+ <item key="0x40" value="0x0040"/> <!-- @ → @ -->
+ <item key="0x41" value="0x1107"/> <!-- A → ᄇᅠ -->
+ <item key="0x42" value="0x11f0"/> <!-- B → ᅟᅠᇰ -->
+ <item key="0x43" value="0x11b8"/> <!-- C → ᅟᅠᆸ -->
+ <item key="0x44" value="0x1103"/> <!-- D → ᄃᅠ -->
+ <item key="0x45" value="0x1102"/> <!-- E → ᄂᅠ -->
+ <item key="0x46" value="0x1100"/> <!-- F → ᄀᅠ -->
+ <item key="0x47" value="0x114c"/> <!-- G → ᅌᅠ -->
+ <item key="0x48" value="0x1165"/> <!-- H → ᅟᅥ -->
+ <item key="0x49" value="0x002f"/> <!-- I → / -->
+ <item key="0x4a" value="0x119e"/> <!-- J → ᅟᆞ -->
+ <item key="0x4b" value="0x1175"/> <!-- K → ᅟᅵ -->
+ <item key="0x4c" value="0x1169"/> <!-- L → ᅟᅩ -->
+ <item key="0x4d" value="0x11ab"/> <!-- M → ᅟᅠᆫ -->
+ <item key="0x4e" value="0x11eb"/> <!-- N → ᅟᅠᇫ -->
+ <item key="0x4f" value="0x005b"/> <!-- O → [ -->
+ <item key="0x50" value="0x005d"/> <!-- P → ] -->
+ <item key="0x51" value="0x1106"/> <!-- Q → ᄆᅠ -->
+ <item key="0x52" value="0x1105"/> <!-- R → ᄅᅠ -->
+ <item key="0x53" value="0x110c"/> <!-- S → ᄌᅠ -->
+ <item key="0x54" value="0x1159"/> <!-- T → ᅙᅠ -->
+ <item key="0x55" value="0x0027"/> <!-- U → ' -->
+ <item key="0x56" value="0x11a8"/> <!-- V → ᅟᅠᆨ -->
+ <item key="0x57" value="0x1140"/> <!-- W → ᅀᅠ -->
+ <item key="0x58" value="0x11ae"/> <!-- X → ᅟᅠᆮ -->
+ <item key="0x59" value="0x003b"/> <!-- Y → ; -->
+ <item key="0x5a" value="0x11bd"/> <!-- Z → ᅟᅠᆽ -->
+ <item key="0x5b" value="0x002c"/> <!-- [ → , -->
+ <item key="0x5c" value="0x005c"/> <!-- \ → \ -->
+ <item key="0x5d" value="0x003f"/> <!-- ] → ? -->
+ <item key="0x5e" value="0x005e"/> <!-- ^ → ^ -->
+ <item key="0x5f" value="0x005f"/> <!-- _ → _ -->
+ <item key="0x60" value="0x0060"/> <!-- ` → ` -->
+ <item key="0x61" value="0x1107"/> <!-- a → ᄇᅠ -->
+ <item key="0x62" value="0x11bc"/> <!-- b → ᅟᅠᆼ -->
+ <item key="0x63" value="0x11b8"/> <!-- c → ᅟᅠᆸ -->
+ <item key="0x64" value="0x1103"/> <!-- d → ᄃᅠ -->
+ <item key="0x65" value="0x1102"/> <!-- e → ᄂᅠ -->
+ <item key="0x66" value="0x1100"/> <!-- f → ᄀᅠ -->
+ <item key="0x67" value="0x110b"/> <!-- g → ᄋᅠ -->
+ <item key="0x68" value="0x1165"/> <!-- h → ᅟᅥ -->
+ <item key="0x69" value="0x1173"/> <!-- i → ᅟᅳ -->
+ <item key="0x6a" value="0x1161"/> <!-- j → ᅟᅡ -->
+ <item key="0x6b" value="0x1175"/> <!-- k → ᅟᅵ -->
+ <item key="0x6c" value="0x1169"/> <!-- l → ᅟᅩ -->
+ <item key="0x6d" value="0x11ab"/> <!-- m → ᅟᅠᆫ -->
+ <item key="0x6e" value="0x11ba"/> <!-- n → ᅟᅠᆺ -->
+ <item key="0x6f" value="0x116d"/> <!-- o → ᅟᅭ -->
+ <item key="0x70" value="0x1172"/> <!-- p → ᅟᅲ -->
+ <item key="0x71" value="0x1106"/> <!-- q → ᄆᅠ -->
+ <item key="0x72" value="0x1105"/> <!-- r → ᄅᅠ -->
+ <item key="0x73" value="0x110c"/> <!-- s → ᄌᅠ -->
+ <item key="0x74" value="0x1112"/> <!-- t → ᄒᅠ -->
+ <item key="0x75" value="0x1163"/> <!-- u → ᅟᅣ -->
+ <item key="0x76" value="0x11a8"/> <!-- v → ᅟᅠᆨ -->
+ <item key="0x77" value="0x1109"/> <!-- w → ᄉᅠ -->
+ <item key="0x78" value="0x11ae"/> <!-- x → ᅟᅠᆮ -->
+ <item key="0x79" value="0x1167"/> <!-- y → ᅟᅧ -->
+ <item key="0x7a" value="0x11bd"/> <!-- z → ᅟᅠᆽ -->
+ <item key="0x7b" value="0x007b"/> <!-- { → { -->
+ <item key="0x7c" value="0x007c"/> <!-- | → | -->
+ <item key="0x7d" value="0x007d"/> <!-- } → } -->
+ <item key="0x7e" value="0x007e"/> <!-- ~ → ~ -->
+ </map>
+
+ <combination id="0">
+ <item first="0x1100" second="0x1103" result="0x1104"/> <!-- ᄀ + ᄃ → ᄄ -->
+ <item first="0x1100" second="0x110b" result="0x1101"/> <!-- ᄀ + ᄋ → ᄁ -->
+ <item first="0x1100" second="0x1112" result="0x110f"/> <!-- ᄀ + ᄒ → ᄏ -->
+ <item first="0x1102" second="0x1109" result="0x110a"/> <!-- ᄂ + ᄉ → ᄊ -->
+ <item first="0x1103" second="0x1100" result="0x1104"/> <!-- ᄃ + ᄀ → ᄄ -->
+ <item first="0x1103" second="0x110c" result="0x110d"/> <!-- ᄃ + ᄌ → ᄍ -->
+ <item first="0x1103" second="0x1112" result="0x1110"/> <!-- ᄃ + ᄒ → ᄐ -->
+ <item first="0x1107" second="0x110c" result="0x1108"/> <!-- ᄇ + ᄌ → ᄈ -->
+ <item first="0x1107" second="0x1112" result="0x1111"/> <!-- ᄇ + ᄒ → ᄑ -->
+ <item first="0x1109" second="0x1102" result="0x110a"/> <!-- ᄉ + ᄂ → ᄊ -->
+ <item first="0x110b" second="0x1100" result="0x1101"/> <!-- ᄋ + ᄀ → ᄁ -->
+ <item first="0x110c" second="0x1103" result="0x110d"/> <!-- ᄌ + ᄃ → ᄍ -->
+ <item first="0x110c" second="0x1107" result="0x1108"/> <!-- ᄌ + ᄇ → ᄈ -->
+ <item first="0x110c" second="0x1112" result="0x110e"/> <!-- ᄌ + ᄒ → ᄎ -->
+ <item first="0x1112" second="0x1100" result="0x110f"/> <!-- ᄒ + ᄀ → ᄏ -->
+ <item first="0x1112" second="0x1103" result="0x1110"/> <!-- ᄒ + ᄃ → ᄐ -->
+ <item first="0x1112" second="0x1107" result="0x1111"/> <!-- ᄒ + ᄇ → ᄑ -->
+ <item first="0x1112" second="0x110c" result="0x110e"/> <!-- ᄒ + ᄌ → ᄎ -->
+ <item first="0x1161" second="0x1169" result="0x116a"/> <!-- ᅡ + ᅩ → ᅪ -->
+ <item first="0x1161" second="0x1175" result="0x1162"/> <!-- ᅡ + ᅵ → ᅢ -->
+ <item first="0x1162" second="0x1169" result="0x116b"/> <!-- ᅢ + ᅩ → ᅫ -->
+ <item first="0x1163" second="0x1175" result="0x1164"/> <!-- ᅣ + ᅵ → ᅤ -->
+ <item first="0x1165" second="0x116e" result="0x116f"/> <!-- ᅥ + ᅮ → ᅯ -->
+ <item first="0x1165" second="0x1175" result="0x1166"/> <!-- ᅥ + ᅵ → ᅦ -->
+ <item first="0x1166" second="0x116e" result="0x1170"/> <!-- ᅦ + ᅮ → ᅰ -->
+ <item first="0x1167" second="0x1175" result="0x1168"/> <!-- ᅧ + ᅵ → ᅨ -->
+ <item first="0x1169" second="0x1161" result="0x116a"/> <!-- ᅩ + ᅡ → ᅪ -->
+ <item first="0x1169" second="0x1175" result="0x116c"/> <!-- ᅩ + ᅵ → ᅬ -->
+ <item first="0x116a" second="0x1175" result="0x116b"/> <!-- ᅪ + ᅵ → ᅫ -->
+ <item first="0x116c" second="0x1161" result="0x116b"/> <!-- ᅬ + ᅡ → ᅫ -->
+ <item first="0x116e" second="0x1165" result="0x116f"/> <!-- ᅮ + ᅥ → ᅯ -->
+ <item first="0x116e" second="0x1175" result="0x1171"/> <!-- ᅮ + ᅵ → ᅱ -->
+ <item first="0x116f" second="0x1175" result="0x1170"/> <!-- ᅯ + ᅵ → ᅰ -->
+ <item first="0x1171" second="0x1165" result="0x1170"/> <!-- ᅱ + ᅥ → ᅰ -->
+ <item first="0x1173" second="0x1175" result="0x1174"/> <!-- ᅳ + ᅵ → ᅴ -->
+ <item first="0x1175" second="0x1161" result="0x1162"/> <!-- ᅵ + ᅡ → ᅢ -->
+ <item first="0x1175" second="0x1163" result="0x1164"/> <!-- ᅵ + ᅣ → ᅤ -->
+ <item first="0x1175" second="0x1165" result="0x1166"/> <!-- ᅵ + ᅥ → ᅦ -->
+ <item first="0x1175" second="0x1167" result="0x1168"/> <!-- ᅵ + ᅧ → ᅨ -->
+ <item first="0x1175" second="0x1169" result="0x116c"/> <!-- ᅵ + ᅩ → ᅬ -->
+ <item first="0x1175" second="0x116e" result="0x1171"/> <!-- ᅵ + ᅮ → ᅱ -->
+ <item first="0x1175" second="0x1173" result="0x1174"/> <!-- ᅵ + ᅳ → ᅴ -->
+ <item first="0x11a8" second="0x11af" result="0x11b0"/> <!-- ᆨ + ᆯ → ᆰ -->
+ <item first="0x11a8" second="0x11ba" result="0x11aa"/> <!-- ᆨ + ᆺ → ᆪ -->
+ <item first="0x11a8" second="0x11bc" result="0x11a9"/> <!-- ᆨ + ᆼ → ᆩ -->
+ <item first="0x11a8" second="0x11c2" result="0x11bf"/> <!-- ᆨ + ᇂ → ᆿ -->
+ <item first="0x11ab" second="0x11ba" result="0x11bb"/> <!-- ᆫ + ᆺ → ᆻ -->
+ <item first="0x11ab" second="0x11bd" result="0x11ac"/> <!-- ᆫ + ᆽ → ᆬ -->
+ <item first="0x11ab" second="0x11c2" result="0x11ad"/> <!-- ᆫ + ᇂ → ᆭ -->
+ <item first="0x11ae" second="0x11af" result="0x11ce"/> <!-- ᆮ + ᆯ → ᇎ -->
+ <item first="0x11ae" second="0x11c2" result="0x11c0"/> <!-- ᆮ + ᇂ → ᇀ -->
+ <item first="0x11af" second="0x11a8" result="0x11b0"/> <!-- ᆯ + ᆨ → ᆰ -->
+ <item first="0x11af" second="0x11ae" result="0x11ce"/> <!-- ᆯ + ᆮ → ᇎ -->
+ <item first="0x11af" second="0x11b7" result="0x11b1"/> <!-- ᆯ + ᆷ → ᆱ -->
+ <item first="0x11af" second="0x11b8" result="0x11b2"/> <!-- ᆯ + ᆸ → ᆲ -->
+ <item first="0x11af" second="0x11ba" result="0x11b3"/> <!-- ᆯ + ᆺ → ᆳ -->
+ <item first="0x11af" second="0x11c2" result="0x11b6"/> <!-- ᆯ + ᇂ → ᆶ -->
+ <item first="0x11b2" second="0x11c2" result="0x11b5"/> <!-- ᆲ + ᇂ → ᆵ -->
+ <item first="0x11b6" second="0x11ae" result="0x11b4"/> <!-- ᆶ + ᆮ → ᆴ -->
+ <item first="0x11b6" second="0x11b8" result="0x11b5"/> <!-- ᆶ + ᆸ → ᆵ -->
+ <item first="0x11b7" second="0x11af" result="0x11b1"/> <!-- ᆷ + ᆯ → ᆱ -->
+ <item first="0x11b8" second="0x11af" result="0x11b2"/> <!-- ᆸ + ᆯ → ᆲ -->
+ <item first="0x11b8" second="0x11ba" result="0x11b9"/> <!-- ᆸ + ᆺ → ᆹ -->
+ <item first="0x11b8" second="0x11c2" result="0x11c1"/> <!-- ᆸ + ᇂ → ᇁ -->
+ <item first="0x11ba" second="0x11a8" result="0x11aa"/> <!-- ᆺ + ᆨ → ᆪ -->
+ <item first="0x11ba" second="0x11ab" result="0x11bb"/> <!-- ᆺ + ᆫ → ᆻ -->
+ <item first="0x11ba" second="0x11af" result="0x11b3"/> <!-- ᆺ + ᆯ → ᆳ -->
+ <item first="0x11ba" second="0x11b8" result="0x11b9"/> <!-- ᆺ + ᆸ → ᆹ -->
+ <item first="0x11bc" second="0x11a8" result="0x11a9"/> <!-- ᆼ + ᆨ → ᆩ -->
+ <item first="0x11bd" second="0x11ab" result="0x11ac"/> <!-- ᆽ + ᆫ → ᆬ -->
+ <item first="0x11bd" second="0x11c2" result="0x11be"/> <!-- ᆽ + ᇂ → ᆾ -->
+ <item first="0x11c0" second="0x11af" result="0x11b4"/> <!-- ᇀ + ᆯ → ᆴ -->
+ <item first="0x11c1" second="0x11af" result="0x11b5"/> <!-- ᇁ + ᆯ → ᆵ -->
+ <item first="0x11c2" second="0x11a8" result="0x11bf"/> <!-- ᇂ + ᆨ → ᆿ -->
+ <item first="0x11c2" second="0x11ab" result="0x11ad"/> <!-- ᇂ + ᆫ → ᆭ -->
+ <item first="0x11c2" second="0x11ae" result="0x11c0"/> <!-- ᇂ + ᆮ → ᇀ -->
+ <item first="0x11c2" second="0x11af" result="0x11b6"/> <!-- ᇂ + ᆯ → ᆶ -->
+ <item first="0x11c2" second="0x11b8" result="0x11c1"/> <!-- ᇂ + ᆸ → ᇁ -->
+ <item first="0x11c2" second="0x11bd" result="0x11be"/> <!-- ᇂ + ᆽ → ᆾ -->
+ <item first="0x11ce" second="0x11c2" result="0x11b4"/> <!-- ᇎ + ᇂ → ᆴ -->
+ </combination>
+
+</hangul-keyboard>
diff --git a/data/keyboards/hangul-keyboard-ro.xml.template b/data/keyboards/hangul-keyboard-ro.xml.template
new file mode 100644
index 0000000..0f40813
--- /dev/null
+++ b/data/keyboards/hangul-keyboard-ro.xml.template
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hangul-keyboard id="ro" type="romaja">
+
+ <_name>Romaja</_name>
+
+ <map id="0">
+ <item key="0x21" value="0x0021"/> <!-- ! → ! -->
+ <item key="0x22" value="0x0022"/> <!-- " → " -->
+ <item key="0x23" value="0x0023"/> <!-- # → # -->
+ <item key="0x24" value="0x0024"/> <!-- $ → $ -->
+ <item key="0x25" value="0x0025"/> <!-- % → % -->
+ <item key="0x26" value="0x0026"/> <!-- & → & -->
+ <item key="0x27" value="0x0027"/> <!-- ' → ' -->
+ <item key="0x28" value="0x0028"/> <!-- ( → ( -->
+ <item key="0x29" value="0x0029"/> <!-- ) → ) -->
+ <item key="0x2a" value="0x002a"/> <!-- * → * -->
+ <item key="0x2b" value="0x002b"/> <!-- + → + -->
+ <item key="0x2c" value="0x002c"/> <!-- , → , -->
+ <item key="0x2d" value="0x002d"/> <!-- - → - -->
+ <item key="0x2e" value="0x002e"/> <!-- . → . -->
+ <item key="0x2f" value="0x002f"/> <!-- / → / -->
+ <item key="0x30" value="0x0030"/> <!-- 0 → 0 -->
+ <item key="0x31" value="0x0031"/> <!-- 1 → 1 -->
+ <item key="0x32" value="0x0032"/> <!-- 2 → 2 -->
+ <item key="0x33" value="0x0033"/> <!-- 3 → 3 -->
+ <item key="0x34" value="0x0034"/> <!-- 4 → 4 -->
+ <item key="0x35" value="0x0035"/> <!-- 5 → 5 -->
+ <item key="0x36" value="0x0036"/> <!-- 6 → 6 -->
+ <item key="0x37" value="0x0037"/> <!-- 7 → 7 -->
+ <item key="0x38" value="0x0038"/> <!-- 8 → 8 -->
+ <item key="0x39" value="0x0039"/> <!-- 9 → 9 -->
+ <item key="0x3a" value="0x003a"/> <!-- : → : -->
+ <item key="0x3b" value="0x003b"/> <!-- ; → ; -->
+ <item key="0x3c" value="0x003c"/> <!-- < → < -->
+ <item key="0x3d" value="0x003d"/> <!-- = → = -->
+ <item key="0x3e" value="0x003e"/> <!-- > → > -->
+ <item key="0x3f" value="0x003f"/> <!-- ? → ? -->
+ <item key="0x40" value="0x0040"/> <!-- @ → @ -->
+ <item key="0x41" value="0x1161"/> <!-- A → ᅟᅡ -->
+ <item key="0x42" value="0x1107"/> <!-- B → ᄇᅠ -->
+ <item key="0x43" value="0x110e"/> <!-- C → ᄎᅠ -->
+ <item key="0x44" value="0x1103"/> <!-- D → ᄃᅠ -->
+ <item key="0x45" value="0x1166"/> <!-- E → ᅟᅦ -->
+ <item key="0x46" value="0x1111"/> <!-- F → ᄑᅠ -->
+ <item key="0x47" value="0x1100"/> <!-- G → ᄀᅠ -->
+ <item key="0x48" value="0x1112"/> <!-- H → ᄒᅠ -->
+ <item key="0x49" value="0x1175"/> <!-- I → ᅟᅵ -->
+ <item key="0x4a" value="0x110c"/> <!-- J → ᄌᅠ -->
+ <item key="0x4b" value="0x110f"/> <!-- K → ᄏᅠ -->
+ <item key="0x4c" value="0x1105"/> <!-- L → ᄅᅠ -->
+ <item key="0x4d" value="0x1106"/> <!-- M → ᄆᅠ -->
+ <item key="0x4e" value="0x1102"/> <!-- N → ᄂᅠ -->
+ <item key="0x4f" value="0x1169"/> <!-- O → ᅟᅩ -->
+ <item key="0x50" value="0x1111"/> <!-- P → ᄑᅠ -->
+ <item key="0x51" value="0x110f"/> <!-- Q → ᄏᅠ -->
+ <item key="0x52" value="0x1105"/> <!-- R → ᄅᅠ -->
+ <item key="0x53" value="0x1109"/> <!-- S → ᄉᅠ -->
+ <item key="0x54" value="0x1110"/> <!-- T → ᄐᅠ -->
+ <item key="0x55" value="0x116e"/> <!-- U → ᅟᅮ -->
+ <item key="0x56" value="0x1107"/> <!-- V → ᄇᅠ -->
+ <item key="0x57" value="0x116e"/> <!-- W → ᅟᅮ -->
+ <item key="0x58" value="0x110c"/> <!-- X → ᄌᅠ -->
+ <item key="0x59" value="0x1175"/> <!-- Y → ᅟᅵ -->
+ <item key="0x5a" value="0x110c"/> <!-- Z → ᄌᅠ -->
+ <item key="0x5b" value="0x005b"/> <!-- [ → [ -->
+ <item key="0x5c" value="0x005c"/> <!-- \ → \ -->
+ <item key="0x5d" value="0x005d"/> <!-- ] → ] -->
+ <item key="0x5e" value="0x005e"/> <!-- ^ → ^ -->
+ <item key="0x5f" value="0x005f"/> <!-- _ → _ -->
+ <item key="0x60" value="0x0060"/> <!-- ` → ` -->
+ <item key="0x61" value="0x1161"/> <!-- a → ᅟᅡ -->
+ <item key="0x62" value="0x1107"/> <!-- b → ᄇᅠ -->
+ <item key="0x63" value="0x110e"/> <!-- c → ᄎᅠ -->
+ <item key="0x64" value="0x1103"/> <!-- d → ᄃᅠ -->
+ <item key="0x65" value="0x1166"/> <!-- e → ᅟᅦ -->
+ <item key="0x66" value="0x1111"/> <!-- f → ᄑᅠ -->
+ <item key="0x67" value="0x1100"/> <!-- g → ᄀᅠ -->
+ <item key="0x68" value="0x1112"/> <!-- h → ᄒᅠ -->
+ <item key="0x69" value="0x1175"/> <!-- i → ᅟᅵ -->
+ <item key="0x6a" value="0x110c"/> <!-- j → ᄌᅠ -->
+ <item key="0x6b" value="0x110f"/> <!-- k → ᄏᅠ -->
+ <item key="0x6c" value="0x1105"/> <!-- l → ᄅᅠ -->
+ <item key="0x6d" value="0x1106"/> <!-- m → ᄆᅠ -->
+ <item key="0x6e" value="0x1102"/> <!-- n → ᄂᅠ -->
+ <item key="0x6f" value="0x1169"/> <!-- o → ᅟᅩ -->
+ <item key="0x70" value="0x1111"/> <!-- p → ᄑᅠ -->
+ <item key="0x71" value="0x110f"/> <!-- q → ᄏᅠ -->
+ <item key="0x72" value="0x1105"/> <!-- r → ᄅᅠ -->
+ <item key="0x73" value="0x1109"/> <!-- s → ᄉᅠ -->
+ <item key="0x74" value="0x1110"/> <!-- t → ᄐᅠ -->
+ <item key="0x75" value="0x116e"/> <!-- u → ᅟᅮ -->
+ <item key="0x76" value="0x1107"/> <!-- v → ᄇᅠ -->
+ <item key="0x77" value="0x116e"/> <!-- w → ᅟᅮ -->
+ <item key="0x78" value="0x11aa"/> <!-- x → ᅟᅠᆪ -->
+ <item key="0x79" value="0x1175"/> <!-- y → ᅟᅵ -->
+ <item key="0x7a" value="0x110c"/> <!-- z → ᄌᅠ -->
+ <item key="0x7b" value="0x007b"/> <!-- { → { -->
+ <item key="0x7c" value="0x007c"/> <!-- | → | -->
+ <item key="0x7d" value="0x007d"/> <!-- } → } -->
+ <item key="0x7e" value="0x007e"/> <!-- ~ → ~ -->
+ </map>
+
+ <combination id="0">
+ <item first="0x1100" second="0x1100" result="0x1101"/> <!-- ᄀ + ᄀ → ᄁ -->
+ <item first="0x1103" second="0x1103" result="0x1104"/> <!-- ᄃ + ᄃ → ᄄ -->
+ <item first="0x1107" second="0x1107" result="0x1108"/> <!-- ᄇ + ᄇ → ᄈ -->
+ <item first="0x1109" second="0x1109" result="0x110a"/> <!-- ᄉ + ᄉ → ᄊ -->
+ <item first="0x110c" second="0x110c" result="0x110d"/> <!-- ᄌ + ᄌ → ᄍ -->
+ <item first="0x110e" second="0x1112" result="0x110e"/> <!-- ᄎ + ᄒ → ᄎ -->
+ <item first="0x1161" second="0x1166" result="0x1162"/> <!-- ᅡ + ᅦ → ᅢ -->
+ <item first="0x1161" second="0x1175" result="0x1162"/> <!-- ᅡ + ᅵ → ᅢ -->
+ <item first="0x1163" second="0x1166" result="0x1164"/> <!-- ᅣ + ᅦ → ᅤ -->
+ <item first="0x1163" second="0x1175" result="0x1164"/> <!-- ᅣ + ᅵ → ᅤ -->
+ <item first="0x1166" second="0x1169" result="0x1165"/> <!-- ᅦ + ᅩ → ᅥ -->
+ <item first="0x1166" second="0x116e" result="0x1173"/> <!-- ᅦ + ᅮ → ᅳ -->
+ <item first="0x1168" second="0x1169" result="0x1167"/> <!-- ᅨ + ᅩ → ᅧ -->
+ <item first="0x1169" second="0x1161" result="0x116a"/> <!-- ᅩ + ᅡ → ᅪ -->
+ <item first="0x1169" second="0x1162" result="0x116b"/> <!-- ᅩ + ᅢ → ᅫ -->
+ <item first="0x1169" second="0x1175" result="0x116c"/> <!-- ᅩ + ᅵ → ᅬ -->
+ <item first="0x116a" second="0x1166" result="0x116b"/> <!-- ᅪ + ᅦ → ᅫ -->
+ <item first="0x116a" second="0x1175" result="0x116b"/> <!-- ᅪ + ᅵ → ᅫ -->
+ <item first="0x116e" second="0x1161" result="0x116a"/> <!-- ᅮ + ᅡ → ᅪ -->
+ <item first="0x116e" second="0x1165" result="0x116f"/> <!-- ᅮ + ᅥ → ᅯ -->
+ <item first="0x116e" second="0x1166" result="0x1170"/> <!-- ᅮ + ᅦ → ᅰ -->
+ <item first="0x116e" second="0x1169" result="0x116f"/> <!-- ᅮ + ᅩ → ᅯ -->
+ <item first="0x116e" second="0x1175" result="0x1171"/> <!-- ᅮ + ᅵ → ᅱ -->
+ <item first="0x1170" second="0x1169" result="0x116f"/> <!-- ᅰ + ᅩ → ᅯ -->
+ <item first="0x1173" second="0x1175" result="0x1174"/> <!-- ᅳ + ᅵ → ᅴ -->
+ <item first="0x1175" second="0x1161" result="0x1163"/> <!-- ᅵ + ᅡ → ᅣ -->
+ <item first="0x1175" second="0x1162" result="0x1164"/> <!-- ᅵ + ᅢ → ᅤ -->
+ <item first="0x1175" second="0x1165" result="0x1167"/> <!-- ᅵ + ᅥ → ᅧ -->
+ <item first="0x1175" second="0x1166" result="0x1168"/> <!-- ᅵ + ᅦ → ᅨ -->
+ <item first="0x1175" second="0x1169" result="0x116d"/> <!-- ᅵ + ᅩ → ᅭ -->
+ <item first="0x1175" second="0x116e" result="0x1172"/> <!-- ᅵ + ᅮ → ᅲ -->
+ <item first="0x11a8" second="0x11a8" result="0x11a9"/> <!-- ᆨ + ᆨ → ᆩ -->
+ <item first="0x11a8" second="0x11ba" result="0x11aa"/> <!-- ᆨ + ᆺ → ᆪ -->
+ <item first="0x11ab" second="0x11a8" result="0x11bc"/> <!-- ᆫ + ᆨ → ᆼ -->
+ <item first="0x11ab" second="0x11bd" result="0x11ac"/> <!-- ᆫ + ᆽ → ᆬ -->
+ <item first="0x11ab" second="0x11c2" result="0x11ad"/> <!-- ᆫ + ᇂ → ᆭ -->
+ <item first="0x11af" second="0x11a8" result="0x11b0"/> <!-- ᆯ + ᆨ → ᆰ -->
+ <item first="0x11af" second="0x11b7" result="0x11b1"/> <!-- ᆯ + ᆷ → ᆱ -->
+ <item first="0x11af" second="0x11b8" result="0x11b2"/> <!-- ᆯ + ᆸ → ᆲ -->
+ <item first="0x11af" second="0x11ba" result="0x11b3"/> <!-- ᆯ + ᆺ → ᆳ -->
+ <item first="0x11af" second="0x11c0" result="0x11b4"/> <!-- ᆯ + ᇀ → ᆴ -->
+ <item first="0x11af" second="0x11c1" result="0x11b5"/> <!-- ᆯ + ᇁ → ᆵ -->
+ <item first="0x11af" second="0x11c2" result="0x11b6"/> <!-- ᆯ + ᇂ → ᆶ -->
+ <item first="0x11b8" second="0x11ba" result="0x11b9"/> <!-- ᆸ + ᆺ → ᆹ -->
+ <item first="0x11ba" second="0x11ba" result="0x11bb"/> <!-- ᆺ + ᆺ → ᆻ -->
+ </combination>
+
+</hangul-keyboard>
diff --git a/hangul/Makefile.am b/hangul/Makefile.am
index e3213c4..5031b36 100644
--- a/hangul/Makefile.am
+++ b/hangul/Makefile.am
@@ -12,11 +12,16 @@ noinst_HEADERS = \
libhangul_la_SOURCES = \
hangulctype.c \
hangulinputcontext.c \
+ hangulkeyboard.c \
hanja.c
libhangul_la_CFLAGS = \
- -DLOCALEDIR=\"$(localedir)\" \
- -DLIBHANGUL_DEFAULT_HANJA_DIC=\"$(datadir)/libhangul/hanja/hanja.txt\"
+ -DLOCALEDIR=\"$(localedir)\" \
+ -DLIBHANGUL_DEFAULT_HANJA_DIC=\"$(pkgdatadir)/hanja/hanja.txt\" \
+ -DLIBHANGUL_DATA_DIR=\"$(pkgdatadir)\" \
+ -DTOP_SRCDIR=\"$(abs_top_srcdir)\" \
+ $(EXPAT_CFLAGS) \
+ $(NULL)
libhangul_la_LDFLAGS = -version-info $(LIBHANGUL_CURRENT):$(LIBHANGUL_REVISION):$(LIBHANGUL_AGE)
-libhangul_la_LIBADD =
+libhangul_la_LIBADD = $(EXPAT_LIBS)
diff --git a/hangul/hangul.h b/hangul/hangul.h
index fe85124..447e1e3 100644
--- a/hangul/hangul.h
+++ b/hangul/hangul.h
@@ -84,7 +84,9 @@ enum {
enum {
HANGUL_KEYBOARD_TYPE_JAMO,
HANGUL_KEYBOARD_TYPE_JASO,
- HANGUL_KEYBOARD_TYPE_ROMAJA
+ HANGUL_KEYBOARD_TYPE_ROMAJA,
+ HANGUL_KEYBOARD_TYPE_JAMO_YET,
+ HANGUL_KEYBOARD_TYPE_JASO_YET,
};
enum {
@@ -93,13 +95,19 @@ enum {
HANGUL_IC_OPTION_NON_CHOSEONG_COMBI,
};
+/* library */
+int hangul_init();
+int hangul_fini();
+
/* keyboard */
HangulKeyboard* hangul_keyboard_new(void);
void hangul_keyboard_delete(HangulKeyboard *keyboard);
-void hangul_keyboard_set_value(HangulKeyboard *keyboard,
- int key, ucschar value);
void hangul_keyboard_set_type(HangulKeyboard *keyboard, int type);
+unsigned int hangul_keyboard_list_get_count();
+const char* hangul_keyboard_list_get_keyboard_id(unsigned index_);
+const char* hangul_keyboard_list_get_keyboard_name(unsigned index_);
+
/* combination */
HangulCombination* hangul_combination_new(void);
void hangul_combination_delete(HangulCombination *combination);
@@ -126,15 +134,9 @@ void hangul_ic_set_keyboard(HangulInputContext *hic,
const HangulKeyboard *keyboard);
void hangul_ic_select_keyboard(HangulInputContext *hic,
const char* id);
-void hangul_ic_set_combination(HangulInputContext *hic,
- const HangulCombination *combination);
void hangul_ic_connect_callback(HangulInputContext* hic, const char* event,
void* callback, void* user_data);
-unsigned hangul_ic_get_n_keyboards();
-const char* hangul_ic_get_keyboard_id(unsigned index_);
-const char* hangul_ic_get_keyboard_name(unsigned index_);
-
const ucschar* hangul_ic_get_preedit_string(HangulInputContext *hic);
const ucschar* hangul_ic_get_commit_string(HangulInputContext *hic);
const ucschar* hangul_ic_flush(HangulInputContext *hic);
@@ -166,6 +168,15 @@ const char* hanja_get_comment(const Hanja* hanja);
}
#endif
+void hangul_keyboard_set_value(HangulKeyboard *keyboard,
+ int key, ucschar value) LIBHANGUL_DEPRECATED;
+void hangul_ic_set_combination(HangulInputContext *hic,
+ const HangulCombination *combination) LIBHANGUL_DEPRECATED;
+
+unsigned hangul_ic_get_n_keyboards() LIBHANGUL_DEPRECATED;
+const char* hangul_ic_get_keyboard_id(unsigned index_) LIBHANGUL_DEPRECATED;
+const char* hangul_ic_get_keyboard_name(unsigned index_) LIBHANGUL_DEPRECATED;
+
#undef LIBHANGUL_DEPRECATED
#endif /* libhangul_hangul_h */
diff --git a/hangul/hangulinputcontext.c b/hangul/hangulinputcontext.c
index 83ce52e..504d507 100644
--- a/hangul/hangulinputcontext.c
+++ b/hangul/hangulinputcontext.c
@@ -1,5 +1,5 @@
/* libhangul
- * Copyright (C) 2004 - 2009 Choe Hwanjin
+ * Copyright (C) 2004 - 2016 Choe Hwanjin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -175,8 +175,6 @@
#define FALSE 0
#endif
-#define HANGUL_KEYBOARD_TABLE_SIZE 0x80
-
typedef void (*HangulOnTranslate) (HangulInputContext*,
int,
ucschar*,
@@ -186,26 +184,6 @@ typedef bool (*HangulOnTransition) (HangulInputContext*,
const ucschar*,
void*);
-typedef struct _HangulCombinationItem HangulCombinationItem;
-
-struct _HangulKeyboard {
- int type;
- const char* id;
- const char* name;
- const ucschar* table;
- const HangulCombination* combination;
-};
-
-struct _HangulCombinationItem {
- uint32_t key;
- ucschar code;
-};
-
-struct _HangulCombination {
- int size;
- HangulCombinationItem *table;
-};
-
struct _HangulBuffer {
ucschar choseong;
ucschar jungseong;
@@ -239,113 +217,6 @@ struct _HangulInputContext {
unsigned int option_non_choseong_combi : 1;
};
-#include "hangulkeyboard.h"
-
-static const HangulCombination hangul_combination_default = {
- N_ELEMENTS(hangul_combination_table_default),
- (HangulCombinationItem*)hangul_combination_table_default
-};
-
-static const HangulCombination hangul_combination_romaja = {
- N_ELEMENTS(hangul_combination_table_romaja),
- (HangulCombinationItem*)hangul_combination_table_romaja
-};
-
-static const HangulCombination hangul_combination_full = {
- N_ELEMENTS(hangul_combination_table_full),
- (HangulCombinationItem*)hangul_combination_table_full
-};
-
-static const HangulCombination hangul_combination_ahn = {
- N_ELEMENTS(hangul_combination_table_ahn),
- (HangulCombinationItem*)hangul_combination_table_ahn
-};
-
-static const HangulKeyboard hangul_keyboard_2 = {
- HANGUL_KEYBOARD_TYPE_JAMO,
- "2",
- N_("Dubeolsik"),
- (ucschar*)hangul_keyboard_table_2,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_2y = {
- HANGUL_KEYBOARD_TYPE_JAMO,
- "2y",
- N_("Dubeolsik Yetgeul"),
- (ucschar*)hangul_keyboard_table_2y,
- &hangul_combination_full
-};
-
-static const HangulKeyboard hangul_keyboard_32 = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "32",
- N_("Sebeolsik Dubeol Layout"),
- (ucschar*)hangul_keyboard_table_32,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_390 = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "39",
- N_("Sebeolsik 390"),
- (ucschar*)hangul_keyboard_table_390,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_3final = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "3f",
- N_("Sebeolsik Final"),
- (ucschar*)hangul_keyboard_table_3final,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_3sun = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "3s",
- N_("Sebeolsik Noshift"),
- (ucschar*)hangul_keyboard_table_3sun,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_3yet = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "3y",
- N_("Sebeolsik Yetgeul"),
- (ucschar*)hangul_keyboard_table_3yet,
- &hangul_combination_full
-};
-
-static const HangulKeyboard hangul_keyboard_romaja = {
- HANGUL_KEYBOARD_TYPE_ROMAJA,
- "ro",
- N_("Romaja"),
- (ucschar*)hangul_keyboard_table_romaja,
- &hangul_combination_romaja
-};
-
-static const HangulKeyboard hangul_keyboard_ahn = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "ahn",
- N_("Ahnmatae"),
- (ucschar*)hangul_keyboard_table_ahn,
- &hangul_combination_ahn
-};
-
-static const HangulKeyboard* hangul_keyboards[] = {
- &hangul_keyboard_2,
- &hangul_keyboard_2y,
- &hangul_keyboard_390,
- &hangul_keyboard_3final,
- &hangul_keyboard_3sun,
- &hangul_keyboard_3yet,
- &hangul_keyboard_32,
- &hangul_keyboard_romaja,
- &hangul_keyboard_ahn,
-};
-
-
static void hangul_buffer_push(HangulBuffer *buffer, ucschar ch);
static ucschar hangul_buffer_pop (HangulBuffer *buffer);
static ucschar hangul_buffer_peek(HangulBuffer *buffer);
@@ -356,162 +227,6 @@ static int hangul_buffer_get_jamo_string(HangulBuffer *buffer, ucschar *buf,
static void hangul_ic_flush_internal(HangulInputContext *hic);
-HangulKeyboard*
-hangul_keyboard_new()
-{
- HangulKeyboard *keyboard = malloc(sizeof(HangulKeyboard));
- if (keyboard != NULL) {
- ucschar* table = malloc(sizeof(ucschar) * HANGUL_KEYBOARD_TABLE_SIZE);
- if (table != NULL) {
- int i;
- for (i = 0; i < HANGUL_KEYBOARD_TABLE_SIZE; i++)
- table[i] = 0;
-
- keyboard->table = table;
- return keyboard;
- }
- free(keyboard);
- }
-
- return NULL;
-}
-
-static ucschar
-hangul_keyboard_get_value(const HangulKeyboard *keyboard, int key)
-{
- if (keyboard != NULL) {
- if (key >= 0 && key < HANGUL_KEYBOARD_TABLE_SIZE)
- return keyboard->table[key];
- }
-
- return 0;
-}
-
-void
-hangul_keyboard_set_value(HangulKeyboard *keyboard, int key, ucschar value)
-{
- if (keyboard != NULL) {
- if (key >= 0 && key < HANGUL_KEYBOARD_TABLE_SIZE) {
- ucschar* table = (ucschar*)keyboard->table;
- table[key] = value;
- }
- }
-}
-
-static int
-hangul_keyboard_get_type(const HangulKeyboard *keyboard)
-{
- int type = 0;
- if (keyboard != NULL) {
- type = keyboard->type;
- }
- return type;
-}
-
-void
-hangul_keyboard_set_type(HangulKeyboard *keyboard, int type)
-{
- if (keyboard != NULL) {
- keyboard->type = type;
- }
-}
-
-void
-hangul_keyboard_delete(HangulKeyboard *keyboard)
-{
- if (keyboard != NULL)
- free(keyboard);
-}
-
-HangulCombination*
-hangul_combination_new()
-{
- HangulCombination *combination = malloc(sizeof(HangulCombination));
- if (combination != NULL) {
- combination->size = 0;
- combination->table = NULL;
- return combination;
- }
-
- return NULL;
-}
-
-void
-hangul_combination_delete(HangulCombination *combination)
-{
- if (combination != NULL) {
- if (combination->table != NULL)
- free(combination->table);
- free(combination);
- }
-}
-
-static uint32_t
-hangul_combination_make_key(ucschar first, ucschar second)
-{
- return first << 16 | second;
-}
-
-bool
-hangul_combination_set_data(HangulCombination* combination,
- ucschar* first, ucschar* second, ucschar* result,
- unsigned int n)
-{
- if (combination == NULL)
- return false;
-
- if (n == 0 || n > ULONG_MAX / sizeof(HangulCombinationItem))
- return false;
-
- combination->table = malloc(sizeof(HangulCombinationItem) * n);
- if (combination->table != NULL) {
- int i;
-
- combination->size = n;
- for (i = 0; i < n; i++) {
- combination->table[i].key = hangul_combination_make_key(first[i], second[i]);
- combination->table[i].code = result[i];
- }
- return true;
- }
-
- return false;
-}
-
-static int
-hangul_combination_cmp(const void* p1, const void* p2)
-{
- const HangulCombinationItem *item1 = p1;
- const HangulCombinationItem *item2 = p2;
-
- /* key는 unsigned int이므로 단순히 빼서 리턴하면 안된다.
- * 두 수의 차가 큰 경우 int로 변환하면서 음수가 될 수 있다. */
- if (item1->key < item2->key)
- return -1;
- else if (item1->key > item2->key)
- return 1;
- else
- return 0;
-}
-
-ucschar
-hangul_combination_combine(const HangulCombination* combination,
- ucschar first, ucschar second)
-{
- HangulCombinationItem *res;
- HangulCombinationItem key;
-
- if (combination == NULL)
- return 0;
-
- key.key = hangul_combination_make_key(first, second);
- res = bsearch(&key, combination->table, combination->size,
- sizeof(combination->table[0]), hangul_combination_cmp);
- if (res != NULL)
- return res->code;
-
- return 0;
-}
static bool
hangul_buffer_is_empty(HangulBuffer *buffer)
@@ -845,7 +560,10 @@ hangul_ic_choseong_to_jongseong(HangulInputContext* hic, ucschar cho)
} else {
/* 옛글 조합 규칙을 사용하는 자판의 경우에는 종성이 conjoinable
* 하지 않아도 상관없다 */
- if (hic->keyboard->combination == &hangul_combination_full) {
+ int type = hangul_keyboard_get_type(hic->keyboard);
+ switch (type) {
+ case HANGUL_KEYBOARD_TYPE_JAMO_YET:
+ case HANGUL_KEYBOARD_TYPE_JASO_YET:
return jong;
}
}
@@ -868,8 +586,7 @@ hangul_ic_combine(HangulInputContext* hic, ucschar first, ucschar second)
}
ucschar combined = 0;
- combined = hangul_combination_combine(hic->keyboard->combination,
- first, second);
+ combined = hangul_keyboard_combine(hic->keyboard, 0, first, second);
if (!hic->option_non_choseong_combi) {
if (hangul_is_choseong(first) && hangul_is_choseong(second) &&
@@ -1362,7 +1079,7 @@ hangul_ic_process(HangulInputContext *hic, int ascii)
hic->preedit_string[0] = 0;
hic->commit_string[0] = 0;
- c = hangul_keyboard_get_value(hic->keyboard, ascii);
+ c = hangul_keyboard_get_mapping(hic->keyboard, 0, ascii);
if (hic->on_translate != NULL)
hic->on_translate(hic, ascii, &c, hic->on_translate_data);
@@ -1370,12 +1087,16 @@ hangul_ic_process(HangulInputContext *hic, int ascii)
return hangul_ic_backspace(hic);
}
- if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JAMO)
- return hangul_ic_process_jamo(hic, c);
- else if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JASO)
+ int type = hangul_keyboard_get_type(hic->keyboard);
+ switch (type) {
+ case HANGUL_KEYBOARD_TYPE_JASO:
+ case HANGUL_KEYBOARD_TYPE_JASO_YET:
return hangul_ic_process_jaso(hic, c);
- else
+ case HANGUL_KEYBOARD_TYPE_ROMAJA:
return hangul_ic_process_romaja(hic, ascii, c);
+ default:
+ return hangul_ic_process_jamo(hic, c);
+ }
}
/**
@@ -1676,25 +1397,6 @@ hangul_ic_set_keyboard(HangulInputContext *hic, const HangulKeyboard* keyboard)
hic->keyboard = keyboard;
}
-static const HangulKeyboard*
-hangul_ic_get_keyboard_by_id(const char* id)
-{
- unsigned i;
- unsigned n;
-
- /* hangul_keyboards 테이블은 id 순으로 정렬되어 있지 않으므로
- * binary search를 할수 없고 linear search를 한다. */
- n = hangul_ic_get_n_keyboards();
- for (i = 0; i < n; ++i) {
- const HangulKeyboard* keyboard = hangul_keyboards[i];
- if (strcmp(id, keyboard->id) == 0) {
- return keyboard;
- }
- }
-
- return NULL;
-}
-
/**
* @ingroup hangulic
* @brief @ref HangulInputContext 의 자판 배열을 바꾸는 함수
@@ -1729,12 +1431,8 @@ hangul_ic_select_keyboard(HangulInputContext *hic, const char* id)
if (id == NULL)
id = "2";
- keyboard = hangul_ic_get_keyboard_by_id(id);
- if (keyboard != NULL) {
- hic->keyboard = keyboard;
- } else {
- hic->keyboard = &hangul_keyboard_2;
- }
+ keyboard = hangul_keyboard_list_get_keyboard(id);
+ hic->keyboard = keyboard;
}
void
@@ -1811,36 +1509,19 @@ hangul_ic_delete(HangulInputContext *hic)
unsigned int
hangul_ic_get_n_keyboards()
{
- return N_ELEMENTS(hangul_keyboards);
+ return hangul_keyboard_list_get_count();
}
const char*
hangul_ic_get_keyboard_id(unsigned index_)
{
- if (index_ < N_ELEMENTS(hangul_keyboards)) {
- return hangul_keyboards[index_]->id;
- }
-
- return NULL;
+ return hangul_keyboard_list_get_keyboard_id(index_);
}
const char*
hangul_ic_get_keyboard_name(unsigned index_)
{
-#ifdef ENABLE_NLS
- static bool isGettextInitialized = false;
- if (!isGettextInitialized) {
- isGettextInitialized = true;
- bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
- bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
- }
-#endif
-
- if (index_ < N_ELEMENTS(hangul_keyboards)) {
- return _(hangul_keyboards[index_]->name);
- }
-
- return NULL;
+ return hangul_keyboard_list_get_keyboard_name(index_);
}
/**
@@ -1869,3 +1550,19 @@ hangul_ic_is_transliteration(HangulInputContext *hic)
return false;
}
+
+int
+hangul_init()
+{
+ int res;
+ res = hangul_keyboard_list_init();
+ return res;
+}
+
+int
+hangul_fini()
+{
+ int res;
+ res = hangul_keyboard_list_fini();
+ return res;
+}
diff --git a/hangul/hangulinternals.h b/hangul/hangulinternals.h
index 70f8857..8fb0d70 100644
--- a/hangul/hangulinternals.h
+++ b/hangul/hangulinternals.h
@@ -1,7 +1,27 @@
+/* libhangul
+ * Copyright (C) 2016 Choe Hwanjin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
#ifndef libhangul_hangulinternals_h
#define libhangul_hangulinternals_h
#define N_ELEMENTS(array) (sizeof (array) / sizeof ((array)[0]))
+#ifndef countof
+#define countof(array) (sizeof (array) / sizeof ((array)[0]))
+#endif
ucschar hangul_jongseong_get_diff(ucschar prevjong, ucschar jong);
@@ -9,4 +29,15 @@ ucschar hangul_choseong_to_jongseong(ucschar ch);
ucschar hangul_jongseong_to_choseong(ucschar ch);
void hangul_jongseong_decompose(ucschar ch, ucschar* jong, ucschar* cho);
+int hangul_keyboard_get_type(const HangulKeyboard *keyboard);
+ucschar hangul_keyboard_combine(const HangulKeyboard* keyboard,
+ unsigned id, ucschar first, ucschar second);
+ucschar hangul_keyboard_get_mapping(const HangulKeyboard* keyboard,
+ int tableid, unsigned key);
+
+int hangul_keyboard_list_init();
+int hangul_keyboard_list_fini();
+
+const HangulKeyboard* hangul_keyboard_list_get_keyboard(const char* id);
+
#endif /* libhangul_hangulinternals_h */
diff --git a/hangul/hangulkeyboard.c b/hangul/hangulkeyboard.c
new file mode 100644
index 0000000..168774a
--- /dev/null
+++ b/hangul/hangulkeyboard.c
@@ -0,0 +1,950 @@
+/* libhangul
+ * Copyright (C) 2016 Choe Hwanjin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <locale.h>
+#include <glob.h>
+#include <libgen.h>
+#include <expat.h>
+
+#include "hangul-gettext.h"
+#include "hangul.h"
+#include "hangulinternals.h"
+
+
+#define LIBHANGUL_KEYBOARD_DIR LIBHANGUL_DATA_DIR "/keyboards"
+//#define LIBHANGUL_KEYBOARD_DIR TOP_SRCDIR "/data/keyboards"
+
+#define HANGUL_KEYBOARD_TABLE_SIZE 0x80
+
+typedef struct _HangulCombinationItem HangulCombinationItem;
+
+struct _HangulCombinationItem {
+ uint32_t key;
+ ucschar code;
+};
+
+struct _HangulCombination {
+ size_t size;
+ size_t size_alloced;
+ HangulCombinationItem *table;
+
+ bool is_static;
+};
+
+struct _HangulKeyboard {
+ char* id;
+ char* name;
+ ucschar* table[4];
+ HangulCombination* combination[4];
+
+ int type;
+ bool is_static;
+};
+
+typedef struct _HangulKeyboardList {
+ size_t n;
+ size_t nalloced;
+ HangulKeyboard** keyboards;
+} HangulKeyboardList;
+
+#include "hangulkeyboard.h"
+
+static const HangulCombination hangul_combination_default = {
+ countof(hangul_combination_table_default),
+ countof(hangul_combination_table_default),
+ (HangulCombinationItem*)hangul_combination_table_default,
+ true
+};
+
+static const HangulCombination hangul_combination_romaja = {
+ countof(hangul_combination_table_romaja),
+ countof(hangul_combination_table_romaja),
+ (HangulCombinationItem*)hangul_combination_table_romaja,
+ true
+};
+
+static const HangulCombination hangul_combination_full = {
+ countof(hangul_combination_table_full),
+ countof(hangul_combination_table_full),
+ (HangulCombinationItem*)hangul_combination_table_full,
+ true
+};
+
+static const HangulCombination hangul_combination_ahn = {
+ countof(hangul_combination_table_ahn),
+ countof(hangul_combination_table_ahn),
+ (HangulCombinationItem*)hangul_combination_table_ahn,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_2 = {
+ (char*)"2",
+ (char*)N_("Dubeolsik"),
+ { (ucschar*)hangul_keyboard_table_2, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JAMO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_2y = {
+ (char*)"2y",
+ (char*)N_("Dubeolsik Yetgeul"),
+ { (ucschar*)hangul_keyboard_table_2y, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_full, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JAMO_YET,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_32 = {
+ (char*)"32",
+ (char*)N_("Sebeolsik Dubeol Layout"),
+ { (ucschar*)hangul_keyboard_table_32, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_390 = {
+ (char*)"39",
+ (char*)N_("Sebeolsik 390"),
+ { (ucschar*)hangul_keyboard_table_390, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_3final = {
+ (char*)"3f",
+ (char*)N_("Sebeolsik Final"),
+ { (ucschar*)hangul_keyboard_table_3final, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_3sun = {
+ (char*)"3s",
+ (char*)N_("Sebeolsik Noshift"),
+ { (ucschar*)hangul_keyboard_table_3sun, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_3yet = {
+ (char*)"3y",
+ (char*)N_("Sebeolsik Yetgeul"),
+ { (ucschar*)hangul_keyboard_table_3yet, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_full, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO_YET,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_romaja = {
+ (char*)"ro",
+ (char*)N_("Romaja"),
+ { (ucschar*)hangul_keyboard_table_romaja, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_romaja, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_ROMAJA,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_ahn = {
+ (char*)"ahn",
+ (char*)N_("Ahnmatae"),
+ { (ucschar*)hangul_keyboard_table_ahn, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_ahn, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard* hangul_builtin_keyboards[] = {
+ &hangul_keyboard_2,
+ &hangul_keyboard_2y,
+ &hangul_keyboard_390,
+ &hangul_keyboard_3final,
+ &hangul_keyboard_3sun,
+ &hangul_keyboard_3yet,
+ &hangul_keyboard_32,
+ &hangul_keyboard_romaja,
+ &hangul_keyboard_ahn,
+};
+static unsigned int hangul_builtin_keyboard_count = countof(hangul_builtin_keyboards);
+
+static HangulKeyboardList hangul_keyboards = { 0, 0, NULL };
+
+typedef struct _HangulKeyboardLoadContext {
+ const char* path;
+ HangulKeyboard* keyboard;
+ int current_id;
+ const char* current_element;
+ bool save_name;
+} HangulKeyboardLoadContext;
+
+static void hangul_keyboard_parse_file(const char* path, void* user_data);
+static bool hangul_keyboard_list_append(HangulKeyboard* keyboard);
+
+
+HangulCombination*
+hangul_combination_new()
+{
+ HangulCombination *combination = malloc(sizeof(HangulCombination));
+ if (combination != NULL) {
+ combination->size = 0;
+ combination->size_alloced = 0;
+ combination->table = NULL;
+ combination->is_static = false;
+ return combination;
+ }
+
+ return NULL;
+}
+
+void
+hangul_combination_delete(HangulCombination *combination)
+{
+ if (combination == NULL)
+ return;
+
+ if (combination->is_static)
+ return;
+
+ if (combination->table != NULL)
+ free(combination->table);
+
+ free(combination);
+}
+
+static uint32_t
+hangul_combination_make_key(ucschar first, ucschar second)
+{
+ return first << 16 | second;
+}
+
+bool
+hangul_combination_set_data(HangulCombination* combination,
+ ucschar* first, ucschar* second, ucschar* result,
+ unsigned int n)
+{
+ if (combination == NULL)
+ return false;
+
+ if (n == 0 || n > ULONG_MAX / sizeof(HangulCombinationItem))
+ return false;
+
+ combination->table = malloc(sizeof(HangulCombinationItem) * n);
+ if (combination->table != NULL) {
+ int i;
+
+ combination->size = n;
+ for (i = 0; i < n; i++) {
+ combination->table[i].key = hangul_combination_make_key(first[i], second[i]);
+ combination->table[i].code = result[i];
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+hangul_combination_add_item(HangulCombination* combination,
+ ucschar first, ucschar second, ucschar result)
+{
+ if (combination == NULL)
+ return false;
+
+ if (combination->is_static)
+ return false;
+
+ if (combination->size >= combination->size_alloced) {
+ size_t size_need = combination->size_alloced * 2;
+ if (size_need == 0) {
+ // 처음 할당할 때에는 64개를 기본값으로 한다.
+ size_need = 64;
+ }
+
+ HangulCombinationItem* table = combination->table;
+ table = realloc(table, size_need * sizeof(table[0]));
+ if (table == NULL)
+ return false;
+
+ combination->size_alloced = size_need;
+ combination->table = table;
+ }
+
+ uint32_t key = hangul_combination_make_key(first, second);
+ size_t i = combination->size;
+ combination->table[i].key = key;
+ combination->table[i].code = result;
+ combination->size = i + 1;
+ return true;
+}
+
+static int
+hangul_combination_cmp(const void* p1, const void* p2)
+{
+ const HangulCombinationItem *item1 = p1;
+ const HangulCombinationItem *item2 = p2;
+
+ /* key는 unsigned int이므로 단순히 빼서 리턴하면 안된다.
+ * 두 수의 차가 큰 경우 int로 변환하면서 음수가 될 수 있다. */
+ if (item1->key < item2->key)
+ return -1;
+ else if (item1->key > item2->key)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+hangul_combination_sort(HangulCombination* combination)
+{
+ if (combination == NULL)
+ return;
+
+ if (combination->is_static)
+ return;
+
+ qsort(combination->table, combination->size,
+ sizeof(combination->table[0]), hangul_combination_cmp);
+}
+
+static ucschar
+hangul_combination_combine(const HangulCombination* combination,
+ ucschar first, ucschar second)
+{
+ HangulCombinationItem *res;
+ HangulCombinationItem key;
+
+ if (combination == NULL)
+ return 0;
+
+ key.key = hangul_combination_make_key(first, second);
+ res = bsearch(&key, combination->table, combination->size,
+ sizeof(combination->table[0]), hangul_combination_cmp);
+ if (res != NULL)
+ return res->code;
+
+ return 0;
+}
+
+HangulKeyboard*
+hangul_keyboard_new()
+{
+ HangulKeyboard *keyboard = malloc(sizeof(HangulKeyboard));
+ if (keyboard == NULL)
+ return NULL;
+
+ keyboard->id = NULL;
+ keyboard->name = NULL;
+
+ keyboard->table[0] = NULL;
+ keyboard->table[1] = NULL;
+ keyboard->table[2] = NULL;
+ keyboard->table[3] = NULL;
+
+ keyboard->combination[0] = NULL;
+ keyboard->combination[1] = NULL;
+ keyboard->combination[2] = NULL;
+ keyboard->combination[3] = NULL;
+
+ keyboard->type = HANGUL_KEYBOARD_TYPE_JAMO;
+ keyboard->is_static = false;
+
+ return keyboard;
+}
+
+static void
+hangul_keyboard_set_id(HangulKeyboard* keyboard, const char* id)
+{
+ if (keyboard == NULL)
+ return;
+
+ if (keyboard->is_static)
+ return;
+
+ free(keyboard->id);
+ keyboard->id = strdup(id);
+}
+
+static void
+hangul_keyboard_set_name(HangulKeyboard* keyboard, const char* name)
+{
+ if (keyboard == NULL)
+ return;
+
+ if (keyboard->is_static)
+ return;
+
+ free(keyboard->name);
+ keyboard->name = strdup(name);
+}
+
+ucschar
+hangul_keyboard_get_mapping(const HangulKeyboard* keyboard, int tableid, unsigned key)
+{
+ if (keyboard == NULL)
+ return 0;
+
+ if (tableid >= countof(keyboard->table))
+ return 0;
+
+ if (key >= HANGUL_KEYBOARD_TABLE_SIZE)
+ return 0;
+
+ ucschar* table = keyboard->table[tableid];
+ if (table == NULL)
+ return 0;
+
+ return table[key];
+}
+
+static void
+hangul_keyboard_set_mapping(HangulKeyboard *keyboard, int tableid, unsigned key, ucschar value)
+{
+ if (keyboard == NULL)
+ return;
+
+ if (tableid >= countof(keyboard->table))
+ return;
+
+ if (key >= HANGUL_KEYBOARD_TABLE_SIZE)
+ return;
+
+ if (keyboard->table[tableid] == NULL) {
+ ucschar* new_table = malloc(sizeof(ucschar) * HANGUL_KEYBOARD_TABLE_SIZE);
+ if (new_table == NULL)
+ return;
+
+ unsigned i;
+ for (i = 0; i < HANGUL_KEYBOARD_TABLE_SIZE; ++i) {
+ new_table[i] = 0;
+ }
+ keyboard->table[tableid] = new_table;
+ }
+
+ ucschar* table = keyboard->table[tableid];
+ table[key] = value;
+}
+
+void
+hangul_keyboard_set_value(HangulKeyboard *keyboard, int key, ucschar value)
+{
+ hangul_keyboard_set_mapping(keyboard, 0, key, value);
+}
+
+int
+hangul_keyboard_get_type(const HangulKeyboard *keyboard)
+{
+ int type = 0;
+ if (keyboard != NULL) {
+ type = keyboard->type;
+ }
+ return type;
+}
+
+void
+hangul_keyboard_set_type(HangulKeyboard *keyboard, int type)
+{
+ if (keyboard != NULL) {
+ keyboard->type = type;
+ }
+}
+
+void
+hangul_keyboard_delete(HangulKeyboard *keyboard)
+{
+ if (keyboard == NULL)
+ return;
+
+ if (keyboard->is_static)
+ return;
+
+ free(keyboard->id);
+ free(keyboard->name);
+
+ unsigned i;
+ for (i = 0; i < countof(keyboard->table); ++i) {
+ if (keyboard->table[i] != NULL) {
+ free(keyboard->table[i]);
+ }
+ }
+
+ for (i = 0; i < countof(keyboard->combination); ++i) {
+ if (keyboard->combination[i] != NULL) {
+ hangul_combination_delete(keyboard->combination[i]);
+ }
+ }
+
+ free(keyboard);
+}
+
+ucschar
+hangul_keyboard_combine(const HangulKeyboard* keyboard,
+ unsigned id, ucschar first, ucschar second)
+{
+ if (keyboard == NULL)
+ return 0;
+
+ if (id >= countof(keyboard->combination))
+ return 0;
+
+ HangulCombination* combination = keyboard->combination[id];
+ ucschar res = hangul_combination_combine(combination, first, second);
+ return res;
+}
+
+static const char*
+attr_lookup(const char** attr, const char* name)
+{
+ if (attr == NULL)
+ return NULL;
+
+ int i;
+ for (i = 0; attr[i] != NULL; i += 2) {
+ if (strcmp(attr[i], name) == 0) {
+ return attr[i + 1];
+ }
+ }
+
+ return NULL;
+}
+
+static unsigned int
+attr_lookup_as_uint(const char** attr, const char* name)
+{
+ const char* valuestr = attr_lookup(attr, name);
+ if (valuestr == NULL)
+ return 0;
+
+ unsigned int value = strtoul(valuestr, NULL, 0);
+ return value;
+}
+
+static void XMLCALL
+on_element_start(void* data, const XML_Char* element, const XML_Char** attr)
+{
+ HangulKeyboardLoadContext* context = (HangulKeyboardLoadContext*)data;
+
+ if (strcmp(element, "hangul-keyboard") == 0) {
+ if (context->keyboard != NULL) {
+ hangul_keyboard_delete(context->keyboard);
+ }
+ context->keyboard = hangul_keyboard_new();
+
+ const char* id = attr_lookup(attr, "id");
+ hangul_keyboard_set_id(context->keyboard, id);
+
+ const char* typestr = attr_lookup(attr, "type");
+ int type = HANGUL_KEYBOARD_TYPE_JAMO;
+ if (strcmp(typestr, "jamo") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_JAMO;
+ } else if (strcmp(typestr, "jamo-yet") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_JAMO_YET;
+ } else if (strcmp(typestr, "jaso") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_JASO;
+ } else if (strcmp(typestr, "jaso-yet") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_JASO_YET;
+ } else if (strcmp(typestr, "romaja") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_ROMAJA;
+ }
+
+ hangul_keyboard_set_type(context->keyboard, type);
+ } else if (strcmp(element, "name") == 0) {
+ if (context->keyboard == NULL)
+ return;
+
+ const char* lang = attr_lookup(attr, "xml:lang");
+ if (lang == NULL) {
+ context->save_name = true;
+ } else {
+ const char* locale = setlocale(LC_ALL, NULL);
+ size_t n = strlen(lang);
+ if (strncmp(lang, locale, n) == 0) {
+ context->save_name = true;
+ }
+ }
+ context->current_element = "name";
+ } else if (strcmp(element, "map") == 0) {
+ if (context->keyboard == NULL)
+ return;
+
+ unsigned int id = attr_lookup_as_uint(attr, "id");
+ if (id < countof(context->keyboard->table)) {
+ context->current_id = id;
+ context->current_element = "map";
+ }
+ } else if (strcmp(element, "combination") == 0) {
+ if (context->keyboard == NULL)
+ return;
+
+ unsigned int id = attr_lookup_as_uint(attr, "id");
+ if (id < countof(context->keyboard->combination)) {
+ if (context->keyboard->combination[id] != NULL) {
+ hangul_combination_delete(context->keyboard->combination[id]);
+ }
+
+ context->current_id = id;
+ context->current_element = "combination";
+ context->keyboard->combination[id] = hangul_combination_new();
+ }
+ } else if (strcmp(element, "item") == 0) {
+ if (context->keyboard == NULL)
+ return;
+
+ unsigned int id = context->current_id;
+ if (strcmp(context->current_element, "map") == 0) {
+ HangulKeyboard* keyboard = context->keyboard;
+ unsigned int key = attr_lookup_as_uint(attr, "key");
+ unsigned int value = attr_lookup_as_uint(attr, "value");
+ hangul_keyboard_set_mapping(keyboard, id, key, value);
+ } else if (strcmp(context->current_element, "combination") == 0) {
+ HangulCombination* combination = context->keyboard->combination[id];
+ unsigned int first = attr_lookup_as_uint(attr, "first");
+ unsigned int second = attr_lookup_as_uint(attr, "second");
+ unsigned int result = attr_lookup_as_uint(attr, "result");
+ hangul_combination_add_item(combination, first, second, result);
+ }
+ } else if (strcmp(element, "include") == 0) {
+ const char* file = attr_lookup(attr, "file");
+ if (file == NULL)
+ return;
+
+ size_t n = strlen(file) + strlen(context->path) + 1;
+ char* path = malloc(n);
+ if (path == NULL)
+ return;
+
+ if (file[0] == '/') {
+ strncpy(path, file, n);
+ } else {
+ char* orig_path = strdup(context->path);
+ char* dir = dirname(orig_path);
+ snprintf(path, n, "%s/%s", dir, file);
+ free(orig_path);
+ }
+
+ hangul_keyboard_parse_file(path, context);
+ free(path);
+ }
+}
+
+static void XMLCALL
+on_element_end(void* data, const XML_Char* element)
+{
+ HangulKeyboardLoadContext* context = (HangulKeyboardLoadContext*)data;
+
+ if (context->keyboard == NULL)
+ return;
+
+ if (strcmp(element, "name") == 0) {
+ context->current_element = "";
+ context->save_name = false;
+ } else if (strcmp(element, "map") == 0) {
+ context->current_id = 0;
+ context->current_element = "";
+ } else if (strcmp(element, "combination") == 0) {
+ unsigned int id = context->current_id;
+ HangulCombination* combination = context->keyboard->combination[id];
+ hangul_combination_sort(combination);
+ context->current_id = 0;
+ context->current_element = "";
+ }
+}
+
+static void XMLCALL
+on_char_data(void* data, const XML_Char* s, int len)
+{
+ HangulKeyboardLoadContext* context = (HangulKeyboardLoadContext*)data;
+
+ if (context->keyboard == NULL)
+ return;
+
+ if (strcmp(context->current_element, "name") == 0) {
+ if (context->save_name) {
+ char buf[1024];
+ if (len >= sizeof(buf))
+ len = sizeof(buf) - 1;
+ memcpy(buf, s, len);
+ buf[len] = '\0';
+ hangul_keyboard_set_name(context->keyboard, buf);
+ }
+ }
+}
+
+static void
+hangul_keyboard_parse_file(const char* path, void* user_data)
+{
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ XML_SetUserData(parser, user_data);
+ XML_SetElementHandler(parser, on_element_start, on_element_end);
+ XML_SetCharacterDataHandler(parser, on_char_data);
+
+ FILE* file = fopen(path, "r");
+ if (file == NULL) {
+ goto done;
+ }
+
+ char buf[8192];
+
+ while (true) {
+ size_t n = fread(buf, 1, sizeof(buf), file);
+ int is_final = feof(file);
+ int res = XML_Parse(parser, buf, n, is_final);
+ if (res == XML_STATUS_ERROR) {
+ goto close;
+ }
+ if (is_final)
+ break;
+ }
+
+close:
+ fclose(file);
+done:
+ XML_ParserFree(parser);
+}
+
+static HangulKeyboard*
+hangul_keyboard_new_from_file(const char* path)
+{
+ HangulKeyboardLoadContext context = { path, NULL, 0, "" };
+
+ hangul_keyboard_parse_file(path, &context);
+
+ return context.keyboard;
+}
+
+static unsigned
+hangul_keyboard_list_load_dir(const char* path)
+{
+ char pattern[PATH_MAX];
+ snprintf(pattern, sizeof(pattern), "%s/*.xml", path);
+
+ glob_t result;
+ int res = glob(pattern, GLOB_ERR, NULL, &result);
+ if (res != 0)
+ return 0;
+
+ size_t i;
+ for (i = 0; i < result.gl_pathc; ++i) {
+ HangulKeyboard* keyboard = hangul_keyboard_new_from_file(result.gl_pathv[i]);
+ if (keyboard == NULL)
+ continue;
+ hangul_keyboard_list_append(keyboard);
+ }
+
+ globfree(&result);
+
+ return hangul_keyboards.n;
+}
+
+static void
+hangul_keyboard_list_clear()
+{
+ size_t i;
+ for (i = 0; i < hangul_keyboards.n; ++i) {
+ hangul_keyboard_delete(hangul_keyboards.keyboards[i]);
+ }
+
+ free(hangul_keyboards.keyboards);
+
+ hangul_keyboards.n = 0;
+ hangul_keyboards.nalloced = 0;
+ hangul_keyboards.keyboards = NULL;
+}
+
+int
+hangul_keyboard_list_init()
+{
+ /* hangul_init을 호출하면 builtin keyboard는 disable되도록 처리한다.
+ * 기본 자판은 외부 파일로 부터 로딩하는 것이 기본 동작이고
+ * builtin 키보드는 하위 호환을 위해 남겨둔다. */
+ hangul_builtin_keyboard_count = 0;
+
+ unsigned n = 0;
+ /* libhangul data dir에서 keyboard 로딩 */
+ n += hangul_keyboard_list_load_dir(LIBHANGUL_KEYBOARD_DIR);
+
+ /* 유저의 개별 키보드 파일 로딩 */
+ char user_data_dir[PATH_MAX];
+ char* xdg_data_home = getenv("XDG_DATA_HOME");
+ if (xdg_data_home == NULL) {
+ char* home_dir = getenv("HOME");
+ snprintf(user_data_dir, sizeof(user_data_dir),
+ "%s/.local/share/libhangul/keyboards", home_dir);
+ } else {
+ snprintf(user_data_dir, sizeof(user_data_dir),
+ "%s/libhangul/keyboards", xdg_data_home);
+ }
+ n += hangul_keyboard_list_load_dir(user_data_dir);
+
+ if (n == 0)
+ return 1;
+
+ return 0;
+}
+
+int
+hangul_keyboard_list_fini()
+{
+ hangul_keyboard_list_clear();
+ hangul_builtin_keyboard_count = countof(hangul_builtin_keyboards);
+ return 0;
+}
+
+static char*
+hangul_builtin_keyboard_list_get_keyboard_id(unsigned index_)
+{
+ if (index_ >= hangul_builtin_keyboard_count)
+ return NULL;
+
+ const HangulKeyboard* keyboard = hangul_builtin_keyboards[index_];
+ if (keyboard == NULL)
+ return NULL;
+
+ return keyboard->id;
+}
+
+static const char*
+hangul_builtin_keyboard_list_get_keyboard_name(unsigned index_)
+{
+#ifdef ENABLE_NLS
+ static bool isGettextInitialized = false;
+ if (!isGettextInitialized) {
+ isGettextInitialized = true;
+ bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+ }
+#endif
+
+ if (index_ >= hangul_builtin_keyboard_count)
+ return NULL;
+
+ const HangulKeyboard* keyboard = hangul_builtin_keyboards[index_];
+ if (keyboard == NULL)
+ return NULL;
+
+ return keyboard->name;
+}
+
+static const HangulKeyboard*
+hangul_builtin_keyboard_list_get_keyboard(const char* id)
+{
+ size_t i;
+ for (i = 0; i < hangul_builtin_keyboard_count; ++i) {
+ const HangulKeyboard* keyboard = hangul_builtin_keyboards[i];
+ if (strcmp(id, keyboard->id) == 0) {
+ return keyboard;
+ }
+ }
+ return NULL;
+}
+
+unsigned int
+hangul_keyboard_list_get_count()
+{
+ if (hangul_builtin_keyboard_count > 0)
+ return hangul_builtin_keyboard_count;
+
+ return hangul_keyboards.n;
+}
+
+const char*
+hangul_keyboard_list_get_keyboard_id(unsigned index_)
+{
+ if (hangul_builtin_keyboard_count > 0) {
+ return hangul_builtin_keyboard_list_get_keyboard_id(index_);
+ }
+
+ if (index_ >= hangul_keyboards.n)
+ return NULL;
+
+ HangulKeyboard* keyboard = hangul_keyboards.keyboards[index_];
+ if (keyboard == NULL)
+ return NULL;
+
+ return keyboard->id;
+}
+
+const char*
+hangul_keyboard_list_get_keyboard_name(unsigned index_)
+{
+ if (hangul_builtin_keyboard_count > 0) {
+ return hangul_builtin_keyboard_list_get_keyboard_name(index_);
+ }
+
+ if (index_ >= hangul_keyboards.n)
+ return NULL;
+
+ HangulKeyboard* keyboard = hangul_keyboards.keyboards[index_];
+ if (keyboard == NULL)
+ return NULL;
+
+ return keyboard->name;
+}
+
+const HangulKeyboard*
+hangul_keyboard_list_get_keyboard(const char* id)
+{
+ if (hangul_builtin_keyboard_count > 0) {
+ return hangul_builtin_keyboard_list_get_keyboard(id);
+ }
+
+ size_t i;
+ for (i = 0; i < hangul_keyboards.n; ++i) {
+ HangulKeyboard* keyboard = hangul_keyboards.keyboards[i];
+ if (strcmp(id, keyboard->id) == 0) {
+ return keyboard;
+ }
+ }
+ return NULL;
+}
+
+static bool
+hangul_keyboard_list_append(HangulKeyboard* keyboard)
+{
+ if (hangul_keyboards.n >= hangul_keyboards.nalloced) {
+ size_t n = hangul_keyboards.nalloced * 2;
+ if (n == 0) {
+ n = 16;
+ }
+ HangulKeyboard** keyboards = hangul_keyboards.keyboards;
+ keyboards = realloc(keyboards, n * sizeof(keyboards[0]));
+ if (keyboards == NULL)
+ return false;
+
+ hangul_keyboards.nalloced = n;
+ hangul_keyboards.keyboards = keyboards;
+ }
+
+ size_t i = hangul_keyboards.n;
+ hangul_keyboards.keyboards[i] = keyboard;
+ hangul_keyboards.n = i + 1;
+
+ return true;
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 82fae4c..cb068dd 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,4 +2,6 @@
hangul/hangulctype.c
hangul/hangulinputcontext.c
+hangul/hangulkeyboard.c
hangul/hanja.c
+tools/hangul.c
diff --git a/test/hangul.c b/test/hangul.c
index 7f829a3..cbaf38f 100644
--- a/test/hangul.c
+++ b/test/hangul.c
@@ -67,6 +67,8 @@ main(int argc, char *argv[])
keyboard = argv[1];
}
+ hangul_init();
+
hic = hangul_ic_new(keyboard);
if (hic == NULL) {
printf("hic is null\n");
@@ -93,5 +95,7 @@ main(int argc, char *argv[])
hangul_ic_delete(hic);
+ hangul_fini();
+
return 0;
}
diff --git a/test/test.c b/test/test.c
index 1697720..3147ee5 100644
--- a/test/test.c
+++ b/test/test.c
@@ -528,18 +528,18 @@ START_TEST(test_hangul_keyboard)
unsigned int n;
unsigned int i;
- n = hangul_ic_get_n_keyboards();
+ n = hangul_keyboard_list_get_count();
fail_unless(n != 0,
- "error: there is no builtin hangul keyboard");
+ "error: there is no hangul keyboard");
for (i = 0; i < n; ++i) {
- id = hangul_ic_get_keyboard_id(i);
+ id = hangul_keyboard_list_get_keyboard_id(i);
fail_unless(id != NULL,
"error: keyboard id == NULL");
}
for (i = 0; i < n; ++i) {
- name = hangul_ic_get_keyboard_name(i);
+ name = hangul_keyboard_list_get_keyboard_name(i);
fail_unless(name != NULL,
"error: keyboard id == NULL");
}
@@ -582,6 +582,8 @@ Suite* libhangul_suite()
int main()
{
+ hangul_init();
+
int number_failed;
Suite* s = libhangul_suite();
SRunner* sr = srunner_create(s);
@@ -594,5 +596,7 @@ int main()
hangul_ic_delete(global_ic);
+ hangul_fini();
+
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/tools/hangul.c b/tools/hangul.c
index bc6bba6..d2783c3 100644
--- a/tools/hangul.c
+++ b/tools/hangul.c
@@ -1,5 +1,5 @@
/* libhangul
- * Copyright (C) 2011 Choe Hwanjin
+ * Copyright (C) 2011 - 2016 Choe Hwanjin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -124,7 +124,7 @@ list_keyboards()
unsigned i;
unsigned n;
- n = hangul_ic_get_n_keyboards();
+ n = hangul_keyboard_list_get_count();
#if defined(ENABLE_NLS) && defined(HAVE_NL_LANGINFO)
if (n > 0) {
@@ -147,8 +147,8 @@ list_keyboards()
const char* id;
const char* name;
- id = hangul_ic_get_keyboard_id(i);
- name = hangul_ic_get_keyboard_name(i);
+ id = hangul_keyboard_list_get_keyboard_id(i);
+ name = hangul_keyboard_list_get_keyboard_name(i);
printf("%-12s %s\n", id, name);
}
@@ -310,6 +310,8 @@ main(int argc, char *argv[])
setlocale(LC_ALL, "");
+ hangul_init();
+
res = EXIT_SUCCESS;
keyboard = "2";
input_string = NULL;
@@ -406,6 +408,8 @@ main(int argc, char *argv[])
hangul_ic_delete(ic);
+ hangul_fini();
+
iconv_close(cd_ucs4_to_utf8);
if (strcmp(output_file, "-") != 0) {