diff --git a/src/config.pri b/src/config.pri
index 11ae1c0ba213f9c23e3a2f71a8f0a44abc4842f5..c1784b140c03f3dfaed24bd542542d4b28edbb93 100644
--- a/src/config.pri
+++ b/src/config.pri
@@ -4,6 +4,11 @@ handwriting:!lipi-toolkit:!t9write {
     equals(T9WRITE_FOUND, 1): CONFIG += t9write
     else: CONFIG += lipi-toolkit
 }
+t9write {
+    !handwriting: include(virtualkeyboard/3rdparty/t9write/t9write-build.pri)
+    equals(T9WRITE_CJK_FOUND, 1): CONFIG += t9write-cjk
+    equals(T9WRITE_ALPHABETIC_FOUND, 1): CONFIG += t9write-alphabetic
+}
 
 # Disable built-in layouts
 disable-layouts {
diff --git a/src/virtualkeyboard/3rdparty/t9write/t9write-build.pri b/src/virtualkeyboard/3rdparty/t9write/t9write-build.pri
index ab519b90184b8f41459cd6d2f925d60e2f50248c..f810badfb7d84bda4f12785d4a3c5c891a746272 100644
--- a/src/virtualkeyboard/3rdparty/t9write/t9write-build.pri
+++ b/src/virtualkeyboard/3rdparty/t9write/t9write-build.pri
@@ -1,25 +1,83 @@
 #
 # Automatically detects the T9Write build directory and sets the following variables:
 #
-#   T9WRITE_BUILD_DIR: A base directory for the architecture specific build directory
-#   T9WRITE_ALPHABETIC_OBJ: Absolute path to the target object file
+#   T9WRITE_FOUND: 0/1 T9Write SDK found
+#   T9WRITE_BUILD_STATIC: 0/1 Static libraries found (0 == shared libraries)
+#   T9WRITE_ALPHABETIC_FOUND: 0/1 T9 Write Alphabetic API header found
+#   T9WRITE_CJK_FOUND: 0/1 T9 Write CJK API header found
+#   T9WRITE_INCLUDE_DIRS: T9 Write include directories
+#   T9WRITE_ALPHABETIC_LIBS: Absolute path to the target library file
+#   T9WRITE_ALPHABETIC_BINS: Absolute path to the target binary file (shared library)
+#   T9WRITE_CJK_LIBS: Absolute path to the target library file
+#   T9WRITE_CJK_BINS: Absolute path to the target binary file (shared library)
 #
 
+T9WRITE_FOUND = 0
+T9WRITE_ALPHABETIC_FOUND = 0
+T9WRITE_CJK_FOUND = 0
+T9WRITE_INCLUDE_DIRS = $$PWD/api
 contains(QT_ARCH, arm) {
-    T9WRITE_BUILD_DIR = $$files(build_Android_ARM*)
+    T9WRITE_BUILD_SHARED_DIR = lib/arm/shared
+    T9WRITE_BUILD_STATIC_DIR = lib/arm/static
 } else:linux {
-    T9WRITE_BUILD_DIR = $$files(build_Android_x86*)
+    T9WRITE_BUILD_SHARED_DIR = lib/linux-x86/shared
+    T9WRITE_BUILD_STATIC_DIR = lib/linux-x86/static
 } else:win32 {
-    T9WRITE_BUILD_DIR = $$files(build_VC*)
+    T9WRITE_BUILD_SHARED_DIR = lib/win32/shared
+    T9WRITE_BUILD_STATIC_DIR = lib/win32/static
 }
 
-count(T9WRITE_BUILD_DIR, 1) {
-    T9WRITE_FOUND = 1
-    T9WRITE_INCLUDE_DIRS = \
-        $$PWD/$$T9WRITE_BUILD_DIR/api \
-        $$PWD/$$T9WRITE_BUILD_DIR/public
-    T9WRITE_ALPHABETIC_LIBS = \
-        $$PWD/$$files($$T9WRITE_BUILD_DIR/objects/t9write_alphabetic*.o*)
-} else {
-    T9WRITE_FOUND = 0
+defineReplace(findStaticLibrary) {
+    win32 {
+        result = $$files($$1/*.obj)
+        isEmpty(result): result = $$files($$1/*.lib)
+    } else {
+        result = $$files($$1/*.o)
+        isEmpty(result): result = $$files($$1/*.a)
+    }
+    return($$result)
+}
+
+defineReplace(findSharedLibrary) {
+    win32 {
+        result = $$files($$1/*.lib)
+    } else {
+        result = $$files($$1/*.so)
+    }
+    return($$result)
+}
+
+defineReplace(findSharedBinary) {
+    win32 {
+        result = $$files($$1/*.dll)
+    } else {
+        result = $$files($$1/*.so)
+    }
+    return($$result)
+}
+
+for(include_dir, T9WRITE_INCLUDE_DIRS) {
+    exists($${include_dir}/decuma_hwr.h): T9WRITE_ALPHABETIC_FOUND = 1
+    exists($${include_dir}/decuma_hwr_cjk.h): T9WRITE_CJK_FOUND = 1
+}
+
+equals(T9WRITE_ALPHABETIC_FOUND, 1)|equals(T9WRITE_CJK_FOUND, 1) {
+    equals(T9WRITE_ALPHABETIC_FOUND, 1) {
+        T9WRITE_ALPHABETIC_LIBS = $$findSharedLibrary($$PWD/$$T9WRITE_BUILD_SHARED_DIR/alphabetic)
+        !isEmpty(T9WRITE_ALPHABETIC_LIBS) {
+            T9WRITE_ALPHABETIC_BINS = $$findSharedBinary($$PWD/$$T9WRITE_BUILD_SHARED_DIR/alphabetic)
+        } else {
+            T9WRITE_ALPHABETIC_LIBS = $$findStaticLibrary($$PWD/$$T9WRITE_BUILD_STATIC_DIR/alphabetic)
+        }
+    }
+    equals(T9WRITE_CJK_FOUND, 1) {
+        T9WRITE_CJK_LIBS = $$findSharedLibrary($$PWD/$$T9WRITE_BUILD_SHARED_DIR/cjk)
+        !isEmpty(T9WRITE_CJK_LIBS) {
+            T9WRITE_CJK_BINS = $$findSharedBinary($$PWD/$$T9WRITE_BUILD_SHARED_DIR/cjk)
+        } else {
+            T9WRITE_CJK_LIBS = $$findStaticLibrary($$PWD/$$T9WRITE_BUILD_STATIC_DIR/cjk)
+        }
+    }
+    equals(T9WRITE_ALPHABETIC_FOUND, 1):!isEmpty(T9WRITE_ALPHABETIC_LIBS): T9WRITE_FOUND = 1
+    equals(T9WRITE_CJK_FOUND, 1):!isEmpty(T9WRITE_CJK_LIBS): T9WRITE_FOUND = 1
 }
diff --git a/src/virtualkeyboard/3rdparty/t9write/t9write.pro b/src/virtualkeyboard/3rdparty/t9write/t9write.pro
index 05f723a727b1fdef9b59053a9281a757106e4e7a..f6dddf1c45f453063b4623b4d7e47b62b2f598b4 100644
--- a/src/virtualkeyboard/3rdparty/t9write/t9write.pro
+++ b/src/virtualkeyboard/3rdparty/t9write/t9write.pro
@@ -2,18 +2,19 @@ TARGET = qtt9write_db
 
 CONFIG += static
 
-T9WRITE_LDBS = $$files(databases/XT9_LDBs/*.ldb)
-
 T9WRITE_RESOURCE_FILES = \
-    databases/HWR_LatinCG/_databas_le.bin \
-    $$T9WRITE_LDBS
+    $$files(data/*.bin) \
+    $$files(data/*.ldb) \
+    $$files(data/*.hdb) \
+    $$files(data/*.phd)
 
 # Note: Compression is disabled, because the resource is accessed directly from the memory
 QMAKE_RESOURCE_FLAGS += -no-compress
+CONFIG += resources_big
 
 include(../../generateresource.pri)
 
-RESOURCES += $$generate_resource(t9write_db.qrc, $$T9WRITE_RESOURCE_FILES)
+RESOURCES += $$generate_resource(t9write_db.qrc, $$T9WRITE_RESOURCE_FILES, /QtQuick/VirtualKeyboard/T9Write)
 
 load(qt_helper_lib)
 
diff --git a/src/virtualkeyboard/3rdparty/t9write/unpack.py b/src/virtualkeyboard/3rdparty/t9write/unpack.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e2136566fe56c914bd21263e7472da53f5dfb9b
--- /dev/null
+++ b/src/virtualkeyboard/3rdparty/t9write/unpack.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#############################################################################
+##
+## Copyright (C) 2017 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:GPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 3 or (at your option) any later version
+## approved by the KDE Free Qt Foundation. The licenses are as published by
+## the Free Software Foundation and appearing in the file LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import os
+import sys
+import zipfile
+import tempfile
+import shutil
+import fnmatch
+
+#
+# This utility script unpacks the T9 Write SDK to appropriate directory
+# structure for Qt Virtual Keyboard.
+#
+# Usage: unpack.py <filename.zip> <target dir>
+#
+# The script will happily overwrite existing files, so be careful.
+#
+
+#
+# Unpack rule map
+#
+# Format:
+#   1. <target dir>: [ 'pattern1', 'pattern2', ... ]
+#       - Each pattern is matched against the zip file contents. The file is
+#         copied to target dir if the pattern matches. Each pattern is handled
+#         independent of each other.
+#
+#   2. <target dir>: [ [ 'file group pattern', 'sub pattern1', ... ] ]
+#       - First the file group pattern is matched against the zip file contents.
+#         Then all the sub patterns are matched in the sub directory specified by
+#         the first match. If all the sub patterns match, then first match from
+#         file group pattern and all the matching files from sub pattterns are copied.
+#         The purpose of this option is to copy coupled files, e.g. DLL and LIB
+#         files found in the same directory.
+#
+
+UNPACK_RULES = {
+'api': [
+    '*/decuma_hwr.h',
+    '*/decuma_hwr_cjk.h',
+    '*/decuma_hwr_types.h',
+    '*/decuma_point.h',
+    '*/decumaBasicTypes.h',
+    '*/decumaBasicTypesMinMax.h',
+    '*/decumaCharacterSetType.h',
+    '*/decumaCurve.h',
+    '*/decumaFunctionalSupport.h',
+    '*/decumaFunctionalSupportCheck.h',
+    '*/decumaLanguages.h',
+    '*/decumaLiteFunctionalSupport.h',
+    '*/decumaRuntimeMallocData.h',
+    '*/decumaStatus.h',
+    '*/decumaStorageSpecifiers.h',
+    '*/decumaSymbolCategories.h',
+    '*/decumaUnicodeTypes.h',
+    '*/t9write_alpha_version.h',
+    '*/t9write_api_version.h',
+    '*/t9write_cjk_version.h',
+    '*/xxt9wApiOem.h',
+    '*/xxt9wOem.h',
+],
+'data': [
+    '*/_databas_le.bin',
+    '*/*.hdb',
+    '*/*.phd',
+    '*/*.ldb',
+],
+'lib/arm/static/alphabetic': [
+    '*T9Write_Alpha*/*Android_ARM*/*.a',
+    '*T9Write_Alpha*/*Android_ARM*/*.o',
+],
+'lib/arm/shared/alphabetic': [
+    '*T9Write_Alpha*/*Android_ARM*/*.so',
+],
+'lib/arm/static/cjk': [
+    '*T9Write_CJK*/*Android_ARM*/*.a',
+    '*T9Write_CJK*/*Android_ARM*/*.o',
+],
+'lib/arm/shared/cjk': [
+    '*T9Write_CJK*/*Android_ARM*/*.so',
+],
+'lib/linux/static/alphabetic': [
+    '*T9Write_Alpha*/*Android_x86*/*.a',
+    '*T9Write_Alpha*/*Android_x86*/*.o',
+],
+'lib/linux/shared/alphabetic': [
+    '*T9Write_Alpha*/*Android_x86*/*.so',
+],
+'lib/linux/static/cjk': [
+    '*T9Write_CJK*/*Android_x86*/*.a',
+    '*T9Write_CJK*/*Android_x86*/*.o',
+],
+'lib/linux/shared/cjk': [
+    '*T9Write_CJK*/*Android_x86*/*.so',
+],
+'lib/win32/static/alphabetic': [
+    '*T9Write_Alpha*/*.obj',
+],
+'lib/win32/shared/alphabetic': [
+    [ '*T9Write_Alpha*/*.dll', '*.lib' ],
+],
+'lib/win32/static/cjk': [
+    '*T9Write_CJK*/*.obj',
+],
+'lib/win32/shared/cjk': [
+    [ '*T9Write_CJK*/*.dll', '*.lib' ],
+],
+'lib/win32/shared/alphabetic': [
+    [ '*T9Write_Alpha*/*.dll', '*.lib' ],
+],
+}
+
+#
+# Blacklist
+#
+# File matching rules for blacklisted items. Matched before UNPACK_RULES.
+#
+
+BLACKLIST_RULES = [
+'*__MACOSX*',
+'*/.DS_Store',
+]
+
+def blacklist(file_list):
+    result = []
+    for file_name in file_list:
+        match = False
+        for blacklist_rule in BLACKLIST_RULES:
+            match = fnmatch.fnmatch(file_name, blacklist_rule)
+            if match:
+                break
+        if not match:
+            result.append(file_name)
+    return result
+
+def unzip(zip_file, target_dir):
+    zip_list = []
+    with zipfile.ZipFile(zip_file, 'r') as z:
+        zip_list = sorted(blacklist(z.namelist()))
+        zip_basename = os.path.splitext(os.path.basename(zip_file))[0]
+        if zip_list and zip_basename in zip_list[0]:
+            zip_basename = ''
+        zip_list = [os.path.join(zip_basename, zip_name).replace('\\', '/') for zip_name in zip_list]
+        z.extractall(os.path.join(target_dir, zip_basename))
+    return zip_list
+
+def match_file_list(file_list, base_dir, fnpattern):
+    return [file_name for file_name in file_list \
+        if fnmatch.fnmatch(file_name, fnpattern) and \
+            os.path.isfile(os.path.join(base_dir, file_name))]
+
+def unpack(zip_list, zip_dir, out_dir):
+    if not zip_list:
+        return
+
+    for (target_dir, match_rules) in UNPACK_RULES.items():
+        for match_rule in match_rules:
+            # Match
+            match_rule_group = match_rule if isinstance(match_rule, list) else [match_rule]
+            match_group_candidates = [match_file_list(zip_list, zip_dir, match_rule_group[0])]
+            if len(match_rule_group) > 1:
+                while len(match_group_candidates[0]) > 0:
+                    match_group0_candidate = match_group_candidates[0][0]
+                    all_sub_groups_match = True
+                    for sub_group_rule in match_rule_group[1:]:
+                        fnpattern = os.path.join(os.path.dirname(match_group0_candidate), sub_group_rule).replace('\\', '/')
+                        sub_group_candidates = match_file_list(zip_list, zip_dir, fnpattern)
+                        if not sub_group_candidates:
+                            all_sub_groups_match = False
+                            break
+                        match_group_candidates.append(sub_group_candidates)
+                    if all_sub_groups_match:
+                        match_group_candidates[0] = [match_group0_candidate]
+                        break
+                    else:
+                        match_group_candidates = [match_group_candidates[0][1:]]
+
+            # Copy
+            if match_group_candidates:
+                for match_group_candidate in match_group_candidates:
+                    for zip_name in match_group_candidate:
+                        dst_dir = os.path.join(out_dir, target_dir)
+                        if not os.path.exists(dst_dir):
+                            os.makedirs(dst_dir)
+                        src = os.path.join(zip_dir, zip_name).replace('\\', '/')
+                        dst = os.path.join(dst_dir, os.path.basename(zip_name)).replace('\\', '/')
+                        print(zip_name + ' -> ' + dst)
+                        shutil.copy2(src, dst)
+
+if __name__ == '__main__':
+    if len(sys.argv) != 3:
+        print("Usage: %s <filename.zip> <target dir>" % os.path.basename(__file__))
+        exit()
+
+    out_dir = sys.argv[2]
+    zip_dir = tempfile.mkdtemp()
+
+    try:
+        unpack(unzip(sys.argv[1], zip_dir), zip_dir, out_dir)
+    finally:
+        shutil.rmtree(zip_dir)
diff --git a/src/virtualkeyboard/content/components/InputModeKey.qml b/src/virtualkeyboard/content/components/InputModeKey.qml
index dcc10d547339be1ceb007885ad416c723c0c197e..d880867f0d64e4ef3fb4fc4bf21baf74459fcfc0 100644
--- a/src/virtualkeyboard/content/components/InputModeKey.qml
+++ b/src/virtualkeyboard/content/components/InputModeKey.qml
@@ -68,6 +68,7 @@ Key {
         "あ",    // InputEngine.Hiragana
         "ã‚«",    // InputEngine.Katakana
         "全角",  // InputEngine.FullwidthLatin
+        "中文",  // InputEngine.ChineseHandwriting
     ]
 
     function __nextInputMode(inputMode) {
diff --git a/src/virtualkeyboard/content/components/Keyboard.qml b/src/virtualkeyboard/content/components/Keyboard.qml
index 2bd2ff2e8637f51f2bac1fe4290ee1bbaf3f365a..8dfd7f7d95dd0ea45a5bb9c931b3e318909e7adb 100644
--- a/src/virtualkeyboard/content/components/Keyboard.qml
+++ b/src/virtualkeyboard/content/components/Keyboard.qml
@@ -1152,7 +1152,7 @@ Item {
                         inputMode = InputEngine.Dialable
                     else if ((InputContext.inputMethodHints & (Qt.ImhFormattedNumbersOnly | Qt.ImhDigitsOnly)) && inputModes.indexOf(InputEngine.Numeric) !== -1)
                         inputMode = InputEngine.Numeric
-                    else
+                    else if (keyboardLayoutLoader.item.inputMode === -1)
                         inputMode = inputModes[0]
                 }
 
diff --git a/src/virtualkeyboard/content/layouts/zh_CN/handwriting.qml b/src/virtualkeyboard/content/layouts/zh_CN/handwriting.qml
new file mode 100644
index 0000000000000000000000000000000000000000..598980d1b6a03a04da34834dc940cbe37d6fd5ff
--- /dev/null
+++ b/src/virtualkeyboard/content/layouts/zh_CN/handwriting.qml
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.0
+import QtQuick.VirtualKeyboard 2.3
+
+KeyboardLayout {
+    function createInputMethod() {
+        return Qt.createQmlObject('import QtQuick 2.0; import QtQuick.VirtualKeyboard 2.3; HandwritingInputMethod {}', parent)
+    }
+    sharedLayouts: ['symbols']
+    inputMode: preferredInputMode()
+
+    Connections {
+        target: InputContext
+        onInputMethodHintsChanged: {
+            var newInputMode = preferredInputMode()
+            if (InputContext.inputEngine.inputModes.indexOf(newInputMode) !== -1)
+                InputContext.inputEngine.inputMode = newInputMode
+        }
+    }
+
+    function preferredInputMode() {
+        return InputContext.inputMethodHints &
+                (Qt.ImhPreferLatin | Qt.ImhEmailCharactersOnly | Qt.ImhUrlCharactersOnly |
+                 Qt.ImhLatinOnly) ? InputEngine.Latin : InputEngine.ChineseHandwriting
+    }
+
+    KeyboardRow {
+        Layout.preferredHeight: 3
+        KeyboardColumn {
+            Layout.preferredWidth: bottomRow.width - hideKeyboardKey.width
+            KeyboardRow {
+                TraceInputKey {
+                    objectName: "hwrInputArea"
+                    patternRecognitionMode: InputEngine.HandwritingRecoginition
+                    horizontalRulers:
+                        InputContext.inputEngine.inputMode !== InputEngine.ChineseHandwriting ? [] :
+                            [Math.round(boundingBox.height / 4), Math.round(boundingBox.height / 4) * 2, Math.round(boundingBox.height / 4) * 3]
+
+                }
+            }
+        }
+        KeyboardColumn {
+            Layout.preferredWidth: hideKeyboardKey.width
+            KeyboardRow {
+                BackspaceKey {}
+            }
+            KeyboardRow {
+                EnterKey {}
+            }
+            KeyboardRow {
+                ShiftKey { }
+            }
+        }
+    }
+    KeyboardRow {
+        id: bottomRow
+        Layout.preferredHeight: 1
+        keyWeight: 154
+        InputModeKey {
+            weight: 217
+        }
+        ChangeLanguageKey {
+            weight: 154
+            customLayoutsOnly: true
+        }
+        HandwritingModeKey {
+            weight: 154
+        }
+        SpaceKey {
+            weight: 864
+        }
+        Key {
+            key: Qt.Key_Apostrophe
+            text: "‘"
+            alternativeKeys: "《》〈〉•…々〆‘’“”「」¥"
+        }
+        Key {
+            key: Qt.Key_Period
+            text: "."
+            alternativeKeys: ":;,.、。?!"
+        }
+        HideKeyboardKey {
+            id: hideKeyboardKey
+            weight: 204
+        }
+    }
+}
diff --git a/src/virtualkeyboard/content/layouts/zh_CN/main.qml b/src/virtualkeyboard/content/layouts/zh_CN/main.qml
index c8e21c8459c5bb278c2fc8c32980cfb3cae40736..44b0ceb7ce9b454aae58f8cfa1add3381251f1c9 100644
--- a/src/virtualkeyboard/content/layouts/zh_CN/main.qml
+++ b/src/virtualkeyboard/content/layouts/zh_CN/main.qml
@@ -181,6 +181,9 @@ KeyboardLayout {
         ChangeLanguageKey {
             weight: 154
         }
+        HandwritingModeKey {
+            weight: 154
+        }
         SpaceKey {
             weight: 864
         }
diff --git a/src/virtualkeyboard/content/styles/default/style.qml b/src/virtualkeyboard/content/styles/default/style.qml
index 03f0344f1fccd2b225f4a5179a43e38ac0ee1b38..9d00bb11418c9cf796708eda46c4c50809346c3b 100644
--- a/src/virtualkeyboard/content/styles/default/style.qml
+++ b/src/virtualkeyboard/content/styles/default/style.qml
@@ -757,7 +757,17 @@ KeyboardStyle {
             Text {
                 id: hwrInputModeIndicator
                 visible: control.patternRecognitionMode === InputEngine.HandwritingRecoginition
-                text: InputContext.inputEngine.inputMode === InputEngine.Latin ? "Abc" : "123"
+                text: {
+                    switch (InputContext.inputEngine.inputMode) {
+                    case InputEngine.Numeric:
+                    case InputEngine.Dialable:
+                        return "123"
+                    case InputEngine.ChineseHandwriting:
+                        return "中文"
+                    default:
+                        return "Abc"
+                    }
+                }
                 color: "white"
                 anchors.left: parent.left
                 anchors.top: parent.top
@@ -786,23 +796,42 @@ KeyboardStyle {
                 ctx.strokeStyle = Qt.rgba(0xFF, 0xFF, 0xFF)
                 ctx.clearRect(0, 0, width, height)
                 var i
+                var margin = Math.round(30 * scaleHint)
                 if (control.horizontalRulers) {
                     for (i = 0; i < control.horizontalRulers.length; i++) {
                         ctx.beginPath()
-                        ctx.moveTo(0, control.horizontalRulers[i])
-                        ctx.lineTo(width, control.horizontalRulers[i])
+                        var y = Math.round(control.horizontalRulers[i])
+                        var rightMargin = Math.round(width - margin)
+                        if (i + 1 === control.horizontalRulers.length) {
+                            ctx.moveTo(margin, y)
+                            ctx.lineTo(rightMargin, y)
+                        } else {
+                            var dashLen = Math.round(20 * scaleHint)
+                            for (var dash = margin, dashCount = 0;
+                                 dash < rightMargin; dash += dashLen, dashCount++) {
+                                if ((dashCount & 1) === 0) {
+                                    ctx.moveTo(dash, y)
+                                    ctx.lineTo(Math.min(dash + dashLen, rightMargin), y)
+                                }
+                            }
+                        }
                         ctx.stroke()
                     }
                 }
                 if (control.verticalRulers) {
                     for (i = 0; i < control.verticalRulers.length; i++) {
                         ctx.beginPath()
-                        ctx.moveTo(control.verticalRulers[i], 0)
-                        ctx.lineTo(control.verticalRulers[i], height)
+                        ctx.moveTo(control.verticalRulers[i], margin)
+                        ctx.lineTo(control.verticalRulers[i], Math.round(height - margin))
                         ctx.stroke()
                     }
                 }
             }
+            Connections {
+                target: control
+                onHorizontalRulersChanged: traceInputKeyGuideLines.requestPaint()
+                onVerticalRulersChanged: traceInputKeyGuideLines.requestPaint()
+            }
         }
     }
 
diff --git a/src/virtualkeyboard/content/styles/retro/style.qml b/src/virtualkeyboard/content/styles/retro/style.qml
index a1cb3ffcf18034f6e2e9f8b7d34b707ef3fa9627..3751608bde77c1799f2282a0b6f4d78c84191d81 100644
--- a/src/virtualkeyboard/content/styles/retro/style.qml
+++ b/src/virtualkeyboard/content/styles/retro/style.qml
@@ -871,7 +871,17 @@ KeyboardStyle {
         Text {
             id: hwrInputModeIndicator
             visible: control.patternRecognitionMode === InputEngine.HandwritingRecoginition
-            text: InputContext.inputEngine.inputMode === InputEngine.Latin ? "Abc" : "123"
+            text: {
+                switch (InputContext.inputEngine.inputMode) {
+                case InputEngine.Numeric:
+                case InputEngine.Dialable:
+                    return "123"
+                case InputEngine.ChineseHandwriting:
+                    return "中文"
+                default:
+                    return "Abc"
+                }
+            }
             color: "black"
             anchors.left: parent.left
             anchors.top: parent.top
@@ -899,11 +909,25 @@ KeyboardStyle {
                 ctx.strokeStyle = Qt.rgba(0xFF, 0xFF, 0xFF)
                 ctx.clearRect(0, 0, width, height)
                 var i
+                var margin = Math.round(30 * scaleHint)
                 if (control.horizontalRulers) {
                     for (i = 0; i < control.horizontalRulers.length; i++) {
                         ctx.beginPath()
-                        ctx.moveTo(0, control.horizontalRulers[i])
-                        ctx.lineTo(width, control.horizontalRulers[i])
+                        var y = Math.round(control.horizontalRulers[i])
+                        var rightMargin = Math.round(width - margin)
+                        if (i + 1 === control.horizontalRulers.length) {
+                            ctx.moveTo(margin, y)
+                            ctx.lineTo(rightMargin, y)
+                        } else {
+                            var dashLen = Math.round(20 * scaleHint)
+                            for (var dash = margin, dashCount = 0;
+                                 dash < rightMargin; dash += dashLen, dashCount++) {
+                                if ((dashCount & 1) === 0) {
+                                    ctx.moveTo(dash, y)
+                                    ctx.lineTo(Math.min(dash + dashLen, rightMargin), y)
+                                }
+                            }
+                        }
                         ctx.stroke()
                     }
                 }
@@ -916,6 +940,11 @@ KeyboardStyle {
                     }
                 }
             }
+            Connections {
+                target: control
+                onHorizontalRulersChanged: traceInputKeyGuideLines.requestPaint()
+                onVerticalRulersChanged: traceInputKeyGuideLines.requestPaint()
+            }
         }
     }
 
diff --git a/src/virtualkeyboard/doc/src/build.qdoc b/src/virtualkeyboard/doc/src/build.qdoc
index 3d9d9eace61745a41237ec46f6e8d49df121767f..9b18a54d11f0fc9e453a31488b5eb43d4e0b8747 100644
--- a/src/virtualkeyboard/doc/src/build.qdoc
+++ b/src/virtualkeyboard/doc/src/build.qdoc
@@ -281,6 +281,136 @@ Hunspell sources and dictionary files is listed below:
             (etc.)
 \endcode
 
+\section2 T9 Write Integration
+
+T9 Write integration supports the T9 Write Alphabetic and T9 Write CJK engines. Both
+engines are integrated via T9WriteInputMethod. The input method can be initialized
+with either of the engines at runtime. The engine selection happens automatically
+based on input locale and input mode from the keyboard.
+
+\section3 T9 Write Compatibility
+
+Qt Virtual Keyboard is compatible with T9 Write v7.5.0 onward.
+
+The latest tested version is v7.8.1.
+
+\section3 T9 Write Build Preparations
+
+The contents of the SDK must be either manually copied to the directory structure
+described below, or by using the \e unpack.py script found in the t9write directory.
+
+To unpack the SDK using the script:
+
+\badcode
+$ cd src/virtualkeyboard/3rdparty/t9write/
+$ python unpack.py T9Write_Alpha_v7-8-0_SDK.zip .
+\endcode
+
+\badcode
+3rdparty
+└── t9write
+    ├─── api
+    │   ├─── decuma*.h
+    │   ├─── t9write*.h
+    │   └─── xxt9w*.h
+    ├─── data
+    │   ├─── *.bin [T9 Write Alphabetic]
+    │   ├─── *.hdb
+    │   ├─── *.phd
+    │   └─── *.ldb [T9 Write v7.5]
+    └─── lib
+        ├─── arm
+        │   ├─── shared
+        │   │   ├─── alphabetic
+        │   │   │   └─── *.so
+        │   │   └─── cjk
+        │   │       └─── *.so
+        │   └─── static
+        │       ├─── alphabetic
+        │       │   └─── *.a / *.o
+        │       └─── cjk
+        │           └─── *.a / *.o
+        ├─── linux-x86
+        │   ├─── shared
+        │   │   ├─── alphabetic
+        │   │   │   └─── *.so
+        │   │   └─── cjk
+        │   │       └─── *.so
+        │   └─── static
+        │       ├─── alphabetic
+        │       │   └─── *.a / *.o
+        │       └─── cjk
+        │           └─── *.a / *.o
+        └─── win32
+            ├─── shared
+            │   ├─── alphabetic
+            │   │   ├─── *.dll
+            │   │   └─── *.lib
+            │   └─── cjk
+            │       ├─── *.dll
+            │       └─── *.lib
+            └─── static
+                ├─── alphabetic
+                │   └─── *.lib / *.obj
+                └─── cjk
+                    └─── *.lib / *.obj
+\endcode
+
+\note The above files are from the T9 Write demo SDK for Windows; the contents may vary for other
+      platforms.
+
+Where the contents of each directory are:
+
+\table
+\header
+    \li Directory
+    \li Description
+    \li Remarks
+\row
+    \li \e api
+    \li This directory should contain all of the API files
+    \li The API files usually located in the "api" and "public" directories
+        of the SDK, but sometimes in the "demo" directory.
+
+        When using both Alphabetic and CJK engines at the same time, any
+        overlapping files can be copied from either SDK.
+\row
+    \li \e data
+    \li This directory should contain all HWR databases and optionally
+        XT9 databases.
+    \li HWR database for the T9 Write Alphabetic:
+        \list
+            \li \e _databas_le.bin
+        \endlist
+
+        HWR database for the T9 Write CJK:
+        \list
+            \li \e cjk_HK_std_le.hdb HongKong Chinese
+            \li \e cjk_J_std_le.hdb Japanese
+            \li \e cjk_K_mkt_le.hdb Korean
+            \li \e cjk_S_gb18030_le.hdb Simplified Chinese
+            \li \e cjk_T_std_le.hdb Traditional Chinese
+        \endlist
+
+        Language database:
+        \list
+            \li File extension is either \e .ldb or \e .phd
+        \endlist
+\row
+    \li \e lib/<target>/<linkage>/<engine-variant>
+    \li Directory structure holding supported target builds.
+    \li These directories should hold the desired target libraries.
+        If both shared and static libraries are found, shared libraries
+        are preferred.
+
+        For example, to enable a static win32 build, copy
+        \e t9write_alphabetic_rel.obj to \e lib/win32/static/alphabetic
+        directory.
+\endtable
+
+Finally, the SDK is included in the build by adding CONFIG+=t9write to the
+qmake command line.
+
 \section2 Static builds
 
 The virtual keyboard can be built and linked statically against the application.
diff --git a/src/virtualkeyboard/inputengine.cpp b/src/virtualkeyboard/inputengine.cpp
index 10f7dc7b5cfdb9c8409d611fa9aa87b0d2565b58..dd04f645e566c4a835a26815a5927c14131f4a09 100644
--- a/src/virtualkeyboard/inputengine.cpp
+++ b/src/virtualkeyboard/inputengine.cpp
@@ -764,6 +764,7 @@ void InputEngine::timerEvent(QTimerEvent *timerEvent)
         \li \c InputEngine.Hiragana Hiragana input mode for Japanese.
         \li \c InputEngine.Katakana Katakana input mode for Japanese.
         \li \c InputEngine.FullwidthLatin Fullwidth latin input mode for East Asian languages.
+        \li \c InputEngine.ChineseHandwriting Chinese handwriting.
     \endlist
 */
 
diff --git a/src/virtualkeyboard/inputengine.h b/src/virtualkeyboard/inputengine.h
index ce0122ac73fc854b43c740e58a46ae0caf0bff20..d1b99ae48c1a6b1414152b62fb9d5869a09120fd 100644
--- a/src/virtualkeyboard/inputengine.h
+++ b/src/virtualkeyboard/inputengine.h
@@ -73,7 +73,8 @@ public:
         Hangul,
         Hiragana,
         Katakana,
-        FullwidthLatin
+        FullwidthLatin,
+        ChineseHandwriting
     };
     enum PatternRecognitionMode {
         PatternRecognitionDisabled,
diff --git a/src/virtualkeyboard/shifthandler.cpp b/src/virtualkeyboard/shifthandler.cpp
index 376410e3fc12e11dfab8e904f52be1f3a8a44381..a5f80d05d66dc558274ef337987459996cd185fa 100644
--- a/src/virtualkeyboard/shifthandler.cpp
+++ b/src/virtualkeyboard/shifthandler.cpp
@@ -51,7 +51,7 @@ public:
         resetWhenVisible(false),
         manualShiftLanguageFilter(QSet<QLocale::Language>() << QLocale::Arabic << QLocale::Persian << QLocale::Hindi << QLocale::Korean),
         manualCapsInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::Cangjie << InputEngine::Zhuyin),
-        noAutoUppercaseInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::FullwidthLatin << InputEngine::Pinyin << InputEngine::Cangjie << InputEngine::Zhuyin),
+        noAutoUppercaseInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::FullwidthLatin << InputEngine::Pinyin << InputEngine::Cangjie << InputEngine::Zhuyin << InputEngine::ChineseHandwriting),
         allCapsInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::Hiragana << InputEngine::Katakana)
     {
         timer.start();
diff --git a/src/virtualkeyboard/t9write.h b/src/virtualkeyboard/t9write.h
new file mode 100644
index 0000000000000000000000000000000000000000..621d231224e4ed82c4811370be70cfc020e804d7
--- /dev/null
+++ b/src/virtualkeyboard/t9write.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef T9WRITE_H
+#define T9WRITE_H
+
+#include "t9write_api_version.h"
+#ifdef HAVE_T9WRITE_ALPHABETIC
+#include "decuma_hwr.h"
+#endif
+#ifdef HAVE_T9WRITE_CJK
+#include "decuma_hwr_cjk.h"
+#endif
+
+#if defined(HAVE_T9WRITE_CJK) && defined(HAVE_T9WRITE_ALPHABETIC)
+#define DECUMA_API(FUNC_NAME) (cjk ? decumaCJK ## FUNC_NAME : decuma ## FUNC_NAME)
+#elif defined(HAVE_T9WRITE_CJK)
+#define DECUMA_API(FUNC_NAME) (decumaCJK ## FUNC_NAME)
+#else // defined(HAVE_T9WRITE_ALPHABETIC)
+#define DECUMA_API(FUNC_NAME) (decuma ## FUNC_NAME)
+#endif
+
+#endif // T9WRITE_H
diff --git a/src/virtualkeyboard/t9writedictionary.cpp b/src/virtualkeyboard/t9writedictionary.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d15b16e3bd4f686e481b786405d271f9accb6423
--- /dev/null
+++ b/src/virtualkeyboard/t9writedictionary.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "t9writedictionary.h"
+#include "virtualkeyboarddebug.h"
+
+namespace QtVirtualKeyboard {
+
+T9WriteDictionary::T9WriteDictionary(DECUMA_SESSION *decumaSession,
+                                     const DECUMA_MEM_FUNCTIONS &memFuncs,
+                                     bool cjk) :
+    decumaSession(decumaSession),
+    memFuncs(memFuncs),
+    cjk(cjk),
+    sourceData(0),
+    sourceSize(0),
+    convertedData(0),
+    convertedSize(0)
+{
+}
+
+T9WriteDictionary::~T9WriteDictionary()
+{
+    if (convertedData) {
+        DECUMA_STATUS status = DECUMA_API(DestroyConvertedDictionary)(&convertedData, &memFuncs);
+        Q_ASSERT(status == decumaNoError);
+        Q_ASSERT(convertedData == 0);
+    }
+}
+
+bool T9WriteDictionary::load(const QString &fileName)
+{
+    if (sourceData || convertedData)
+        return false;
+
+    file.setFileName(fileName);
+    if (file.open(QIODevice::ReadOnly)) {
+        sourceSize = file.size();
+        sourceData = file.map(0, sourceSize, QFile::NoOptions);
+        if (!sourceData) {
+            sourceSize = 0;
+            qWarning() << "Could not read dictionary file" << fileName;
+        }
+        file.close();
+    } else {
+        qWarning() << "Could not open dictionary file" << fileName;
+    }
+
+    return sourceData != 0;
+}
+
+bool T9WriteDictionary::convert(const DECUMA_SRC_DICTIONARY_INFO &dictionaryInfo)
+{
+    if (!sourceData || convertedData)
+        return false;
+
+    DECUMA_STATUS status;
+    status = DECUMA_API(ConvertDictionary)(&convertedData, sourceData, (DECUMA_UINT32)sourceSize,
+                                           &dictionaryInfo, &convertedSize, &memFuncs);
+
+    if (status != decumaNoError) {
+        qWarning() << "Could not convert dictionary" << file.fileName();
+        file.unmap((uchar *)sourceData);
+        sourceSize = 0;
+        sourceData = 0;
+    }
+
+    return status == decumaNoError;
+}
+
+QString T9WriteDictionary::fileName() const
+{
+    return file.fileName();
+}
+
+const void *T9WriteDictionary::data() const
+{
+    return convertedData ? convertedData : sourceData;
+}
+
+qint64 T9WriteDictionary::size() const
+{
+    return convertedData ? convertedSize : sourceSize;
+}
+
+bool T9WriteDictionary::isConverted() const
+{
+    return convertedData != 0;
+}
+
+} // namespace QtVirtualKeyboard
diff --git a/src/virtualkeyboard/t9writedictionary.h b/src/virtualkeyboard/t9writedictionary.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc2d9475cb5b1a6e487b959089107d47e7f8851e
--- /dev/null
+++ b/src/virtualkeyboard/t9writedictionary.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef T9WRITEDICTIONARY_H
+#define T9WRITEDICTIONARY_H
+
+#include <QtGlobal>
+#include <QFile>
+#include "t9write.h"
+
+namespace QtVirtualKeyboard {
+
+class T9WriteDictionary
+{
+    Q_DISABLE_COPY(T9WriteDictionary)
+public:
+    explicit T9WriteDictionary(DECUMA_SESSION *decumaSession, const DECUMA_MEM_FUNCTIONS &memFuncs, bool cjk);
+    ~T9WriteDictionary();
+
+    bool load(const QString &fileName);
+    bool convert(const DECUMA_SRC_DICTIONARY_INFO &dictionaryInfo);
+
+    QString fileName() const;
+    const void *data() const;
+    qint64 size() const;
+    bool isConverted() const;
+
+private:
+    QFile file;
+    DECUMA_SESSION *decumaSession;
+    const DECUMA_MEM_FUNCTIONS &memFuncs;
+    bool cjk;
+    void *sourceData;
+    qint64 sourceSize;
+    void *convertedData;
+    DECUMA_UINT32 convertedSize;
+};
+
+}
+
+#endif // T9WRITEDICTIONARY_H
diff --git a/src/virtualkeyboard/t9writeinputmethod.cpp b/src/virtualkeyboard/t9writeinputmethod.cpp
index eb4cf9fbde37389d9a8c92ac524302abfb19f9f2..3028d7d01ebdfb5a83620b09e7e89fba92518ff2 100644
--- a/src/virtualkeyboard/t9writeinputmethod.cpp
+++ b/src/virtualkeyboard/t9writeinputmethod.cpp
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
 ** Contact: https://www.qt.io/licensing/
 **
 ** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
@@ -34,15 +34,27 @@
 #include "t9writeworker.h"
 #include "virtualkeyboarddebug.h"
 #include <QDirIterator>
+#include <QCryptographicHash>
 #ifdef QT_VIRTUALKEYBOARD_DEBUG
 #include <QTime>
 #endif
+#include "handwritinggesturerecognizer.h"
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+#include "unipentrace.h"
+#include <QStandardPaths>
+#endif
 
-#include "decuma_hwr.h"
 #include "decumaStatus.h"
 #include "decumaSymbolCategories.h"
 #include "decumaLanguages.h"
-#include "xxt9wOem.h"
+
+/*  Set to 1 to enable T9 Write log.
+
+    The log is routed to qDebug() and it can be enabled for troubleshooting
+    and when reporting issues. The log must not to be enabled in production
+    build.
+*/
+#define QT_VIRTUALKEYBOARD_T9WRITE_LOG 0
 
 namespace QtVirtualKeyboard {
 
@@ -94,12 +106,26 @@ class T9WriteInputMethodPrivate : public AbstractInputMethodPrivate
     Q_DECLARE_PUBLIC(T9WriteInputMethod)
 public:
 
+    enum EngineMode {
+        EngineUninitialized,
+        Alphabetic,
+        SimplifiedChinese,
+        TraditionalChinese,
+        HongKongChinese,
+        Japanese,
+        Korean
+    };
+
     T9WriteInputMethodPrivate(T9WriteInputMethod *q_ptr) :
         AbstractInputMethodPrivate(),
         q_ptr(q_ptr),
+        cjk(false),
+        engineMode(EngineUninitialized),
+        defaultHwrDbPath(QLatin1String(":/QtQuick/VirtualKeyboard/T9Write/data/")),
+        defaultDictionaryDbPath(defaultHwrDbPath),
         dictionaryLock(QMutex::Recursive),
-        convertedDictionary(0),
         attachedDictionary(0),
+        traceListHardLimit(32),
         resultId(0),
         resultTimer(0),
         decumaSession(0),
@@ -107,6 +133,9 @@ public:
         arcAdditionStarted(false),
         ignoreUpdate(false),
         textCase(InputEngine::Lower)
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+        , unipenTrace()
+#endif
     {
     }
 
@@ -128,34 +157,74 @@ public:
         free(ptr);
     }
 
-    void initEngine()
+#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
+    static void decumaLogString(void *pUserData, const char *pLogString, DECUMA_UINT32 nLogStringLength)
+    {
+        static QMutex s_logMutex;
+        static QByteArray s_logString;
+        Q_UNUSED(pUserData)
+        Q_UNUSED(nLogStringLength)
+        QMutexLocker guard(&s_logMutex);
+        s_logString.append(pLogString, nLogStringLength);
+        if (s_logString.endsWith('\n')) {
+            while (s_logString.endsWith('\n'))
+                s_logString.chop(1);
+            qDebug() << (const char *)s_logString.constData();
+            s_logString.clear();
+        }
+    }
+#endif
+
+    bool initEngine(EngineMode newEngineMode)
     {
-        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::initEngine()";
+        if (engineMode == newEngineMode)
+            return engineMode != EngineUninitialized;
+
+        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::initEngine()" << newEngineMode;
 
         if (decumaSession)
-            return;
+            exitEngine();
 
-        symbolCategories.clear();
-        symbolCategories.append(DECUMA_CATEGORY_ANSI);
-        languageCategories.clear();
-        languageCategories.append(DECUMA_LANG_EN);
+        if (newEngineMode == EngineUninitialized)
+            return false;
+
+        switch (newEngineMode) {
+        case Alphabetic:
+            cjk = false;
+            break;
+        case SimplifiedChinese:
+        case TraditionalChinese:
+        case HongKongChinese:
+        case Japanese:
+        case Korean:
+            cjk = true;
+            break;
+        default:
+            Q_ASSERT(0 && "Invalid EngineMode!");
+            return false;
+        }
+        engineMode = newEngineMode;
 
         memset(&sessionSettings, 0, sizeof(sessionSettings));
 
-        QString latinDb = findLatinDb(":/databases/HWR_LatinCG/");
-        hwrDbFile.setFileName(latinDb);
+        QString hwrDb = findHwrDb(engineMode, defaultHwrDbPath);
+        hwrDbFile.setFileName(hwrDb);
         if (!hwrDbFile.open(QIODevice::ReadOnly)) {
-            qWarning() << "Could not open hwr database file" << latinDb;
-            return;
+            qCritical() << "Could not open HWR database" << hwrDb;
+            exitEngine();
+            return false;
         }
 
         sessionSettings.pStaticDB = (DECUMA_STATIC_DB_PTR)hwrDbFile.map(0, hwrDbFile.size(), QFile::NoOptions);
         if (!sessionSettings.pStaticDB) {
-            hwrDbFile.close();
-            qWarning() << "Could not map hwr database" << latinDb;
-            return;
+            qCritical() << "Could not read HWR database" << hwrDb;
+            exitEngine();
+            return false;
         }
 
+        symbolCategories.append(DECUMA_CATEGORY_ANSI);
+        languageCategories.append(DECUMA_LANG_EN);
+
         sessionSettings.recognitionMode = mcrMode;
         sessionSettings.bMinimizeAddArcPreProcessing = 1;
         sessionSettings.writingDirection = unknownWriting;
@@ -164,17 +233,25 @@ public:
         sessionSettings.charSet.pLanguages = languageCategories.data();
         sessionSettings.charSet.nLanguages = languageCategories.size();
 
-        session = QByteArray(decumaGetSessionSize(), 0);
+        session = QByteArray(DECUMA_API(GetSessionSize)(), 0);
         decumaSession = (DECUMA_SESSION *)(!session.isEmpty() ? session.data() : 0);
 
-        DECUMA_STATUS status = decumaBeginSession(decumaSession, &sessionSettings, &memFuncs);
+        DECUMA_STATUS status = DECUMA_API(BeginSession)(decumaSession, &sessionSettings, &memFuncs);
         Q_ASSERT(status == decumaNoError);
         if (status != decumaNoError) {
-            qWarning() << "Could not initialize T9Write engine" << status;
+            qCritical() << "Could not initialize T9Write engine" << status;
+            exitEngine();
+            return false;
         }
 
-        worker.reset(new T9WriteWorker(decumaSession));
+#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
+        DECUMA_API(StartLogging)(decumaSession, 0, decumaLogString);
+#endif
+
+        worker.reset(new T9WriteWorker(decumaSession, cjk));
         worker->start();
+
+        return true;
     }
 
     void exitEngine()
@@ -188,36 +265,62 @@ public:
             hwrDbFile.close();
         }
 
