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,475 @@
1
+ """fontTools.pens.basePen.py -- Tools and base classes to build pen objects.
2
+
3
+ The Pen Protocol
4
+
5
+ A Pen is a kind of object that standardizes the way how to "draw" outlines:
6
+ it is a middle man between an outline and a drawing. In other words:
7
+ it is an abstraction for drawing outlines, making sure that outline objects
8
+ don't need to know the details about how and where they're being drawn, and
9
+ that drawings don't need to know the details of how outlines are stored.
10
+
11
+ The most basic pattern is this::
12
+
13
+ outline.draw(pen) # 'outline' draws itself onto 'pen'
14
+
15
+ Pens can be used to render outlines to the screen, but also to construct
16
+ new outlines. Eg. an outline object can be both a drawable object (it has a
17
+ draw() method) as well as a pen itself: you *build* an outline using pen
18
+ methods.
19
+
20
+ The AbstractPen class defines the Pen protocol. It implements almost
21
+ nothing (only no-op closePath() and endPath() methods), but is useful
22
+ for documentation purposes. Subclassing it basically tells the reader:
23
+ "this class implements the Pen protocol.". An examples of an AbstractPen
24
+ subclass is :py:class:`fontTools.pens.transformPen.TransformPen`.
25
+
26
+ The BasePen class is a base implementation useful for pens that actually
27
+ draw (for example a pen renders outlines using a native graphics engine).
28
+ BasePen contains a lot of base functionality, making it very easy to build
29
+ a pen that fully conforms to the pen protocol. Note that if you subclass
30
+ BasePen, you *don't* override moveTo(), lineTo(), etc., but _moveTo(),
31
+ _lineTo(), etc. See the BasePen doc string for details. Examples of
32
+ BasePen subclasses are fontTools.pens.boundsPen.BoundsPen and
33
+ fontTools.pens.cocoaPen.CocoaPen.
34
+
35
+ Coordinates are usually expressed as (x, y) tuples, but generally any
36
+ sequence of length 2 will do.
37
+ """
38
+
39
+ from typing import Tuple, Dict
40
+
41
+ from fontTools.misc.loggingTools import LogMixin
42
+ from fontTools.misc.transform import DecomposedTransform, Identity
43
+
44
+ __all__ = [
45
+ "AbstractPen",
46
+ "NullPen",
47
+ "BasePen",
48
+ "PenError",
49
+ "decomposeSuperBezierSegment",
50
+ "decomposeQuadraticSegment",
51
+ ]
52
+
53
+
54
+ class PenError(Exception):
55
+ """Represents an error during penning."""
56
+
57
+
58
+ class OpenContourError(PenError):
59
+ pass
60
+
61
+
62
+ class AbstractPen:
63
+ def moveTo(self, pt: Tuple[float, float]) -> None:
64
+ """Begin a new sub path, set the current point to 'pt'. You must
65
+ end each sub path with a call to pen.closePath() or pen.endPath().
66
+ """
67
+ raise NotImplementedError
68
+
69
+ def lineTo(self, pt: Tuple[float, float]) -> None:
70
+ """Draw a straight line from the current point to 'pt'."""
71
+ raise NotImplementedError
72
+
73
+ def curveTo(self, *points: Tuple[float, float]) -> None:
74
+ """Draw a cubic bezier with an arbitrary number of control points.
75
+
76
+ The last point specified is on-curve, all others are off-curve
77
+ (control) points. If the number of control points is > 2, the
78
+ segment is split into multiple bezier segments. This works
79
+ like this:
80
+
81
+ Let n be the number of control points (which is the number of
82
+ arguments to this call minus 1). If n==2, a plain vanilla cubic
83
+ bezier is drawn. If n==1, we fall back to a quadratic segment and
84
+ if n==0 we draw a straight line. It gets interesting when n>2:
85
+ n-1 PostScript-style cubic segments will be drawn as if it were
86
+ one curve. See decomposeSuperBezierSegment().
87
+
88
+ The conversion algorithm used for n>2 is inspired by NURB
89
+ splines, and is conceptually equivalent to the TrueType "implied
90
+ points" principle. See also decomposeQuadraticSegment().
91
+ """
92
+ raise NotImplementedError
93
+
94
+ def qCurveTo(self, *points: Tuple[float, float]) -> None:
95
+ """Draw a whole string of quadratic curve segments.
96
+
97
+ The last point specified is on-curve, all others are off-curve
98
+ points.
99
+
100
+ This method implements TrueType-style curves, breaking up curves
101
+ using 'implied points': between each two consequtive off-curve points,
102
+ there is one implied point exactly in the middle between them. See
103
+ also decomposeQuadraticSegment().
104
+
105
+ The last argument (normally the on-curve point) may be None.
106
+ This is to support contours that have NO on-curve points (a rarely
107
+ seen feature of TrueType outlines).
108
+ """
109
+ raise NotImplementedError
110
+
111
+ def closePath(self) -> None:
112
+ """Close the current sub path. You must call either pen.closePath()
113
+ or pen.endPath() after each sub path.
114
+ """
115
+ pass
116
+
117
+ def endPath(self) -> None:
118
+ """End the current sub path, but don't close it. You must call
119
+ either pen.closePath() or pen.endPath() after each sub path.
120
+ """
121
+ pass
122
+
123
+ def addComponent(
124
+ self,
125
+ glyphName: str,
126
+ transformation: Tuple[float, float, float, float, float, float],
127
+ ) -> None:
128
+ """Add a sub glyph. The 'transformation' argument must be a 6-tuple
129
+ containing an affine transformation, or a Transform object from the
130
+ fontTools.misc.transform module. More precisely: it should be a
131
+ sequence containing 6 numbers.
132
+ """
133
+ raise NotImplementedError
134
+
135
+ def addVarComponent(
136
+ self,
137
+ glyphName: str,
138
+ transformation: DecomposedTransform,
139
+ location: Dict[str, float],
140
+ ) -> None:
141
+ """Add a VarComponent sub glyph. The 'transformation' argument
142
+ must be a DecomposedTransform from the fontTools.misc.transform module,
143
+ and the 'location' argument must be a dictionary mapping axis tags
144
+ to their locations.
145
+ """
146
+ # GlyphSet decomposes for us
147
+ raise AttributeError
148
+
149
+
150
+ class NullPen(AbstractPen):
151
+ """A pen that does nothing."""
152
+
153
+ def moveTo(self, pt):
154
+ pass
155
+
156
+ def lineTo(self, pt):
157
+ pass
158
+
159
+ def curveTo(self, *points):
160
+ pass
161
+
162
+ def qCurveTo(self, *points):
163
+ pass
164
+
165
+ def closePath(self):
166
+ pass
167
+
168
+ def endPath(self):
169
+ pass
170
+
171
+ def addComponent(self, glyphName, transformation):
172
+ pass
173
+
174
+ def addVarComponent(self, glyphName, transformation, location):
175
+ pass
176
+
177
+
178
+ class LoggingPen(LogMixin, AbstractPen):
179
+ """A pen with a ``log`` property (see fontTools.misc.loggingTools.LogMixin)"""
180
+
181
+ pass
182
+
183
+
184
+ class MissingComponentError(KeyError):
185
+ """Indicates a component pointing to a non-existent glyph in the glyphset."""
186
+
187
+
188
+ class DecomposingPen(LoggingPen):
189
+ """Implements a 'addComponent' method that decomposes components
190
+ (i.e. draws them onto self as simple contours).
191
+ It can also be used as a mixin class (e.g. see ContourRecordingPen).
192
+
193
+ You must override moveTo, lineTo, curveTo and qCurveTo. You may
194
+ additionally override closePath, endPath and addComponent.
195
+
196
+ By default a warning message is logged when a base glyph is missing;
197
+ set the class variable ``skipMissingComponents`` to False if you want
198
+ all instances of a sub-class to raise a :class:`MissingComponentError`
199
+ exception by default.
200
+ """
201
+
202
+ skipMissingComponents = True
203
+ # alias error for convenience
204
+ MissingComponentError = MissingComponentError
205
+
206
+ def __init__(
207
+ self,
208
+ glyphSet,
209
+ *args,
210
+ skipMissingComponents=None,
211
+ reverseFlipped=False,
212
+ **kwargs,
213
+ ):
214
+ """Takes a 'glyphSet' argument (dict), in which the glyphs that are referenced
215
+ as components are looked up by their name.
216
+
217
+ If the optional 'reverseFlipped' argument is True, components whose transformation
218
+ matrix has a negative determinant will be decomposed with a reversed path direction
219
+ to compensate for the flip.
220
+
221
+ The optional 'skipMissingComponents' argument can be set to True/False to
222
+ override the homonymous class attribute for a given pen instance.
223
+ """
224
+ super(DecomposingPen, self).__init__(*args, **kwargs)
225
+ self.glyphSet = glyphSet
226
+ self.skipMissingComponents = (
227
+ self.__class__.skipMissingComponents
228
+ if skipMissingComponents is None
229
+ else skipMissingComponents
230
+ )
231
+ self.reverseFlipped = reverseFlipped
232
+
233
+ def addComponent(self, glyphName, transformation):
234
+ """Transform the points of the base glyph and draw it onto self."""
235
+ from fontTools.pens.transformPen import TransformPen
236
+
237
+ try:
238
+ glyph = self.glyphSet[glyphName]
239
+ except KeyError:
240
+ if not self.skipMissingComponents:
241
+ raise MissingComponentError(glyphName)
242
+ self.log.warning("glyph '%s' is missing from glyphSet; skipped" % glyphName)
243
+ else:
244
+ pen = self
245
+ if transformation != Identity:
246
+ pen = TransformPen(pen, transformation)
247
+ if self.reverseFlipped:
248
+ # if the transformation has a negative determinant, it will
249
+ # reverse the contour direction of the component
250
+ a, b, c, d = transformation[:4]
251
+ det = a * d - b * c
252
+ if det < 0:
253
+ from fontTools.pens.reverseContourPen import ReverseContourPen
254
+
255
+ pen = ReverseContourPen(pen)
256
+ glyph.draw(pen)
257
+
258
+ def addVarComponent(self, glyphName, transformation, location):
259
+ # GlyphSet decomposes for us
260
+ raise AttributeError
261
+
262
+
263
+ class BasePen(DecomposingPen):
264
+ """Base class for drawing pens. You must override _moveTo, _lineTo and
265
+ _curveToOne. You may additionally override _closePath, _endPath,
266
+ addComponent, addVarComponent, and/or _qCurveToOne. You should not
267
+ override any other methods.
268
+ """
269
+
270
+ def __init__(self, glyphSet=None):
271
+ super(BasePen, self).__init__(glyphSet)
272
+ self.__currentPoint = None
273
+
274
+ # must override
275
+
276
+ def _moveTo(self, pt):
277
+ raise NotImplementedError
278
+
279
+ def _lineTo(self, pt):
280
+ raise NotImplementedError
281
+
282
+ def _curveToOne(self, pt1, pt2, pt3):
283
+ raise NotImplementedError
284
+
285
+ # may override
286
+
287
+ def _closePath(self):
288
+ pass
289
+
290
+ def _endPath(self):
291
+ pass
292
+
293
+ def _qCurveToOne(self, pt1, pt2):
294
+ """This method implements the basic quadratic curve type. The
295
+ default implementation delegates the work to the cubic curve
296
+ function. Optionally override with a native implementation.
297
+ """
298
+ pt0x, pt0y = self.__currentPoint
299
+ pt1x, pt1y = pt1
300
+ pt2x, pt2y = pt2
301
+ mid1x = pt0x + 0.66666666666666667 * (pt1x - pt0x)
302
+ mid1y = pt0y + 0.66666666666666667 * (pt1y - pt0y)
303
+ mid2x = pt2x + 0.66666666666666667 * (pt1x - pt2x)
304
+ mid2y = pt2y + 0.66666666666666667 * (pt1y - pt2y)
305
+ self._curveToOne((mid1x, mid1y), (mid2x, mid2y), pt2)
306
+
307
+ # don't override
308
+
309
+ def _getCurrentPoint(self):
310
+ """Return the current point. This is not part of the public
311
+ interface, yet is useful for subclasses.
312
+ """
313
+ return self.__currentPoint
314
+
315
+ def closePath(self):
316
+ self._closePath()
317
+ self.__currentPoint = None
318
+
319
+ def endPath(self):
320
+ self._endPath()
321
+ self.__currentPoint = None
322
+
323
+ def moveTo(self, pt):
324
+ self._moveTo(pt)
325
+ self.__currentPoint = pt
326
+
327
+ def lineTo(self, pt):
328
+ self._lineTo(pt)
329
+ self.__currentPoint = pt
330
+
331
+ def curveTo(self, *points):
332
+ n = len(points) - 1 # 'n' is the number of control points
333
+ assert n >= 0
334
+ if n == 2:
335
+ # The common case, we have exactly two BCP's, so this is a standard
336
+ # cubic bezier. Even though decomposeSuperBezierSegment() handles
337
+ # this case just fine, we special-case it anyway since it's so
338
+ # common.
339
+ self._curveToOne(*points)
340
+ self.__currentPoint = points[-1]
341
+ elif n > 2:
342
+ # n is the number of control points; split curve into n-1 cubic
343
+ # bezier segments. The algorithm used here is inspired by NURB
344
+ # splines and the TrueType "implied point" principle, and ensures
345
+ # the smoothest possible connection between two curve segments,
346
+ # with no disruption in the curvature. It is practical since it
347
+ # allows one to construct multiple bezier segments with a much
348
+ # smaller amount of points.
349
+ _curveToOne = self._curveToOne
350
+ for pt1, pt2, pt3 in decomposeSuperBezierSegment(points):
351
+ _curveToOne(pt1, pt2, pt3)
352
+ self.__currentPoint = pt3
353
+ elif n == 1:
354
+ self.qCurveTo(*points)
355
+ elif n == 0:
356
+ self.lineTo(points[0])
357
+ else:
358
+ raise AssertionError("can't get there from here")
359
+
360
+ def qCurveTo(self, *points):
361
+ n = len(points) - 1 # 'n' is the number of control points
362
+ assert n >= 0
363
+ if points[-1] is None:
364
+ # Special case for TrueType quadratics: it is possible to
365
+ # define a contour with NO on-curve points. BasePen supports
366
+ # this by allowing the final argument (the expected on-curve
367
+ # point) to be None. We simulate the feature by making the implied
368
+ # on-curve point between the last and the first off-curve points
369
+ # explicit.
370
+ x, y = points[-2] # last off-curve point
371
+ nx, ny = points[0] # first off-curve point
372
+ impliedStartPoint = (0.5 * (x + nx), 0.5 * (y + ny))
373
+ self.__currentPoint = impliedStartPoint
374
+ self._moveTo(impliedStartPoint)
375
+ points = points[:-1] + (impliedStartPoint,)
376
+ if n > 0:
377
+ # Split the string of points into discrete quadratic curve
378
+ # segments. Between any two consecutive off-curve points
379
+ # there's an implied on-curve point exactly in the middle.
380
+ # This is where the segment splits.
381
+ _qCurveToOne = self._qCurveToOne
382
+ for pt1, pt2 in decomposeQuadraticSegment(points):
383
+ _qCurveToOne(pt1, pt2)
384
+ self.__currentPoint = pt2
385
+ else:
386
+ self.lineTo(points[0])
387
+
388
+
389
+ def decomposeSuperBezierSegment(points):
390
+ """Split the SuperBezier described by 'points' into a list of regular
391
+ bezier segments. The 'points' argument must be a sequence with length
392
+ 3 or greater, containing (x, y) coordinates. The last point is the
393
+ destination on-curve point, the rest of the points are off-curve points.
394
+ The start point should not be supplied.
395
+
396
+ This function returns a list of (pt1, pt2, pt3) tuples, which each
397
+ specify a regular curveto-style bezier segment.
398
+ """
399
+ n = len(points) - 1
400
+ assert n > 1
401
+ bezierSegments = []
402
+ pt1, pt2, pt3 = points[0], None, None
403
+ for i in range(2, n + 1):
404
+ # calculate points in between control points.
405
+ nDivisions = min(i, 3, n - i + 2)
406
+ for j in range(1, nDivisions):
407
+ factor = j / nDivisions
408
+ temp1 = points[i - 1]
409
+ temp2 = points[i - 2]
410
+ temp = (
411
+ temp2[0] + factor * (temp1[0] - temp2[0]),
412
+ temp2[1] + factor * (temp1[1] - temp2[1]),
413
+ )
414
+ if pt2 is None:
415
+ pt2 = temp
416
+ else:
417
+ pt3 = (0.5 * (pt2[0] + temp[0]), 0.5 * (pt2[1] + temp[1]))
418
+ bezierSegments.append((pt1, pt2, pt3))
419
+ pt1, pt2, pt3 = temp, None, None
420
+ bezierSegments.append((pt1, points[-2], points[-1]))
421
+ return bezierSegments
422
+
423
+
424
+ def decomposeQuadraticSegment(points):
425
+ """Split the quadratic curve segment described by 'points' into a list
426
+ of "atomic" quadratic segments. The 'points' argument must be a sequence
427
+ with length 2 or greater, containing (x, y) coordinates. The last point
428
+ is the destination on-curve point, the rest of the points are off-curve
429
+ points. The start point should not be supplied.
430
+
431
+ This function returns a list of (pt1, pt2) tuples, which each specify a
432
+ plain quadratic bezier segment.
433
+ """
434
+ n = len(points) - 1
435
+ assert n > 0
436
+ quadSegments = []
437
+ for i in range(n - 1):
438
+ x, y = points[i]
439
+ nx, ny = points[i + 1]
440
+ impliedPt = (0.5 * (x + nx), 0.5 * (y + ny))
441
+ quadSegments.append((points[i], impliedPt))
442
+ quadSegments.append((points[-2], points[-1]))
443
+ return quadSegments
444
+
445
+
446
+ class _TestPen(BasePen):
447
+ """Test class that prints PostScript to stdout."""
448
+
449
+ def _moveTo(self, pt):
450
+ print("%s %s moveto" % (pt[0], pt[1]))
451
+
452
+ def _lineTo(self, pt):
453
+ print("%s %s lineto" % (pt[0], pt[1]))
454
+
455
+ def _curveToOne(self, bcp1, bcp2, pt):
456
+ print(
457
+ "%s %s %s %s %s %s curveto"
458
+ % (bcp1[0], bcp1[1], bcp2[0], bcp2[1], pt[0], pt[1])
459
+ )
460
+
461
+ def _closePath(self):
462
+ print("closepath")
463
+
464
+
465
+ if __name__ == "__main__":
466
+ pen = _TestPen(None)
467
+ pen.moveTo((0, 0))
468
+ pen.lineTo((0, 100))
469
+ pen.curveTo((50, 75), (60, 50), (50, 25), (0, 0))
470
+ pen.closePath()
471
+
472
+ pen = _TestPen(None)
473
+ # testing the "no on-curve point" scenario
474
+ pen.qCurveTo((0, 0), (0, 100), (100, 100), (100, 0), None)
475
+ pen.closePath()
@@ -0,0 +1,98 @@
1
+ from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
2
+ from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
3
+ from fontTools.pens.basePen import BasePen
4
+
5
+
6
+ __all__ = ["BoundsPen", "ControlBoundsPen"]
7
+
8
+
9
+ class ControlBoundsPen(BasePen):
10
+ """Pen to calculate the "control bounds" of a shape. This is the
11
+ bounding box of all control points, so may be larger than the
12
+ actual bounding box if there are curves that don't have points
13
+ on their extremes.
14
+
15
+ When the shape has been drawn, the bounds are available as the
16
+ ``bounds`` attribute of the pen object. It's a 4-tuple::
17
+
18
+ (xMin, yMin, xMax, yMax).
19
+
20
+ If ``ignoreSinglePoints`` is True, single points are ignored.
21
+ """
22
+
23
+ def __init__(self, glyphSet, ignoreSinglePoints=False):
24
+ BasePen.__init__(self, glyphSet)
25
+ self.ignoreSinglePoints = ignoreSinglePoints
26
+ self.init()
27
+
28
+ def init(self):
29
+ self.bounds = None
30
+ self._start = None
31
+
32
+ def _moveTo(self, pt):
33
+ self._start = pt
34
+ if not self.ignoreSinglePoints:
35
+ self._addMoveTo()
36
+
37
+ def _addMoveTo(self):
38
+ if self._start is None:
39
+ return
40
+ bounds = self.bounds
41
+ if bounds:
42
+ self.bounds = updateBounds(bounds, self._start)
43
+ else:
44
+ x, y = self._start
45
+ self.bounds = (x, y, x, y)
46
+ self._start = None
47
+
48
+ def _lineTo(self, pt):
49
+ self._addMoveTo()
50
+ self.bounds = updateBounds(self.bounds, pt)
51
+
52
+ def _curveToOne(self, bcp1, bcp2, pt):
53
+ self._addMoveTo()
54
+ bounds = self.bounds
55
+ bounds = updateBounds(bounds, bcp1)
56
+ bounds = updateBounds(bounds, bcp2)
57
+ bounds = updateBounds(bounds, pt)
58
+ self.bounds = bounds
59
+
60
+ def _qCurveToOne(self, bcp, pt):
61
+ self._addMoveTo()
62
+ bounds = self.bounds
63
+ bounds = updateBounds(bounds, bcp)
64
+ bounds = updateBounds(bounds, pt)
65
+ self.bounds = bounds
66
+
67
+
68
+ class BoundsPen(ControlBoundsPen):
69
+ """Pen to calculate the bounds of a shape. It calculates the
70
+ correct bounds even when the shape contains curves that don't
71
+ have points on their extremes. This is somewhat slower to compute
72
+ than the "control bounds".
73
+
74
+ When the shape has been drawn, the bounds are available as the
75
+ ``bounds`` attribute of the pen object. It's a 4-tuple::
76
+
77
+ (xMin, yMin, xMax, yMax)
78
+ """
79
+
80
+ def _curveToOne(self, bcp1, bcp2, pt):
81
+ self._addMoveTo()
82
+ bounds = self.bounds
83
+ bounds = updateBounds(bounds, pt)
84
+ if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds):
85
+ bounds = unionRect(
86
+ bounds, calcCubicBounds(self._getCurrentPoint(), bcp1, bcp2, pt)
87
+ )
88
+ self.bounds = bounds
89
+
90
+ def _qCurveToOne(self, bcp, pt):
91
+ self._addMoveTo()
92
+ bounds = self.bounds
93
+ bounds = updateBounds(bounds, pt)
94
+ if not pointInRect(bcp, bounds):
95
+ bounds = unionRect(
96
+ bounds, calcQuadraticBounds(self._getCurrentPoint(), bcp, pt)
97
+ )
98
+ self.bounds = bounds
@@ -0,0 +1,26 @@
1
+ """Pen to draw to a Cairo graphics library context."""
2
+
3
+ from fontTools.pens.basePen import BasePen
4
+
5
+
6
+ __all__ = ["CairoPen"]
7
+
8
+
9
+ class CairoPen(BasePen):
10
+ """Pen to draw to a Cairo graphics library context."""
11
+
12
+ def __init__(self, glyphSet, context):
13
+ BasePen.__init__(self, glyphSet)
14
+ self.context = context
15
+
16
+ def _moveTo(self, p):
17
+ self.context.move_to(*p)
18
+
19
+ def _lineTo(self, p):
20
+ self.context.line_to(*p)
21
+
22
+ def _curveToOne(self, p1, p2, p3):
23
+ self.context.curve_to(*p1, *p2, *p3)
24
+
25
+ def _closePath(self):
26
+ self.context.close_path()
@@ -0,0 +1,26 @@
1
+ from fontTools.pens.basePen import BasePen
2
+
3
+
4
+ __all__ = ["CocoaPen"]
5
+
6
+
7
+ class CocoaPen(BasePen):
8
+ def __init__(self, glyphSet, path=None):
9
+ BasePen.__init__(self, glyphSet)
10
+ if path is None:
11
+ from AppKit import NSBezierPath
12
+
13
+ path = NSBezierPath.bezierPath()
14
+ self.path = path
15
+
16
+ def _moveTo(self, p):
17
+ self.path.moveToPoint_(p)
18
+
19
+ def _lineTo(self, p):
20
+ self.path.lineToPoint_(p)
21
+
22
+ def _curveToOne(self, p1, p2, p3):
23
+ self.path.curveToPoint_controlPoint1_controlPoint2_(p3, p1, p2)
24
+
25
+ def _closePath(self):
26
+ self.path.closePath()