├── 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 |
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 |
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 |