fonttools 4.58.3__cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (334) hide show
  1. fontTools/__init__.py +8 -0
  2. fontTools/__main__.py +35 -0
  3. fontTools/afmLib.py +439 -0
  4. fontTools/agl.py +5233 -0
  5. fontTools/cffLib/CFF2ToCFF.py +203 -0
  6. fontTools/cffLib/CFFToCFF2.py +305 -0
  7. fontTools/cffLib/__init__.py +3694 -0
  8. fontTools/cffLib/specializer.py +927 -0
  9. fontTools/cffLib/transforms.py +490 -0
  10. fontTools/cffLib/width.py +210 -0
  11. fontTools/colorLib/__init__.py +0 -0
  12. fontTools/colorLib/builder.py +664 -0
  13. fontTools/colorLib/errors.py +2 -0
  14. fontTools/colorLib/geometry.py +143 -0
  15. fontTools/colorLib/table_builder.py +223 -0
  16. fontTools/colorLib/unbuilder.py +81 -0
  17. fontTools/config/__init__.py +90 -0
  18. fontTools/cu2qu/__init__.py +15 -0
  19. fontTools/cu2qu/__main__.py +6 -0
  20. fontTools/cu2qu/benchmark.py +54 -0
  21. fontTools/cu2qu/cli.py +198 -0
  22. fontTools/cu2qu/cu2qu.c +15545 -0
  23. fontTools/cu2qu/cu2qu.cpython-310-x86_64-linux-gnu.so +0 -0
  24. fontTools/cu2qu/cu2qu.py +531 -0
  25. fontTools/cu2qu/errors.py +77 -0
  26. fontTools/cu2qu/ufo.py +349 -0
  27. fontTools/designspaceLib/__init__.py +3338 -0
  28. fontTools/designspaceLib/__main__.py +6 -0
  29. fontTools/designspaceLib/split.py +475 -0
  30. fontTools/designspaceLib/statNames.py +260 -0
  31. fontTools/designspaceLib/types.py +147 -0
  32. fontTools/encodings/MacRoman.py +258 -0
  33. fontTools/encodings/StandardEncoding.py +258 -0
  34. fontTools/encodings/__init__.py +1 -0
  35. fontTools/encodings/codecs.py +135 -0
  36. fontTools/feaLib/__init__.py +4 -0
  37. fontTools/feaLib/__main__.py +78 -0
  38. fontTools/feaLib/ast.py +2142 -0
  39. fontTools/feaLib/builder.py +1796 -0
  40. fontTools/feaLib/error.py +22 -0
  41. fontTools/feaLib/lexer.c +17336 -0
  42. fontTools/feaLib/lexer.cpython-310-x86_64-linux-gnu.so +0 -0
  43. fontTools/feaLib/lexer.py +287 -0
  44. fontTools/feaLib/location.py +12 -0
  45. fontTools/feaLib/lookupDebugInfo.py +12 -0
  46. fontTools/feaLib/parser.py +2379 -0
  47. fontTools/feaLib/variableScalar.py +113 -0
  48. fontTools/fontBuilder.py +1014 -0
  49. fontTools/help.py +36 -0
  50. fontTools/merge/__init__.py +248 -0
  51. fontTools/merge/__main__.py +6 -0
  52. fontTools/merge/base.py +81 -0
  53. fontTools/merge/cmap.py +173 -0
  54. fontTools/merge/layout.py +526 -0
  55. fontTools/merge/options.py +85 -0
  56. fontTools/merge/tables.py +352 -0
  57. fontTools/merge/unicode.py +78 -0
  58. fontTools/merge/util.py +143 -0
  59. fontTools/misc/__init__.py +1 -0
  60. fontTools/misc/arrayTools.py +424 -0
  61. fontTools/misc/bezierTools.c +40136 -0
  62. fontTools/misc/bezierTools.cpython-310-x86_64-linux-gnu.so +0 -0
  63. fontTools/misc/bezierTools.py +1497 -0
  64. fontTools/misc/classifyTools.py +170 -0
  65. fontTools/misc/cliTools.py +53 -0
  66. fontTools/misc/configTools.py +349 -0
  67. fontTools/misc/cython.py +27 -0
  68. fontTools/misc/dictTools.py +83 -0
  69. fontTools/misc/eexec.py +119 -0
  70. fontTools/misc/encodingTools.py +72 -0
  71. fontTools/misc/etree.py +456 -0
  72. fontTools/misc/filenames.py +245 -0
  73. fontTools/misc/fixedTools.py +253 -0
  74. fontTools/misc/intTools.py +25 -0
  75. fontTools/misc/iterTools.py +12 -0
  76. fontTools/misc/lazyTools.py +42 -0
  77. fontTools/misc/loggingTools.py +543 -0
  78. fontTools/misc/macCreatorType.py +56 -0
  79. fontTools/misc/macRes.py +261 -0
  80. fontTools/misc/plistlib/__init__.py +681 -0
  81. fontTools/misc/plistlib/py.typed +0 -0
  82. fontTools/misc/psCharStrings.py +1496 -0
  83. fontTools/misc/psLib.py +398 -0
  84. fontTools/misc/psOperators.py +572 -0
  85. fontTools/misc/py23.py +96 -0
  86. fontTools/misc/roundTools.py +110 -0
  87. fontTools/misc/sstruct.py +231 -0
  88. fontTools/misc/symfont.py +242 -0
  89. fontTools/misc/testTools.py +233 -0
  90. fontTools/misc/textTools.py +154 -0
  91. fontTools/misc/timeTools.py +88 -0
  92. fontTools/misc/transform.py +516 -0
  93. fontTools/misc/treeTools.py +45 -0
  94. fontTools/misc/vector.py +147 -0
  95. fontTools/misc/visitor.py +142 -0
  96. fontTools/misc/xmlReader.py +188 -0
  97. fontTools/misc/xmlWriter.py +204 -0
  98. fontTools/mtiLib/__init__.py +1400 -0
  99. fontTools/mtiLib/__main__.py +5 -0
  100. fontTools/otlLib/__init__.py +1 -0
  101. fontTools/otlLib/builder.py +3435 -0
  102. fontTools/otlLib/error.py +11 -0
  103. fontTools/otlLib/maxContextCalc.py +96 -0
  104. fontTools/otlLib/optimize/__init__.py +53 -0
  105. fontTools/otlLib/optimize/__main__.py +6 -0
  106. fontTools/otlLib/optimize/gpos.py +439 -0
  107. fontTools/pens/__init__.py +1 -0
  108. fontTools/pens/areaPen.py +52 -0
  109. fontTools/pens/basePen.py +475 -0
  110. fontTools/pens/boundsPen.py +98 -0
  111. fontTools/pens/cairoPen.py +26 -0
  112. fontTools/pens/cocoaPen.py +26 -0
  113. fontTools/pens/cu2quPen.py +325 -0
  114. fontTools/pens/explicitClosingLinePen.py +101 -0
  115. fontTools/pens/filterPen.py +241 -0
  116. fontTools/pens/freetypePen.py +462 -0
  117. fontTools/pens/hashPointPen.py +89 -0
  118. fontTools/pens/momentsPen.c +13459 -0
  119. fontTools/pens/momentsPen.cpython-310-x86_64-linux-gnu.so +0 -0
  120. fontTools/pens/momentsPen.py +879 -0
  121. fontTools/pens/perimeterPen.py +69 -0
  122. fontTools/pens/pointInsidePen.py +192 -0
  123. fontTools/pens/pointPen.py +609 -0
  124. fontTools/pens/qtPen.py +29 -0
  125. fontTools/pens/qu2cuPen.py +105 -0
  126. fontTools/pens/quartzPen.py +43 -0
  127. fontTools/pens/recordingPen.py +335 -0
  128. fontTools/pens/reportLabPen.py +79 -0
  129. fontTools/pens/reverseContourPen.py +96 -0
  130. fontTools/pens/roundingPen.py +130 -0
  131. fontTools/pens/statisticsPen.py +312 -0
  132. fontTools/pens/svgPathPen.py +310 -0
  133. fontTools/pens/t2CharStringPen.py +88 -0
  134. fontTools/pens/teePen.py +55 -0
  135. fontTools/pens/transformPen.py +115 -0
  136. fontTools/pens/ttGlyphPen.py +335 -0
  137. fontTools/pens/wxPen.py +29 -0
  138. fontTools/qu2cu/__init__.py +15 -0
  139. fontTools/qu2cu/__main__.py +7 -0
  140. fontTools/qu2cu/benchmark.py +56 -0
  141. fontTools/qu2cu/cli.py +125 -0
  142. fontTools/qu2cu/qu2cu.c +16738 -0
  143. fontTools/qu2cu/qu2cu.cpython-310-x86_64-linux-gnu.so +0 -0
  144. fontTools/qu2cu/qu2cu.py +405 -0
  145. fontTools/subset/__init__.py +3929 -0
  146. fontTools/subset/__main__.py +6 -0
  147. fontTools/subset/cff.py +184 -0
  148. fontTools/subset/svg.py +253 -0
  149. fontTools/subset/util.py +25 -0
  150. fontTools/svgLib/__init__.py +3 -0
  151. fontTools/svgLib/path/__init__.py +65 -0
  152. fontTools/svgLib/path/arc.py +154 -0
  153. fontTools/svgLib/path/parser.py +322 -0
  154. fontTools/svgLib/path/shapes.py +183 -0
  155. fontTools/t1Lib/__init__.py +648 -0
  156. fontTools/tfmLib.py +460 -0
  157. fontTools/ttLib/__init__.py +30 -0
  158. fontTools/ttLib/__main__.py +148 -0
  159. fontTools/ttLib/macUtils.py +54 -0
  160. fontTools/ttLib/removeOverlaps.py +393 -0
  161. fontTools/ttLib/reorderGlyphs.py +285 -0
  162. fontTools/ttLib/scaleUpem.py +436 -0
  163. fontTools/ttLib/sfnt.py +662 -0
  164. fontTools/ttLib/standardGlyphOrder.py +271 -0
  165. fontTools/ttLib/tables/B_A_S_E_.py +14 -0
  166. fontTools/ttLib/tables/BitmapGlyphMetrics.py +64 -0
  167. fontTools/ttLib/tables/C_B_D_T_.py +113 -0
  168. fontTools/ttLib/tables/C_B_L_C_.py +19 -0
  169. fontTools/ttLib/tables/C_F_F_.py +61 -0
  170. fontTools/ttLib/tables/C_F_F__2.py +26 -0
  171. fontTools/ttLib/tables/C_O_L_R_.py +165 -0
  172. fontTools/ttLib/tables/C_P_A_L_.py +305 -0
  173. fontTools/ttLib/tables/D_S_I_G_.py +158 -0
  174. fontTools/ttLib/tables/D__e_b_g.py +35 -0
  175. fontTools/ttLib/tables/DefaultTable.py +49 -0
  176. fontTools/ttLib/tables/E_B_D_T_.py +835 -0
  177. fontTools/ttLib/tables/E_B_L_C_.py +718 -0
  178. fontTools/ttLib/tables/F_F_T_M_.py +52 -0
  179. fontTools/ttLib/tables/F__e_a_t.py +149 -0
  180. fontTools/ttLib/tables/G_D_E_F_.py +13 -0
  181. fontTools/ttLib/tables/G_M_A_P_.py +148 -0
  182. fontTools/ttLib/tables/G_P_K_G_.py +133 -0
  183. fontTools/ttLib/tables/G_P_O_S_.py +14 -0
  184. fontTools/ttLib/tables/G_S_U_B_.py +13 -0
  185. fontTools/ttLib/tables/G_V_A_R_.py +5 -0
  186. fontTools/ttLib/tables/G__l_a_t.py +235 -0
  187. fontTools/ttLib/tables/G__l_o_c.py +85 -0
  188. fontTools/ttLib/tables/H_V_A_R_.py +13 -0
  189. fontTools/ttLib/tables/J_S_T_F_.py +13 -0
  190. fontTools/ttLib/tables/L_T_S_H_.py +58 -0
  191. fontTools/ttLib/tables/M_A_T_H_.py +13 -0
  192. fontTools/ttLib/tables/M_E_T_A_.py +352 -0
  193. fontTools/ttLib/tables/M_V_A_R_.py +13 -0
  194. fontTools/ttLib/tables/O_S_2f_2.py +752 -0
  195. fontTools/ttLib/tables/S_I_N_G_.py +99 -0
  196. fontTools/ttLib/tables/S_T_A_T_.py +15 -0
  197. fontTools/ttLib/tables/S_V_G_.py +223 -0
  198. fontTools/ttLib/tables/S__i_l_f.py +1040 -0
  199. fontTools/ttLib/tables/S__i_l_l.py +92 -0
  200. fontTools/ttLib/tables/T_S_I_B_.py +13 -0
  201. fontTools/ttLib/tables/T_S_I_C_.py +14 -0
  202. fontTools/ttLib/tables/T_S_I_D_.py +13 -0
  203. fontTools/ttLib/tables/T_S_I_J_.py +13 -0
  204. fontTools/ttLib/tables/T_S_I_P_.py +13 -0
  205. fontTools/ttLib/tables/T_S_I_S_.py +13 -0
  206. fontTools/ttLib/tables/T_S_I_V_.py +26 -0
  207. fontTools/ttLib/tables/T_S_I__0.py +70 -0
  208. fontTools/ttLib/tables/T_S_I__1.py +166 -0
  209. fontTools/ttLib/tables/T_S_I__2.py +17 -0
  210. fontTools/ttLib/tables/T_S_I__3.py +22 -0
  211. fontTools/ttLib/tables/T_S_I__5.py +60 -0
  212. fontTools/ttLib/tables/T_T_F_A_.py +14 -0
  213. fontTools/ttLib/tables/TupleVariation.py +884 -0
  214. fontTools/ttLib/tables/V_A_R_C_.py +12 -0
  215. fontTools/ttLib/tables/V_D_M_X_.py +249 -0
  216. fontTools/ttLib/tables/V_O_R_G_.py +165 -0
  217. fontTools/ttLib/tables/V_V_A_R_.py +13 -0
  218. fontTools/ttLib/tables/__init__.py +98 -0
  219. fontTools/ttLib/tables/_a_n_k_r.py +15 -0
  220. fontTools/ttLib/tables/_a_v_a_r.py +191 -0
  221. fontTools/ttLib/tables/_b_s_l_n.py +15 -0
  222. fontTools/ttLib/tables/_c_i_d_g.py +24 -0
  223. fontTools/ttLib/tables/_c_m_a_p.py +1591 -0
  224. fontTools/ttLib/tables/_c_v_a_r.py +94 -0
  225. fontTools/ttLib/tables/_c_v_t.py +57 -0
  226. fontTools/ttLib/tables/_f_e_a_t.py +15 -0
  227. fontTools/ttLib/tables/_f_p_g_m.py +62 -0
  228. fontTools/ttLib/tables/_f_v_a_r.py +261 -0
  229. fontTools/ttLib/tables/_g_a_s_p.py +63 -0
  230. fontTools/ttLib/tables/_g_c_i_d.py +13 -0
  231. fontTools/ttLib/tables/_g_l_y_f.py +2312 -0
  232. fontTools/ttLib/tables/_g_v_a_r.py +337 -0
  233. fontTools/ttLib/tables/_h_d_m_x.py +127 -0
  234. fontTools/ttLib/tables/_h_e_a_d.py +130 -0
  235. fontTools/ttLib/tables/_h_h_e_a.py +147 -0
  236. fontTools/ttLib/tables/_h_m_t_x.py +160 -0
  237. fontTools/ttLib/tables/_k_e_r_n.py +289 -0
  238. fontTools/ttLib/tables/_l_c_a_r.py +13 -0
  239. fontTools/ttLib/tables/_l_o_c_a.py +70 -0
  240. fontTools/ttLib/tables/_l_t_a_g.py +72 -0
  241. fontTools/ttLib/tables/_m_a_x_p.py +147 -0
  242. fontTools/ttLib/tables/_m_e_t_a.py +112 -0
  243. fontTools/ttLib/tables/_m_o_r_t.py +14 -0
  244. fontTools/ttLib/tables/_m_o_r_x.py +15 -0
  245. fontTools/ttLib/tables/_n_a_m_e.py +1237 -0
  246. fontTools/ttLib/tables/_o_p_b_d.py +14 -0
  247. fontTools/ttLib/tables/_p_o_s_t.py +320 -0
  248. fontTools/ttLib/tables/_p_r_e_p.py +16 -0
  249. fontTools/ttLib/tables/_p_r_o_p.py +12 -0
  250. fontTools/ttLib/tables/_s_b_i_x.py +129 -0
  251. fontTools/ttLib/tables/_t_r_a_k.py +332 -0
  252. fontTools/ttLib/tables/_v_h_e_a.py +139 -0
  253. fontTools/ttLib/tables/_v_m_t_x.py +19 -0
  254. fontTools/ttLib/tables/asciiTable.py +20 -0
  255. fontTools/ttLib/tables/grUtils.py +92 -0
  256. fontTools/ttLib/tables/otBase.py +1466 -0
  257. fontTools/ttLib/tables/otConverters.py +2068 -0
  258. fontTools/ttLib/tables/otData.py +6400 -0
  259. fontTools/ttLib/tables/otTables.py +2708 -0
  260. fontTools/ttLib/tables/otTraverse.py +163 -0
  261. fontTools/ttLib/tables/sbixGlyph.py +149 -0
  262. fontTools/ttLib/tables/sbixStrike.py +177 -0
  263. fontTools/ttLib/tables/table_API_readme.txt +91 -0
  264. fontTools/ttLib/tables/ttProgram.py +594 -0
  265. fontTools/ttLib/ttCollection.py +125 -0
  266. fontTools/ttLib/ttFont.py +1157 -0
  267. fontTools/ttLib/ttGlyphSet.py +490 -0
  268. fontTools/ttLib/ttVisitor.py +32 -0
  269. fontTools/ttLib/woff2.py +1683 -0
  270. fontTools/ttx.py +479 -0
  271. fontTools/ufoLib/__init__.py +2477 -0
  272. fontTools/ufoLib/converters.py +398 -0
  273. fontTools/ufoLib/errors.py +30 -0
  274. fontTools/ufoLib/etree.py +6 -0
  275. fontTools/ufoLib/filenames.py +346 -0
  276. fontTools/ufoLib/glifLib.py +2029 -0
  277. fontTools/ufoLib/kerning.py +121 -0
  278. fontTools/ufoLib/plistlib.py +47 -0
  279. fontTools/ufoLib/pointPen.py +6 -0
  280. fontTools/ufoLib/utils.py +79 -0
  281. fontTools/ufoLib/validators.py +1186 -0
  282. fontTools/unicode.py +50 -0
  283. fontTools/unicodedata/Blocks.py +801 -0
  284. fontTools/unicodedata/Mirrored.py +446 -0
  285. fontTools/unicodedata/OTTags.py +50 -0
  286. fontTools/unicodedata/ScriptExtensions.py +826 -0
  287. fontTools/unicodedata/Scripts.py +3617 -0
  288. fontTools/unicodedata/__init__.py +302 -0
  289. fontTools/varLib/__init__.py +1517 -0
  290. fontTools/varLib/__main__.py +6 -0
  291. fontTools/varLib/avar.py +260 -0
  292. fontTools/varLib/avarPlanner.py +1004 -0
  293. fontTools/varLib/builder.py +215 -0
  294. fontTools/varLib/cff.py +631 -0
  295. fontTools/varLib/errors.py +219 -0
  296. fontTools/varLib/featureVars.py +695 -0
  297. fontTools/varLib/hvar.py +113 -0
  298. fontTools/varLib/instancer/__init__.py +1946 -0
  299. fontTools/varLib/instancer/__main__.py +5 -0
  300. fontTools/varLib/instancer/featureVars.py +190 -0
  301. fontTools/varLib/instancer/names.py +388 -0
  302. fontTools/varLib/instancer/solver.py +309 -0
  303. fontTools/varLib/interpolatable.py +1209 -0
  304. fontTools/varLib/interpolatableHelpers.py +396 -0
  305. fontTools/varLib/interpolatablePlot.py +1269 -0
  306. fontTools/varLib/interpolatableTestContourOrder.py +82 -0
  307. fontTools/varLib/interpolatableTestStartingPoint.py +107 -0
  308. fontTools/varLib/interpolate_layout.py +124 -0
  309. fontTools/varLib/iup.c +19830 -0
  310. fontTools/varLib/iup.cpython-310-x86_64-linux-gnu.so +0 -0
  311. fontTools/varLib/iup.py +490 -0
  312. fontTools/varLib/merger.py +1717 -0
  313. fontTools/varLib/models.py +642 -0
  314. fontTools/varLib/multiVarStore.py +253 -0
  315. fontTools/varLib/mutator.py +518 -0
  316. fontTools/varLib/mvar.py +40 -0
  317. fontTools/varLib/plot.py +238 -0
  318. fontTools/varLib/stat.py +149 -0
  319. fontTools/varLib/varStore.py +739 -0
  320. fontTools/voltLib/__init__.py +5 -0
  321. fontTools/voltLib/__main__.py +206 -0
  322. fontTools/voltLib/ast.py +452 -0
  323. fontTools/voltLib/error.py +12 -0
  324. fontTools/voltLib/lexer.py +99 -0
  325. fontTools/voltLib/parser.py +664 -0
  326. fontTools/voltLib/voltToFea.py +911 -0
  327. fonttools-4.58.3.data/data/share/man/man1/ttx.1 +225 -0
  328. fonttools-4.58.3.dist-info/METADATA +2133 -0
  329. fonttools-4.58.3.dist-info/RECORD +334 -0
  330. fonttools-4.58.3.dist-info/WHEEL +6 -0
  331. fonttools-4.58.3.dist-info/entry_points.txt +5 -0
  332. fonttools-4.58.3.dist-info/licenses/LICENSE +21 -0
  333. fonttools-4.58.3.dist-info/licenses/LICENSE.external +359 -0
  334. fonttools-4.58.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,518 @@
