├── src ├── __init__.py ├── static │ ├── .DS_Store │ ├── img │ │ └── ub_logo.png │ └── fonts │ │ └── Junicode-Regular.ttf ├── keyboard_builder.py ├── unicode_categories.py └── templates │ └── index.html ├── assets └── keyboard_builder.png ├── LICENSE ├── setup.py └── README.md /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/static/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UB-Mannheim/keyboardBuilder4eScriptorium/main/src/static/.DS_Store -------------------------------------------------------------------------------- /src/static/img/ub_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UB-Mannheim/keyboardBuilder4eScriptorium/main/src/static/img/ub_logo.png -------------------------------------------------------------------------------- /assets/keyboard_builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UB-Mannheim/keyboardBuilder4eScriptorium/main/assets/keyboard_builder.png -------------------------------------------------------------------------------- /src/static/fonts/Junicode-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UB-Mannheim/keyboardBuilder4eScriptorium/main/src/static/fonts/Junicode-Regular.ttf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 ths 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setup( 7 | name="Keyboard Builder for eScriptorium", 8 | author="Thomas Schmidt", 9 | version='0.1', 10 | packages=find_packages(), 11 | package_data={ 12 | '': ['templates/*.html', 'static/fonts/*', 'static/img/*'], 13 | }, 14 | license="MIT License", 15 | description="Build virtual keyboards for eScriptorium with ease...", 16 | long_description=long_description, 17 | long_description_content_type="text/markdown", 18 | url="https://github.com/tsmdt/keyboardBuilder4eScriptorium", 19 | install_requires=[ 20 | 'Flask==3.0.3', 21 | 'click==8.1.7' 22 | ], 23 | classifiers=[ 24 | "Programming Language :: Python :: 3", 25 | "License :: OSI Approved :: MIT License", 26 | "Operating System :: OS Independent", 27 | ], 28 | python_requires='>=3.9', 29 | entry_points={ 30 | 'console_scripts': [ 31 | 'keybuilder=src.keyboard_builder:main', 32 | ], 33 | }, 34 | ) 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keyboard Builder for eScriptorium ⌨️ 2 | This browser app lets you build **virtual keyboards** for [eScriptorium](https://en.wikipedia.org/wiki/EScriptorium) with ease.

It's also useful if you want to transition from another transcription client with an already existing virtual keyboard, without having to manually add all your custom Unicode characters again (*which is a drag!*). 3 | 4 | **Features**: 5 | * `Junicode2` as standard font for displaying a wide range of Unicodes. [↗ Junicode Font by Peter S. Baker](https://psb1558.github.io/Junicode-font/) 6 | * `Filter` by Unicode categories (e.g. `Greek and Coptic`, `Hebrew`, `Latin Extended-A` ...) for easier access to the Unicode range you have in mind 7 | * `Search` for a specific Unicode by name (e.g. `"Greek Capital Letter Omega"`). You don't have to memorize the complete name, a part is enough (e.g. `Greek Cap`) ... 8 | * `Paste` a **single** Unicode character you copied elsewhere and add it to your keyboard 9 | * `Paste` a **string** of Unicode characters (e.g. `£q«±²ſē∓ↄ`) and add each individual character to your keyboard automatically 10 | * Adjust the `order` of your keyboard via **drag** and **drop** 11 | * Adjust the `layout` of your keyboard by increasing or decreasing the column / row count of your grid (i.e. you can design a keyboard layout in a `2 x 10` grid or `4 x 5` and so forth...) 12 | * `Download` your keyboard as an eScriptorium compatible `.json` file 13 | 14 | keyboard_builder 15 | 16 | ## Table of Contents 17 | 1. [Requirements](#requirements) 18 | 2. [Installation](#installation) 19 | 3. [Usage](#usage) 20 | 4. [Acknowledgement](#acknowledgement) 21 | 22 | ## Requirements 23 | - Python 3.9 and above 24 | 25 | ## Installation 26 | #### 1. Clone this repository 27 | ```shell 28 | git clone https://github.com/th-schmidt/keyboardBuilder4eScriptorium/ 29 | ``` 30 | #### 2. Change to the project folder 31 | ```shell 32 | cd keyboardBuilder4eScriptorium 33 | ``` 34 | #### 3. Create a Python virtual environment 35 | ```python 36 | python -m venv venv 37 | ``` 38 | #### 4. Activate your virtual environment 39 | ```shell 40 | source venv/bin/activate 41 | ``` 42 | #### 5. Install the app 43 | ```python 44 | pip install . 45 | ``` 46 | #### 6. Run the app 47 | ``` 48 | >>> keybuilder --help 49 | Usage: keybuilder [OPTIONS] 50 | 51 | Run the Keyboard Builder 4 eScriptorium web application server on the 52 | specified port and with the specified debug mode. 53 | 54 | Options: 55 | --host TEXT The hostname or IP address to listen on. Defaults to 56 | 127.0.0.1. 57 | --port INTEGER The port number on which the server will run. Default is 58 | 8000. 59 | --debug Enable or disable debug mode. Debug mode is enabled by 60 | default. 61 | --help Show this message and exit. 62 | ``` 63 | 64 | ## Usage 65 | ### User guide 66 | You can find a detailed user guide by clicking on the logo graphic in the top left corner. 67 | 68 | ### Import an existing virtual keyboard 69 | If you want to import an already existing virtual keyboard (e.g. in *Transkribus*) to eScriptorium you can use the Keyboard Builder like this: 70 | 71 | 1. Simply paste all Unicodes of your existing keyboard in a new Transkribus document without any whitespaces between them. 72 | 2. Copy the resulting string (e.g. `£q«±²ſœē∓ↄ`) 73 | 3. Paste this string to the "Paste character(s) here" field of the Keyboard Builder. 74 | 4. Click on the "Add Character(s)" button and every single unique character of the string will be added to your custom keyboard. 75 | 76 | ## Acknowledgement 77 | This app was built with help from [OpenAI's GPT-4o](https://chatgpt.com/) and uses [Junicode Font by Peter S. Baker](https://psb1558.github.io/Junicode-font/), licensed under Open Font License, v. 1.1. 78 | -------------------------------------------------------------------------------- /src/keyboard_builder.py: -------------------------------------------------------------------------------- 1 | import os 2 | import click 3 | import unicodedata 4 | from flask import Flask, request, jsonify, session, render_template 5 | from src import unicode_categories as uc 6 | 7 | 8 | app = Flask( 9 | __name__, 10 | template_folder='templates', 11 | static_folder='static' 12 | ) 13 | app.secret_key = os.urandom(24) 14 | 15 | 16 | def get_unicode_characters(start, limit): 17 | return [chr(i) for i in range(start, start + limit)] 18 | 19 | 20 | @app.route('/') 21 | def index(): 22 | return render_template('index.html') 23 | 24 | 25 | @app.route('/live_search_unicode_name', methods=['POST']) 26 | def live_search_unicode_name(): 27 | data = request.json 28 | partial_name = data.get('partial_name').lower() 29 | matching_names = [] 30 | 31 | # Unicode range limit 32 | for codepoint in range(0x110000): 33 | # Limit to 100 results 34 | if len(matching_names) >= 100: 35 | break 36 | try: 37 | name = unicodedata.name(chr(codepoint)).lower() 38 | if partial_name in name: 39 | char = chr(codepoint) 40 | matching_names.append({"name": name, "character": char}) 41 | except ValueError: 42 | continue 43 | return jsonify(matching_names) 44 | 45 | 46 | @app.route('/get_unicode_characters', methods=['GET']) 47 | def get_unicode_characters_endpoint(): 48 | start = int(request.args.get('start', 0)) 49 | limit = int(request.args.get('limit', 40)) 50 | category = request.args.get('category', None) 51 | unicode_chars = [] 52 | 53 | if category and category in uc.unicode_category_ranges: 54 | category_range = uc.unicode_category_ranges[category] 55 | codepoint = category_range[0] + start 56 | end_codepoint = category_range[1] 57 | while len(unicode_chars) < limit and codepoint <= end_codepoint: 58 | try: 59 | char = chr(codepoint) 60 | name = unicodedata.name(char) 61 | unicode_chars.append({"character": char, "name": name}) 62 | except ValueError: 63 | pass 64 | codepoint += 1 65 | else: 66 | codepoint = start 67 | 68 | while len(unicode_chars) < limit and codepoint < 0x110000: 69 | try: 70 | char = chr(codepoint) 71 | name = unicodedata.name(char) 72 | unicode_chars.append({"character": char, "name": name}) 73 | except ValueError: 74 | pass 75 | codepoint += 1 76 | 77 | return jsonify(unicode_chars) 78 | 79 | 80 | @app.route('/get_unicode_categories', methods=['GET']) 81 | def get_unicode_categories(): 82 | categories = list(uc.unicode_category_ranges.keys()) 83 | return jsonify(categories) 84 | 85 | 86 | @app.route('/add_character', methods=['POST']) 87 | def add_character(): 88 | data = request.json 89 | character = data.get('character') 90 | name = unicodedata.name(character) 91 | row = data.get('row') 92 | column = data.get('column') 93 | 94 | if 'custom_characters' not in session: 95 | session['custom_characters'] = [] 96 | 97 | session['custom_characters'].append({ 98 | 'character': character, 99 | 'row': row, 100 | 'column': column, 101 | 'name': name 102 | }) 103 | 104 | # Ensure session changes are saved 105 | session.modified = True 106 | return jsonify(session['custom_characters']) 107 | 108 | 109 | @app.route('/remove_character', methods=['POST']) 110 | def remove_character(): 111 | data = request.json 112 | character = data.get('character') 113 | 114 | # Get the current custom characters from the session 115 | custom_characters = session.get('custom_characters', []) 116 | 117 | # Filter out the character to be removed 118 | updated_characters = [ 119 | char_obj for char_obj in custom_characters 120 | if char_obj["character"] != character 121 | ] 122 | 123 | session['custom_characters'] = updated_characters 124 | 125 | # Ensure session changes are saved 126 | session.modified = True 127 | 128 | return jsonify({"message": "Character removed successfully!"}) 129 | 130 | 131 | @app.route('/get_characters', methods=['GET']) 132 | def get_characters(): 133 | custom_characters = session.get('custom_characters', []) 134 | return jsonify({'characters': custom_characters}) 135 | 136 | 137 | @app.route('/clear_characters', methods=['POST']) 138 | def clear_characters(): 139 | session.pop('custom_characters', None) 140 | return jsonify({"status": "cleared"}) 141 | 142 | 143 | @app.route('/match_character', methods=['POST']) 144 | def match_character(): 145 | data = request.json 146 | characters = data.get('characters') 147 | 148 | if not characters: 149 | return jsonify({"message": "No characters provided!"}), 400 150 | 151 | valid_characters = [] 152 | for char in characters: 153 | try: 154 | # Check if the character is a valid Unicode character 155 | if char.isprintable() and char not in valid_characters: 156 | valid_characters.append(char) 157 | except: 158 | continue 159 | 160 | if 'custom_characters' not in session: 161 | session['custom_characters'] = [] 162 | 163 | custom_characters = session['custom_characters'] 164 | 165 | # Add the new characters to the session 166 | for char in valid_characters: 167 | custom_characters.append({ 168 | 'character': char, 169 | 'row': 0, 170 | 'column': 0, 171 | 'name': unicodedata.name(char) 172 | }) 173 | 174 | row_limit = session.get('row_limit', 10) 175 | 176 | # Recalculate rows and columns 177 | for i, char_obj in enumerate(custom_characters): 178 | char_obj['row'] = i // row_limit 179 | char_obj['column'] = i % row_limit 180 | 181 | session['custom_characters'] = custom_characters 182 | session.modified = True 183 | 184 | return jsonify({"message": "Characters matched and added successfully!", "valid_characters": valid_characters}) 185 | 186 | 187 | @app.route('/update_custom_order', methods=['POST']) 188 | def update_custom_order(): 189 | data = request.json 190 | new_order = data.get('order', []) 191 | 192 | custom_characters = session.get('custom_characters', []) 193 | character_dict = {char['character']: char for char in custom_characters} 194 | 195 | # Get the current row limit from the session 196 | row_limit = session.get('row_limit', 10) 197 | updated_characters = [] 198 | 199 | for i, char in enumerate(new_order): 200 | if char in character_dict: 201 | char_obj = character_dict[char] 202 | char_obj['row'] = i // row_limit 203 | char_obj['column'] = i % row_limit 204 | updated_characters.append(char_obj) 205 | 206 | session['custom_characters'] = updated_characters 207 | session.modified = True 208 | 209 | return jsonify({"status": "success"}) 210 | 211 | 212 | @app.route('/update_row_limit', methods=['POST']) 213 | def update_row_limit(): 214 | data = request.json 215 | row_limit = data.get('row_limit') 216 | session['row_limit'] = row_limit 217 | 218 | # Recalculate row and column values for existing characters 219 | custom_characters = session.get('custom_characters', []) 220 | for i, char_obj in enumerate(custom_characters): 221 | char_obj['row'] = i // row_limit 222 | char_obj['column'] = i % row_limit 223 | 224 | session['custom_characters'] = custom_characters 225 | session.modified = True 226 | 227 | return jsonify({"status": "success", "row_limit": row_limit}) 228 | 229 | 230 | @app.route('/recalculate_rows_columns', methods=['POST']) 231 | def recalculate_rows_columns(): 232 | custom_characters = session.get('custom_characters', []) 233 | row_limit = session.get('row_limit', 10) 234 | 235 | # Recalculate rows and columns 236 | for i, char_obj in enumerate(custom_characters): 237 | char_obj['row'] = i // row_limit 238 | char_obj['column'] = i % row_limit 239 | 240 | session['custom_characters'] = custom_characters 241 | session.modified = True 242 | 243 | return jsonify({"status": "success"}) 244 | 245 | 246 | @app.route('/download_characters', methods=['POST']) 247 | def download_characters(): 248 | data = request.json 249 | 250 | # Get the keyboard name from the request 251 | keyboard_name = data.get('keyboard_name', 'my_keyboard') 252 | custom_characters = session.get('custom_characters', []) 253 | 254 | response = { 255 | "version": "0.1", 256 | "author": "Keyboard Builder for eScriptorium v0.1", 257 | "name": keyboard_name, 258 | "characters": 259 | [ 260 | { 261 | "character": char['character'], 262 | "row": char['row'], 263 | "column": char['column'] 264 | } 265 | for char in custom_characters 266 | ] 267 | } 268 | return jsonify(response) 269 | 270 | 271 | @click.command() 272 | @click.option('--host', default='127.0.0.1', help='The hostname or IP address to listen on. Defaults to 127.0.0.1.') 273 | @click.option('--port', type=int, default=8000, help='The port number on which the server will run. Default is 8000.') 274 | @click.option('--debug', is_flag=True, default=True, help='Enable or disable debug mode. Debug mode is enabled by default.') 275 | def main(host, port, debug): 276 | """ 277 | Run the Keyboard Builder 4 eScriptorium web application server on the specified port and with the specified debug mode. 278 | """ 279 | app.run(host=host, port=port, debug=debug) 280 | 281 | 282 | if __name__ == '__main__': 283 | main() 284 | -------------------------------------------------------------------------------- /src/unicode_categories.py: -------------------------------------------------------------------------------- 1 | unicode_category_ranges = { 2 | "Adlam": (0x1E900, 0x1E95F), 3 | "Aegean Numbers": (0x10100, 0x1013F), 4 | "Alphabetic Presentation Forms": (0xFB00, 0xFB4F), 5 | "Anatolian Hieroglyphs": (0x14400, 0x1467F), 6 | "Ancient Greek Musical Notation": (0x1D200, 0x1D24F), 7 | "Ancient Greek Numbers": (0x10140, 0x1018F), 8 | "Ancient Symbols": (0x10190, 0x101CF), 9 | "Arabic": (0x0600, 0x06FF), 10 | "Arabic Extended-A": (0x08A0, 0x08FF), 11 | "Arabic Presentation Forms-A": (0xFB50, 0xFDFF), 12 | "Arabic Presentation Forms-B": (0xFE70, 0xFEFF), 13 | "Arabic Supplement": (0x0750, 0x077F), 14 | "Armenian": (0x0530, 0x058F), 15 | "Arrows": (0x2190, 0x21FF), 16 | "Balinese": (0x1B00, 0x1B7F), 17 | "Bamum": (0xA6A0, 0xA6FF), 18 | "Bamum Supplement": (0x16800, 0x16A38), 19 | "Bassa Vah": (0x16AD0, 0x16AFF), 20 | "Batak": (0x1BC0, 0x1BFF), 21 | "Bengali": (0x0980, 0x09FF), 22 | "Bhaiksuki": (0x11C00, 0x11C6F), 23 | "Block Elements": (0x2580, 0x259F), 24 | "Bopomofo": (0x3100, 0x312F), 25 | "Bopomofo Extended": (0x31A0, 0x31BF), 26 | "Box Drawing": (0x2500, 0x257F), 27 | "Brahmi": (0x11000, 0x1107F), 28 | "Braille Patterns": (0x2800, 0x28FF), 29 | "Buginese": (0x1A00, 0x1A1F), 30 | "Buhid": (0x1740, 0x175F), 31 | "Byzantine Musical Symbols": (0x1D000, 0x1D0FF), 32 | "Canadian Aboriginal Syllabics Extended": (0x18B0, 0x18FF), 33 | "Carian": (0x102A0, 0x102DF), 34 | "Caucasian Albanian": (0x10530, 0x1056F), 35 | "Chakma": (0x11100, 0x1114F), 36 | "Cham": (0xAA00, 0xAA5F), 37 | "Cherokee": (0x13A0, 0x13FF), 38 | "Cherokee Supplement": (0xAB70, 0xABBF), 39 | "Chorasmian": (0x10FB0, 0x10FDF), 40 | "CJK Compatibility": (0x3300, 0x33FF), 41 | "CJK Compatibility Forms": (0xFE30, 0xFE4F), 42 | "CJK Compatibility Ideographs": (0xF900, 0xFAFF), 43 | "CJK Compatibility Ideographs Supplement": (0x2F800, 0x2FA1F), 44 | "CJK Radicals Supplement": (0x2E80, 0x2EFF), 45 | "CJK Strokes": (0x31C0, 0x31EF), 46 | "CJK Symbols and Punctuation": (0x3000, 0x303F), 47 | "CJK Unified Ideographs": (0x4E00, 0x9FFF), 48 | "CJK Unified Ideographs Extension A": (0x3400, 0x4DBF), 49 | "CJK Unified Ideographs Extension B": (0x20000, 0x2A6DF), 50 | "CJK Unified Ideographs Extension C": (0x2A700, 0x2B73F), 51 | "CJK Unified Ideographs Extension D": (0x2B740, 0x2B81F), 52 | "CJK Unified Ideographs Extension E": (0x2B820, 0x2CEAF), 53 | "CJK Unified Ideographs Extension F": (0x2CEB0, 0x2EBEF), 54 | "Combining Diacritical Marks": (0x0300, 0x036F), 55 | "Combining Diacritical Marks Extended": (0x1AB0, 0x1AFF), 56 | "Combining Diacritical Marks Supplement": (0x1DC0, 0x1DFF), 57 | "Combining Half Marks": (0xFE20, 0xFE2F), 58 | "Combining Marks for Symbols": (0x20D0, 0x20FF), 59 | "Common Indic Number Forms": (0xA830, 0xA83F), 60 | "Control Pictures": (0x2400, 0x243F), 61 | "Coptic": (0x2C80, 0x2CFF), 62 | "Coptic Epact Numbers": (0x102E0, 0x102FF), 63 | "Cuneiform": (0x12000, 0x123FF), 64 | "Currency Symbols": (0x20A0, 0x20CF), 65 | "Cypriot Syllabary": (0x10800, 0x1083F), 66 | "Cyrillic": (0x0400, 0x04FF), 67 | "Cyrillic Extended-A": (0x2DE0, 0x2DFF), 68 | "Cyrillic Extended-B": (0xA640, 0xA69F), 69 | "Cyrillic Extended-C": (0x1C80, 0x1C8F), 70 | "Cyrillic Supplement": (0x0500, 0x052F), 71 | "Deseret": (0x10400, 0x1044F), 72 | "Devanagari": (0x0900, 0x097F), 73 | "Devanagari Extended": (0xA8E0, 0xA8FF), 74 | "Dingbats": (0x2700, 0x27BF), 75 | "Dives Akuru": (0x11900, 0x1195F), 76 | "Dogra": (0x11800, 0x1184F), 77 | "Duployan": (0x1BC00, 0x1BC9F), 78 | "Early Dynastic Cuneiform": (0x12480, 0x1254F), 79 | "Egyptian Hieroglyph Format Controls": (0x13430, 0x1345F), 80 | "Egyptian Hieroglyphs": (0x13000, 0x1342F), 81 | "Elbasan": (0x10500, 0x1052F), 82 | "Elymaic": (0x10FE0, 0x10FFF), 83 | "Ethiopic": (0x1200, 0x137F), 84 | "Ethiopic Extended": (0x2D80, 0x2DDF), 85 | "Ethiopic Extended-A": (0xAB00, 0xAB2F), 86 | "Ethiopic Supplement": (0x1380, 0x139F), 87 | "General Punctuation": (0x2000, 0x206F), 88 | "Geometric Shapes": (0x25A0, 0x25FF), 89 | "Georgian": (0x10A0, 0x10FF), 90 | "Georgian Extended": (0x1C90, 0x1CBF), 91 | "Georgian Supplement": (0x2D00, 0x2D2F), 92 | "Glagolitic": (0x2C00, 0x2C5F), 93 | "Glagolitic Supplement": (0x1E000, 0x1E02F), 94 | "Gothic": (0x10330, 0x1034F), 95 | "Grantha": (0x11300, 0x1137F), 96 | "Greek and Coptic": (0x0370, 0x03FF), 97 | "Greek Extended": (0x1F00, 0x1FFF), 98 | "Gujarati": (0x0A80, 0x0AFF), 99 | "Gunjala Gondi": (0x11D60, 0x11DAF), 100 | "Gurmukhi": (0x0A00, 0x0A7F), 101 | "Halfwidth and Fullwidth Forms": (0xFF00, 0xFFEF), 102 | "Hangul Compatibility Jamo": (0x3130, 0x318F), 103 | "Hangul Jamo": (0x1100, 0x11FF), 104 | "Hangul Jamo Extended-A": (0xA960, 0xA97F), 105 | "Hangul Jamo Extended-B": (0xD7B0, 0xD7FF), 106 | "Hangul Syllables": (0xAC00, 0xD7AF), 107 | "Hanifi Rohingya": (0x10D00, 0x10D3F), 108 | "Hanunoo": (0x1720, 0x173F), 109 | "Hatran": (0x108E0, 0x108FF), 110 | "Hebrew": (0x0590, 0x05FF), 111 | "High Private Use Surrogates": (0xDB80, 0xDBFF), 112 | "High Surrogates": (0xD800, 0xDB7F), 113 | "Hiragana": (0x3040, 0x309F), 114 | "Ideographic Description Characters": (0x2FF0, 0x2FFF), 115 | "Imperial Aramaic": (0x10840, 0x1085F), 116 | "Indic Siyaq Numbers": (0x1EC70, 0x1ECBF), 117 | "Inscriptional Pahlavi": (0x10B60, 0x10B7F), 118 | "Inscriptional Parthian": (0x10B40, 0x10B5F), 119 | "IPA Extensions": (0x0250, 0x02AF), 120 | "Javanese": (0xA980, 0xA9DF), 121 | "Kaithi": (0x11080, 0x110CF), 122 | "Kana Extended-A": (0x1B100, 0x1B12F), 123 | "Kana Supplement": (0x1B000, 0x1B0FF), 124 | "Kanbun": (0x3190, 0x319F), 125 | "Kangxi Radicals": (0x2F00, 0x2FDF), 126 | "Kannada": (0x0C80, 0x0CFF), 127 | "Katakana": (0x30A0, 0x30FF), 128 | "Katakana Phonetic Extensions": (0x31F0, 0x31FF), 129 | "Kayah Li": (0xA900, 0xA92F), 130 | "Kharoshthi": (0x10A00, 0x10A5F), 131 | "Khitan Small Script": (0x18B00, 0x18CFF), 132 | "Khmer": (0x1780, 0x17FF), 133 | "Khmer Symbols": (0x19E0, 0x19FF), 134 | "Khojki": (0x11200, 0x1124F), 135 | "Khudawadi": (0x112B0, 0x112FF), 136 | "Lao": (0x0E80, 0x0EFF), 137 | "Latin Extended-A": (0x0100, 0x017F), 138 | "Latin Extended-B": (0x0180, 0x024F), 139 | "Latin Extended-C": (0x2C60, 0x2C7F), 140 | "Latin Extended-D": (0xA720, 0xA7FF), 141 | "Latin Extended-E": (0xAB30, 0xAB6F), 142 | "Latin Extended Additional": (0x1E00, 0x1EFF), 143 | "Latin-1 Supplement": (0x0080, 0x00FF), 144 | "Lepcha": (0x1C00, 0x1C4F), 145 | "Letterlike Symbols": (0x2100, 0x214F), 146 | "Limbu": (0x1900, 0x194F), 147 | "Linear A": (0x10600, 0x1077F), 148 | "Linear B Ideograms": (0x10080, 0x100FF), 149 | "Linear B Syllabary": (0x10000, 0x1007F), 150 | "Lisu": (0xA4D0, 0xA4FF), 151 | "Lycian": (0x10280, 0x1029F), 152 | "Lydian": (0x102A0, 0x102DF), 153 | "Mahajani": (0x11150, 0x1117F), 154 | "Makasar": (0x11EE0, 0x11EFF), 155 | "Malayalam": (0x0D00, 0x0D7F), 156 | "Mandaic": (0x0840, 0x085F), 157 | "Manichaean": (0x10AC0, 0x10AFF), 158 | "Marchen": (0x11C70, 0x11CBF), 159 | "Masaram Gondi": (0x11D00, 0x11D5F), 160 | "Mathematical Alphanumeric Symbols": (0x1D400, 0x1D7FF), 161 | "Mathematical Operators": (0x2200, 0x22FF), 162 | "Mayan Numerals": (0x1D2E0, 0x1D2FF), 163 | "Medefaidrin": (0x16E40, 0x16E9F), 164 | "Meetei Mayek": (0xABC0, 0xABFF), 165 | "Meetei Mayek Extensions": (0xAAE0, 0xAAFF), 166 | "Mende Kikakui": (0x1E800, 0x1E8DF), 167 | "Meroitic Cursive": (0x109A0, 0x109FF), 168 | "Meroitic Hieroglyphs": (0x10980, 0x1099F), 169 | "Miao": (0x16F00, 0x16F9F), 170 | "Miscellaneous Mathematical Symbols-A": (0x27C0, 0x27EF), 171 | "Miscellaneous Mathematical Symbols-B": (0x2980, 0x29FF), 172 | "Miscellaneous Symbols": (0x2600, 0x26FF), 173 | "Miscellaneous Symbols and Arrows": (0x2B00, 0x2BFF), 174 | "Miscellaneous Technical": (0x2300, 0x23FF), 175 | "Modi": (0x11600, 0x1165F), 176 | "Modifier Tone Letters": (0xA700, 0xA71F), 177 | "Mongolian": (0x1800, 0x18AF), 178 | "Mongolian Supplement": (0x11660, 0x1167F), 179 | "Mro": (0x16A40, 0x16A6F), 180 | "Multani": (0x11280, 0x112AF), 181 | "Musical Symbols": (0x1D100, 0x1D1FF), 182 | "Myanmar": (0x1000, 0x109F), 183 | "Myanmar Extended-A": (0xAA60, 0xAA7F), 184 | "Myanmar Extended-B": (0xA9E0, 0xA9FF), 185 | "NKo": (0x07C0, 0x07FF), 186 | "Nabataean": (0x10880, 0x108AF), 187 | "Nandinagri": (0x119A0, 0x119FF), 188 | "New Tai Lue": (0x1980, 0x19DF), 189 | "Newa": (0x11400, 0x1147F), 190 | "Number Forms": (0x2150, 0x218F), 191 | "Nushu": (0x1B170, 0x1B2FF), 192 | "Nyiakeng Puachue Hmong": (0x1E100, 0x1E14F), 193 | "Ogham": (0x1680, 0x169F), 194 | "Ol Chiki": (0x1C50, 0x1C7F), 195 | "Old Hungarian": (0x10C80, 0x10CFF), 196 | "Old Italic": (0x10300, 0x1032F), 197 | "Old North Arabian": (0x10A80, 0x10A9F), 198 | "Old Permic": (0x10350, 0x1037F), 199 | "Old Persian": (0x103A0, 0x103DF), 200 | "Old Sogdian": (0x10F00, 0x10F2F), 201 | "Old South Arabian": (0x10A60, 0x10A7F), 202 | "Old Turkic": (0x10C00, 0x10C4F), 203 | "Oriya": (0x0B00, 0x0B7F), 204 | "Ornamental Dingbats": (0x1F650, 0x1F67F), 205 | "Osage": (0x104B0, 0x104FF), 206 | "Osmanya": (0x10480, 0x104AF), 207 | "Pahawh Hmong": (0x16B00, 0x16B8F), 208 | "Palmyrene": (0x10860, 0x1087F), 209 | "Pau Cin Hau": (0x11AC0, 0x11AFF), 210 | "Phags-pa": (0xA840, 0xA87F), 211 | "Phoenician": (0x10900, 0x1091F), 212 | "Phonetic Extensions": (0x1D00, 0x1D7F), 213 | "Phonetic Extensions Supplement": (0x1D80, 0x1DBF), 214 | "Playing Cards": (0x1F0A0, 0x1F0FF), 215 | "Psalter Pahlavi": (0x10B80, 0x10BAF), 216 | "Rejang": (0xA930, 0xA95F), 217 | "Rumi Numeral Symbols": (0x10E60, 0x10E7F), 218 | "Runic": (0x16A0, 0x16FF), 219 | "Samaritan": (0x0800, 0x083F), 220 | "Saurashtra": (0xA880, 0xA8DF), 221 | "Sharada": (0x11180, 0x111DF), 222 | "Shavian": (0x10450, 0x1047F), 223 | "Shorthand Format Controls": (0x1BCA0, 0x1BCAF), 224 | "Siddham": (0x11580, 0x115FF), 225 | "Sinhala": (0x0D80, 0x0DFF), 226 | "Sinhala Archaic Numbers": (0x111E0, 0x111FF), 227 | "Small Form Variants": (0xFE50, 0xFE6F), 228 | "Sogdian": (0x10F30, 0x10F6F), 229 | "Sora Sompeng": (0x110D0, 0x110FF), 230 | "Soyombo": (0x11A50, 0x11AAF), 231 | "Spacing Modifier Letters": (0x02B0, 0x02FF), 232 | "Specials": (0xFFF0, 0xFFFF), 233 | "Sundanese": (0x1B80, 0x1BBF), 234 | "Sundanese Supplement": (0x1CC0, 0x1CCF), 235 | "Superscripts and Subscripts": (0x2070, 0x209F), 236 | "Supplemental Arrows-A": (0x27F0, 0x27FF), 237 | "Supplemental Arrows-B": (0x2900, 0x297F), 238 | "Supplemental Mathematical Operators": (0x2A00, 0x2AFF), 239 | "Supplemental Punctuation": (0x2E00, 0x2E7F), 240 | "Sutton SignWriting": (0x1D800, 0x1DAAF), 241 | "Syloti Nagri": (0xA800, 0xA82F), 242 | "Syriac": (0x0700, 0x074F), 243 | "Syriac Supplement": (0x0860, 0x086F), 244 | "Tagalog": (0x1700, 0x171F), 245 | "Tagbanwa": (0x1760, 0x177F), 246 | "Tai Le": (0x1950, 0x197F), 247 | "Tai Tham": (0x1A20, 0x1AAF), 248 | "Tai Viet": (0xAA80, 0xAADF), 249 | "Tai Xuan Jing Symbols": (0x1D300, 0x1D35F), 250 | "Takri": (0x11680, 0x116CF), 251 | "Tamil": (0x0B80, 0x0BFF), 252 | "Tamil Supplement": (0x11FC0, 0x11FFF), 253 | "Tangsa": (0x16A70, 0x16ACF), 254 | "Tangut": (0x17000, 0x187FF), 255 | "Telugu": (0x0C00, 0x0C7F), 256 | "Thaana": (0x0780, 0x07BF), 257 | "Thai": (0x0E00, 0x0E7F), 258 | "Tibetan": (0x0F00, 0x0FFF), 259 | "Tifinagh": (0x2D30, 0x2D7F), 260 | "Tirhuta": (0x11480, 0x114DF), 261 | "Transport and Map Symbols": (0x1F680, 0x1F6FF), 262 | "Ugaritic": (0x10380, 0x1039F), 263 | "Unified Canadian Aboriginal Syllabics": (0x1400, 0x167F), 264 | "Unified Canadian Aboriginal Syllabics Extended": (0x18B0, 0x18FF), 265 | "Vai": (0xA500, 0xA63F), 266 | "Vedic Extensions": (0x1CD0, 0x1CFF), 267 | "Vertical Forms": (0xFE10, 0xFE1F), 268 | "Vithkuqi": (0x10570, 0x105BF), 269 | "Wancho": (0x1E2C0, 0x1E2FF), 270 | "Warang Citi": (0x118A0, 0x118FF), 271 | "Yezidi": (0x10E80, 0x10EBF), 272 | "Yi Radicals": (0xA490, 0xA4CF), 273 | "Yi Syllables": (0xA000, 0xA48F), 274 | "Yijing Hexagram Symbols": (0x4DC0, 0x4DFF), 275 | "Zanabazar Square": (0x11A00, 0x11A4F) 276 | } 277 | -------------------------------------------------------------------------------- /src/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Virtual Keyboard Builder for eScriptorium 7 | 258 | 259 | 260 |
261 |
262 |
263 |
264 | Watermark 265 |

Virtual Keyboard Builder for eScriptorium

266 |
267 |
268 |

269 | Build virtual keyboards for eScriptorium with ease... 🌞

270 | You can filter by Unicode categories using the dropdown menu, 271 | search for a Unicode character by name or simple 272 | paste and add a character or string of characters to your custom keyboard.

273 | The latter option makes building already existing virtual keyboards (e.g. in Transkribus) especially easy: Simply paste all Unicodes of your existing keyboard in a new Transkribus document 274 | without any whitespaces between them. Copy the resulting string (e.g. £q«±²ſœŒē∓ↄ) and paste this string to the "Paste character(s) here" field. 275 | Click on the "Add Character(s)" button and every single unique character of the string will be added to your custom keyboard.

276 | * Use the buttons and to browse through the Unicode characters.

277 | * Click on a Unicode character in the grid to add it to your custom keyboard.

278 | * Remove an already added character by clicking on it again in your custom keyboard.

279 | * Change the order of your custom keyboard by dragging and dropping a certain Unicode character to the position you like.

280 | * The + and - buttons adjust the width of the keyboard rows. The default is set to 10 Unicode characters per keyboard row, but you can adjust this value to your needs.

281 | * Download your custom keyboard by clicking on the button Download JSON and import the .json into eScriptorium. 282 |

283 | 🤖 Find the source code on GitHub 🤖 284 |

285 |
286 |
287 |
288 |
289 | 290 | 291 | 292 |
293 | 296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 | 304 | 305 | 306 | 307 | 308 | 309 |
310 | 687 |
688 | 689 | --------------------------------------------------------------------------------