-        detachDictionary(&attachedDictionary);
-        destroyConvertedDictionary(&convertedDictionary);
+        if (attachedDictionary) {
+            detachDictionary(attachedDictionary);
+            attachedDictionary.reset();
+        }
+        loadedDictionary.reset();
 
         if (decumaSession) {
-            decumaEndSession(decumaSession);
+#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
+            DECUMA_API(StopLogging)(decumaSession);
+#endif
+            DECUMA_API(EndSession)(decumaSession);
             decumaSession = 0;
             session.clear();
         }
 
         memset(&sessionSettings, 0, sizeof(sessionSettings));
-    }
 
-    QString findLatinDb(const QString &dir)
-    {
-        QString latinDb;
-        QDirIterator it(dir, QDirIterator::NoIteratorFlags);
-        while (it.hasNext()) {
-            QString fileEntry = it.next();
+        symbolCategories.clear();
+        languageCategories.clear();
 
-            if (!fileEntry.endsWith(QLatin1String(".bin")))
-                continue;
+        engineMode = EngineUninitialized;
+        cjk = false;
+    }
 
-            latinDb = fileEntry;
+    QString findHwrDb(EngineMode mode, const QString &dir) const
+    {
+        QString hwrDbPath(dir);
+        switch (mode) {
+        case Alphabetic:
+            hwrDbPath.append(QLatin1String("_databas_le.bin"));
             break;
+        case SimplifiedChinese:
+            hwrDbPath.append(QLatin1String("cjk_S_gb18030_le.hdb"));
+            break;
+        case TraditionalChinese:
+            hwrDbPath.append(QLatin1String("cjk_T_std_le.hdb"));
+            break;
+        case HongKongChinese:
+            hwrDbPath.append(QLatin1String("cjk_HK_std_le.hdb"));
+            break;
+        case Japanese:
+            hwrDbPath.append(QLatin1String("cjk_J_std_le.hdb"));
+            break;
+        case Korean:
+            hwrDbPath.append(QLatin1String("cjk_K_mkt_le.hdb"));
+            break;
+        default:
+            return QString();
         }
-        return latinDb;
+        return QFileInfo::exists(hwrDbPath) ? hwrDbPath : QString();
     }
 
-    QString findDictionary(const QString &dir, const QLocale &locale)
+    QString findDictionary(const QString &dir, const QLocale &locale, DECUMA_SRC_DICTIONARY_TYPE &srcType)
     {
+        srcType = numberOfSrcDictionaryTypes;
+
         QStringList languageCountry = locale.name().split("_");
         if (languageCountry.length() != 2)
             return QString();
@@ -227,11 +330,27 @@ public:
         while (it.hasNext()) {
             QString fileEntry = it.next();
 
-            if (!fileEntry.endsWith(QLatin1String(".ldb")))
+            if (!fileEntry.contains("_" + languageCountry[0].toUpper()))
                 continue;
 
-            if (!fileEntry.contains("_" + languageCountry[0].toUpper()))
+            if (fileEntry.endsWith(QLatin1String(".ldb"))) {
+#if T9WRITEAPIMAJORVERNUM >= 20
+                qCritical() << "Incompatible T9 Write dictionary" << fileEntry;
+                continue;
+#else
+                srcType = decumaXT9LDB;
+#endif
+            } else if (fileEntry.endsWith(QLatin1String(".phd"))) {
+#if T9WRITEAPIMAJORVERNUM >= 20
+                srcType = decumaPortableHWRDictionary;
+#else
+                qCritical() << "Incompatible T9 Write dictionary" << fileEntry;
+                continue;
+#endif
+            } else {
+                qWarning() << "Incompatible T9 Write dictionary" << fileEntry;
                 continue;
+            }
 
             dictionary = fileEntry;
             break;
@@ -240,201 +359,117 @@ public:
         return dictionary;
     }
 
-    bool attachDictionary(void *dictionary)
+    bool attachDictionary(const QSharedPointer<T9WriteDictionary> &dictionary)
     {
-        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::attachDictionary():" << dictionary;
-
         QMutexLocker dictionaryGuard(&dictionaryLock);
         Q_ASSERT(decumaSession != 0);
         Q_ASSERT(dictionary != 0);
-
-        DECUMA_STATUS status = decumaAttachConvertedDictionary(decumaSession, dictionary);
-        Q_ASSERT(status == decumaNoError);
+        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::attachDictionary():" << dictionary->fileName();
+#if T9WRITEAPIMAJORVERNUM >= 20
+        DECUMA_STATUS status = DECUMA_API(AttachDictionary)(decumaSession, dictionary->data(), dictionary->size());
+#else
+        DECUMA_STATUS status = DECUMA_API(AttachConvertedDictionary)(decumaSession, dictionary->data());
+#endif
         return status == decumaNoError;
     }
 
-    void detachDictionary(void **dictionary)
+    void detachDictionary(const QSharedPointer<T9WriteDictionary> &dictionary)
     {
         QMutexLocker dictionaryGuard(&dictionaryLock);
-        Q_ASSERT(decumaSession != 0);
-        if (!dictionary || !*dictionary)
+        if (!dictionary)
             return;
 
-        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::detachDictionary():" << *dictionary;
+        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::detachDictionary():" << dictionary->fileName();
 
-        DECUMA_STATUS status = decumaDetachDictionary(decumaSession, *dictionary);
-        Q_UNUSED(status)
-        Q_ASSERT(status == decumaNoError);
-        *dictionary = 0;
-    }
-
-    void destroyConvertedDictionary(void **dictionary)
-    {
-        QMutexLocker dictionaryGuard(&dictionaryLock);
         Q_ASSERT(decumaSession != 0);
-        if (!dictionary || !*dictionary)
-            return;
-
-        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::destroyConvertedDictionary():" << *dictionary;
-
-        DECUMA_STATUS status = decumaDestroyConvertedDictionary(dictionary, &memFuncs);
+        DECUMA_STATUS status = DECUMA_API(DetachDictionary)(decumaSession, dictionary->data());
         Q_UNUSED(status)
         Q_ASSERT(status == decumaNoError);
-        Q_ASSERT(*dictionary == 0);
     }
 
     bool setInputMode(const QLocale &locale, InputEngine::InputMode inputMode)
     {
+        Q_Q(T9WriteInputMethod);
         VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::setInputMode():" << locale << inputMode;
 
-        Q_Q(T9WriteInputMethod);
-        DECUMA_UINT32 language = mapToDecumaLanguage(locale);
+        finishRecognition();
+
+        if (!initEngine(mapLocaleToEngineMode(locale)))
+            return false;
+
+        DECUMA_UINT32 language = mapToDecumaLanguage(locale, inputMode);
         if (language == DECUMA_LANG_GSMDEFAULT) {
-            qWarning() << "Handwriting input does not support the language" << locale.name();
+            qWarning() << "Handwriting is not supported in" << locale.name();
             return false;
         }
 
         int isLanguageSupported = 0;
-        decumaDatabaseIsLanguageSupported(sessionSettings.pStaticDB, language, &isLanguageSupported);
+        DECUMA_API(DatabaseIsLanguageSupported)(sessionSettings.pStaticDB, language, &isLanguageSupported);
         if (!isLanguageSupported) {
-            qWarning() << "Handwriting input does not support the language" << locale.name();
+            qWarning() << "Handwriting is not supported in" << locale.name();
             return false;
         }
 
-        finishRecognition();
-
-        bool languageChanged = languageCategories.isEmpty() || !languageCategories.contains(language);
+        bool languageChanged = languageCategories.isEmpty() || languageCategories.first() != language;
         if (languageChanged) {
             languageCategories.clear();
             languageCategories.append(language);
-        }
 
-        // Choose the symbol categories by input mode, script and input method hints
-        bool leftToRightGestures = true;
-        Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
-        symbolCategories.clear();
-        switch (inputMode) {
-        case InputEngine::Latin:
-            if (inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly)) {
-                symbolCategories.append(DECUMA_CATEGORY_EMAIL);
-            } else if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly)) {
-                symbolCategories.append(DECUMA_CATEGORY_URL);
-            } else {
-                bool includeDigits = true;
-                bool includeBasicPunctuation = true;
-                switch (locale.script()) {
-                case QLocale::LatinScript:
-                    if (language == DECUMA_LANG_EN)
-                        symbolCategories.append(DECUMA_CATEGORY_ANSI);
-                    else
-                        symbolCategories.append(DECUMA_CATEGORY_ISO8859_1);
-                    break;
-
-                case QLocale::CyrillicScript:
-                    symbolCategories.append(DECUMA_CATEGORY_CYRILLIC);
-                    break;
-
-                case QLocale::GreekScript:
-                    symbolCategories.append(DECUMA_CATEGORY_GREEK);
-                    break;
-
-                default:
-                    qWarning() << "Handwriting input does not support the language" << locale.name();
-                    return false;
-                }
-
-                if (includeDigits)
-                    symbolCategories.append(DECUMA_CATEGORY_DIGIT);
-
-                if (includeBasicPunctuation) {
-                    symbolCategories.append(DECUMA_CATEGORY_BASIC_PUNCTUATIONS);
-                    symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
-                }
-
-                if (language == DECUMA_LANG_ES)
-                    symbolCategories.append(DECUMA_CATEGORY_SPANISH_PUNCTUATIONS);
-            }
-            break;
-
-        case InputEngine::Numeric:
-            symbolCategories.append(DECUMA_CATEGORY_DIGIT);
-            if (!inputMethodHints.testFlag(Qt::ImhDigitsOnly))
-                symbolCategories.append(DECUMA_CATEGORY_NUM_SUP);
-            break;
-
-        case InputEngine::Dialable:
-            symbolCategories.append(DECUMA_CATEGORY_PHONE_NUMBER);
-            break;
-
-        default:
-            return false;
+            // Add English as secondary language for punctuation
+            if (language == DECUMA_LANG_PRC)
+                languageCategories.append(DECUMA_LANG_EN);
         }
 
