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
Binary file
@@ -0,0 +1,563 @@
1
+ # cython: language_level=3
2
+ # distutils: define_macros=CYTHON_TRACE_NOGIL=1
3
+
4
+ # Copyright 2015 Google Inc. All Rights Reserved.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ try:
19
+ import cython
20
+ except (AttributeError, ImportError):
21
+ # if cython not installed, use mock module with no-op decorators and types
22
+ from fontTools.misc import cython
23
+ COMPILED = cython.compiled
24
+
25
+ import math
26
+
27
+ from .errors import Error as Cu2QuError, ApproxNotFoundError
28
+
29
+
30
+ __all__ = ["curve_to_quadratic", "curves_to_quadratic"]
31
+
32
+ MAX_N = 100
33
+
34
+ NAN = float("NaN")
35
+
36
+
37
+ @cython.cfunc
38
+ @cython.inline
39
+ @cython.returns(cython.double)
40
+ @cython.locals(v1=cython.complex, v2=cython.complex, result=cython.double)
41
+ def dot(v1, v2):
42
+ """Return the dot product of two vectors.
43
+
44
+ Args:
45
+ v1 (complex): First vector.
46
+ v2 (complex): Second vector.
47
+
48
+ Returns:
49
+ double: Dot product.
50
+ """
51
+ result = (v1 * v2.conjugate()).real
52
+ # When vectors are perpendicular (i.e. dot product is 0), the above expression may
53
+ # yield slightly different results when running in pure Python vs C/Cython,
54
+ # both of which are correct within IEEE-754 floating-point precision.
55
+ # It's probably due to the different order of operations and roundings in each
56
+ # implementation. Because we are using the result in a denominator and catching
57
+ # ZeroDivisionError (see `calc_intersect`), it's best to normalize the result here.
58
+ if abs(result) < 1e-15:
59
+ result = 0.0
60
+ return result
61
+
62
+
63
+ @cython.cfunc
64
+ @cython.locals(z=cython.complex, den=cython.double)
65
+ @cython.locals(zr=cython.double, zi=cython.double)
66
+ def _complex_div_by_real(z, den):
67
+ """Divide complex by real using Python's method (two separate divisions).
68
+
69
+ This ensures bit-exact compatibility with Python's complex division,
70
+ avoiding C's multiply-by-reciprocal optimization that can cause 1 ULP differences
71
+ on some platforms/compilers (e.g. clang on macOS arm64).
72
+
73
+ https://github.com/fonttools/fonttools/issues/3928
74
+ """
75
+ zr = z.real
76
+ zi = z.imag
77
+ return complex(zr / den, zi / den)
78
+
79
+
80
+ @cython.cfunc
81
+ @cython.inline
82
+ @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
83
+ @cython.locals(
84
+ _1=cython.complex, _2=cython.complex, _3=cython.complex, _4=cython.complex
85
+ )
86
+ def calc_cubic_points(a, b, c, d):
87
+ _1 = d
88
+ _2 = _complex_div_by_real(c, 3.0) + d
89
+ _3 = _complex_div_by_real(b + c, 3.0) + _2
90
+ _4 = a + d + c + b
91
+ return _1, _2, _3, _4
92
+
93
+
94
+ @cython.cfunc
95
+ @cython.inline
96
+ @cython.locals(
97
+ p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex
98
+ )
99
+ @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
100
+ def calc_cubic_parameters(p0, p1, p2, p3):
101
+ c = (p1 - p0) * 3.0
102
+ b = (p2 - p1) * 3.0 - c
103
+ d = p0
104
+ a = p3 - d - c - b
105
+ return a, b, c, d
106
+
107
+
108
+ @cython.cfunc
109
+ @cython.inline
110
+ @cython.locals(
111
+ p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex
112
+ )
113
+ def split_cubic_into_n_iter(p0, p1, p2, p3, n):
114
+ """Split a cubic Bezier into n equal parts.
115
+
116
+ Splits the curve into `n` equal parts by curve time.
117
+ (t=0..1/n, t=1/n..2/n, ...)
118
+
119
+ Args:
120
+ p0 (complex): Start point of curve.
121
+ p1 (complex): First handle of curve.
122
+ p2 (complex): Second handle of curve.
123
+ p3 (complex): End point of curve.
124
+
125
+ Returns:
126
+ An iterator yielding the control points (four complex values) of the
127
+ subcurves.
128
+ """
129
+ # Hand-coded special-cases
130
+ if n == 2:
131
+ return iter(split_cubic_into_two(p0, p1, p2, p3))
132
+ if n == 3:
133
+ return iter(split_cubic_into_three(p0, p1, p2, p3))
134
+ if n == 4:
135
+ a, b = split_cubic_into_two(p0, p1, p2, p3)
136
+ return iter(
137
+ split_cubic_into_two(a[0], a[1], a[2], a[3])
138
+ + split_cubic_into_two(b[0], b[1], b[2], b[3])
139
+ )
140
+ if n == 6:
141
+ a, b = split_cubic_into_two(p0, p1, p2, p3)
142
+ return iter(
143
+ split_cubic_into_three(a[0], a[1], a[2], a[3])
144
+ + split_cubic_into_three(b[0], b[1], b[2], b[3])
145
+ )
146
+
147
+ return _split_cubic_into_n_gen(p0, p1, p2, p3, n)
148
+
149
+
150
+ @cython.locals(
151
+ p0=cython.complex,
152
+ p1=cython.complex,
153
+ p2=cython.complex,
154
+ p3=cython.complex,
155
+ n=cython.int,
156
+ )
157
+ @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
158
+ @cython.locals(
159
+ dt=cython.double, delta_2=cython.double, delta_3=cython.double, i=cython.int
160
+ )
161
+ @cython.locals(
162
+ a1=cython.complex, b1=cython.complex, c1=cython.complex, d1=cython.complex
163
+ )
164
+ def _split_cubic_into_n_gen(p0, p1, p2, p3, n):
165
+ a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3)
166
+ dt = 1 / n
167
+ delta_2 = dt * dt
168
+ delta_3 = dt * delta_2
169
+ for i in range(n):
170
+ t1 = i * dt
171
+ t1_2 = t1 * t1
172
+ # calc new a, b, c and d
173
+ a1 = a * delta_3
174
+ b1 = (3 * a * t1 + b) * delta_2
175
+ c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt
176
+ d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d
177
+ yield calc_cubic_points(a1, b1, c1, d1)
178
+
179
+
180
+ @cython.cfunc
181
+ @cython.inline
182
+ @cython.locals(
183
+ p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex
184
+ )
185
+ @cython.locals(mid=cython.complex, deriv3=cython.complex)
186
+ def split_cubic_into_two(p0, p1, p2, p3):
187
+ """Split a cubic Bezier into two equal parts.
188
+
189
+ Splits the curve into two equal parts at t = 0.5
190
+
191
+ Args:
192
+ p0 (complex): Start point of curve.
193
+ p1 (complex): First handle of curve.
194
+ p2 (complex): Second handle of curve.
195
+ p3 (complex): End point of curve.
196
+
197
+ Returns:
198
+ tuple: Two cubic Beziers (each expressed as a tuple of four complex
199
+ values).
200
+ """
201
+ mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
202
+ deriv3 = (p3 + p2 - p1 - p0) * 0.125
203
+ return (
204
+ (p0, (p0 + p1) * 0.5, mid - deriv3, mid),
205
+ (mid, mid + deriv3, (p2 + p3) * 0.5, p3),
206
+ )
207
+
208
+
209
+ @cython.cfunc
210
+ @cython.inline
211
+ @cython.locals(
212
+ p0=cython.complex,
213
+ p1=cython.complex,
214
+ p2=cython.complex,
215
+ p3=cython.complex,
216
+ )
217
+ @cython.locals(
218
+ mid1=cython.complex,
219
+ deriv1=cython.complex,
220
+ mid2=cython.complex,
221
+ deriv2=cython.complex,
222
+ )
223
+ def split_cubic_into_three(p0, p1, p2, p3):
224
+ """Split a cubic Bezier into three equal parts.
225
+
226
+ Splits the curve into three equal parts at t = 1/3 and t = 2/3
227
+
228
+ Args:
229
+ p0 (complex): Start point of curve.
230
+ p1 (complex): First handle of curve.
231
+ p2 (complex): Second handle of curve.
232
+ p3 (complex): End point of curve.
233
+
234
+ Returns:
235
+ tuple: Three cubic Beziers (each expressed as a tuple of four complex
236
+ values).
237
+ """
238
+ mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27)
239
+ deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27)
240
+ mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27)
241
+ deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27)
242
+ return (
243
+ (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1),
244
+ (mid1, mid1 + deriv1, mid2 - deriv2, mid2),
245
+ (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3),
246
+ )
247
+
248
+
249
+ @cython.cfunc
250
+ @cython.inline
251
+ @cython.returns(cython.complex)
252
+ @cython.locals(
253
+ t=cython.double,
254
+ p0=cython.complex,
255
+ p1=cython.complex,
256
+ p2=cython.complex,
257
+ p3=cython.complex,
258
+ )
259
+ @cython.locals(_p1=cython.complex, _p2=cython.complex)
260
+ def cubic_approx_control(t, p0, p1, p2, p3):
261
+ """Approximate a cubic Bezier using a quadratic one.
262
+
263
+ Args:
264
+ t (double): Position of control point.
265
+ p0 (complex): Start point of curve.
266
+ p1 (complex): First handle of curve.
267
+ p2 (complex): Second handle of curve.
268
+ p3 (complex): End point of curve.
269
+
270
+ Returns:
271
+ complex: Location of candidate control point on quadratic curve.
272
+ """
273
+ _p1 = p0 + (p1 - p0) * 1.5
274
+ _p2 = p3 + (p2 - p3) * 1.5
275
+ return _p1 + (_p2 - _p1) * t
276
+
277
+
278
+ @cython.cfunc
279
+ @cython.inline
280
+ @cython.returns(cython.complex)
281
+ @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
282
+ @cython.locals(ab=cython.complex, cd=cython.complex, p=cython.complex, h=cython.double)
283
+ def calc_intersect(a, b, c, d):
284
+ """Calculate the intersection of two lines.
285
+
286
+ Args:
287
+ a (complex): Start point of first line.
288
+ b (complex): End point of first line.
289
+ c (complex): Start point of second line.
290
+ d (complex): End point of second line.
291
+
292
+ Returns:
293
+ complex: Location of intersection if one present, ``complex(NaN,NaN)``
294
+ if no intersection was found.
295
+ """
296
+ ab = b - a
297
+ cd = d - c
298
+ p = ab * 1j
299
+ try:
300
+ h = dot(p, a - c) / dot(p, cd)
301
+ except ZeroDivisionError:
302
+ # if 3 or 4 points are equal, we do have an intersection despite the zero-div:
303
+ # return one of the off-curves so that the algorithm can attempt a one-curve
304
+ # solution if it's within tolerance:
305
+ # https://github.com/linebender/kurbo/pull/484
306
+ if b == c and (a == b or c == d):
307
+ return b
308
+ return complex(NAN, NAN)
309
+ return c + cd * h
310
+
311
+
312
+ @cython.cfunc
313
+ @cython.returns(cython.int)
314
+ @cython.locals(
315
+ tolerance=cython.double,
316
+ p0=cython.complex,
317
+ p1=cython.complex,
318
+ p2=cython.complex,
319
+ p3=cython.complex,
320
+ )
321
+ @cython.locals(mid=cython.complex, deriv3=cython.complex)
322
+ def cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
323
+ """Check if a cubic Bezier lies within a given distance of the origin.
324
+
325
+ "Origin" means *the* origin (0,0), not the start of the curve. Note that no
326
+ checks are made on the start and end positions of the curve; this function
327
+ only checks the inside of the curve.
328
+
329
+ Args:
330
+ p0 (complex): Start point of curve.
331
+ p1 (complex): First handle of curve.
332
+ p2 (complex): Second handle of curve.
333
+ p3 (complex): End point of curve.
334
+ tolerance (double): Distance from origin.
335
+
336
+ Returns:
337
+ bool: True if the cubic Bezier ``p`` entirely lies within a distance
338
+ ``tolerance`` of the origin, False otherwise.
339
+ """
340
+ # First check p2 then p1, as p2 has higher error early on.
341
+ if abs(p2) <= tolerance and abs(p1) <= tolerance:
342
+ return True
343
+
344
+ # Split.
345
+ mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
346
+ if abs(mid) > tolerance:
347
+ return False
348
+ deriv3 = (p3 + p2 - p1 - p0) * 0.125
349
+ return cubic_farthest_fit_inside(
350
+ p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance
351
+ ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance)
352
+
353
+
354
+ @cython.cfunc
355
+ @cython.inline
356
+ @cython.locals(tolerance=cython.double)
357
+ @cython.locals(
358
+ q1=cython.complex,
359
+ c0=cython.complex,
360
+ c1=cython.complex,
361
+ c2=cython.complex,
362
+ c3=cython.complex,
363
+ )
364
+ def cubic_approx_quadratic(cubic, tolerance):
365
+ """Approximate a cubic Bezier with a single quadratic within a given tolerance.
366
+
367
+ Args:
368
+ cubic (sequence): Four complex numbers representing control points of
369
+ the cubic Bezier curve.
370
+ tolerance (double): Permitted deviation from the original curve.
371
+
372
+ Returns:
373
+ Three complex numbers representing control points of the quadratic
374
+ curve if it fits within the given tolerance, or ``None`` if no suitable
375
+ curve could be calculated.
376
+ """
377
+
378
+ q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3])
379
+ if math.isnan(q1.imag):
380
+ return None
381
+ c0 = cubic[0]
382
+ c3 = cubic[3]
383
+ c1 = c0 + (q1 - c0) * (2 / 3)
384
+ c2 = c3 + (q1 - c3) * (2 / 3)
385
+ if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance):
386
+ return None
387
+ return c0, q1, c3
388
+
389
+
390
+ @cython.cfunc
391
+ @cython.locals(n=cython.int, tolerance=cython.double)
392
+ @cython.locals(i=cython.int)
393
+ @cython.locals(all_quadratic=cython.int)
394
+ @cython.locals(
395
+ c0=cython.complex, c1=cython.complex, c2=cython.complex, c3=cython.complex
396
+ )
397
+ @cython.locals(
398
+ q0=cython.complex,
399
+ q1=cython.complex,
400
+ next_q1=cython.complex,
401
+ q2=cython.complex,
402
+ d1=cython.complex,
403
+ )
404
+ def cubic_approx_spline(cubic, n, tolerance, all_quadratic):
405
+ """Approximate a cubic Bezier curve with a spline of n quadratics.
406
+
407
+ Args:
408
+ cubic (sequence): Four complex numbers representing control points of
409
+ the cubic Bezier curve.
410
+ n (int): Number of quadratic Bezier curves in the spline.
411
+ tolerance (double): Permitted deviation from the original curve.
412
+
413
+ Returns:
414
+ A list of ``n+2`` complex numbers, representing control points of the
415
+ quadratic spline if it fits within the given tolerance, or ``None`` if
416
+ no suitable spline could be calculated.
417
+ """
418
+
419
+ if n == 1:
420
+ return cubic_approx_quadratic(cubic, tolerance)
421
+ if n == 2 and all_quadratic == False:
422
+ return cubic
423
+
424
+ cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n)
425
+
426
+ # calculate the spline of quadratics and check errors at the same time.
427
+ next_cubic = next(cubics)
428
+ next_q1 = cubic_approx_control(
429
+ 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3]
430
+ )
431
+ q2 = cubic[0]
432
+ d1 = 0j
433
+ spline = [cubic[0], next_q1]
434
+ for i in range(1, n + 1):
435
+ # Current cubic to convert
436
+ c0, c1, c2, c3 = next_cubic
437
+
438
+ # Current quadratic approximation of current cubic
439
+ q0 = q2
440
+ q1 = next_q1
441
+ if i < n:
442
+ next_cubic = next(cubics)
443
+ next_q1 = cubic_approx_control(
444
+ i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3]
445
+ )
446
+ spline.append(next_q1)
447
+ q2 = (q1 + next_q1) * 0.5
448
+ else:
449
+ q2 = c3
450
+
451
+ # End-point deltas
452
+ d0 = d1
453
+ d1 = q2 - c3
454
+
455
+ if abs(d1) > tolerance or not cubic_farthest_fit_inside(
456
+ d0,
457
+ q0 + (q1 - q0) * (2 / 3) - c1,
458
+ q2 + (q1 - q2) * (2 / 3) - c2,
459
+ d1,
460
+ tolerance,
461
+ ):
462
+ return None
463
+ spline.append(cubic[3])
464
+
465
+ return spline
466
+
467
+
468
+ @cython.locals(max_err=cython.double)
469
+ @cython.locals(n=cython.int)
470
+ @cython.locals(all_quadratic=cython.int)
471
+ def curve_to_quadratic(curve, max_err, all_quadratic=True):
472
+ """Approximate a cubic Bezier curve with a spline of n quadratics.
473
+
474
+ Args:
475
+ cubic (sequence): Four 2D tuples representing control points of
476
+ the cubic Bezier curve.
477
+ max_err (double): Permitted deviation from the original curve.
478
+ all_quadratic (bool): If True (default) returned value is a
479
+ quadratic spline. If False, it's either a single quadratic
480
+ curve or a single cubic curve.
481
+
482
+ Returns:
483
+ If all_quadratic is True: A list of 2D tuples, representing
484
+ control points of the quadratic spline if it fits within the
485
+ given tolerance, or ``None`` if no suitable spline could be
486
+ calculated.
487
+
488
+ If all_quadratic is False: Either a quadratic curve (if length
489
+ of output is 3), or a cubic curve (if length of output is 4).
490
+ """
491
+
492
+ curve = [complex(*p) for p in curve]
493
+
494
+ for n in range(1, MAX_N + 1):
495
+ spline = cubic_approx_spline(curve, n, max_err, all_quadratic)
496
+ if spline is not None:
497
+ # done. go home
498
+ return [(s.real, s.imag) for s in spline]
499
+
500
+ raise ApproxNotFoundError(curve)
501
+
502
+
503
+ @cython.locals(l=cython.int, last_i=cython.int, i=cython.int)
504
+ @cython.locals(all_quadratic=cython.int)
505
+ def curves_to_quadratic(curves, max_errors, all_quadratic=True):
506
+ """Return quadratic Bezier splines approximating the input cubic Beziers.
507
+
508
+ Args:
509
+ curves: A sequence of *n* curves, each curve being a sequence of four
510
+ 2D tuples.
511
+ max_errors: A sequence of *n* floats representing the maximum permissible
512
+ deviation from each of the cubic Bezier curves.
513
+ all_quadratic (bool): If True (default) returned values are a
514
+ quadratic spline. If False, they are either a single quadratic
515
+ curve or a single cubic curve.
516
+
517
+ Example::
518
+
519
+ >>> curves_to_quadratic( [
520
+ ... [ (50,50), (100,100), (150,100), (200,50) ],
521
+ ... [ (75,50), (120,100), (150,75), (200,60) ]
522
+ ... ], [1,1] )
523
+ [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]]
524
+
525
+ The returned splines have "implied oncurve points" suitable for use in
526
+ TrueType ``glif`` outlines - i.e. in the first spline returned above,
527
+ the first quadratic segment runs from (50,50) to
528
+ ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...).
529
+
530
+ Returns:
531
+ If all_quadratic is True, a list of splines, each spline being a list
532
+ of 2D tuples.
533
+
534
+ If all_quadratic is False, a list of curves, each curve being a quadratic
535
+ (length 3), or cubic (length 4).
536
+
537
+ Raises:
538
+ fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation
539
+ can be found for all curves with the given parameters.
540
+ """
541
+
542
+ curves = [[complex(*p) for p in curve] for curve in curves]
543
+ assert len(max_errors) == len(curves)
544
+
545
+ l = len(curves)
546
+ splines = [None] * l
547
+ last_i = i = 0
548
+ n = 1
549
+ while True:
550
+ spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic)
551
+ if spline is None:
552
+ if n == MAX_N:
553
+ break
554
+ n += 1
555
+ last_i = i
556
+ continue
557
+ splines[i] = spline
558
+ i = (i + 1) % l
559
+ if i == last_i:
560
+ # done. go home
561
+ return [[(s.real, s.imag) for s in spline] for spline in splines]
562
+
563
+ raise ApproxNotFoundError(curves)
@@ -0,0 +1,77 @@
1
+ # Copyright 2016 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
+ class Error(Exception):
17
+ """Base Cu2Qu exception class for all other errors."""
18
+
19
+
20
+ class ApproxNotFoundError(Error):
21
+ def __init__(self, curve):
22
+ message = "no approximation found: %s" % curve
23
+ super().__init__(message)
24
+ self.curve = curve
25
+
26
+
27
+ class UnequalZipLengthsError(Error):
28
+ pass
29
+
30
+
31
+ class IncompatibleGlyphsError(Error):
32
+ def __init__(self, glyphs):
33
+ assert len(glyphs) > 1
34
+ self.glyphs = glyphs
35
+ names = set(repr(g.name) for g in glyphs)
36
+ if len(names) > 1:
37
+ self.combined_name = "{%s}" % ", ".join(sorted(names))
38
+ else:
39
+ self.combined_name = names.pop()
40
+
41
+ def __repr__(self):
42
+ return "<%s %s>" % (type(self).__name__, self.combined_name)
43
+
44
+
45
+ class IncompatibleSegmentNumberError(IncompatibleGlyphsError):
46
+ def __str__(self):
47
+ return "Glyphs named %s have different number of segments" % (
48
+ self.combined_name
49
+ )
50
+
51
+
52
+ class IncompatibleSegmentTypesError(IncompatibleGlyphsError):
53
+ def __init__(self, glyphs, segments):
54
+ IncompatibleGlyphsError.__init__(self, glyphs)
55
+ self.segments = segments
56
+
57
+ def __str__(self):
58
+ lines = []
59
+ ndigits = len(str(max(self.segments)))
60
+ for i, tags in sorted(self.segments.items()):
61
+ lines.append(
62
+ "%s: (%s)" % (str(i).rjust(ndigits), ", ".join(repr(t) for t in tags))
63
+ )
64
+ return "Glyphs named %s have incompatible segment types:\n %s" % (
65
+ self.combined_name,
66
+ "\n ".join(lines),
67
+ )
68
+
69
+
70
+ class IncompatibleFontsError(Error):
71
+ def __init__(self, glyph_errors):
72
+ self.glyph_errors = glyph_errors
73
+
74
+ def __str__(self):
75
+ return "fonts contains incompatible glyphs: %s" % (
76
+ ", ".join(repr(g) for g in sorted(self.glyph_errors.keys()))
77
+ )