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/cu2qu/ufo.py ADDED
@@ -0,0 +1,363 @@
1
+ # Copyright 2015 Google Inc. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ """Converts cubic bezier curves to quadratic splines.
17
+
18
+ Conversion is performed such that the quadratic splines keep the same end-curve
19
+ tangents as the original cubics. The approach is iterative, increasing the
20
+ number of segments for a spline until the error gets below a bound.
21
+
22
+ Respective curves from multiple fonts will be converted at once to ensure that
23
+ the resulting splines are interpolation-compatible.
24
+ """
25
+
26
+ import logging
27
+ from fontTools.pens.basePen import AbstractPen
28
+ from fontTools.pens.pointPen import PointToSegmentPen
29
+ from fontTools.pens.reverseContourPen import ReverseContourPen
30
+
31
+ from . import curves_to_quadratic
32
+ from .errors import (
33
+ UnequalZipLengthsError,
34
+ IncompatibleSegmentNumberError,
35
+ IncompatibleSegmentTypesError,
36
+ IncompatibleGlyphsError,
37
+ IncompatibleFontsError,
38
+ )
39
+
40
+
41
+ __all__ = ["fonts_to_quadratic", "font_to_quadratic"]
42
+
43
+ # The default approximation error below is a relative value (1/1000 of the EM square).
44
+ # Later on, we convert it to absolute font units by multiplying it by a font's UPEM
45
+ # (see fonts_to_quadratic).
46
+ DEFAULT_MAX_ERR = 0.001
47
+ CURVE_TYPE_LIB_KEY = "com.github.googlei18n.cu2qu.curve_type"
48
+
49
+ logger = logging.getLogger(__name__)
50
+
51
+
52
+ _zip = zip
53
+
54
+
55
+ def zip(*args):
56
+ """Ensure each argument to zip has the same length. Also make sure a list is
57
+ returned for python 2/3 compatibility.
58
+ """
59
+
60
+ if len(set(len(a) for a in args)) != 1:
61
+ raise UnequalZipLengthsError(*args)
62
+ return list(_zip(*args))
63
+
64
+
65
+ class GetSegmentsPen(AbstractPen):
66
+ """Pen to collect segments into lists of points for conversion.
67
+
68
+ Curves always include their initial on-curve point, so some points are
69
+ duplicated between segments.
70
+ """
71
+
72
+ def __init__(self):
73
+ self._last_pt = None
74
+ self.segments = []
75
+
76
+ def _add_segment(self, tag, *args):
77
+ if tag in ["move", "line", "qcurve", "curve"]:
78
+ self._last_pt = args[-1]
79
+ self.segments.append((tag, args))
80
+
81
+ def moveTo(self, pt):
82
+ self._add_segment("move", pt)
83
+
84
+ def lineTo(self, pt):
85
+ self._add_segment("line", pt)
86
+
87
+ def qCurveTo(self, *points):
88
+ self._add_segment("qcurve", self._last_pt, *points)
89
+
90
+ def curveTo(self, *points):
91
+ self._add_segment("curve", self._last_pt, *points)
92
+
93
+ def closePath(self):
94
+ self._add_segment("close")
95
+
96
+ def endPath(self):
97
+ self._add_segment("end")
98
+
99
+ def addComponent(self, glyphName, transformation):
100
+ pass
101
+
102
+
103
+ def _get_segments(glyph):
104
+ """Get a glyph's segments as extracted by GetSegmentsPen."""
105
+
106
+ pen = GetSegmentsPen()
107
+ # glyph.draw(pen)
108
+ # We can't simply draw the glyph with the pen, but we must initialize the
109
+ # PointToSegmentPen explicitly with outputImpliedClosingLine=True.
110
+ # By default PointToSegmentPen does not outputImpliedClosingLine -- unless
111
+ # last and first point on closed contour are duplicated. Because we are
112
+ # converting multiple glyphs at the same time, we want to make sure
113
+ # this function returns the same number of segments, whether or not
114
+ # the last and first point overlap.
115
+ # https://github.com/googlefonts/fontmake/issues/572
116
+ # https://github.com/fonttools/fonttools/pull/1720
117
+ pointPen = PointToSegmentPen(pen, outputImpliedClosingLine=True)
118
+ glyph.drawPoints(pointPen)
119
+ return pen.segments
120
+
121
+
122
+ def _set_segments(glyph, segments, reverse_direction):
123
+ """Draw segments as extracted by GetSegmentsPen back to a glyph."""
124
+
125
+ glyph.clearContours()
126
+ pen = glyph.getPen()
127
+ if reverse_direction:
128
+ pen = ReverseContourPen(pen)
129
+ for tag, args in segments:
130
+ if tag == "move":
131
+ pen.moveTo(*args)
132
+ elif tag == "line":
133
+ pen.lineTo(*args)
134
+ elif tag == "curve":
135
+ pen.curveTo(*args[1:])
136
+ elif tag == "qcurve":
137
+ pen.qCurveTo(*args[1:])
138
+ elif tag == "close":
139
+ pen.closePath()
140
+ elif tag == "end":
141
+ pen.endPath()
142
+ else:
143
+ raise AssertionError('Unhandled segment type "%s"' % tag)
144
+
145
+
146
+ def _segments_to_quadratic(segments, max_err, stats, all_quadratic=True):
147
+ """Return quadratic approximations of cubic segments."""
148
+
149
+ assert all(s[0] == "curve" for s in segments), "Non-cubic given to convert"
150
+
151
+ new_points = curves_to_quadratic([s[1] for s in segments], max_err, all_quadratic)
152
+ n = len(new_points[0])
153
+ assert all(len(s) == n for s in new_points[1:]), "Converted incompatibly"
154
+
155
+ spline_length = str(n - 2)
156
+ stats[spline_length] = stats.get(spline_length, 0) + 1
157
+
158
+ if all_quadratic or n == 3:
159
+ return [("qcurve", p) for p in new_points]
160
+ else:
161
+ return [("curve", p) for p in new_points]
162
+
163
+
164
+ def _glyphs_to_quadratic(glyphs, max_err, reverse_direction, stats, all_quadratic=True):
165
+ """Do the actual conversion of a set of compatible glyphs, after arguments
166
+ have been set up.
167
+
168
+ Empty glyphs (without contours) are ignored and passed through unchanged.
169
+
170
+ Return True if the glyphs were modified, else return False.
171
+ """
172
+
173
+ # Skip empty glyphs (with zero contours)
174
+ non_empty_indices = [i for i, g in enumerate(glyphs) if len(g) > 0]
175
+ if not non_empty_indices:
176
+ return False
177
+
178
+ glyphs = [glyphs[i] for i in non_empty_indices]
179
+ max_err = [max_err[i] for i in non_empty_indices]
180
+
181
+ try:
182
+ segments_by_location = zip(*[_get_segments(g) for g in glyphs])
183
+ except UnequalZipLengthsError:
184
+ raise IncompatibleSegmentNumberError(glyphs)
185
+ if not any(segments_by_location):
186
+ return False
187
+
188
+ # always modify input glyphs if reverse_direction is True
189
+ glyphs_modified = reverse_direction
190
+
191
+ new_segments_by_location = []
192
+ incompatible = {}
193
+ for i, segments in enumerate(segments_by_location):
194
+ tag = segments[0][0]
195
+ if not all(s[0] == tag for s in segments[1:]):
196
+ incompatible[i] = [s[0] for s in segments]
197
+ elif tag == "curve":
198
+ new_segments = _segments_to_quadratic(
199
+ segments, max_err, stats, all_quadratic
200
+ )
201
+ if all_quadratic or new_segments != segments:
202
+ glyphs_modified = True
203
+ segments = new_segments
204
+ new_segments_by_location.append(segments)
205
+
206
+ if glyphs_modified:
207
+ new_segments_by_glyph = zip(*new_segments_by_location)
208
+ for glyph, new_segments in zip(glyphs, new_segments_by_glyph):
209
+ _set_segments(glyph, new_segments, reverse_direction)
210
+
211
+ if incompatible:
212
+ raise IncompatibleSegmentTypesError(glyphs, segments=incompatible)
213
+ return glyphs_modified
214
+
215
+
216
+ def glyphs_to_quadratic(
217
+ glyphs, max_err=None, reverse_direction=False, stats=None, all_quadratic=True
218
+ ):
219
+ """Convert the curves of a set of compatible of glyphs to quadratic.
220
+
221
+ All curves will be converted to quadratic at once, ensuring interpolation
222
+ compatibility. If this is not required, calling glyphs_to_quadratic with one
223
+ glyph at a time may yield slightly more optimized results.
224
+
225
+ Empty glyphs (without contours) are ignored and passed through unchanged.
226
+
227
+ Return True if glyphs were modified, else return False.
228
+
229
+ Raises IncompatibleGlyphsError if glyphs have non-interpolatable outlines.
230
+ """
231
+ if stats is None:
232
+ stats = {}
233
+
234
+ if not max_err:
235
+ # assume 1000 is the default UPEM
236
+ max_err = DEFAULT_MAX_ERR * 1000
237
+
238
+ if isinstance(max_err, (list, tuple)):
239
+ max_errors = max_err
240
+ else:
241
+ max_errors = [max_err] * len(glyphs)
242
+ assert len(max_errors) == len(glyphs)
243
+
244
+ return _glyphs_to_quadratic(
245
+ glyphs, max_errors, reverse_direction, stats, all_quadratic
246
+ )
247
+
248
+
249
+ def fonts_to_quadratic(
250
+ fonts,
251
+ max_err_em=None,
252
+ max_err=None,
253
+ reverse_direction=False,
254
+ stats=None,
255
+ dump_stats=False,
256
+ remember_curve_type=True,
257
+ all_quadratic=True,
258
+ ):
259
+ """Convert the curves of a collection of fonts to quadratic.
260
+
261
+ All curves will be converted to quadratic at once, ensuring interpolation
262
+ compatibility. If this is not required, calling fonts_to_quadratic with one
263
+ font at a time may yield slightly more optimized results.
264
+
265
+ Empty glyphs (without contours) are ignored and passed through unchanged.
266
+
267
+ Return the set of modified glyph names if any, else return an empty set.
268
+
269
+ By default, cu2qu stores the curve type in the fonts' lib, under a private
270
+ key "com.github.googlei18n.cu2qu.curve_type", and will not try to convert
271
+ them again if the curve type is already set to "quadratic".
272
+ Setting 'remember_curve_type' to False disables this optimization.
273
+
274
+ Raises IncompatibleFontsError if same-named glyphs from different fonts
275
+ have non-interpolatable outlines.
276
+ """
277
+
278
+ if remember_curve_type:
279
+ curve_types = {f.lib.get(CURVE_TYPE_LIB_KEY, "cubic") for f in fonts}
280
+ if len(curve_types) == 1:
281
+ curve_type = next(iter(curve_types))
282
+ if curve_type in ("quadratic", "mixed"):
283
+ logger.info("Curves already converted to quadratic")
284
+ return False
285
+ elif curve_type == "cubic":
286
+ pass # keep converting
287
+ else:
288
+ raise NotImplementedError(curve_type)
289
+ elif len(curve_types) > 1:
290
+ # going to crash later if they do differ
291
+ logger.warning("fonts may contain different curve types")
292
+
293
+ if stats is None:
294
+ stats = {}
295
+
296
+ if max_err_em and max_err:
297
+ raise TypeError("Only one of max_err and max_err_em can be specified.")
298
+ if not (max_err_em or max_err):
299
+ max_err_em = DEFAULT_MAX_ERR
300
+
301
+ if isinstance(max_err, (list, tuple)):
302
+ assert len(max_err) == len(fonts)
303
+ max_errors = max_err
304
+ elif max_err:
305
+ max_errors = [max_err] * len(fonts)
306
+
307
+ if isinstance(max_err_em, (list, tuple)):
308
+ assert len(fonts) == len(max_err_em)
309
+ max_errors = [f.info.unitsPerEm * e for f, e in zip(fonts, max_err_em)]
310
+ elif max_err_em:
311
+ max_errors = [f.info.unitsPerEm * max_err_em for f in fonts]
312
+
313
+ modified = set()
314
+ glyph_errors = {}
315
+ for name in set().union(*(f.keys() for f in fonts)):
316
+ glyphs = []
317
+ cur_max_errors = []
318
+ for font, error in zip(fonts, max_errors):
319
+ if name in font:
320
+ glyphs.append(font[name])
321
+ cur_max_errors.append(error)
322
+ try:
323
+ if _glyphs_to_quadratic(
324
+ glyphs, cur_max_errors, reverse_direction, stats, all_quadratic
325
+ ):
326
+ modified.add(name)
327
+ except IncompatibleGlyphsError as exc:
328
+ logger.error(exc)
329
+ glyph_errors[name] = exc
330
+
331
+ if glyph_errors:
332
+ raise IncompatibleFontsError(glyph_errors)
333
+
334
+ if modified and dump_stats:
335
+ spline_lengths = sorted(stats.keys())
336
+ logger.info(
337
+ "New spline lengths: %s"
338
+ % (", ".join("%s: %d" % (l, stats[l]) for l in spline_lengths))
339
+ )
340
+
341
+ if remember_curve_type:
342
+ for font in fonts:
343
+ curve_type = font.lib.get(CURVE_TYPE_LIB_KEY, "cubic")
344
+ new_curve_type = "quadratic" if all_quadratic else "mixed"
345
+ if curve_type != new_curve_type:
346
+ font.lib[CURVE_TYPE_LIB_KEY] = new_curve_type
347
+ return modified
348
+
349
+
350
+ def glyph_to_quadratic(glyph, **kwargs):
351
+ """Convenience wrapper around glyphs_to_quadratic, for just one glyph.
352
+ Return True if the glyph was modified, else return False.
353
+ """
354
+
355
+ return glyphs_to_quadratic([glyph], **kwargs)
356
+
357
+
358
+ def font_to_quadratic(font, **kwargs):
359
+ """Convenience wrapper around fonts_to_quadratic, for just one font.
360
+ Return the set of modified glyph names if any, else return empty set.
361
+ """
362
+
363
+ return fonts_to_quadratic([font], **kwargs)