fonttools 4.60.2__cp311-cp311-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (353) hide show
  1. fontTools/__init__.py +8 -0
  2. fontTools/__main__.py +35 -0
  3. fontTools/afmLib.py +439 -0
  4. fontTools/agl.py +5233 -0
  5. fontTools/annotations.py +30 -0
  6. fontTools/cffLib/CFF2ToCFF.py +258 -0
  7. fontTools/cffLib/CFFToCFF2.py +305 -0
  8. fontTools/cffLib/__init__.py +3694 -0
  9. fontTools/cffLib/specializer.py +927 -0
  10. fontTools/cffLib/transforms.py +495 -0
  11. fontTools/cffLib/width.py +210 -0
  12. fontTools/colorLib/__init__.py +0 -0
  13. fontTools/colorLib/builder.py +664 -0
  14. fontTools/colorLib/errors.py +2 -0
  15. fontTools/colorLib/geometry.py +143 -0
  16. fontTools/colorLib/table_builder.py +223 -0
  17. fontTools/colorLib/unbuilder.py +81 -0
  18. fontTools/config/__init__.py +90 -0
  19. fontTools/cu2qu/__init__.py +15 -0
  20. fontTools/cu2qu/__main__.py +6 -0
  21. fontTools/cu2qu/benchmark.py +54 -0
  22. fontTools/cu2qu/cli.py +198 -0
  23. fontTools/cu2qu/cu2qu.c +15817 -0
  24. fontTools/cu2qu/cu2qu.cp311-win32.pyd +0 -0
  25. fontTools/cu2qu/cu2qu.py +563 -0
  26. fontTools/cu2qu/errors.py +77 -0
  27. fontTools/cu2qu/ufo.py +363 -0
  28. fontTools/designspaceLib/__init__.py +3343 -0
  29. fontTools/designspaceLib/__main__.py +6 -0
  30. fontTools/designspaceLib/split.py +475 -0
  31. fontTools/designspaceLib/statNames.py +260 -0
  32. fontTools/designspaceLib/types.py +147 -0
  33. fontTools/encodings/MacRoman.py +258 -0
  34. fontTools/encodings/StandardEncoding.py +258 -0
  35. fontTools/encodings/__init__.py +1 -0
  36. fontTools/encodings/codecs.py +135 -0
  37. fontTools/feaLib/__init__.py +4 -0
  38. fontTools/feaLib/__main__.py +78 -0
  39. fontTools/feaLib/ast.py +2143 -0
  40. fontTools/feaLib/builder.py +1814 -0
  41. fontTools/feaLib/error.py +22 -0
  42. fontTools/feaLib/lexer.c +17029 -0
  43. fontTools/feaLib/lexer.cp311-win32.pyd +0 -0
  44. fontTools/feaLib/lexer.py +287 -0
  45. fontTools/feaLib/location.py +12 -0
  46. fontTools/feaLib/lookupDebugInfo.py +12 -0
  47. fontTools/feaLib/parser.py +2394 -0
  48. fontTools/feaLib/variableScalar.py +118 -0
  49. fontTools/fontBuilder.py +1014 -0
  50. fontTools/help.py +36 -0
  51. fontTools/merge/__init__.py +248 -0
  52. fontTools/merge/__main__.py +6 -0
  53. fontTools/merge/base.py +81 -0
  54. fontTools/merge/cmap.py +173 -0
  55. fontTools/merge/layout.py +526 -0
  56. fontTools/merge/options.py +85 -0
  57. fontTools/merge/tables.py +352 -0
  58. fontTools/merge/unicode.py +78 -0
  59. fontTools/merge/util.py +143 -0
  60. fontTools/misc/__init__.py +1 -0
  61. fontTools/misc/arrayTools.py +424 -0
  62. fontTools/misc/bezierTools.c +39731 -0
  63. fontTools/misc/bezierTools.cp311-win32.pyd +0 -0
  64. fontTools/misc/bezierTools.py +1500 -0
  65. fontTools/misc/classifyTools.py +170 -0
  66. fontTools/misc/cliTools.py +53 -0
  67. fontTools/misc/configTools.py +349 -0
  68. fontTools/misc/cython.py +27 -0
  69. fontTools/misc/dictTools.py +83 -0
  70. fontTools/misc/eexec.py +119 -0
  71. fontTools/misc/encodingTools.py +72 -0
  72. fontTools/misc/enumTools.py +23 -0
  73. fontTools/misc/etree.py +456 -0
  74. fontTools/misc/filenames.py +245 -0
  75. fontTools/misc/filesystem/__init__.py +68 -0
  76. fontTools/misc/filesystem/_base.py +134 -0
  77. fontTools/misc/filesystem/_copy.py +45 -0
  78. fontTools/misc/filesystem/_errors.py +54 -0
  79. fontTools/misc/filesystem/_info.py +75 -0
  80. fontTools/misc/filesystem/_osfs.py +164 -0
  81. fontTools/misc/filesystem/_path.py +67 -0
  82. fontTools/misc/filesystem/_subfs.py +92 -0
  83. fontTools/misc/filesystem/_tempfs.py +34 -0
  84. fontTools/misc/filesystem/_tools.py +34 -0
  85. fontTools/misc/filesystem/_walk.py +55 -0
  86. fontTools/misc/filesystem/_zipfs.py +204 -0
  87. fontTools/misc/fixedTools.py +253 -0
  88. fontTools/misc/intTools.py +25 -0
  89. fontTools/misc/iterTools.py +12 -0
  90. fontTools/misc/lazyTools.py +42 -0
  91. fontTools/misc/loggingTools.py +543 -0
  92. fontTools/misc/macCreatorType.py +56 -0
  93. fontTools/misc/macRes.py +261 -0
  94. fontTools/misc/plistlib/__init__.py +681 -0
  95. fontTools/misc/plistlib/py.typed +0 -0
  96. fontTools/misc/psCharStrings.py +1511 -0
  97. fontTools/misc/psLib.py +398 -0
  98. fontTools/misc/psOperators.py +572 -0
  99. fontTools/misc/py23.py +96 -0
  100. fontTools/misc/roundTools.py +110 -0
  101. fontTools/misc/sstruct.py +227 -0
  102. fontTools/misc/symfont.py +242 -0
  103. fontTools/misc/testTools.py +233 -0
  104. fontTools/misc/textTools.py +156 -0
  105. fontTools/misc/timeTools.py +88 -0
  106. fontTools/misc/transform.py +516 -0
  107. fontTools/misc/treeTools.py +45 -0
  108. fontTools/misc/vector.py +147 -0
  109. fontTools/misc/visitor.py +158 -0
  110. fontTools/misc/xmlReader.py +188 -0
  111. fontTools/misc/xmlWriter.py +231 -0
  112. fontTools/mtiLib/__init__.py +1400 -0
  113. fontTools/mtiLib/__main__.py +5 -0
  114. fontTools/otlLib/__init__.py +1 -0
  115. fontTools/otlLib/builder.py +3465 -0
  116. fontTools/otlLib/error.py +11 -0
  117. fontTools/otlLib/maxContextCalc.py +96 -0
  118. fontTools/otlLib/optimize/__init__.py +53 -0
  119. fontTools/otlLib/optimize/__main__.py +6 -0
  120. fontTools/otlLib/optimize/gpos.py +439 -0
  121. fontTools/pens/__init__.py +1 -0
  122. fontTools/pens/areaPen.py +52 -0
  123. fontTools/pens/basePen.py +475 -0
  124. fontTools/pens/boundsPen.py +98 -0
  125. fontTools/pens/cairoPen.py +26 -0
  126. fontTools/pens/cocoaPen.py +26 -0
  127. fontTools/pens/cu2quPen.py +325 -0
  128. fontTools/pens/explicitClosingLinePen.py +101 -0
  129. fontTools/pens/filterPen.py +433 -0
  130. fontTools/pens/freetypePen.py +462 -0
  131. fontTools/pens/hashPointPen.py +89 -0
  132. fontTools/pens/momentsPen.c +13378 -0
  133. fontTools/pens/momentsPen.cp311-win32.pyd +0 -0
  134. fontTools/pens/momentsPen.py +879 -0
  135. fontTools/pens/perimeterPen.py +69 -0
  136. fontTools/pens/pointInsidePen.py +192 -0
  137. fontTools/pens/pointPen.py +643 -0
  138. fontTools/pens/qtPen.py +29 -0
  139. fontTools/pens/qu2cuPen.py +105 -0
  140. fontTools/pens/quartzPen.py +43 -0
  141. fontTools/pens/recordingPen.py +335 -0
  142. fontTools/pens/reportLabPen.py +79 -0
  143. fontTools/pens/reverseContourPen.py +96 -0
  144. fontTools/pens/roundingPen.py +130 -0
  145. fontTools/pens/statisticsPen.py +312 -0
  146. fontTools/pens/svgPathPen.py +310 -0
  147. fontTools/pens/t2CharStringPen.py +88 -0
  148. fontTools/pens/teePen.py +55 -0
  149. fontTools/pens/transformPen.py +115 -0
  150. fontTools/pens/ttGlyphPen.py +335 -0
  151. fontTools/pens/wxPen.py +29 -0
  152. fontTools/qu2cu/__init__.py +15 -0
  153. fontTools/qu2cu/__main__.py +7 -0
  154. fontTools/qu2cu/benchmark.py +56 -0
  155. fontTools/qu2cu/cli.py +125 -0
  156. fontTools/qu2cu/qu2cu.c +16682 -0
  157. fontTools/qu2cu/qu2cu.cp311-win32.pyd +0 -0
  158. fontTools/qu2cu/qu2cu.py +405 -0
  159. fontTools/subset/__init__.py +4096 -0
  160. fontTools/subset/__main__.py +6 -0
  161. fontTools/subset/cff.py +184 -0
  162. fontTools/subset/svg.py +253 -0
  163. fontTools/subset/util.py +25 -0
  164. fontTools/svgLib/__init__.py +3 -0
  165. fontTools/svgLib/path/__init__.py +65 -0
  166. fontTools/svgLib/path/arc.py +154 -0
  167. fontTools/svgLib/path/parser.py +322 -0
  168. fontTools/svgLib/path/shapes.py +183 -0
  169. fontTools/t1Lib/__init__.py +648 -0
  170. fontTools/tfmLib.py +460 -0
  171. fontTools/ttLib/__init__.py +30 -0
  172. fontTools/ttLib/__main__.py +148 -0
  173. fontTools/ttLib/macUtils.py +54 -0
  174. fontTools/ttLib/removeOverlaps.py +395 -0
  175. fontTools/ttLib/reorderGlyphs.py +285 -0
  176. fontTools/ttLib/scaleUpem.py +436 -0
  177. fontTools/ttLib/sfnt.py +661 -0
  178. fontTools/ttLib/standardGlyphOrder.py +271 -0
  179. fontTools/ttLib/tables/B_A_S_E_.py +14 -0
  180. fontTools/ttLib/tables/BitmapGlyphMetrics.py +64 -0
  181. fontTools/ttLib/tables/C_B_D_T_.py +113 -0
  182. fontTools/ttLib/tables/C_B_L_C_.py +19 -0
  183. fontTools/ttLib/tables/C_F_F_.py +61 -0
  184. fontTools/ttLib/tables/C_F_F__2.py +26 -0
  185. fontTools/ttLib/tables/C_O_L_R_.py +165 -0
  186. fontTools/ttLib/tables/C_P_A_L_.py +305 -0
  187. fontTools/ttLib/tables/D_S_I_G_.py +158 -0
  188. fontTools/ttLib/tables/D__e_b_g.py +35 -0
  189. fontTools/ttLib/tables/DefaultTable.py +49 -0
  190. fontTools/ttLib/tables/E_B_D_T_.py +835 -0
  191. fontTools/ttLib/tables/E_B_L_C_.py +718 -0
  192. fontTools/ttLib/tables/F_F_T_M_.py +52 -0
  193. fontTools/ttLib/tables/F__e_a_t.py +149 -0
  194. fontTools/ttLib/tables/G_D_E_F_.py +13 -0
  195. fontTools/ttLib/tables/G_M_A_P_.py +148 -0
  196. fontTools/ttLib/tables/G_P_K_G_.py +133 -0
  197. fontTools/ttLib/tables/G_P_O_S_.py +14 -0
  198. fontTools/ttLib/tables/G_S_U_B_.py +13 -0
  199. fontTools/ttLib/tables/G_V_A_R_.py +5 -0
  200. fontTools/ttLib/tables/G__l_a_t.py +235 -0
  201. fontTools/ttLib/tables/G__l_o_c.py +85 -0
  202. fontTools/ttLib/tables/H_V_A_R_.py +13 -0
  203. fontTools/ttLib/tables/J_S_T_F_.py +13 -0
  204. fontTools/ttLib/tables/L_T_S_H_.py +58 -0
  205. fontTools/ttLib/tables/M_A_T_H_.py +13 -0
  206. fontTools/ttLib/tables/M_E_T_A_.py +352 -0
  207. fontTools/ttLib/tables/M_V_A_R_.py +13 -0
  208. fontTools/ttLib/tables/O_S_2f_2.py +752 -0
  209. fontTools/ttLib/tables/S_I_N_G_.py +99 -0
  210. fontTools/ttLib/tables/S_T_A_T_.py +15 -0
  211. fontTools/ttLib/tables/S_V_G_.py +223 -0
  212. fontTools/ttLib/tables/S__i_l_f.py +1040 -0
  213. fontTools/ttLib/tables/S__i_l_l.py +92 -0
  214. fontTools/ttLib/tables/T_S_I_B_.py +13 -0
  215. fontTools/ttLib/tables/T_S_I_C_.py +14 -0
  216. fontTools/ttLib/tables/T_S_I_D_.py +13 -0
  217. fontTools/ttLib/tables/T_S_I_J_.py +13 -0
  218. fontTools/ttLib/tables/T_S_I_P_.py +13 -0
  219. fontTools/ttLib/tables/T_S_I_S_.py +13 -0
  220. fontTools/ttLib/tables/T_S_I_V_.py +26 -0
  221. fontTools/ttLib/tables/T_S_I__0.py +70 -0
  222. fontTools/ttLib/tables/T_S_I__1.py +163 -0
  223. fontTools/ttLib/tables/T_S_I__2.py +17 -0
  224. fontTools/ttLib/tables/T_S_I__3.py +22 -0
  225. fontTools/ttLib/tables/T_S_I__5.py +60 -0
  226. fontTools/ttLib/tables/T_T_F_A_.py +14 -0
  227. fontTools/ttLib/tables/TupleVariation.py +884 -0
  228. fontTools/ttLib/tables/V_A_R_C_.py +12 -0
  229. fontTools/ttLib/tables/V_D_M_X_.py +249 -0
  230. fontTools/ttLib/tables/V_O_R_G_.py +165 -0
  231. fontTools/ttLib/tables/V_V_A_R_.py +13 -0
  232. fontTools/ttLib/tables/__init__.py +98 -0
  233. fontTools/ttLib/tables/_a_n_k_r.py +15 -0
  234. fontTools/ttLib/tables/_a_v_a_r.py +193 -0
  235. fontTools/ttLib/tables/_b_s_l_n.py +15 -0
  236. fontTools/ttLib/tables/_c_i_d_g.py +24 -0
  237. fontTools/ttLib/tables/_c_m_a_p.py +1591 -0
  238. fontTools/ttLib/tables/_c_v_a_r.py +94 -0
  239. fontTools/ttLib/tables/_c_v_t.py +56 -0
  240. fontTools/ttLib/tables/_f_e_a_t.py +15 -0
  241. fontTools/ttLib/tables/_f_p_g_m.py +62 -0
  242. fontTools/ttLib/tables/_f_v_a_r.py +261 -0
  243. fontTools/ttLib/tables/_g_a_s_p.py +63 -0
  244. fontTools/ttLib/tables/_g_c_i_d.py +13 -0
  245. fontTools/ttLib/tables/_g_l_y_f.py +2311 -0
  246. fontTools/ttLib/tables/_g_v_a_r.py +340 -0
  247. fontTools/ttLib/tables/_h_d_m_x.py +127 -0
  248. fontTools/ttLib/tables/_h_e_a_d.py +130 -0
  249. fontTools/ttLib/tables/_h_h_e_a.py +147 -0
  250. fontTools/ttLib/tables/_h_m_t_x.py +164 -0
  251. fontTools/ttLib/tables/_k_e_r_n.py +289 -0
  252. fontTools/ttLib/tables/_l_c_a_r.py +13 -0
  253. fontTools/ttLib/tables/_l_o_c_a.py +70 -0
  254. fontTools/ttLib/tables/_l_t_a_g.py +72 -0
  255. fontTools/ttLib/tables/_m_a_x_p.py +147 -0
  256. fontTools/ttLib/tables/_m_e_t_a.py +112 -0
  257. fontTools/ttLib/tables/_m_o_r_t.py +14 -0
  258. fontTools/ttLib/tables/_m_o_r_x.py +15 -0
  259. fontTools/ttLib/tables/_n_a_m_e.py +1242 -0
  260. fontTools/ttLib/tables/_o_p_b_d.py +14 -0
  261. fontTools/ttLib/tables/_p_o_s_t.py +319 -0
  262. fontTools/ttLib/tables/_p_r_e_p.py +16 -0
  263. fontTools/ttLib/tables/_p_r_o_p.py +12 -0
  264. fontTools/ttLib/tables/_s_b_i_x.py +129 -0
  265. fontTools/ttLib/tables/_t_r_a_k.py +332 -0
  266. fontTools/ttLib/tables/_v_h_e_a.py +139 -0
  267. fontTools/ttLib/tables/_v_m_t_x.py +19 -0
  268. fontTools/ttLib/tables/asciiTable.py +20 -0
  269. fontTools/ttLib/tables/grUtils.py +92 -0
  270. fontTools/ttLib/tables/otBase.py +1458 -0
  271. fontTools/ttLib/tables/otConverters.py +2068 -0
  272. fontTools/ttLib/tables/otData.py +6400 -0
  273. fontTools/ttLib/tables/otTables.py +2703 -0
  274. fontTools/ttLib/tables/otTraverse.py +163 -0
  275. fontTools/ttLib/tables/sbixGlyph.py +149 -0
  276. fontTools/ttLib/tables/sbixStrike.py +177 -0
  277. fontTools/ttLib/tables/table_API_readme.txt +91 -0
  278. fontTools/ttLib/tables/ttProgram.py +594 -0
  279. fontTools/ttLib/ttCollection.py +125 -0
  280. fontTools/ttLib/ttFont.py +1148 -0
  281. fontTools/ttLib/ttGlyphSet.py +490 -0
  282. fontTools/ttLib/ttVisitor.py +32 -0
  283. fontTools/ttLib/woff2.py +1680 -0
  284. fontTools/ttx.py +479 -0
  285. fontTools/ufoLib/__init__.py +2575 -0
  286. fontTools/ufoLib/converters.py +407 -0
  287. fontTools/ufoLib/errors.py +30 -0
  288. fontTools/ufoLib/etree.py +6 -0
  289. fontTools/ufoLib/filenames.py +356 -0
  290. fontTools/ufoLib/glifLib.py +2120 -0
  291. fontTools/ufoLib/kerning.py +141 -0
  292. fontTools/ufoLib/plistlib.py +47 -0
  293. fontTools/ufoLib/pointPen.py +6 -0
  294. fontTools/ufoLib/utils.py +107 -0
  295. fontTools/ufoLib/validators.py +1208 -0
  296. fontTools/unicode.py +50 -0
  297. fontTools/unicodedata/Blocks.py +817 -0
  298. fontTools/unicodedata/Mirrored.py +446 -0
  299. fontTools/unicodedata/OTTags.py +50 -0
  300. fontTools/unicodedata/ScriptExtensions.py +832 -0
  301. fontTools/unicodedata/Scripts.py +3639 -0
  302. fontTools/unicodedata/__init__.py +306 -0
  303. fontTools/varLib/__init__.py +1600 -0
  304. fontTools/varLib/__main__.py +6 -0
  305. fontTools/varLib/avar/__init__.py +0 -0
  306. fontTools/varLib/avar/__main__.py +72 -0
  307. fontTools/varLib/avar/build.py +79 -0
  308. fontTools/varLib/avar/map.py +108 -0
  309. fontTools/varLib/avar/plan.py +1004 -0
  310. fontTools/varLib/avar/unbuild.py +271 -0
  311. fontTools/varLib/avarPlanner.py +8 -0
  312. fontTools/varLib/builder.py +215 -0
  313. fontTools/varLib/cff.py +631 -0
  314. fontTools/varLib/errors.py +219 -0
  315. fontTools/varLib/featureVars.py +703 -0
  316. fontTools/varLib/hvar.py +113 -0
  317. fontTools/varLib/instancer/__init__.py +2052 -0
  318. fontTools/varLib/instancer/__main__.py +5 -0
  319. fontTools/varLib/instancer/featureVars.py +190 -0
  320. fontTools/varLib/instancer/names.py +388 -0
  321. fontTools/varLib/instancer/solver.py +309 -0
  322. fontTools/varLib/interpolatable.py +1209 -0
  323. fontTools/varLib/interpolatableHelpers.py +399 -0
  324. fontTools/varLib/interpolatablePlot.py +1269 -0
  325. fontTools/varLib/interpolatableTestContourOrder.py +82 -0
  326. fontTools/varLib/interpolatableTestStartingPoint.py +107 -0
  327. fontTools/varLib/interpolate_layout.py +124 -0
  328. fontTools/varLib/iup.c +19815 -0
  329. fontTools/varLib/iup.cp311-win32.pyd +0 -0
  330. fontTools/varLib/iup.py +490 -0
  331. fontTools/varLib/merger.py +1717 -0
  332. fontTools/varLib/models.py +642 -0
  333. fontTools/varLib/multiVarStore.py +253 -0
  334. fontTools/varLib/mutator.py +529 -0
  335. fontTools/varLib/mvar.py +40 -0
  336. fontTools/varLib/plot.py +238 -0
  337. fontTools/varLib/stat.py +149 -0
  338. fontTools/varLib/varStore.py +739 -0
  339. fontTools/voltLib/__init__.py +5 -0
  340. fontTools/voltLib/__main__.py +206 -0
  341. fontTools/voltLib/ast.py +452 -0
  342. fontTools/voltLib/error.py +12 -0
  343. fontTools/voltLib/lexer.py +99 -0
  344. fontTools/voltLib/parser.py +664 -0
  345. fontTools/voltLib/voltToFea.py +911 -0
  346. fonttools-4.60.2.data/data/share/man/man1/ttx.1 +225 -0
  347. fonttools-4.60.2.dist-info/METADATA +2250 -0
  348. fonttools-4.60.2.dist-info/RECORD +353 -0
  349. fonttools-4.60.2.dist-info/WHEEL +5 -0
  350. fonttools-4.60.2.dist-info/entry_points.txt +5 -0
  351. fonttools-4.60.2.dist-info/licenses/LICENSE +21 -0
  352. fonttools-4.60.2.dist-info/licenses/LICENSE.external +388 -0
  353. fonttools-4.60.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,105 @@