-        if (leftToRightGestures) {
-            symbolCategories.append(DECUMA_CATEGORY_BACKSPACE_STROKE);
-            symbolCategories.append(DECUMA_CATEGORY_RETURN_STROKE);
-            symbolCategories.append(DECUMA_CATEGORY_WHITESPACE_STROKE);
-        }
-
-        /*  The dictionary is loaded in the background thread. Once the loading is
-            complete the dictionary will be attached to the current session. The
-            attachment happens in the worker thread context, thus the direct
-            connection for the signal handler and the mutex protecting the
-            converted dictionary for concurrent access.
-
-            The loading operation is blocking for the main thread only if the
-            user starts handwriting input before the operation is complete.
-        */
-        {
-            QMutexLocker dictionaryGuard(&dictionaryLock);
-
-            // Select recognition mode
-            // Note: MCR mode is preferred in all cases, since it eliminates the need
-            //       for recognition timer, thus provides better user experience.
-            sessionSettings.recognitionMode = inputMethodHints.testFlag(Qt::ImhHiddenText) ? scrMode : mcrMode;
-
-            // Detach previous dictionary if the language is being changed
-            // or the recognizer mode is single-character mode
-            if ((languageChanged || inputMethodHints.testFlag(Qt::ImhNoPredictiveText) || sessionSettings.recognitionMode == scrMode) && attachedDictionary) {
-                detachDictionary(&attachedDictionary);
-            }
-
-            // Check if a dictionary needs to be loaded
-            if (languageChanged || !convertedDictionary) {
-                destroyConvertedDictionary(&convertedDictionary);
-                dictionaryFileName = findDictionary(":/databases/XT9_LDBs/", locale);
-                if (!dictionaryFileName.isEmpty()) {
-                    if (dictionaryTask.isNull() || dictionaryTask->fileUri != dictionaryFileName) {
-                        dictionaryTask.reset(new T9WriteDictionaryTask(dictionaryFileName, memFuncs));
-                        q->connect(dictionaryTask.data(), SIGNAL(completed(QString,void*)),
-                                   SLOT(dictionaryLoadCompleted(QString,void*)), Qt::DirectConnection);
-                        worker->addTask(dictionaryTask);
-                    }
-                }
-            }
+        if (!updateSymbolCategories(language, locale, inputMode))
+            return false;
+        updateRecognitionMode(language, locale, inputMode);
+        updateDictionary(language, locale, languageChanged);
+        static const QList<DECUMA_UINT32> rtlLanguages = QList<DECUMA_UINT32>()
+                << DECUMA_LANG_AR << DECUMA_LANG_IW << DECUMA_LANG_FA << DECUMA_LANG_UR;
+        sessionSettings.writingDirection = rtlLanguages.contains(language) ? rightToLeft : unknownWriting;
 
-            // Attach existing dictionary, if necessary
-            if (sessionSettings.recognitionMode == mcrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) &&
-                    convertedDictionary && !attachedDictionary) {
-                attachDictionary(convertedDictionary);
-                attachedDictionary = convertedDictionary;
-            }
-        }
+        VIRTUALKEYBOARD_DEBUG() << " -> language categories:" << languageCategories;
+        VIRTUALKEYBOARD_DEBUG() << " -> symbol categories:" << symbolCategories;
+        VIRTUALKEYBOARD_DEBUG() << " -> recognition mode:" << sessionSettings.recognitionMode;
 
         // Change session settings
         sessionSettings.charSet.pSymbolCategories = symbolCategories.data();
         sessionSettings.charSet.nSymbolCategories = symbolCategories.size();
         sessionSettings.charSet.pLanguages = languageCategories.data();
         sessionSettings.charSet.nLanguages = languageCategories.size();
-        DECUMA_STATUS status = decumaChangeSessionSettings(decumaSession, &sessionSettings);
+        DECUMA_STATUS status = DECUMA_API(ChangeSessionSettings)(decumaSession, &sessionSettings);
         Q_ASSERT(status == decumaNoError);
 
-        caseFormatter.preferLowercase = inputMethodHints.testFlag(Qt::ImhPreferLowercase);
+        caseFormatter.preferLowercase = q->inputContext()->inputMethodHints().testFlag(Qt::ImhPreferLowercase);
 
         return status == decumaNoError;
     }
 
-    DECUMA_UINT32 mapToDecumaLanguage(const QLocale &locale)
+    EngineMode mapLocaleToEngineMode(const QLocale &locale)
+    {
+#ifdef HAVE_T9WRITE_CJK
+        switch (locale.language()) {
+        case QLocale::Chinese: {
+            if (locale.script() == QLocale::TraditionalChineseScript)
+                return locale.country() == QLocale::HongKong ? HongKongChinese : TraditionalChinese;
+            return SimplifiedChinese;
+            break;
+        }
+        default:
+            break;
+        }
+#else
+        Q_UNUSED(locale)
+#endif
+
+#ifdef HAVE_T9WRITE_ALPHABETIC
+        return T9WriteInputMethodPrivate::Alphabetic;
+#else
+        return T9WriteInputMethodPrivate::EngineUninitialized;
+#endif
+    }
+
+    DECUMA_UINT32 mapToDecumaLanguage(const QLocale &locale, InputEngine::InputMode inputMode)
     {
         static const QLocale::Language maxLanguage = QLocale::Vietnamese;
         static const DECUMA_UINT32 languageMap[maxLanguage + 1] = {
@@ -573,22 +608,319 @@ public:
             DECUMA_LANG_VI                  // Vietnamese = 132
         };
 
+        int localeLanguage = locale.language();
         if (locale.language() > maxLanguage)
             return DECUMA_LANG_GSMDEFAULT;
 
-        DECUMA_UINT32 language = languageMap[locale.language()];
+        DECUMA_UINT32 language = languageMap[localeLanguage];
+        if (language == DECUMA_LANG_PRC) {
+            if (inputMode != InputEngine::ChineseHandwriting)
+                language = DECUMA_LANG_EN;
+        }
 
         return language;
     }
 
+    void updateRecognitionMode(DECUMA_UINT32 language, const QLocale &locale,
+                               InputEngine::InputMode inputMode)
+    {
+        Q_Q(T9WriteInputMethod);
+        Q_UNUSED(language)
+        Q_UNUSED(locale)
+        Q_UNUSED(inputMode)
+
+        // Select recognition mode
+        // Note: MCR mode is preferred, as it does not require recognition
+        //       timer and provides better user experience.
+        sessionSettings.recognitionMode = mcrMode;
+
+        // Use scrMode with hidden text or with no predictive mode
+        if (inputMode != InputEngine::ChineseHandwriting) {
+            const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
+            if (inputMethodHints.testFlag(Qt::ImhHiddenText) || inputMethodHints.testFlag(Qt::ImhNoPredictiveText))
+                sessionSettings.recognitionMode = scrMode;
+        }
+    }
+
+    bool updateSymbolCategories(DECUMA_UINT32 language, const QLocale &locale,
+                                InputEngine::InputMode inputMode)
+    {
+        // Handle CJK in separate method
+        if (cjk)
+            return updateSymbolCategoriesCjk(language, locale, inputMode);
+
+        symbolCategories.clear();
+
+        // Choose the symbol categories by input mode, script and input method hints
+        bool leftToRightGestures = true;
+        Q_Q(T9WriteInputMethod);
+        const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
+        switch (inputMode) {
+        case InputEngine::Latin:
+            if (inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly)) {
+                symbolCategories.append(DECUMA_CATEGORY_EMAIL);
+            } else if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly)) {
+                symbolCategories.append(DECUMA_CATEGORY_URL);
+            } else {
+                bool includeDigits = true;
+                bool includeBasicPunctuation = true;
+                switch (locale.script()) {
+                case QLocale::LatinScript:
+                    if (language == DECUMA_LANG_EN)
+                        symbolCategories.append(DECUMA_CATEGORY_ANSI);
+                    else
+                        symbolCategories.append(DECUMA_CATEGORY_ISO8859_1);
+                    break;
+
+                case QLocale::CyrillicScript:
+                    symbolCategories.append(DECUMA_CATEGORY_CYRILLIC);
+                    break;
+
+                case QLocale::GreekScript:
+                    symbolCategories.append(DECUMA_CATEGORY_GREEK);
+                    break;
+
+                default:
+                    qWarning() << "Handwriting is not supported in" << locale.name();
+                    return false;
+                }
+
+                if (includeDigits)
+                    symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+
+                if (includeBasicPunctuation) {
+                    symbolCategories.append(DECUMA_CATEGORY_BASIC_PUNCTUATIONS);
+                    symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
+                }
+
+                if (language == DECUMA_LANG_ES)
+                    symbolCategories.append(DECUMA_CATEGORY_SPANISH_PUNCTUATIONS);
+            }
+            break;
+
+        case InputEngine::Numeric:
+            symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+            if (!inputMethodHints.testFlag(Qt::ImhDigitsOnly))
+                symbolCategories.append(DECUMA_CATEGORY_NUM_SUP);
+            break;
+
+        case InputEngine::Dialable:
+            symbolCategories.append(DECUMA_CATEGORY_PHONE_NUMBER);
+            break;
+
+        default:
+            qWarning() << "Handwriting is not supported in" << locale.name();
+            return false;
+        }
+
+        if (leftToRightGestures) {
+            symbolCategories.append(DECUMA_CATEGORY_BACKSPACE_STROKE);
+            symbolCategories.append(DECUMA_CATEGORY_RETURN_STROKE);
+            symbolCategories.append(DECUMA_CATEGORY_WHITESPACE_STROKE);
+        }
+
+        return true;
+    }
+
+    bool updateSymbolCategoriesCjk(DECUMA_UINT32 language, const QLocale &locale,
+                                   InputEngine::InputMode inputMode)
+    {
+        Q_UNUSED(language)
+        Q_ASSERT(cjk);
+
+        symbolCategories.clear();
+
+        switch (inputMode) {
+        case InputEngine::Latin:
+            symbolCategories.append(DECUMA_CATEGORY_ANSI);
+            symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+            symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
+            break;
+
+        case InputEngine::Numeric:
+            symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+            symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+            symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
+            break;
+
+        case InputEngine::Dialable:
+            symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+            symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+            break;
+
+        case InputEngine::ChineseHandwriting:
+            switch (locale.script()) {
+            case QLocale::SimplifiedChineseScript:
+                symbolCategories.append(DECUMA_CATEGORY_GB2312_A);
+                symbolCategories.append(DECUMA_CATEGORY_GB2312_B_CHARS_ONLY);
+                symbolCategories.append(DECUMA_CATEGORY_GBK_3);
+                symbolCategories.append(DECUMA_CATEGORY_GBK_4);
+                symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+                symbolCategories.append(DECUMA_CATEGORY_CJK_GENERAL_PUNCTUATIONS);
+                symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
+                break;
+
+            case QLocale::TraditionalChineseScript:
+                symbolCategories.append(DECUMA_CATEGORY_BIGFIVE);
+                if (language == DECUMA_LANG_HK)
+                    symbolCategories.append(DECUMA_CATEGORY_HKSCS_CHARS_ONLY);
+                symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+                symbolCategories.append(DECUMA_CATEGORY_CJK_GENERAL_PUNCTUATIONS);
+                symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
+                break;
+
+            default:
+                qWarning() << "Chinese handwriting is not supported in" << locale.name();
+                return false;
+            }
+            break;
+
+        default:
+            return false;
+        }
+
+        return true;
+    }
+
+    void updateDictionary(DECUMA_UINT32 language, const QLocale &locale, bool languageChanged)
+    {
+        Q_Q(T9WriteInputMethod);
+
+        /*  The dictionary is loaded in the background thread. Once the loading is
+            complete the dictionary will be attached to the current session. The
+            attachment happens in the worker thread context, thus the direct
+            connection for the signal handler and the mutex protecting the
+            converted dictionary for concurrent access.
+            The loading operation is blocking for the main thread only if the
+            user starts handwriting input before the operation is complete.
+        */
+        QMutexLocker dictionaryGuard(&dictionaryLock);
+
+        // Detach previous dictionary if the language is being changed
+        // or the recognizer mode is single-character mode
+        const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
+        if ((languageChanged || inputMethodHints.testFlag(Qt::ImhNoPredictiveText) || sessionSettings.recognitionMode == scrMode) && attachedDictionary) {
+            detachDictionary(attachedDictionary);
+            attachedDictionary.reset();
+        }
+
+        // Check if a dictionary needs to be loaded
+        if (languageChanged || !loadedDictionary) {
+            loadedDictionary.reset();
+
+            DECUMA_SRC_DICTIONARY_INFO dictionaryInfo;
+            memset(&dictionaryInfo, 0, sizeof(dictionaryInfo));
+
+            QList<QLocale> decumaLocales;
+            decumaLocales.append(locale);
+
+            // CJK: No dictionary for latin input
+            if (cjk && language == DECUMA_LANG_EN)
+                decumaLocales.clear();
+
+            dictionaryFileName.clear();
+            QLocale decumaLocale;
+            for (QLocale tryLocale : decumaLocales) {
+                dictionaryFileName = findDictionary(defaultDictionaryDbPath, tryLocale, dictionaryInfo.srcType);
+                if (!dictionaryFileName.isEmpty()) {
+                    decumaLocale = tryLocale;
+                    break;
+                }
+            }
+            if (!dictionaryFileName.isEmpty()) {
+                if (dictionaryTask.isNull() || dictionaryTask->dictionaryFileName != dictionaryFileName) {
+                    VIRTUALKEYBOARD_DEBUG() << " -> load dictionary:" << dictionaryFileName;
+
+                    bool convertDictionary = true;
+#if defined(HAVE_T9WRITE_CJK) && T9WRITEAPIMAJORVERNUM >= 20
+                    // Chinese dictionary cannot be converted (PHD)
+                    if (dictionaryInfo.srcType == decumaPortableHWRDictionary && decumaLocale.language() == QLocale::Chinese)
+                        convertDictionary = false;
+#endif
+
+                    QSharedPointer<T9WriteDictionary> newDictionary(new T9WriteDictionary(decumaSession, memFuncs, cjk));
+                    dictionaryTask.reset(new T9WriteDictionaryTask(newDictionary, dictionaryFileName, convertDictionary, dictionaryInfo));
+
+                    QObject::connect(dictionaryTask.data(), &T9WriteDictionaryTask::completed,
+                                     q, &T9WriteInputMethod::dictionaryLoadCompleted, Qt::DirectConnection);
+                    worker->addTask(dictionaryTask);
+                }
+            }
+        }
+
+        // Attach existing dictionary, if available
+        if (sessionSettings.recognitionMode == mcrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) &&
+                loadedDictionary && !attachedDictionary) {
+            if (attachDictionary(loadedDictionary))
+                attachedDictionary = loadedDictionary;
+        }
+    }
+
+    QByteArray getContext(InputEngine::PatternRecognitionMode patternRecognitionMode,
+                          const QVariantMap &traceCaptureDeviceInfo,
+                          const QVariantMap &traceScreenInfo) const
+    {
+        QCryptographicHash hash(QCryptographicHash::Md5);
+
+        hash.addData((const char *)&patternRecognitionMode, sizeof(patternRecognitionMode));
+
+        QByteArray mapData;
+        QDataStream ds(&mapData, QIODevice::WriteOnly);
+        ds << traceCaptureDeviceInfo;
+        ds << traceScreenInfo;
+        hash.addData(mapData);
+
+        return hash.result();
+    }
+
+    void setContext(InputEngine::PatternRecognitionMode patternRecognitionMode,
+                    const QVariantMap &traceCaptureDeviceInfo,
+                    const QVariantMap &traceScreenInfo)
+    {
+        QByteArray context = getContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
+        if (context == currentContext)
+            return;
+        currentContext = context;
+
+        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::setContext():" << QString(context.toHex());
+
+        finishRecognition();
+
+        const int dpi = traceCaptureDeviceInfo.value("dpi", 96).toInt();
+        static const int INSTANT_GESTURE_WIDTH_THRESHOLD_MM = 25;
+        static const int INSTANT_GESTURE_HEIGHT_THRESHOLD_MM = 25;
+        instantGestureSettings.widthThreshold = INSTANT_GESTURE_WIDTH_THRESHOLD_MM / 25.4 * dpi;
+        instantGestureSettings.heightThreshold = INSTANT_GESTURE_HEIGHT_THRESHOLD_MM / 25.4 * dpi;
+
+        gestureRecognizer.setDpi(dpi);
+
+        QVariantList horizontalRulers(traceScreenInfo.value("horizontalRulers", QVariantList()).toList());
+        if (horizontalRulers.count() >= 2) {
+            sessionSettings.baseline = horizontalRulers.last().toInt();
+            sessionSettings.helpline = 0;
+            sessionSettings.topline = horizontalRulers.first().toInt();
+            sessionSettings.supportLineSet = baselineAndTopline;
+        } else {
+            sessionSettings.baseline = 0;
+            sessionSettings.helpline = 0;
+            sessionSettings.topline = 0;
+            sessionSettings.supportLineSet = baselineAndTopline;
+        }
+
+        DECUMA_STATUS status = DECUMA_API(ChangeSessionSettings)(decumaSession, &sessionSettings);
+        Q_ASSERT(status == decumaNoError);
+    }
+
     Trace *traceBegin(int traceId, InputEngine::PatternRecognitionMode patternRecognitionMode,
                       const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
     {
         Q_UNUSED(traceId)
         Q_UNUSED(patternRecognitionMode)
-        Q_UNUSED(traceCaptureDeviceInfo)
         Q_UNUSED(traceScreenInfo)
 
+        if (!worker)
+            return 0;
+
         stopResultTimer();
 
         // Dictionary must be completed before the arc addition can begin
@@ -606,30 +938,34 @@ public:
             recognitionTask.reset();
         }
 
-        const int dpi = traceCaptureDeviceInfo.value("dpi", 96).toInt();
-        static const int INSTANT_GESTURE_WIDTH_THRESHOLD_MM = 25;
-        static const int INSTANT_GESTURE_HEIGHT_THRESHOLD_MM = 25;
-        instantGestureSettings.widthThreshold = INSTANT_GESTURE_WIDTH_THRESHOLD_MM / 25.4 * dpi;
-        instantGestureSettings.heightThreshold = INSTANT_GESTURE_HEIGHT_THRESHOLD_MM / 25.4 * dpi;
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+        if (!unipenTrace)
+            unipenTrace.reset(new UnipenTrace(traceCaptureDeviceInfo, traceScreenInfo));
+#endif
+
+        setContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
 
         DECUMA_STATUS status;
 
         if (!arcAdditionStarted) {
-            status = decumaBeginArcAddition(decumaSession);
+            status = DECUMA_API(BeginArcAddition)(decumaSession);
             Q_ASSERT(status == decumaNoError);
             arcAdditionStarted = true;
         }
 
         DECUMA_UINT32 arcID = (DECUMA_UINT32)traceId;
-        status = decumaStartNewArc(decumaSession, arcID);
+        status = DECUMA_API(StartNewArc)(decumaSession, arcID);
         Q_ASSERT(status == decumaNoError);
         if (status != decumaNoError) {
-            decumaEndArcAddition(decumaSession);
+            DECUMA_API(EndArcAddition)(decumaSession);
             arcAdditionStarted = false;
             return NULL;
         }
 
         Trace *trace = new Trace();
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+        trace->setChannels(QStringList("t"));
+#endif
         traceList.append(trace);
 
         return trace;
@@ -638,7 +974,7 @@ public:
     void traceEnd(Trace *trace)
     {
         if (trace->isCanceled()) {
-            decumaCancelArc(decumaSession, trace->traceId());
+            DECUMA_API(CancelArc)(decumaSession, trace->traceId());
             traceList.removeOne(trace);
             delete trace;
         } else {
@@ -646,10 +982,16 @@ public:
         }
         if (!traceList.isEmpty()) {
             Q_ASSERT(arcAdditionStarted);
-            if (countActiveTraces() == 0)
+            if (countActiveTraces() == 0) {
                 restartRecognition();
+                if (cjk) {
+                    // For some reason gestures don't seem to work in CJK mode
+                    // Using our own gesture recognizer as fallback
+                    handleGesture();
+                }
+            }
         } else if (arcAdditionStarted) {
-            decumaEndArcAddition(decumaSession);
+            DECUMA_API(EndArcAddition)(decumaSession);
             arcAdditionStarted = false;
         }
     }
@@ -675,17 +1017,26 @@ public:
         Q_ASSERT(decumaSession != 0);
 
         const QVariantList points = trace->points();
+        Q_ASSERT(!points.isEmpty());
         DECUMA_UINT32 arcID = (DECUMA_UINT32)trace->traceId();
         DECUMA_STATUS status;
 
         for (const QVariant &p : points) {
             const QPoint pt(p.toPointF().toPoint());
-            status = decumaAddPoint(decumaSession, (DECUMA_COORD)pt.x(),(DECUMA_COORD)pt.y(), arcID);
-            Q_ASSERT(status == decumaNoError);
+            status = DECUMA_API(AddPoint)(decumaSession, (DECUMA_COORD)pt.x(),(DECUMA_COORD)pt.y(), arcID);
+            if (status != decumaNoError) {
+                VIRTUALKEYBOARD_DEBUG() << "decumaAddPoint failed" << status;
+                finishRecognition();
+                return;
+            }
         }
 
-        status = decumaCommitArc(decumaSession, arcID);
-        Q_ASSERT(status == decumaNoError);
+        status = DECUMA_API(CommitArc)(decumaSession, arcID);
+        if (status != decumaNoError) {
+            VIRTUALKEYBOARD_DEBUG() << "decumaCommitArc failed" << status;
+            finishRecognition();
+            return;
+        }
     }
 
     void noteSelected(int index)
@@ -696,7 +1047,7 @@ public:
         VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::noteSelected():" << index;
         Q_ASSERT(index >= 0 && index < wordCandidatesHwrResultIndex.length());
         int resultIndex = wordCandidatesHwrResultIndex[index];
-        DECUMA_STATUS status = decumaNoteSelectedCandidate(decumaSession, resultIndex);
+        DECUMA_STATUS status = DECUMA_API(NoteSelectedCandidate)(decumaSession, resultIndex);
         Q_UNUSED(status)
         Q_ASSERT(status == decumaNoError);
     }
@@ -723,6 +1074,8 @@ public:
     bool finishRecognition(bool emitSelectionListChanged = true)
     {
         VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::finishRecognition()";
+        if (!worker)
+            return false;
 
         bool result = !traceList.isEmpty();
 
@@ -742,7 +1095,7 @@ public:
         }
 
         if (arcAdditionStarted) {
-            decumaEndArcAddition(decumaSession);
+            DECUMA_API(EndArcAddition)(decumaSession);
             arcAdditionStarted = false;
         }
 
@@ -762,11 +1115,18 @@ public:
         scrResult.clear();
         caseFormatter.clear();
 
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+        unipenTrace.reset();
+#endif
+
         return result;
     }
 
     bool select(int index = -1)
     {
+        if (!worker)
+            return false;
+
         if (sessionSettings.recognitionMode == mcrMode && wordCandidates.isEmpty()) {
             finishRecognition();
             return false;
@@ -783,6 +1143,25 @@ public:
             index = index >= 0 ? index : activeWordIndex;
             noteSelected(index);
             QString finalWord = wordCandidates.at(index);
+
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+            // Record trace
+            if (unipenTrace) {
+                if (finalWord.length() == 1) {
+                    // In recording mode, the text case must match with the current text case
+                    QChar ch(finalWord.at(0));
+                    if (!ch.isLetter() || (ch.isUpper() == (textCase == InputEngine::Upper))) {
+                        QStringList homeLocations = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
+                        if (!homeLocations.isEmpty()) {
+                            unipenTrace->setDirectory(QStringLiteral("%1/%2").arg(homeLocations.at(0)).arg("VIRTUAL_KEYBOARD_TRACES"));
+                            unipenTrace->record(traceList);
+                            unipenTrace->save(ch.unicode(), 100);
+                        }
+                    }
+                }
+            }
+#endif
+
             finishRecognition();
             q->inputContext()->commit(finalWord);
         } else if (sessionSettings.recognitionMode == scrMode) {
@@ -865,8 +1244,11 @@ public:
 
         bool wordCandidatesChanged = wordCandidates != newWordCandidates;
 
+#ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
         // Delete trace history
-        if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty()) {
+        const InputEngine::InputMode inputMode = q->inputEngine()->inputMode();
+        if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty() &&
+                inputMode != InputEngine::ChineseHandwriting) {
             int activeTraces = symbolStrokes.at(symbolStrokes.count() - 1).toInt();
             if (symbolStrokes.count() > 1)
                 activeTraces += symbolStrokes.at(symbolStrokes.count() - 2).toInt();
@@ -874,7 +1256,14 @@ public:
                 delete traceList.takeFirst();
         }
 
-        // Look for a gesture at the end of first result
+        // Enforce hard limit for number of traces
+        if (traceList.count() >= traceListHardLimit) {
+            VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::processResult(): Clearing traces (hard limit):" << traceList.count();
+            clearTraces();
+        }
+#endif
+
+        // Find a gesture at the end of the first result
         if (!gesture.isEmpty()) {
 
             DECUMA_UNICODE gestureSymbol = gesture.at(0).unicode();
@@ -892,8 +1281,8 @@ public:
                 break;
 
             default:
-                finishRecognition();
                 ic->commit(ic->preeditText());
+                finishRecognition();
                 break;
             }
 
@@ -920,6 +1309,85 @@ public:
         }
     }
 
