fonttools 4.55.6__cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.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 (329) hide show
  1. fontTools/__init__.py +8 -0
  2. fontTools/__main__.py +35 -0
  3. fontTools/afmLib.py +439 -0
  4. fontTools/agl.py +5233 -0
  5. fontTools/cffLib/CFF2ToCFF.py +203 -0
  6. fontTools/cffLib/CFFToCFF2.py +305 -0
  7. fontTools/cffLib/__init__.py +3659 -0
  8. fontTools/cffLib/specializer.py +924 -0
  9. fontTools/cffLib/transforms.py +490 -0
  10. fontTools/cffLib/width.py +210 -0
  11. fontTools/colorLib/__init__.py +0 -0
  12. fontTools/colorLib/builder.py +664 -0
  13. fontTools/colorLib/errors.py +2 -0
  14. fontTools/colorLib/geometry.py +143 -0
  15. fontTools/colorLib/table_builder.py +223 -0
  16. fontTools/colorLib/unbuilder.py +81 -0
  17. fontTools/config/__init__.py +75 -0
  18. fontTools/cu2qu/__init__.py +15 -0
  19. fontTools/cu2qu/__main__.py +6 -0
  20. fontTools/cu2qu/benchmark.py +54 -0
  21. fontTools/cu2qu/cli.py +198 -0
  22. fontTools/cu2qu/cu2qu.c +14829 -0
  23. fontTools/cu2qu/cu2qu.cpython-39-x86_64-linux-gnu.so +0 -0
  24. fontTools/cu2qu/cu2qu.py +531 -0
  25. fontTools/cu2qu/errors.py +77 -0
  26. fontTools/cu2qu/ufo.py +349 -0
  27. fontTools/designspaceLib/__init__.py +3338 -0
  28. fontTools/designspaceLib/__main__.py +6 -0
  29. fontTools/designspaceLib/split.py +475 -0
  30. fontTools/designspaceLib/statNames.py +253 -0
  31. fontTools/designspaceLib/types.py +147 -0
  32. fontTools/encodings/MacRoman.py +258 -0
  33. fontTools/encodings/StandardEncoding.py +258 -0
  34. fontTools/encodings/__init__.py +1 -0
  35. fontTools/encodings/codecs.py +135 -0
  36. fontTools/feaLib/__init__.py +4 -0
  37. fontTools/feaLib/__main__.py +78 -0
  38. fontTools/feaLib/ast.py +2134 -0
  39. fontTools/feaLib/builder.py +1747 -0
  40. fontTools/feaLib/error.py +22 -0
  41. fontTools/feaLib/lexer.c +17986 -0
  42. fontTools/feaLib/lexer.cpython-39-x86_64-linux-gnu.so +0 -0
  43. fontTools/feaLib/lexer.py +287 -0
  44. fontTools/feaLib/location.py +12 -0
  45. fontTools/feaLib/lookupDebugInfo.py +12 -0
  46. fontTools/feaLib/parser.py +2359 -0
  47. fontTools/feaLib/variableScalar.py +113 -0
  48. fontTools/fontBuilder.py +1008 -0
  49. fontTools/help.py +36 -0
  50. fontTools/merge/__init__.py +248 -0
  51. fontTools/merge/__main__.py +6 -0
  52. fontTools/merge/base.py +81 -0
  53. fontTools/merge/cmap.py +141 -0
  54. fontTools/merge/layout.py +526 -0
  55. fontTools/merge/options.py +85 -0
  56. fontTools/merge/tables.py +341 -0
  57. fontTools/merge/unicode.py +78 -0
  58. fontTools/merge/util.py +143 -0
  59. fontTools/misc/__init__.py +1 -0
  60. fontTools/misc/arrayTools.py +424 -0
  61. fontTools/misc/bezierTools.c +41831 -0
  62. fontTools/misc/bezierTools.cpython-39-x86_64-linux-gnu.so +0 -0
  63. fontTools/misc/bezierTools.py +1497 -0
  64. fontTools/misc/classifyTools.py +170 -0
  65. fontTools/misc/cliTools.py +53 -0
  66. fontTools/misc/configTools.py +349 -0
  67. fontTools/misc/cython.py +27 -0
  68. fontTools/misc/dictTools.py +83 -0
  69. fontTools/misc/eexec.py +119 -0
  70. fontTools/misc/encodingTools.py +72 -0
  71. fontTools/misc/etree.py +479 -0
  72. fontTools/misc/filenames.py +245 -0
  73. fontTools/misc/fixedTools.py +253 -0
  74. fontTools/misc/intTools.py +25 -0
  75. fontTools/misc/iterTools.py +12 -0
  76. fontTools/misc/lazyTools.py +42 -0
  77. fontTools/misc/loggingTools.py +543 -0
  78. fontTools/misc/macCreatorType.py +56 -0
  79. fontTools/misc/macRes.py +261 -0
  80. fontTools/misc/plistlib/__init__.py +681 -0
  81. fontTools/misc/plistlib/py.typed +0 -0
  82. fontTools/misc/psCharStrings.py +1496 -0
  83. fontTools/misc/psLib.py +398 -0
  84. fontTools/misc/psOperators.py +572 -0
  85. fontTools/misc/py23.py +96 -0
  86. fontTools/misc/roundTools.py +110 -0
  87. fontTools/misc/sstruct.py +231 -0
  88. fontTools/misc/symfont.py +244 -0
  89. fontTools/misc/testTools.py +229 -0
  90. fontTools/misc/textTools.py +154 -0
  91. fontTools/misc/timeTools.py +88 -0
  92. fontTools/misc/transform.py +516 -0
  93. fontTools/misc/treeTools.py +45 -0
  94. fontTools/misc/vector.py +147 -0
  95. fontTools/misc/visitor.py +142 -0
  96. fontTools/misc/xmlReader.py +188 -0
  97. fontTools/misc/xmlWriter.py +204 -0
  98. fontTools/mtiLib/__init__.py +1402 -0
  99. fontTools/mtiLib/__main__.py +5 -0
  100. fontTools/otlLib/__init__.py +1 -0
  101. fontTools/otlLib/builder.py +3221 -0
  102. fontTools/otlLib/error.py +11 -0
  103. fontTools/otlLib/maxContextCalc.py +96 -0
  104. fontTools/otlLib/optimize/__init__.py +53 -0
  105. fontTools/otlLib/optimize/__main__.py +6 -0
  106. fontTools/otlLib/optimize/gpos.py +453 -0
  107. fontTools/pens/__init__.py +1 -0
  108. fontTools/pens/areaPen.py +52 -0
  109. fontTools/pens/basePen.py +475 -0
  110. fontTools/pens/boundsPen.py +98 -0
  111. fontTools/pens/cairoPen.py +26 -0
  112. fontTools/pens/cocoaPen.py +26 -0
  113. fontTools/pens/cu2quPen.py +325 -0
  114. fontTools/pens/explicitClosingLinePen.py +101 -0
  115. fontTools/pens/filterPen.py +241 -0
  116. fontTools/pens/freetypePen.py +462 -0
  117. fontTools/pens/hashPointPen.py +89 -0
  118. fontTools/pens/momentsPen.c +13448 -0
  119. fontTools/pens/momentsPen.cpython-39-x86_64-linux-gnu.so +0 -0
  120. fontTools/pens/momentsPen.py +879 -0
  121. fontTools/pens/perimeterPen.py +69 -0
  122. fontTools/pens/pointInsidePen.py +192 -0
  123. fontTools/pens/pointPen.py +600 -0
  124. fontTools/pens/qtPen.py +29 -0
  125. fontTools/pens/qu2cuPen.py +105 -0
  126. fontTools/pens/quartzPen.py +43 -0
  127. fontTools/pens/recordingPen.py +335 -0
  128. fontTools/pens/reportLabPen.py +79 -0
  129. fontTools/pens/reverseContourPen.py +96 -0
  130. fontTools/pens/roundingPen.py +130 -0
  131. fontTools/pens/statisticsPen.py +312 -0
  132. fontTools/pens/svgPathPen.py +310 -0
  133. fontTools/pens/t2CharStringPen.py +68 -0
  134. fontTools/pens/teePen.py +55 -0
  135. fontTools/pens/transformPen.py +115 -0
  136. fontTools/pens/ttGlyphPen.py +335 -0
  137. fontTools/pens/wxPen.py +29 -0
  138. fontTools/qu2cu/__init__.py +15 -0
  139. fontTools/qu2cu/__main__.py +7 -0
  140. fontTools/qu2cu/benchmark.py +56 -0
  141. fontTools/qu2cu/cli.py +125 -0
  142. fontTools/qu2cu/qu2cu.c +16269 -0
  143. fontTools/qu2cu/qu2cu.cpython-39-x86_64-linux-gnu.so +0 -0
  144. fontTools/qu2cu/qu2cu.py +405 -0
  145. fontTools/subset/__init__.py +3838 -0
  146. fontTools/subset/__main__.py +6 -0
  147. fontTools/subset/cff.py +184 -0
  148. fontTools/subset/svg.py +253 -0
  149. fontTools/subset/util.py +25 -0
  150. fontTools/svgLib/__init__.py +3 -0
  151. fontTools/svgLib/path/__init__.py +65 -0
  152. fontTools/svgLib/path/arc.py +154 -0
  153. fontTools/svgLib/path/parser.py +322 -0
  154. fontTools/svgLib/path/shapes.py +183 -0
  155. fontTools/t1Lib/__init__.py +648 -0
  156. fontTools/tfmLib.py +460 -0
  157. fontTools/ttLib/__init__.py +26 -0
  158. fontTools/ttLib/__main__.py +109 -0
  159. fontTools/ttLib/macUtils.py +54 -0
  160. fontTools/ttLib/removeOverlaps.py +393 -0
  161. fontTools/ttLib/reorderGlyphs.py +284 -0
  162. fontTools/ttLib/scaleUpem.py +436 -0
  163. fontTools/ttLib/sfnt.py +662 -0
  164. fontTools/ttLib/standardGlyphOrder.py +271 -0
  165. fontTools/ttLib/tables/B_A_S_E_.py +14 -0
  166. fontTools/ttLib/tables/BitmapGlyphMetrics.py +64 -0
  167. fontTools/ttLib/tables/C_B_D_T_.py +113 -0
  168. fontTools/ttLib/tables/C_B_L_C_.py +19 -0
  169. fontTools/ttLib/tables/C_F_F_.py +61 -0
  170. fontTools/ttLib/tables/C_F_F__2.py +26 -0
  171. fontTools/ttLib/tables/C_O_L_R_.py +165 -0
  172. fontTools/ttLib/tables/C_P_A_L_.py +305 -0
  173. fontTools/ttLib/tables/D_S_I_G_.py +158 -0
  174. fontTools/ttLib/tables/D__e_b_g.py +17 -0
  175. fontTools/ttLib/tables/DefaultTable.py +49 -0
  176. fontTools/ttLib/tables/E_B_D_T_.py +835 -0
  177. fontTools/ttLib/tables/E_B_L_C_.py +718 -0
  178. fontTools/ttLib/tables/F_F_T_M_.py +52 -0
  179. fontTools/ttLib/tables/F__e_a_t.py +149 -0
  180. fontTools/ttLib/tables/G_D_E_F_.py +13 -0
  181. fontTools/ttLib/tables/G_M_A_P_.py +148 -0
  182. fontTools/ttLib/tables/G_P_K_G_.py +133 -0
  183. fontTools/ttLib/tables/G_P_O_S_.py +14 -0
  184. fontTools/ttLib/tables/G_S_U_B_.py +13 -0
  185. fontTools/ttLib/tables/G__l_a_t.py +235 -0
  186. fontTools/ttLib/tables/G__l_o_c.py +85 -0
  187. fontTools/ttLib/tables/H_V_A_R_.py +13 -0
  188. fontTools/ttLib/tables/J_S_T_F_.py +13 -0
  189. fontTools/ttLib/tables/L_T_S_H_.py +58 -0
  190. fontTools/ttLib/tables/M_A_T_H_.py +13 -0
  191. fontTools/ttLib/tables/M_E_T_A_.py +352 -0
  192. fontTools/ttLib/tables/M_V_A_R_.py +13 -0
  193. fontTools/ttLib/tables/O_S_2f_2.py +752 -0
  194. fontTools/ttLib/tables/S_I_N_G_.py +99 -0
  195. fontTools/ttLib/tables/S_T_A_T_.py +15 -0
  196. fontTools/ttLib/tables/S_V_G_.py +223 -0
  197. fontTools/ttLib/tables/S__i_l_f.py +1040 -0
  198. fontTools/ttLib/tables/S__i_l_l.py +92 -0
  199. fontTools/ttLib/tables/T_S_I_B_.py +13 -0
  200. fontTools/ttLib/tables/T_S_I_C_.py +14 -0
  201. fontTools/ttLib/tables/T_S_I_D_.py +13 -0
  202. fontTools/ttLib/tables/T_S_I_J_.py +13 -0
  203. fontTools/ttLib/tables/T_S_I_P_.py +13 -0
  204. fontTools/ttLib/tables/T_S_I_S_.py +13 -0
  205. fontTools/ttLib/tables/T_S_I_V_.py +26 -0
  206. fontTools/ttLib/tables/T_S_I__0.py +59 -0
  207. fontTools/ttLib/tables/T_S_I__1.py +166 -0
  208. fontTools/ttLib/tables/T_S_I__2.py +17 -0
  209. fontTools/ttLib/tables/T_S_I__3.py +22 -0
  210. fontTools/ttLib/tables/T_S_I__5.py +49 -0
  211. fontTools/ttLib/tables/T_T_F_A_.py +14 -0
  212. fontTools/ttLib/tables/TupleVariation.py +884 -0
  213. fontTools/ttLib/tables/V_A_R_C_.py +12 -0
  214. fontTools/ttLib/tables/V_D_M_X_.py +249 -0
  215. fontTools/ttLib/tables/V_O_R_G_.py +165 -0
  216. fontTools/ttLib/tables/V_V_A_R_.py +13 -0
  217. fontTools/ttLib/tables/__init__.py +97 -0
  218. fontTools/ttLib/tables/_a_n_k_r.py +15 -0
  219. fontTools/ttLib/tables/_a_v_a_r.py +191 -0
  220. fontTools/ttLib/tables/_b_s_l_n.py +15 -0
  221. fontTools/ttLib/tables/_c_i_d_g.py +24 -0
  222. fontTools/ttLib/tables/_c_m_a_p.py +1578 -0
  223. fontTools/ttLib/tables/_c_v_a_r.py +94 -0
  224. fontTools/ttLib/tables/_c_v_t.py +55 -0
  225. fontTools/ttLib/tables/_f_e_a_t.py +15 -0
  226. fontTools/ttLib/tables/_f_p_g_m.py +60 -0
  227. fontTools/ttLib/tables/_f_v_a_r.py +261 -0
  228. fontTools/ttLib/tables/_g_a_s_p.py +63 -0
  229. fontTools/ttLib/tables/_g_c_i_d.py +13 -0
  230. fontTools/ttLib/tables/_g_l_y_f.py +2311 -0
  231. fontTools/ttLib/tables/_g_v_a_r.py +292 -0
  232. fontTools/ttLib/tables/_h_d_m_x.py +127 -0
  233. fontTools/ttLib/tables/_h_e_a_d.py +130 -0
  234. fontTools/ttLib/tables/_h_h_e_a.py +147 -0
  235. fontTools/ttLib/tables/_h_m_t_x.py +160 -0
  236. fontTools/ttLib/tables/_k_e_r_n.py +289 -0
  237. fontTools/ttLib/tables/_l_c_a_r.py +13 -0
  238. fontTools/ttLib/tables/_l_o_c_a.py +70 -0
  239. fontTools/ttLib/tables/_l_t_a_g.py +72 -0
  240. fontTools/ttLib/tables/_m_a_x_p.py +147 -0
  241. fontTools/ttLib/tables/_m_e_t_a.py +112 -0
  242. fontTools/ttLib/tables/_m_o_r_t.py +14 -0
  243. fontTools/ttLib/tables/_m_o_r_x.py +15 -0
  244. fontTools/ttLib/tables/_n_a_m_e.py +1237 -0
  245. fontTools/ttLib/tables/_o_p_b_d.py +14 -0
  246. fontTools/ttLib/tables/_p_o_s_t.py +317 -0
  247. fontTools/ttLib/tables/_p_r_e_p.py +16 -0
  248. fontTools/ttLib/tables/_p_r_o_p.py +12 -0
  249. fontTools/ttLib/tables/_s_b_i_x.py +129 -0
  250. fontTools/ttLib/tables/_t_r_a_k.py +332 -0
  251. fontTools/ttLib/tables/_v_h_e_a.py +139 -0
  252. fontTools/ttLib/tables/_v_m_t_x.py +19 -0
  253. fontTools/ttLib/tables/asciiTable.py +20 -0
  254. fontTools/ttLib/tables/grUtils.py +92 -0
  255. fontTools/ttLib/tables/otBase.py +1465 -0
  256. fontTools/ttLib/tables/otConverters.py +2065 -0
  257. fontTools/ttLib/tables/otData.py +6400 -0
  258. fontTools/ttLib/tables/otTables.py +2700 -0
  259. fontTools/ttLib/tables/otTraverse.py +162 -0
  260. fontTools/ttLib/tables/sbixGlyph.py +149 -0
  261. fontTools/ttLib/tables/sbixStrike.py +177 -0
  262. fontTools/ttLib/tables/table_API_readme.txt +91 -0
  263. fontTools/ttLib/tables/ttProgram.py +594 -0
  264. fontTools/ttLib/ttCollection.py +125 -0
  265. fontTools/ttLib/ttFont.py +1155 -0
  266. fontTools/ttLib/ttGlyphSet.py +500 -0
  267. fontTools/ttLib/ttVisitor.py +32 -0
  268. fontTools/ttLib/woff2.py +1683 -0
  269. fontTools/ttx.py +467 -0
  270. fontTools/ufoLib/__init__.py +2477 -0
  271. fontTools/ufoLib/converters.py +334 -0
  272. fontTools/ufoLib/errors.py +22 -0
  273. fontTools/ufoLib/etree.py +6 -0
  274. fontTools/ufoLib/filenames.py +291 -0
  275. fontTools/ufoLib/glifLib.py +2022 -0
  276. fontTools/ufoLib/kerning.py +91 -0
  277. fontTools/ufoLib/plistlib.py +47 -0
  278. fontTools/ufoLib/pointPen.py +6 -0
  279. fontTools/ufoLib/utils.py +76 -0
  280. fontTools/ufoLib/validators.py +1186 -0
  281. fontTools/unicode.py +50 -0
  282. fontTools/unicodedata/Blocks.py +802 -0
  283. fontTools/unicodedata/OTTags.py +50 -0
  284. fontTools/unicodedata/ScriptExtensions.py +806 -0
  285. fontTools/unicodedata/Scripts.py +3618 -0
  286. fontTools/unicodedata/__init__.py +298 -0
  287. fontTools/varLib/__init__.py +1511 -0
  288. fontTools/varLib/__main__.py +6 -0
  289. fontTools/varLib/avar.py +260 -0
  290. fontTools/varLib/avarPlanner.py +1004 -0
  291. fontTools/varLib/builder.py +215 -0
  292. fontTools/varLib/cff.py +631 -0
  293. fontTools/varLib/errors.py +219 -0
  294. fontTools/varLib/featureVars.py +689 -0
  295. fontTools/varLib/instancer/__init__.py +1937 -0
  296. fontTools/varLib/instancer/__main__.py +5 -0
  297. fontTools/varLib/instancer/featureVars.py +190 -0
  298. fontTools/varLib/instancer/names.py +388 -0
  299. fontTools/varLib/instancer/solver.py +309 -0
  300. fontTools/varLib/interpolatable.py +1209 -0
  301. fontTools/varLib/interpolatableHelpers.py +396 -0
  302. fontTools/varLib/interpolatablePlot.py +1269 -0
  303. fontTools/varLib/interpolatableTestContourOrder.py +82 -0
  304. fontTools/varLib/interpolatableTestStartingPoint.py +107 -0
  305. fontTools/varLib/interpolate_layout.py +124 -0
  306. fontTools/varLib/iup.c +19154 -0
  307. fontTools/varLib/iup.cpython-39-x86_64-linux-gnu.so +0 -0
  308. fontTools/varLib/iup.py +490 -0
  309. fontTools/varLib/merger.py +1717 -0
  310. fontTools/varLib/models.py +642 -0
  311. fontTools/varLib/multiVarStore.py +253 -0
  312. fontTools/varLib/mutator.py +518 -0
  313. fontTools/varLib/mvar.py +40 -0
  314. fontTools/varLib/plot.py +238 -0
  315. fontTools/varLib/stat.py +149 -0
  316. fontTools/varLib/varStore.py +767 -0
  317. fontTools/voltLib/__init__.py +5 -0
  318. fontTools/voltLib/ast.py +448 -0
  319. fontTools/voltLib/error.py +12 -0
  320. fontTools/voltLib/lexer.py +99 -0
  321. fontTools/voltLib/parser.py +656 -0
  322. fontTools/voltLib/voltToFea.py +730 -0
  323. fonttools-4.55.6.data/data/share/man/man1/ttx.1 +225 -0
  324. fonttools-4.55.6.dist-info/LICENSE +21 -0
  325. fonttools-4.55.6.dist-info/METADATA +3413 -0
  326. fonttools-4.55.6.dist-info/RECORD +329 -0
  327. fonttools-4.55.6.dist-info/WHEEL +6 -0
  328. fonttools-4.55.6.dist-info/entry_points.txt +5 -0
  329. fonttools-4.55.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,334 @@