1
+ """
2
+ Instantiate a variation font. Run, eg:
3
+
4
+ .. code-block:: sh
5
+
6
+ $ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85
7
+ """
8
+
9
+ from fontTools.misc.fixedTools import floatToFixedToFloat, floatToFixed
10
+ from fontTools.misc.roundTools import otRound
11
+ from fontTools.pens.boundsPen import BoundsPen
12
+ from fontTools.ttLib import TTFont, newTable
13
+ from fontTools.ttLib.tables import ttProgram
14
+ from fontTools.ttLib.tables._g_l_y_f import (
15
+ GlyphCoordinates,
16
+ flagOverlapSimple,
17
+ OVERLAP_COMPOUND,
18
+ )
19
+ from fontTools.varLib.models import (
20
+ supportScalar,
21
+ normalizeLocation,
22
+ piecewiseLinearMap,
23
+ )
24
+ from fontTools.varLib.merger import MutatorMerger
25
+ from fontTools.varLib.varStore import VarStoreInstancer
26
+ from fontTools.varLib.mvar import MVAR_ENTRIES
27
+ from fontTools.varLib.iup import iup_delta
28
+ import fontTools.subset.cff
29
+ import os.path
30
+ import logging
31
+ from io import BytesIO
32
+
33
+
34
+ log = logging.getLogger("fontTools.varlib.mutator")
35
+
36
+ # map 'wdth' axis (1..200) to OS/2.usWidthClass (1..9), rounding to closest
37
+ OS2_WIDTH_CLASS_VALUES = {}
38
+ percents = [50.0, 62.5, 75.0, 87.5, 100.0, 112.5, 125.0, 150.0, 200.0]
39
+ for i, (prev, curr) in enumerate(zip(percents[:-1], percents[1:]), start=1):
40
+ half = (prev + curr) / 2
41
+ OS2_WIDTH_CLASS_VALUES[half] = i
42
+
43
+
44
+ def interpolate_cff2_PrivateDict(topDict, interpolateFromDeltas):
45
+ pd_blend_lists = (
46
+ "BlueValues",
47
+ "OtherBlues",
48
+ "FamilyBlues",
49
+ "FamilyOtherBlues",
50
+ "StemSnapH",
51
+ "StemSnapV",
52
+ )
53
+ pd_blend_values = ("BlueScale", "BlueShift", "BlueFuzz", "StdHW", "StdVW")
54
+ for fontDict in topDict.FDArray:
55
+ pd = fontDict.Private
56
+ vsindex = pd.vsindex if (hasattr(pd, "vsindex")) else 0
57
+ for key, value in pd.rawDict.items():
58
+ if (key in pd_blend_values) and isinstance(value, list):
59
+ delta = interpolateFromDeltas(vsindex, value[1:])
60
+ pd.rawDict[key] = otRound(value[0] + delta)
61
+ elif (key in pd_blend_lists) and isinstance(value[0], list):
62
+ """If any argument in a BlueValues list is a blend list,
63
+ then they all are. The first value of each list is an
64
+ absolute value. The delta tuples are calculated from
65
+ relative master values, hence we need to append all the
66
+ deltas to date to each successive absolute value."""
67
+ delta = 0
68
+ for i, val_list in enumerate(value):
69
+ delta += otRound(interpolateFromDeltas(vsindex, val_list[1:]))
70
+ value[i] = val_list[0] + delta
71
+
72
+
73
+ def interpolate_cff2_charstrings(topDict, interpolateFromDeltas, glyphOrder):
74
+ charstrings = topDict.CharStrings
75
+ for gname in glyphOrder:
76
+ # Interpolate charstring
77
+ # e.g replace blend op args with regular args,
78
+ # and use and discard vsindex op.
79
+ charstring = charstrings[gname]
80
+ new_program = []
81
+ vsindex = 0
82
+ last_i = 0
83
+ for i, token in enumerate(charstring.program):
84
+ if token == "vsindex":
85
+ vsindex = charstring.program[i - 1]
86
+ if last_i != 0:
87
+ new_program.extend(charstring.program[last_i : i - 1])
88
+ last_i = i + 1
89
+ elif token == "blend":
90
+ num_regions = charstring.getNumRegions(vsindex)
91
+ numMasters = 1 + num_regions
92
+ num_args = charstring.program[i - 1]
93
+ # The program list starting at program[i] is now:
94
+ # ..args for following operations
95
+ # num_args values from the default font
96
+ # num_args tuples, each with numMasters-1 delta values
97
+ # num_blend_args
98
+ # 'blend'
99
+ argi = i - (num_args * numMasters + 1)
100
+ end_args = tuplei = argi + num_args
101
+ while argi < end_args:
102
+ next_ti = tuplei + num_regions
103
+ deltas = charstring.program[tuplei:next_ti]
104
+ delta = interpolateFromDeltas(vsindex, deltas)
105
+ charstring.program[argi] += otRound(delta)
106
+ tuplei = next_ti
107
+ argi += 1
108
+ new_program.extend(charstring.program[last_i:end_args])
109
+ last_i = i + 1
110
+ if last_i != 0:
111
+ new_program.extend(charstring.program[last_i:])
112
+ charstring.program = new_program
113
+
114
+
115
+ def interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc):
116
+ """Unlike TrueType glyphs, neither advance width nor bounding box
117
+ info is stored in a CFF2 charstring. The width data exists only in
118
+ the hmtx and HVAR tables. Since LSB data cannot be interpolated
119
+ reliably from the master LSB values in the hmtx table, we traverse
120
+ the charstring to determine the actual bound box."""
121
+
122
+ charstrings = topDict.CharStrings
123
+ boundsPen = BoundsPen(glyphOrder)
124
+ hmtx = varfont["hmtx"]
125
+ hvar_table = None
126
+ if "HVAR" in varfont:
127
+ hvar_table = varfont["HVAR"].table
128
+ fvar = varfont["fvar"]
129
+ varStoreInstancer = VarStoreInstancer(hvar_table.VarStore, fvar.axes, loc)
130
+
131
+ for gid, gname in enumerate(glyphOrder):
132
+ entry = list(hmtx[gname])
133
+ # get width delta.
134
+ if hvar_table:
135
+ if hvar_table.AdvWidthMap:
136
+ width_idx = hvar_table.AdvWidthMap.mapping[gname]
137
+ else:
138
+ width_idx = gid
139
+ width_delta = otRound(varStoreInstancer[width_idx])
140
+ else:
141
+ width_delta = 0
142
+
143
+ # get LSB.
144
+ boundsPen.init()
145
+ charstring = charstrings[gname]
146
+ charstring.draw(boundsPen)
147
+ if boundsPen.bounds is None:
148
+ # Happens with non-marking glyphs
149
+ lsb_delta = 0
150
+ else:
151
+ lsb = otRound(boundsPen.bounds[0])
152
+ lsb_delta = entry[1] - lsb
153
+
154
+ if lsb_delta or width_delta:
155
+ if width_delta:
156
+ entry[0] = max(0, entry[0] + width_delta)
157
+ if lsb_delta:
158
+ entry[1] = lsb
159
+ hmtx[gname] = tuple(entry)
160
+
161
+
162
+ def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
163
+ """Generate a static instance from a variable TTFont and a dictionary
164
+ defining the desired location along the variable font's axes.
165
+ The location values must be specified as user-space coordinates, e.g.:
166
+
167
+ .. code-block::
168
+
169
+ {'wght': 400, 'wdth': 100}
170
+
171
+ By default, a new TTFont object is returned. If ``inplace`` is True, the
172
+ input varfont is modified and reduced to a static font.
173
+
174
+ When the overlap parameter is defined as True,
175
+ OVERLAP_SIMPLE and OVERLAP_COMPOUND bits are set to 1. See
176
+ https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
177
+ """
178
+ if not inplace:
179
+ # make a copy to leave input varfont unmodified
180
+ stream = BytesIO()
181
+ varfont.save(stream)
182
+ stream.seek(0)
183
+ varfont = TTFont(stream)
184
+
185
+ fvar = varfont["fvar"]
186
+ axes = {a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in fvar.axes}
187
+ loc = normalizeLocation(location, axes)
188
+ if "avar" in varfont:
189
+ maps = varfont["avar"].segments
190
+ loc = {k: piecewiseLinearMap(v, maps[k]) for k, v in loc.items()}
191
+ # Quantize to F2Dot14, to avoid surprise interpolations.
192
+ loc = {k: floatToFixedToFloat(v, 14) for k, v in loc.items()}
193
+ # Location is normalized now
194
+ log.info("Normalized location: %s", loc)
195
+
196
+ if "gvar" in varfont:
197
+ log.info("Mutating glyf/gvar tables")
198
+ gvar = varfont["gvar"]
199
+ glyf = varfont["glyf"]
200
+ hMetrics = varfont["hmtx"].metrics
201
+ vMetrics = getattr(varfont.get("vmtx"), "metrics", None)
202
+ # get list of glyph names in gvar sorted by component depth
203
+ glyphnames = sorted(
204
+ gvar.variations.keys(),
205
+ key=lambda name: (
206
+ (
207
+ glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
208
+ if glyf[name].isComposite()
209
+ else 0
210
+ ),
211
+ name,
212
+ ),
213
+ )
214
+ for glyphname in glyphnames:
215
+ variations = gvar.variations[glyphname]
216
+ coordinates, _ = glyf._getCoordinatesAndControls(
217
+ glyphname, hMetrics, vMetrics
218
+ )
219
+ origCoords, endPts = None, None
220
+ for var in variations:
221
+ scalar = supportScalar(loc, var.axes)
222
+ if not scalar:
223
+ continue
224
+ delta = var.coordinates
225
+ if None in delta:
226
+ if origCoords is None:
227
+ origCoords, g = glyf._getCoordinatesAndControls(
228
+ glyphname, hMetrics, vMetrics
229
+ )
230
+ delta = iup_delta(delta, origCoords, g.endPts)
231
+ coordinates += GlyphCoordinates(delta) * scalar
232
+ glyf._setCoordinates(glyphname, coordinates, hMetrics, vMetrics)
233
+ else:
234
+ glyf = None
235
+
236
+ if "DSIG" in varfont:
237
+ del varfont["DSIG"]
238
+
239
+ if "cvar" in varfont:
240
+ log.info("Mutating cvt/cvar tables")
241
+ cvar = varfont["cvar"]
242
+ cvt = varfont["cvt "]
243
+ deltas = {}
244
+ for var in cvar.variations:
245
+ scalar = supportScalar(loc, var.axes)
246
+ if not scalar:
247
+ continue
248
+ for i, c in enumerate(var.coordinates):
249
+ if c is not None:
250
+ deltas[i] = deltas.get(i, 0) + scalar * c
251
+ for i, delta in deltas.items():
252
+ cvt[i] += otRound(delta)
253
+
254
+ if "CFF2" in varfont:
255
+ log.info("Mutating CFF2 table")
256
+ glyphOrder = varfont.getGlyphOrder()
257
+ CFF2 = varfont["CFF2"]
258
+ topDict = CFF2.cff.topDictIndex[0]
259
+ vsInstancer = VarStoreInstancer(topDict.VarStore.otVarStore, fvar.axes, loc)
260
+ interpolateFromDeltas = vsInstancer.interpolateFromDeltas
261
+ interpolate_cff2_PrivateDict(topDict, interpolateFromDeltas)
262
+ CFF2.desubroutinize()
263
+ interpolate_cff2_charstrings(topDict, interpolateFromDeltas, glyphOrder)
264
+ interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc)
265
+ del topDict.rawDict["VarStore"]
266
+ del topDict.VarStore
267
+
268
+ if "MVAR" in varfont:
269
+ log.info("Mutating MVAR table")
270
+ mvar = varfont["MVAR"].table
271
+ varStoreInstancer = VarStoreInstancer(mvar.VarStore, fvar.axes, loc)
272
+ records = mvar.ValueRecord
273
+ for rec in records:
274
+ mvarTag = rec.ValueTag
275
+ if mvarTag not in MVAR_ENTRIES:
276
+ continue
277
+ tableTag, itemName = MVAR_ENTRIES[mvarTag]
278
+ delta = otRound(varStoreInstancer[rec.VarIdx])
279
+ if not delta:
280
+ continue
281
+ setattr(
282
+ varfont[tableTag],
283
+ itemName,
284
+ getattr(varfont[tableTag], itemName) + delta,
285
+ )
286
+
287
+ log.info("Mutating FeatureVariations")
288
+ for tableTag in "GSUB", "GPOS":
289
+ if not tableTag in varfont:
290
+ continue
291
+ table = varfont[tableTag].table
292
+ if not getattr(table, "FeatureVariations", None):
293
+ continue
294
+ variations = table.FeatureVariations
295
+ for record in variations.FeatureVariationRecord:
296
+ applies = True
297
+ for condition in record.ConditionSet.ConditionTable:
298
+ if condition.Format == 1:
299
+ axisIdx = condition.AxisIndex
300
+ axisTag = fvar.axes[axisIdx].axisTag
301
+ Min = condition.FilterRangeMinValue
302
+ Max = condition.FilterRangeMaxValue
303
+ v = loc[axisTag]
304
+ if not (Min <= v <= Max):
305
+ applies = False
306
+ else:
307
+ applies = False
308
+ if not applies:
309
+ break
310
+
311
+ if applies:
312
+ assert record.FeatureTableSubstitution.Version == 0x00010000
313
+ for rec in record.FeatureTableSubstitution.SubstitutionRecord:
314
+ table.FeatureList.FeatureRecord[rec.FeatureIndex].Feature = (
315
+ rec.Feature
316
+ )
317
+ break
318
+ del table.FeatureVariations
319
+
320
+ if "GDEF" in varfont and varfont["GDEF"].table.Version >= 0x00010003:
321
+ log.info("Mutating GDEF/GPOS/GSUB tables")
322
+ gdef = varfont["GDEF"].table
323
+ instancer = VarStoreInstancer(gdef.VarStore, fvar.axes, loc)
324
+
325
+ merger = MutatorMerger(varfont, instancer)
326
+ merger.mergeTables(varfont, [varfont], ["GDEF", "GPOS"])
327
+
328
+ # Downgrade GDEF.
329
+ del gdef.VarStore
330
+ gdef.Version = 0x00010002
331
+ if gdef.MarkGlyphSetsDef is None:
332
+ del gdef.MarkGlyphSetsDef
333
+ gdef.Version = 0x00010000
334
+
335
+ if not (
336
+ gdef.LigCaretList
337
+ or gdef.MarkAttachClassDef
338
+ or gdef.GlyphClassDef
339
+ or gdef.AttachList
340
+ or (gdef.Version >= 0x00010002 and gdef.MarkGlyphSetsDef)
341
+ ):
342
+ del varfont["GDEF"]
343
+
344
+ addidef = False
345
+ if glyf:
346
+ for glyph in glyf.glyphs.values():
347
+ if hasattr(glyph, "program"):
348
+ instructions = glyph.program.getAssembly()
349
+ # If GETVARIATION opcode is used in bytecode of any glyph add IDEF
350
+ addidef = any(op.startswith("GETVARIATION") for op in instructions)
351
+ if addidef:
352
+ break
353
+ if overlap:
354
+ for glyph_name in glyf.keys():
355
+ glyph = glyf[glyph_name]
356
+ # Set OVERLAP_COMPOUND bit for compound glyphs
357
+ if glyph.isComposite():
358
+ glyph.components[0].flags |= OVERLAP_COMPOUND
359
+ # Set OVERLAP_SIMPLE bit for simple glyphs
360
+ elif glyph.numberOfContours > 0:
361
+ glyph.flags[0] |= flagOverlapSimple
362
+ if addidef:
363
+ log.info("Adding IDEF to fpgm table for GETVARIATION opcode")
364
+ asm = []
365
+ if "fpgm" in varfont:
366
+ fpgm = varfont["fpgm"]
367
+ asm = fpgm.program.getAssembly()
368
+ else:
369
+ fpgm = newTable("fpgm")
370
+ fpgm.program = ttProgram.Program()
371
+ varfont["fpgm"] = fpgm
372
+ asm.append("PUSHB[000] 145")
373
+ asm.append("IDEF[ ]")
374
+ args = [str(len(loc))]
375
+ for a in fvar.axes:
376
+ args.append(str(floatToFixed(loc[a.axisTag], 14)))
377
+ asm.append("NPUSHW[ ] " + " ".join(args))
378
+ asm.append("ENDF[ ]")
379
+ fpgm.program.fromAssembly(asm)
380
+
381
+ # Change maxp attributes as IDEF is added
382
+ if "maxp" in varfont:
383
+ maxp = varfont["maxp"]
384
+ setattr(
385
+ maxp, "maxInstructionDefs", 1 + getattr(maxp, "maxInstructionDefs", 0)
386
+ )
387
+ setattr(
388
+ maxp,
389
+ "maxStackElements",
390
+ max(len(loc), getattr(maxp, "maxStackElements", 0)),
391
+ )
392
+
393
+ if "name" in varfont:
394
+ log.info("Pruning name table")
395
+ exclude = {a.axisNameID for a in fvar.axes}
396
+ for i in fvar.instances:
397
+ exclude.add(i.subfamilyNameID)
398
+ exclude.add(i.postscriptNameID)
399
+ if "ltag" in varfont:
400
+ # Drop the whole 'ltag' table if all its language tags are referenced by
401
+ # name records to be pruned.
402
+ # TODO: prune unused ltag tags and re-enumerate langIDs accordingly
403
+ excludedUnicodeLangIDs = [
404
+ n.langID
405
+ for n in varfont["name"].names
406
+ if n.nameID in exclude and n.platformID == 0 and n.langID != 0xFFFF
407
+ ]
408
+ if set(excludedUnicodeLangIDs) == set(range(len((varfont["ltag"].tags)))):
409
+ del varfont["ltag"]
410
+ varfont["name"].names[:] = [
411
+ n
412
+ for n in varfont["name"].names
413
+ if n.nameID < 256 or n.nameID not in exclude
414
+ ]
415
+
416
+ if "wght" in location and "OS/2" in varfont:
417
+ varfont["OS/2"].usWeightClass = otRound(max(1, min(location["wght"], 1000)))
418
+ if "wdth" in location:
419
+ wdth = location["wdth"]
420
+ for percent, widthClass in sorted(OS2_WIDTH_CLASS_VALUES.items()):
421
+ if wdth < percent:
422
+ varfont["OS/2"].usWidthClass = widthClass
423
+ break
424
+ else:
425
+ varfont["OS/2"].usWidthClass = 9
426
+ if "slnt" in location and "post" in varfont:
427
+ varfont["post"].italicAngle = max(-90, min(location["slnt"], 90))
428
+
429
+ log.info("Removing variable tables")
430
+ for tag in ("avar", "cvar", "fvar", "gvar", "HVAR", "MVAR", "VVAR", "STAT"):
431
+ if tag in varfont:
432
+ del varfont[tag]
433
+
434
+ return varfont
435
+
436
+
437
+ def main(args=None):
438
+ """Instantiate a variation font"""
439
+ from fontTools import configLogger
440
+ import argparse
441
+
442
+ parser = argparse.ArgumentParser(
443
+ "fonttools varLib.mutator", description="Instantiate a variable font"
444
+ )
445
+ parser.add_argument("input", metavar="INPUT.ttf", help="Input variable TTF file.")
446
+ parser.add_argument(
447
+ "locargs",
448
+ metavar="AXIS=LOC",
449
+ nargs="*",
450
+ help="List of space separated locations. A location consist in "
451
+ "the name of a variation axis, followed by '=' and a number. E.g.: "
452
+ " wght=700 wdth=80. The default is the location of the base master.",
453
+ )
454
+ parser.add_argument(
455
+ "-o",
456
+ "--output",
457
+ metavar="OUTPUT.ttf",
458
+ default=None,
459
+ help="Output instance TTF file (default: INPUT-instance.ttf).",
460
+ )
461
+ parser.add_argument(
462
+ "--no-recalc-timestamp",
463
+ dest="recalc_timestamp",
464
+ action="store_false",
465
+ help="Don't set the output font's timestamp to the current time.",
466
+ )
467
+ logging_group = parser.add_mutually_exclusive_group(required=False)
468
+ logging_group.add_argument(
469
+ "-v", "--verbose", action="store_true", help="Run more verbosely."
470
+ )
471
+ logging_group.add_argument(
472
+ "-q", "--quiet", action="store_true", help="Turn verbosity off."
473
+ )
474
+ parser.add_argument(
475
+ "--no-overlap",
476
+ dest="overlap",
477
+ action="store_false",
478
+ help="Don't set OVERLAP_SIMPLE/OVERLAP_COMPOUND glyf flags.",
479
+ )
480
+ options = parser.parse_args(args)
481
+
482
+ varfilename = options.input
483
+ outfile = (
484
+ os.path.splitext(varfilename)[0] + "-instance.ttf"
485
+ if not options.output
486
+ else options.output
487
+ )
488
+ configLogger(
489
+ level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO")
490
+ )
491
+
492
+ loc = {}
493
+ for arg in options.locargs:
494
+ try:
495
+ tag, val = arg.split("=")
496
+ assert len(tag) <= 4
497
+ loc[tag.ljust(4)] = float(val)
498
+ except (ValueError, AssertionError):
499
+ parser.error("invalid location argument format: %r" % arg)
500
+ log.info("Location: %s", loc)
501
+
502
+ log.info("Loading variable font")
503
+ varfont = TTFont(varfilename, recalcTimestamp=options.recalc_timestamp)
504
+
505
+ instantiateVariableFont(varfont, loc, inplace=True, overlap=options.overlap)
506
+
507
+ log.info("Saving instance font %s", outfile)
508
+ varfont.save(outfile)
509
+
510
+
511
+ if __name__ == "__main__":
512
+ import sys
513
+
514
+ if len(sys.argv) > 1:
515
+ sys.exit(main())
516
+ import doctest
517
+
518
+ sys.exit(doctest.testmod().failed)
@@ -0,0 +1,40 @@
1
+ MVAR_ENTRIES = {
2
+ "hasc": ("OS/2", "sTypoAscender"), # horizontal ascender
3
+ "hdsc": ("OS/2", "sTypoDescender"), # horizontal descender
4
+ "hlgp": ("OS/2", "sTypoLineGap"), # horizontal line gap
5
+ "hcla": ("OS/2", "usWinAscent"), # horizontal clipping ascent
6
+ "hcld": ("OS/2", "usWinDescent"), # horizontal clipping descent
7
+ "vasc": ("vhea", "ascent"), # vertical ascender
8
+ "vdsc": ("vhea", "descent"), # vertical descender
9
+ "vlgp": ("vhea", "lineGap"), # vertical line gap
10
+ "hcrs": ("hhea", "caretSlopeRise"), # horizontal caret rise
11
+ "hcrn": ("hhea", "caretSlopeRun"), # horizontal caret run
12
+ "hcof": ("hhea", "caretOffset"), # horizontal caret offset
13
+ "vcrs": ("vhea", "caretSlopeRise"), # vertical caret rise
14
+ "vcrn": ("vhea", "caretSlopeRun"), # vertical caret run
15
+ "vcof": ("vhea", "caretOffset"), # vertical caret offset
16
+ "xhgt": ("OS/2", "sxHeight"), # x height
17
+ "cpht": ("OS/2", "sCapHeight"), # cap height
18
+ "sbxs": ("OS/2", "ySubscriptXSize"), # subscript em x size
19
+ "sbys": ("OS/2", "ySubscriptYSize"), # subscript em y size
20
+ "sbxo": ("OS/2", "ySubscriptXOffset"), # subscript em x offset
21
+ "sbyo": ("OS/2", "ySubscriptYOffset"), # subscript em y offset
22
+ "spxs": ("OS/2", "ySuperscriptXSize"), # superscript em x size
23
+ "spys": ("OS/2", "ySuperscriptYSize"), # superscript em y size
24
+ "spxo": ("OS/2", "ySuperscriptXOffset"), # superscript em x offset
25
+ "spyo": ("OS/2", "ySuperscriptYOffset"), # superscript em y offset
26
+ "strs": ("OS/2", "yStrikeoutSize"), # strikeout size
27
+ "stro": ("OS/2", "yStrikeoutPosition"), # strikeout offset
28
+ "unds": ("post", "underlineThickness"), # underline size
29
+ "undo": ("post", "underlinePosition"), # underline offset
30
+ #'gsp0': ('gasp', 'gaspRange[0].rangeMaxPPEM'), # gaspRange[0]
31
+ #'gsp1': ('gasp', 'gaspRange[1].rangeMaxPPEM'), # gaspRange[1]
32
+ #'gsp2': ('gasp', 'gaspRange[2].rangeMaxPPEM'), # gaspRange[2]
33
+ #'gsp3': ('gasp', 'gaspRange[3].rangeMaxPPEM'), # gaspRange[3]
34
+ #'gsp4': ('gasp', 'gaspRange[4].rangeMaxPPEM'), # gaspRange[4]
35
+ #'gsp5': ('gasp', 'gaspRange[5].rangeMaxPPEM'), # gaspRange[5]
36
+ #'gsp6': ('gasp', 'gaspRange[6].rangeMaxPPEM'), # gaspRange[6]
37
+ #'gsp7': ('gasp', 'gaspRange[7].rangeMaxPPEM'), # gaspRange[7]
38
+ #'gsp8': ('gasp', 'gaspRange[8].rangeMaxPPEM'), # gaspRange[8]
39
+ #'gsp9': ('gasp', 'gaspRange[9].rangeMaxPPEM'), # gaspRange[9]
40
+ }