+    bool handleGesture()
+    {
+        if (countActiveTraces() > 0)
+            return false;
+
+        QVariantMap gesture(gestureRecognizer.recognize(traceList.mid(traceList.length() - 1, 1)));
+        if (gesture.isEmpty())
+            return false;
+
+        VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::handleGesture():" << gesture;
+
+        if (gesture[QLatin1String("type")].toString() == QLatin1String("swipe")) {
+
+            static const int SWIPE_ANGLE_THRESHOLD = 15;    // degrees +-
+
+            qreal swipeLength = gesture[QLatin1String("length")].toReal();
+            if (swipeLength >= instantGestureSettings.widthThreshold) {
+
+                Q_Q(T9WriteInputMethod);
+                InputContext *ic = q->inputContext();
+                if (!ic)
+                    return false;
+
+                qreal swipeAngle = gesture[QLatin1String("angle_degrees")].toReal();
+                int swipeTouchCount = gesture[QLatin1String("touch_count")].toInt();
+
+                // Swipe left
+                if (swipeAngle <= 180 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 180 - SWIPE_ANGLE_THRESHOLD) {
+                    if (swipeTouchCount == 1) {
+                        // Single swipe: backspace
+                        ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier);
+                        return true;
+                    }
+                    return false;
+                }
+
+                // Swipe right
+                const InputEngine::InputMode inputMode = q->inputEngine()->inputMode();
+                if (inputMode != InputEngine::ChineseHandwriting) {
+                    if (swipeAngle <= SWIPE_ANGLE_THRESHOLD || swipeAngle >= 360 - SWIPE_ANGLE_THRESHOLD) {
+                        if (swipeTouchCount == 1) {
+                            // Single swipe: space
+                            ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QString(" "), Qt::NoModifier);
+                            return true;
+                        }
+                        return false;
+                    }
+                }
+
+                // Swipe up
+                if (swipeAngle <= 270 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 270 - SWIPE_ANGLE_THRESHOLD) {
+                    if (swipeTouchCount == 1) {
+                        // Single swipe: toggle input mode
+                        select();
+                        if (!(ic->inputMethodHints() & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly))) {
+                            QList<int> inputModes = ic->inputEngine()->inputModes();
+                            // Filter out duplicate numeric mode (in favor of Numeric)
+                            int indexOfNumericInputMode = inputModes.indexOf(InputEngine::Numeric);
+                            int indexOfDialableInputMode = inputModes.indexOf(InputEngine::Dialable);
+                            if (indexOfNumericInputMode != -1 && indexOfDialableInputMode != -1)
+                                inputModes.removeAt(inputMode != InputEngine::Dialable ?
+                                            indexOfDialableInputMode :
+                                            indexOfNumericInputMode);
+                            if (inputModes.count() > 1) {
+                                int inputModeIndex = inputModes.indexOf((int)inputMode) + 1;
+                                if (inputModeIndex >= inputModes.count())
+                                    inputModeIndex = 0;
+                                ic->inputEngine()->setInputMode((InputEngine::InputMode)inputModes.at(inputModeIndex));
+                            }
+                        }
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
     bool isValidInputChar(const QChar &c) const
     {
         if (c.isLetterOrNumber())
@@ -948,17 +1416,23 @@ public:
 
     T9WriteInputMethod *q_ptr;
     static const DECUMA_MEM_FUNCTIONS memFuncs;
+    bool cjk;
+    EngineMode engineMode;
+    QByteArray currentContext;
     DECUMA_SESSION_SETTINGS sessionSettings;
     DECUMA_INSTANT_GESTURE_SETTINGS instantGestureSettings;
+    QString defaultHwrDbPath;
+    QString defaultDictionaryDbPath;
     QFile hwrDbFile;
     QVector<DECUMA_UINT32> languageCategories;
     QVector<DECUMA_UINT32> symbolCategories;
     QScopedPointer<T9WriteWorker> worker;
     QList<Trace *> traceList;
+    int traceListHardLimit;
     QMutex dictionaryLock;
     QString dictionaryFileName;
-    void *convertedDictionary;
-    void *attachedDictionary;
+    QSharedPointer<T9WriteDictionary> loadedDictionary;
+    QSharedPointer<T9WriteDictionary> attachedDictionary;
     QSharedPointer<T9WriteDictionaryTask> dictionaryTask;
     QSharedPointer<T9WriteRecognitionTask> recognitionTask;
     int resultId;
@@ -974,6 +1448,10 @@ public:
     bool ignoreUpdate;
     InputEngine::TextCase textCase;
     T9WriteCaseFormatter caseFormatter;
+    HandwritingGestureRecognizer gestureRecognizer;
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+    QScopedPointer<UnipenTrace> unipenTrace;
+#endif
 };
 
 const DECUMA_MEM_FUNCTIONS T9WriteInputMethodPrivate::memFuncs = {
@@ -991,8 +1469,6 @@ const DECUMA_MEM_FUNCTIONS T9WriteInputMethodPrivate::memFuncs = {
 T9WriteInputMethod::T9WriteInputMethod(QObject *parent) :
     AbstractInputMethod(*new T9WriteInputMethodPrivate(this), parent)
 {
-    Q_D(T9WriteInputMethod);
-    d->initEngine();
 }
 
 T9WriteInputMethod::~T9WriteInputMethod()
@@ -1003,18 +1479,58 @@ T9WriteInputMethod::~T9WriteInputMethod()
 
 QList<InputEngine::InputMode> T9WriteInputMethod::inputModes(const QString &locale)
 {
-    Q_UNUSED(locale)
-    return QList<InputEngine::InputMode>()
-            << InputEngine::Latin
-            << InputEngine::Numeric
-            << InputEngine::Dialable;
+    Q_D(T9WriteInputMethod);
+    QList<InputEngine::InputMode> availableInputModes;
+    const Qt::InputMethodHints inputMethodHints(inputContext()->inputMethodHints());
+    T9WriteInputMethodPrivate::EngineMode mode = d->mapLocaleToEngineMode(QLocale(locale));
+
+    // Add primary input mode
+    switch (mode) {
+#ifdef HAVE_T9WRITE_ALPHABETIC
+    case T9WriteInputMethodPrivate::Alphabetic:
+        if (d->findHwrDb(T9WriteInputMethodPrivate::Alphabetic, d->defaultHwrDbPath).isEmpty())
+            return availableInputModes;
+        if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly)))
+            availableInputModes.append(InputEngine::Latin);
+        break;
+#endif
+#ifdef HAVE_T9WRITE_CJK
+    case T9WriteInputMethodPrivate::SimplifiedChinese:
+    case T9WriteInputMethodPrivate::TraditionalChinese:
+    case T9WriteInputMethodPrivate::HongKongChinese:
+        if (d->findHwrDb(mode, d->defaultHwrDbPath).isEmpty())
+            return availableInputModes;
+        if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly)))
+            availableInputModes.append(InputEngine::ChineseHandwriting);
+        break;
+#endif
+    default:
+        return availableInputModes;
+    }
+
+    // Add exclusive input modes
+    if (inputMethodHints.testFlag(Qt::ImhDialableCharactersOnly) || inputMethodHints.testFlag(Qt::ImhDigitsOnly)) {
+        availableInputModes.append(InputEngine::Dialable);
+    } else if (inputMethodHints.testFlag(Qt::ImhFormattedNumbersOnly)) {
+        availableInputModes.append(InputEngine::Numeric);
+    } else if (inputMethodHints.testFlag(Qt::ImhLatinOnly)) {
+        availableInputModes.append(InputEngine::Latin);
+    } else {
+        // Add other input modes
+        Q_ASSERT(!availableInputModes.isEmpty());
+        if (!availableInputModes.contains(InputEngine::Latin))
+            availableInputModes.append(InputEngine::Latin);
+        availableInputModes.append(InputEngine::Numeric);
+    }
+
+    return availableInputModes;
 }
 
 bool T9WriteInputMethod::setInputMode(const QString &locale, InputEngine::InputMode inputMode)
 {
     Q_D(T9WriteInputMethod);
     d->select();
-    return d->setInputMode(locale, inputMode);
+    return d->setInputMode(QLocale(locale), inputMode);
 }
 
 bool T9WriteInputMethod::setTextCase(InputEngine::TextCase textCase)
