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
fontTools/help.py ADDED
@@ -0,0 +1,36 @@
1
+ import pkgutil
2
+ import sys
3
+ import fontTools
4
+ import importlib
5
+ import os
6
+ from pathlib import Path
7
+
8
+
9
+ def main():
10
+ """Show this help"""
11
+ path = fontTools.__path__
12
+ descriptions = {}
13
+ for pkg in sorted(
14
+ mod.name
15
+ for mod in pkgutil.walk_packages([fontTools.__path__[0]], prefix="fontTools.")
16
+ ):
17
+ try:
18
+ imports = __import__(pkg, globals(), locals(), ["main"])
19
+ except ImportError as e:
20
+ continue
21
+ try:
22
+ description = imports.main.__doc__
23
+ # Cython modules seem to return "main()" as the docstring
24
+ if description and description != "main()":
25
+ pkg = pkg.replace("fontTools.", "").replace(".__main__", "")
26
+ # show the docstring's first line only
27
+ descriptions[pkg] = description.splitlines()[0]
28
+ except AttributeError as e:
29
+ pass
30
+ for pkg, description in descriptions.items():
31
+ print("fonttools %-25s %s" % (pkg, description), file=sys.stderr)
32
+
33
+
34
+ if __name__ == "__main__":
35
+ print("fonttools v%s\n" % fontTools.__version__, file=sys.stderr)
36
+ main()
@@ -0,0 +1,248 @@
1
+ # Copyright 2013 Google, Inc. All Rights Reserved.
2
+ #
3
+ # Google Author(s): Behdad Esfahbod, Roozbeh Pournader
4
+
5
+ from fontTools import ttLib
6
+ import fontTools.merge.base
7
+ from fontTools.merge.cmap import (
8
+ computeMegaGlyphOrder,
9
+ computeMegaCmap,
10
+ renameCFFCharStrings,
11
+ )
12
+ from fontTools.merge.layout import layoutPreMerge, layoutPostMerge
13
+ from fontTools.merge.options import Options
14
+ import fontTools.merge.tables
15
+ from fontTools.misc.loggingTools import Timer
16
+ from functools import reduce
17
+ import sys
18
+ import logging
19
+
20
+
21
+ log = logging.getLogger("fontTools.merge")
22
+ timer = Timer(logger=logging.getLogger(__name__ + ".timer"), level=logging.INFO)
23
+
24
+
25
+ class Merger(object):
26
+ """Font merger.
27
+
28
+ This class merges multiple files into a single OpenType font, taking into
29
+ account complexities such as OpenType layout (``GSUB``/``GPOS``) tables and
30
+ cross-font metrics (for example ``hhea.ascent`` is set to the maximum value
31
+ across all the fonts).
32
+
33
+ If multiple glyphs map to the same Unicode value, and the glyphs are considered
34
+ sufficiently different (that is, they differ in any of paths, widths, or
35
+ height), then subsequent glyphs are renamed and a lookup in the ``locl``
36
+ feature will be created to disambiguate them. For example, if the arguments
37
+ are an Arabic font and a Latin font and both contain a set of parentheses,
38
+ the Latin glyphs will be renamed to ``parenleft.1`` and ``parenright.1``,
39
+ and a lookup will be inserted into the to ``locl`` feature (creating it if
40
+ necessary) under the ``latn`` script to substitute ``parenleft`` with
41
+ ``parenleft.1`` etc.
42
+
43
+ Restrictions:
44
+
45
+ - All fonts must have the same units per em.
46
+ - If duplicate glyph disambiguation takes place as described above then the
47
+ fonts must have a ``GSUB`` table.
48
+
49
+ Attributes:
50
+ options: Currently unused.
51
+ """
52
+
53
+ def __init__(self, options=None):
54
+ if not options:
55
+ options = Options()
56
+
57
+ self.options = options
58
+
59
+ def _openFonts(self, fontfiles):
60
+ fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
61
+ for font, fontfile in zip(fonts, fontfiles):
62
+ font._merger__fontfile = fontfile
63
+ font._merger__name = font["name"].getDebugName(4)
64
+ return fonts
65
+
66
+ def merge(self, fontfiles):
67
+ """Merges fonts together.
68
+
69
+ Args:
70
+ fontfiles: A list of file names to be merged
71
+
72
+ Returns:
73
+ A :class:`fontTools.ttLib.TTFont` object. Call the ``save`` method on
74
+ this to write it out to an OTF file.
75
+ """
76
+ #
77
+ # Settle on a mega glyph order.
78
+ #
79
+ fonts = self._openFonts(fontfiles)
80
+ glyphOrders = [list(font.getGlyphOrder()) for font in fonts]
81
+ computeMegaGlyphOrder(self, glyphOrders)
82
+
83
+ # Take first input file sfntVersion
84
+ sfntVersion = fonts[0].sfntVersion
85
+
86
+ # Reload fonts and set new glyph names on them.
87
+ fonts = self._openFonts(fontfiles)
88
+ for font, glyphOrder in zip(fonts, glyphOrders):
89
+ font.setGlyphOrder(glyphOrder)
90
+ if "CFF " in font:
91
+ renameCFFCharStrings(self, glyphOrder, font["CFF "])
92
+
93
+ cmaps = [font["cmap"] for font in fonts]
94
+ self.duplicateGlyphsPerFont = [{} for _ in fonts]
95
+ computeMegaCmap(self, cmaps)
96
+
97
+ mega = ttLib.TTFont(sfntVersion=sfntVersion)
98
+ mega.setGlyphOrder(self.glyphOrder)
99
+
100
+ for font in fonts:
101
+ self._preMerge(font)
102
+
103
+ self.fonts = fonts
104
+
105
+ allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
106
+ allTags.remove("GlyphOrder")
107
+
108
+ for tag in sorted(allTags):
109
+ if tag in self.options.drop_tables:
110
+ continue
111
+
112
+ with timer("merge '%s'" % tag):
113
+ tables = [font.get(tag, NotImplemented) for font in fonts]
114
+
115
+ log.info("Merging '%s'.", tag)
116
+ clazz = ttLib.getTableClass(tag)
117
+ table = clazz(tag).merge(self, tables)
118
+ # XXX Clean this up and use: table = mergeObjects(tables)
119
+
120
+ if table is not NotImplemented and table is not False:
121
+ mega[tag] = table
122
+ log.info("Merged '%s'.", tag)
123
+ else:
124
+ log.info("Dropped '%s'.", tag)
125
+
126
+ del self.duplicateGlyphsPerFont
127
+ del self.fonts
128
+
129
+ self._postMerge(mega)
130
+
131
+ return mega
132
+
133
+ def mergeObjects(self, returnTable, logic, tables):
134
+ # Right now we don't use self at all. Will use in the future
135
+ # for options and logging.
136
+
137
+ allKeys = set.union(
138
+ set(),
139
+ *(vars(table).keys() for table in tables if table is not NotImplemented),
140
+ )
141
+ for key in allKeys:
142
+ log.info(" %s", key)
143
+ try:
144
+ mergeLogic = logic[key]
145
+ except KeyError:
146
+ try:
147
+ mergeLogic = logic["*"]
148
+ except KeyError:
149
+ raise Exception(
150
+ "Don't know how to merge key %s of class %s"
151
+ % (key, returnTable.__class__.__name__)
152
+ )
153
+ if mergeLogic is NotImplemented:
154
+ continue
155
+ value = mergeLogic(getattr(table, key, NotImplemented) for table in tables)
156
+ if value is not NotImplemented:
157
+ setattr(returnTable, key, value)
158
+
159
+ return returnTable
160
+
161
+ def _preMerge(self, font):
162
+ layoutPreMerge(font)
163
+
164
+ def _postMerge(self, font):
165
+ layoutPostMerge(font)
166
+
167
+ if "OS/2" in font:
168
+ # https://github.com/fonttools/fonttools/issues/2538
169
+ # TODO: Add an option to disable this?
170
+ font["OS/2"].recalcAvgCharWidth(font)
171
+
172
+
173
+ __all__ = ["Options", "Merger", "main"]
174
+
175
+
176
+ @timer("make one with everything (TOTAL TIME)")
177
+ def main(args=None):
178
+ """Merge multiple fonts into one"""
179
+ from fontTools import configLogger
180
+
181
+ if args is None:
182
+ args = sys.argv[1:]
183
+
184
+ options = Options()
185
+ args = options.parse_opts(args)
186
+ fontfiles = []
187
+ if options.input_file:
188
+ with open(options.input_file) as inputfile:
189
+ fontfiles = [
190
+ line.strip()
191
+ for line in inputfile.readlines()
192
+ if not line.lstrip().startswith("#")
193
+ ]
194
+ for g in args:
195
+ fontfiles.append(g)
196
+
197
+ if len(fontfiles) < 1:
198
+ print(
199
+ "usage: fonttools merge [font1 ... fontN] [--input-file=filelist.txt] [--output-file=merged.ttf] [--import-file=tables.ttx]",
200
+ file=sys.stderr,
201
+ )
202
+ print(
203
+ " [--drop-tables=tags] [--verbose] [--timing]",
204
+ file=sys.stderr,
205
+ )
206
+ print("", file=sys.stderr)
207
+ print(" font1 ... fontN Files to merge.", file=sys.stderr)
208
+ print(
209
+ " --input-file=<filename> Read files to merge from a text file, each path new line. # Comment lines allowed.",
210
+ file=sys.stderr,
211
+ )
212
+ print(
213
+ " --output-file=<filename> Specify output file name (default: merged.ttf).",
214
+ file=sys.stderr,
215
+ )
216
+ print(
217
+ " --import-file=<filename> TTX file to import after merging. This can be used to set metadata.",
218
+ file=sys.stderr,
219
+ )
220
+ print(
221
+ " --drop-tables=<table tags> Comma separated list of table tags to skip, case sensitive.",
222
+ file=sys.stderr,
223
+ )
224
+ print(
225
+ " --verbose Output progress information.",
226
+ file=sys.stderr,
227
+ )
228
+ print(" --timing Output progress timing.", file=sys.stderr)
229
+ return 1
230
+
231
+ configLogger(level=logging.INFO if options.verbose else logging.WARNING)
232
+ if options.timing:
233
+ timer.logger.setLevel(logging.DEBUG)
234
+ else:
235
+ timer.logger.disabled = True
236
+
237
+ merger = Merger(options=options)
238
+ font = merger.merge(fontfiles)
239
+
240
+ if options.import_file:
241
+ font.importXML(options.import_file)
242
+
243
+ with timer("compile and save font"):
244
+ font.save(options.output_file)
245
+
246
+
247
+ if __name__ == "__main__":
248
+ sys.exit(main())
@@ -0,0 +1,6 @@
1
+ import sys
2
+ from fontTools.merge import main
3
+
4
+
5
+ if __name__ == "__main__":
6
+ sys.exit(main())
@@ -0,0 +1,81 @@
1
+ # Copyright 2013 Google, Inc. All Rights Reserved.
2
+ #
3
+ # Google Author(s): Behdad Esfahbod, Roozbeh Pournader
4
+
5
+ from fontTools.ttLib.tables.DefaultTable import DefaultTable
6
+ import logging
7
+
8
+
9
+ log = logging.getLogger("fontTools.merge")
10
+
11
+
12
+ def add_method(*clazzes, **kwargs):
13
+ """Returns a decorator function that adds a new method to one or
14
+ more classes."""
15
+ allowDefault = kwargs.get("allowDefaultTable", False)
16
+
17
+ def wrapper(method):
18
+ done = []
19
+ for clazz in clazzes:
20
+ if clazz in done:
21
+ continue # Support multiple names of a clazz
22
+ done.append(clazz)
23
+ assert allowDefault or clazz != DefaultTable, "Oops, table class not found."
24
+ assert (
25
+ method.__name__ not in clazz.__dict__
26
+ ), "Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__)
27
+ setattr(clazz, method.__name__, method)
28
+ return None
29
+
30
+ return wrapper
31
+
32
+
33
+ def mergeObjects(lst):
34
+ lst = [item for item in lst if item is not NotImplemented]
35
+ if not lst:
36
+ return NotImplemented
37
+ lst = [item for item in lst if item is not None]
38
+ if not lst:
39
+ return None
40
+
41
+ clazz = lst[0].__class__
42
+ assert all(type(item) == clazz for item in lst), lst
43
+
44
+ logic = clazz.mergeMap
45
+ returnTable = clazz()
46
+ returnDict = {}
47
+
48
+ allKeys = set.union(set(), *(vars(table).keys() for table in lst))
49
+ for key in allKeys:
50
+ try:
51
+ mergeLogic = logic[key]
52
+ except KeyError:
53
+ try:
54
+ mergeLogic = logic["*"]
55
+ except KeyError:
56
+ raise Exception(
57
+ "Don't know how to merge key %s of class %s" % (key, clazz.__name__)
58
+ )
59
+ if mergeLogic is NotImplemented:
60
+ continue
61
+ value = mergeLogic(getattr(table, key, NotImplemented) for table in lst)
62
+ if value is not NotImplemented:
63
+ returnDict[key] = value
64
+
65
+ returnTable.__dict__ = returnDict
66
+
67
+ return returnTable
68
+
69
+
70
+ @add_method(DefaultTable, allowDefaultTable=True)
71
+ def merge(self, m, tables):
72
+ if not hasattr(self, "mergeMap"):
73
+ log.info("Don't know how to merge '%s'.", self.tableTag)
74
+ return NotImplemented
75
+
76
+ logic = self.mergeMap
77
+
78
+ if isinstance(logic, dict):
79
+ return m.mergeObjects(self, self.mergeMap, tables)
80
+ else:
81
+ return logic(tables)
@@ -0,0 +1,173 @@
1
+ # Copyright 2013 Google, Inc. All Rights Reserved.
2
+ #
3
+ # Google Author(s): Behdad Esfahbod, Roozbeh Pournader
4
+
5
+ from fontTools.merge.unicode import is_Default_Ignorable
6
+ from fontTools.pens.recordingPen import DecomposingRecordingPen
7
+ import logging
8
+
9
+
10
+ log = logging.getLogger("fontTools.merge")
11
+
12
+
13
+ def computeMegaGlyphOrder(merger, glyphOrders):
14
+ """Modifies passed-in glyphOrders to reflect new glyph names.
15
+ Stores merger.glyphOrder."""
16
+ megaOrder = {}
17
+ for glyphOrder in glyphOrders:
18
+ for i, glyphName in enumerate(glyphOrder):
19
+ if glyphName in megaOrder:
20
+ n = megaOrder[glyphName]
21
+ while (glyphName + "." + repr(n)) in megaOrder:
22
+ n += 1
23
+ megaOrder[glyphName] = n
24
+ glyphName += "." + repr(n)
25
+ glyphOrder[i] = glyphName
26
+ megaOrder[glyphName] = 1
27
+ merger.glyphOrder = megaOrder = list(megaOrder.keys())
28
+
29
+
30
+ def _glyphsAreSame(
31
+ glyphSet1,
32
+ glyphSet2,
33
+ glyph1,
34
+ glyph2,
35
+ advanceTolerance=0.05,
36
+ advanceToleranceEmpty=0.20,
37
+ ):
38
+ pen1 = DecomposingRecordingPen(glyphSet1)
39
+ pen2 = DecomposingRecordingPen(glyphSet2)
40
+ g1 = glyphSet1[glyph1]
41
+ g2 = glyphSet2[glyph2]
42
+ g1.draw(pen1)
43
+ g2.draw(pen2)
44
+ if pen1.value != pen2.value:
45
+ return False
46
+ # Allow more width tolerance for glyphs with no ink
47
+ tolerance = advanceTolerance if pen1.value else advanceToleranceEmpty
48
+ # TODO Warn if advances not the same but within tolerance.
49
+ if abs(g1.width - g2.width) > g1.width * tolerance:
50
+ return False
51
+ if hasattr(g1, "height") and g1.height is not None:
52
+ if abs(g1.height - g2.height) > g1.height * tolerance:
53
+ return False
54
+ return True
55
+
56
+
57
+ def computeMegaUvs(merger, uvsTables):
58
+ """Returns merged UVS subtable (cmap format=14)."""
59
+ uvsDict = {}
60
+ cmap = merger.cmap
61
+ for table in uvsTables:
62
+ for variationSelector, uvsMapping in table.uvsDict.items():
63
+ if variationSelector not in uvsDict:
64
+ uvsDict[variationSelector] = {}
65
+ for unicodeValue, glyphName in uvsMapping:
66
+ if cmap.get(unicodeValue) == glyphName:
67
+ # this is a default variation
68
+ glyphName = None
69
+ # prefer previous glyph id if both fonts defined UVS
70
+ if unicodeValue not in uvsDict[variationSelector]:
71
+ uvsDict[variationSelector][unicodeValue] = glyphName
72
+
73
+ for variationSelector in uvsDict:
74
+ uvsDict[variationSelector] = [*uvsDict[variationSelector].items()]
75
+
76
+ return uvsDict
77
+
78
+
79
+ # Valid (format, platformID, platEncID) triplets for cmap subtables containing
80
+ # Unicode BMP-only and Unicode Full Repertoire semantics.
81
+ # Cf. OpenType spec for "Platform specific encodings":
82
+ # https://docs.microsoft.com/en-us/typography/opentype/spec/name
83
+ class _CmapUnicodePlatEncodings:
84
+ BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)}
85
+ FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)}
86
+ UVS = {(14, 0, 5)}
87
+
88
+
89
+ def computeMegaCmap(merger, cmapTables):
90
+ """Sets merger.cmap and merger.uvsDict."""
91
+
92
+ # TODO Handle format=14.
93
+ # Only merge format 4 and 12 Unicode subtables, ignores all other subtables
94
+ # If there is a format 12 table for a font, ignore the format 4 table of it
95
+ chosenCmapTables = []
96
+ chosenUvsTables = []
97
+ for fontIdx, table in enumerate(cmapTables):
98
+ format4 = None
99
+ format12 = None
100
+ format14 = None
101
+ for subtable in table.tables:
102
+ properties = (subtable.format, subtable.platformID, subtable.platEncID)
103
+ if properties in _CmapUnicodePlatEncodings.BMP:
104
+ format4 = subtable
105
+ elif properties in _CmapUnicodePlatEncodings.FullRepertoire:
106
+ format12 = subtable
107
+ elif properties in _CmapUnicodePlatEncodings.UVS:
108
+ format14 = subtable
109
+ else:
110
+ log.warning(
111
+ "Dropped cmap subtable from font '%s':\t"
112
+ "format %2s, platformID %2s, platEncID %2s",
113
+ fontIdx,
114
+ subtable.format,
115
+ subtable.platformID,
116
+ subtable.platEncID,
117
+ )
118
+ if format12 is not None:
119
+ chosenCmapTables.append((format12, fontIdx))
120
+ elif format4 is not None:
121
+ chosenCmapTables.append((format4, fontIdx))
122
+
123
+ if format14 is not None:
124
+ chosenUvsTables.append(format14)
125
+
126
+ # Build the unicode mapping
127
+ merger.cmap = cmap = {}
128
+ fontIndexForGlyph = {}
129
+ glyphSets = [None for f in merger.fonts] if hasattr(merger, "fonts") else None
130
+
131
+ for table, fontIdx in chosenCmapTables:
132
+ # handle duplicates
133
+ for uni, gid in table.cmap.items():
134
+ oldgid = cmap.get(uni, None)
135
+ if oldgid is None:
136
+ cmap[uni] = gid
137
+ fontIndexForGlyph[gid] = fontIdx
138
+ elif is_Default_Ignorable(uni) or uni in (0x25CC,): # U+25CC DOTTED CIRCLE
139
+ continue
140
+ elif oldgid != gid:
141
+ # Char previously mapped to oldgid, now to gid.
142
+ # Record, to fix up in GSUB 'locl' later.
143
+ if merger.duplicateGlyphsPerFont[fontIdx].get(oldgid) is None:
144
+ if glyphSets is not None:
145
+ oldFontIdx = fontIndexForGlyph[oldgid]
146
+ for idx in (fontIdx, oldFontIdx):
147
+ if glyphSets[idx] is None:
148
+ glyphSets[idx] = merger.fonts[idx].getGlyphSet()
149
+ # if _glyphsAreSame(glyphSets[oldFontIdx], glyphSets[fontIdx], oldgid, gid):
150
+ # continue
151
+ merger.duplicateGlyphsPerFont[fontIdx][oldgid] = gid
152
+ elif merger.duplicateGlyphsPerFont[fontIdx][oldgid] != gid:
153
+ # Char previously mapped to oldgid but oldgid is already remapped to a different
154
+ # gid, because of another Unicode character.
155
+ # TODO: Try harder to do something about these.
156
+ log.warning(
157
+ "Dropped mapping from codepoint %#06X to glyphId '%s'", uni, gid
158
+ )
159
+
160
+ merger.uvsDict = computeMegaUvs(merger, chosenUvsTables)
161
+
162
+
163
+ def renameCFFCharStrings(merger, glyphOrder, cffTable):
164
+ """Rename topDictIndex charStrings based on glyphOrder."""
165
+ td = cffTable.cff.topDictIndex[0]
166
+
167
+ charStrings = {}
168
+ for i, v in enumerate(td.CharStrings.charStrings.values()):
169
+ glyphName = glyphOrder[i]
170
+ charStrings[glyphName] = v
171
+ td.CharStrings.charStrings = charStrings
172
+
173
+ td.charset = list(glyphOrder)