1
+ # Copyright 2016 Google Inc. All Rights Reserved.
2
+ # Copyright 2023 Behdad Esfahbod. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from fontTools.qu2cu import quadratic_to_curves
17
+ from fontTools.pens.filterPen import ContourFilterPen
18
+ from fontTools.pens.reverseContourPen import ReverseContourPen
19
+ import math
20
+
21
+
22
+ class Qu2CuPen(ContourFilterPen):
23
+ """A filter pen to convert quadratic bezier splines to cubic curves
24
+ using the FontTools SegmentPen protocol.
25
+
26
+ Args:
27
+
28
+ other_pen: another SegmentPen used to draw the transformed outline.
29
+ max_err: maximum approximation error in font units. For optimal results,
30
+ if you know the UPEM of the font, we recommend setting this to a
31
+ value equal, or close to UPEM / 1000.
32
+ reverse_direction: flip the contours' direction but keep starting point.
33
+ stats: a dictionary counting the point numbers of cubic segments.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ other_pen,
39
+ max_err,
40
+ all_cubic=False,
41
+ reverse_direction=False,
42
+ stats=None,
43
+ ):
44
+ if reverse_direction:
45
+ other_pen = ReverseContourPen(other_pen)
46
+ super().__init__(other_pen)
47
+ self.all_cubic = all_cubic
48
+ self.max_err = max_err
49
+ self.stats = stats
50
+
51
+ def _quadratics_to_curve(self, q):
52
+ curves = quadratic_to_curves(q, self.max_err, all_cubic=self.all_cubic)
53
+ if self.stats is not None:
54
+ for curve in curves:
55
+ n = str(len(curve) - 2)
56
+ self.stats[n] = self.stats.get(n, 0) + 1
57
+ for curve in curves:
58
+ if len(curve) == 4:
59
+ yield ("curveTo", curve[1:])
60
+ else:
61
+ yield ("qCurveTo", curve[1:])
62
+
63
+ def filterContour(self, contour):
64
+ quadratics = []
65
+ currentPt = None
66
+ newContour = []
67
+ for op, args in contour:
68
+ if op == "qCurveTo" and (
69
+ self.all_cubic or (len(args) > 2 and args[-1] is not None)
70
+ ):
71
+ if args[-1] is None:
72
+ raise NotImplementedError(
73
+ "oncurve-less contours with all_cubic not implemented"
74
+ )
75
+ quadratics.append((currentPt,) + args)
76
+ else:
77
+ if quadratics:
78
+ newContour.extend(self._quadratics_to_curve(quadratics))
79
+ quadratics = []
80
+ newContour.append((op, args))
81
+ currentPt = args[-1] if args else None
82
+ if quadratics:
83
+ newContour.extend(self._quadratics_to_curve(quadratics))
84
+
85
+ if not self.all_cubic:
86
+ # Add back implicit oncurve points
87
+ contour = newContour
88
+ newContour = []
89
+ for op, args in contour:
90
+ if op == "qCurveTo" and newContour and newContour[-1][0] == "qCurveTo":
91
+ pt0 = newContour[-1][1][-2]
92
+ pt1 = newContour[-1][1][-1]
93
+ pt2 = args[0]
94
+ if (
95
+ pt1 is not None
96
+ and math.isclose(pt2[0] - pt1[0], pt1[0] - pt0[0])
97
+ and math.isclose(pt2[1] - pt1[1], pt1[1] - pt0[1])
98
+ ):
99
+ newArgs = newContour[-1][1][:-1] + args
100
+ newContour[-1] = (op, newArgs)
101
+ continue
102
+
103
+ newContour.append((op, args))
104
+
105
+ return newContour
@@ -0,0 +1,43 @@
1
+ from fontTools.pens.basePen import BasePen
2
+
3
+ from Quartz.CoreGraphics import CGPathCreateMutable, CGPathMoveToPoint
4
+ from Quartz.CoreGraphics import CGPathAddLineToPoint, CGPathAddCurveToPoint
5
+ from Quartz.CoreGraphics import CGPathAddQuadCurveToPoint, CGPathCloseSubpath
6
+
7
+
8
+ __all__ = ["QuartzPen"]
9
+
10
+
11
+ class QuartzPen(BasePen):
12
+ """A pen that creates a CGPath
13
+
14
+ Parameters
15
+ - path: an optional CGPath to add to
16
+ - xform: an optional CGAffineTransform to apply to the path
17
+ """
18
+
19
+ def __init__(self, glyphSet, path=None, xform=None):
20
+ BasePen.__init__(self, glyphSet)
21
+ if path is None:
22
+ path = CGPathCreateMutable()
23
+ self.path = path
24
+ self.xform = xform
25
+
26
+ def _moveTo(self, pt):
27
+ x, y = pt
28
+ CGPathMoveToPoint(self.path, self.xform, x, y)
29
+
30
+ def _lineTo(self, pt):
31
+ x, y = pt
32
+ CGPathAddLineToPoint(self.path, self.xform, x, y)
33
+
34
+ def _curveToOne(self, p1, p2, p3):
35
+ (x1, y1), (x2, y2), (x3, y3) = p1, p2, p3
36
+ CGPathAddCurveToPoint(self.path, self.xform, x1, y1, x2, y2, x3, y3)
37
+
38
+ def _qCurveToOne(self, p1, p2):
39
+ (x1, y1), (x2, y2) = p1, p2
40
+ CGPathAddQuadCurveToPoint(self.path, self.xform, x1, y1, x2, y2)
41
+
42
+ def _closePath(self):
43
+ CGPathCloseSubpath(self.path)
@@ -0,0 +1,335 @@
1
+ """Pen recording operations that can be accessed or replayed."""
2
+
3
+ from fontTools.pens.basePen import AbstractPen, DecomposingPen
4
+ from fontTools.pens.pointPen import AbstractPointPen, DecomposingPointPen
5
+
6
+
7
+ __all__ = [
8
+ "replayRecording",
9
+ "RecordingPen",
10
+ "DecomposingRecordingPen",
11
+ "DecomposingRecordingPointPen",
12
+ "RecordingPointPen",
13
+ "lerpRecordings",
14
+ ]
15
+
16
+
17
+ def replayRecording(recording, pen):
18
+ """Replay a recording, as produced by RecordingPen or DecomposingRecordingPen,
19
+ to a pen.
20
+
21
+ Note that recording does not have to be produced by those pens.
22
+ It can be any iterable of tuples of method name and tuple-of-arguments.
23
+ Likewise, pen can be any objects receiving those method calls.
24
+ """
25
+ for operator, operands in recording:
26
+ getattr(pen, operator)(*operands)
27
+
28
+
29
+ class RecordingPen(AbstractPen):
30
+ """Pen recording operations that can be accessed or replayed.
31
+
32
+ The recording can be accessed as pen.value; or replayed using
33
+ pen.replay(otherPen).
34
+
35
+ :Example:
36
+ .. code-block::
37
+
38
+ from fontTools.ttLib import TTFont
39
+ from fontTools.pens.recordingPen import RecordingPen
40
+
41
+ glyph_name = 'dollar'
42
+ font_path = 'MyFont.otf'
43
+
44
+ font = TTFont(font_path)
45
+ glyphset = font.getGlyphSet()
46
+ glyph = glyphset[glyph_name]
47
+
48
+ pen = RecordingPen()
49
+ glyph.draw(pen)
50
+ print(pen.value)
51
+ """
52
+
53
+ def __init__(self):
54
+ self.value = []
55
+
56
+ def moveTo(self, p0):
57
+ self.value.append(("moveTo", (p0,)))
58
+
59
+ def lineTo(self, p1):
60
+ self.value.append(("lineTo", (p1,)))
61
+
62
+ def qCurveTo(self, *points):
63
+ self.value.append(("qCurveTo", points))
64
+
65
+ def curveTo(self, *points):
66
+ self.value.append(("curveTo", points))
67
+
68
+ def closePath(self):
69
+ self.value.append(("closePath", ()))
70
+
71
+ def endPath(self):
72
+ self.value.append(("endPath", ()))
73
+
74
+ def addComponent(self, glyphName, transformation):
75
+ self.value.append(("addComponent", (glyphName, transformation)))
76
+
77
+ def addVarComponent(self, glyphName, transformation, location):
78
+ self.value.append(("addVarComponent", (glyphName, transformation, location)))
79
+
80
+ def replay(self, pen):
81
+ replayRecording(self.value, pen)
82
+
83
+ draw = replay
84
+
85
+
86
+ class DecomposingRecordingPen(DecomposingPen, RecordingPen):
87
+ """Same as RecordingPen, except that it doesn't keep components
88
+ as references, but draws them decomposed as regular contours.
89
+
90
+ The constructor takes a required 'glyphSet' positional argument,
91
+ a dictionary of glyph objects (i.e. with a 'draw' method) keyed
92
+ by thir name; other arguments are forwarded to the DecomposingPen's
93
+ constructor::
94
+
95
+ >>> class SimpleGlyph(object):
96
+ ... def draw(self, pen):
97
+ ... pen.moveTo((0, 0))
98
+ ... pen.curveTo((1, 1), (2, 2), (3, 3))
99
+ ... pen.closePath()
100
+ >>> class CompositeGlyph(object):
101
+ ... def draw(self, pen):
102
+ ... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
103
+ >>> class MissingComponent(object):
104
+ ... def draw(self, pen):
105
+ ... pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
106
+ >>> class FlippedComponent(object):
107
+ ... def draw(self, pen):
108
+ ... pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
109
+ >>> glyphSet = {
110
+ ... 'a': SimpleGlyph(),
111
+ ... 'b': CompositeGlyph(),
112
+ ... 'c': MissingComponent(),
113
+ ... 'd': FlippedComponent(),
114
+ ... }
115
+ >>> for name, glyph in sorted(glyphSet.items()):
116
+ ... pen = DecomposingRecordingPen(glyphSet)
117
+ ... try:
118
+ ... glyph.draw(pen)
119
+ ... except pen.MissingComponentError:
120
+ ... pass
121
+ ... print("{}: {}".format(name, pen.value))
122
+ a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
123
+ b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
124
+ c: []
125
+ d: [('moveTo', ((0, 0),)), ('curveTo', ((-1, 1), (-2, 2), (-3, 3))), ('closePath', ())]
126
+
127
+ >>> for name, glyph in sorted(glyphSet.items()):
128
+ ... pen = DecomposingRecordingPen(
129
+ ... glyphSet, skipMissingComponents=True, reverseFlipped=True,
130
+ ... )
131
+ ... glyph.draw(pen)
132
+ ... print("{}: {}".format(name, pen.value))
133
+ a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
134
+ b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
135
+ c: []
136
+ d: [('moveTo', ((0, 0),)), ('lineTo', ((-3, 3),)), ('curveTo', ((-2, 2), (-1, 1), (0, 0))), ('closePath', ())]
137
+ """
138
+
139
+ # raises MissingComponentError(KeyError) if base glyph is not found in glyphSet
140
+ skipMissingComponents = False
141
+
142
+
143
+ class RecordingPointPen(AbstractPointPen):
144
+ """PointPen recording operations that can be accessed or replayed.
145
+
146
+ The recording can be accessed as pen.value; or replayed using
147
+ pointPen.replay(otherPointPen).
148
+
149
+ :Example:
150
+ .. code-block::
151
+
152
+ from defcon import Font
153
+ from fontTools.pens.recordingPen import RecordingPointPen
154
+
155
+ glyph_name = 'a'
156
+ font_path = 'MyFont.ufo'
157
+
158
+ font = Font(font_path)
159
+ glyph = font[glyph_name]
160
+
161
+ pen = RecordingPointPen()
162
+ glyph.drawPoints(pen)
163
+ print(pen.value)
164
+
165
+ new_glyph = font.newGlyph('b')
166
+ pen.replay(new_glyph.getPointPen())
167
+ """
168
+
169
+ def __init__(self):
170
+ self.value = []
171
+
172
+ def beginPath(self, identifier=None, **kwargs):
173
+ if identifier is not None:
174
+ kwargs["identifier"] = identifier
175
+ self.value.append(("beginPath", (), kwargs))
176
+
177
+ def endPath(self):
178
+ self.value.append(("endPath", (), {}))
179
+
180
+ def addPoint(
181
+ self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
182
+ ):
183
+ if identifier is not None:
184
+ kwargs["identifier"] = identifier
185
+ self.value.append(("addPoint", (pt, segmentType, smooth, name), kwargs))
186
+
187
+ def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
188
+ if identifier is not None:
189
+ kwargs["identifier"] = identifier
190
+ self.value.append(("addComponent", (baseGlyphName, transformation), kwargs))
191
+
192
+ def addVarComponent(
193
+ self, baseGlyphName, transformation, location, identifier=None, **kwargs
194
+ ):
195
+ if identifier is not None:
196
+ kwargs["identifier"] = identifier
197
+ self.value.append(
198
+ ("addVarComponent", (baseGlyphName, transformation, location), kwargs)
199
+ )
200
+
201
+ def replay(self, pointPen):
202
+ for operator, args, kwargs in self.value:
203
+ getattr(pointPen, operator)(*args, **kwargs)
204
+
205
+ drawPoints = replay
206
+
207
+
208
+ class DecomposingRecordingPointPen(DecomposingPointPen, RecordingPointPen):
209
+ """Same as RecordingPointPen, except that it doesn't keep components
210
+ as references, but draws them decomposed as regular contours.
211
+
212
+ The constructor takes a required 'glyphSet' positional argument,
213
+ a dictionary of pointPen-drawable glyph objects (i.e. with a 'drawPoints' method)
214
+ keyed by thir name; other arguments are forwarded to the DecomposingPointPen's
215
+ constructor::
216
+
217
+ >>> from pprint import pprint
218
+ >>> class SimpleGlyph(object):
219
+ ... def drawPoints(self, pen):
220
+ ... pen.beginPath()
221
+ ... pen.addPoint((0, 0), "line")
222
+ ... pen.addPoint((1, 1))
223
+ ... pen.addPoint((2, 2))
224
+ ... pen.addPoint((3, 3), "curve")
225
+ ... pen.endPath()
226
+ >>> class CompositeGlyph(object):
227
+ ... def drawPoints(self, pen):
228
+ ... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
229
+ >>> class MissingComponent(object):
230
+ ... def drawPoints(self, pen):
231
+ ... pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
232
+ >>> class FlippedComponent(object):
233
+ ... def drawPoints(self, pen):
234
+ ... pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
235
+ >>> glyphSet = {
236
+ ... 'a': SimpleGlyph(),
237
+ ... 'b': CompositeGlyph(),
238
+ ... 'c': MissingComponent(),
239
+ ... 'd': FlippedComponent(),
240
+ ... }
241
+ >>> for name, glyph in sorted(glyphSet.items()):
242
+ ... pen = DecomposingRecordingPointPen(glyphSet)
243
+ ... try:
244
+ ... glyph.drawPoints(pen)
245
+ ... except pen.MissingComponentError:
246
+ ... pass
247
+ ... pprint({name: pen.value})
248
+ {'a': [('beginPath', (), {}),
249
+ ('addPoint', ((0, 0), 'line', False, None), {}),
250
+ ('addPoint', ((1, 1), None, False, None), {}),
251
+ ('addPoint', ((2, 2), None, False, None), {}),
252
+ ('addPoint', ((3, 3), 'curve', False, None), {}),
253
+ ('endPath', (), {})]}
254
+ {'b': [('beginPath', (), {}),
255
+ ('addPoint', ((-1, 1), 'line', False, None), {}),
256
+ ('addPoint', ((0, 2), None, False, None), {}),
257
+ ('addPoint', ((1, 3), None, False, None), {}),
258
+ ('addPoint', ((2, 4), 'curve', False, None), {}),
259
+ ('endPath', (), {})]}
260
+ {'c': []}
261
+ {'d': [('beginPath', (), {}),
262
+ ('addPoint', ((0, 0), 'line', False, None), {}),
263
+ ('addPoint', ((-1, 1), None, False, None), {}),
264
+ ('addPoint', ((-2, 2), None, False, None), {}),
265
+ ('addPoint', ((-3, 3), 'curve', False, None), {}),
266
+ ('endPath', (), {})]}
267
+
268
+ >>> for name, glyph in sorted(glyphSet.items()):
269
+ ... pen = DecomposingRecordingPointPen(
270
+ ... glyphSet, skipMissingComponents=True, reverseFlipped=True,
271
+ ... )
272
+ ... glyph.drawPoints(pen)
273
+ ... pprint({name: pen.value})
274
+ {'a': [('beginPath', (), {}),
275
+ ('addPoint', ((0, 0), 'line', False, None), {}),
276
+ ('addPoint', ((1, 1), None, False, None), {}),
277
+ ('addPoint', ((2, 2), None, False, None), {}),
278
+ ('addPoint', ((3, 3), 'curve', False, None), {}),
279
+ ('endPath', (), {})]}
280
+ {'b': [('beginPath', (), {}),
281
+ ('addPoint', ((-1, 1), 'line', False, None), {}),
282
+ ('addPoint', ((0, 2), None, False, None), {}),
283
+ ('addPoint', ((1, 3), None, False, None), {}),
284
+ ('addPoint', ((2, 4), 'curve', False, None), {}),
285
+ ('endPath', (), {})]}
286
+ {'c': []}
287
+ {'d': [('beginPath', (), {}),
288
+ ('addPoint', ((0, 0), 'curve', False, None), {}),
289
+ ('addPoint', ((-3, 3), 'line', False, None), {}),
290
+ ('addPoint', ((-2, 2), None, False, None), {}),
291
+ ('addPoint', ((-1, 1), None, False, None), {}),
292
+ ('endPath', (), {})]}
293
+ """
294
+
295
+ # raises MissingComponentError(KeyError) if base glyph is not found in glyphSet
296
+ skipMissingComponents = False
297
+
298
+
299
+ def lerpRecordings(recording1, recording2, factor=0.5):
300
+ """Linearly interpolate between two recordings. The recordings
301
+ must be decomposed, i.e. they must not contain any components.
302
+
303
+ Factor is typically between 0 and 1. 0 means the first recording,
304
+ 1 means the second recording, and 0.5 means the average of the
305
+ two recordings. Other values are possible, and can be useful to
306
+ extrapolate. Defaults to 0.5.
307
+
308
+ Returns a generator with the new recording.
309
+ """
310
+ if len(recording1) != len(recording2):
311
+ raise ValueError(
312
+ "Mismatched lengths: %d and %d" % (len(recording1), len(recording2))
313
+ )
314
+ for (op1, args1), (op2, args2) in zip(recording1, recording2):
315
+ if op1 != op2:
316
+ raise ValueError("Mismatched operations: %s, %s" % (op1, op2))
317
+ if op1 == "addComponent":
318
+ raise ValueError("Cannot interpolate components")
319
+ else:
320
+ mid_args = [
321
+ (x1 + (x2 - x1) * factor, y1 + (y2 - y1) * factor)
322
+ for (x1, y1), (x2, y2) in zip(args1, args2)
323
+ ]
324
+ yield (op1, mid_args)
325
+
326
+
327
+ if __name__ == "__main__":
328
+ pen = RecordingPen()
329
+ pen.moveTo((0, 0))
330
+ pen.lineTo((0, 100))
331
+ pen.curveTo((50, 75), (60, 50), (50, 25))
332
+ pen.closePath()
333
+ from pprint import pprint
334
+
335
+ pprint(pen.value)
@@ -0,0 +1,79 @@
1
+ from fontTools.pens.basePen import BasePen
2
+ from reportlab.graphics.shapes import Path
3
+
4
+
5
+ __all__ = ["ReportLabPen"]
6
+
7
+
8
+ class ReportLabPen(BasePen):
9
+ """A pen for drawing onto a ``reportlab.graphics.shapes.Path`` object."""
10
+
11
+ def __init__(self, glyphSet, path=None):
12
+ BasePen.__init__(self, glyphSet)
13
+ if path is None:
14
+ path = Path()
15
+ self.path = path
16
+
17
+ def _moveTo(self, p):
18
+ (x, y) = p
19
+ self.path.moveTo(x, y)
20
+
21
+ def _lineTo(self, p):
22
+ (x, y) = p
23
+ self.path.lineTo(x, y)
24
+
25
+ def _curveToOne(self, p1, p2, p3):
26
+ (x1, y1) = p1
27
+ (x2, y2) = p2
28
+ (x3, y3) = p3
29
+ self.path.curveTo(x1, y1, x2, y2, x3, y3)
30
+
31
+ def _closePath(self):
32
+ self.path.closePath()
33
+
34
+
35
+ if __name__ == "__main__":
36
+ import sys
37
+
38
+ if len(sys.argv) < 3:
39
+ print(
40
+ "Usage: reportLabPen.py <OTF/TTF font> <glyphname> [<image file to create>]"
41
+ )
42
+ print(
43
+ " If no image file name is created, by default <glyphname>.png is created."
44
+ )
45
+ print(" example: reportLabPen.py Arial.TTF R test.png")
46
+ print(
47
+ " (The file format will be PNG, regardless of the image file name supplied)"
48
+ )
49
+ sys.exit(0)
50
+
51
+ from fontTools.ttLib import TTFont
52
+ from reportlab.lib import colors
53
+
54
+ path = sys.argv[1]
55
+ glyphName = sys.argv[2]
56
+ if len(sys.argv) > 3:
57
+ imageFile = sys.argv[3]
58
+ else:
59
+ imageFile = "%s.png" % glyphName
60
+
61
+ font = TTFont(path) # it would work just as well with fontTools.t1Lib.T1Font
62
+ gs = font.getGlyphSet()
63
+ pen = ReportLabPen(gs, Path(fillColor=colors.red, strokeWidth=5))
64
+ g = gs[glyphName]
65
+ g.draw(pen)
66
+
67
+ w, h = g.width, 1000
68
+ from reportlab.graphics import renderPM
69
+ from reportlab.graphics.shapes import Group, Drawing, scale
70
+
71
+ # Everything is wrapped in a group to allow transformations.
72
+ g = Group(pen.path)
73
+ g.translate(0, 200)
74
+ g.scale(0.3, 0.3)
75
+
76
+ d = Drawing(w, h)
77
+ d.add(g)
78
+
79
+ renderPM.drawToFile(d, imageFile, fmt="PNG")
@@ -0,0 +1,96 @@
1
+ from fontTools.misc.arrayTools import pairwise
2
+ from fontTools.pens.filterPen import ContourFilterPen
3
+
4
+
5
+ __all__ = ["reversedContour", "ReverseContourPen"]
6
+
7
+
8
+ class ReverseContourPen(ContourFilterPen):
9
+ """Filter pen that passes outline data to another pen, but reversing
10
+ the winding direction of all contours. Components are simply passed
11
+ through unchanged.
12
+
13
+ Closed contours are reversed in such a way that the first point remains
14
+ the first point.
15
+ """
16
+
17
+ def __init__(self, outPen, outputImpliedClosingLine=False):
18
+ super().__init__(outPen)
19
+ self.outputImpliedClosingLine = outputImpliedClosingLine
20
+
21
+ def filterContour(self, contour):
22
+ return reversedContour(contour, self.outputImpliedClosingLine)
23
+
24
+
25
+ def reversedContour(contour, outputImpliedClosingLine=False):
26
+ """Generator that takes a list of pen's (operator, operands) tuples,
27
+ and yields them with the winding direction reversed.
28
+ """
29
+ if not contour:
30
+ return # nothing to do, stop iteration
31
+
32
+ # valid contours must have at least a starting and ending command,
33
+ # can't have one without the other
34
+ assert len(contour) > 1, "invalid contour"
35
+
36
+ # the type of the last command determines if the contour is closed
37
+ contourType = contour.pop()[0]
38
+ assert contourType in ("endPath", "closePath")
39
+ closed = contourType == "closePath"
40
+
41
+ firstType, firstPts = contour.pop(0)
42
+ assert firstType in ("moveTo", "qCurveTo"), (
43
+ "invalid initial segment type: %r" % firstType
44
+ )
45
+ firstOnCurve = firstPts[-1]
46
+ if firstType == "qCurveTo":
47
+ # special case for TrueType paths contaning only off-curve points
48
+ assert firstOnCurve is None, "off-curve only paths must end with 'None'"
49
+ assert not contour, "only one qCurveTo allowed per off-curve path"
50
+ firstPts = (firstPts[0],) + tuple(reversed(firstPts[1:-1])) + (None,)
51
+
52
+ if not contour:
53
+ # contour contains only one segment, nothing to reverse
54
+ if firstType == "moveTo":
55
+ closed = False # single-point paths can't be closed
56
+ else:
57
+ closed = True # off-curve paths are closed by definition
58
+ yield firstType, firstPts
59
+ else:
60
+ lastType, lastPts = contour[-1]
61
+ lastOnCurve = lastPts[-1]
62
+ if closed:
63
+ # for closed paths, we keep the starting point
64
+ yield firstType, firstPts
65
+ if firstOnCurve != lastOnCurve:
66
+ # emit an implied line between the last and first points
67
+ yield "lineTo", (lastOnCurve,)
68
+ contour[-1] = (lastType, tuple(lastPts[:-1]) + (firstOnCurve,))
69
+
70
+ if len(contour) > 1:
71
+ secondType, secondPts = contour[0]
72
+ else:
73
+ # contour has only two points, the second and last are the same
74
+ secondType, secondPts = lastType, lastPts
75
+
76
+ if not outputImpliedClosingLine:
77
+ # if a lineTo follows the initial moveTo, after reversing it
78
+ # will be implied by the closePath, so we don't emit one;
79
+ # unless the lineTo and moveTo overlap, in which case we keep the
80
+ # duplicate points
81
+ if secondType == "lineTo" and firstPts != secondPts:
82
+ del contour[0]
83
+ if contour:
84
+ contour[-1] = (lastType, tuple(lastPts[:-1]) + secondPts)
85
+ else:
86
+ # for open paths, the last point will become the first
87
+ yield firstType, (lastOnCurve,)
88
+ contour[-1] = (lastType, tuple(lastPts[:-1]) + (firstOnCurve,))
89
+
90
+ # we iterate over all segment pairs in reverse order, and yield
91
+ # each one with the off-curve points reversed (if any), and
92
+ # with the on-curve point of the following segment
93
+ for (curType, curPts), (_, nextPts) in pairwise(contour, reverse=True):
94
+ yield curType, tuple(reversed(curPts[:-1])) + (nextPts[-1],)
95
+
96
+ yield "closePath" if closed else "endPath", ()