fonttools 4.60.2__cp311-cp311-win32.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.
Files changed (353) 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/annotations.py +30 -0
  6. fontTools/cffLib/CFF2ToCFF.py +258 -0
  7. fontTools/cffLib/CFFToCFF2.py +305 -0
  8. fontTools/cffLib/__init__.py +3694 -0
  9. fontTools/cffLib/specializer.py +927 -0
  10. fontTools/cffLib/transforms.py +495 -0
  11. fontTools/cffLib/width.py +210 -0
  12. fontTools/colorLib/__init__.py +0 -0
  13. fontTools/colorLib/builder.py +664 -0
  14. fontTools/colorLib/errors.py +2 -0
  15. fontTools/colorLib/geometry.py +143 -0
  16. fontTools/colorLib/table_builder.py +223 -0
  17. fontTools/colorLib/unbuilder.py +81 -0
  18. fontTools/config/__init__.py +90 -0
  19. fontTools/cu2qu/__init__.py +15 -0
  20. fontTools/cu2qu/__main__.py +6 -0
  21. fontTools/cu2qu/benchmark.py +54 -0
  22. fontTools/cu2qu/cli.py +198 -0
  23. fontTools/cu2qu/cu2qu.c +15817 -0
  24. fontTools/cu2qu/cu2qu.cp311-win32.pyd +0 -0
  25. fontTools/cu2qu/cu2qu.py +563 -0
  26. fontTools/cu2qu/errors.py +77 -0
  27. fontTools/cu2qu/ufo.py +363 -0
  28. fontTools/designspaceLib/__init__.py +3343 -0
  29. fontTools/designspaceLib/__main__.py +6 -0
  30. fontTools/designspaceLib/split.py +475 -0
  31. fontTools/designspaceLib/statNames.py +260 -0
  32. fontTools/designspaceLib/types.py +147 -0
  33. fontTools/encodings/MacRoman.py +258 -0
  34. fontTools/encodings/StandardEncoding.py +258 -0
  35. fontTools/encodings/__init__.py +1 -0
  36. fontTools/encodings/codecs.py +135 -0
  37. fontTools/feaLib/__init__.py +4 -0
  38. fontTools/feaLib/__main__.py +78 -0
  39. fontTools/feaLib/ast.py +2143 -0
  40. fontTools/feaLib/builder.py +1814 -0
  41. fontTools/feaLib/error.py +22 -0
  42. fontTools/feaLib/lexer.c +17029 -0
  43. fontTools/feaLib/lexer.cp311-win32.pyd +0 -0
  44. fontTools/feaLib/lexer.py +287 -0
  45. fontTools/feaLib/location.py +12 -0
  46. fontTools/feaLib/lookupDebugInfo.py +12 -0
  47. fontTools/feaLib/parser.py +2394 -0
  48. fontTools/feaLib/variableScalar.py +118 -0
  49. fontTools/fontBuilder.py +1014 -0
  50. fontTools/help.py +36 -0
  51. fontTools/merge/__init__.py +248 -0
  52. fontTools/merge/__main__.py +6 -0
  53. fontTools/merge/base.py +81 -0
  54. fontTools/merge/cmap.py +173 -0
  55. fontTools/merge/layout.py +526 -0
  56. fontTools/merge/options.py +85 -0
  57. fontTools/merge/tables.py +352 -0
  58. fontTools/merge/unicode.py +78 -0
  59. fontTools/merge/util.py +143 -0
  60. fontTools/misc/__init__.py +1 -0
  61. fontTools/misc/arrayTools.py +424 -0
  62. fontTools/misc/bezierTools.c +39731 -0
  63. fontTools/misc/bezierTools.cp311-win32.pyd +0 -0
  64. fontTools/misc/bezierTools.py +1500 -0
  65. fontTools/misc/classifyTools.py +170 -0
  66. fontTools/misc/cliTools.py +53 -0
  67. fontTools/misc/configTools.py +349 -0
  68. fontTools/misc/cython.py +27 -0
  69. fontTools/misc/dictTools.py +83 -0
  70. fontTools/misc/eexec.py +119 -0
  71. fontTools/misc/encodingTools.py +72 -0
  72. fontTools/misc/enumTools.py +23 -0
  73. fontTools/misc/etree.py +456 -0
  74. fontTools/misc/filenames.py +245 -0
  75. fontTools/misc/filesystem/__init__.py +68 -0
  76. fontTools/misc/filesystem/_base.py +134 -0
  77. fontTools/misc/filesystem/_copy.py +45 -0
  78. fontTools/misc/filesystem/_errors.py +54 -0
  79. fontTools/misc/filesystem/_info.py +75 -0
  80. fontTools/misc/filesystem/_osfs.py +164 -0
  81. fontTools/misc/filesystem/_path.py +67 -0
  82. fontTools/misc/filesystem/_subfs.py +92 -0
  83. fontTools/misc/filesystem/_tempfs.py +34 -0
  84. fontTools/misc/filesystem/_tools.py +34 -0
  85. fontTools/misc/filesystem/_walk.py +55 -0
  86. fontTools/misc/filesystem/_zipfs.py +204 -0
  87. fontTools/misc/fixedTools.py +253 -0
  88. fontTools/misc/intTools.py +25 -0
  89. fontTools/misc/iterTools.py +12 -0
  90. fontTools/misc/lazyTools.py +42 -0
  91. fontTools/misc/loggingTools.py +543 -0
  92. fontTools/misc/macCreatorType.py +56 -0
  93. fontTools/misc/macRes.py +261 -0
  94. fontTools/misc/plistlib/__init__.py +681 -0
  95. fontTools/misc/plistlib/py.typed +0 -0
  96. fontTools/misc/psCharStrings.py +1511 -0
  97. fontTools/misc/psLib.py +398 -0
  98. fontTools/misc/psOperators.py +572 -0
  99. fontTools/misc/py23.py +96 -0
  100. fontTools/misc/roundTools.py +110 -0
  101. fontTools/misc/sstruct.py +227 -0
  102. fontTools/misc/symfont.py +242 -0
  103. fontTools/misc/testTools.py +233 -0
  104. fontTools/misc/textTools.py +156 -0
  105. fontTools/misc/timeTools.py +88 -0
  106. fontTools/misc/transform.py +516 -0
  107. fontTools/misc/treeTools.py +45 -0
  108. fontTools/misc/vector.py +147 -0
  109. fontTools/misc/visitor.py +158 -0
  110. fontTools/misc/xmlReader.py +188 -0
  111. fontTools/misc/xmlWriter.py +231 -0
  112. fontTools/mtiLib/__init__.py +1400 -0
  113. fontTools/mtiLib/__main__.py +5 -0
  114. fontTools/otlLib/__init__.py +1 -0
  115. fontTools/otlLib/builder.py +3465 -0
  116. fontTools/otlLib/error.py +11 -0
  117. fontTools/otlLib/maxContextCalc.py +96 -0
  118. fontTools/otlLib/optimize/__init__.py +53 -0
  119. fontTools/otlLib/optimize/__main__.py +6 -0
  120. fontTools/otlLib/optimize/gpos.py +439 -0
  121. fontTools/pens/__init__.py +1 -0
  122. fontTools/pens/areaPen.py +52 -0
  123. fontTools/pens/basePen.py +475 -0
  124. fontTools/pens/boundsPen.py +98 -0
  125. fontTools/pens/cairoPen.py +26 -0
  126. fontTools/pens/cocoaPen.py +26 -0
  127. fontTools/pens/cu2quPen.py +325 -0
  128. fontTools/pens/explicitClosingLinePen.py +101 -0
  129. fontTools/pens/filterPen.py +433 -0
  130. fontTools/pens/freetypePen.py +462 -0
  131. fontTools/pens/hashPointPen.py +89 -0
  132. fontTools/pens/momentsPen.c +13378 -0
  133. fontTools/pens/momentsPen.cp311-win32.pyd +0 -0
  134. fontTools/pens/momentsPen.py +879 -0
  135. fontTools/pens/perimeterPen.py +69 -0
  136. fontTools/pens/pointInsidePen.py +192 -0
  137. fontTools/pens/pointPen.py +643 -0
  138. fontTools/pens/qtPen.py +29 -0
  139. fontTools/pens/qu2cuPen.py +105 -0
  140. fontTools/pens/quartzPen.py +43 -0
  141. fontTools/pens/recordingPen.py +335 -0
  142. fontTools/pens/reportLabPen.py +79 -0
  143. fontTools/pens/reverseContourPen.py +96 -0
  144. fontTools/pens/roundingPen.py +130 -0
  145. fontTools/pens/statisticsPen.py +312 -0
  146. fontTools/pens/svgPathPen.py +310 -0
  147. fontTools/pens/t2CharStringPen.py +88 -0
  148. fontTools/pens/teePen.py +55 -0
  149. fontTools/pens/transformPen.py +115 -0
  150. fontTools/pens/ttGlyphPen.py +335 -0
  151. fontTools/pens/wxPen.py +29 -0
  152. fontTools/qu2cu/__init__.py +15 -0
  153. fontTools/qu2cu/__main__.py +7 -0
  154. fontTools/qu2cu/benchmark.py +56 -0
  155. fontTools/qu2cu/cli.py +125 -0
  156. fontTools/qu2cu/qu2cu.c +16682 -0
  157. fontTools/qu2cu/qu2cu.cp311-win32.pyd +0 -0
  158. fontTools/qu2cu/qu2cu.py +405 -0
  159. fontTools/subset/__init__.py +4096 -0
  160. fontTools/subset/__main__.py +6 -0
  161. fontTools/subset/cff.py +184 -0
  162. fontTools/subset/svg.py +253 -0
  163. fontTools/subset/util.py +25 -0
  164. fontTools/svgLib/__init__.py +3 -0
  165. fontTools/svgLib/path/__init__.py +65 -0
  166. fontTools/svgLib/path/arc.py +154 -0
  167. fontTools/svgLib/path/parser.py +322 -0
  168. fontTools/svgLib/path/shapes.py +183 -0
  169. fontTools/t1Lib/__init__.py +648 -0
  170. fontTools/tfmLib.py +460 -0
  171. fontTools/ttLib/__init__.py +30 -0
  172. fontTools/ttLib/__main__.py +148 -0
  173. fontTools/ttLib/macUtils.py +54 -0
  174. fontTools/ttLib/removeOverlaps.py +395 -0
  175. fontTools/ttLib/reorderGlyphs.py +285 -0
  176. fontTools/ttLib/scaleUpem.py +436 -0
  177. fontTools/ttLib/sfnt.py +661 -0
  178. fontTools/ttLib/standardGlyphOrder.py +271 -0
  179. fontTools/ttLib/tables/B_A_S_E_.py +14 -0
  180. fontTools/ttLib/tables/BitmapGlyphMetrics.py +64 -0
  181. fontTools/ttLib/tables/C_B_D_T_.py +113 -0
  182. fontTools/ttLib/tables/C_B_L_C_.py +19 -0
  183. fontTools/ttLib/tables/C_F_F_.py +61 -0
  184. fontTools/ttLib/tables/C_F_F__2.py +26 -0
  185. fontTools/ttLib/tables/C_O_L_R_.py +165 -0
  186. fontTools/ttLib/tables/C_P_A_L_.py +305 -0
  187. fontTools/ttLib/tables/D_S_I_G_.py +158 -0
  188. fontTools/ttLib/tables/D__e_b_g.py +35 -0
  189. fontTools/ttLib/tables/DefaultTable.py +49 -0
  190. fontTools/ttLib/tables/E_B_D_T_.py +835 -0
  191. fontTools/ttLib/tables/E_B_L_C_.py +718 -0
  192. fontTools/ttLib/tables/F_F_T_M_.py +52 -0
  193. fontTools/ttLib/tables/F__e_a_t.py +149 -0
  194. fontTools/ttLib/tables/G_D_E_F_.py +13 -0
  195. fontTools/ttLib/tables/G_M_A_P_.py +148 -0
  196. fontTools/ttLib/tables/G_P_K_G_.py +133 -0
  197. fontTools/ttLib/tables/G_P_O_S_.py +14 -0
  198. fontTools/ttLib/tables/G_S_U_B_.py +13 -0
  199. fontTools/ttLib/tables/G_V_A_R_.py +5 -0
  200. fontTools/ttLib/tables/G__l_a_t.py +235 -0
  201. fontTools/ttLib/tables/G__l_o_c.py +85 -0
  202. fontTools/ttLib/tables/H_V_A_R_.py +13 -0
  203. fontTools/ttLib/tables/J_S_T_F_.py +13 -0
  204. fontTools/ttLib/tables/L_T_S_H_.py +58 -0
  205. fontTools/ttLib/tables/M_A_T_H_.py +13 -0
  206. fontTools/ttLib/tables/M_E_T_A_.py +352 -0
  207. fontTools/ttLib/tables/M_V_A_R_.py +13 -0
  208. fontTools/ttLib/tables/O_S_2f_2.py +752 -0
  209. fontTools/ttLib/tables/S_I_N_G_.py +99 -0
  210. fontTools/ttLib/tables/S_T_A_T_.py +15 -0
  211. fontTools/ttLib/tables/S_V_G_.py +223 -0
  212. fontTools/ttLib/tables/S__i_l_f.py +1040 -0
  213. fontTools/ttLib/tables/S__i_l_l.py +92 -0
  214. fontTools/ttLib/tables/T_S_I_B_.py +13 -0
  215. fontTools/ttLib/tables/T_S_I_C_.py +14 -0
  216. fontTools/ttLib/tables/T_S_I_D_.py +13 -0
  217. fontTools/ttLib/tables/T_S_I_J_.py +13 -0
  218. fontTools/ttLib/tables/T_S_I_P_.py +13 -0
  219. fontTools/ttLib/tables/T_S_I_S_.py +13 -0
  220. fontTools/ttLib/tables/T_S_I_V_.py +26 -0
  221. fontTools/ttLib/tables/T_S_I__0.py +70 -0
  222. fontTools/ttLib/tables/T_S_I__1.py +163 -0
  223. fontTools/ttLib/tables/T_S_I__2.py +17 -0
  224. fontTools/ttLib/tables/T_S_I__3.py +22 -0
  225. fontTools/ttLib/tables/T_S_I__5.py +60 -0
  226. fontTools/ttLib/tables/T_T_F_A_.py +14 -0
  227. fontTools/ttLib/tables/TupleVariation.py +884 -0
  228. fontTools/ttLib/tables/V_A_R_C_.py +12 -0
  229. fontTools/ttLib/tables/V_D_M_X_.py +249 -0
  230. fontTools/ttLib/tables/V_O_R_G_.py +165 -0
  231. fontTools/ttLib/tables/V_V_A_R_.py +13 -0
  232. fontTools/ttLib/tables/__init__.py +98 -0
  233. fontTools/ttLib/tables/_a_n_k_r.py +15 -0
  234. fontTools/ttLib/tables/_a_v_a_r.py +193 -0
  235. fontTools/ttLib/tables/_b_s_l_n.py +15 -0
  236. fontTools/ttLib/tables/_c_i_d_g.py +24 -0
  237. fontTools/ttLib/tables/_c_m_a_p.py +1591 -0
  238. fontTools/ttLib/tables/_c_v_a_r.py +94 -0
  239. fontTools/ttLib/tables/_c_v_t.py +56 -0
  240. fontTools/ttLib/tables/_f_e_a_t.py +15 -0
  241. fontTools/ttLib/tables/_f_p_g_m.py +62 -0
  242. fontTools/ttLib/tables/_f_v_a_r.py +261 -0
  243. fontTools/ttLib/tables/_g_a_s_p.py +63 -0
  244. fontTools/ttLib/tables/_g_c_i_d.py +13 -0
  245. fontTools/ttLib/tables/_g_l_y_f.py +2311 -0
  246. fontTools/ttLib/tables/_g_v_a_r.py +340 -0
  247. fontTools/ttLib/tables/_h_d_m_x.py +127 -0
  248. fontTools/ttLib/tables/_h_e_a_d.py +130 -0
  249. fontTools/ttLib/tables/_h_h_e_a.py +147 -0
  250. fontTools/ttLib/tables/_h_m_t_x.py +164 -0
  251. fontTools/ttLib/tables/_k_e_r_n.py +289 -0
  252. fontTools/ttLib/tables/_l_c_a_r.py +13 -0
  253. fontTools/ttLib/tables/_l_o_c_a.py +70 -0
  254. fontTools/ttLib/tables/_l_t_a_g.py +72 -0
  255. fontTools/ttLib/tables/_m_a_x_p.py +147 -0
  256. fontTools/ttLib/tables/_m_e_t_a.py +112 -0
  257. fontTools/ttLib/tables/_m_o_r_t.py +14 -0
  258. fontTools/ttLib/tables/_m_o_r_x.py +15 -0
  259. fontTools/ttLib/tables/_n_a_m_e.py +1242 -0
  260. fontTools/ttLib/tables/_o_p_b_d.py +14 -0
  261. fontTools/ttLib/tables/_p_o_s_t.py +319 -0
  262. fontTools/ttLib/tables/_p_r_e_p.py +16 -0
  263. fontTools/ttLib/tables/_p_r_o_p.py +12 -0
  264. fontTools/ttLib/tables/_s_b_i_x.py +129 -0
  265. fontTools/ttLib/tables/_t_r_a_k.py +332 -0
  266. fontTools/ttLib/tables/_v_h_e_a.py +139 -0
  267. fontTools/ttLib/tables/_v_m_t_x.py +19 -0
  268. fontTools/ttLib/tables/asciiTable.py +20 -0
  269. fontTools/ttLib/tables/grUtils.py +92 -0
  270. fontTools/ttLib/tables/otBase.py +1458 -0
  271. fontTools/ttLib/tables/otConverters.py +2068 -0
  272. fontTools/ttLib/tables/otData.py +6400 -0
  273. fontTools/ttLib/tables/otTables.py +2703 -0
  274. fontTools/ttLib/tables/otTraverse.py +163 -0
  275. fontTools/ttLib/tables/sbixGlyph.py +149 -0
  276. fontTools/ttLib/tables/sbixStrike.py +177 -0
  277. fontTools/ttLib/tables/table_API_readme.txt +91 -0
  278. fontTools/ttLib/tables/ttProgram.py +594 -0
  279. fontTools/ttLib/ttCollection.py +125 -0
  280. fontTools/ttLib/ttFont.py +1148 -0
  281. fontTools/ttLib/ttGlyphSet.py +490 -0
  282. fontTools/ttLib/ttVisitor.py +32 -0
  283. fontTools/ttLib/woff2.py +1680 -0
  284. fontTools/ttx.py +479 -0
  285. fontTools/ufoLib/__init__.py +2575 -0
  286. fontTools/ufoLib/converters.py +407 -0
  287. fontTools/ufoLib/errors.py +30 -0
  288. fontTools/ufoLib/etree.py +6 -0
  289. fontTools/ufoLib/filenames.py +356 -0
  290. fontTools/ufoLib/glifLib.py +2120 -0
  291. fontTools/ufoLib/kerning.py +141 -0
  292. fontTools/ufoLib/plistlib.py +47 -0
  293. fontTools/ufoLib/pointPen.py +6 -0
  294. fontTools/ufoLib/utils.py +107 -0
  295. fontTools/ufoLib/validators.py +1208 -0
  296. fontTools/unicode.py +50 -0
  297. fontTools/unicodedata/Blocks.py +817 -0
  298. fontTools/unicodedata/Mirrored.py +446 -0
  299. fontTools/unicodedata/OTTags.py +50 -0
  300. fontTools/unicodedata/ScriptExtensions.py +832 -0
  301. fontTools/unicodedata/Scripts.py +3639 -0
  302. fontTools/unicodedata/__init__.py +306 -0
  303. fontTools/varLib/__init__.py +1600 -0
  304. fontTools/varLib/__main__.py +6 -0
  305. fontTools/varLib/avar/__init__.py +0 -0
  306. fontTools/varLib/avar/__main__.py +72 -0
  307. fontTools/varLib/avar/build.py +79 -0
  308. fontTools/varLib/avar/map.py +108 -0
  309. fontTools/varLib/avar/plan.py +1004 -0
  310. fontTools/varLib/avar/unbuild.py +271 -0
  311. fontTools/varLib/avarPlanner.py +8 -0
  312. fontTools/varLib/builder.py +215 -0
  313. fontTools/varLib/cff.py +631 -0
  314. fontTools/varLib/errors.py +219 -0
  315. fontTools/varLib/featureVars.py +703 -0
  316. fontTools/varLib/hvar.py +113 -0
  317. fontTools/varLib/instancer/__init__.py +2052 -0
  318. fontTools/varLib/instancer/__main__.py +5 -0
  319. fontTools/varLib/instancer/featureVars.py +190 -0
  320. fontTools/varLib/instancer/names.py +388 -0
  321. fontTools/varLib/instancer/solver.py +309 -0
  322. fontTools/varLib/interpolatable.py +1209 -0
  323. fontTools/varLib/interpolatableHelpers.py +399 -0
  324. fontTools/varLib/interpolatablePlot.py +1269 -0
  325. fontTools/varLib/interpolatableTestContourOrder.py +82 -0
  326. fontTools/varLib/interpolatableTestStartingPoint.py +107 -0
  327. fontTools/varLib/interpolate_layout.py +124 -0
  328. fontTools/varLib/iup.c +19815 -0
  329. fontTools/varLib/iup.cp311-win32.pyd +0 -0
  330. fontTools/varLib/iup.py +490 -0
  331. fontTools/varLib/merger.py +1717 -0
  332. fontTools/varLib/models.py +642 -0
  333. fontTools/varLib/multiVarStore.py +253 -0
  334. fontTools/varLib/mutator.py +529 -0
  335. fontTools/varLib/mvar.py +40 -0
  336. fontTools/varLib/plot.py +238 -0
  337. fontTools/varLib/stat.py +149 -0
  338. fontTools/varLib/varStore.py +739 -0
  339. fontTools/voltLib/__init__.py +5 -0
  340. fontTools/voltLib/__main__.py +206 -0
  341. fontTools/voltLib/ast.py +452 -0
  342. fontTools/voltLib/error.py +12 -0
  343. fontTools/voltLib/lexer.py +99 -0
  344. fontTools/voltLib/parser.py +664 -0
  345. fontTools/voltLib/voltToFea.py +911 -0
  346. fonttools-4.60.2.data/data/share/man/man1/ttx.1 +225 -0
  347. fonttools-4.60.2.dist-info/METADATA +2250 -0
  348. fonttools-4.60.2.dist-info/RECORD +353 -0
  349. fonttools-4.60.2.dist-info/WHEEL +5 -0
  350. fonttools-4.60.2.dist-info/entry_points.txt +5 -0
  351. fonttools-4.60.2.dist-info/licenses/LICENSE +21 -0
  352. fonttools-4.60.2.dist-info/licenses/LICENSE.external +388 -0
  353. fonttools-4.60.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,158 @@
