fonttools 4.57.0__cp39-cp39-win_amd64.whl → 4.58.1__cp39-cp39-win_amd64.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.

Potentially problematic release.


This version of fonttools might be problematic. Click here for more details.

Files changed (67) hide show
  1. fontTools/__init__.py +1 -1
  2. fontTools/cffLib/__init__.py +61 -26
  3. fontTools/cffLib/specializer.py +4 -1
  4. fontTools/cu2qu/cu2qu.cp39-win_amd64.pyd +0 -0
  5. fontTools/designspaceLib/statNames.py +14 -7
  6. fontTools/feaLib/ast.py +12 -9
  7. fontTools/feaLib/builder.py +75 -49
  8. fontTools/feaLib/lexer.cp39-win_amd64.pyd +0 -0
  9. fontTools/feaLib/parser.py +1 -39
  10. fontTools/fontBuilder.py +6 -0
  11. fontTools/merge/cmap.py +33 -1
  12. fontTools/merge/tables.py +12 -1
  13. fontTools/misc/bezierTools.cp39-win_amd64.pyd +0 -0
  14. fontTools/misc/etree.py +4 -27
  15. fontTools/misc/loggingTools.py +1 -1
  16. fontTools/misc/symfont.py +6 -8
  17. fontTools/mtiLib/__init__.py +1 -3
  18. fontTools/otlLib/builder.py +359 -145
  19. fontTools/otlLib/optimize/gpos.py +42 -62
  20. fontTools/pens/momentsPen.cp39-win_amd64.pyd +0 -0
  21. fontTools/pens/pointPen.py +21 -12
  22. fontTools/pens/t2CharStringPen.py +31 -11
  23. fontTools/qu2cu/qu2cu.cp39-win_amd64.pyd +0 -0
  24. fontTools/subset/__init__.py +12 -1
  25. fontTools/ttLib/tables/G_V_A_R_.py +5 -0
  26. fontTools/ttLib/tables/T_S_I__0.py +14 -3
  27. fontTools/ttLib/tables/T_S_I__5.py +16 -5
  28. fontTools/ttLib/tables/__init__.py +1 -0
  29. fontTools/ttLib/tables/_c_v_t.py +2 -0
  30. fontTools/ttLib/tables/_f_p_g_m.py +3 -1
  31. fontTools/ttLib/tables/_g_l_y_f.py +2 -6
  32. fontTools/ttLib/tables/_g_v_a_r.py +58 -15
  33. fontTools/ttLib/tables/_p_o_s_t.py +5 -2
  34. fontTools/ttLib/tables/otBase.py +1 -0
  35. fontTools/ufoLib/__init__.py +3 -3
  36. fontTools/ufoLib/converters.py +89 -25
  37. fontTools/ufoLib/errors.py +8 -0
  38. fontTools/ufoLib/etree.py +1 -1
  39. fontTools/ufoLib/filenames.py +155 -100
  40. fontTools/ufoLib/glifLib.py +9 -2
  41. fontTools/ufoLib/kerning.py +66 -36
  42. fontTools/ufoLib/utils.py +5 -2
  43. fontTools/unicodedata/Mirrored.py +446 -0
  44. fontTools/unicodedata/__init__.py +6 -2
  45. fontTools/varLib/__init__.py +20 -6
  46. fontTools/varLib/featureVars.py +13 -7
  47. fontTools/varLib/hvar.py +1 -1
  48. fontTools/varLib/instancer/__init__.py +14 -5
  49. fontTools/varLib/iup.cp39-win_amd64.pyd +0 -0
  50. fontTools/voltLib/__main__.py +206 -0
  51. fontTools/voltLib/ast.py +4 -0
  52. fontTools/voltLib/parser.py +16 -8
  53. fontTools/voltLib/voltToFea.py +347 -166
  54. {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/METADATA +64 -11
  55. {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/RECORD +61 -63
  56. {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/WHEEL +1 -1
  57. fonttools-4.58.1.dist-info/licenses/LICENSE.external +359 -0
  58. fontTools/cu2qu/cu2qu.c +0 -14829
  59. fontTools/feaLib/lexer.c +0 -17986
  60. fontTools/misc/bezierTools.c +0 -41831
  61. fontTools/pens/momentsPen.c +0 -13448
  62. fontTools/qu2cu/qu2cu.c +0 -16269
  63. fontTools/varLib/iup.c +0 -19154
  64. {fonttools-4.57.0.data → fonttools-4.58.1.data}/data/share/man/man1/ttx.1 +0 -0
  65. {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/entry_points.txt +0 -0
  66. {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/licenses/LICENSE +0 -0
  67. {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/top_level.txt +0 -0
@@ -122,13 +122,16 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
122
122
  glyphName = psName = self.glyphOrder[i]
123
123
  if glyphName == "":
124
124
  glyphName = "glyph%.5d" % i
125
+
125
126
  if glyphName in allNames:
126
127
  # make up a new glyphName that's unique
127
128
  n = allNames[glyphName]
128
- while (glyphName + "#" + str(n)) in allNames:
129
+ # check if the exists in any of the seen names or later ones
130
+ names = set(allNames.keys()) | set(self.glyphOrder)
131
+ while (glyphName + "." + str(n)) in names:
129
132
  n += 1
130
133
  allNames[glyphName] = n + 1
131
- glyphName = glyphName + "#" + str(n)
134
+ glyphName = glyphName + "." + str(n)
132
135
 
133
136
  self.glyphOrder[i] = glyphName
134
137
  allNames[glyphName] = 1
@@ -398,6 +398,7 @@ class OTTableWriter(object):
398
398
  self.localState = localState
399
399
  self.tableTag = tableTag
400
400
  self.parent = None
401
+ self.name = "<none>"
401
402
 
402
403
  def __setitem__(self, name, value):
403
404
  state = self.localState.copy() if self.localState else dict()
@@ -204,7 +204,7 @@ class UFOReader(_UFOBaseIO):
204
204
  """Read the various components of a .ufo.
205
205
 
206
206
  Attributes:
207
- path: An `os.PathLike` object pointing to the .ufo.
207
+ path: An :class:`os.PathLike` object pointing to the .ufo.
208
208
  validate: A boolean indicating if the data read should be
209
209
  validated. Defaults to `True`.
210
210
 
@@ -654,7 +654,7 @@ class UFOReader(_UFOBaseIO):
654
654
  The returned string is empty if the file is missing.
655
655
  """
656
656
  try:
657
- with self.fs.open(FEATURES_FILENAME, "r", encoding="utf-8") as f:
657
+ with self.fs.open(FEATURES_FILENAME, "r", encoding="utf-8-sig") as f:
658
658
  return f.read()
659
659
  except fs.errors.ResourceNotFound:
660
660
  return ""
@@ -891,7 +891,7 @@ class UFOWriter(UFOReader):
891
891
  """Write the various components of a .ufo.
892
892
 
893
893
  Attributes:
894
- path: An `os.PathLike` object pointing to the .ufo.
894
+ path: An :class:`os.PathLike` object pointing to the .ufo.
895
895
  formatVersion: the UFO format version as a tuple of integers (major, minor),
896
896
  or as a single integer for the major digit only (minor is implied to be 0).
897
897
  By default, the latest formatVersion will be used; currently it is 3.0,
@@ -1,11 +1,33 @@
1
1
  """
2
- Conversion functions.
2
+ Functions for converting UFO1 or UFO2 files into UFO3 format.
3
+
4
+ Currently provides functionality for converting kerning rules
5
+ and kerning groups. Conversion is only supported _from_ UFO1
6
+ or UFO2, and _to_ UFO3.
3
7
  """
4
8
 
5
9
  # adapted from the UFO spec
6
10
 
7
11
 
8
12
  def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
13
+ """Convert kerning data in UFO1 or UFO2 syntax into UFO3 syntax.
14
+
15
+ Args:
16
+ kerning:
17
+ A dictionary containing the kerning rules defined in
18
+ the UFO font, as used in :class:`.UFOReader` objects.
19
+ groups:
20
+ A dictionary containing the groups defined in the UFO
21
+ font, as used in :class:`.UFOReader` objects.
22
+ glyphSet:
23
+ Optional; a set of glyph objects to skip (default: None).
24
+
25
+ Returns:
26
+ 1. A dictionary representing the converted kerning data.
27
+ 2. A copy of the groups dictionary, with all groups renamed to UFO3 syntax.
28
+ 3. A dictionary containing the mapping of old group names to new group names.
29
+
30
+ """
9
31
  # gather known kerning groups based on the prefixes
10
32
  firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
11
33
  # Make lists of groups referenced in kerning pairs.
@@ -63,35 +85,54 @@ def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
63
85
 
64
86
 
65
87
  def findKnownKerningGroups(groups):
66
- """
67
- This will find kerning groups with known prefixes.
68
- In some cases not all kerning groups will be referenced
69
- by the kerning pairs. The algorithm for locating groups
70
- in convertUFO1OrUFO2KerningToUFO3Kerning will miss these
71
- unreferenced groups. By scanning for known prefixes
88
+ """Find all kerning groups in a UFO1 or UFO2 font that use known prefixes.
89
+
90
+ In some cases, not all kerning groups will be referenced
91
+ by the kerning pairs in a UFO. The algorithm for locating
92
+ groups in :func:`convertUFO1OrUFO2KerningToUFO3Kerning` will
93
+ miss these unreferenced groups. By scanning for known prefixes,
72
94
  this function will catch all of the prefixed groups.
73
95
 
74
- These are the prefixes and sides that are handled:
96
+ The prefixes and sides by this function are:
97
+
75
98
  @MMK_L_ - side 1
76
99
  @MMK_R_ - side 2
77
100
 
78
- >>> testGroups = {
79
- ... "@MMK_L_1" : None,
80
- ... "@MMK_L_2" : None,
81
- ... "@MMK_L_3" : None,
82
- ... "@MMK_R_1" : None,
83
- ... "@MMK_R_2" : None,
84
- ... "@MMK_R_3" : None,
85
- ... "@MMK_l_1" : None,
86
- ... "@MMK_r_1" : None,
87
- ... "@MMK_X_1" : None,
88
- ... "foo" : None,
89
- ... }
90
- >>> first, second = findKnownKerningGroups(testGroups)
91
- >>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
92
- True
93
- >>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
94
- True
101
+ as defined in the UFO1 specification.
102
+
103
+ Args:
104
+ groups:
105
+ A dictionary containing the groups defined in the UFO
106
+ font, as read by :class:`.UFOReader`.
107
+
108
+ Returns:
109
+ Two sets; the first containing the names of all
110
+ first-side kerning groups identified in the ``groups``
111
+ dictionary, and the second containing the names of all
112
+ second-side kerning groups identified.
113
+
114
+ "First-side" and "second-side" are with respect to the
115
+ writing direction of the script.
116
+
117
+ Example::
118
+
119
+ >>> testGroups = {
120
+ ... "@MMK_L_1" : None,
121
+ ... "@MMK_L_2" : None,
122
+ ... "@MMK_L_3" : None,
123
+ ... "@MMK_R_1" : None,
124
+ ... "@MMK_R_2" : None,
125
+ ... "@MMK_R_3" : None,
126
+ ... "@MMK_l_1" : None,
127
+ ... "@MMK_r_1" : None,
128
+ ... "@MMK_X_1" : None,
129
+ ... "foo" : None,
130
+ ... }
131
+ >>> first, second = findKnownKerningGroups(testGroups)
132
+ >>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
133
+ True
134
+ >>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
135
+ True
95
136
  """
96
137
  knownFirstGroupPrefixes = ["@MMK_L_"]
97
138
  knownSecondGroupPrefixes = ["@MMK_R_"]
@@ -110,6 +151,27 @@ def findKnownKerningGroups(groups):
110
151
 
111
152
 
112
153
  def makeUniqueGroupName(name, groupNames, counter=0):
154
+ """Make a kerning group name that will be unique within the set of group names.
155
+
156
+ If the requested kerning group name already exists within the set, this
157
+ will return a new name by adding an incremented counter to the end
158
+ of the requested name.
159
+
160
+ Args:
161
+ name:
162
+ The requested kerning group name.
163
+ groupNames:
164
+ A list of the existing kerning group names.
165
+ counter:
166
+ Optional; a counter of group names already seen (default: 0). If
167
+ :attr:`.counter` is not provided, the function will recurse,
168
+ incrementing the value of :attr:`.counter` until it finds the
169
+ first unused ``name+counter`` combination, and return that result.
170
+
171
+ Returns:
172
+ A unique kerning group name composed of the requested name suffixed
173
+ by the smallest available integer counter.
174
+ """
113
175
  # Add a number to the name if the counter is higher than zero.
114
176
  newName = name
115
177
  if counter > 0:
@@ -123,6 +185,8 @@ def makeUniqueGroupName(name, groupNames, counter=0):
123
185
 
124
186
  def test():
125
187
  """
188
+ Tests for :func:`.convertUFO1OrUFO2KerningToUFO3Kerning`.
189
+
126
190
  No known prefixes.
127
191
 
128
192
  >>> testKerning = {
@@ -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
 
@@ -1,6 +1,22 @@
1
1
  """
2
- User name to file name conversion.
3
- This was taken from the UFO 3 spec.
2
+ Convert user-provided internal UFO names to spec-compliant filenames.
3
+
4
+ This module implements the algorithm for converting between a "user name" -
5
+ something that a user can choose arbitrarily inside a font editor - and a file
6
+ name suitable for use in a wide range of operating systems and filesystems.
7
+
8
+ The `UFO 3 specification <http://unifiedfontobject.org/versions/ufo3/conventions/>`_
9
+ provides an example of an algorithm for such conversion, which avoids illegal
10
+ characters, reserved file names, ambiguity between upper- and lower-case
11
+ characters, and clashes with existing files.
12
+
13
+ This code was originally copied from
14
+ `ufoLib <https://github.com/unified-font-object/ufoLib/blob/8747da7/Lib/ufoLib/filenames.py>`_
15
+ by Tal Leming and is copyright (c) 2005-2016, The RoboFab Developers:
16
+
17
+ - Erik van Blokland
18
+ - Tal Leming
19
+ - Just van Rossum
4
20
  """
5
21
 
6
22
  # Restrictions are taken mostly from
@@ -93,53 +109,69 @@ class NameTranslationError(Exception):
93
109
 
94
110
 
95
111
  def userNameToFileName(userName: str, existing=(), prefix="", suffix=""):
96
- """
97
- `existing` should be a set-like object.
98
-
99
- >>> userNameToFileName("a") == "a"
100
- True
101
- >>> userNameToFileName("A") == "A_"
102
- True
103
- >>> userNameToFileName("AE") == "A_E_"
104
- True
105
- >>> userNameToFileName("Ae") == "A_e"
106
- True
107
- >>> userNameToFileName("ae") == "ae"
108
- True
109
- >>> userNameToFileName("aE") == "aE_"
110
- True
111
- >>> userNameToFileName("a.alt") == "a.alt"
112
- True
113
- >>> userNameToFileName("A.alt") == "A_.alt"
114
- True
115
- >>> userNameToFileName("A.Alt") == "A_.A_lt"
116
- True
117
- >>> userNameToFileName("A.aLt") == "A_.aL_t"
118
- True
119
- >>> userNameToFileName(u"A.alT") == "A_.alT_"
120
- True
121
- >>> userNameToFileName("T_H") == "T__H_"
122
- True
123
- >>> userNameToFileName("T_h") == "T__h"
124
- True
125
- >>> userNameToFileName("t_h") == "t_h"
126
- True
127
- >>> userNameToFileName("F_F_I") == "F__F__I_"
128
- True
129
- >>> userNameToFileName("f_f_i") == "f_f_i"
130
- True
131
- >>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
132
- True
133
- >>> userNameToFileName(".notdef") == "_notdef"
134
- True
135
- >>> userNameToFileName("con") == "_con"
136
- True
137
- >>> userNameToFileName("CON") == "C_O_N_"
138
- True
139
- >>> userNameToFileName("con.alt") == "_con.alt"
140
- True
141
- >>> userNameToFileName("alt.con") == "alt._con"
142
- True
112
+ """Converts from a user name to a file name.
113
+
114
+ Takes care to avoid illegal characters, reserved file names, ambiguity between
115
+ upper- and lower-case characters, and clashes with existing files.
116
+
117
+ Args:
118
+ userName (str): The input file name.
119
+ existing: A case-insensitive list of all existing file names.
120
+ prefix: Prefix to be prepended to the file name.
121
+ suffix: Suffix to be appended to the file name.
122
+
123
+ Returns:
124
+ A suitable filename.
125
+
126
+ Raises:
127
+ NameTranslationError: If no suitable name could be generated.
128
+
129
+ Examples::
130
+
131
+ >>> userNameToFileName("a") == "a"
132
+ True
133
+ >>> userNameToFileName("A") == "A_"
134
+ True
135
+ >>> userNameToFileName("AE") == "A_E_"
136
+ True
137
+ >>> userNameToFileName("Ae") == "A_e"
138
+ True
139
+ >>> userNameToFileName("ae") == "ae"
140
+ True
141
+ >>> userNameToFileName("aE") == "aE_"
142
+ True
143
+ >>> userNameToFileName("a.alt") == "a.alt"
144
+ True
145
+ >>> userNameToFileName("A.alt") == "A_.alt"
146
+ True
147
+ >>> userNameToFileName("A.Alt") == "A_.A_lt"
148
+ True
149
+ >>> userNameToFileName("A.aLt") == "A_.aL_t"
150
+ True
151
+ >>> userNameToFileName(u"A.alT") == "A_.alT_"
152
+ True
153
+ >>> userNameToFileName("T_H") == "T__H_"
154
+ True
155
+ >>> userNameToFileName("T_h") == "T__h"
156
+ True
157
+ >>> userNameToFileName("t_h") == "t_h"
158
+ True
159
+ >>> userNameToFileName("F_F_I") == "F__F__I_"
160
+ True
161
+ >>> userNameToFileName("f_f_i") == "f_f_i"
162
+ True
163
+ >>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
164
+ True
165
+ >>> userNameToFileName(".notdef") == "_notdef"
166
+ True
167
+ >>> userNameToFileName("con") == "_con"
168
+ True
169
+ >>> userNameToFileName("CON") == "C_O_N_"
170
+ True
171
+ >>> userNameToFileName("con.alt") == "_con.alt"
172
+ True
173
+ >>> userNameToFileName("alt.con") == "alt._con"
174
+ True
143
175
  """
144
176
  # the incoming name must be a string
145
177
  if not isinstance(userName, str):
@@ -181,33 +213,42 @@ def userNameToFileName(userName: str, existing=(), prefix="", suffix=""):
181
213
 
182
214
 
183
215
  def handleClash1(userName, existing=[], prefix="", suffix=""):
184
- """
185
- existing should be a case-insensitive list
186
- of all existing file names.
187
-
188
- >>> prefix = ("0" * 5) + "."
189
- >>> suffix = "." + ("0" * 10)
190
- >>> existing = ["a" * 5]
191
-
192
- >>> e = list(existing)
193
- >>> handleClash1(userName="A" * 5, existing=e,
194
- ... prefix=prefix, suffix=suffix) == (
195
- ... '00000.AAAAA000000000000001.0000000000')
196
- True
197
-
198
- >>> e = list(existing)
199
- >>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
200
- >>> handleClash1(userName="A" * 5, existing=e,
201
- ... prefix=prefix, suffix=suffix) == (
202
- ... '00000.AAAAA000000000000002.0000000000')
203
- True
204
-
205
- >>> e = list(existing)
206
- >>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
207
- >>> handleClash1(userName="A" * 5, existing=e,
208
- ... prefix=prefix, suffix=suffix) == (
209
- ... '00000.AAAAA000000000000001.0000000000')
210
- True
216
+ """A helper function that resolves collisions with existing names when choosing a filename.
217
+
218
+ This function attempts to append an unused integer counter to the filename.
219
+
220
+ Args:
221
+ userName (str): The input file name.
222
+ existing: A case-insensitive list of all existing file names.
223
+ prefix: Prefix to be prepended to the file name.
224
+ suffix: Suffix to be appended to the file name.
225
+
226
+ Returns:
227
+ A suitable filename.
228
+
229
+ >>> prefix = ("0" * 5) + "."
230
+ >>> suffix = "." + ("0" * 10)
231
+ >>> existing = ["a" * 5]
232
+
233
+ >>> e = list(existing)
234
+ >>> handleClash1(userName="A" * 5, existing=e,
235
+ ... prefix=prefix, suffix=suffix) == (
236
+ ... '00000.AAAAA000000000000001.0000000000')
237
+ True
238
+
239
+ >>> e = list(existing)
240
+ >>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
241
+ >>> handleClash1(userName="A" * 5, existing=e,
242
+ ... prefix=prefix, suffix=suffix) == (
243
+ ... '00000.AAAAA000000000000002.0000000000')
244
+ True
245
+
246
+ >>> e = list(existing)
247
+ >>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
248
+ >>> handleClash1(userName="A" * 5, existing=e,
249
+ ... prefix=prefix, suffix=suffix) == (
250
+ ... '00000.AAAAA000000000000001.0000000000')
251
+ True
211
252
  """
212
253
  # if the prefix length + user name length + suffix length + 15 is at
213
254
  # or past the maximum length, silce 15 characters off of the user name
@@ -238,30 +279,44 @@ def handleClash1(userName, existing=[], prefix="", suffix=""):
238
279
 
239
280
 
240
281
  def handleClash2(existing=[], prefix="", suffix=""):
241
- """
242
- existing should be a case-insensitive list
243
- of all existing file names.
244
-
245
- >>> prefix = ("0" * 5) + "."
246
- >>> suffix = "." + ("0" * 10)
247
- >>> existing = [prefix + str(i) + suffix for i in range(100)]
248
-
249
- >>> e = list(existing)
250
- >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
251
- ... '00000.100.0000000000')
252
- True
253
-
254
- >>> e = list(existing)
255
- >>> e.remove(prefix + "1" + suffix)
256
- >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
257
- ... '00000.1.0000000000')
258
- True
259
-
260
- >>> e = list(existing)
261
- >>> e.remove(prefix + "2" + suffix)
262
- >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
263
- ... '00000.2.0000000000')
264
- True
282
+ """A helper function that resolves collisions with existing names when choosing a filename.
283
+
284
+ This function is a fallback to :func:`handleClash1`. It attempts to append an unused integer counter to the filename.
285
+
286
+ Args:
287
+ userName (str): The input file name.
288
+ existing: A case-insensitive list of all existing file names.
289
+ prefix: Prefix to be prepended to the file name.
290
+ suffix: Suffix to be appended to the file name.
291
+
292
+ Returns:
293
+ A suitable filename.
294
+
295
+ Raises:
296
+ NameTranslationError: If no suitable name could be generated.
297
+
298
+ Examples::
299
+
300
+ >>> prefix = ("0" * 5) + "."
301
+ >>> suffix = "." + ("0" * 10)
302
+ >>> existing = [prefix + str(i) + suffix for i in range(100)]
303
+
304
+ >>> e = list(existing)
305
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
306
+ ... '00000.100.0000000000')
307
+ True
308
+
309
+ >>> e = list(existing)
310
+ >>> e.remove(prefix + "1" + suffix)
311
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
312
+ ... '00000.1.0000000000')
313
+ True
314
+
315
+ >>> e = list(existing)
316
+ >>> e.remove(prefix + "2" + suffix)
317
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
318
+ ... '00000.2.0000000000')
319
+ True
265
320
  """
266
321
  # calculate the longest possible string
267
322
  maxLength = maxFileNameLength - len(prefix) - len(suffix)
@@ -1,11 +1,11 @@
1
1
  """
2
- glifLib.py -- Generic module for reading and writing the .glif format.
2
+ Generic module for reading and writing the .glif format.
3
3
 
4
4
  More info about the .glif format (GLyphInterchangeFormat) can be found here:
5
5
 
6
6
  http://unifiedfontobject.org
7
7
 
8
- The main class in this module is GlyphSet. It manages a set of .glif files
8
+ The main class in this module is :class:`GlyphSet`. It manages a set of .glif files
9
9
  in a folder. It offers two ways to read glyph data, and one way to write
10
10
  glyph data. See the class doc string for details.
11
11
  """
@@ -60,6 +60,13 @@ LAYERINFO_FILENAME = "layerinfo.plist"
60
60
 
61
61
 
62
62
  class GLIFFormatVersion(tuple, _VersionTupleEnumMixin, enum.Enum):
63
+ """Class representing the versions of the .glif format supported by the UFO version in use.
64
+
65
+ For a given :mod:`fontTools.ufoLib.UFOFormatVersion`, the :func:`supported_versions` method will
66
+ return the supported versions of the GLIF file format. If the UFO version is unspecified, the
67
+ :func:`supported_versions` method will return all available GLIF format versions.
68
+ """
69
+
63
70
  FORMAT_1_0 = (1, 0)
64
71
  FORMAT_2_0 = (2, 0)
65
72
 
@@ -1,43 +1,73 @@
1
1
  def lookupKerningValue(
2
2
  pair, kerning, groups, fallback=0, glyphToFirstGroup=None, glyphToSecondGroup=None
3
3
  ):
4
- """
5
- Note: This expects kerning to be a flat dictionary
6
- of kerning pairs, not the nested structure used
7
- in kerning.plist.
4
+ """Retrieve the kerning value (if any) between a pair of elements.
5
+
6
+ The elments can be either individual glyphs (by name) or kerning
7
+ groups (by name), or any combination of the two.
8
+
9
+ Args:
10
+ pair:
11
+ A tuple, in logical order (first, second) with respect
12
+ to the reading direction, to query the font for kerning
13
+ information on. Each element in the tuple can be either
14
+ a glyph name or a kerning group name.
15
+ kerning:
16
+ A dictionary of kerning pairs.
17
+ groups:
18
+ A set of kerning groups.
19
+ fallback:
20
+ The fallback value to return if no kern is found between
21
+ the elements in ``pair``. Defaults to 0.
22
+ glyphToFirstGroup:
23
+ A dictionary mapping glyph names to the first-glyph kerning
24
+ groups to which they belong. Defaults to ``None``.
25
+ glyphToSecondGroup:
26
+ A dictionary mapping glyph names to the second-glyph kerning
27
+ groups to which they belong. Defaults to ``None``.
28
+
29
+ Returns:
30
+ The kerning value between the element pair. If no kerning for
31
+ the pair is found, the fallback value is returned.
32
+
33
+ Note: This function expects the ``kerning`` argument to be a flat
34
+ dictionary of kerning pairs, not the nested structure used in a
35
+ kerning.plist file.
36
+
37
+ Examples::
8
38
 
9
- >>> groups = {
10
- ... "public.kern1.O" : ["O", "D", "Q"],
11
- ... "public.kern2.E" : ["E", "F"]
12
- ... }
13
- >>> kerning = {
14
- ... ("public.kern1.O", "public.kern2.E") : -100,
15
- ... ("public.kern1.O", "F") : -200,
16
- ... ("D", "F") : -300
17
- ... }
18
- >>> lookupKerningValue(("D", "F"), kerning, groups)
19
- -300
20
- >>> lookupKerningValue(("O", "F"), kerning, groups)
21
- -200
22
- >>> lookupKerningValue(("O", "E"), kerning, groups)
23
- -100
24
- >>> lookupKerningValue(("O", "O"), kerning, groups)
25
- 0
26
- >>> lookupKerningValue(("E", "E"), kerning, groups)
27
- 0
28
- >>> lookupKerningValue(("E", "O"), kerning, groups)
29
- 0
30
- >>> lookupKerningValue(("X", "X"), kerning, groups)
31
- 0
32
- >>> lookupKerningValue(("public.kern1.O", "public.kern2.E"),
33
- ... kerning, groups)
34
- -100
35
- >>> lookupKerningValue(("public.kern1.O", "F"), kerning, groups)
36
- -200
37
- >>> lookupKerningValue(("O", "public.kern2.E"), kerning, groups)
38
- -100
39
- >>> lookupKerningValue(("public.kern1.X", "public.kern2.X"), kerning, groups)
40
- 0
39
+ >>> groups = {
40
+ ... "public.kern1.O" : ["O", "D", "Q"],
41
+ ... "public.kern2.E" : ["E", "F"]
42
+ ... }
43
+ >>> kerning = {
44
+ ... ("public.kern1.O", "public.kern2.E") : -100,
45
+ ... ("public.kern1.O", "F") : -200,
46
+ ... ("D", "F") : -300
47
+ ... }
48
+ >>> lookupKerningValue(("D", "F"), kerning, groups)
49
+ -300
50
+ >>> lookupKerningValue(("O", "F"), kerning, groups)
51
+ -200
52
+ >>> lookupKerningValue(("O", "E"), kerning, groups)
53
+ -100
54
+ >>> lookupKerningValue(("O", "O"), kerning, groups)
55
+ 0
56
+ >>> lookupKerningValue(("E", "E"), kerning, groups)
57
+ 0
58
+ >>> lookupKerningValue(("E", "O"), kerning, groups)
59
+ 0
60
+ >>> lookupKerningValue(("X", "X"), kerning, groups)
61
+ 0
62
+ >>> lookupKerningValue(("public.kern1.O", "public.kern2.E"),
63
+ ... kerning, groups)
64
+ -100
65
+ >>> lookupKerningValue(("public.kern1.O", "F"), kerning, groups)
66
+ -200
67
+ >>> lookupKerningValue(("O", "public.kern2.E"), kerning, groups)
68
+ -100
69
+ >>> lookupKerningValue(("public.kern1.X", "public.kern2.X"), kerning, groups)
70
+ 0
41
71
  """
42
72
  # quickly check to see if the pair is in the kerning dictionary
43
73
  if pair in kerning: