fonttools 4.55.4__cp313-cp313-musllinux_1_2_aarch64.whl → 4.61.1__cp313-cp313-musllinux_1_2_aarch64.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.
- fontTools/__init__.py +1 -1
- fontTools/annotations.py +30 -0
- fontTools/cffLib/CFF2ToCFF.py +65 -10
- fontTools/cffLib/__init__.py +61 -26
- fontTools/cffLib/specializer.py +4 -1
- fontTools/cffLib/transforms.py +11 -6
- fontTools/config/__init__.py +15 -0
- fontTools/cu2qu/cu2qu.c +6567 -5579
- fontTools/cu2qu/cu2qu.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/cu2qu/cu2qu.py +36 -4
- fontTools/cu2qu/ufo.py +14 -0
- fontTools/designspaceLib/__init__.py +8 -3
- fontTools/designspaceLib/statNames.py +14 -7
- fontTools/feaLib/ast.py +24 -15
- fontTools/feaLib/builder.py +139 -66
- fontTools/feaLib/error.py +1 -1
- fontTools/feaLib/lexer.c +7038 -7995
- fontTools/feaLib/lexer.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/feaLib/parser.py +75 -40
- fontTools/feaLib/variableScalar.py +6 -1
- fontTools/fontBuilder.py +50 -44
- fontTools/merge/__init__.py +1 -1
- fontTools/merge/cmap.py +33 -1
- fontTools/merge/tables.py +12 -1
- fontTools/misc/bezierTools.c +14913 -17013
- fontTools/misc/bezierTools.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/misc/bezierTools.py +4 -1
- fontTools/misc/configTools.py +3 -1
- fontTools/misc/enumTools.py +23 -0
- fontTools/misc/etree.py +4 -27
- fontTools/misc/filesystem/__init__.py +68 -0
- fontTools/misc/filesystem/_base.py +134 -0
- fontTools/misc/filesystem/_copy.py +45 -0
- fontTools/misc/filesystem/_errors.py +54 -0
- fontTools/misc/filesystem/_info.py +75 -0
- fontTools/misc/filesystem/_osfs.py +164 -0
- fontTools/misc/filesystem/_path.py +67 -0
- fontTools/misc/filesystem/_subfs.py +92 -0
- fontTools/misc/filesystem/_tempfs.py +34 -0
- fontTools/misc/filesystem/_tools.py +34 -0
- fontTools/misc/filesystem/_walk.py +55 -0
- fontTools/misc/filesystem/_zipfs.py +204 -0
- fontTools/misc/fixedTools.py +1 -1
- fontTools/misc/loggingTools.py +1 -1
- fontTools/misc/psCharStrings.py +17 -2
- fontTools/misc/sstruct.py +2 -6
- fontTools/misc/symfont.py +6 -8
- fontTools/misc/testTools.py +5 -1
- fontTools/misc/textTools.py +4 -2
- fontTools/misc/visitor.py +32 -16
- fontTools/misc/xmlWriter.py +44 -8
- fontTools/mtiLib/__init__.py +1 -3
- fontTools/otlLib/builder.py +402 -155
- fontTools/otlLib/optimize/gpos.py +49 -63
- fontTools/pens/filterPen.py +218 -26
- fontTools/pens/momentsPen.c +5514 -5584
- fontTools/pens/momentsPen.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/pens/pointPen.py +61 -18
- fontTools/pens/roundingPen.py +2 -2
- fontTools/pens/t2CharStringPen.py +31 -11
- fontTools/qu2cu/qu2cu.c +6581 -6168
- fontTools/qu2cu/qu2cu.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/subset/__init__.py +283 -25
- fontTools/subset/svg.py +2 -3
- fontTools/ttLib/__init__.py +4 -0
- fontTools/ttLib/__main__.py +47 -8
- fontTools/ttLib/removeOverlaps.py +7 -5
- fontTools/ttLib/reorderGlyphs.py +8 -7
- fontTools/ttLib/sfnt.py +11 -9
- fontTools/ttLib/tables/D__e_b_g.py +20 -2
- fontTools/ttLib/tables/G_V_A_R_.py +5 -0
- fontTools/ttLib/tables/S__i_l_f.py +2 -2
- fontTools/ttLib/tables/T_S_I__0.py +14 -3
- fontTools/ttLib/tables/T_S_I__1.py +2 -5
- fontTools/ttLib/tables/T_S_I__5.py +18 -7
- fontTools/ttLib/tables/__init__.py +1 -0
- fontTools/ttLib/tables/_a_v_a_r.py +12 -3
- fontTools/ttLib/tables/_c_m_a_p.py +20 -7
- fontTools/ttLib/tables/_c_v_t.py +3 -2
- fontTools/ttLib/tables/_f_p_g_m.py +3 -1
- fontTools/ttLib/tables/_g_l_y_f.py +45 -21
- fontTools/ttLib/tables/_g_v_a_r.py +67 -19
- fontTools/ttLib/tables/_h_d_m_x.py +4 -4
- fontTools/ttLib/tables/_h_m_t_x.py +7 -3
- fontTools/ttLib/tables/_l_o_c_a.py +2 -2
- fontTools/ttLib/tables/_n_a_m_e.py +11 -6
- fontTools/ttLib/tables/_p_o_s_t.py +9 -7
- fontTools/ttLib/tables/otBase.py +5 -12
- fontTools/ttLib/tables/otConverters.py +5 -2
- fontTools/ttLib/tables/otData.py +1 -1
- fontTools/ttLib/tables/otTables.py +33 -30
- fontTools/ttLib/tables/otTraverse.py +2 -1
- fontTools/ttLib/tables/sbixStrike.py +3 -3
- fontTools/ttLib/ttFont.py +666 -120
- fontTools/ttLib/ttGlyphSet.py +0 -10
- fontTools/ttLib/woff2.py +10 -13
- fontTools/ttx.py +13 -1
- fontTools/ufoLib/__init__.py +300 -202
- fontTools/ufoLib/converters.py +103 -30
- fontTools/ufoLib/errors.py +8 -0
- fontTools/ufoLib/etree.py +1 -1
- fontTools/ufoLib/filenames.py +171 -106
- fontTools/ufoLib/glifLib.py +303 -205
- fontTools/ufoLib/kerning.py +98 -48
- fontTools/ufoLib/utils.py +46 -15
- fontTools/ufoLib/validators.py +121 -99
- fontTools/unicodedata/Blocks.py +35 -20
- fontTools/unicodedata/Mirrored.py +446 -0
- fontTools/unicodedata/ScriptExtensions.py +63 -37
- fontTools/unicodedata/Scripts.py +173 -152
- fontTools/unicodedata/__init__.py +10 -2
- fontTools/varLib/__init__.py +198 -109
- fontTools/varLib/avar/__init__.py +0 -0
- fontTools/varLib/avar/__main__.py +72 -0
- fontTools/varLib/avar/build.py +79 -0
- fontTools/varLib/avar/map.py +108 -0
- fontTools/varLib/avar/plan.py +1004 -0
- fontTools/varLib/{avar.py → avar/unbuild.py} +70 -59
- fontTools/varLib/avarPlanner.py +3 -999
- fontTools/varLib/featureVars.py +21 -7
- fontTools/varLib/hvar.py +113 -0
- fontTools/varLib/instancer/__init__.py +180 -65
- fontTools/varLib/interpolatableHelpers.py +3 -0
- fontTools/varLib/iup.c +7564 -6903
- fontTools/varLib/iup.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/varLib/models.py +17 -2
- fontTools/varLib/mutator.py +11 -0
- fontTools/varLib/varStore.py +10 -38
- fontTools/voltLib/__main__.py +206 -0
- fontTools/voltLib/ast.py +4 -0
- fontTools/voltLib/parser.py +16 -8
- fontTools/voltLib/voltToFea.py +347 -166
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/METADATA +269 -1410
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/RECORD +318 -294
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/WHEEL +1 -1
- fonttools-4.61.1.dist-info/licenses/LICENSE.external +388 -0
- {fonttools-4.55.4.data → fonttools-4.61.1.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/entry_points.txt +0 -0
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info/licenses}/LICENSE +0 -0
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/top_level.txt +0 -0
fontTools/ufoLib/converters.py
CHANGED
|
@@ -1,11 +1,42 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Mapping, Any
|
|
4
|
+
from collections.abc import Container
|
|
5
|
+
|
|
6
|
+
from fontTools.annotations import KerningNested
|
|
7
|
+
|
|
1
8
|
"""
|
|
2
|
-
|
|
9
|
+
Functions for converting UFO1 or UFO2 files into UFO3 format.
|
|
10
|
+
|
|
11
|
+
Currently provides functionality for converting kerning rules
|
|
12
|
+
and kerning groups. Conversion is only supported _from_ UFO1
|
|
13
|
+
or UFO2, and _to_ UFO3.
|
|
3
14
|
"""
|
|
4
15
|
|
|
5
16
|
# adapted from the UFO spec
|
|
6
17
|
|
|
7
18
|
|
|
8
|
-
def convertUFO1OrUFO2KerningToUFO3Kerning(
|
|
19
|
+
def convertUFO1OrUFO2KerningToUFO3Kerning(
|
|
20
|
+
kerning: KerningNested, groups: dict[str, list[str]], glyphSet: Container[str] = ()
|
|
21
|
+
) -> tuple[KerningNested, dict[str, list[str]], dict[str, dict[str, str]]]:
|
|
22
|
+
"""Convert kerning data in UFO1 or UFO2 syntax into UFO3 syntax.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
kerning:
|
|
26
|
+
A dictionary containing the kerning rules defined in
|
|
27
|
+
the UFO font, as used in :class:`.UFOReader` objects.
|
|
28
|
+
groups:
|
|
29
|
+
A dictionary containing the groups defined in the UFO
|
|
30
|
+
font, as used in :class:`.UFOReader` objects.
|
|
31
|
+
glyphSet:
|
|
32
|
+
Optional; a set of glyph objects to skip (default: None).
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
1. A dictionary representing the converted kerning data.
|
|
36
|
+
2. A copy of the groups dictionary, with all groups renamed to UFO3 syntax.
|
|
37
|
+
3. A dictionary containing the mapping of old group names to new group names.
|
|
38
|
+
|
|
39
|
+
"""
|
|
9
40
|
# gather known kerning groups based on the prefixes
|
|
10
41
|
firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
|
|
11
42
|
# Make lists of groups referenced in kerning pairs.
|
|
@@ -18,7 +49,7 @@ def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
|
|
|
18
49
|
if not second.startswith("public.kern2."):
|
|
19
50
|
secondReferencedGroups.add(second)
|
|
20
51
|
# Create new names for these groups.
|
|
21
|
-
firstRenamedGroups = {}
|
|
52
|
+
firstRenamedGroups: dict[str, str] = {}
|
|
22
53
|
for first in firstReferencedGroups:
|
|
23
54
|
# Make a list of existing group names.
|
|
24
55
|
existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys())
|
|
@@ -30,7 +61,7 @@ def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
|
|
|
30
61
|
newName = makeUniqueGroupName(newName, existingGroupNames)
|
|
31
62
|
# Store for use later.
|
|
32
63
|
firstRenamedGroups[first] = newName
|
|
33
|
-
secondRenamedGroups = {}
|
|
64
|
+
secondRenamedGroups: dict[str, str] = {}
|
|
34
65
|
for second in secondReferencedGroups:
|
|
35
66
|
# Make a list of existing group names.
|
|
36
67
|
existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys())
|
|
@@ -62,36 +93,55 @@ def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
|
|
|
62
93
|
return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups)
|
|
63
94
|
|
|
64
95
|
|
|
65
|
-
def findKnownKerningGroups(groups):
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
In some cases not all kerning groups will be referenced
|
|
69
|
-
by the kerning pairs. The algorithm for locating
|
|
70
|
-
in convertUFO1OrUFO2KerningToUFO3Kerning will
|
|
71
|
-
unreferenced groups. By scanning for known prefixes
|
|
96
|
+
def findKnownKerningGroups(groups: Mapping[str, Any]) -> tuple[set[str], set[str]]:
|
|
97
|
+
"""Find all kerning groups in a UFO1 or UFO2 font that use known prefixes.
|
|
98
|
+
|
|
99
|
+
In some cases, not all kerning groups will be referenced
|
|
100
|
+
by the kerning pairs in a UFO. The algorithm for locating
|
|
101
|
+
groups in :func:`convertUFO1OrUFO2KerningToUFO3Kerning` will
|
|
102
|
+
miss these unreferenced groups. By scanning for known prefixes,
|
|
72
103
|
this function will catch all of the prefixed groups.
|
|
73
104
|
|
|
74
|
-
|
|
105
|
+
The prefixes and sides by this function are:
|
|
106
|
+
|
|
75
107
|
@MMK_L_ - side 1
|
|
76
108
|
@MMK_R_ - side 2
|
|
77
109
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
110
|
+
as defined in the UFO1 specification.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
groups:
|
|
114
|
+
A dictionary containing the groups defined in the UFO
|
|
115
|
+
font, as read by :class:`.UFOReader`.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Two sets; the first containing the names of all
|
|
119
|
+
first-side kerning groups identified in the ``groups``
|
|
120
|
+
dictionary, and the second containing the names of all
|
|
121
|
+
second-side kerning groups identified.
|
|
122
|
+
|
|
123
|
+
"First-side" and "second-side" are with respect to the
|
|
124
|
+
writing direction of the script.
|
|
125
|
+
|
|
126
|
+
Example::
|
|
127
|
+
|
|
128
|
+
>>> testGroups = {
|
|
129
|
+
... "@MMK_L_1" : None,
|
|
130
|
+
... "@MMK_L_2" : None,
|
|
131
|
+
... "@MMK_L_3" : None,
|
|
132
|
+
... "@MMK_R_1" : None,
|
|
133
|
+
... "@MMK_R_2" : None,
|
|
134
|
+
... "@MMK_R_3" : None,
|
|
135
|
+
... "@MMK_l_1" : None,
|
|
136
|
+
... "@MMK_r_1" : None,
|
|
137
|
+
... "@MMK_X_1" : None,
|
|
138
|
+
... "foo" : None,
|
|
139
|
+
... }
|
|
140
|
+
>>> first, second = findKnownKerningGroups(testGroups)
|
|
141
|
+
>>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
|
|
142
|
+
True
|
|
143
|
+
>>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
|
|
144
|
+
True
|
|
95
145
|
"""
|
|
96
146
|
knownFirstGroupPrefixes = ["@MMK_L_"]
|
|
97
147
|
knownSecondGroupPrefixes = ["@MMK_R_"]
|
|
@@ -109,7 +159,28 @@ def findKnownKerningGroups(groups):
|
|
|
109
159
|
return firstGroups, secondGroups
|
|
110
160
|
|
|
111
161
|
|
|
112
|
-
def makeUniqueGroupName(name, groupNames, counter=0):
|
|
162
|
+
def makeUniqueGroupName(name: str, groupNames: list[str], counter: int = 0) -> str:
|
|
163
|
+
"""Make a kerning group name that will be unique within the set of group names.
|
|
164
|
+
|
|
165
|
+
If the requested kerning group name already exists within the set, this
|
|
166
|
+
will return a new name by adding an incremented counter to the end
|
|
167
|
+
of the requested name.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
name:
|
|
171
|
+
The requested kerning group name.
|
|
172
|
+
groupNames:
|
|
173
|
+
A list of the existing kerning group names.
|
|
174
|
+
counter:
|
|
175
|
+
Optional; a counter of group names already seen (default: 0). If
|
|
176
|
+
:attr:`.counter` is not provided, the function will recurse,
|
|
177
|
+
incrementing the value of :attr:`.counter` until it finds the
|
|
178
|
+
first unused ``name+counter`` combination, and return that result.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
A unique kerning group name composed of the requested name suffixed
|
|
182
|
+
by the smallest available integer counter.
|
|
183
|
+
"""
|
|
113
184
|
# Add a number to the name if the counter is higher than zero.
|
|
114
185
|
newName = name
|
|
115
186
|
if counter > 0:
|
|
@@ -123,6 +194,8 @@ def makeUniqueGroupName(name, groupNames, counter=0):
|
|
|
123
194
|
|
|
124
195
|
def test():
|
|
125
196
|
"""
|
|
197
|
+
Tests for :func:`.convertUFO1OrUFO2KerningToUFO3Kerning`.
|
|
198
|
+
|
|
126
199
|
No known prefixes.
|
|
127
200
|
|
|
128
201
|
>>> testKerning = {
|
fontTools/ufoLib/errors.py
CHANGED
|
@@ -10,6 +10,14 @@ class UnsupportedUFOFormat(UFOLibError):
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class GlifLibError(UFOLibError):
|
|
13
|
+
"""An error raised by glifLib.
|
|
14
|
+
|
|
15
|
+
This class is a loose backport of PEP 678, adding a :attr:`.note`
|
|
16
|
+
attribute that can hold additional context for errors encountered.
|
|
17
|
+
|
|
18
|
+
It will be maintained until only Python 3.11-and-later are supported.
|
|
19
|
+
"""
|
|
20
|
+
|
|
13
21
|
def _add_note(self, note: str) -> None:
|
|
14
22
|
# Loose backport of PEP 678 until we only support Python 3.11+, used for
|
|
15
23
|
# adding additional context to errors.
|
fontTools/ufoLib/etree.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""DEPRECATED - This module is kept here only as a backward compatibility shim
|
|
2
|
-
for the old ufoLib.etree module, which was moved to fontTools.misc.etree
|
|
2
|
+
for the old ufoLib.etree module, which was moved to :mod:`fontTools.misc.etree`.
|
|
3
3
|
Please use the latter instead.
|
|
4
4
|
"""
|
|
5
5
|
|
fontTools/ufoLib/filenames.py
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
|
|
1
5
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
6
|
+
Convert user-provided internal UFO names to spec-compliant filenames.
|
|
7
|
+
|
|
8
|
+
This module implements the algorithm for converting between a "user name" -
|
|
9
|
+
something that a user can choose arbitrarily inside a font editor - and a file
|
|
10
|
+
name suitable for use in a wide range of operating systems and filesystems.
|
|
11
|
+
|
|
12
|
+
The `UFO 3 specification <http://unifiedfontobject.org/versions/ufo3/conventions/>`_
|
|
13
|
+
provides an example of an algorithm for such conversion, which avoids illegal
|
|
14
|
+
characters, reserved file names, ambiguity between upper- and lower-case
|
|
15
|
+
characters, and clashes with existing files.
|
|
16
|
+
|
|
17
|
+
This code was originally copied from
|
|
18
|
+
`ufoLib <https://github.com/unified-font-object/ufoLib/blob/8747da7/Lib/ufoLib/filenames.py>`_
|
|
19
|
+
by Tal Leming and is copyright (c) 2005-2016, The RoboFab Developers:
|
|
20
|
+
|
|
21
|
+
- Erik van Blokland
|
|
22
|
+
- Tal Leming
|
|
23
|
+
- Just van Rossum
|
|
4
24
|
"""
|
|
5
25
|
|
|
6
26
|
# Restrictions are taken mostly from
|
|
@@ -11,7 +31,7 @@ This was taken from the UFO 3 spec.
|
|
|
11
31
|
# inclusive.
|
|
12
32
|
# 3. Various characters that (mostly) Windows and POSIX-y filesystems don't
|
|
13
33
|
# allow, plus "(" and ")", as per the specification.
|
|
14
|
-
illegalCharacters = {
|
|
34
|
+
illegalCharacters: set[str] = {
|
|
15
35
|
"\x00",
|
|
16
36
|
"\x01",
|
|
17
37
|
"\x02",
|
|
@@ -60,7 +80,7 @@ illegalCharacters = {
|
|
|
60
80
|
"|",
|
|
61
81
|
"\x7f",
|
|
62
82
|
}
|
|
63
|
-
reservedFileNames = {
|
|
83
|
+
reservedFileNames: set[str] = {
|
|
64
84
|
"aux",
|
|
65
85
|
"clock$",
|
|
66
86
|
"com1",
|
|
@@ -85,61 +105,79 @@ reservedFileNames = {
|
|
|
85
105
|
"nul",
|
|
86
106
|
"prn",
|
|
87
107
|
}
|
|
88
|
-
maxFileNameLength = 255
|
|
108
|
+
maxFileNameLength: int = 255
|
|
89
109
|
|
|
90
110
|
|
|
91
111
|
class NameTranslationError(Exception):
|
|
92
112
|
pass
|
|
93
113
|
|
|
94
114
|
|
|
95
|
-
def userNameToFileName(
|
|
96
|
-
"""
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
115
|
+
def userNameToFileName(
|
|
116
|
+
userName: str, existing: Iterable[str] = (), prefix: str = "", suffix: str = ""
|
|
117
|
+
) -> str:
|
|
118
|
+
"""Converts from a user name to a file name.
|
|
119
|
+
|
|
120
|
+
Takes care to avoid illegal characters, reserved file names, ambiguity between
|
|
121
|
+
upper- and lower-case characters, and clashes with existing files.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
userName (str): The input file name.
|
|
125
|
+
existing: A case-insensitive list of all existing file names.
|
|
126
|
+
prefix: Prefix to be prepended to the file name.
|
|
127
|
+
suffix: Suffix to be appended to the file name.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
A suitable filename.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
NameTranslationError: If no suitable name could be generated.
|
|
134
|
+
|
|
135
|
+
Examples::
|
|
136
|
+
|
|
137
|
+
>>> userNameToFileName("a") == "a"
|
|
138
|
+
True
|
|
139
|
+
>>> userNameToFileName("A") == "A_"
|
|
140
|
+
True
|
|
141
|
+
>>> userNameToFileName("AE") == "A_E_"
|
|
142
|
+
True
|
|
143
|
+
>>> userNameToFileName("Ae") == "A_e"
|
|
144
|
+
True
|
|
145
|
+
>>> userNameToFileName("ae") == "ae"
|
|
146
|
+
True
|
|
147
|
+
>>> userNameToFileName("aE") == "aE_"
|
|
148
|
+
True
|
|
149
|
+
>>> userNameToFileName("a.alt") == "a.alt"
|
|
150
|
+
True
|
|
151
|
+
>>> userNameToFileName("A.alt") == "A_.alt"
|
|
152
|
+
True
|
|
153
|
+
>>> userNameToFileName("A.Alt") == "A_.A_lt"
|
|
154
|
+
True
|
|
155
|
+
>>> userNameToFileName("A.aLt") == "A_.aL_t"
|
|
156
|
+
True
|
|
157
|
+
>>> userNameToFileName(u"A.alT") == "A_.alT_"
|
|
158
|
+
True
|
|
159
|
+
>>> userNameToFileName("T_H") == "T__H_"
|
|
160
|
+
True
|
|
161
|
+
>>> userNameToFileName("T_h") == "T__h"
|
|
162
|
+
True
|
|
163
|
+
>>> userNameToFileName("t_h") == "t_h"
|
|
164
|
+
True
|
|
165
|
+
>>> userNameToFileName("F_F_I") == "F__F__I_"
|
|
166
|
+
True
|
|
167
|
+
>>> userNameToFileName("f_f_i") == "f_f_i"
|
|
168
|
+
True
|
|
169
|
+
>>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
|
|
170
|
+
True
|
|
171
|
+
>>> userNameToFileName(".notdef") == "_notdef"
|
|
172
|
+
True
|
|
173
|
+
>>> userNameToFileName("con") == "_con"
|
|
174
|
+
True
|
|
175
|
+
>>> userNameToFileName("CON") == "C_O_N_"
|
|
176
|
+
True
|
|
177
|
+
>>> userNameToFileName("con.alt") == "_con.alt"
|
|
178
|
+
True
|
|
179
|
+
>>> userNameToFileName("alt.con") == "alt._con"
|
|
180
|
+
True
|
|
143
181
|
"""
|
|
144
182
|
# the incoming name must be a string
|
|
145
183
|
if not isinstance(userName, str):
|
|
@@ -180,34 +218,45 @@ def userNameToFileName(userName: str, existing=(), prefix="", suffix=""):
|
|
|
180
218
|
return fullName
|
|
181
219
|
|
|
182
220
|
|
|
183
|
-
def handleClash1(
|
|
184
|
-
"""
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
221
|
+
def handleClash1(
|
|
222
|
+
userName: str, existing: Iterable[str] = [], prefix: str = "", suffix: str = ""
|
|
223
|
+
) -> str:
|
|
224
|
+
"""A helper function that resolves collisions with existing names when choosing a filename.
|
|
225
|
+
|
|
226
|
+
This function attempts to append an unused integer counter to the filename.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
userName (str): The input file name.
|
|
230
|
+
existing: A case-insensitive list of all existing file names.
|
|
231
|
+
prefix: Prefix to be prepended to the file name.
|
|
232
|
+
suffix: Suffix to be appended to the file name.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
A suitable filename.
|
|
236
|
+
|
|
237
|
+
>>> prefix = ("0" * 5) + "."
|
|
238
|
+
>>> suffix = "." + ("0" * 10)
|
|
239
|
+
>>> existing = ["a" * 5]
|
|
240
|
+
|
|
241
|
+
>>> e = list(existing)
|
|
242
|
+
>>> handleClash1(userName="A" * 5, existing=e,
|
|
243
|
+
... prefix=prefix, suffix=suffix) == (
|
|
244
|
+
... '00000.AAAAA000000000000001.0000000000')
|
|
245
|
+
True
|
|
246
|
+
|
|
247
|
+
>>> e = list(existing)
|
|
248
|
+
>>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
|
|
249
|
+
>>> handleClash1(userName="A" * 5, existing=e,
|
|
250
|
+
... prefix=prefix, suffix=suffix) == (
|
|
251
|
+
... '00000.AAAAA000000000000002.0000000000')
|
|
252
|
+
True
|
|
253
|
+
|
|
254
|
+
>>> e = list(existing)
|
|
255
|
+
>>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
|
|
256
|
+
>>> handleClash1(userName="A" * 5, existing=e,
|
|
257
|
+
... prefix=prefix, suffix=suffix) == (
|
|
258
|
+
... '00000.AAAAA000000000000001.0000000000')
|
|
259
|
+
True
|
|
211
260
|
"""
|
|
212
261
|
# if the prefix length + user name length + suffix length + 15 is at
|
|
213
262
|
# or past the maximum length, silce 15 characters off of the user name
|
|
@@ -237,31 +286,47 @@ def handleClash1(userName, existing=[], prefix="", suffix=""):
|
|
|
237
286
|
return finalName
|
|
238
287
|
|
|
239
288
|
|
|
240
|
-
def handleClash2(
|
|
241
|
-
"""
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
289
|
+
def handleClash2(
|
|
290
|
+
existing: Iterable[str] = [], prefix: str = "", suffix: str = ""
|
|
291
|
+
) -> str:
|
|
292
|
+
"""A helper function that resolves collisions with existing names when choosing a filename.
|
|
293
|
+
|
|
294
|
+
This function is a fallback to :func:`handleClash1`. It attempts to append an unused integer counter to the filename.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
userName (str): The input file name.
|
|
298
|
+
existing: A case-insensitive list of all existing file names.
|
|
299
|
+
prefix: Prefix to be prepended to the file name.
|
|
300
|
+
suffix: Suffix to be appended to the file name.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
A suitable filename.
|
|
304
|
+
|
|
305
|
+
Raises:
|
|
306
|
+
NameTranslationError: If no suitable name could be generated.
|
|
307
|
+
|
|
308
|
+
Examples::
|
|
309
|
+
|
|
310
|
+
>>> prefix = ("0" * 5) + "."
|
|
311
|
+
>>> suffix = "." + ("0" * 10)
|
|
312
|
+
>>> existing = [prefix + str(i) + suffix for i in range(100)]
|
|
313
|
+
|
|
314
|
+
>>> e = list(existing)
|
|
315
|
+
>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
|
|
316
|
+
... '00000.100.0000000000')
|
|
317
|
+
True
|
|
318
|
+
|
|
319
|
+
>>> e = list(existing)
|
|
320
|
+
>>> e.remove(prefix + "1" + suffix)
|
|
321
|
+
>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
|
|
322
|
+
... '00000.1.0000000000')
|
|
323
|
+
True
|
|
324
|
+
|
|
325
|
+
>>> e = list(existing)
|
|
326
|
+
>>> e.remove(prefix + "2" + suffix)
|
|
327
|
+
>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
|
|
328
|
+
... '00000.2.0000000000')
|
|
329
|
+
True
|
|
265
330
|
"""
|
|
266
331
|
# calculate the longest possible string
|
|
267
332
|
maxLength = maxFileNameLength - len(prefix) - len(suffix)
|