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,271 @@
1
+ from fontTools.varLib.models import VariationModel
2
+ from fontTools.varLib.varStore import VarStoreInstancer
3
+ from fontTools.misc.fixedTools import fixedToFloat as fi2fl
4
+ from itertools import product
5
+ import sys
6
+
7
+
8
+ def _denormalize(v, axis):
9
+ if v >= 0:
10
+ return axis.defaultValue + v * (axis.maxValue - axis.defaultValue)
11
+ else:
12
+ return axis.defaultValue + v * (axis.defaultValue - axis.minValue)
13
+
14
+
15
+ def _pruneLocations(locations, poles, axisTags):
16
+ # Now we have all the input locations, find which ones are
17
+ # not needed and remove them.
18
+
19
+ # Note: This algorithm is heavily tied to how VariationModel
20
+ # is implemented. It assumes that input was extracted from
21
+ # VariationModel-generated object, like an ItemVariationStore
22
+ # created by fontmake using varLib.models.VariationModel.
23
+ # Some CoPilot blabbering:
24
+ # I *think* I can prove that this algorithm is correct, but
25
+ # I'm not 100% sure. It's possible that there are edge cases
26
+ # where this algorithm will fail. I'm not sure how to prove
27
+ # that it's correct, but I'm also not sure how to prove that
28
+ # it's incorrect. I'm not sure how to write a test case that
29
+ # would prove that it's incorrect. I'm not sure how to write
30
+ # a test case that would prove that it's correct.
31
+
32
+ model = VariationModel(locations, axisTags)
33
+ modelMapping = model.mapping
34
+ modelSupports = model.supports
35
+ pins = {tuple(k.items()): None for k in poles}
36
+ for location in poles:
37
+ i = locations.index(location)
38
+ i = modelMapping[i]
39
+ support = modelSupports[i]
40
+ supportAxes = set(support.keys())
41
+ for axisTag, (minV, _, maxV) in support.items():
42
+ for v in (minV, maxV):
43
+ if v in (-1, 0, 1):
44
+ continue
45
+ for pin in pins.keys():
46
+ pinLocation = dict(pin)
47
+ pinAxes = set(pinLocation.keys())
48
+ if pinAxes != supportAxes:
49
+ continue
50
+ if axisTag not in pinAxes:
51
+ continue
52
+ if pinLocation[axisTag] == v:
53
+ break
54
+ else:
55
+ # No pin found. Go through the previous masters
56
+ # and find a suitable pin. Going backwards is
57
+ # better because it can find a pin that is close
58
+ # to the pole in more dimensions, and reducing
59
+ # the total number of pins needed.
60
+ for candidateIdx in range(i - 1, -1, -1):
61
+ candidate = modelSupports[candidateIdx]
62
+ candidateAxes = set(candidate.keys())
63
+ if candidateAxes != supportAxes:
64
+ continue
65
+ if axisTag not in candidateAxes:
66
+ continue
67
+ candidate = {
68
+ k: defaultV for k, (_, defaultV, _) in candidate.items()
69
+ }
70
+ if candidate[axisTag] == v:
71
+ pins[tuple(candidate.items())] = None
72
+ break
73
+ else:
74
+ assert False, "No pin found"
75
+ return [dict(t) for t in pins.keys()]
76
+
77
+
78
+ def mappings_from_avar(font, denormalize=True):
79
+ fvarAxes = font["fvar"].axes
80
+ axisMap = {a.axisTag: a for a in fvarAxes}
81
+ axisTags = [a.axisTag for a in fvarAxes]
82
+ axisIndexes = {a.axisTag: i for i, a in enumerate(fvarAxes)}
83
+ if "avar" not in font:
84
+ return {}, {}
85
+ avar = font["avar"]
86
+ axisMaps = {
87
+ tag: seg
88
+ for tag, seg in avar.segments.items()
89
+ if seg and seg != {-1: -1, 0: 0, 1: 1}
90
+ }
91
+ mappings = []
92
+
93
+ if getattr(avar, "majorVersion", 1) == 2:
94
+ varStore = avar.table.VarStore
95
+ regions = varStore.VarRegionList.Region
96
+
97
+ # Find all the input locations; this finds "poles", that are
98
+ # locations of the peaks, and "corners", that are locations
99
+ # of the corners of the regions. These two sets of locations
100
+ # together constitute inputLocations to consider.
101
+
102
+ poles = {(): None} # Just using it as an ordered set
103
+ inputLocations = set({()})
104
+ for varData in varStore.VarData:
105
+ regionIndices = varData.VarRegionIndex
106
+ for regionIndex in regionIndices:
107
+ peakLocation = []
108
+ corners = []
109
+ region = regions[regionIndex]
110
+ for axisIndex, axis in enumerate(region.VarRegionAxis):
111
+ if axis.PeakCoord == 0:
112
+ continue
113
+ axisTag = axisTags[axisIndex]
114
+ peakLocation.append((axisTag, axis.PeakCoord))
115
+ corner = []
116
+ if axis.StartCoord != 0:
117
+ corner.append((axisTag, axis.StartCoord))
118
+ if axis.EndCoord != 0:
119
+ corner.append((axisTag, axis.EndCoord))
120
+ corners.append(corner)
121
+ corners = set(product(*corners))
122
+ peakLocation = tuple(peakLocation)
123
+ poles[peakLocation] = None
124
+ inputLocations.add(peakLocation)
125
+ inputLocations.update(corners)
126
+
127
+ # Sort them by number of axes, then by axis order
128
+ inputLocations = [
129
+ dict(t)
130
+ for t in sorted(
131
+ inputLocations,
132
+ key=lambda t: (len(t), tuple(axisIndexes[tag] for tag, _ in t)),
133
+ )
134
+ ]
135
+ poles = [dict(t) for t in poles.keys()]
136
+ inputLocations = _pruneLocations(inputLocations, list(poles), axisTags)
137
+
138
+ # Find the output locations, at input locations
139
+ varIdxMap = avar.table.VarIdxMap
140
+ instancer = VarStoreInstancer(varStore, fvarAxes)
141
+ for location in inputLocations:
142
+ instancer.setLocation(location)
143
+ outputLocation = {}
144
+ for axisIndex, axisTag in enumerate(axisTags):
145
+ varIdx = axisIndex
146
+ if varIdxMap is not None:
147
+ varIdx = varIdxMap[varIdx]
148
+ delta = instancer[varIdx]
149
+ if delta != 0:
150
+ v = location.get(axisTag, 0)
151
+ v = v + fi2fl(delta, 14)
152
+ # See https://github.com/fonttools/fonttools/pull/3598#issuecomment-2266082009
153
+ # v = max(-1, min(1, v))
154
+ outputLocation[axisTag] = v
155
+ mappings.append((location, outputLocation))
156
+
157
+ # Remove base master we added, if it maps to the default location
158
+ assert mappings[0][0] == {}
159
+ if mappings[0][1] == {}:
160
+ mappings.pop(0)
161
+
162
+ if denormalize:
163
+ for tag, seg in axisMaps.items():
164
+ if tag not in axisMap:
165
+ raise ValueError(f"Unknown axis tag {tag}")
166
+ denorm = lambda v: _denormalize(v, axisMap[tag])
167
+ axisMaps[tag] = {denorm(k): denorm(v) for k, v in seg.items()}
168
+
169
+ for i, (inputLoc, outputLoc) in enumerate(mappings):
170
+ inputLoc = {
171
+ tag: _denormalize(val, axisMap[tag]) for tag, val in inputLoc.items()
172
+ }
173
+ outputLoc = {
174
+ tag: _denormalize(val, axisMap[tag]) for tag, val in outputLoc.items()
175
+ }
176
+ mappings[i] = (inputLoc, outputLoc)
177
+
178
+ return axisMaps, mappings
179
+
180
+
181
+ def unbuild(font, f=sys.stdout):
182
+ fvar = font["fvar"]
183
+ axes = fvar.axes
184
+ segments, mappings = mappings_from_avar(font)
185
+
186
+ if "name" in font:
187
+ name = font["name"]
188
+ axisNames = {axis.axisTag: name.getDebugName(axis.axisNameID) for axis in axes}
189
+ else:
190
+ axisNames = {a.axisTag: a.axisTag for a in axes}
191
+
192
+ print("<?xml version='1.0' encoding='UTF-8'?>", file=f)
193
+ print('<designspace format="5.1">', file=f)
194
+ print(" <axes>", file=f)
195
+ for axis in axes:
196
+
197
+ axisName = axisNames[axis.axisTag]
198
+
199
+ triplet = (axis.minValue, axis.defaultValue, axis.maxValue)
200
+ triplet = [int(v) if v == int(v) else v for v in triplet]
201
+
202
+ axisMap = segments.get(axis.axisTag)
203
+ closing = "/>" if axisMap is None else ">"
204
+
205
+ print(
206
+ f' <axis tag="{axis.axisTag}" name="{axisName}" minimum="{triplet[0]}" maximum="{triplet[2]}" default="{triplet[1]}"{closing}',
207
+ file=f,
208
+ )
209
+ if axisMap is not None:
210
+ for k in sorted(axisMap.keys()):
211
+ v = axisMap[k]
212
+ k = int(k) if k == int(k) else k
213
+ v = int(v) if v == int(v) else v
214
+ print(f' <map input="{k}" output="{v}"/>', file=f)
215
+ print(" </axis>", file=f)
216
+ if mappings:
217
+ print(" <mappings>", file=f)
218
+ for inputLoc, outputLoc in mappings:
219
+ print(" <mapping>", file=f)
220
+ print(" <input>", file=f)
221
+ for tag in sorted(inputLoc.keys()):
222
+ v = inputLoc[tag]
223
+ v = int(v) if v == int(v) else v
224
+ print(
225
+ f' <dimension name="{axisNames[tag]}" xvalue="{v}"/>',
226
+ file=f,
227
+ )
228
+ print(" </input>", file=f)
229
+ print(" <output>", file=f)
230
+ for tag in sorted(outputLoc.keys()):
231
+ v = outputLoc[tag]
232
+ v = int(v) if v == int(v) else v
233
+ print(
234
+ f' <dimension name="{axisNames[tag]}" xvalue="{v}"/>',
235
+ file=f,
236
+ )
237
+ print(" </output>", file=f)
238
+ print(" </mapping>", file=f)
239
+ print(" </mappings>", file=f)
240
+ print(" </axes>", file=f)
241
+ print("</designspace>", file=f)
242
+
243
+
244
+ def main(args=None):
245
+ """Print `avar` table as a designspace snippet."""
246
+
247
+ if args is None:
248
+ args = sys.argv[1:]
249
+
250
+ from fontTools.ttLib import TTFont
251
+ import argparse
252
+
253
+ parser = argparse.ArgumentParser(
254
+ "fonttools varLib.avar.unbuild",
255
+ description="Print `avar` table as a designspace snippet.",
256
+ )
257
+ parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
258
+ options = parser.parse_args(args)
259
+
260
+ font = TTFont(options.font)
261
+ if "fvar" not in font:
262
+ print("Not a variable font.", file=sys.stderr)
263
+ return 1
264
+
265
+ unbuild(font)
266
+
267
+
268
+ if __name__ == "__main__":
269
+ import sys
270
+
271
+ sys.exit(main())
@@ -0,0 +1,8 @@
1
+ def main(args=None):
2
+ from .avar.plan import main
3
+
4
+ main(args)
5
+
6
+
7
+ if __name__ == "__main__":
8
+ main()
@@ -0,0 +1,215 @@
1
+ from fontTools import ttLib
2
+ from fontTools.ttLib.tables import otTables as ot
3
+
4
+ # VariationStore
5
+
6
+
7
+ def buildVarRegionAxis(axisSupport):
8
+ self = ot.VarRegionAxis()
9
+ self.StartCoord, self.PeakCoord, self.EndCoord = [float(v) for v in axisSupport]
10
+ return self
11
+
12
+
13
+ def buildSparseVarRegionAxis(axisIndex, axisSupport):
14
+ self = ot.SparseVarRegionAxis()
15
+ self.AxisIndex = axisIndex
16
+ self.StartCoord, self.PeakCoord, self.EndCoord = [float(v) for v in axisSupport]
17
+ return self
18
+
19
+
20
+ def buildVarRegion(support, axisTags):
21
+ assert all(tag in axisTags for tag in support.keys()), (
22
+ "Unknown axis tag found.",
23
+ support,
24
+ axisTags,
25
+ )
26
+ self = ot.VarRegion()
27
+ self.VarRegionAxis = []
28
+ for tag in axisTags:
29
+ self.VarRegionAxis.append(buildVarRegionAxis(support.get(tag, (0, 0, 0))))
30
+ return self
31
+
32
+
33
+ def buildSparseVarRegion(support, axisTags):
34
+ assert all(tag in axisTags for tag in support.keys()), (
35
+ "Unknown axis tag found.",
36
+ support,
37
+ axisTags,
38
+ )
39
+ self = ot.SparseVarRegion()
40
+ self.SparseVarRegionAxis = []
41
+ for i, tag in enumerate(axisTags):
42
+ if tag not in support:
43
+ continue
44
+ self.SparseVarRegionAxis.append(
45
+ buildSparseVarRegionAxis(i, support.get(tag, (0, 0, 0)))
46
+ )
47
+ self.SparseRegionCount = len(self.SparseVarRegionAxis)
48
+ return self
49
+
50
+
51
+ def buildVarRegionList(supports, axisTags):
52
+ self = ot.VarRegionList()
53
+ self.RegionAxisCount = len(axisTags)
54
+ self.Region = []
55
+ for support in supports:
56
+ self.Region.append(buildVarRegion(support, axisTags))
57
+ self.RegionCount = len(self.Region)
58
+ return self
59
+
60
+
61
+ def buildSparseVarRegionList(supports, axisTags):
62
+ self = ot.SparseVarRegionList()
63
+ self.RegionAxisCount = len(axisTags)
64
+ self.Region = []
65
+ for support in supports:
66
+ self.Region.append(buildSparseVarRegion(support, axisTags))
67
+ self.RegionCount = len(self.Region)
68
+ return self
69
+
70
+
71
+ def _reorderItem(lst, mapping):
72
+ return [lst[i] for i in mapping]
73
+
74
+
75
+ def VarData_calculateNumShorts(self, optimize=False):
76
+ count = self.VarRegionCount
77
+ items = self.Item
78
+ bit_lengths = [0] * count
79
+ for item in items:
80
+ # The "+ (i < -1)" magic is to handle two's-compliment.
81
+ # That is, we want to get back 7 for -128, whereas
82
+ # bit_length() returns 8. Similarly for -65536.
83
+ # The reason "i < -1" is used instead of "i < 0" is that
84
+ # the latter would make it return 0 for "-1" instead of 1.
85
+ bl = [(i + (i < -1)).bit_length() for i in item]
86
+ bit_lengths = [max(*pair) for pair in zip(bl, bit_lengths)]
87
+ # The addition of 8, instead of seven, is to account for the sign bit.
88
+ # This "((b + 8) >> 3) if b else 0" when combined with the above
89
+ # "(i + (i < -1)).bit_length()" is a faster way to compute byte-lengths
90
+ # conforming to:
91
+ #
92
+ # byte_length = (0 if i == 0 else
93
+ # 1 if -128 <= i < 128 else
94
+ # 2 if -65536 <= i < 65536 else
95
+ # ...)
96
+ byte_lengths = [((b + 8) >> 3) if b else 0 for b in bit_lengths]
97
+
98
+ # https://github.com/fonttools/fonttools/issues/2279
99
+ longWords = any(b > 2 for b in byte_lengths)
100
+
101
+ if optimize:
102
+ # Reorder columns such that wider columns come before narrower columns
103
+ mapping = []
104
+ mapping.extend(i for i, b in enumerate(byte_lengths) if b > 2)
105
+ mapping.extend(i for i, b in enumerate(byte_lengths) if b == 2)
106
+ mapping.extend(i for i, b in enumerate(byte_lengths) if b == 1)
107
+
108
+ byte_lengths = _reorderItem(byte_lengths, mapping)
109
+ self.VarRegionIndex = _reorderItem(self.VarRegionIndex, mapping)
110
+ self.VarRegionCount = len(self.VarRegionIndex)
111
+ for i in range(len(items)):
112
+ items[i] = _reorderItem(items[i], mapping)
113
+
114
+ if longWords:
115
+ self.NumShorts = (
116
+ max((i for i, b in enumerate(byte_lengths) if b > 2), default=-1) + 1
117
+ )
118
+ self.NumShorts |= 0x8000
119
+ else:
120
+ self.NumShorts = (
121
+ max((i for i, b in enumerate(byte_lengths) if b > 1), default=-1) + 1
122
+ )
123
+
124
+ self.VarRegionCount = len(self.VarRegionIndex)
125
+ return self
126
+
127
+
128
+ ot.VarData.calculateNumShorts = VarData_calculateNumShorts
129
+
130
+
131
+ def VarData_CalculateNumShorts(self, optimize=True):
132
+ """Deprecated name for VarData_calculateNumShorts() which
133
+ defaults to optimize=True. Use varData.calculateNumShorts()
134
+ or varData.optimize()."""
135
+ return VarData_calculateNumShorts(self, optimize=optimize)
136
+
137
+
138
+ def VarData_optimize(self):
139
+ return VarData_calculateNumShorts(self, optimize=True)
140
+
141
+
142
+ ot.VarData.optimize = VarData_optimize
143
+
144
+
145
+ def buildVarData(varRegionIndices, items, optimize=True):
146
+ self = ot.VarData()
147
+ self.VarRegionIndex = list(varRegionIndices)
148
+ regionCount = self.VarRegionCount = len(self.VarRegionIndex)
149
+ records = self.Item = []
150
+ if items:
151
+ for item in items:
152
+ assert len(item) == regionCount
153
+ records.append(list(item))
154
+ self.ItemCount = len(self.Item)
155
+ self.calculateNumShorts(optimize=optimize)
156
+ return self
157
+
158
+
159
+ def buildVarStore(varRegionList, varDataList):
160
+ self = ot.VarStore()
161
+ self.Format = 1
162
+ self.VarRegionList = varRegionList
163
+ self.VarData = list(varDataList)
164
+ self.VarDataCount = len(self.VarData)
165
+ return self
166
+
167
+
168
+ def buildMultiVarData(varRegionIndices, items):
169
+ self = ot.MultiVarData()
170
+ self.Format = 1
171
+ self.VarRegionIndex = list(varRegionIndices)
172
+ regionCount = self.VarRegionCount = len(self.VarRegionIndex)
173
+ records = self.Item = []
174
+ if items:
175
+ for item in items:
176
+ assert len(item) == regionCount
177
+ records.append(list(item))
178
+ self.ItemCount = len(self.Item)
179
+ return self
180
+
181
+
182
+ def buildMultiVarStore(varRegionList, multiVarDataList):
183
+ self = ot.MultiVarStore()
184
+ self.Format = 1
185
+ self.SparseVarRegionList = varRegionList
186
+ self.MultiVarData = list(multiVarDataList)
187
+ self.MultiVarDataCount = len(self.MultiVarData)
188
+ return self
189
+
190
+
191
+ # Variation helpers
192
+
193
+
194
+ def buildVarIdxMap(varIdxes, glyphOrder):
195
+ self = ot.VarIdxMap()
196
+ self.mapping = {g: v for g, v in zip(glyphOrder, varIdxes)}
197
+ return self
198
+
199
+
200
+ def buildDeltaSetIndexMap(varIdxes):
201
+ mapping = list(varIdxes)
202
+ if all(i == v for i, v in enumerate(mapping)):
203
+ return None
204
+ self = ot.DeltaSetIndexMap()
205
+ self.mapping = mapping
206
+ self.Format = 1 if len(mapping) > 0xFFFF else 0
207
+ return self
208
+
209
+
210
+ def buildVarDevTable(varIdx):
211
+ self = ot.Device()
212
+ self.DeltaFormat = 0x8000
213
+ self.StartSize = varIdx >> 16
214
+ self.EndSize = varIdx & 0xFFFF
215
+ return self