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.
- fontTools/__init__.py +8 -0
- fontTools/__main__.py +35 -0
- fontTools/afmLib.py +439 -0
- fontTools/agl.py +5233 -0
- fontTools/annotations.py +30 -0
- fontTools/cffLib/CFF2ToCFF.py +258 -0
- fontTools/cffLib/CFFToCFF2.py +305 -0
- fontTools/cffLib/__init__.py +3694 -0
- fontTools/cffLib/specializer.py +927 -0
- fontTools/cffLib/transforms.py +495 -0
- fontTools/cffLib/width.py +210 -0
- fontTools/colorLib/__init__.py +0 -0
- fontTools/colorLib/builder.py +664 -0
- fontTools/colorLib/errors.py +2 -0
- fontTools/colorLib/geometry.py +143 -0
- fontTools/colorLib/table_builder.py +223 -0
- fontTools/colorLib/unbuilder.py +81 -0
- fontTools/config/__init__.py +90 -0
- fontTools/cu2qu/__init__.py +15 -0
- fontTools/cu2qu/__main__.py +6 -0
- fontTools/cu2qu/benchmark.py +54 -0
- fontTools/cu2qu/cli.py +198 -0
- fontTools/cu2qu/cu2qu.c +15817 -0
- fontTools/cu2qu/cu2qu.cp311-win32.pyd +0 -0
- fontTools/cu2qu/cu2qu.py +563 -0
- fontTools/cu2qu/errors.py +77 -0
- fontTools/cu2qu/ufo.py +363 -0
- fontTools/designspaceLib/__init__.py +3343 -0
- fontTools/designspaceLib/__main__.py +6 -0
- fontTools/designspaceLib/split.py +475 -0
- fontTools/designspaceLib/statNames.py +260 -0
- fontTools/designspaceLib/types.py +147 -0
- fontTools/encodings/MacRoman.py +258 -0
- fontTools/encodings/StandardEncoding.py +258 -0
- fontTools/encodings/__init__.py +1 -0
- fontTools/encodings/codecs.py +135 -0
- fontTools/feaLib/__init__.py +4 -0
- fontTools/feaLib/__main__.py +78 -0
- fontTools/feaLib/ast.py +2143 -0
- fontTools/feaLib/builder.py +1814 -0
- fontTools/feaLib/error.py +22 -0
- fontTools/feaLib/lexer.c +17029 -0
- fontTools/feaLib/lexer.cp311-win32.pyd +0 -0
- fontTools/feaLib/lexer.py +287 -0
- fontTools/feaLib/location.py +12 -0
- fontTools/feaLib/lookupDebugInfo.py +12 -0
- fontTools/feaLib/parser.py +2394 -0
- fontTools/feaLib/variableScalar.py +118 -0
- fontTools/fontBuilder.py +1014 -0
- fontTools/help.py +36 -0
- fontTools/merge/__init__.py +248 -0
- fontTools/merge/__main__.py +6 -0
- fontTools/merge/base.py +81 -0
- fontTools/merge/cmap.py +173 -0
- fontTools/merge/layout.py +526 -0
- fontTools/merge/options.py +85 -0
- fontTools/merge/tables.py +352 -0
- fontTools/merge/unicode.py +78 -0
- fontTools/merge/util.py +143 -0
- fontTools/misc/__init__.py +1 -0
- fontTools/misc/arrayTools.py +424 -0
- fontTools/misc/bezierTools.c +39731 -0
- fontTools/misc/bezierTools.cp311-win32.pyd +0 -0
- fontTools/misc/bezierTools.py +1500 -0
- fontTools/misc/classifyTools.py +170 -0
- fontTools/misc/cliTools.py +53 -0
- fontTools/misc/configTools.py +349 -0
- fontTools/misc/cython.py +27 -0
- fontTools/misc/dictTools.py +83 -0
- fontTools/misc/eexec.py +119 -0
- fontTools/misc/encodingTools.py +72 -0
- fontTools/misc/enumTools.py +23 -0
- fontTools/misc/etree.py +456 -0
- fontTools/misc/filenames.py +245 -0
- fontTools/misc/filesystem/__init__.py +68 -0
- fontTools/misc/filesystem/_base.py +134 -0
- fontTools/misc/filesystem/_copy.py +45 -0
- fontTools/misc/filesystem/_errors.py +54 -0
- fontTools/misc/filesystem/_info.py +75 -0
- fontTools/misc/filesystem/_osfs.py +164 -0
- fontTools/misc/filesystem/_path.py +67 -0
- fontTools/misc/filesystem/_subfs.py +92 -0
- fontTools/misc/filesystem/_tempfs.py +34 -0
- fontTools/misc/filesystem/_tools.py +34 -0
- fontTools/misc/filesystem/_walk.py +55 -0
- fontTools/misc/filesystem/_zipfs.py +204 -0
- fontTools/misc/fixedTools.py +253 -0
- fontTools/misc/intTools.py +25 -0
- fontTools/misc/iterTools.py +12 -0
- fontTools/misc/lazyTools.py +42 -0
- fontTools/misc/loggingTools.py +543 -0
- fontTools/misc/macCreatorType.py +56 -0
- fontTools/misc/macRes.py +261 -0
- fontTools/misc/plistlib/__init__.py +681 -0
- fontTools/misc/plistlib/py.typed +0 -0
- fontTools/misc/psCharStrings.py +1511 -0
- fontTools/misc/psLib.py +398 -0
- fontTools/misc/psOperators.py +572 -0
- fontTools/misc/py23.py +96 -0
- fontTools/misc/roundTools.py +110 -0
- fontTools/misc/sstruct.py +227 -0
- fontTools/misc/symfont.py +242 -0
- fontTools/misc/testTools.py +233 -0
- fontTools/misc/textTools.py +156 -0
- fontTools/misc/timeTools.py +88 -0
- fontTools/misc/transform.py +516 -0
- fontTools/misc/treeTools.py +45 -0
- fontTools/misc/vector.py +147 -0
- fontTools/misc/visitor.py +158 -0
- fontTools/misc/xmlReader.py +188 -0
- fontTools/misc/xmlWriter.py +231 -0
- fontTools/mtiLib/__init__.py +1400 -0
- fontTools/mtiLib/__main__.py +5 -0
- fontTools/otlLib/__init__.py +1 -0
- fontTools/otlLib/builder.py +3465 -0
- fontTools/otlLib/error.py +11 -0
- fontTools/otlLib/maxContextCalc.py +96 -0
- fontTools/otlLib/optimize/__init__.py +53 -0
- fontTools/otlLib/optimize/__main__.py +6 -0
- fontTools/otlLib/optimize/gpos.py +439 -0
- fontTools/pens/__init__.py +1 -0
- fontTools/pens/areaPen.py +52 -0
- fontTools/pens/basePen.py +475 -0
- fontTools/pens/boundsPen.py +98 -0
- fontTools/pens/cairoPen.py +26 -0
- fontTools/pens/cocoaPen.py +26 -0
- fontTools/pens/cu2quPen.py +325 -0
- fontTools/pens/explicitClosingLinePen.py +101 -0
- fontTools/pens/filterPen.py +433 -0
- fontTools/pens/freetypePen.py +462 -0
- fontTools/pens/hashPointPen.py +89 -0
- fontTools/pens/momentsPen.c +13378 -0
- fontTools/pens/momentsPen.cp311-win32.pyd +0 -0
- fontTools/pens/momentsPen.py +879 -0
- fontTools/pens/perimeterPen.py +69 -0
- fontTools/pens/pointInsidePen.py +192 -0
- fontTools/pens/pointPen.py +643 -0
- fontTools/pens/qtPen.py +29 -0
- fontTools/pens/qu2cuPen.py +105 -0
- fontTools/pens/quartzPen.py +43 -0
- fontTools/pens/recordingPen.py +335 -0
- fontTools/pens/reportLabPen.py +79 -0
- fontTools/pens/reverseContourPen.py +96 -0
- fontTools/pens/roundingPen.py +130 -0
- fontTools/pens/statisticsPen.py +312 -0
- fontTools/pens/svgPathPen.py +310 -0
- fontTools/pens/t2CharStringPen.py +88 -0
- fontTools/pens/teePen.py +55 -0
- fontTools/pens/transformPen.py +115 -0
- fontTools/pens/ttGlyphPen.py +335 -0
- fontTools/pens/wxPen.py +29 -0
- fontTools/qu2cu/__init__.py +15 -0
- fontTools/qu2cu/__main__.py +7 -0
- fontTools/qu2cu/benchmark.py +56 -0
- fontTools/qu2cu/cli.py +125 -0
- fontTools/qu2cu/qu2cu.c +16682 -0
- fontTools/qu2cu/qu2cu.cp311-win32.pyd +0 -0
- fontTools/qu2cu/qu2cu.py +405 -0
- fontTools/subset/__init__.py +4096 -0
- fontTools/subset/__main__.py +6 -0
- fontTools/subset/cff.py +184 -0
- fontTools/subset/svg.py +253 -0
- fontTools/subset/util.py +25 -0
- fontTools/svgLib/__init__.py +3 -0
- fontTools/svgLib/path/__init__.py +65 -0
- fontTools/svgLib/path/arc.py +154 -0
- fontTools/svgLib/path/parser.py +322 -0
- fontTools/svgLib/path/shapes.py +183 -0
- fontTools/t1Lib/__init__.py +648 -0
- fontTools/tfmLib.py +460 -0
- fontTools/ttLib/__init__.py +30 -0
- fontTools/ttLib/__main__.py +148 -0
- fontTools/ttLib/macUtils.py +54 -0
- fontTools/ttLib/removeOverlaps.py +395 -0
- fontTools/ttLib/reorderGlyphs.py +285 -0
- fontTools/ttLib/scaleUpem.py +436 -0
- fontTools/ttLib/sfnt.py +661 -0
- fontTools/ttLib/standardGlyphOrder.py +271 -0
- fontTools/ttLib/tables/B_A_S_E_.py +14 -0
- fontTools/ttLib/tables/BitmapGlyphMetrics.py +64 -0
- fontTools/ttLib/tables/C_B_D_T_.py +113 -0
- fontTools/ttLib/tables/C_B_L_C_.py +19 -0
- fontTools/ttLib/tables/C_F_F_.py +61 -0
- fontTools/ttLib/tables/C_F_F__2.py +26 -0
- fontTools/ttLib/tables/C_O_L_R_.py +165 -0
- fontTools/ttLib/tables/C_P_A_L_.py +305 -0
- fontTools/ttLib/tables/D_S_I_G_.py +158 -0
- fontTools/ttLib/tables/D__e_b_g.py +35 -0
- fontTools/ttLib/tables/DefaultTable.py +49 -0
- fontTools/ttLib/tables/E_B_D_T_.py +835 -0
- fontTools/ttLib/tables/E_B_L_C_.py +718 -0
- fontTools/ttLib/tables/F_F_T_M_.py +52 -0
- fontTools/ttLib/tables/F__e_a_t.py +149 -0
- fontTools/ttLib/tables/G_D_E_F_.py +13 -0
- fontTools/ttLib/tables/G_M_A_P_.py +148 -0
- fontTools/ttLib/tables/G_P_K_G_.py +133 -0
- fontTools/ttLib/tables/G_P_O_S_.py +14 -0
- fontTools/ttLib/tables/G_S_U_B_.py +13 -0
- fontTools/ttLib/tables/G_V_A_R_.py +5 -0
- fontTools/ttLib/tables/G__l_a_t.py +235 -0
- fontTools/ttLib/tables/G__l_o_c.py +85 -0
- fontTools/ttLib/tables/H_V_A_R_.py +13 -0
- fontTools/ttLib/tables/J_S_T_F_.py +13 -0
- fontTools/ttLib/tables/L_T_S_H_.py +58 -0
- fontTools/ttLib/tables/M_A_T_H_.py +13 -0
- fontTools/ttLib/tables/M_E_T_A_.py +352 -0
- fontTools/ttLib/tables/M_V_A_R_.py +13 -0
- fontTools/ttLib/tables/O_S_2f_2.py +752 -0
- fontTools/ttLib/tables/S_I_N_G_.py +99 -0
- fontTools/ttLib/tables/S_T_A_T_.py +15 -0
- fontTools/ttLib/tables/S_V_G_.py +223 -0
- fontTools/ttLib/tables/S__i_l_f.py +1040 -0
- fontTools/ttLib/tables/S__i_l_l.py +92 -0
- fontTools/ttLib/tables/T_S_I_B_.py +13 -0
- fontTools/ttLib/tables/T_S_I_C_.py +14 -0
- fontTools/ttLib/tables/T_S_I_D_.py +13 -0
- fontTools/ttLib/tables/T_S_I_J_.py +13 -0
- fontTools/ttLib/tables/T_S_I_P_.py +13 -0
- fontTools/ttLib/tables/T_S_I_S_.py +13 -0
- fontTools/ttLib/tables/T_S_I_V_.py +26 -0
- fontTools/ttLib/tables/T_S_I__0.py +70 -0
- fontTools/ttLib/tables/T_S_I__1.py +163 -0
- fontTools/ttLib/tables/T_S_I__2.py +17 -0
- fontTools/ttLib/tables/T_S_I__3.py +22 -0
- fontTools/ttLib/tables/T_S_I__5.py +60 -0
- fontTools/ttLib/tables/T_T_F_A_.py +14 -0
- fontTools/ttLib/tables/TupleVariation.py +884 -0
- fontTools/ttLib/tables/V_A_R_C_.py +12 -0
- fontTools/ttLib/tables/V_D_M_X_.py +249 -0
- fontTools/ttLib/tables/V_O_R_G_.py +165 -0
- fontTools/ttLib/tables/V_V_A_R_.py +13 -0
- fontTools/ttLib/tables/__init__.py +98 -0
- fontTools/ttLib/tables/_a_n_k_r.py +15 -0
- fontTools/ttLib/tables/_a_v_a_r.py +193 -0
- fontTools/ttLib/tables/_b_s_l_n.py +15 -0
- fontTools/ttLib/tables/_c_i_d_g.py +24 -0
- fontTools/ttLib/tables/_c_m_a_p.py +1591 -0
- fontTools/ttLib/tables/_c_v_a_r.py +94 -0
- fontTools/ttLib/tables/_c_v_t.py +56 -0
- fontTools/ttLib/tables/_f_e_a_t.py +15 -0
- fontTools/ttLib/tables/_f_p_g_m.py +62 -0
- fontTools/ttLib/tables/_f_v_a_r.py +261 -0
- fontTools/ttLib/tables/_g_a_s_p.py +63 -0
- fontTools/ttLib/tables/_g_c_i_d.py +13 -0
- fontTools/ttLib/tables/_g_l_y_f.py +2311 -0
- fontTools/ttLib/tables/_g_v_a_r.py +340 -0
- fontTools/ttLib/tables/_h_d_m_x.py +127 -0
- fontTools/ttLib/tables/_h_e_a_d.py +130 -0
- fontTools/ttLib/tables/_h_h_e_a.py +147 -0
- fontTools/ttLib/tables/_h_m_t_x.py +164 -0
- fontTools/ttLib/tables/_k_e_r_n.py +289 -0
- fontTools/ttLib/tables/_l_c_a_r.py +13 -0
- fontTools/ttLib/tables/_l_o_c_a.py +70 -0
- fontTools/ttLib/tables/_l_t_a_g.py +72 -0
- fontTools/ttLib/tables/_m_a_x_p.py +147 -0
- fontTools/ttLib/tables/_m_e_t_a.py +112 -0
- fontTools/ttLib/tables/_m_o_r_t.py +14 -0
- fontTools/ttLib/tables/_m_o_r_x.py +15 -0
- fontTools/ttLib/tables/_n_a_m_e.py +1242 -0
- fontTools/ttLib/tables/_o_p_b_d.py +14 -0
- fontTools/ttLib/tables/_p_o_s_t.py +319 -0
- fontTools/ttLib/tables/_p_r_e_p.py +16 -0
- fontTools/ttLib/tables/_p_r_o_p.py +12 -0
- fontTools/ttLib/tables/_s_b_i_x.py +129 -0
- fontTools/ttLib/tables/_t_r_a_k.py +332 -0
- fontTools/ttLib/tables/_v_h_e_a.py +139 -0
- fontTools/ttLib/tables/_v_m_t_x.py +19 -0
- fontTools/ttLib/tables/asciiTable.py +20 -0
- fontTools/ttLib/tables/grUtils.py +92 -0
- fontTools/ttLib/tables/otBase.py +1458 -0
- fontTools/ttLib/tables/otConverters.py +2068 -0
- fontTools/ttLib/tables/otData.py +6400 -0
- fontTools/ttLib/tables/otTables.py +2703 -0
- fontTools/ttLib/tables/otTraverse.py +163 -0
- fontTools/ttLib/tables/sbixGlyph.py +149 -0
- fontTools/ttLib/tables/sbixStrike.py +177 -0
- fontTools/ttLib/tables/table_API_readme.txt +91 -0
- fontTools/ttLib/tables/ttProgram.py +594 -0
- fontTools/ttLib/ttCollection.py +125 -0
- fontTools/ttLib/ttFont.py +1148 -0
- fontTools/ttLib/ttGlyphSet.py +490 -0
- fontTools/ttLib/ttVisitor.py +32 -0
- fontTools/ttLib/woff2.py +1680 -0
- fontTools/ttx.py +479 -0
- fontTools/ufoLib/__init__.py +2575 -0
- fontTools/ufoLib/converters.py +407 -0
- fontTools/ufoLib/errors.py +30 -0
- fontTools/ufoLib/etree.py +6 -0
- fontTools/ufoLib/filenames.py +356 -0
- fontTools/ufoLib/glifLib.py +2120 -0
- fontTools/ufoLib/kerning.py +141 -0
- fontTools/ufoLib/plistlib.py +47 -0
- fontTools/ufoLib/pointPen.py +6 -0
- fontTools/ufoLib/utils.py +107 -0
- fontTools/ufoLib/validators.py +1208 -0
- fontTools/unicode.py +50 -0
- fontTools/unicodedata/Blocks.py +817 -0
- fontTools/unicodedata/Mirrored.py +446 -0
- fontTools/unicodedata/OTTags.py +50 -0
- fontTools/unicodedata/ScriptExtensions.py +832 -0
- fontTools/unicodedata/Scripts.py +3639 -0
- fontTools/unicodedata/__init__.py +306 -0
- fontTools/varLib/__init__.py +1600 -0
- fontTools/varLib/__main__.py +6 -0
- fontTools/varLib/avar/__init__.py +0 -0
- fontTools/varLib/avar/__main__.py +72 -0
- fontTools/varLib/avar/build.py +79 -0
- fontTools/varLib/avar/map.py +108 -0
- fontTools/varLib/avar/plan.py +1004 -0
- fontTools/varLib/avar/unbuild.py +271 -0
- fontTools/varLib/avarPlanner.py +8 -0
- fontTools/varLib/builder.py +215 -0
- fontTools/varLib/cff.py +631 -0
- fontTools/varLib/errors.py +219 -0
- fontTools/varLib/featureVars.py +703 -0
- fontTools/varLib/hvar.py +113 -0
- fontTools/varLib/instancer/__init__.py +2052 -0
- fontTools/varLib/instancer/__main__.py +5 -0
- fontTools/varLib/instancer/featureVars.py +190 -0
- fontTools/varLib/instancer/names.py +388 -0
- fontTools/varLib/instancer/solver.py +309 -0
- fontTools/varLib/interpolatable.py +1209 -0
- fontTools/varLib/interpolatableHelpers.py +399 -0
- fontTools/varLib/interpolatablePlot.py +1269 -0
- fontTools/varLib/interpolatableTestContourOrder.py +82 -0
- fontTools/varLib/interpolatableTestStartingPoint.py +107 -0
- fontTools/varLib/interpolate_layout.py +124 -0
- fontTools/varLib/iup.c +19815 -0
- fontTools/varLib/iup.cp311-win32.pyd +0 -0
- fontTools/varLib/iup.py +490 -0
- fontTools/varLib/merger.py +1717 -0
- fontTools/varLib/models.py +642 -0
- fontTools/varLib/multiVarStore.py +253 -0
- fontTools/varLib/mutator.py +529 -0
- fontTools/varLib/mvar.py +40 -0
- fontTools/varLib/plot.py +238 -0
- fontTools/varLib/stat.py +149 -0
- fontTools/varLib/varStore.py +739 -0
- fontTools/voltLib/__init__.py +5 -0
- fontTools/voltLib/__main__.py +206 -0
- fontTools/voltLib/ast.py +452 -0
- fontTools/voltLib/error.py +12 -0
- fontTools/voltLib/lexer.py +99 -0
- fontTools/voltLib/parser.py +664 -0
- fontTools/voltLib/voltToFea.py +911 -0
- fonttools-4.60.2.data/data/share/man/man1/ttx.1 +225 -0
- fonttools-4.60.2.dist-info/METADATA +2250 -0
- fonttools-4.60.2.dist-info/RECORD +353 -0
- fonttools-4.60.2.dist-info/WHEEL +5 -0
- fonttools-4.60.2.dist-info/entry_points.txt +5 -0
- fonttools-4.60.2.dist-info/licenses/LICENSE +21 -0
- fonttools-4.60.2.dist-info/licenses/LICENSE.external +388 -0
- fonttools-4.60.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from fontTools.misc.roundTools import noRound, otRound
|
|
2
|
+
from fontTools.misc.transform import Transform
|
|
3
|
+
from fontTools.pens.filterPen import FilterPen, FilterPointPen
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__all__ = ["RoundingPen", "RoundingPointPen"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RoundingPen(FilterPen):
|
|
10
|
+
"""
|
|
11
|
+
Filter pen that rounds point coordinates and component XY offsets to integer. For
|
|
12
|
+
rounding the component transform values, a separate round function can be passed to
|
|
13
|
+
the pen.
|
|
14
|
+
|
|
15
|
+
>>> from fontTools.pens.recordingPen import RecordingPen
|
|
16
|
+
>>> recpen = RecordingPen()
|
|
17
|
+
>>> roundpen = RoundingPen(recpen)
|
|
18
|
+
>>> roundpen.moveTo((0.4, 0.6))
|
|
19
|
+
>>> roundpen.lineTo((1.6, 2.5))
|
|
20
|
+
>>> roundpen.qCurveTo((2.4, 4.6), (3.3, 5.7), (4.9, 6.1))
|
|
21
|
+
>>> roundpen.curveTo((6.4, 8.6), (7.3, 9.7), (8.9, 10.1))
|
|
22
|
+
>>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
|
|
23
|
+
>>> recpen.value == [
|
|
24
|
+
... ('moveTo', ((0, 1),)),
|
|
25
|
+
... ('lineTo', ((2, 3),)),
|
|
26
|
+
... ('qCurveTo', ((2, 5), (3, 6), (5, 6))),
|
|
27
|
+
... ('curveTo', ((6, 9), (7, 10), (9, 10))),
|
|
28
|
+
... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10))),
|
|
29
|
+
... ]
|
|
30
|
+
True
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
|
|
34
|
+
super().__init__(outPen)
|
|
35
|
+
self.roundFunc = roundFunc
|
|
36
|
+
self.transformRoundFunc = transformRoundFunc
|
|
37
|
+
|
|
38
|
+
def moveTo(self, pt):
|
|
39
|
+
self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
|
|
40
|
+
|
|
41
|
+
def lineTo(self, pt):
|
|
42
|
+
self._outPen.lineTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
|
|
43
|
+
|
|
44
|
+
def curveTo(self, *points):
|
|
45
|
+
self._outPen.curveTo(
|
|
46
|
+
*((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def qCurveTo(self, *points):
|
|
50
|
+
self._outPen.qCurveTo(
|
|
51
|
+
*((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def addComponent(self, glyphName, transformation):
|
|
55
|
+
xx, xy, yx, yy, dx, dy = transformation
|
|
56
|
+
self._outPen.addComponent(
|
|
57
|
+
glyphName,
|
|
58
|
+
Transform(
|
|
59
|
+
self.transformRoundFunc(xx),
|
|
60
|
+
self.transformRoundFunc(xy),
|
|
61
|
+
self.transformRoundFunc(yx),
|
|
62
|
+
self.transformRoundFunc(yy),
|
|
63
|
+
self.roundFunc(dx),
|
|
64
|
+
self.roundFunc(dy),
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class RoundingPointPen(FilterPointPen):
|
|
70
|
+
"""
|
|
71
|
+
Filter point pen that rounds point coordinates and component XY offsets to integer.
|
|
72
|
+
For rounding the component scale values, a separate round function can be passed to
|
|
73
|
+
the pen.
|
|
74
|
+
|
|
75
|
+
>>> from fontTools.pens.recordingPen import RecordingPointPen
|
|
76
|
+
>>> recpen = RecordingPointPen()
|
|
77
|
+
>>> roundpen = RoundingPointPen(recpen)
|
|
78
|
+
>>> roundpen.beginPath()
|
|
79
|
+
>>> roundpen.addPoint((0.4, 0.6), 'line')
|
|
80
|
+
>>> roundpen.addPoint((1.6, 2.5), 'line')
|
|
81
|
+
>>> roundpen.addPoint((2.4, 4.6))
|
|
82
|
+
>>> roundpen.addPoint((3.3, 5.7))
|
|
83
|
+
>>> roundpen.addPoint((4.9, 6.1), 'qcurve')
|
|
84
|
+
>>> roundpen.endPath()
|
|
85
|
+
>>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
|
|
86
|
+
>>> recpen.value == [
|
|
87
|
+
... ('beginPath', (), {}),
|
|
88
|
+
... ('addPoint', ((0, 1), 'line', False, None), {}),
|
|
89
|
+
... ('addPoint', ((2, 3), 'line', False, None), {}),
|
|
90
|
+
... ('addPoint', ((2, 5), None, False, None), {}),
|
|
91
|
+
... ('addPoint', ((3, 6), None, False, None), {}),
|
|
92
|
+
... ('addPoint', ((5, 6), 'qcurve', False, None), {}),
|
|
93
|
+
... ('endPath', (), {}),
|
|
94
|
+
... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10)), {}),
|
|
95
|
+
... ]
|
|
96
|
+
True
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
|
|
100
|
+
super().__init__(outPen)
|
|
101
|
+
self.roundFunc = roundFunc
|
|
102
|
+
self.transformRoundFunc = transformRoundFunc
|
|
103
|
+
|
|
104
|
+
def addPoint(
|
|
105
|
+
self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
|
|
106
|
+
):
|
|
107
|
+
self._outPen.addPoint(
|
|
108
|
+
(self.roundFunc(pt[0]), self.roundFunc(pt[1])),
|
|
109
|
+
segmentType=segmentType,
|
|
110
|
+
smooth=smooth,
|
|
111
|
+
name=name,
|
|
112
|
+
identifier=identifier,
|
|
113
|
+
**kwargs,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
|
|
117
|
+
xx, xy, yx, yy, dx, dy = transformation
|
|
118
|
+
self._outPen.addComponent(
|
|
119
|
+
baseGlyphName,
|
|
120
|
+
Transform(
|
|
121
|
+
self.transformRoundFunc(xx),
|
|
122
|
+
self.transformRoundFunc(xy),
|
|
123
|
+
self.transformRoundFunc(yx),
|
|
124
|
+
self.transformRoundFunc(yy),
|
|
125
|
+
self.roundFunc(dx),
|
|
126
|
+
self.roundFunc(dy),
|
|
127
|
+
),
|
|
128
|
+
identifier=identifier,
|
|
129
|
+
**kwargs,
|
|
130
|
+
)
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Pen calculating area, center of mass, variance and standard-deviation,
|
|
2
|
+
covariance and correlation, and slant, of glyph shapes."""
|
|
3
|
+
|
|
4
|
+
from math import sqrt, degrees, atan
|
|
5
|
+
from fontTools.pens.basePen import BasePen, OpenContourError
|
|
6
|
+
from fontTools.pens.momentsPen import MomentsPen
|
|
7
|
+
|
|
8
|
+
__all__ = ["StatisticsPen", "StatisticsControlPen"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StatisticsBase:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self._zero()
|
|
14
|
+
|
|
15
|
+
def _zero(self):
|
|
16
|
+
self.area = 0
|
|
17
|
+
self.meanX = 0
|
|
18
|
+
self.meanY = 0
|
|
19
|
+
self.varianceX = 0
|
|
20
|
+
self.varianceY = 0
|
|
21
|
+
self.stddevX = 0
|
|
22
|
+
self.stddevY = 0
|
|
23
|
+
self.covariance = 0
|
|
24
|
+
self.correlation = 0
|
|
25
|
+
self.slant = 0
|
|
26
|
+
|
|
27
|
+
def _update(self):
|
|
28
|
+
# XXX The variance formulas should never produce a negative value,
|
|
29
|
+
# but due to reasons I don't understand, both of our pens do.
|
|
30
|
+
# So we take the absolute value here.
|
|
31
|
+
self.varianceX = abs(self.varianceX)
|
|
32
|
+
self.varianceY = abs(self.varianceY)
|
|
33
|
+
|
|
34
|
+
self.stddevX = stddevX = sqrt(self.varianceX)
|
|
35
|
+
self.stddevY = stddevY = sqrt(self.varianceY)
|
|
36
|
+
|
|
37
|
+
# Correlation(X,Y) = Covariance(X,Y) / ( stddev(X) * stddev(Y) )
|
|
38
|
+
# https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient
|
|
39
|
+
if stddevX * stddevY == 0:
|
|
40
|
+
correlation = float("NaN")
|
|
41
|
+
else:
|
|
42
|
+
# XXX The above formula should never produce a value outside
|
|
43
|
+
# the range [-1, 1], but due to reasons I don't understand,
|
|
44
|
+
# (probably the same issue as above), it does. So we clamp.
|
|
45
|
+
correlation = self.covariance / (stddevX * stddevY)
|
|
46
|
+
correlation = max(-1, min(1, correlation))
|
|
47
|
+
self.correlation = correlation if abs(correlation) > 1e-3 else 0
|
|
48
|
+
|
|
49
|
+
slant = (
|
|
50
|
+
self.covariance / self.varianceY if self.varianceY != 0 else float("NaN")
|
|
51
|
+
)
|
|
52
|
+
self.slant = slant if abs(slant) > 1e-3 else 0
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class StatisticsPen(StatisticsBase, MomentsPen):
|
|
56
|
+
"""Pen calculating area, center of mass, variance and
|
|
57
|
+
standard-deviation, covariance and correlation, and slant,
|
|
58
|
+
of glyph shapes.
|
|
59
|
+
|
|
60
|
+
Note that if the glyph shape is self-intersecting, the values
|
|
61
|
+
are not correct (but well-defined). Moreover, area will be
|
|
62
|
+
negative if contour directions are clockwise."""
|
|
63
|
+
|
|
64
|
+
def __init__(self, glyphset=None):
|
|
65
|
+
MomentsPen.__init__(self, glyphset=glyphset)
|
|
66
|
+
StatisticsBase.__init__(self)
|
|
67
|
+
|
|
68
|
+
def _closePath(self):
|
|
69
|
+
MomentsPen._closePath(self)
|
|
70
|
+
self._update()
|
|
71
|
+
|
|
72
|
+
def _update(self):
|
|
73
|
+
area = self.area
|
|
74
|
+
if not area:
|
|
75
|
+
self._zero()
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# Center of mass
|
|
79
|
+
# https://en.wikipedia.org/wiki/Center_of_mass#A_continuous_volume
|
|
80
|
+
self.meanX = meanX = self.momentX / area
|
|
81
|
+
self.meanY = meanY = self.momentY / area
|
|
82
|
+
|
|
83
|
+
# Var(X) = E[X^2] - E[X]^2
|
|
84
|
+
self.varianceX = self.momentXX / area - meanX * meanX
|
|
85
|
+
self.varianceY = self.momentYY / area - meanY * meanY
|
|
86
|
+
|
|
87
|
+
# Covariance(X,Y) = (E[X.Y] - E[X]E[Y])
|
|
88
|
+
self.covariance = self.momentXY / area - meanX * meanY
|
|
89
|
+
|
|
90
|
+
StatisticsBase._update(self)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class StatisticsControlPen(StatisticsBase, BasePen):
|
|
94
|
+
"""Pen calculating area, center of mass, variance and
|
|
95
|
+
standard-deviation, covariance and correlation, and slant,
|
|
96
|
+
of glyph shapes, using the control polygon only.
|
|
97
|
+
|
|
98
|
+
Note that if the glyph shape is self-intersecting, the values
|
|
99
|
+
are not correct (but well-defined). Moreover, area will be
|
|
100
|
+
negative if contour directions are clockwise."""
|
|
101
|
+
|
|
102
|
+
def __init__(self, glyphset=None):
|
|
103
|
+
BasePen.__init__(self, glyphset)
|
|
104
|
+
StatisticsBase.__init__(self)
|
|
105
|
+
self._nodes = []
|
|
106
|
+
|
|
107
|
+
def _moveTo(self, pt):
|
|
108
|
+
self._nodes.append(complex(*pt))
|
|
109
|
+
self._startPoint = pt
|
|
110
|
+
|
|
111
|
+
def _lineTo(self, pt):
|
|
112
|
+
self._nodes.append(complex(*pt))
|
|
113
|
+
|
|
114
|
+
def _qCurveToOne(self, pt1, pt2):
|
|
115
|
+
for pt in (pt1, pt2):
|
|
116
|
+
self._nodes.append(complex(*pt))
|
|
117
|
+
|
|
118
|
+
def _curveToOne(self, pt1, pt2, pt3):
|
|
119
|
+
for pt in (pt1, pt2, pt3):
|
|
120
|
+
self._nodes.append(complex(*pt))
|
|
121
|
+
|
|
122
|
+
def _closePath(self):
|
|
123
|
+
p0 = self._getCurrentPoint()
|
|
124
|
+
if p0 != self._startPoint:
|
|
125
|
+
self._lineTo(self._startPoint)
|
|
126
|
+
self._update()
|
|
127
|
+
|
|
128
|
+
def _endPath(self):
|
|
129
|
+
p0 = self._getCurrentPoint()
|
|
130
|
+
if p0 != self._startPoint:
|
|
131
|
+
raise OpenContourError("Glyph statistics not defined on open contours.")
|
|
132
|
+
self._update()
|
|
133
|
+
|
|
134
|
+
def _update(self):
|
|
135
|
+
nodes = self._nodes
|
|
136
|
+
n = len(nodes)
|
|
137
|
+
|
|
138
|
+
# Triangle formula
|
|
139
|
+
self.area = (
|
|
140
|
+
sum(
|
|
141
|
+
(p0.real * p1.imag - p1.real * p0.imag)
|
|
142
|
+
for p0, p1 in zip(nodes, nodes[1:] + nodes[:1])
|
|
143
|
+
)
|
|
144
|
+
/ 2
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Center of mass
|
|
148
|
+
# https://en.wikipedia.org/wiki/Center_of_mass#A_system_of_particles
|
|
149
|
+
sumNodes = sum(nodes)
|
|
150
|
+
self.meanX = meanX = sumNodes.real / n
|
|
151
|
+
self.meanY = meanY = sumNodes.imag / n
|
|
152
|
+
|
|
153
|
+
if n > 1:
|
|
154
|
+
# Var(X) = (sum[X^2] - sum[X]^2 / n) / (n - 1)
|
|
155
|
+
# https://www.statisticshowto.com/probability-and-statistics/descriptive-statistics/sample-variance/
|
|
156
|
+
self.varianceX = varianceX = (
|
|
157
|
+
sum(p.real * p.real for p in nodes)
|
|
158
|
+
- (sumNodes.real * sumNodes.real) / n
|
|
159
|
+
) / (n - 1)
|
|
160
|
+
self.varianceY = varianceY = (
|
|
161
|
+
sum(p.imag * p.imag for p in nodes)
|
|
162
|
+
- (sumNodes.imag * sumNodes.imag) / n
|
|
163
|
+
) / (n - 1)
|
|
164
|
+
|
|
165
|
+
# Covariance(X,Y) = (sum[X.Y] - sum[X].sum[Y] / n) / (n - 1)
|
|
166
|
+
self.covariance = covariance = (
|
|
167
|
+
sum(p.real * p.imag for p in nodes)
|
|
168
|
+
- (sumNodes.real * sumNodes.imag) / n
|
|
169
|
+
) / (n - 1)
|
|
170
|
+
else:
|
|
171
|
+
self.varianceX = varianceX = 0
|
|
172
|
+
self.varianceY = varianceY = 0
|
|
173
|
+
self.covariance = covariance = 0
|
|
174
|
+
|
|
175
|
+
StatisticsBase._update(self)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _test(glyphset, upem, glyphs, quiet=False, *, control=False):
|
|
179
|
+
from fontTools.pens.transformPen import TransformPen
|
|
180
|
+
from fontTools.misc.transform import Scale
|
|
181
|
+
|
|
182
|
+
wght_sum = 0
|
|
183
|
+
wght_sum_perceptual = 0
|
|
184
|
+
wdth_sum = 0
|
|
185
|
+
slnt_sum = 0
|
|
186
|
+
slnt_sum_perceptual = 0
|
|
187
|
+
for glyph_name in glyphs:
|
|
188
|
+
glyph = glyphset[glyph_name]
|
|
189
|
+
if control:
|
|
190
|
+
pen = StatisticsControlPen(glyphset=glyphset)
|
|
191
|
+
else:
|
|
192
|
+
pen = StatisticsPen(glyphset=glyphset)
|
|
193
|
+
transformer = TransformPen(pen, Scale(1.0 / upem))
|
|
194
|
+
glyph.draw(transformer)
|
|
195
|
+
|
|
196
|
+
area = abs(pen.area)
|
|
197
|
+
width = glyph.width
|
|
198
|
+
wght_sum += area
|
|
199
|
+
wght_sum_perceptual += pen.area * width
|
|
200
|
+
wdth_sum += width
|
|
201
|
+
slnt_sum += pen.slant
|
|
202
|
+
slnt_sum_perceptual += pen.slant * width
|
|
203
|
+
|
|
204
|
+
if quiet:
|
|
205
|
+
continue
|
|
206
|
+
|
|
207
|
+
print()
|
|
208
|
+
print("glyph:", glyph_name)
|
|
209
|
+
|
|
210
|
+
for item in [
|
|
211
|
+
"area",
|
|
212
|
+
"momentX",
|
|
213
|
+
"momentY",
|
|
214
|
+
"momentXX",
|
|
215
|
+
"momentYY",
|
|
216
|
+
"momentXY",
|
|
217
|
+
"meanX",
|
|
218
|
+
"meanY",
|
|
219
|
+
"varianceX",
|
|
220
|
+
"varianceY",
|
|
221
|
+
"stddevX",
|
|
222
|
+
"stddevY",
|
|
223
|
+
"covariance",
|
|
224
|
+
"correlation",
|
|
225
|
+
"slant",
|
|
226
|
+
]:
|
|
227
|
+
print("%s: %g" % (item, getattr(pen, item)))
|
|
228
|
+
|
|
229
|
+
if not quiet:
|
|
230
|
+
print()
|
|
231
|
+
print("font:")
|
|
232
|
+
|
|
233
|
+
print("weight: %g" % (wght_sum * upem / wdth_sum))
|
|
234
|
+
print("weight (perceptual): %g" % (wght_sum_perceptual / wdth_sum))
|
|
235
|
+
print("width: %g" % (wdth_sum / upem / len(glyphs)))
|
|
236
|
+
slant = slnt_sum / len(glyphs)
|
|
237
|
+
print("slant: %g" % slant)
|
|
238
|
+
print("slant angle: %g" % -degrees(atan(slant)))
|
|
239
|
+
slant_perceptual = slnt_sum_perceptual / wdth_sum
|
|
240
|
+
print("slant (perceptual): %g" % slant_perceptual)
|
|
241
|
+
print("slant (perceptual) angle: %g" % -degrees(atan(slant_perceptual)))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def main(args):
|
|
245
|
+
"""Report font glyph shape geometricsl statistics"""
|
|
246
|
+
|
|
247
|
+
if args is None:
|
|
248
|
+
import sys
|
|
249
|
+
|
|
250
|
+
args = sys.argv[1:]
|
|
251
|
+
|
|
252
|
+
import argparse
|
|
253
|
+
|
|
254
|
+
parser = argparse.ArgumentParser(
|
|
255
|
+
"fonttools pens.statisticsPen",
|
|
256
|
+
description="Report font glyph shape geometricsl statistics",
|
|
257
|
+
)
|
|
258
|
+
parser.add_argument("font", metavar="font.ttf", help="Font file.")
|
|
259
|
+
parser.add_argument("glyphs", metavar="glyph-name", help="Glyph names.", nargs="*")
|
|
260
|
+
parser.add_argument(
|
|
261
|
+
"-y",
|
|
262
|
+
metavar="<number>",
|
|
263
|
+
help="Face index into a collection to open. Zero based.",
|
|
264
|
+
)
|
|
265
|
+
parser.add_argument(
|
|
266
|
+
"-c",
|
|
267
|
+
"--control",
|
|
268
|
+
action="store_true",
|
|
269
|
+
help="Use the control-box pen instead of the Green therem.",
|
|
270
|
+
)
|
|
271
|
+
parser.add_argument(
|
|
272
|
+
"-q", "--quiet", action="store_true", help="Only report font-wide statistics."
|
|
273
|
+
)
|
|
274
|
+
parser.add_argument(
|
|
275
|
+
"--variations",
|
|
276
|
+
metavar="AXIS=LOC",
|
|
277
|
+
default="",
|
|
278
|
+
help="List of space separated locations. A location consist in "
|
|
279
|
+
"the name of a variation axis, followed by '=' and a number. E.g.: "
|
|
280
|
+
"wght=700 wdth=80. The default is the location of the base master.",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
options = parser.parse_args(args)
|
|
284
|
+
|
|
285
|
+
glyphs = options.glyphs
|
|
286
|
+
fontNumber = int(options.y) if options.y is not None else 0
|
|
287
|
+
|
|
288
|
+
location = {}
|
|
289
|
+
for tag_v in options.variations.split():
|
|
290
|
+
fields = tag_v.split("=")
|
|
291
|
+
tag = fields[0].strip()
|
|
292
|
+
v = int(fields[1])
|
|
293
|
+
location[tag] = v
|
|
294
|
+
|
|
295
|
+
from fontTools.ttLib import TTFont
|
|
296
|
+
|
|
297
|
+
font = TTFont(options.font, fontNumber=fontNumber)
|
|
298
|
+
if not glyphs:
|
|
299
|
+
glyphs = font.getGlyphOrder()
|
|
300
|
+
_test(
|
|
301
|
+
font.getGlyphSet(location=location),
|
|
302
|
+
font["head"].unitsPerEm,
|
|
303
|
+
glyphs,
|
|
304
|
+
quiet=options.quiet,
|
|
305
|
+
control=options.control,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
if __name__ == "__main__":
|
|
310
|
+
import sys
|
|
311
|
+
|
|
312
|
+
main(sys.argv[1:])
|