1
+ """
2
+ Conversion functions.
3
+ """
4
+
5
+ # adapted from the UFO spec
6
+
7
+
8
+ def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
9
+ # gather known kerning groups based on the prefixes
10
+ firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
11
+ # Make lists of groups referenced in kerning pairs.
12
+ for first, seconds in list(kerning.items()):
13
+ if first in groups and first not in glyphSet:
14
+ if not first.startswith("public.kern1."):
15
+ firstReferencedGroups.add(first)
16
+ for second in list(seconds.keys()):
17
+ if second in groups and second not in glyphSet:
18
+ if not second.startswith("public.kern2."):
19
+ secondReferencedGroups.add(second)
20
+ # Create new names for these groups.
21
+ firstRenamedGroups = {}
22
+ for first in firstReferencedGroups:
23
+ # Make a list of existing group names.
24
+ existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys())
25
+ # Remove the old prefix from the name
26
+ newName = first.replace("@MMK_L_", "")
27
+ # Add the new prefix to the name.
28
+ newName = "public.kern1." + newName
29
+ # Make a unique group name.
30
+ newName = makeUniqueGroupName(newName, existingGroupNames)
31
+ # Store for use later.
32
+ firstRenamedGroups[first] = newName
33
+ secondRenamedGroups = {}
34
+ for second in secondReferencedGroups:
35
+ # Make a list of existing group names.
36
+ existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys())
37
+ # Remove the old prefix from the name
38
+ newName = second.replace("@MMK_R_", "")
39
+ # Add the new prefix to the name.
40
+ newName = "public.kern2." + newName
41
+ # Make a unique group name.
42
+ newName = makeUniqueGroupName(newName, existingGroupNames)
43
+ # Store for use later.
44
+ secondRenamedGroups[second] = newName
45
+ # Populate the new group names into the kerning dictionary as needed.
46
+ newKerning = {}
47
+ for first, seconds in list(kerning.items()):
48
+ first = firstRenamedGroups.get(first, first)
49
+ newSeconds = {}
50
+ for second, value in list(seconds.items()):
51
+ second = secondRenamedGroups.get(second, second)
52
+ newSeconds[second] = value
53
+ newKerning[first] = newSeconds
54
+ # Make copies of the referenced groups and store them
55
+ # under the new names in the overall groups dictionary.
56
+ allRenamedGroups = list(firstRenamedGroups.items())
57
+ allRenamedGroups += list(secondRenamedGroups.items())
58
+ for oldName, newName in allRenamedGroups:
59
+ group = list(groups[oldName])
60
+ groups[newName] = group
61
+ # Return the kerning and the groups.
62
+ return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups)
63
+
64
+
65
+ 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
72
+ this function will catch all of the prefixed groups.
73
+
74
+ These are the prefixes and sides that are handled:
75
+ @MMK_L_ - side 1
76
+ @MMK_R_ - side 2
77
+
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
95
+ """
96
+ knownFirstGroupPrefixes = ["@MMK_L_"]
97
+ knownSecondGroupPrefixes = ["@MMK_R_"]
98
+ firstGroups = set()
99
+ secondGroups = set()
100
+ for groupName in list(groups.keys()):
101
+ for firstPrefix in knownFirstGroupPrefixes:
102
+ if groupName.startswith(firstPrefix):
103
+ firstGroups.add(groupName)
104
+ break
105
+ for secondPrefix in knownSecondGroupPrefixes:
106
+ if groupName.startswith(secondPrefix):
107
+ secondGroups.add(groupName)
108
+ break
109
+ return firstGroups, secondGroups
110
+
111
+
112
+ def makeUniqueGroupName(name, groupNames, counter=0):
113
+ # Add a number to the name if the counter is higher than zero.
114
+ newName = name
115
+ if counter > 0:
116
+ newName = "%s%d" % (newName, counter)
117
+ # If the new name is in the existing group names, recurse.
118
+ if newName in groupNames:
119
+ return makeUniqueGroupName(name, groupNames, counter + 1)
120
+ # Otherwise send back the new name.
121
+ return newName
122
+
123
+
124
+ def test():
125
+ """
126
+ No known prefixes.
127
+
128
+ >>> testKerning = {
129
+ ... "A" : {
130
+ ... "A" : 1,
131
+ ... "B" : 2,
132
+ ... "CGroup" : 3,
133
+ ... "DGroup" : 4
134
+ ... },
135
+ ... "BGroup" : {
136
+ ... "A" : 5,
137
+ ... "B" : 6,
138
+ ... "CGroup" : 7,
139
+ ... "DGroup" : 8
140
+ ... },
141
+ ... "CGroup" : {
142
+ ... "A" : 9,
143
+ ... "B" : 10,
144
+ ... "CGroup" : 11,
145
+ ... "DGroup" : 12
146
+ ... },
147
+ ... }
148
+ >>> testGroups = {
149
+ ... "BGroup" : ["B"],
150
+ ... "CGroup" : ["C"],
151
+ ... "DGroup" : ["D"],
152
+ ... }
153
+ >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
154
+ ... testKerning, testGroups, [])
155
+ >>> expected = {
156
+ ... "A" : {
157
+ ... "A": 1,
158
+ ... "B": 2,
159
+ ... "public.kern2.CGroup": 3,
160
+ ... "public.kern2.DGroup": 4
161
+ ... },
162
+ ... "public.kern1.BGroup": {
163
+ ... "A": 5,
164
+ ... "B": 6,
165
+ ... "public.kern2.CGroup": 7,
166
+ ... "public.kern2.DGroup": 8
167
+ ... },
168
+ ... "public.kern1.CGroup": {
169
+ ... "A": 9,
170
+ ... "B": 10,
171
+ ... "public.kern2.CGroup": 11,
172
+ ... "public.kern2.DGroup": 12
173
+ ... }
174
+ ... }
175
+ >>> kerning == expected
176
+ True
177
+ >>> expected = {
178
+ ... "BGroup": ["B"],
179
+ ... "CGroup": ["C"],
180
+ ... "DGroup": ["D"],
181
+ ... "public.kern1.BGroup": ["B"],
182
+ ... "public.kern1.CGroup": ["C"],
183
+ ... "public.kern2.CGroup": ["C"],
184
+ ... "public.kern2.DGroup": ["D"],
185
+ ... }
186
+ >>> groups == expected
187
+ True
188
+
189
+ Known prefixes.
190
+
191
+ >>> testKerning = {
192
+ ... "A" : {
193
+ ... "A" : 1,
194
+ ... "B" : 2,
195
+ ... "@MMK_R_CGroup" : 3,
196
+ ... "@MMK_R_DGroup" : 4
197
+ ... },
198
+ ... "@MMK_L_BGroup" : {
199
+ ... "A" : 5,
200
+ ... "B" : 6,
201
+ ... "@MMK_R_CGroup" : 7,
202
+ ... "@MMK_R_DGroup" : 8
203
+ ... },
204
+ ... "@MMK_L_CGroup" : {
205
+ ... "A" : 9,
206
+ ... "B" : 10,
207
+ ... "@MMK_R_CGroup" : 11,
208
+ ... "@MMK_R_DGroup" : 12
209
+ ... },
210
+ ... }
211
+ >>> testGroups = {
212
+ ... "@MMK_L_BGroup" : ["B"],
213
+ ... "@MMK_L_CGroup" : ["C"],
214
+ ... "@MMK_L_XGroup" : ["X"],
215
+ ... "@MMK_R_CGroup" : ["C"],
216
+ ... "@MMK_R_DGroup" : ["D"],
217
+ ... "@MMK_R_XGroup" : ["X"],
218
+ ... }
219
+ >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
220
+ ... testKerning, testGroups, [])
221
+ >>> expected = {
222
+ ... "A" : {
223
+ ... "A": 1,
224
+ ... "B": 2,
225
+ ... "public.kern2.CGroup": 3,
226
+ ... "public.kern2.DGroup": 4
227
+ ... },
228
+ ... "public.kern1.BGroup": {
229
+ ... "A": 5,
230
+ ... "B": 6,
231
+ ... "public.kern2.CGroup": 7,
232
+ ... "public.kern2.DGroup": 8
233
+ ... },
234
+ ... "public.kern1.CGroup": {
235
+ ... "A": 9,
236
+ ... "B": 10,
237
+ ... "public.kern2.CGroup": 11,
238
+ ... "public.kern2.DGroup": 12
239
+ ... }
240
+ ... }
241
+ >>> kerning == expected
242
+ True
243
+ >>> expected = {
244
+ ... "@MMK_L_BGroup": ["B"],
245
+ ... "@MMK_L_CGroup": ["C"],
246
+ ... "@MMK_L_XGroup": ["X"],
247
+ ... "@MMK_R_CGroup": ["C"],
248
+ ... "@MMK_R_DGroup": ["D"],
249
+ ... "@MMK_R_XGroup": ["X"],
250
+ ... "public.kern1.BGroup": ["B"],
251
+ ... "public.kern1.CGroup": ["C"],
252
+ ... "public.kern1.XGroup": ["X"],
253
+ ... "public.kern2.CGroup": ["C"],
254
+ ... "public.kern2.DGroup": ["D"],
255
+ ... "public.kern2.XGroup": ["X"],
256
+ ... }
257
+ >>> groups == expected
258
+ True
259
+
260
+ >>> from .validators import kerningValidator
261
+ >>> kerningValidator(kerning)
262
+ (True, None)
263
+
264
+ Mixture of known prefixes and groups without prefixes.
265
+
266
+ >>> testKerning = {
267
+ ... "A" : {
268
+ ... "A" : 1,
269
+ ... "B" : 2,
270
+ ... "@MMK_R_CGroup" : 3,
271
+ ... "DGroup" : 4
272
+ ... },
273
+ ... "BGroup" : {
274
+ ... "A" : 5,
275
+ ... "B" : 6,
276
+ ... "@MMK_R_CGroup" : 7,
277
+ ... "DGroup" : 8
278
+ ... },
279
+ ... "@MMK_L_CGroup" : {
280
+ ... "A" : 9,
281
+ ... "B" : 10,
282
+ ... "@MMK_R_CGroup" : 11,
283
+ ... "DGroup" : 12
284
+ ... },
285
+ ... }
286
+ >>> testGroups = {
287
+ ... "BGroup" : ["B"],
288
+ ... "@MMK_L_CGroup" : ["C"],
289
+ ... "@MMK_R_CGroup" : ["C"],
290
+ ... "DGroup" : ["D"],
291
+ ... }
292
+ >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
293
+ ... testKerning, testGroups, [])
294
+ >>> expected = {
295
+ ... "A" : {
296
+ ... "A": 1,
297
+ ... "B": 2,
298
+ ... "public.kern2.CGroup": 3,
299
+ ... "public.kern2.DGroup": 4
300
+ ... },
301
+ ... "public.kern1.BGroup": {
302
+ ... "A": 5,
303
+ ... "B": 6,
304
+ ... "public.kern2.CGroup": 7,
305
+ ... "public.kern2.DGroup": 8
306
+ ... },
307
+ ... "public.kern1.CGroup": {
308
+ ... "A": 9,
309
+ ... "B": 10,
310
+ ... "public.kern2.CGroup": 11,
311
+ ... "public.kern2.DGroup": 12
312
+ ... }
313
+ ... }
314
+ >>> kerning == expected
315
+ True
316
+ >>> expected = {
317
+ ... "BGroup": ["B"],
318
+ ... "@MMK_L_CGroup": ["C"],
319
+ ... "@MMK_R_CGroup": ["C"],
320
+ ... "DGroup": ["D"],
321
+ ... "public.kern1.BGroup": ["B"],
322
+ ... "public.kern1.CGroup": ["C"],
323
+ ... "public.kern2.CGroup": ["C"],
324
+ ... "public.kern2.DGroup": ["D"],
325
+ ... }
326
+ >>> groups == expected
327
+ True
328
+ """
329
+
330
+
331
+ if __name__ == "__main__":
332
+ import doctest
333
+
334
+ doctest.testmod()
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class UFOLibError(Exception):
5
+ pass
6
+
7
+
8
+ class UnsupportedUFOFormat(UFOLibError):
9
+ pass
10
+
11
+
12
+ class GlifLibError(UFOLibError):
13
+ def _add_note(self, note: str) -> None:
14
+ # Loose backport of PEP 678 until we only support Python 3.11+, used for
15
+ # adding additional context to errors.
16
+ # TODO: Replace with https://docs.python.org/3.11/library/exceptions.html#BaseException.add_note
17
+ (message, *rest) = self.args
18
+ self.args = ((message + "\n" + note), *rest)
19
+
20
+
21
+ class UnsupportedGLIFFormat(GlifLibError):
22
+ pass
@@ -0,0 +1,6 @@
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.
3
+ Please use the latter instead.
4
+ """
5
+
6
+ from fontTools.misc.etree import *
@@ -0,0 +1,291 @@
1
+ """
2
+ User name to file name conversion.
3
+ This was taken from the UFO 3 spec.
4
+ """
5
+
6
+ # Restrictions are taken mostly from
7
+ # https://docs.microsoft.com/en-gb/windows/win32/fileio/naming-a-file#naming-conventions.
8
+ #
9
+ # 1. Integer value zero, sometimes referred to as the ASCII NUL character.
10
+ # 2. Characters whose integer representations are in the range 1 to 31,
11
+ # inclusive.
12
+ # 3. Various characters that (mostly) Windows and POSIX-y filesystems don't
13
+ # allow, plus "(" and ")", as per the specification.
14
+ illegalCharacters = {
15
+ "\x00",
16
+ "\x01",
17
+ "\x02",
18
+ "\x03",
19
+ "\x04",
20
+ "\x05",
21
+ "\x06",
22
+ "\x07",
23
+ "\x08",
24
+ "\t",
25
+ "\n",
26
+ "\x0b",
27
+ "\x0c",
28
+ "\r",
29
+ "\x0e",
30
+ "\x0f",
31
+ "\x10",
32
+ "\x11",
33
+ "\x12",
34
+ "\x13",
35
+ "\x14",
36
+ "\x15",
37
+ "\x16",
38
+ "\x17",
39
+ "\x18",
40
+ "\x19",
41
+ "\x1a",
42
+ "\x1b",
43
+ "\x1c",
44
+ "\x1d",
45
+ "\x1e",
46
+ "\x1f",
47
+ '"',
48
+ "*",
49
+ "+",
50
+ "/",
51
+ ":",
52
+ "<",
53
+ ">",
54
+ "?",
55
+ "[",
56
+ "\\",
57
+ "]",
58
+ "(",
59
+ ")",
60
+ "|",
61
+ "\x7f",
62
+ }
63
+ reservedFileNames = {
64
+ "aux",
65
+ "clock$",
66
+ "com1",
67
+ "com2",
68
+ "com3",
69
+ "com4",
70
+ "com5",
71
+ "com6",
72
+ "com7",
73
+ "com8",
74
+ "com9",
75
+ "con",
76
+ "lpt1",
77
+ "lpt2",
78
+ "lpt3",
79
+ "lpt4",
80
+ "lpt5",
81
+ "lpt6",
82
+ "lpt7",
83
+ "lpt8",
84
+ "lpt9",
85
+ "nul",
86
+ "prn",
87
+ }
88
+ maxFileNameLength = 255
89
+
90
+
91
+ class NameTranslationError(Exception):
92
+ pass
93
+
94
+
95
+ 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
143
+ """
144
+ # the incoming name must be a string
145
+ if not isinstance(userName, str):
146
+ raise ValueError("The value for userName must be a string.")
147
+ # establish the prefix and suffix lengths
148
+ prefixLength = len(prefix)
149
+ suffixLength = len(suffix)
150
+ # replace an initial period with an _
151
+ # if no prefix is to be added
152
+ if not prefix and userName[0] == ".":
153
+ userName = "_" + userName[1:]
154
+ # filter the user name
155
+ filteredUserName = []
156
+ for character in userName:
157
+ # replace illegal characters with _
158
+ if character in illegalCharacters:
159
+ character = "_"
160
+ # add _ to all non-lower characters
161
+ elif character != character.lower():
162
+ character += "_"
163
+ filteredUserName.append(character)
164
+ userName = "".join(filteredUserName)
165
+ # clip to 255
166
+ sliceLength = maxFileNameLength - prefixLength - suffixLength
167
+ userName = userName[:sliceLength]
168
+ # test for illegal files names
169
+ parts = []
170
+ for part in userName.split("."):
171
+ if part.lower() in reservedFileNames:
172
+ part = "_" + part
173
+ parts.append(part)
174
+ userName = ".".join(parts)
175
+ # test for clash
176
+ fullName = prefix + userName + suffix
177
+ if fullName.lower() in existing:
178
+ fullName = handleClash1(userName, existing, prefix, suffix)
179
+ # finished
180
+ return fullName
181
+
182
+
183
+ 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
211
+ """
212
+ # if the prefix length + user name length + suffix length + 15 is at
213
+ # or past the maximum length, silce 15 characters off of the user name
214
+ prefixLength = len(prefix)
215
+ suffixLength = len(suffix)
216
+ if prefixLength + len(userName) + suffixLength + 15 > maxFileNameLength:
217
+ l = prefixLength + len(userName) + suffixLength + 15
218
+ sliceLength = maxFileNameLength - l
219
+ userName = userName[:sliceLength]
220
+ finalName = None
221
+ # try to add numbers to create a unique name
222
+ counter = 1
223
+ while finalName is None:
224
+ name = userName + str(counter).zfill(15)
225
+ fullName = prefix + name + suffix
226
+ if fullName.lower() not in existing:
227
+ finalName = fullName
228
+ break
229
+ else:
230
+ counter += 1
231
+ if counter >= 999999999999999:
232
+ break
233
+ # if there is a clash, go to the next fallback
234
+ if finalName is None:
235
+ finalName = handleClash2(existing, prefix, suffix)
236
+ # finished
237
+ return finalName
238
+
239
+
240
+ 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
265
+ """
266
+ # calculate the longest possible string
267
+ maxLength = maxFileNameLength - len(prefix) - len(suffix)
268
+ maxValue = int("9" * maxLength)
269
+ # try to find a number
270
+ finalName = None
271
+ counter = 1
272
+ while finalName is None:
273
+ fullName = prefix + str(counter) + suffix
274
+ if fullName.lower() not in existing:
275
+ finalName = fullName
276
+ break
277
+ else:
278
+ counter += 1
279
+ if counter >= maxValue:
280
+ break
281
+ # raise an error if nothing has been found
282
+ if finalName is None:
283
+ raise NameTranslationError("No unique name could be found.")
284
+ # finished
285
+ return finalName
286
+
287
+
288
+ if __name__ == "__main__":
289
+ import doctest
290
+
291
+ doctest.testmod()