1
+ """Generic visitor pattern implementation for Python objects."""
2
+
3
+ import enum
4
+ import weakref
5
+
6
+
7
+ class Visitor(object):
8
+ defaultStop = False
9
+
10
+ _visitors = {
11
+ # By default we skip visiting weak references to avoid recursion
12
+ # issues. Users can override this by registering a visit
13
+ # function for weakref.ProxyType.
14
+ weakref.ProxyType: {None: lambda self, obj, *args, **kwargs: False}
15
+ }
16
+
17
+ @classmethod
18
+ def _register(celf, clazzes_attrs):
19
+ assert celf != Visitor, "Subclass Visitor instead."
20
+ if "_visitors" not in celf.__dict__:
21
+ celf._visitors = {}
22
+
23
+ def wrapper(method):
24
+ assert method.__name__ == "visit"
25
+ for clazzes, attrs in clazzes_attrs:
26
+ if type(clazzes) != tuple:
27
+ clazzes = (clazzes,)
28
+ if type(attrs) == str:
29
+ attrs = (attrs,)
30
+ for clazz in clazzes:
31
+ _visitors = celf._visitors.setdefault(clazz, {})
32
+ for attr in attrs:
33
+ assert attr not in _visitors, (
34
+ "Oops, class '%s' has visitor function for '%s' defined already."
35
+ % (clazz.__name__, attr)
36
+ )
37
+ _visitors[attr] = method
38
+ return None
39
+
40
+ return wrapper
41
+
42
+ @classmethod
43
+ def register(celf, clazzes):
44
+ if type(clazzes) != tuple:
45
+ clazzes = (clazzes,)
46
+ return celf._register([(clazzes, (None,))])
47
+
48
+ @classmethod
49
+ def register_attr(celf, clazzes, attrs):
50
+ clazzes_attrs = []
51
+ if type(clazzes) != tuple:
52
+ clazzes = (clazzes,)
53
+ if type(attrs) == str:
54
+ attrs = (attrs,)
55
+ for clazz in clazzes:
56
+ clazzes_attrs.append((clazz, attrs))
57
+ return celf._register(clazzes_attrs)
58
+
59
+ @classmethod
60
+ def register_attrs(celf, clazzes_attrs):
61
+ return celf._register(clazzes_attrs)
62
+
63
+ @classmethod
64
+ def _visitorsFor(celf, thing, _default={}):
65
+ typ = type(thing)
66
+
67
+ for celf in celf.mro():
68
+ _visitors = getattr(celf, "_visitors", None)
69
+ if _visitors is None:
70
+ break
71
+
72
+ for base in typ.mro():
73
+ m = celf._visitors.get(base, None)
74
+ if m is not None:
75
+ return m
76
+
77
+ return _default
78
+
79
+ def visitObject(self, obj, *args, **kwargs):
80
+ """Called to visit an object. This function loops over all non-private
81
+ attributes of the objects and calls any user-registered (via
82
+ ``@register_attr()`` or ``@register_attrs()``) ``visit()`` functions.
83
+
84
+ The visitor will proceed to call ``self.visitAttr()``, unless there is a
85
+ user-registered visit function and:
86
+
87
+ * It returns ``False``; or
88
+ * It returns ``None`` (or doesn't return anything) and
89
+ ``visitor.defaultStop`` is ``True`` (non-default).
90
+ """
91
+
92
+ keys = sorted(vars(obj).keys())
93
+ _visitors = self._visitorsFor(obj)
94
+ defaultVisitor = _visitors.get("*", None)
95
+ for key in keys:
96
+ if key[0] == "_":
97
+ continue
98
+ value = getattr(obj, key)
99
+ visitorFunc = _visitors.get(key, defaultVisitor)
100
+ if visitorFunc is not None:
101
+ ret = visitorFunc(self, obj, key, value, *args, **kwargs)
102
+ if ret == False or (ret is None and self.defaultStop):
103
+ continue
104
+ self.visitAttr(obj, key, value, *args, **kwargs)
105
+
106
+ def visitAttr(self, obj, attr, value, *args, **kwargs):
107
+ """Called to visit an attribute of an object."""
108
+ self.visit(value, *args, **kwargs)
109
+
110
+ def visitList(self, obj, *args, **kwargs):
111
+ """Called to visit any value that is a list."""
112
+ for value in obj:
113
+ self.visit(value, *args, **kwargs)
114
+
115
+ def visitDict(self, obj, *args, **kwargs):
116
+ """Called to visit any value that is a dictionary."""
117
+ for value in obj.values():
118
+ self.visit(value, *args, **kwargs)
119
+
120
+ def visitLeaf(self, obj, *args, **kwargs):
121
+ """Called to visit any value that is not an object, list,
122
+ or dictionary."""
123
+ pass
124
+
125
+ def visit(self, obj, *args, **kwargs):
126
+ """This is the main entry to the visitor. The visitor will visit object
127
+ ``obj``.
128
+
129
+ The visitor will first determine if there is a registered (via
130
+ ``@register()``) visit function for the type of object. If there is, it
131
+ will be called, and ``(visitor, obj, *args, **kwargs)`` will be passed
132
+ to the user visit function.
133
+
134
+ The visitor will not recurse if there is a user-registered visit
135
+ function and:
136
+
137
+ * It returns ``False``; or
138
+ * It returns ``None`` (or doesn't return anything) and
139
+ ``visitor.defaultStop`` is ``True`` (non-default)
140
+
141
+ Otherwise, the visitor will proceed to dispatch to one of
142
+ ``self.visitObject()``, ``self.visitList()``, ``self.visitDict()``, or
143
+ ``self.visitLeaf()`` (any of which can be overriden in a subclass).
144
+ """
145
+
146
+ visitorFunc = self._visitorsFor(obj).get(None, None)
147
+ if visitorFunc is not None:
148
+ ret = visitorFunc(self, obj, *args, **kwargs)
149
+ if ret == False or (ret is None and self.defaultStop):
150
+ return
151
+ if hasattr(obj, "__dict__") and not isinstance(obj, enum.Enum):
152
+ self.visitObject(obj, *args, **kwargs)
153
+ elif isinstance(obj, list):
154
+ self.visitList(obj, *args, **kwargs)
155
+ elif isinstance(obj, dict):
156
+ self.visitDict(obj, *args, **kwargs)
157
+ else:
158
+ self.visitLeaf(obj, *args, **kwargs)
@@ -0,0 +1,188 @@
1
+ from fontTools import ttLib
2
+ from fontTools.misc.textTools import safeEval
3
+ from fontTools.ttLib.tables.DefaultTable import DefaultTable
4
+ import sys
5
+ import os
6
+ import logging
7
+
8
+
9
+ log = logging.getLogger(__name__)
10
+
11
+
12
+ class TTXParseError(Exception):
13
+ pass
14
+
15
+
16
+ BUFSIZE = 0x4000
17
+
18
+
19
+ class XMLReader(object):
20
+ def __init__(
21
+ self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False
22
+ ):
23
+ if fileOrPath == "-":
24
+ fileOrPath = sys.stdin
25
+ if not hasattr(fileOrPath, "read"):
26
+ self.file = open(fileOrPath, "rb")
27
+ self._closeStream = True
28
+ else:
29
+ # assume readable file object
30
+ self.file = fileOrPath
31
+ self._closeStream = False
32
+ self.ttFont = ttFont
33
+ self.progress = progress
34
+ if quiet is not None:
35
+ from fontTools.misc.loggingTools import deprecateArgument
36
+
37
+ deprecateArgument("quiet", "configure logging instead")
38
+ self.quiet = quiet
39
+ self.root = None
40
+ self.contentStack = []
41
+ self.contentOnly = contentOnly
42
+ self.stackSize = 0
43
+
44
+ def read(self, rootless=False):
45
+ if rootless:
46
+ self.stackSize += 1
47
+ if self.progress:
48
+ self.file.seek(0, 2)
49
+ fileSize = self.file.tell()
50
+ self.progress.set(0, fileSize // 100 or 1)
51
+ self.file.seek(0)
52
+ self._parseFile(self.file)
53
+ if self._closeStream:
54
+ self.close()
55
+ if rootless:
56
+ self.stackSize -= 1
57
+
58
+ def close(self):
59
+ self.file.close()
60
+
61
+ def _parseFile(self, file):
62
+ from xml.parsers.expat import ParserCreate
63
+
64
+ parser = ParserCreate()
65
+ parser.StartElementHandler = self._startElementHandler
66
+ parser.EndElementHandler = self._endElementHandler
67
+ parser.CharacterDataHandler = self._characterDataHandler
68
+
69
+ pos = 0
70
+ while True:
71
+ chunk = file.read(BUFSIZE)
72
+ if not chunk:
73
+ parser.Parse(chunk, 1)
74
+ break
75
+ pos = pos + len(chunk)
76
+ if self.progress:
77
+ self.progress.set(pos // 100)
78
+ parser.Parse(chunk, 0)
79
+
80
+ def _startElementHandler(self, name, attrs):
81
+ if self.stackSize == 1 and self.contentOnly:
82
+ # We already know the table we're parsing, skip
83
+ # parsing the table tag and continue to
84
+ # stack '2' which begins parsing content
85
+ self.contentStack.append([])
86
+ self.stackSize = 2
87
+ return
88
+ stackSize = self.stackSize
89
+ self.stackSize = stackSize + 1
90
+ subFile = attrs.get("src")
91
+ if subFile is not None:
92
+ if hasattr(self.file, "name"):
93
+ # if file has a name, get its parent directory
94
+ dirname = os.path.dirname(self.file.name)
95
+ else:
96
+ # else fall back to using the current working directory
97
+ dirname = os.getcwd()
98
+ subFile = os.path.join(dirname, subFile)
99
+ if not stackSize:
100
+ if name != "ttFont":
101
+ raise TTXParseError("illegal root tag: %s" % name)
102
+ if self.ttFont.reader is None and not self.ttFont.tables:
103
+ sfntVersion = attrs.get("sfntVersion")
104
+ if sfntVersion is not None:
105
+ if len(sfntVersion) != 4:
106
+ sfntVersion = safeEval('"' + sfntVersion + '"')
107
+ self.ttFont.sfntVersion = sfntVersion
108
+ self.contentStack.append([])
109
+ elif stackSize == 1:
110
+ if subFile is not None:
111
+ subReader = XMLReader(subFile, self.ttFont, self.progress)
112
+ subReader.read()
113
+ self.contentStack.append([])
114
+ return
115
+ tag = ttLib.xmlToTag(name)
116
+ msg = "Parsing '%s' table..." % tag
117
+ if self.progress:
118
+ self.progress.setLabel(msg)
119
+ log.info(msg)
120
+ if tag == "GlyphOrder":
121
+ tableClass = ttLib.GlyphOrder
122
+ elif "ERROR" in attrs or ("raw" in attrs and safeEval(attrs["raw"])):
123
+ tableClass = DefaultTable
124
+ else:
125
+ tableClass = ttLib.getTableClass(tag)
126
+ if tableClass is None:
127
+ tableClass = DefaultTable
128
+ if tag == "loca" and tag in self.ttFont:
129
+ # Special-case the 'loca' table as we need the
130
+ # original if the 'glyf' table isn't recompiled.
131
+ self.currentTable = self.ttFont[tag]
132
+ else:
133
+ self.currentTable = tableClass(tag)
134
+ self.ttFont[tag] = self.currentTable
135
+ self.contentStack.append([])
136
+ elif stackSize == 2 and subFile is not None:
137
+ subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True)
138
+ subReader.read()
139
+ self.contentStack.append([])
140
+ self.root = subReader.root
141
+ elif stackSize == 2:
142
+ self.contentStack.append([])
143
+ self.root = (name, attrs, self.contentStack[-1])
144
+ else:
145
+ l = []
146
+ self.contentStack[-1].append((name, attrs, l))
147
+ self.contentStack.append(l)
148
+
149
+ def _characterDataHandler(self, data):
150
+ if self.stackSize > 1:
151
+ # parser parses in chunks, so we may get multiple calls
152
+ # for the same text node; thus we need to append the data
153
+ # to the last item in the content stack:
154
+ # https://github.com/fonttools/fonttools/issues/2614
155
+ if (
156
+ data != "\n"
157
+ and self.contentStack[-1]
158
+ and isinstance(self.contentStack[-1][-1], str)
159
+ and self.contentStack[-1][-1] != "\n"
160
+ ):
161
+ self.contentStack[-1][-1] += data
162
+ else:
163
+ self.contentStack[-1].append(data)
164
+
165
+ def _endElementHandler(self, name):
166
+ self.stackSize = self.stackSize - 1
167
+ del self.contentStack[-1]
168
+ if not self.contentOnly:
169
+ if self.stackSize == 1:
170
+ self.root = None
171
+ elif self.stackSize == 2:
172
+ name, attrs, content = self.root
173
+ self.currentTable.fromXML(name, attrs, content, self.ttFont)
174
+ self.root = None
175
+
176
+
177
+ class ProgressPrinter(object):
178
+ def __init__(self, title, maxval=100):
179
+ print(title)
180
+
181
+ def set(self, val, maxval=None):
182
+ pass
183
+
184
+ def increment(self, val=1):
185
+ pass
186
+
187
+ def setLabel(self, text):
188
+ print(text)
@@ -0,0 +1,231 @@
1
+ """xmlWriter.py -- Simple XML authoring class"""
2
+
3
+ from fontTools.misc.textTools import byteord, strjoin, tobytes, tostr
4
+ import sys
5
+ import os
6
+ import string
7
+ import logging
8
+ import itertools
9
+
10
+ INDENT = " "
11
+ TTX_LOG = logging.getLogger("fontTools.ttx")
12
+ REPLACEMENT = "?"
13
+ ILLEGAL_XML_CHARS = dict.fromkeys(
14
+ itertools.chain(
15
+ range(0x00, 0x09),
16
+ (0x0B, 0x0C),
17
+ range(0x0E, 0x20),
18
+ range(0xD800, 0xE000),
19
+ (0xFFFE, 0xFFFF),
20
+ ),
21
+ REPLACEMENT,
22
+ )
23
+
24
+
25
+ class XMLWriter(object):
26
+ def __init__(
27
+ self,
28
+ fileOrPath,
29
+ indentwhite=INDENT,
30
+ idlefunc=None,
31
+ encoding="utf_8",
32
+ newlinestr="\n",
33
+ ):
34
+ if encoding.lower().replace("-", "").replace("_", "") != "utf8":
35
+ raise Exception("Only UTF-8 encoding is supported.")
36
+ if fileOrPath == "-":
37
+ fileOrPath = sys.stdout
38
+ if not hasattr(fileOrPath, "write"):
39
+ self.filename = fileOrPath
40
+ self.file = open(fileOrPath, "wb")
41
+ self._closeStream = True
42
+ else:
43
+ self.filename = None
44
+ # assume writable file object
45
+ self.file = fileOrPath
46
+ self._closeStream = False
47
+
48
+ # Figure out if writer expects bytes or unicodes
49
+ try:
50
+ # The bytes check should be first. See:
51
+ # https://github.com/fonttools/fonttools/pull/233
52
+ self.file.write(b"")
53
+ self.totype = tobytes
54
+ except TypeError:
55
+ # This better not fail.
56
+ self.file.write("")
57
+ self.totype = tostr
58
+ self.indentwhite = self.totype(indentwhite)
59
+ if newlinestr is None:
60
+ self.newlinestr = self.totype(os.linesep)
61
+ else:
62
+ self.newlinestr = self.totype(newlinestr)
63
+ self.indentlevel = 0
64
+ self.stack = []
65
+ self.needindent = 1
66
+ self.idlefunc = idlefunc
67
+ self.idlecounter = 0
68
+ self._writeraw('<?xml version="1.0" encoding="UTF-8"?>')
69
+ self.newline()
70
+
71
+ def __enter__(self):
72
+ return self
73
+
74
+ def __exit__(self, exception_type, exception_value, traceback):
75
+ self.close()
76
+
77
+ def close(self):
78
+ if self._closeStream:
79
+ self.file.close()
80
+
81
+ def write(self, string, indent=True):
82
+ """Writes text."""
83
+ self._writeraw(escape(string), indent=indent)
84
+
85
+ def writecdata(self, string):
86
+ """Writes text in a CDATA section."""
87
+ self._writeraw("<![CDATA[" + string + "]]>")
88
+
89
+ def write8bit(self, data, strip=False):
90
+ """Writes a bytes() sequence into the XML, escaping
91
+ non-ASCII bytes. When this is read in xmlReader,
92
+ the original bytes can be recovered by encoding to
93
+ 'latin-1'."""
94
+ self._writeraw(escape8bit(data), strip=strip)
95
+
96
+ def write_noindent(self, string):
97
+ """Writes text without indentation."""
98
+ self._writeraw(escape(string), indent=False)
99
+
100
+ def _writeraw(self, data, indent=True, strip=False):
101
+ """Writes bytes, possibly indented."""
102
+ if indent and self.needindent:
103
+ self.file.write(self.indentlevel * self.indentwhite)
104
+ self.needindent = 0
105
+ s = self.totype(data, encoding="utf_8")
106
+ if strip:
107
+ s = s.strip()
108
+ self.file.write(s)
109
+
110
+ def newline(self):
111
+ self.file.write(self.newlinestr)
112
+ self.needindent = 1
113
+ idlecounter = self.idlecounter
114
+ if not idlecounter % 100 and self.idlefunc is not None:
115
+ self.idlefunc()
116
+ self.idlecounter = idlecounter + 1
117
+
118
+ def comment(self, data):
119
+ data = escape(data)
120
+ lines = data.split("\n")
121
+ self._writeraw("<!-- " + lines[0])
122
+ for line in lines[1:]:
123
+ self.newline()
124
+ self._writeraw(" " + line)
125
+ self._writeraw(" -->")
126
+
127
+ def simpletag(self, _TAG_, *args, **kwargs):
128
+ attrdata = self.stringifyattrs(*args, **kwargs)
129
+ data = "<%s%s/>" % (_TAG_, attrdata)
130
+ self._writeraw(data)
131
+
132
+ def begintag(self, _TAG_, *args, **kwargs):
133
+ attrdata = self.stringifyattrs(*args, **kwargs)
134
+ data = "<%s%s>" % (_TAG_, attrdata)
135
+ self._writeraw(data)
136
+ self.stack.append(_TAG_)
137
+ self.indent()
138
+
139
+ def endtag(self, _TAG_):
140
+ assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
141
+ del self.stack[-1]
142
+ self.dedent()
143
+ data = "</%s>" % _TAG_
144
+ self._writeraw(data)
145
+
146
+ def dumphex(self, data):
147
+ linelength = 16
148
+ hexlinelength = linelength * 2
149
+ chunksize = 8
150
+ for i in range(0, len(data), linelength):
151
+ hexline = hexStr(data[i : i + linelength])
152
+ line = ""
153
+ white = ""
154
+ for j in range(0, hexlinelength, chunksize):
155
+ line = line + white + hexline[j : j + chunksize]
156
+ white = " "
157
+ self._writeraw(line)
158
+ self.newline()
159
+
160
+ def indent(self):
161
+ self.indentlevel = self.indentlevel + 1
162
+
163
+ def dedent(self):
164
+ assert self.indentlevel > 0
165
+ self.indentlevel = self.indentlevel - 1
166
+
167
+ def stringifyattrs(self, *args, **kwargs):
168
+ if kwargs:
169
+ assert not args
170
+ attributes = sorted(kwargs.items())
171
+ elif args:
172
+ assert len(args) == 1
173
+ attributes = args[0]
174
+ else:
175
+ return ""
176
+ data = ""
177
+ for attr, value in attributes:
178
+ if not isinstance(value, (bytes, str)):
179
+ value = str(value)
180
+ data = data + ' %s="%s"' % (attr, escapeattr(value))
181
+ return data
182
+
183
+
184
+ def escape(data):
185
+ """Escape characters not allowed in `XML 1.0 <https://www.w3.org/TR/xml/#NT-Char>`_."""
186
+ data = tostr(data, "utf_8")
187
+ data = data.replace("&", "&amp;")
188
+ data = data.replace("<", "&lt;")
189
+ data = data.replace(">", "&gt;")
190
+ data = data.replace("\r", "&#13;")
191
+
192
+ newData = data.translate(ILLEGAL_XML_CHARS)
193
+ if newData != data:
194
+ maxLen = 10
195
+ preview = repr(data)
196
+ if len(data) > maxLen:
197
+ preview = repr(data[:maxLen])[1:-1] + "..."
198
+ TTX_LOG.warning(
199
+ "Illegal XML character(s) found; replacing offending " "string %r with %r",
200
+ preview,
201
+ REPLACEMENT,
202
+ )
203
+ return newData
204
+
205
+
206
+ def escapeattr(data):
207
+ data = escape(data)
208
+ data = data.replace('"', "&quot;")
209
+ return data
210
+
211
+
212
+ def escape8bit(data):
213
+ """Input is Unicode string."""
214
+
215
+ def escapechar(c):
216
+ n = ord(c)
217
+ if 32 <= n <= 127 and c not in "<&>":
218
+ return c
219
+ else:
220
+ return "&#" + repr(n) + ";"
221
+
222
+ return strjoin(map(escapechar, data.decode("latin-1")))
223
+
224
+
225
+ def hexStr(s):
226
+ h = string.hexdigits
227
+ r = ""
228
+ for c in s:
229
+ i = byteord(c)
230
+ r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
231
+ return r