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,130 @@
1
+ from fontTools.misc.roundTools import noRound, otRound
2
+ from fontTools.misc.transform import Transform
3
+ from fontTools.pens.filterPen import FilterPen, FilterPointPen
4
+
5
+
6
+ __all__ = ["RoundingPen", "RoundingPointPen"]
7
+
8
+
9
+ class RoundingPen(FilterPen):
10
+ """
11
+ Filter pen that rounds point coordinates and component XY offsets to integer. For
12
+ rounding the component transform values, a separate round function can be passed to
13
+ the pen.
14
+
15
+ >>> from fontTools.pens.recordingPen import RecordingPen
16
+ >>> recpen = RecordingPen()
17
+ >>> roundpen = RoundingPen(recpen)
18
+ >>> roundpen.moveTo((0.4, 0.6))
19
+ >>> roundpen.lineTo((1.6, 2.5))
20
+ >>> roundpen.qCurveTo((2.4, 4.6), (3.3, 5.7), (4.9, 6.1))
21
+ >>> roundpen.curveTo((6.4, 8.6), (7.3, 9.7), (8.9, 10.1))
22
+ >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
23
+ >>> recpen.value == [
24
+ ... ('moveTo', ((0, 1),)),
25
+ ... ('lineTo', ((2, 3),)),
26
+ ... ('qCurveTo', ((2, 5), (3, 6), (5, 6))),
27
+ ... ('curveTo', ((6, 9), (7, 10), (9, 10))),
28
+ ... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10))),
29
+ ... ]
30
+ True
31
+ """
32
+
33
+ def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
34
+ super().__init__(outPen)
35
+ self.roundFunc = roundFunc
36
+ self.transformRoundFunc = transformRoundFunc
37
+
38
+ def moveTo(self, pt):
39
+ self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
40
+
41
+ def lineTo(self, pt):
42
+ self._outPen.lineTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
43
+
44
+ def curveTo(self, *points):
45
+ self._outPen.curveTo(
46
+ *((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
47
+ )
48
+
49
+ def qCurveTo(self, *points):
50
+ self._outPen.qCurveTo(
51
+ *((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
52
+ )
53
+
54
+ def addComponent(self, glyphName, transformation):
55
+ xx, xy, yx, yy, dx, dy = transformation
56
+ self._outPen.addComponent(
57
+ glyphName,
58
+ Transform(
59
+ self.transformRoundFunc(xx),
60
+ self.transformRoundFunc(xy),
61
+ self.transformRoundFunc(yx),
62
+ self.transformRoundFunc(yy),
63
+ self.roundFunc(dx),
64
+ self.roundFunc(dy),
65
+ ),
66
+ )
67
+
68
+
69
+ class RoundingPointPen(FilterPointPen):
70
+ """
71
+ Filter point pen that rounds point coordinates and component XY offsets to integer.
72
+ For rounding the component scale values, a separate round function can be passed to
73
+ the pen.
74
+
75
+ >>> from fontTools.pens.recordingPen import RecordingPointPen
76
+ >>> recpen = RecordingPointPen()
77
+ >>> roundpen = RoundingPointPen(recpen)
78
+ >>> roundpen.beginPath()
79
+ >>> roundpen.addPoint((0.4, 0.6), 'line')
80
+ >>> roundpen.addPoint((1.6, 2.5), 'line')
81
+ >>> roundpen.addPoint((2.4, 4.6))
82
+ >>> roundpen.addPoint((3.3, 5.7))
83
+ >>> roundpen.addPoint((4.9, 6.1), 'qcurve')
84
+ >>> roundpen.endPath()
85
+ >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
86
+ >>> recpen.value == [
87
+ ... ('beginPath', (), {}),
88
+ ... ('addPoint', ((0, 1), 'line', False, None), {}),
89
+ ... ('addPoint', ((2, 3), 'line', False, None), {}),
90
+ ... ('addPoint', ((2, 5), None, False, None), {}),
91
+ ... ('addPoint', ((3, 6), None, False, None), {}),
92
+ ... ('addPoint', ((5, 6), 'qcurve', False, None), {}),
93
+ ... ('endPath', (), {}),
94
+ ... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10)), {}),
95
+ ... ]
96
+ True
97
+ """
98
+
99
+ def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
100
+ super().__init__(outPen)
101
+ self.roundFunc = roundFunc
102
+ self.transformRoundFunc = transformRoundFunc
103
+
104
+ def addPoint(
105
+ self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
106
+ ):
107
+ self._outPen.addPoint(
108
+ (self.roundFunc(pt[0]), self.roundFunc(pt[1])),
109
+ segmentType=segmentType,
110
+ smooth=smooth,
111
+ name=name,
112
+ identifier=identifier,
113
+ **kwargs,
114
+ )
115
+
116
+ def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
117
+ xx, xy, yx, yy, dx, dy = transformation
118
+ self._outPen.addComponent(
119
+ baseGlyphName,
120
+ Transform(
121
+ self.transformRoundFunc(xx),
122
+ self.transformRoundFunc(xy),
123
+ self.transformRoundFunc(yx),
124
+ self.transformRoundFunc(yy),
125
+ self.roundFunc(dx),
126
+ self.roundFunc(dy),
127
+ ),
128
+ identifier=identifier,
129
+ **kwargs,
130
+ )
@@ -0,0 +1,312 @@
1
+ """Pen calculating area, center of mass, variance and standard-deviation,
2
+ covariance and correlation, and slant, of glyph shapes."""
3
+
4
+ from math import sqrt, degrees, atan
5
+ from fontTools.pens.basePen import BasePen, OpenContourError
6
+ from fontTools.pens.momentsPen import MomentsPen
7
+
8
+ __all__ = ["StatisticsPen", "StatisticsControlPen"]
9
+
10
+
11
+ class StatisticsBase:
12
+ def __init__(self):
13
+ self._zero()
14
+
15
+ def _zero(self):
16
+ self.area = 0
17
+ self.meanX = 0
18
+ self.meanY = 0
19
+ self.varianceX = 0
20
+ self.varianceY = 0
21
+ self.stddevX = 0
22
+ self.stddevY = 0
23
+ self.covariance = 0
24
+ self.correlation = 0
25
+ self.slant = 0
26
+
27
+ def _update(self):
28
+ # XXX The variance formulas should never produce a negative value,
29
+ # but due to reasons I don't understand, both of our pens do.
30
+ # So we take the absolute value here.
31
+ self.varianceX = abs(self.varianceX)
32
+ self.varianceY = abs(self.varianceY)
33
+
34
+ self.stddevX = stddevX = sqrt(self.varianceX)
35
+ self.stddevY = stddevY = sqrt(self.varianceY)
36
+
37
+ # Correlation(X,Y) = Covariance(X,Y) / ( stddev(X) * stddev(Y) )
38
+ # https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient
39
+ if stddevX * stddevY == 0:
40
+ correlation = float("NaN")
41
+ else:
42
+ # XXX The above formula should never produce a value outside
43
+ # the range [-1, 1], but due to reasons I don't understand,
44
+ # (probably the same issue as above), it does. So we clamp.
45
+ correlation = self.covariance / (stddevX * stddevY)
46
+ correlation = max(-1, min(1, correlation))
47
+ self.correlation = correlation if abs(correlation) > 1e-3 else 0
48
+
49
+ slant = (
50
+ self.covariance / self.varianceY if self.varianceY != 0 else float("NaN")
51
+ )
52
+ self.slant = slant if abs(slant) > 1e-3 else 0
53
+
54
+
55
+ class StatisticsPen(StatisticsBase, MomentsPen):
56
+ """Pen calculating area, center of mass, variance and
57
+ standard-deviation, covariance and correlation, and slant,
58
+ of glyph shapes.
59
+
60
+ Note that if the glyph shape is self-intersecting, the values
61
+ are not correct (but well-defined). Moreover, area will be
62
+ negative if contour directions are clockwise."""
63
+
64
+ def __init__(self, glyphset=None):
65
+ MomentsPen.__init__(self, glyphset=glyphset)
66
+ StatisticsBase.__init__(self)
67
+
68
+ def _closePath(self):
69
+ MomentsPen._closePath(self)
70
+ self._update()
71
+
72
+ def _update(self):
73
+ area = self.area
74
+ if not area:
75
+ self._zero()
76
+ return
77
+
78
+ # Center of mass
79
+ # https://en.wikipedia.org/wiki/Center_of_mass#A_continuous_volume
80
+ self.meanX = meanX = self.momentX / area
81
+ self.meanY = meanY = self.momentY / area
82
+
83
+ # Var(X) = E[X^2] - E[X]^2
84
+ self.varianceX = self.momentXX / area - meanX * meanX
85
+ self.varianceY = self.momentYY / area - meanY * meanY
86
+
87
+ # Covariance(X,Y) = (E[X.Y] - E[X]E[Y])
88
+ self.covariance = self.momentXY / area - meanX * meanY
89
+
90
+ StatisticsBase._update(self)
91
+
92
+
93
+ class StatisticsControlPen(StatisticsBase, BasePen):
94
+ """Pen calculating area, center of mass, variance and
95
+ standard-deviation, covariance and correlation, and slant,
96
+ of glyph shapes, using the control polygon only.
97
+
98
+ Note that if the glyph shape is self-intersecting, the values
99
+ are not correct (but well-defined). Moreover, area will be
100
+ negative if contour directions are clockwise."""
101
+
102
+ def __init__(self, glyphset=None):
103
+ BasePen.__init__(self, glyphset)
104
+ StatisticsBase.__init__(self)
105
+ self._nodes = []
106
+
107
+ def _moveTo(self, pt):
108
+ self._nodes.append(complex(*pt))
109
+ self._startPoint = pt
110
+
111
+ def _lineTo(self, pt):
112
+ self._nodes.append(complex(*pt))
113
+
114
+ def _qCurveToOne(self, pt1, pt2):
115
+ for pt in (pt1, pt2):
116
+ self._nodes.append(complex(*pt))
117
+
118
+ def _curveToOne(self, pt1, pt2, pt3):
119
+ for pt in (pt1, pt2, pt3):
120
+ self._nodes.append(complex(*pt))
121
+
122
+ def _closePath(self):
123
+ p0 = self._getCurrentPoint()
124
+ if p0 != self._startPoint:
125
+ self._lineTo(self._startPoint)
126
+ self._update()
127
+
128
+ def _endPath(self):
129
+ p0 = self._getCurrentPoint()
130
+ if p0 != self._startPoint:
131
+ raise OpenContourError("Glyph statistics not defined on open contours.")
132
+ self._update()
133
+
134
+ def _update(self):
135
+ nodes = self._nodes
136
+ n = len(nodes)
137
+
138
+ # Triangle formula
139
+ self.area = (
140
+ sum(
141
+ (p0.real * p1.imag - p1.real * p0.imag)
142
+ for p0, p1 in zip(nodes, nodes[1:] + nodes[:1])
143
+ )
144
+ / 2
145
+ )
146
+
147
+ # Center of mass
148
+ # https://en.wikipedia.org/wiki/Center_of_mass#A_system_of_particles
149
+ sumNodes = sum(nodes)
150
+ self.meanX = meanX = sumNodes.real / n
151
+ self.meanY = meanY = sumNodes.imag / n
152
+
153
+ if n > 1:
154
+ # Var(X) = (sum[X^2] - sum[X]^2 / n) / (n - 1)
155
+ # https://www.statisticshowto.com/probability-and-statistics/descriptive-statistics/sample-variance/
156
+ self.varianceX = varianceX = (
157
+ sum(p.real * p.real for p in nodes)
158
+ - (sumNodes.real * sumNodes.real) / n
159
+ ) / (n - 1)
160
+ self.varianceY = varianceY = (
161
+ sum(p.imag * p.imag for p in nodes)
162
+ - (sumNodes.imag * sumNodes.imag) / n
163
+ ) / (n - 1)
164
+
165
+ # Covariance(X,Y) = (sum[X.Y] - sum[X].sum[Y] / n) / (n - 1)
166
+ self.covariance = covariance = (
167
+ sum(p.real * p.imag for p in nodes)
168
+ - (sumNodes.real * sumNodes.imag) / n
169
+ ) / (n - 1)
170
+ else:
171
+ self.varianceX = varianceX = 0
172
+ self.varianceY = varianceY = 0
173
+ self.covariance = covariance = 0
174
+
175
+ StatisticsBase._update(self)
176
+
177
+
178
+ def _test(glyphset, upem, glyphs, quiet=False, *, control=False):
179
+ from fontTools.pens.transformPen import TransformPen
180
+ from fontTools.misc.transform import Scale
181
+
182
+ wght_sum = 0
183
+ wght_sum_perceptual = 0
184
+ wdth_sum = 0
185
+ slnt_sum = 0
186
+ slnt_sum_perceptual = 0
187
+ for glyph_name in glyphs:
188
+ glyph = glyphset[glyph_name]
189
+ if control:
190
+ pen = StatisticsControlPen(glyphset=glyphset)
191
+ else:
192
+ pen = StatisticsPen(glyphset=glyphset)
193
+ transformer = TransformPen(pen, Scale(1.0 / upem))
194
+ glyph.draw(transformer)
195
+
196
+ area = abs(pen.area)
197
+ width = glyph.width
198
+ wght_sum += area
199
+ wght_sum_perceptual += pen.area * width
200
+ wdth_sum += width
201
+ slnt_sum += pen.slant
202
+ slnt_sum_perceptual += pen.slant * width
203
+
204
+ if quiet:
205
+ continue
206
+
207
+ print()
208
+ print("glyph:", glyph_name)
209
+
210
+ for item in [
211
+ "area",
212
+ "momentX",
213
+ "momentY",
214
+ "momentXX",
215
+ "momentYY",
216
+ "momentXY",
217
+ "meanX",
218
+ "meanY",
219
+ "varianceX",
220
+ "varianceY",
221
+ "stddevX",
222
+ "stddevY",
223
+ "covariance",
224
+ "correlation",
225
+ "slant",
226
+ ]:
227
+ print("%s: %g" % (item, getattr(pen, item)))
228
+
229
+ if not quiet:
230
+ print()
231
+ print("font:")
232
+
233
+ print("weight: %g" % (wght_sum * upem / wdth_sum))
234
+ print("weight (perceptual): %g" % (wght_sum_perceptual / wdth_sum))
235
+ print("width: %g" % (wdth_sum / upem / len(glyphs)))
236
+ slant = slnt_sum / len(glyphs)
237
+ print("slant: %g" % slant)
238
+ print("slant angle: %g" % -degrees(atan(slant)))
239
+ slant_perceptual = slnt_sum_perceptual / wdth_sum
240
+ print("slant (perceptual): %g" % slant_perceptual)
241
+ print("slant (perceptual) angle: %g" % -degrees(atan(slant_perceptual)))
242
+
243
+
244
+ def main(args):
245
+ """Report font glyph shape geometricsl statistics"""
246
+
247
+ if args is None:
248
+ import sys
249
+
250
+ args = sys.argv[1:]
251
+
252
+ import argparse
253
+
254
+ parser = argparse.ArgumentParser(
255
+ "fonttools pens.statisticsPen",
256
+ description="Report font glyph shape geometricsl statistics",
257
+ )
258
+ parser.add_argument("font", metavar="font.ttf", help="Font file.")
259
+ parser.add_argument("glyphs", metavar="glyph-name", help="Glyph names.", nargs="*")
260
+ parser.add_argument(
261
+ "-y",
262
+ metavar="<number>",
263
+ help="Face index into a collection to open. Zero based.",
264
+ )
265
+ parser.add_argument(
266
+ "-c",
267
+ "--control",
268
+ action="store_true",
269
+ help="Use the control-box pen instead of the Green therem.",
270
+ )
271
+ parser.add_argument(
272
+ "-q", "--quiet", action="store_true", help="Only report font-wide statistics."
273
+ )
274
+ parser.add_argument(
275
+ "--variations",
276
+ metavar="AXIS=LOC",
277
+ default="",
278
+ help="List of space separated locations. A location consist in "
279
+ "the name of a variation axis, followed by '=' and a number. E.g.: "
280
+ "wght=700 wdth=80. The default is the location of the base master.",
281
+ )
282
+
283
+ options = parser.parse_args(args)
284
+
285
+ glyphs = options.glyphs
286
+ fontNumber = int(options.y) if options.y is not None else 0
287
+
288
+ location = {}
289
+ for tag_v in options.variations.split():
290
+ fields = tag_v.split("=")
291
+ tag = fields[0].strip()
292
+ v = int(fields[1])
293
+ location[tag] = v
294
+
295
+ from fontTools.ttLib import TTFont
296
+
297
+ font = TTFont(options.font, fontNumber=fontNumber)
298
+ if not glyphs:
299
+ glyphs = font.getGlyphOrder()
300
+ _test(
301
+ font.getGlyphSet(location=location),
302
+ font["head"].unitsPerEm,
303
+ glyphs,
304
+ quiet=options.quiet,
305
+ control=options.control,
306
+ )
307
+
308
+
309
+ if __name__ == "__main__":
310
+ import sys
311
+
312
+ main(sys.argv[1:])