@@ -1045,6 +1561,13 @@ bool T9WriteInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::Keyboard
             if (preeditText.length() > 1) {
                 preeditText.chop(1);
                 ic->setPreeditText(preeditText);
+                // WA:  T9Write CJK may crash in some cases with long stringStart.
+                //      Therefore we commit the current input and finish the recognition.
+                if (d->cjk) {
+                    ic->commit();
+                    d->finishRecognition();
+                    return true;
+                }
                 d->caseFormatter.ensureLength(preeditText.length(), d->textCase);
                 T9WriteCaseFormatter caseFormatter(d->caseFormatter);
                 d->finishRecognition(false);
@@ -1182,11 +1705,13 @@ bool T9WriteInputMethod::reselect(int cursorPosition, const InputEngine::Reselec
     if (!ic)
         return false;
 
+    const InputEngine::InputMode inputMode = inputEngine()->inputMode();
+    const int maxLength = inputMode == InputEngine::ChineseHandwriting ? 0 : 32;
     const QString surroundingText = ic->surroundingText();
     int replaceFrom = 0;
 
     if (reselectFlags.testFlag(InputEngine::WordBeforeCursor)) {
-        for (int i = cursorPosition - 1; i >= 0; --i) {
+        for (int i = cursorPosition - 1; i >= 0 && d->stringStart.length() < maxLength; --i) {
             QChar c = surroundingText.at(i);
             if (!d->isValidInputChar(c))
                 break;
@@ -1206,7 +1731,7 @@ bool T9WriteInputMethod::reselect(int cursorPosition, const InputEngine::Reselec
     }
 
     if (reselectFlags.testFlag(InputEngine::WordAfterCursor)) {
-        for (int i = cursorPosition; i < surroundingText.length(); ++i) {
+        for (int i = cursorPosition; i < surroundingText.length() && d->stringStart.length() < maxLength; ++i) {
             QChar c = surroundingText.at(i);
             if (!d->isValidInputChar(c))
                 break;
@@ -1224,7 +1749,7 @@ bool T9WriteInputMethod::reselect(int cursorPosition, const InputEngine::Reselec
     if (d->stringStart.isEmpty())
         return false;
 
-    if (reselectFlags.testFlag(InputEngine::WordAtCursor) && replaceFrom == -d->stringStart.length()) {
+    if (reselectFlags.testFlag(InputEngine::WordAtCursor) && replaceFrom == -d->stringStart.length() && d->stringStart.length() < maxLength) {
         d->stringStart.clear();
         return false;
     }
@@ -1258,35 +1783,38 @@ void T9WriteInputMethod::timerEvent(QTimerEvent *timerEvent)
     if (timerId == d->resultTimer) {
         if (d->sessionSettings.recognitionMode == mcrMode) {
             d->stopResultTimer();
-            d->clearTraces();
+#ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+            const InputEngine::InputMode inputMode = inputEngine()->inputMode();
+            if (inputMode != InputEngine::ChineseHandwriting)
+                d->clearTraces();
+#endif
         } else if (d->sessionSettings.recognitionMode == scrMode) {
             d->select();
         }
     }
 }
 
-void T9WriteInputMethod::dictionaryLoadCompleted(const QString &fileUri, void *dictionary)
+void T9WriteInputMethod::dictionaryLoadCompleted(QSharedPointer<T9WriteDictionary> dictionary)
 {
     Q_D(T9WriteInputMethod);
     // Note: This method is called in worker thread context
     QMutexLocker dictionaryGuard(&d->dictionaryLock);
 
-    VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::dictionaryLoadCompleted():" << fileUri << dictionary;
-
     if (!dictionary)
         return;
 
+    VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::dictionaryLoadCompleted():"
+                            << dictionary->fileName() << dictionary->data() << dictionary->size();
+
     InputContext *ic = inputContext();
-    if (ic && fileUri == d->dictionaryFileName) {
-        d->convertedDictionary = dictionary;
+    if (ic && dictionary->fileName() == d->dictionaryFileName) {
+        d->loadedDictionary = dictionary;
         if (d->sessionSettings.recognitionMode == mcrMode &&
                 !ic->inputMethodHints().testFlag(Qt::ImhNoPredictiveText) &&
                 !d->attachedDictionary) {
-            d->attachDictionary(d->convertedDictionary);
-            d->attachedDictionary = d->convertedDictionary;
+            if (d->attachDictionary(d->loadedDictionary))
+                d->attachedDictionary = d->loadedDictionary;
         }
-    } else {
-        d->destroyConvertedDictionary(&dictionary);
     }
 }
 
@@ -1315,4 +1843,10 @@ void T9WriteInputMethod::resultsAvailable(const QVariantList &resultList)
     d->resultsAvailable(resultList);
 }
 
+void T9WriteInputMethod::recognitionError(int status)
+{
+    VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::recognitionError():" << status;
+    reset();
+}
+
 } // namespace QtVirtualKeyboard
diff --git a/src/virtualkeyboard/t9writeinputmethod.h b/src/virtualkeyboard/t9writeinputmethod.h
index 9b87ebdf6f6017d57834040113db29f51fd59770..173abd990d1704cd6e9794b0bbaafa22c0d9b5a5 100644
--- a/src/virtualkeyboard/t9writeinputmethod.h
+++ b/src/virtualkeyboard/t9writeinputmethod.h
@@ -31,10 +31,12 @@
 #define T9WRITEINPUTMETHOD_H
 
 #include "abstractinputmethod.h"
+#include <QSharedPointer>
 
 namespace QtVirtualKeyboard {
 
 class T9WriteInputMethodPrivate;
+class T9WriteDictionary;
 
 class T9WriteInputMethod : public AbstractInputMethod
 {
@@ -70,8 +72,9 @@ protected:
     void timerEvent(QTimerEvent *timerEvent);
 
 protected slots:
-    void dictionaryLoadCompleted(const QString &fileUri, void *dictionary);
+    void dictionaryLoadCompleted(QSharedPointer<T9WriteDictionary> dictionary);
     void resultsAvailable(const QVariantList &resultList);
+    void recognitionError(int status);
 };
 
 }
diff --git a/src/virtualkeyboard/t9writeworker.cpp b/src/virtualkeyboard/t9writeworker.cpp
index 733033ce26fe716824a9de24d370401ab48b9913..c2f3312434202f0db75fba56bdc971e3c246966e 100644
--- a/src/virtualkeyboard/t9writeworker.cpp
+++ b/src/virtualkeyboard/t9writeworker.cpp
@@ -58,10 +58,14 @@ void T9WriteTask::wait()
     \internal
 */
 
-T9WriteDictionaryTask::T9WriteDictionaryTask(const QString &fileUri,
-                                             const DECUMA_MEM_FUNCTIONS &memFuncs) :
-    fileUri(fileUri),
-    memFuncs(memFuncs)
+T9WriteDictionaryTask::T9WriteDictionaryTask(QSharedPointer<T9WriteDictionary> dictionary,
+                                             const QString &dictionaryFileName,
+                                             bool convertDictionary,
+                                             const DECUMA_SRC_DICTIONARY_INFO &dictionaryInfo) :
+    dictionary(dictionary),
+    dictionaryFileName(dictionaryFileName),
+    convertDictionary(convertDictionary),
+    dictionaryInfo(dictionaryInfo)
 {
 }
 
@@ -74,33 +78,19 @@ void T9WriteDictionaryTask::run()
     perf.start();
 #endif
 
-    void *dictionary = 0;
-
-    QFile dictionaryFile(fileUri);
-    if (dictionaryFile.open(QIODevice::ReadOnly)) {
-        uchar *dictionaryData = dictionaryFile.map(0, dictionaryFile.size(), QFile::NoOptions);
-        if (dictionaryData) {
-
-            DECUMA_SRC_DICTIONARY_INFO dictionaryInfo;
-            memset(&dictionaryInfo, 0, sizeof(dictionaryInfo));
-            dictionaryInfo.srcType = decumaXT9LDB;
-            DECUMA_UINT32 dictionarySize = 0;
-            DECUMA_STATUS status = decumaConvertDictionary(&dictionary, dictionaryData, dictionaryFile.size(), &dictionaryInfo, &dictionarySize, &memFuncs);
-            Q_UNUSED(status)
-            Q_ASSERT(status == decumaNoError);
-            dictionaryFile.unmap(dictionaryData);
-        } else {
-            qWarning() << "Could not map dictionary file" << fileUri;
-        }
-    } else {
-        qWarning() << "Could not open dictionary file" << fileUri;
+    bool result = false;
+    if (dictionary) {
+        result = dictionary->load(dictionaryFileName);
+        if (result && convertDictionary)
+            result = dictionary->convert(dictionaryInfo);
     }
 
 #ifdef QT_VIRTUALKEYBOARD_DEBUG
     VIRTUALKEYBOARD_DEBUG() << "T9WriteDictionaryTask::run(): time:" << perf.elapsed() << "ms";
 #endif
 
-    emit completed(fileUri, dictionary);
+    if (result)
+        emit completed(dictionary);
 }
 
 /*!
@@ -109,6 +99,7 @@ void T9WriteDictionaryTask::run()
 */
 
 T9WriteRecognitionResult::T9WriteRecognitionResult(int id, int maxResults, int maxCharsPerWord) :
+    status(decumaNoError),
     numResults(0),
     instantGesture(0),
     id(id),
@@ -176,16 +167,18 @@ void T9WriteRecognitionTask::run()
     perf.start();
 #endif
 
-    DECUMA_STATUS status = decumaIndicateInstantGesture(decumaSession, &result->instantGesture, &instantGestureSettings);
-    Q_ASSERT(status == decumaNoError);
+    DECUMA_STATUS status;
+    if (!cjk) {
+        status = DECUMA_API(IndicateInstantGesture)(decumaSession, &result->instantGesture, &instantGestureSettings);
+        Q_ASSERT(status == decumaNoError);
+    }
 
     DECUMA_INTERRUPT_FUNCTIONS interruptFunctions;
     interruptFunctions.pShouldAbortRecognize = shouldAbortRecognize;
     interruptFunctions.pUserData = (void *)this;
-    status = decumaRecognize(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, &interruptFunctions);
-    if (status == decumaAbortRecognitionUnsupported)
-        status = decumaRecognize(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, NULL);
-    Q_ASSERT(status == decumaNoError);
+    result->status = DECUMA_API(Recognize)(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, &interruptFunctions);
+    if (result->status == decumaAbortRecognitionUnsupported)
+        result->status = DECUMA_API(Recognize)(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, NULL);
 
     QStringList resultList;
     QString gesture;
@@ -261,6 +254,11 @@ void T9WriteRecognitionResultsTask::run()
     if (!result)
         return;
 
+    if (result->status != decumaNoError) {
+        emit recognitionError(result->status);
+        return;
+    }
+
     QVariantList resultList;
     for (int i = 0; i < result->numResults; i++)
     {
@@ -306,12 +304,13 @@ void T9WriteRecognitionResultsTask::run()
     \internal
 */
 
-T9WriteWorker::T9WriteWorker(DECUMA_SESSION *decumaSession, QObject *parent) :
+T9WriteWorker::T9WriteWorker(DECUMA_SESSION *decumaSession, const bool cjk, QObject *parent) :
     QThread(parent),
     taskSema(),
     taskLock(),
     decumaSession(decumaSession),
-    abort(false)
+    abort(false),
+    cjk(cjk)
 {
 }
 
@@ -369,6 +368,7 @@ void T9WriteWorker::run()
         }
         if (currentTask) {
             currentTask->decumaSession = decumaSession;
+            currentTask->cjk  = cjk;
             currentTask->run();
             currentTask->runSema.release();
         }
diff --git a/src/virtualkeyboard/t9writeworker.h b/src/virtualkeyboard/t9writeworker.h
index 23a4cf026615891b4caf68d46a722d4974196474..5dc8c0d91b61015d7c08ecd9ef89c561f07cff11 100644
--- a/src/virtualkeyboard/t9writeworker.h
+++ b/src/virtualkeyboard/t9writeworker.h
@@ -41,7 +41,8 @@
 #include <QMap>
 #include <QVector>
 
-#include "decuma_hwr.h"
+#include "t9write.h"
+#include "t9writedictionary.h"
 
 namespace QtVirtualKeyboard {
 
@@ -59,6 +60,7 @@ public:
 
 protected:
     DECUMA_SESSION *decumaSession;
+    bool cjk;
 
 private:
     QSemaphore runSema;
@@ -68,16 +70,20 @@ class T9WriteDictionaryTask : public T9WriteTask
 {
     Q_OBJECT
 public:
-    explicit T9WriteDictionaryTask(const QString &fileUri,
-                                   const DECUMA_MEM_FUNCTIONS &memFuncs);
+    explicit T9WriteDictionaryTask(QSharedPointer<T9WriteDictionary> dictionary,
+                                   const QString &dictionaryFileName,
+                                   bool convertDictionary,
+                                   const DECUMA_SRC_DICTIONARY_INFO &dictionaryInfo);
 
     void run();
 
-    const QString fileUri;
-    const DECUMA_MEM_FUNCTIONS &memFuncs;
+    QSharedPointer<T9WriteDictionary> dictionary;
+    const QString dictionaryFileName;
+    bool convertDictionary;
+    const DECUMA_SRC_DICTIONARY_INFO dictionaryInfo;
 
 signals:
-    void completed(const QString &fileUri, void *dictionary);
+    void completed(QSharedPointer<T9WriteDictionary> dictionary);
 };
 
 class T9WriteRecognitionResult
@@ -87,12 +93,14 @@ class T9WriteRecognitionResult
 public:
     explicit T9WriteRecognitionResult(int id, int maxResults, int maxCharsPerWord);
 
+    DECUMA_STATUS status;
     QVector<DECUMA_HWR_RESULT> results;
     DECUMA_UINT16 numResults;
     int instantGesture;
     const int id;
     const int maxResults;
     const int maxCharsPerWord;
+
 private:
     QVector<DECUMA_UNICODE> _chars;
     QVector<DECUMA_INT16> _symbolChars;
@@ -135,6 +143,7 @@ public:
 
 signals:
     void resultsAvailable(const QVariantList &resultList);
+    void recognitionError(int status);
 
 private:
     QSharedPointer<T9WriteRecognitionResult> result;
@@ -144,7 +153,7 @@ class T9WriteWorker : public QThread
 {
     Q_OBJECT
 public:
-    explicit T9WriteWorker(DECUMA_SESSION *decumaSession, QObject *parent = 0);
+    explicit T9WriteWorker(DECUMA_SESSION *decumaSession, const bool cjk, QObject *parent = 0);
     ~T9WriteWorker();
 
     void addTask(QSharedPointer<T9WriteTask> task);
@@ -176,6 +185,7 @@ private:
     QMutex taskLock;
     DECUMA_SESSION *decumaSession;
     QAtomicInteger<bool> abort;
+    const bool cjk;
 };
 
 } // namespace QtVirtualKeyboard
diff --git a/src/virtualkeyboard/virtualkeyboard.pro b/src/virtualkeyboard/virtualkeyboard.pro
index 25b1586f79299fbd3b16168f9dc6107c50d88014..e16eaba9fd81dc856770f7dddd65ed290e83734d 100644
--- a/src/virtualkeyboard/virtualkeyboard.pro
+++ b/src/virtualkeyboard/virtualkeyboard.pro
@@ -74,8 +74,9 @@ LAYOUT_FILES += \
 contains(CONFIG, lang-en.*) {
     LAYOUT_FILES += \
         content/layouts/en_GB/main.qml \
-        content/layouts/en_GB/handwriting.qml \
         content/layouts/en_GB/symbols.qml
+t9write-alphabetic|lipi-toolkit: LAYOUT_FILES += \
+        content/layouts/en_GB/handwriting.qml
 }
 contains(CONFIG, lang-ar.*) {
     LAYOUT_FILES += \
@@ -88,21 +89,21 @@ contains(CONFIG, lang-da.*) {
     LAYOUT_FILES += \
         content/layouts/da_DK/main.qml \
         content/layouts/da_DK/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/da_DK/handwriting.qml
 }
 contains(CONFIG, lang-de.*) {
     LAYOUT_FILES += \
         content/layouts/de_DE/main.qml \
         content/layouts/de_DE/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/de_DE/handwriting.qml
 }
 contains(CONFIG, lang-es.*) {
     LAYOUT_FILES += \
         content/layouts/es_ES/main.qml \
         content/layouts/es_ES/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/es_ES/handwriting.qml
 }
 contains(CONFIG, lang-fa.*) {
@@ -116,14 +117,14 @@ contains(CONFIG, lang-fi.*) {
     LAYOUT_FILES += \
         content/layouts/fi_FI/main.qml \
         content/layouts/fi_FI/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/fi_FI/handwriting.qml
 }
 contains(CONFIG, lang-fr.*) {
     LAYOUT_FILES += \
         content/layouts/fr_FR/main.qml \
         content/layouts/fr_FR/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/fr_FR/handwriting.qml
 }
 contains(CONFIG, lang-hi.*) {
@@ -135,7 +136,7 @@ contains(CONFIG, lang-it.*) {
     LAYOUT_FILES += \
         content/layouts/it_IT/main.qml \
         content/layouts/it_IT/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/it_IT/handwriting.qml
 }
 contains(CONFIG, lang-ja.*) {
@@ -152,48 +153,50 @@ contains(CONFIG, lang-nb.*) {
     LAYOUT_FILES += \
         content/layouts/nb_NO/main.qml \
         content/layouts/nb_NO/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/nb_NO/handwriting.qml
 }
 contains(CONFIG, lang-pl.*) {
     LAYOUT_FILES += \
         content/layouts/pl_PL/main.qml \
         content/layouts/pl_PL/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/pl_PL/handwriting.qml
 }
 contains(CONFIG, lang-pt.*) {
     LAYOUT_FILES += \
         content/layouts/pt_PT/main.qml \
         content/layouts/pt_PT/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/pt_PT/handwriting.qml
 }
 contains(CONFIG, lang-ro.*) {
     LAYOUT_FILES += \
         content/layouts/ro_RO/main.qml \
         content/layouts/ro_RO/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/ro_RO/handwriting.qml
 }
 contains(CONFIG, lang-ru.*) {
     LAYOUT_FILES += \
         content/layouts/ru_RU/main.qml \
         content/layouts/ru_RU/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/ru_RU/handwriting.qml
 }
 contains(CONFIG, lang-sv.*) {
     LAYOUT_FILES += \
         content/layouts/sv_SE/main.qml \
         content/layouts/sv_SE/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
         content/layouts/sv_SE/handwriting.qml
 }
 contains(CONFIG, lang-zh(_CN)?) {
     LAYOUT_FILES += \
         content/layouts/zh_CN/main.qml \
         content/layouts/zh_CN/symbols.qml
+t9write-cjk: LAYOUT_FILES += \
+        content/layouts/zh_CN/handwriting.qml
 }
 contains(CONFIG, lang-zh(_TW)?) {
     LAYOUT_FILES += \
@@ -340,18 +343,41 @@ lipi-toolkit {
 t9write {
     include(3rdparty/t9write/t9write-build.pri)
     equals(T9WRITE_FOUND, 0): \
-        error("T9Write SDK could not be found. Please make sure you have extracted" \
-              "the contents of the T9Write SDK to $$PWD/3rdparty/t9write")
+        error("T9Write SDK could not be found. For more information, see" \
+              "the documentation in Building Qt Virtual Keyboard")
     SOURCES += \
         t9writeinputmethod.cpp \
-        t9writeworker.cpp
+        t9writeworker.cpp \
+        t9writedictionary.cpp
     HEADERS += \
         t9writeinputmethod.h \
-        t9writeworker.h
+        t9writeworker.h \
+        t9writedictionary.h \
+        t9write.h
     DEFINES += HAVE_T9WRITE
     QMAKE_USE += t9write_db
     INCLUDEPATH += $$T9WRITE_INCLUDE_DIRS
-    LIBS += $$T9WRITE_ALPHABETIC_LIBS
+    t9write-alphabetic {
+        LIBS += $$T9WRITE_ALPHABETIC_LIBS
+        DEFINES += HAVE_T9WRITE_ALPHABETIC
+        !isEmpty(T9WRITE_ALPHABETIC_BINS) {
+            t9write_alphabetic_bins.files = $$T9WRITE_ALPHABETIC_BINS
+            t9write_alphabetic_bins.path = $$[QT_INSTALL_BINS]
+            INSTALLS += t9write_alphabetic_bins
+            !prefix_build: COPIES += t9write_alphabetic_bins
+        }
+    }
+    t9write-cjk {
+        LIBS += $$T9WRITE_CJK_LIBS
+        DEFINES += HAVE_T9WRITE_CJK
+        !isEmpty(T9WRITE_CJK_BINS) {
+            t9write_cjk_bins.files = $$T9WRITE_CJK_BINS
+            t9write_cjk_bins.path = $$[QT_INSTALL_BINS]
+            INSTALLS += t9write_cjk_bins
+            !prefix_build: COPIES += t9write_cjk_bins
+        }
+    }
+    DEFINES += QT_VIRTUALKEYBOARD_DEBUG
 }
 
 record-trace-input {
diff --git a/tests/auto/inputpanel/data/inputpanel/handwriting.js b/tests/auto/inputpanel/data/inputpanel/handwriting.js
index 5f5fd4bc3190bd6d82a7bb8010a4f6a183b9684d..7ec5e1798fc603e5eead704cf086bdba7e8695f0 100644
--- a/tests/auto/inputpanel/data/inputpanel/handwriting.js
+++ b/tests/auto/inputpanel/data/inputpanel/handwriting.js
@@ -30,17 +30,23 @@
 .pragma library
 
 .import "unipen_data.js" as UnipenData
+.import "unipen_data_simp_chinese.js" as UnipenDataSimpChinese
 
 function emulate(testcase, hwrInputArea, ch, instant) {
     var chKey = (((typeof ch == "number") ? ch : ch.charCodeAt(0)) + 0x100000000).toString(16).substr(1)
     while (chKey.length > 4 && chKey[0] === '0')
         chKey = chKey.substring(1)
     chKey = "0x" + chKey
-    if (!UnipenData.unipenData.hasOwnProperty(chKey))
+    var unipenData
+    if (UnipenData.unipenData.hasOwnProperty(chKey))
+        unipenData = UnipenData
+    else if (UnipenDataSimpChinese.unipenData.hasOwnProperty(chKey))
+        unipenData = UnipenDataSimpChinese
+    else
         return false
-    var chData = UnipenData.unipenData[chKey]
+    var chData = unipenData.unipenData[chKey]
     var scale = Math.min(hwrInputArea.width / chData[".X_DIM"], hwrInputArea.height / chData[".Y_DIM"])
-    var strokes = UnipenData.unipenData[chKey][".PEN"]
+    var strokes = unipenData.unipenData[chKey][".PEN"]
     var t = 0
     for (var strokeIndex = 0; strokeIndex < strokes.length; strokeIndex++) {
         var stroke = strokes[strokeIndex]
diff --git a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
index f082115a9e290339d130a27798bab5ee1fa9b315..05667524437c48dde0097e42d40b494374d7ef91 100644
--- a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
+++ b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
@@ -29,7 +29,7 @@
 
 import QtTest 1.0
 import QtQuick 2.0
-import QtQuick.VirtualKeyboard 2.2
+import QtQuick.VirtualKeyboard 2.3
 import QtQuick.VirtualKeyboard.Settings 2.2
 import "handwriting.js" as Handwriting
 import "utils.js" as Utils
@@ -263,6 +263,8 @@ InputPanel {
             return InputEngine.Katakana
         else if (inputModeName === "FullwidthLatin")
             return InputEngine.FullwidthLatin
+        else if (inputModeName === "ChineseHandwriting")
+            return InputEngine.ChineseHandwriting
         else
             return -1
     }
diff --git a/tests/auto/inputpanel/data/inputpanel/unipen_data.js b/tests/auto/inputpanel/data/inputpanel/unipen_data.js
index 4c69b43dd2966e67de785040c6e87cd3a5a243a8..83ad1a2c50c741555665d64ab682a227ab32f861 100644
--- a/tests/auto/inputpanel/data/inputpanel/unipen_data.js
+++ b/tests/auto/inputpanel/data/inputpanel/unipen_data.js
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
 ** Contact: https://www.qt.io/licensing/
 **
 ** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
@@ -10170,239 +10170,339 @@ var unipenData = {
         ".PEN": [
             [
                 [
-                    435,
-                    78,
+                    407,
+                    75,
                     0
                 ],
                 [
-                    435,
-                    77,
-                    13
-                ],
-                [
-                    435,
-                    76,
-                    33
-                ],
-                [
-                    435,
-                    75,
-                    70
+                    406,
+                    74,
+                    76
                 ],
                 [
-                    434,
+                    403,
                     74,
-                    84
+                    96
                 ],
                 [
-                    434,
+                    401,
                     73,
-                    109
+                    100
                 ],
                 [
-                    431,
-                    70,
-                    130
+                    395,
+                    73,
+                    128
                 ],
                 [
-                    417,
-                    65,
-                    156
+                    392,
+                    73,
+                    129
                 ],
                 [
-                    407,
-                    65,
-                    174
+                    388,
+                    73,
+                    131
                 ],
                 [
                     385,
-                    70,
-                    194
+                    73,
+                    162
                 ],
                 [
-                    377,
+                    375,
                     73,
-                    214
+                    162
                 ],
                 [
-                    361,
-                    83,
-                    238
+                    370,
+                    75,
+                    163
                 ],
                 [
-                    355,
-                    88,
-                    247
+                    360,
+                    78,
+                    195
                 ],
                 [
                     349,
-                    100,
-                    275
+                    85,
+                    196
                 ],
                 [
-                    354,
-                    113,
-                    310
+                    342,
+                    91,
+                    228
                 ],
                 [
-                    360,
-                    117,
-                    338
+                    338,
+                    98,
+                    229
                 ],
                 [
-                    368,
-                    120,
-                    357
+                    337,
+                    107,
+                    261
                 ],
                 [
-                    379,
-                    120,
-                    377
+                    337,
+                    115,
+                    261
                 ],
                 [
-                    408,
-                    110,
-                    412
+                    342,
+                    125,
+                    294
                 ],
                 [
-                    414,
-                    105,
-                    437
+                    347,
+                    133,
+                    295
                 ],
                 [
-                    423,
-                    95,
-                    462
+                    351,
+                    135,
+                    299
                 ],
                 [
-                    424,
-                    91,
-                    484
+                    353,
+                    139,
+                    328
                 ],
                 [
-                    426,
-                    87,
-                    511
+                    359,
+                    143,
+                    329
                 ],
                 [
-                    426,
-                    83,
-                    541
+                    363,
+                    144,
+                    331
                 ],
                 [
-                    427,
-                    81,
+                    365,
+                    145,
+                    361
+                ],
+                [
+                    369,
+                    148,
+                    362
+                ],
+                [
+                    371,
+                    148,
+                    363
+                ],
+                [
+                    372,
+                    148,
+                    395
+                ],
+                [
+                    376,
+                    146,
+                    396
+                ],
+                [
+                    382,
+                    143,
+                    427
+                ],
+                [
+                    387,
+                    137,
+                    428
+                ],
+                [
+                    391,
+                    129,
+                    461
+                ],
+                [
+                    395,
+                    123,
+                    461
+                ],
+                [
+                    400,
+                    118,
+                    494
+                ],
+                [
+                    401,
+                    113,
+                    494
+                ],
+                [
+                    402,
+                    108,
+                    527
+                ],
+                [
+                    404,
+                    103,
+                    528
+                ],
+                [
+                    406,
+                    99,
+                    531
+                ],
+                [
+                    406,
+                    96,
+                    561
+                ],
+                [
+                    408,
+                    90,
+                    561
+                ],
+                [
+                    408,
+                    88,
                     564
                 ],
                 [
-                    426,
-                    80,
-                    591
+                    408,
+                    86,
+                    593
                 ],
                 [
-                    427,
-                    79,
-                    648
+                    409,
+                    84,
+                    594
                 ],
                 [
-                    427,
+                    409,
+                    83,
+                    627
+                ],
+                [
+                    409,
+                    82,
+                    661
+                ],
+                [
+                    409,
                     81,
-                    698
+                    694
                 ],
                 [
-                    427,
+                    409,
                     83,
-                    722
+                    844
                 ],
                 [
-                    428,
-                    90,
-                    741
+                    409,
+                    89,
+                    878
                 ],
                 [
-                    430,
+                    409,
+                    93,
+                    879
+                ],
+                [
+                    409,
                     98,
-                    760
+                    884
                 ],
                 [
-                    432,
-                    118,
-                    788
+                    408,
+                    103,
+                    910
                 ],
                 [
-                    434,
-                    129,
-                    797
+                    407,
+                    118,
+                    911
                 ],
                 [
-                    436,
-                    151,
-                    829
+                    407,
+                    135,
+                    943
                 ],
                 [
-                    436,
-                    160,
-                    844
+                    407,
+                    157,
+                    944
                 ],
                 [
-                    437,
+                    407,
                     166,
-                    863
+                    948
                 ],
                 [
-                    437,
-                    171,
-                    899
+                    409,
+                    175,
+                    976
                 ],
                 [
-                    437,
-                    173,
-                    921
+                    410,
+                    186,
+                    977
                 ],
                 [
-                    437,
-                    176,
-                    938
+                    410,
+                    191,
+                    979
                 ],
                 [
-                    437,
-                    177,
-                    964
+                    411,
+                    196,
+                    1009
                 ],
                 [
-                    437,
-                    178,
-                    985
+                    411,
+                    203,
+                    1010
                 ],
                 [
-                    437,
-                    179,
-                    1013
+                    413,
+                    207,
+                    1011
                 ],
                 [
-                    437,
-                    180,
-                    1032
+                    413,
+                    211,
+                    1042
                 ],
                 [
-                    437,
-                    181,
-                    1101
+                    413,
+                    223,
+                    1043
                 ],
                 [
-                    438,
-                    181,
-                    1136
+                    413,
+                    230,
+                    1076
+                ],
+                [
+                    413,
+                    234,
+                    1077
+                ],
+                [
+                    413,
+                    235,
+                    1109
+                ],
+                [
+                    413,
+                    236,
+                    1110
                 ]
             ]
         ],
         ".POINTS_PER_SECOND": 60,
         ".SEGMENT": "CHARACTER",
         ".VERSION": "1.0",
-        ".X_DIM": 821,
-        ".X_POINTS_PER_INCH": 100,
-        ".Y_DIM": 211,
-        ".Y_POINTS_PER_INCH": 100
+        ".X_DIM": 1032,
+        ".X_POINTS_PER_INCH": 95,
+        ".Y_DIM": 263,
+        ".Y_POINTS_PER_INCH": 95
     },
     "0x0072": {
         ".COORD": [
diff --git a/tests/auto/inputpanel/data/inputpanel/unipen_data_simp_chinese.js b/tests/auto/inputpanel/data/inputpanel/unipen_data_simp_chinese.js
new file mode 100644
index 0000000000000000000000000000000000000000..b5942e6b325f8da5c8b91ad391ea8b5abe1890b0
--- /dev/null
+++ b/tests/auto/inputpanel/data/inputpanel/unipen_data_simp_chinese.js
@@ -0,0 +1,1851 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+var unipenData = {
+    "0x4e2d": {
+        ".COORD": [
+            "X",
+            "Y",
+            "T"
+        ],
+        ".HIERARCHY": "CHARACTER",
+        ".PEN": [
+            [
+                [
+                    185,
+                    59,
+                    0
+                ],
+                [
+                    185,
+                    61,
+                    181
+                ],
+                [
+                    185,
+                    63,
+                    202
+                ],
+                [
+                    185,
+                    65,
+                    205
+                ],
+                [
+                    185,
+                    68,
+                    235
+                ],
+                [
+                    185,
+                    70,
+                    236
+                ],
+                [
+                    185,
+                    73,
+                    236
+                ],
+                [
+                    185,
+                    75,
+                    268
+                ],
+                [
+                    185,
+                    80,
+                    269
+                ],
+                [
+                    185,
+                    84,
+                    301
+                ],
+                [
+                    185,
+                    87,
+                    302
+                ],
+                [
+                    185,
+                    89,
+                    334
+                ],
+                [
+                    185,
+                    92,
+                    335
+                ],
+                [
+                    185,
+                    94,
+                    368
+                ],
+                [
+                    184,
+                    97,
+                    369
+                ],
+                [
+                    184,
+                    98,
+                    372
+                ],
+                [
+                    184,
+                    99,
+                    401
+                ],
+                [
+                    184,
+                    101,
+                    402
+                ],
+                [
+                    184,
+                    102,
+                    405
+                ],
+                [
+                    184,
+                    104,
+                    434
+                ],
+                [
+                    184,
+                    105,
+                    467
+                ],
+                [
+                    184,
+                    106,
+                    501
+                ],
+                [
+                    184,
+                    107,
+                    534
+                ],
+                [
+                    184,
+                    108,
+                    535
+                ],
+                [
+                    184,
+                    109,
+                    567
+                ],
+                [
+                    184,
+                    111,
+                    567
+                ],
+                [
+                    184,
+                    112,
+                    604
+                ],
+                [
+                    184,
+                    113,
+                    634
+                ],
+                [
+                    184,
+                    114,
+                    667
+                ],
+                [
+                    184,
+                    115,
+                    700
+                ],
+                [
+                    184,
+                    116,
+                    734
+                ],
+                [
+                    184,
+                    117,
+                    767
+                ],
+                [
+                    184,
+                    118,
+                    801
+                ],
+                [
+                    184,
+                    119,
+                    805
+                ],
+                [
+                    184,
+                    120,
+                    833
+                ],
+                [
+                    184,
+                    121,
+                    837
+                ],
+                [
+                    184,
+                    123,
+                    866
+                ],
+                [
+                    184,
+                    124,
+                    902
+                ],
+                [
+                    184,
+                    125,
+                    937
+                ],
+                [
+                    184,
+                    126,
+                    997
+                ],
+                [
+                    184,
+                    127,
+                    1040
+                ],
+                [
+                    184,
+                    128,
+                    1149
+                ]
+            ],
+            [
+                [
+                    184,
+                    63,
+                    1871
+                ],
+                [
+                    185,
+                    63,
+                    1917
+                ],
+                [
+                    186,
+                    63,
+                    1918
+                ],
+                [
+                    188,
+                    63,
+                    1947
+                ],
+                [
+                    190,
+                    62,
+                    1948
+                ],
+                [
+                    191,
+                    62,
+                    1948
+                ],
+                [
+                    196,
+                    62,
+                    1980
+                ],
+                [
+                    202,
+                    62,
+                    1981
+                ],
+                [
+                    209,
+                    61,
+                    2014
+                ],
+                [
+                    217,
+                    61,
+                    2015
+                ],
+                [
+                    228,
+                    61,
+                    2047
+                ],
+                [
+                    241,
+                    61,
+                    2048
+                ],
+                [
+                    248,
+                    61,
+                    2052
+                ],
+                [
+                    253,
+                    61,
+                    2080
+                ],
+                [
+                    264,
+                    61,
+                    2081
+                ],
+                [
+                    267,
+                    61,
+                    2084
+                ],
+                [
+                    271,
+                    61,
+                    2114
+                ],
+                [
+                    277,
+                    61,
+                    2114
+                ],
+                [
+                    279,
+                    61,
+                    2117
+                ],
+                [
+                    283,
+                    61,
+                    2147
+                ],
+                [
+                    288,
+                    61,
+                    2148
+                ],
+                [
+                    290,
+                    61,
+                    2149
+                ],
+                [
+                    292,
+                    61,
+                    2180
+                ],
+                [
+                    296,
+                    61,
+                    2181
+                ],
+                [
+                    298,
+                    61,
+                    2213
+                ],
+                [
+                    299,
+                    61,
+                    2214
+                ],
+                [
+                    301,
+                    60,
+                    2246
+                ],
+                [
+                    302,
+                    60,
+                    2437
+                ],
+                [
+                    303,
+                    60,
+                    2485
+                ],
+                [
+                    304,
+                    60,
+                    2532
+                ],
+                [
+                    305,
+                    60,
+                    2564
+                ],
+                [
+                    305,
+                    61,
+                    2596
+                ],
+                [
+                    305,
+                    62,
+                    2629
+                ],
+                [
+                    305,
+                    64,
+                    2630
+                ],
+                [
+                    305,
+                    66,
+                    2662
+                ],
+                [
+                    305,
+                    67,
+                    2663
+                ],
+                [
+                    306,
+                    69,
+                    2695
+                ],
+                [
+                    306,
+                    71,
+                    2696
+                ],
+                [
+                    306,
+                    75,
+                    2729
+                ],
+                [
+                    306,
+                    78,
+                    2730
+                ],
+                [
+                    306,
+                    79,
+                    2732
+                ],
+                [
+                    306,
+                    81,
+                    2762
+                ],
+                [
+                    306,
+                    85,
+                    2763
+                ],
+                [
+                    306,
+                    87,
+                    2764
+                ],
+                [
+                    306,
+                    89,
+                    2796
+                ],
+                [
+                    306,
+                    93,
+                    2796
+                ],
+                [
+                    306,
+                    95,
+                    2796
+                ],
+                [
+                    306,
+                    101,
+                    2828
+                ],
+                [
+                    305,
+                    105,
+                    2829
+                ],
+                [
+                    305,
+                    109,
+                    2862
+                ],
+                [
+                    305,
+                    113,
+                    2863
+                ],
+                [
+                    304,
+                    119,
+                    2896
+                ],
+                [
+                    304,
+                    122,
+                    2896
+                ],
+                [
+                    304,
+                    123,
+                    2901
+                ],
+                [
+                    303,
+                    126,
+                    2929
+                ],
+                [
+                    303,
+                    128,
+                    2929
+                ],
+                [
+                    303,
+                    130,
+                    2962
+                ],
+                [
+                    303,
+                    131,
+                    2962
+                ],
+                [
+                    303,
+                    132,
+                    2995
+                ],
+                [
+                    303,
+                    133,
+                    3029
+                ]
+            ],
+            [
+                [
+                    184,
+                    129,
+                    4367
+                ],
+                [
+                    188,
+                    129,
+                    4409
+                ],
+                [
+                    190,
+                    129,
+                    4410
+                ],
+                [
+                    191,
+                    129,
+                    4413
+                ],
+                [
+                    193,
+                    129,
+                    4441
+                ],
+                [
+                    197,
+                    128,
+                    4442
+                ],
+                [
+                    198,
+                    128,
+                    4444
+                ],
+                [
+                    200,
+                    128,
+                    4474
+                ],
+                [
+                    203,
+                    128,
+                    4475
+                ],
+                [
+                    205,
+                    127,
+                    4476
+                ],
+                [
+                    209,
+                    127,
+                    4508
+                ],
+                [
+                    219,
+                    127,
+                    4509
+                ],
+                [
+                    227,
+                    127,
+                    4541
+                ],
+                [
+                    237,
+                    127,
+                    4542
+                ],
+                [
+                    244,
+                    127,
+                    4574
+                ],
+                [
+                    246,
+                    127,
+                    4575
+                ],
+                [
+                    248,
+                    127,
+                    4607
+                ],
+                [
+                    250,
+                    127,
+                    4608
+                ],
+                [
+                    252,
+                    126,
+                    4640
+                ],
+                [
+                    255,
+                    126,
+                    4641
+                ],
+                [
+                    257,
+                    126,
+                    4644
+                ],
+                [
+                    259,
+                    125,
+                    4674
+                ],
+                [
+                    261,
+                    125,
+                    4675
+                ],
+                [
+                    263,
+                    125,
+                    4677
+                ],
+                [
+                    265,
+                    125,
+                    4707
+                ],
+                [
+                    266,
+                    125,
+                    4740
+                ],
+                [
+                    269,
+                    125,
+                    4741
+                ],
+                [
+                    270,
+                    125,
+                    4774
+                ],
+                [
+                    275,
+                    125,
+                    4775
+                ],
+                [
+                    279,
+                    125,
+                    4807
+                ],
+                [
+                    283,
+                    125,
+                    4808
+                ],
+                [
+                    287,
+                    125,
+                    4840
+                ],
+                [
+                    289,
+                    125,
+                    4841
+                ],
+                [
+                    291,
+                    125,
+                    4873
+                ],
+                [
+                    292,
+                    125,
+                    4874
+                ],
+                [
+                    293,
+                    125,
+                    4876
+                ],
+                [
+                    294,
+                    125,
+                    4907
+                ],
+                [
+                    296,
+                    125,
+                    4908
+                ],
+                [
+                    298,
+                    125,
+                    4909
+                ],
+                [
+                    299,
+                    125,
+                    4940
+                ],
+                [
+                    302,
+                    125,
+                    4941
+                ],
+                [
+                    303,
+                    126,
+                    4974
+                ],
+                [
+                    304,
+                    126,
+                    5006
+                ],
+                [
+                    305,
+                    126,
+                    5007
+                ],
+                [
+                    306,
+                    126,
+                    5040
+                ]
+            ],
+            [
+                [
+                    244,
+                    32,
+                    6303
+                ],
+                [
+                    244,
+                    34,
+                    6340
+                ],
+                [
+                    244,
+                    35,
+                    6355
+                ],
+                [
+                    244,
+                    37,
+                    6356
+                ],
+                [
+                    244,
+                    40,
+                    6386
+                ],
+                [
+                    244,
+                    46,
+                    6387
+                ],
+                [
+                    244,
+                    50,
+                    6388
+                ],
+                [
+                    244,
+                    54,
+                    6419
+                ],
+                [
+                    245,
+                    67,
+                    6420
+                ],
+                [
+                    247,
+                    75,
+                    6453
+                ],
+                [
+                    248,
+                    84,
+                    6453
+                ],
+                [
+                    249,
+                    89,
+                    6486
+                ],
+                [
+                    249,
+                    96,
+                    6487
+                ],
+                [
+                    250,
+                    102,
+                    6519
+                ],
+                [
+                    250,
+                    107,
+                    6520
+                ],
+                [
+                    251,
+                    111,
+                    6537
+                ],
+                [
+                    251,
+                    113,
+                    6553
+                ],
+                [
+                    251,
+                    118,
+                    6553
+                ],
+                [
+                    251,
+                    120,
+                    6557
+                ],
+                [
+                    251,
+                    122,
+                    6586
+                ],
+                [
+                    251,
+                    126,
+                    6587
+                ],
+                [
+                    251,
+                    128,
+                    6588
+                ],
+                [
+                    251,
+                    130,
+                    6619
+                ],
+                [
+                    252,
+                    133,
+                    6620
+                ],
+                [
+                    252,
+                    135,
+                    6620
+                ],
+                [
+                    252,
+                    138,
+                    6652
+                ],
+                [
+                    252,
+                    141,
+                    6653
+                ],
+                [
+                    252,
+                    144,
+                    6686
+                ],
+                [
+                    252,
+                    147,
+                    6686
+                ],
+                [
+                    252,
+                    151,
+                    6719
+                ],
+                [
+                    252,
+                    154,
+                    6719
+                ],
+                [
+                    252,
+                    156,
+                    6724
+                ],
+                [
+                    252,
+                    157,
+                    6752
+                ],
+                [
+                    252,
+                    161,
+                    6753
+                ],
+                [
+                    252,
+                    162,
+                    6756
+                ],
+                [
+                    252,
+                    165,
+                    6786
+                ],
+                [
+                    252,
+                    167,
+                    6788
+                ],
+                [
+                    252,
+                    170,
+                    6819
+                ],
+                [
+                    252,
+                    172,
+                    6820
+                ],
+                [
+                    252,
+                    174,
+                    6852
+                ],
+                [
+                    252,
+                    176,
+                    6852
+                ],
+                [
+                    252,
+                    178,
+                    6885
+                ],
+                [
+                    252,
+                    180,
+                    6886
+                ],
+                [
+                    252,
+                    182,
+                    6918
+                ],
+                [
+                    252,
+                    183,
+                    6919
+                ],
+                [
+                    252,
+                    184,
+                    6935
+                ],
+                [
+                    251,
+                    185,
+                    6952
+                ],
+                [
+                    251,
+                    186,
+                    6956
+                ],
+                [
+                    251,
+                    187,
+                    6985
+                ],
+                [
+                    251,
+                    188,
+                    7018
+                ],
+                [
+                    251,
+                    189,
+                    7205
+                ],
+                [
+                    251,
+                    190,
+                    7293
+                ]
+            ]
+        ],
+        ".POINTS_PER_SECOND": 60,
+        ".SEGMENT": "CHARACTER",
+        ".VERSION": "1.0",
+        ".X_DIM": 1032,
+        ".X_POINTS_PER_INCH": 95,
+        ".Y_DIM": 263,
+        ".Y_POINTS_PER_INCH": 95
+    },
+    "0x6587": {
+        ".COORD": [
+            "X",
+            "Y",
+            "T"
+        ],
+        ".HIERARCHY": "CHARACTER",
+        ".PEN": [
+            [
+                [
+                    415,
+                    26,
+                    0
+                ],
+                [
+                    416,
+                    28,
+                    46
+                ],
+                [
+                    417,
+                    28,
+                    63
+                ],
+                [
+                    418,
+                    30,
+                    96
+                ],
+                [
+                    419,
+                    31,
+                    97
+                ],
+                [
+                    420,
+                    31,
+                    100
+                ],
+                [
+                    422,
+                    34,
+                    130
+                ],
+                [
+                    424,
+                    36,
+                    163
+                ],
+                [
+                    425,
+                    38,
+                    163
+                ],
+                [
+                    426,
+                    39,
+                    164
+                ],
+                [
+                    427,
+                    42,
+                    196
+                ],
+                [
+                    428,
+                    45,
+                    196
+                ],
+                [
+                    429,
+                    47,
+                    229
+                ],
+                [
+                    430,
+                    50,
+                    230
+                ],
+                [
+                    431,
+                    52,
+                    263
+                ],
+                [
+                    431,
+                    54,
+                    263
+                ],
+                [
+                    432,
+                    55,
+                    296
+                ],
+                [
+                    433,
+                    56,
+                    296
+                ],
+                [
+                    433,
+                    57,
+                    300
+                ],
+                [
+                    433,
+                    58,
+                    330
+                ],
+                [
+                    433,
+                    59,
+                    330
+                ],
+                [
+                    433,
+                    60,
+                    364
+                ],
+                [
+                    433,
+                    61,
+                    365
+                ],
+                [
+                    433,
+                    62,
+                    404
+                ]
+            ],
+            [
+                [
+                    356,
+                    80,
+                    1935
+                ],
+                [
+                    357,
+                    80,
+                    1980
+                ],
+                [
+                    358,
+                    81,
+                    1981
+                ],
+                [
+                    359,
+                    81,
+                    2008
+                ],
+                [
+                    361,
+                    81,
+                    2012
+                ],
+                [
+                    363,
+                    81,
+                    2041
+                ],
+                [
+                    365,
+                    81,
+                    2045
+                ],
+                [
+                    367,
+                    81,
+                    2075
+                ],
+                [
+                    370,
+                    81,
+                    2075
+                ],
+                [
+                    372,
+                    81,
+                    2076
+                ],
+                [
+                    375,
+                    81,
+                    2108
+                ],
+                [
+                    379,
+                    81,
+                    2109
+                ],
+                [
+                    385,
+                    80,
+                    2141
+                ],
+                [
+                    390,
+                    80,
+                    2142
+                ],
+                [
+                    396,
+                    79,
+                    2174
+                ],
+                [
+                    403,
+                    79,
+                    2175
+                ],
+                [
+                    406,
+                    79,
+                    2180
+                ],
+                [
+                    410,
+                    77,
+                    2208
+                ],
+                [
+                    416,
+                    77,
+                    2209
+                ],
+                [
+                    420,
+                    76,
+                    2213
+                ],
+                [
+                    422,
+                    76,
+                    2241
+                ],
+                [
+                    428,
+                    75,
+                    2242
+                ],
+                [
+                    431,
+                    75,
+                    2245
+                ],
+                [
+                    434,
+                    74,
+                    2274
+                ],
+                [
+                    443,
+                    73,
+                    2275
+                ],
+                [
+                    446,
+                    73,
+                    2276
+                ],
+                [
+                    451,
+                    71,
+                    2307
+                ],
+                [
+                    460,
+                    70,
+                    2308
+                ],
+                [
+                    464,
+                    68,
+                    2341
+                ],
+                [
+                    468,
+                    67,
+                    2341
+                ],
+                [
+                    470,
+                    66,
+                    2374
+                ],
+                [
+                    475,
+                    64,
+                    2374
+                ],
+                [
+                    477,
+                    64,
+                    2392
+                ],
+                [
+                    479,
+                    63,
+                    2407
+                ],
+                [
+                    484,
+                    62,
+                    2408
+                ],
+                [
+                    485,
+                    61,
+                    2440
+                ],
+                [
+                    487,
+                    60,
+                    2441
+                ],
+                [
+                    489,
+                    59,
+                    2444
+                ],
+                [
+                    491,
+                    57,
+                    2474
+                ],
+                [
+                    493,
+                    56,
+                    2508
+                ],
+                [
+                    495,
+                    55,
+                    2508
+                ],
+                [
+                    496,
+                    54,
+                    2508
+                ],
+                [
+                    498,
+                    53,
+                    2540
+                ],
+                [
+                    499,
+                    52,
+                    2541
+                ],
+                [
+                    500,
+                    50,
+                    2573
+                ],
+                [
+                    501,
+                    49,
+                    2607
+                ],
+                [
+                    502,
+                    48,
+                    2640
+                ],
+                [
+                    503,
+                    47,
+                    2641
+                ],
+                [
+                    504,
+                    46,
+                    2673
+                ],
+                [
+                    504,
+                    45,
+                    2724
+                ],
+                [
+                    504,
+                    44,
+                    3053
+                ]
+            ],
+            [
+                [
+                    434,
+                    82,
+                    4671
+                ],
+                [
+                    434,
+                    84,
+                    4718
+                ],
+                [
+                    434,
+                    86,
+                    4735
+                ],
+                [
+                    434,
+                    88,
+                    4740
+                ],
+                [
+                    434,
+                    89,
+                    4768
+                ],
+                [
+                    434,
+                    92,
+                    4769
+                ],
+                [
+                    433,
+                    94,
+                    4772
+                ],
+                [
+                    432,
+                    96,
+                    4802
+                ],
+                [
+                    430,
+                    101,
+                    4802
+                ],
+                [
+                    429,
+                    103,
+                    4804
+                ],
+                [
+                    429,
+                    106,
+                    4835
+                ],
+                [
+                    428,
+                    112,
+                    4836
+                ],
+                [
+                    428,
+                    114,
+                    4836
+                ],
+                [
+                    425,
+                    121,
+                    4868
+                ],
+                [
+                    423,
+                    127,
+                    4869
+                ],
+                [
+                    421,
+                    132,
+                    4901
+                ],
+                [
+                    417,
+                    137,
+                    4902
+                ],
+                [
+                    414,
+                    143,
+                    4934
+                ],
+                [
+                    412,
+                    148,
+                    4935
+                ],
+                [
+                    406,
+                    156,
+                    4968
+                ],
+                [
+                    401,
+                    163,
+                    4969
+                ],
+                [
+                    398,
+                    166,
+                    4972
+                ],
+                [
+                    397,
+                    169,
+                    5001
+                ],
+                [
+                    393,
+                    173,
+                    5002
+                ],
+                [
+                    392,
+                    175,
+                    5005
+                ],
+                [
+                    391,
+                    176,
+                    5034
+                ],
+                [
+                    390,
+                    178,
+                    5035
+                ],
+                [
+                    389,
+                    179,
+                    5036
+                ],
+                [
+                    388,
+                    180,
+                    5067
+                ],
+                [
+                    387,
+                    181,
+                    5068
+                ],
+                [
+                    386,
+                    182,
+                    5101
+                ],
+                [
+                    385,
+                    182,
+                    5589
+                ],
+                [
+                    384,
+                    182,
+                    5616
+                ]
+            ],
+            [
+                [
+                    385,
+                    84,
+                    6399
+                ],
+                [
+                    386,
+                    84,
+                    6468
+                ],
+                [
+                    387,
+                    87,
+                    6489
+                ],
+                [
+                    388,
+                    90,
+                    6531
+                ],
+                [
+                    391,
+                    95,
+                    6531
+                ],
+                [
+                    392,
+                    97,
+                    6532
+                ],
+                [
+                    394,
+                    99,
+                    6564
+                ],
+                [
+                    396,
+                    106,
+                    6564
+                ],
+                [
+                    401,
+                    113,
+                    6597
+                ],
+                [
+                    406,
+                    120,
+                    6598
+                ],
+                [
+                    411,
+                    126,
+                    6630
+                ],
+                [
+                    416,
+                    132,
+                    6631
+                ],
+                [
+                    422,
+                    138,
+                    6663
+                ],
+                [
+                    426,
+                    143,
+                    6664
+                ],
+                [
+                    427,
+                    143,
+                    6668
+                ],
+                [
+                    429,
+                    145,
+                    6697
+                ],
+                [
+                    432,
+                    150,
+                    6697
+                ],
+                [
+                    433,
+                    153,
+                    6700
+                ],
+                [
+                    436,
+                    156,
+                    6730
+                ],
+                [
+                    440,
+                    160,
+                    6730
+                ],
+                [
+                    444,
+                    163,
+                    6732
+                ],
+                [
+                    447,
+                    165,
+                    6763
+                ],
+                [
+                    454,
+                    171,
+                    6764
+                ],
+                [
+                    457,
+                    173,
+                    6797
+                ],
+                [
+                    458,
+                    173,
+                    6798
+                ],
+                [
+                    459,
+                    174,
+                    6830
+                ],
+                [
+                    460,
+                    175,
+                    6831
+                ],
+                [
+                    462,
+                    176,
+                    6863
+                ],
+                [
+                    466,
+                    178,
+                    6864
+                ],
+                [
+                    471,
+                    179,
+                    6897
+                ],
+                [
+                    473,
+                    179,
+                    6897
+                ],
+                [
+                    474,
+                    180,
+                    6900
+                ],
+                [
+                    475,
+                    180,
+                    6930
+                ],
+                [
+                    477,
+                    180,
+                    6964
+                ],
+                [
+                    478,
+                    180,
+                    6996
+                ],
+                [
+                    479,
+                    180,
+                    7029
+                ],
+                [
+                    480,
+                    180,
+                    7064
+                ],
+                [
+                    481,
+                    181,
+                    7097
+                ],
+                [
+                    483,
+                    181,
+                    7100
+                ],
+                [
+                    485,
+                    181,
+                    7149
+                ],
+                [
+                    486,
+                    181,
+                    7180
+                ],
+                [
+                    487,
+                    181,
+                    7213
+                ],
+                [
+                    488,
+                    181,
+                    7304
+                ],
+                [
+                    489,
+                    181,
+                    7346
+                ],
+                [
+                    490,
+                    181,
+                    7396
+                ],
+                [
+                    491,
+                    181,
+                    7428
+                ],
+                [
+                    491,
+                    182,
+                    7629
+                ]
+            ]
+        ],
+        ".POINTS_PER_SECOND": 60,
+        ".SEGMENT": "CHARACTER",
+        ".VERSION": "1.0",
+        ".X_DIM": 1032,
+        ".X_POINTS_PER_INCH": 95,
+        ".Y_DIM": 263,
+        ".Y_POINTS_PER_INCH": 95
+    }
+}
diff --git a/tests/auto/inputpanel/data/tst_inputpanel.qml b/tests/auto/inputpanel/data/tst_inputpanel.qml
index 7d83d31dfe510ceab5b12d2160f3107a47761daf..95cbe5d16a90544094f6bacf29a0ce212687cdb6 100644
--- a/tests/auto/inputpanel/data/tst_inputpanel.qml
+++ b/tests/auto/inputpanel/data/tst_inputpanel.qml
@@ -105,6 +105,11 @@ Rectangle {
             verify(inputPanel.setLocale(locale))
             if (localeChanged && !(textInput.inputMethodHints & Qt.ImhNoPredictiveText))
                 wait(300)
+            if (data !== undefined && data.hasOwnProperty("initHwrMode") && data.initHwrMode) {
+                if (!inputPanel.setHandwritingMode(true))
+                    expectFail("", "Handwriting not enabled")
+                verify(inputPanel.handwritingMode === true)
+            }
             if (data !== undefined && data.hasOwnProperty("initInputMode")) {
                 var inputMode = inputPanel.mapInputMode(data.initInputMode)
                 if (!inputPanel.isInputModeSupported(inputMode))
@@ -1093,19 +1098,16 @@ Rectangle {
 
         function test_hwrInputSequence_data() {
             return [
-                { initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 0, inputSequence: "abcdefghij", outputText: "Abcdefghij" },
-                { initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 1, inputSequence: "klmnopqrst", outputText: "klmnopqrst" },
-                { initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 3, inputSequence: "uvwxyz", outputText: "UVWXYZ" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 0, inputSequence: "abcdefghij", outputText: "Abcdefghij" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 1, inputSequence: "klmnopqrst", outputText: "klmnopqrst" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 3, inputSequence: "uvwxyz", outputText: "UVWXYZ" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNone, initLocale: "zh_CN", initInputMode: "ChineseHandwriting", inputSequence: "\u4e2d\u6587", outputText: "\u4e2d\u6587" },
             ]
         }
 
         function test_hwrInputSequence(data) {
             prepareTest(data)
 
-            if (!inputPanel.setHandwritingMode(true))
-                expectFail("", "Handwriting not enabled")
-            verify(inputPanel.handwritingMode === true)
-
             for (var i = 0; i < data.toggleShiftCount; i++) {
                 inputPanel.toggleShift()
             }
@@ -1126,20 +1128,20 @@ Rectangle {
 
         function test_hwrNumericInputSequence_data() {
             return [
-                { initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhPreferNumbers, modeSwitchAllowed: true, inputSequence: "0123456789", outputText: "0123456789" },
-                { initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDigitsOnly, modeSwitchAllowed: false, inputSequence: "1234567890", outputText: "1234567890" },
-                { initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhFormattedNumbersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
-                { initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDialableCharactersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhPreferNumbers, modeSwitchAllowed: true, inputSequence: "0123456789", outputText: "0123456789" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDigitsOnly, modeSwitchAllowed: false, inputSequence: "1234567890", outputText: "1234567890" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhFormattedNumbersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDialableCharactersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
+                { initHwrMode: true, initLocale: "zh_CN", initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhPreferNumbers, modeSwitchAllowed: true, inputSequence: "0123456789", outputText: "0123456789" },
+                { initHwrMode: true, initLocale: "zh_CN", initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDigitsOnly, modeSwitchAllowed: false, inputSequence: "1234567890", outputText: "1234567890" },
+                { initHwrMode: true, initLocale: "zh_CN", initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhFormattedNumbersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
+                { initHwrMode: true, initLocale: "zh_CN", initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDialableCharactersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
             ]
         }
 
         function test_hwrNumericInputSequence(data) {
             prepareTest(data)
 
-            if (!inputPanel.setHandwritingMode(true))
-                expectFail("", "Handwriting not enabled")
-            verify(inputPanel.handwritingMode === true)
-
             for (var inputIndex in data.inputSequence) {
                 verify(inputPanel.emulateHandwriting(data.inputSequence.charAt(inputIndex), true))
             }
@@ -1265,6 +1267,7 @@ Rectangle {
         function test_hwrFullScreenGestures_data() {
             return [
                 { initInputMethodHints: Qt.ImhNoPredictiveText, inputSequence: ["a","b","c",Qt.Key_Backspace,Qt.Key_Space,"c"], outputText: "Ab c" },
+                { initHwrMode: true, initInputMethodHints: Qt.ImhNone, initLocale: "zh_CN", initInputMode: "ChineseHandwriting", inputSequence: ["\u4e2d", "\u6587", Qt.Key_Backspace], outputText: "\u4e2d" },
             ]
         }
 
@@ -1298,7 +1301,8 @@ Rectangle {
             prepareTest(data)
 
             if (!handwritingInputPanel.enabled)
-                skip("Handwriting not enabled")
+                expectFail("", "Handwriting not enabled")
+            verify(handwritingInputPanel.enabled)
             handwritingInputPanel.available = true
             if (!inputPanel.wordCandidateListVisibleHint)
                 skip("Word candidates not available (spell correction/hwr suggestions)")
diff --git a/tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt b/tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt
index 714ad4c8cfcc1cf0e27823a22105b1288821f3a0..14d57b7766feb0bd95b13586f826dbde7bc43ba3 100644
--- a/tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt
+++ b/tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt
@@ -2,55 +2,75 @@
 .HIERARCHY CHARACTER
 .COORD X Y T
 .SEGMENT CHARACTER
-.X_DIM 821
-.Y_DIM 211
-.X_POINTS_PER_INCH 100
-.Y_POINTS_PER_INCH 100
+.X_DIM 1032
+.Y_DIM 263
+.X_POINTS_PER_INCH 95
+.Y_POINTS_PER_INCH 95
 .POINTS_PER_SECOND 60
 .PEN_DOWN
-435 78 0
-435 77 13
-435 76 33
-435 75 70
-434 74 84
-434 73 109
-431 70 130
-417 65 156
-407 65 174
-385 70 194
-377 73 214
-361 83 238
-355 88 247
-349 100 275
-354 113 310
-360 117 338
-368 120 357
-379 120 377
-408 110 412
-414 105 437
-423 95 462
-424 91 484
-426 87 511
-426 83 541
-427 81 564
-426 80 591
-427 79 648
-427 81 698
-427 83 722
-428 90 741
-430 98 760
-432 118 788
-434 129 797
-436 151 829
-436 160 844
-437 166 863
-437 171 899
-437 173 921
-437 176 938
-437 177 964
-437 178 985
-437 179 1013
-437 180 1032
-437 181 1101
-438 181 1136
+407 75 0
+406 74 76
+403 74 96
+401 73 100
+395 73 128
+392 73 129
+388 73 131
+385 73 162
+375 73 162
+370 75 163
+360 78 195
+349 85 196
+342 91 228
+338 98 229
+337 107 261
+337 115 261
+342 125 294
+347 133 295
+351 135 299
+353 139 328
+359 143 329
+363 144 331
+365 145 361
+369 148 362
+371 148 363
+372 148 395
+376 146 396
+382 143 427
+387 137 428
+391 129 461
+395 123 461
+400 118 494
+401 113 494
+402 108 527
+404 103 528
+406 99 531
+406 96 561
+408 90 561
+408 88 564
+408 86 593
+409 84 594
+409 83 627
+409 82 661
+409 81 694
+409 83 844
+409 89 878
+409 93 879
+409 98 884
+408 103 910
+407 118 911
+407 135 943
+407 157 944
+407 166 948
+409 175 976
+410 186 977
+410 191 979
+411 196 1009
+411 203 1010
+413 207 1011
+413 211 1042
+413 223 1043
+413 230 1076
+413 234 1077
+413 235 1109
+413 236 1110
 .PEN_UP
diff --git a/tests/auto/inputpanel/hwr_test_data/build_unipen_data.py b/tests/auto/inputpanel/hwr_test_data/build_unipen_data.py
index 639912b9151f87ffcf77829470b792e8343adbdf..eb4231c3d48f015712204e6a5842efda9cefe8fb 100755
--- a/tests/auto/inputpanel/hwr_test_data/build_unipen_data.py
+++ b/tests/auto/inputpanel/hwr_test_data/build_unipen_data.py
@@ -2,19 +2,30 @@
 
 #############################################################################
 ##
-## Copyright (C) 2015 The Qt Company Ltd
-## All rights reserved.
-## For any questions to The Qt Company, please use contact form at http://qt.io
+## Copyright (C) 2017 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
 ##
-## This file is part of the Qt Virtual Keyboard add-on.
+## This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
 ##
-## Licensees holding valid commercial license for Qt may use this file in
-## accordance with the Qt License Agreement provided with the Software
-## or, alternatively, in accordance with the terms contained in a written
-## agreement between you and The Qt Company.
+## $QT_BEGIN_LICENSE:GPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
 ##
-## If you have questions regarding the use of this file, please use
-## contact form at http://qt.io
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 3 or (at your option) any later version
+## approved by the KDE Free Qt Foundation. The licenses are as published by
+## the Free Software Foundation and appearing in the file LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
 ##
 #############################################################################
 
@@ -28,23 +39,34 @@ import re
 unipen_file_pattern = re.compile(r'(^[0-9]{2,9}).*\.txt')
 
 def print_header():
-    print """/******************************************************************************
+    print """/****************************************************************************
+**
+** Copyright (C) %s The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
 **
-** Copyright (C) %s The Qt Company Ltd
-** All rights reserved.
-** For any questions to The Qt Company, please use contact form at http://qt.io
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
 **
-** This file is part of the Qt Virtual Keyboard add-on.
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
 **
-** Licensees holding valid commercial license for Qt may use this file in
-** accordance with the Qt License Agreement provided with the Software
-** or, alternatively, in accordance with the terms contained in a written
-** agreement between you and The Qt Company.
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
 **
-** If you have questions regarding the use of this file, please use
-** contact form at http://qt.io
+** $QT_END_LICENSE$
 **
-******************************************************************************/""" % datetime.datetime.now().year
+****************************************************************************/""" % datetime.datetime.now().year
 
 def scan_unipen_files(path):
     file_list = []
diff --git a/tests/auto/inputpanel/hwr_test_data/simp_chinese/20013_100_0.txt b/tests/auto/inputpanel/hwr_test_data/simp_chinese/20013_100_0.txt
new file mode 100644
index 0000000000000000000000000000000000000000..27414acd4db6b1f2c4e9e9d2b1f5133f766c695b
--- /dev/null
+++ b/tests/auto/inputpanel/hwr_test_data/simp_chinese/20013_100_0.txt
@@ -0,0 +1,216 @@
+.VERSION 1.0
+.HIERARCHY CHARACTER
+.COORD X Y T
+.SEGMENT CHARACTER
+.X_DIM 1032
+.Y_DIM 263
+.X_POINTS_PER_INCH 95
+.Y_POINTS_PER_INCH 95
+.POINTS_PER_SECOND 60
+.PEN_DOWN
+185 59 0
+185 61 181
+185 63 202
+185 65 205
+185 68 235
+185 70 236
+185 73 236
+185 75 268
+185 80 269
+185 84 301
+185 87 302
+185 89 334
+185 92 335
+185 94 368
+184 97 369
+184 98 372
+184 99 401
+184 101 402
+184 102 405
+184 104 434
+184 105 467
+184 106 501
+184 107 534
+184 108 535
+184 109 567
+184 111 567
+184 112 604
+184 113 634
+184 114 667
+184 115 700
+184 116 734
+184 117 767
+184 118 801
+184 119 805
+184 120 833
+184 121 837
+184 123 866
+184 124 902
+184 125 937
+184 126 997
+184 127 1040
+184 128 1149
+.PEN_UP
+.PEN_DOWN
+184 63 1871
+185 63 1917
+186 63 1918
+188 63 1947
+190 62 1948
+191 62 1948
+196 62 1980
+202 62 1981
+209 61 2014
+217 61 2015
+228 61 2047
+241 61 2048
+248 61 2052
+253 61 2080
+264 61 2081
+267 61 2084
+271 61 2114
+277 61 2114
+279 61 2117
+283 61 2147
+288 61 2148
+290 61 2149
+292 61 2180
+296 61 2181
+298 61 2213
+299 61 2214
+301 60 2246
+302 60 2437
+303 60 2485
+304 60 2532
+305 60 2564
+305 61 2596
+305 62 2629
+305 64 2630
+305 66 2662
+305 67 2663
+306 69 2695
+306 71 2696
+306 75 2729
+306 78 2730
+306 79 2732
+306 81 2762
+306 85 2763
+306 87 2764
+306 89 2796
+306 93 2796
+306 95 2796
+306 101 2828
+305 105 2829
+305 109 2862
+305 113 2863
+304 119 2896
+304 122 2896
+304 123 2901
+303 126 2929
+303 128 2929
+303 130 2962
+303 131 2962
+303 132 2995
+303 133 3029
+.PEN_UP
+.PEN_DOWN
+184 129 4367
+188 129 4409
+190 129 4410
+191 129 4413
+193 129 4441
+197 128 4442
+198 128 4444
+200 128 4474
+203 128 4475
+205 127 4476
+209 127 4508
+219 127 4509
+227 127 4541
+237 127 4542
+244 127 4574
+246 127 4575
+248 127 4607
+250 127 4608
+252 126 4640
+255 126 4641
+257 126 4644
+259 125 4674
+261 125 4675
+263 125 4677
+265 125 4707
+266 125 4740
+269 125 4741
+270 125 4774
+275 125 4775
+279 125 4807
+283 125 4808
+287 125 4840
+289 125 4841
+291 125 4873
+292 125 4874
+293 125 4876
+294 125 4907
+296 125 4908
+298 125 4909
+299 125 4940
+302 125 4941
+303 126 4974
+304 126 5006
+305 126 5007
+306 126 5040
+.PEN_UP
+.PEN_DOWN
+244 32 6303
+244 34 6340
+244 35 6355
+244 37 6356
+244 40 6386
+244 46 6387
+244 50 6388
+244 54 6419
+245 67 6420
+247 75 6453
+248 84 6453
+249 89 6486
+249 96 6487
+250 102 6519
+250 107 6520
+251 111 6537
+251 113 6553
+251 118 6553
+251 120 6557
+251 122 6586
+251 126 6587
+251 128 6588
+251 130 6619
+252 133 6620
+252 135 6620
+252 138 6652
+252 141 6653
+252 144 6686
+252 147 6686
+252 151 6719
+252 154 6719
+252 156 6724
+252 157 6752
+252 161 6753
+252 162 6756
+252 165 6786
+252 167 6788
+252 170 6819
+252 172 6820
+252 174 6852
+252 176 6852
+252 178 6885
+252 180 6886
+252 182 6918
+252 183 6919
+252 184 6935
+251 185 6952
+251 186 6956
+251 187 6985
+251 188 7018
+251 189 7205
+251 190 7293
+.PEN_UP
diff --git a/tests/auto/inputpanel/hwr_test_data/simp_chinese/25991_100_0.txt b/tests/auto/inputpanel/hwr_test_data/simp_chinese/25991_100_0.txt
new file mode 100644
index 0000000000000000000000000000000000000000..eec059f59d732b90b6a1194f6f40dc50e2798dbe
--- /dev/null
+++ b/tests/auto/inputpanel/hwr_test_data/simp_chinese/25991_100_0.txt
@@ -0,0 +1,172 @@
+.VERSION 1.0
+.HIERARCHY CHARACTER
+.COORD X Y T
+.SEGMENT CHARACTER
+.X_DIM 1032
+.Y_DIM 263
+.X_POINTS_PER_INCH 95
+.Y_POINTS_PER_INCH 95
+.POINTS_PER_SECOND 60
+.PEN_DOWN
+415 26 0
+416 28 46
+417 28 63
+418 30 96
+419 31 97
+420 31 100
+422 34 130
+424 36 163
+425 38 163
+426 39 164
+427 42 196
+428 45 196
+429 47 229
+430 50 230
+431 52 263
+431 54 263
+432 55 296
+433 56 296
+433 57 300
+433 58 330
+433 59 330
+433 60 364
+433 61 365
+433 62 404
+.PEN_UP
+.PEN_DOWN
+356 80 1935
+357 80 1980
+358 81 1981
+359 81 2008
+361 81 2012
+363 81 2041
+365 81 2045
+367 81 2075
+370 81 2075
+372 81 2076
+375 81 2108
+379 81 2109
+385 80 2141
+390 80 2142
+396 79 2174
+403 79 2175
+406 79 2180
+410 77 2208
+416 77 2209
+420 76 2213
+422 76 2241
+428 75 2242
+431 75 2245
+434 74 2274
+443 73 2275
+446 73 2276
+451 71 2307
+460 70 2308
+464 68 2341
+468 67 2341
+470 66 2374
+475 64 2374
+477 64 2392
+479 63 2407
+484 62 2408
+485 61 2440
+487 60 2441
+489 59 2444
+491 57 2474
+493 56 2508
+495 55 2508
+496 54 2508
+498 53 2540
+499 52 2541
+500 50 2573
+501 49 2607
+502 48 2640
+503 47 2641
+504 46 2673
+504 45 2724
+504 44 3053
+.PEN_UP
+.PEN_DOWN
+434 82 4671
+434 84 4718
+434 86 4735
+434 88 4740
+434 89 4768
+434 92 4769
+433 94 4772
+432 96 4802
+430 101 4802
+429 103 4804
+429 106 4835
+428 112 4836
+428 114 4836
+425 121 4868
+423 127 4869
+421 132 4901
+417 137 4902
+414 143 4934
+412 148 4935
+406 156 4968
+401 163 4969
+398 166 4972
+397 169 5001
+393 173 5002
+392 175 5005
+391 176 5034
+390 178 5035
+389 179 5036
+388 180 5067
+387 181 5068
+386 182 5101
+385 182 5589
+384 182 5616
+.PEN_UP
+.PEN_DOWN
+385 84 6399
+386 84 6468
+387 87 6489
+388 90 6531
+391 95 6531
+392 97 6532
+394 99 6564
+396 106 6564
+401 113 6597
+406 120 6598
+411 126 6630
+416 132 6631
+422 138 6663
+426 143 6664
+427 143 6668
+429 145 6697
+432 150 6697
+433 153 6700
+436 156 6730
+440 160 6730
+444 163 6732
+447 165 6763
+454 171 6764
+457 173 6797
+458 173 6798
+459 174 6830
+460 175 6831
+462 176 6863
+466 178 6864
+471 179 6897
+473 179 6897
+474 180 6900
+475 180 6930
+477 180 6964
+478 180 6996
+479 180 7029
+480 180 7064
+481 181 7097
+483 181 7100
+485 181 7149
+486 181 7180
+487 181 7213
+488 181 7304
+489 181 7346
+490 181 7396
+491 181 7428
+491 182 7629
+.PEN_UP