jkUnicode 2.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jkUnicode/__init__.py +280 -0
- jkUnicode/aglfn.py +52 -0
- jkUnicode/aglfnData.py +590 -0
- jkUnicode/cmdline/__init__.py +0 -0
- jkUnicode/cmdline/ortho.py +132 -0
- jkUnicode/cmdline/uniinfo.py +38 -0
- jkUnicode/json/ignored_characters.json +6 -0
- jkUnicode/json/ignored_languages.json +356 -0
- jkUnicode/json/language_characters.json +47268 -0
- jkUnicode/json/language_characters_hyperglot.json +68450 -0
- jkUnicode/json/language_speakers.json +887 -0
- jkUnicode/json/languages.json +314 -0
- jkUnicode/json/scripts.json +204 -0
- jkUnicode/json/territories.json +297 -0
- jkUnicode/orthography.py +990 -0
- jkUnicode/py.typed +0 -0
- jkUnicode/tools/__init__.py +0 -0
- jkUnicode/tools/helpers.py +42 -0
- jkUnicode/tools/jsonhelpers.py +39 -0
- jkUnicode/tools/xmlhelpers.py +189 -0
- jkUnicode/uniBlock.py +41 -0
- jkUnicode/uniBlockData.py +342 -0
- jkUnicode/uniCase.py +2948 -0
- jkUnicode/uniCat.py +40120 -0
- jkUnicode/uniDecomposition.py +2085 -0
- jkUnicode/uniName.py +40120 -0
- jkUnicode/uniNiceName.py +77 -0
- jkUnicode/uniRangesBits.py +274 -0
- jkUnicode/uniScript.py +16 -0
- jkUnicode/uniScriptData.py +2257 -0
- jkUnicode-2.2.2.dist-info/LICENSE +21 -0
- jkUnicode-2.2.2.dist-info/METADATA +193 -0
- jkUnicode-2.2.2.dist-info/RECORD +36 -0
- jkUnicode-2.2.2.dist-info/WHEEL +5 -0
- jkUnicode-2.2.2.dist-info/entry_points.txt +3 -0
- jkUnicode-2.2.2.dist-info/top_level.txt +1 -0
jkUnicode/__init__.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
from jkUnicode.aglfn import getGlyphnameForUnicode
|
|
5
|
+
from jkUnicode.uniBlock import get_block
|
|
6
|
+
from jkUnicode.uniCase import uniLowerCaseMapping, uniUpperCaseMapping
|
|
7
|
+
from jkUnicode.uniCat import uniCat
|
|
8
|
+
from jkUnicode.uniDecomposition import uniDecompositionMapping
|
|
9
|
+
from jkUnicode.uniName import uniName
|
|
10
|
+
from jkUnicode.uniNiceName import nice_name_rules
|
|
11
|
+
from jkUnicode.uniScript import get_script
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
categoryName = {
|
|
15
|
+
"Lu": "Letter, Uppercase",
|
|
16
|
+
"Ll": "Letter, Lowercase",
|
|
17
|
+
"Lt": "Letter, Titlecase",
|
|
18
|
+
"Lm": "Letter, Modifier",
|
|
19
|
+
"Lo": "Letter, Other",
|
|
20
|
+
"Mn": "Mark, Nonspacing",
|
|
21
|
+
"Mc": "Mark, Spacing Combining",
|
|
22
|
+
"Me": "Mark, Enclosing",
|
|
23
|
+
"Nd": "Number, Decimal Digit",
|
|
24
|
+
"Nl": "Number, Letter",
|
|
25
|
+
"No": "Number, Other",
|
|
26
|
+
"Pc": "Punctuation, Connector",
|
|
27
|
+
"Pd": "Punctuation, Dash",
|
|
28
|
+
"Ps": "Punctuation, Open",
|
|
29
|
+
"Pe": "Punctuation, Close",
|
|
30
|
+
"Pi": "Punctuation, Initial quote (may behave like Ps or Pe depending on usage)",
|
|
31
|
+
"Pf": "Punctuation, Final quote (may behave like Ps or Pe depending on usage)",
|
|
32
|
+
"Po": "Punctuation, Other",
|
|
33
|
+
"Sm": "Symbol, Math",
|
|
34
|
+
"Sc": "Symbol, Currency",
|
|
35
|
+
"Sk": "Symbol, Modifier",
|
|
36
|
+
"So": "Symbol, Other",
|
|
37
|
+
"Zs": "Separator, Space",
|
|
38
|
+
"Zl": "Separator, Line",
|
|
39
|
+
"Zp": "Separator, Paragraph",
|
|
40
|
+
"Cc": "Other, Control",
|
|
41
|
+
"Cf": "Other, Format",
|
|
42
|
+
"Cs": "Other, Surrogate",
|
|
43
|
+
"Co": "Other, Private Use",
|
|
44
|
+
"Cn": "Other, Not Assigned",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_expanded_glyph_list(
|
|
49
|
+
unicodes: list[int], ui: UniInfo | None = None
|
|
50
|
+
) -> list[tuple[int, str | None]]:
|
|
51
|
+
""" "Expand" or annotate a list of codepoints.
|
|
52
|
+
|
|
53
|
+
For codepoints that have a case mapping (UC or LC), the target codepoint of
|
|
54
|
+
the case mapping will be added to the list. AGLFN glyph names are added to
|
|
55
|
+
the list too, so the returned list contains tuples of `(codepoint,
|
|
56
|
+
glyphname)`, sorted by the codepoint value.
|
|
57
|
+
|
|
58
|
+
:param unicodes: A list of codepoints
|
|
59
|
+
:type unicodes: list
|
|
60
|
+
|
|
61
|
+
:param ui: The UniInfo instance to use. If None, one will be instantiated.
|
|
62
|
+
:type ui: UniInfo"""
|
|
63
|
+
glyphs = []
|
|
64
|
+
if ui is None:
|
|
65
|
+
ui = UniInfo(0)
|
|
66
|
+
for ch in unicodes:
|
|
67
|
+
ui.unicode = ch
|
|
68
|
+
glyphs.append((ch, ui.glyphname))
|
|
69
|
+
if ui.lc_mapping is not None:
|
|
70
|
+
ui.unicode = ui.lc_mapping
|
|
71
|
+
glyphs.append((ui.unicode, ui.glyphname))
|
|
72
|
+
elif ui.uc_mapping is not None:
|
|
73
|
+
ui.unicode = ui.uc_mapping
|
|
74
|
+
glyphs.append((ui.unicode, ui.glyphname))
|
|
75
|
+
return sorted(list(set(glyphs)))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def getUnicodeChar(code: int) -> str:
|
|
79
|
+
"""Return the Unicode character for a Unicode codepoint.
|
|
80
|
+
|
|
81
|
+
:param code: The codepoint
|
|
82
|
+
:type code: int"""
|
|
83
|
+
return chr(code)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class UniInfo:
|
|
87
|
+
"""The main Unicode Info object. It gets its Unicode information from the
|
|
88
|
+
submodules aglfn, uniCase, uniCat, uniDecomposition, uniName, and
|
|
89
|
+
uniRangesBits which are generated from the official Unicode data. You can
|
|
90
|
+
find tools to download and regenerate the data in the `tools` subfolder.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def __init__(self, uni: int | None = None) -> None:
|
|
94
|
+
"""The Unicode Info object is meant to be instantiated once and then
|
|
95
|
+
reused to get information about different codepoints. Avoid to
|
|
96
|
+
instantiate it often, because it is rather expensive.
|
|
97
|
+
|
|
98
|
+
Initialize the Info object with a None e.g. before a loop and then in
|
|
99
|
+
the loop assign the actual codepoints that you want information about by
|
|
100
|
+
setting the `unicode` instance variable. This will automatically update
|
|
101
|
+
the other instance variables with the correct information from the
|
|
102
|
+
Unicode standard.
|
|
103
|
+
|
|
104
|
+
:param uni: The codepoint.
|
|
105
|
+
:type uni: int"""
|
|
106
|
+
self._unicode = None
|
|
107
|
+
if uni is not None:
|
|
108
|
+
self.unicode = uni
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def unicode(self) -> int | None:
|
|
112
|
+
"""The Unicode codepoint. Setting this value will look up and fill the
|
|
113
|
+
other pieces of information, like category, range, decomposition
|
|
114
|
+
mapping, and case mapping."""
|
|
115
|
+
return self._unicode
|
|
116
|
+
|
|
117
|
+
@unicode.setter
|
|
118
|
+
def unicode(self, value: int | None):
|
|
119
|
+
if value == self._unicode:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
self._unicode = value
|
|
123
|
+
|
|
124
|
+
# Invalidate cached properties
|
|
125
|
+
for attr in (
|
|
126
|
+
"block",
|
|
127
|
+
"category",
|
|
128
|
+
"category_short",
|
|
129
|
+
"decomposition_mapping",
|
|
130
|
+
"glyphname",
|
|
131
|
+
"lc_mapping",
|
|
132
|
+
"name",
|
|
133
|
+
"nice_name",
|
|
134
|
+
"script",
|
|
135
|
+
"uc_mapping",
|
|
136
|
+
):
|
|
137
|
+
try:
|
|
138
|
+
delattr(self, attr)
|
|
139
|
+
except AttributeError:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
if self._unicode is None:
|
|
143
|
+
self.category_short = "<undefined>"
|
|
144
|
+
self.category = "<undefined>"
|
|
145
|
+
self.decomposition_mapping = []
|
|
146
|
+
|
|
147
|
+
def __repr__(self) -> str:
|
|
148
|
+
if self.unicode is None:
|
|
149
|
+
s = " Unicode: None"
|
|
150
|
+
else:
|
|
151
|
+
s = " Unicode: 0x{:04X} (dec. {})".format(
|
|
152
|
+
self.unicode,
|
|
153
|
+
self.unicode,
|
|
154
|
+
)
|
|
155
|
+
s += "\n Name: %s" % self.name
|
|
156
|
+
s += "\n Category: {} ({})".format(self.category_short, self.category)
|
|
157
|
+
if self.uc_mapping:
|
|
158
|
+
s += "\n Uppercase: 0x%04X" % self.uc_mapping
|
|
159
|
+
if self.lc_mapping:
|
|
160
|
+
s += "\n Lowercase: 0x%04X" % self.lc_mapping
|
|
161
|
+
if self.decomposition_mapping:
|
|
162
|
+
s += "\nDecomposition: %s" % (
|
|
163
|
+
" ".join(["0x%04X" % m for m in self.decomposition_mapping])
|
|
164
|
+
)
|
|
165
|
+
return s
|
|
166
|
+
|
|
167
|
+
@cached_property
|
|
168
|
+
def block(self) -> str | None:
|
|
169
|
+
"""The name of the block for the current codepoint."""
|
|
170
|
+
return get_block(self._unicode)
|
|
171
|
+
|
|
172
|
+
@cached_property
|
|
173
|
+
def category(self) -> str | None:
|
|
174
|
+
"""The name of the category for the current codepoint."""
|
|
175
|
+
if self._unicode is None:
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
if self.category_short is None:
|
|
179
|
+
return "<undefined>"
|
|
180
|
+
|
|
181
|
+
return categoryName.get(self.category_short, "<undefined>")
|
|
182
|
+
|
|
183
|
+
@cached_property
|
|
184
|
+
def category_short(self) -> str | None:
|
|
185
|
+
"""The short name of the category for the current codepoint."""
|
|
186
|
+
if self._unicode is None:
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
return uniCat.get(self._unicode, "<undefined>")
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def char(self) -> str | None:
|
|
193
|
+
"""The character for the current codepoint."""
|
|
194
|
+
if self.unicode is None:
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
return getUnicodeChar(self.unicode)
|
|
198
|
+
|
|
199
|
+
@char.setter
|
|
200
|
+
def char(self, value: str | None) -> None:
|
|
201
|
+
if value is None:
|
|
202
|
+
self.unicode = None
|
|
203
|
+
else:
|
|
204
|
+
self.unicode = ord(value)
|
|
205
|
+
|
|
206
|
+
@cached_property
|
|
207
|
+
def glyphname(self) -> str | None:
|
|
208
|
+
"""The AGLFN glyph name for the current codepoint."""
|
|
209
|
+
|
|
210
|
+
return getGlyphnameForUnicode(self.unicode)
|
|
211
|
+
|
|
212
|
+
@cached_property
|
|
213
|
+
def name(self) -> str | None:
|
|
214
|
+
"""The Unicode name for the current codepoint."""
|
|
215
|
+
if self._unicode is None:
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
name = uniName.get(self._unicode, None)
|
|
219
|
+
# TODO: Add nicer names based on original Unicode names?
|
|
220
|
+
if name is None:
|
|
221
|
+
if 0xE000 <= self._unicode < 0xF8FF:
|
|
222
|
+
return "<Private Use>"
|
|
223
|
+
if 0xD800 <= self._unicode < 0xDB7F:
|
|
224
|
+
return "<Non Private Use High Surrogate #%i>" % (
|
|
225
|
+
self._unicode - 0xD8000
|
|
226
|
+
)
|
|
227
|
+
if 0xDB80 <= self._unicode < 0xDBFF:
|
|
228
|
+
return "<Private Use High Surrogate #%i>" % (self._unicode - 0xDB80)
|
|
229
|
+
if 0xDC00 <= self._unicode < 0xDFFF:
|
|
230
|
+
return "<Low Surrogate #%i>" % (self._unicode - 0xDC00)
|
|
231
|
+
return "<undefined>"
|
|
232
|
+
return name
|
|
233
|
+
|
|
234
|
+
@cached_property
|
|
235
|
+
def nice_name(self) -> str | None:
|
|
236
|
+
"""A more human-readable Unicode name for the current codepoint."""
|
|
237
|
+
if self.name is None:
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
for transform_function in nice_name_rules:
|
|
241
|
+
result = transform_function(self.name)
|
|
242
|
+
if result:
|
|
243
|
+
return result
|
|
244
|
+
|
|
245
|
+
return self.name.capitalize()
|
|
246
|
+
|
|
247
|
+
@cached_property
|
|
248
|
+
def decomposition_mapping(self) -> list[int]:
|
|
249
|
+
"""The decomposition mapping for the current codepoint."""
|
|
250
|
+
if self._unicode is None:
|
|
251
|
+
return []
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
dc = uniDecompositionMapping[self._unicode]
|
|
255
|
+
except KeyError:
|
|
256
|
+
dc = []
|
|
257
|
+
return dc
|
|
258
|
+
|
|
259
|
+
@cached_property
|
|
260
|
+
def lc_mapping(self) -> int | None:
|
|
261
|
+
"""The lowercase mapping for the current codepoint."""
|
|
262
|
+
if self._unicode is None:
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
return uniLowerCaseMapping.get(self._unicode, None)
|
|
266
|
+
|
|
267
|
+
@cached_property
|
|
268
|
+
def uc_mapping(self) -> int | None:
|
|
269
|
+
"""The uppercase mapping for the current codepoint."""
|
|
270
|
+
if self._unicode is None:
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
return uniUpperCaseMapping.get(self._unicode, None)
|
|
274
|
+
|
|
275
|
+
@cached_property
|
|
276
|
+
def script(self) -> str | None:
|
|
277
|
+
if self._unicode is None:
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
return get_script(self._unicode)
|
jkUnicode/aglfn.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from re import compile
|
|
4
|
+
from .aglfnData import nameToUnicode
|
|
5
|
+
|
|
6
|
+
# Build reverse dictionary
|
|
7
|
+
unicodeToName = {value: key for key, value in nameToUnicode.items()}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def getUnicodeForGlyphname(name: str) -> int | None:
|
|
11
|
+
"""Return the Unicode value as integer or None that is assigned to the
|
|
12
|
+
specified glyph name. It handles AGLFN names, uniXXXX names, uXXXXX names,
|
|
13
|
+
ligature names, variant names, and PUA-encoded ornament names (orn001 -
|
|
14
|
+
orn999, starting at 0xEA01).
|
|
15
|
+
|
|
16
|
+
:param name: The glyph name.
|
|
17
|
+
:type name: str"""
|
|
18
|
+
ornName = compile("^orn[0-9]{3}$")
|
|
19
|
+
length = len(name)
|
|
20
|
+
if "_" in name:
|
|
21
|
+
return None
|
|
22
|
+
elif "." in name[1:]:
|
|
23
|
+
return None
|
|
24
|
+
elif name in nameToUnicode.keys():
|
|
25
|
+
return nameToUnicode[name]
|
|
26
|
+
elif length == 7 and name.startswith("uni"):
|
|
27
|
+
return int(name[3:], 16)
|
|
28
|
+
elif length == 6 and name.startswith("u"):
|
|
29
|
+
try:
|
|
30
|
+
return int(name[1:], 16)
|
|
31
|
+
except ValueError:
|
|
32
|
+
return None
|
|
33
|
+
elif ornName.match(name):
|
|
34
|
+
return 0xEA00 + int(name[3:6])
|
|
35
|
+
else:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def getGlyphnameForUnicode(code: int | None) -> str | None:
|
|
40
|
+
"""Return the name as string or None that is assigned to the specified
|
|
41
|
+
Unicode value.
|
|
42
|
+
|
|
43
|
+
:param code: The codepoint.
|
|
44
|
+
:type code: int"""
|
|
45
|
+
if code is None:
|
|
46
|
+
return None
|
|
47
|
+
elif code in unicodeToName.keys():
|
|
48
|
+
return unicodeToName[code]
|
|
49
|
+
elif code < 0xFFFF:
|
|
50
|
+
return "uni%04X" % code
|
|
51
|
+
else:
|
|
52
|
+
return "u%05X" % code
|