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,143 @@
|
|
|
1
|
+
"""Helpers for manipulating 2D points and vectors in COLR table."""
|
|
2
|
+
|
|
3
|
+
from math import copysign, cos, hypot, isclose, pi
|
|
4
|
+
from fontTools.misc.roundTools import otRound
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _vector_between(origin, target):
|
|
8
|
+
return (target[0] - origin[0], target[1] - origin[1])
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _round_point(pt):
|
|
12
|
+
return (otRound(pt[0]), otRound(pt[1]))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _unit_vector(vec):
|
|
16
|
+
length = hypot(*vec)
|
|
17
|
+
if length == 0:
|
|
18
|
+
return None
|
|
19
|
+
return (vec[0] / length, vec[1] / length)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_CIRCLE_INSIDE_TOLERANCE = 1e-4
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# The unit vector's X and Y components are respectively
|
|
26
|
+
# U = (cos(α), sin(α))
|
|
27
|
+
# where α is the angle between the unit vector and the positive x axis.
|
|
28
|
+
_UNIT_VECTOR_THRESHOLD = cos(3 / 8 * pi) # == sin(1/8 * pi) == 0.38268343236508984
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _rounding_offset(direction):
|
|
32
|
+
# Return 2-tuple of -/+ 1.0 or 0.0 approximately based on the direction vector.
|
|
33
|
+
# We divide the unit circle in 8 equal slices oriented towards the cardinal
|
|
34
|
+
# (N, E, S, W) and intermediate (NE, SE, SW, NW) directions. To each slice we
|
|
35
|
+
# map one of the possible cases: -1, 0, +1 for either X and Y coordinate.
|
|
36
|
+
# E.g. Return (+1.0, -1.0) if unit vector is oriented towards SE, or
|
|
37
|
+
# (-1.0, 0.0) if it's pointing West, etc.
|
|
38
|
+
uv = _unit_vector(direction)
|
|
39
|
+
if not uv:
|
|
40
|
+
return (0, 0)
|
|
41
|
+
|
|
42
|
+
result = []
|
|
43
|
+
for uv_component in uv:
|
|
44
|
+
if -_UNIT_VECTOR_THRESHOLD <= uv_component < _UNIT_VECTOR_THRESHOLD:
|
|
45
|
+
# unit vector component near 0: direction almost orthogonal to the
|
|
46
|
+
# direction of the current axis, thus keep coordinate unchanged
|
|
47
|
+
result.append(0)
|
|
48
|
+
else:
|
|
49
|
+
# nudge coord by +/- 1.0 in direction of unit vector
|
|
50
|
+
result.append(copysign(1.0, uv_component))
|
|
51
|
+
return tuple(result)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Circle:
|
|
55
|
+
def __init__(self, centre, radius):
|
|
56
|
+
self.centre = centre
|
|
57
|
+
self.radius = radius
|
|
58
|
+
|
|
59
|
+
def __repr__(self):
|
|
60
|
+
return f"Circle(centre={self.centre}, radius={self.radius})"
|
|
61
|
+
|
|
62
|
+
def round(self):
|
|
63
|
+
return Circle(_round_point(self.centre), otRound(self.radius))
|
|
64
|
+
|
|
65
|
+
def inside(self, outer_circle, tolerance=_CIRCLE_INSIDE_TOLERANCE):
|
|
66
|
+
dist = self.radius + hypot(*_vector_between(self.centre, outer_circle.centre))
|
|
67
|
+
return (
|
|
68
|
+
isclose(outer_circle.radius, dist, rel_tol=_CIRCLE_INSIDE_TOLERANCE)
|
|
69
|
+
or outer_circle.radius > dist
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def concentric(self, other):
|
|
73
|
+
return self.centre == other.centre
|
|
74
|
+
|
|
75
|
+
def move(self, dx, dy):
|
|
76
|
+
self.centre = (self.centre[0] + dx, self.centre[1] + dy)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def round_start_circle_stable_containment(c0, r0, c1, r1):
|
|
80
|
+
"""Round start circle so that it stays inside/outside end circle after rounding.
|
|
81
|
+
|
|
82
|
+
The rounding of circle coordinates to integers may cause an abrupt change
|
|
83
|
+
if the start circle c0 is so close to the end circle c1's perimiter that
|
|
84
|
+
it ends up falling outside (or inside) as a result of the rounding.
|
|
85
|
+
To keep the gradient unchanged, we nudge it in the right direction.
|
|
86
|
+
|
|
87
|
+
See:
|
|
88
|
+
https://github.com/googlefonts/colr-gradients-spec/issues/204
|
|
89
|
+
https://github.com/googlefonts/picosvg/issues/158
|
|
90
|
+
"""
|
|
91
|
+
start, end = Circle(c0, r0), Circle(c1, r1)
|
|
92
|
+
|
|
93
|
+
inside_before_round = start.inside(end)
|
|
94
|
+
|
|
95
|
+
round_start = start.round()
|
|
96
|
+
round_end = end.round()
|
|
97
|
+
inside_after_round = round_start.inside(round_end)
|
|
98
|
+
|
|
99
|
+
if inside_before_round == inside_after_round:
|
|
100
|
+
return round_start
|
|
101
|
+
elif inside_after_round:
|
|
102
|
+
# start was outside before rounding: we need to push start away from end
|
|
103
|
+
direction = _vector_between(round_end.centre, round_start.centre)
|
|
104
|
+
radius_delta = +1.0
|
|
105
|
+
else:
|
|
106
|
+
# start was inside before rounding: we need to push start towards end
|
|
107
|
+
direction = _vector_between(round_start.centre, round_end.centre)
|
|
108
|
+
radius_delta = -1.0
|
|
109
|
+
dx, dy = _rounding_offset(direction)
|
|
110
|
+
|
|
111
|
+
# At most 2 iterations ought to be enough to converge. Before the loop, we
|
|
112
|
+
# know the start circle didn't keep containment after normal rounding; thus
|
|
113
|
+
# we continue adjusting by -/+ 1.0 until containment is restored.
|
|
114
|
+
# Normal rounding can at most move each coordinates -/+0.5; in the worst case
|
|
115
|
+
# both the start and end circle's centres and radii will be rounded in opposite
|
|
116
|
+
# directions, e.g. when they move along a 45 degree diagonal:
|
|
117
|
+
# c0 = (1.5, 1.5) ===> (2.0, 2.0)
|
|
118
|
+
# r0 = 0.5 ===> 1.0
|
|
119
|
+
# c1 = (0.499, 0.499) ===> (0.0, 0.0)
|
|
120
|
+
# r1 = 2.499 ===> 2.0
|
|
121
|
+
# In this example, the relative distance between the circles, calculated
|
|
122
|
+
# as r1 - (r0 + distance(c0, c1)) is initially 0.57437 (c0 is inside c1), and
|
|
123
|
+
# -1.82842 after rounding (c0 is now outside c1). Nudging c0 by -1.0 on both
|
|
124
|
+
# x and y axes moves it towards c1 by hypot(-1.0, -1.0) = 1.41421. Two of these
|
|
125
|
+
# moves cover twice that distance, which is enough to restore containment.
|
|
126
|
+
max_attempts = 2
|
|
127
|
+
for _ in range(max_attempts):
|
|
128
|
+
if round_start.concentric(round_end):
|
|
129
|
+
# can't move c0 towards c1 (they are the same), so we change the radius
|
|
130
|
+
round_start.radius += radius_delta
|
|
131
|
+
assert round_start.radius >= 0
|
|
132
|
+
else:
|
|
133
|
+
round_start.move(dx, dy)
|
|
134
|
+
if inside_before_round == round_start.inside(round_end):
|
|
135
|
+
break
|
|
136
|
+
else: # likely a bug
|
|
137
|
+
raise AssertionError(
|
|
138
|
+
f"Rounding circle {start} "
|
|
139
|
+
f"{'inside' if inside_before_round else 'outside'} "
|
|
140
|
+
f"{end} failed after {max_attempts} attempts!"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return round_start
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""
|
|
2
|
+
colorLib.table_builder: Generic helper for filling in BaseTable derivatives from tuples and maps and such.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import collections
|
|
7
|
+
import enum
|
|
8
|
+
from fontTools.ttLib.tables.otBase import (
|
|
9
|
+
BaseTable,
|
|
10
|
+
FormatSwitchingBaseTable,
|
|
11
|
+
UInt8FormatSwitchingBaseTable,
|
|
12
|
+
)
|
|
13
|
+
from fontTools.ttLib.tables.otConverters import (
|
|
14
|
+
ComputedInt,
|
|
15
|
+
SimpleValue,
|
|
16
|
+
Struct,
|
|
17
|
+
Short,
|
|
18
|
+
UInt8,
|
|
19
|
+
UShort,
|
|
20
|
+
IntValue,
|
|
21
|
+
FloatValue,
|
|
22
|
+
OptionalValue,
|
|
23
|
+
)
|
|
24
|
+
from fontTools.misc.roundTools import otRound
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BuildCallback(enum.Enum):
|
|
28
|
+
"""Keyed on (BEFORE_BUILD, class[, Format if available]).
|
|
29
|
+
Receives (dest, source).
|
|
30
|
+
Should return (dest, source), which can be new objects.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
BEFORE_BUILD = enum.auto()
|
|
34
|
+
|
|
35
|
+
"""Keyed on (AFTER_BUILD, class[, Format if available]).
|
|
36
|
+
Receives (dest).
|
|
37
|
+
Should return dest, which can be a new object.
|
|
38
|
+
"""
|
|
39
|
+
AFTER_BUILD = enum.auto()
|
|
40
|
+
|
|
41
|
+
"""Keyed on (CREATE_DEFAULT, class[, Format if available]).
|
|
42
|
+
Receives no arguments.
|
|
43
|
+
Should return a new instance of class.
|
|
44
|
+
"""
|
|
45
|
+
CREATE_DEFAULT = enum.auto()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _assignable(convertersByName):
|
|
49
|
+
return {k: v for k, v in convertersByName.items() if not isinstance(v, ComputedInt)}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _isNonStrSequence(value):
|
|
53
|
+
return isinstance(value, collections.abc.Sequence) and not isinstance(value, str)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _split_format(cls, source):
|
|
57
|
+
if _isNonStrSequence(source):
|
|
58
|
+
assert len(source) > 0, f"{cls} needs at least format from {source}"
|
|
59
|
+
fmt, remainder = source[0], source[1:]
|
|
60
|
+
elif isinstance(source, collections.abc.Mapping):
|
|
61
|
+
assert "Format" in source, f"{cls} needs at least Format from {source}"
|
|
62
|
+
remainder = source.copy()
|
|
63
|
+
fmt = remainder.pop("Format")
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError(f"Not sure how to populate {cls} from {source}")
|
|
66
|
+
|
|
67
|
+
assert isinstance(
|
|
68
|
+
fmt, collections.abc.Hashable
|
|
69
|
+
), f"{cls} Format is not hashable: {fmt!r}"
|
|
70
|
+
assert fmt in cls.convertersByName, f"{cls} invalid Format: {fmt!r}"
|
|
71
|
+
|
|
72
|
+
return fmt, remainder
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TableBuilder:
|
|
76
|
+
"""
|
|
77
|
+
Helps to populate things derived from BaseTable from maps, tuples, etc.
|
|
78
|
+
|
|
79
|
+
A table of lifecycle callbacks may be provided to add logic beyond what is possible
|
|
80
|
+
based on otData info for the target class. See BuildCallbacks.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, callbackTable=None):
|
|
84
|
+
if callbackTable is None:
|
|
85
|
+
callbackTable = {}
|
|
86
|
+
self._callbackTable = callbackTable
|
|
87
|
+
|
|
88
|
+
def _convert(self, dest, field, converter, value):
|
|
89
|
+
enumClass = getattr(converter, "enumClass", None)
|
|
90
|
+
|
|
91
|
+
if enumClass:
|
|
92
|
+
if isinstance(value, enumClass):
|
|
93
|
+
pass
|
|
94
|
+
elif isinstance(value, str):
|
|
95
|
+
try:
|
|
96
|
+
value = getattr(enumClass, value.upper())
|
|
97
|
+
except AttributeError:
|
|
98
|
+
raise ValueError(f"{value} is not a valid {enumClass}")
|
|
99
|
+
else:
|
|
100
|
+
value = enumClass(value)
|
|
101
|
+
|
|
102
|
+
elif isinstance(converter, IntValue):
|
|
103
|
+
value = otRound(value)
|
|
104
|
+
elif isinstance(converter, FloatValue):
|
|
105
|
+
value = float(value)
|
|
106
|
+
|
|
107
|
+
elif isinstance(converter, Struct):
|
|
108
|
+
if converter.repeat:
|
|
109
|
+
if _isNonStrSequence(value):
|
|
110
|
+
value = [self.build(converter.tableClass, v) for v in value]
|
|
111
|
+
else:
|
|
112
|
+
value = [self.build(converter.tableClass, value)]
|
|
113
|
+
setattr(dest, converter.repeat, len(value))
|
|
114
|
+
else:
|
|
115
|
+
value = self.build(converter.tableClass, value)
|
|
116
|
+
elif callable(converter):
|
|
117
|
+
value = converter(value)
|
|
118
|
+
|
|
119
|
+
setattr(dest, field, value)
|
|
120
|
+
|
|
121
|
+
def build(self, cls, source):
|
|
122
|
+
assert issubclass(cls, BaseTable)
|
|
123
|
+
|
|
124
|
+
if isinstance(source, cls):
|
|
125
|
+
return source
|
|
126
|
+
|
|
127
|
+
callbackKey = (cls,)
|
|
128
|
+
fmt = None
|
|
129
|
+
if issubclass(cls, FormatSwitchingBaseTable):
|
|
130
|
+
fmt, source = _split_format(cls, source)
|
|
131
|
+
callbackKey = (cls, fmt)
|
|
132
|
+
|
|
133
|
+
dest = self._callbackTable.get(
|
|
134
|
+
(BuildCallback.CREATE_DEFAULT,) + callbackKey, lambda: cls()
|
|
135
|
+
)()
|
|
136
|
+
assert isinstance(dest, cls)
|
|
137
|
+
|
|
138
|
+
convByName = _assignable(cls.convertersByName)
|
|
139
|
+
skippedFields = set()
|
|
140
|
+
|
|
141
|
+
# For format switchers we need to resolve converters based on format
|
|
142
|
+
if issubclass(cls, FormatSwitchingBaseTable):
|
|
143
|
+
dest.Format = fmt
|
|
144
|
+
convByName = _assignable(convByName[dest.Format])
|
|
145
|
+
skippedFields.add("Format")
|
|
146
|
+
|
|
147
|
+
# Convert sequence => mapping so before thunk only has to handle one format
|
|
148
|
+
if _isNonStrSequence(source):
|
|
149
|
+
# Sequence (typically list or tuple) assumed to match fields in declaration order
|
|
150
|
+
assert len(source) <= len(
|
|
151
|
+
convByName
|
|
152
|
+
), f"Sequence of {len(source)} too long for {cls}; expected <= {len(convByName)} values"
|
|
153
|
+
source = dict(zip(convByName.keys(), source))
|
|
154
|
+
|
|
155
|
+
dest, source = self._callbackTable.get(
|
|
156
|
+
(BuildCallback.BEFORE_BUILD,) + callbackKey, lambda d, s: (d, s)
|
|
157
|
+
)(dest, source)
|
|
158
|
+
|
|
159
|
+
if isinstance(source, collections.abc.Mapping):
|
|
160
|
+
for field, value in source.items():
|
|
161
|
+
if field in skippedFields:
|
|
162
|
+
continue
|
|
163
|
+
converter = convByName.get(field, None)
|
|
164
|
+
if not converter:
|
|
165
|
+
raise ValueError(
|
|
166
|
+
f"Unrecognized field {field} for {cls}; expected one of {sorted(convByName.keys())}"
|
|
167
|
+
)
|
|
168
|
+
self._convert(dest, field, converter, value)
|
|
169
|
+
else:
|
|
170
|
+
# let's try as a 1-tuple
|
|
171
|
+
dest = self.build(cls, (source,))
|
|
172
|
+
|
|
173
|
+
for field, conv in convByName.items():
|
|
174
|
+
if not hasattr(dest, field) and isinstance(conv, OptionalValue):
|
|
175
|
+
setattr(dest, field, conv.DEFAULT)
|
|
176
|
+
|
|
177
|
+
dest = self._callbackTable.get(
|
|
178
|
+
(BuildCallback.AFTER_BUILD,) + callbackKey, lambda d: d
|
|
179
|
+
)(dest)
|
|
180
|
+
|
|
181
|
+
return dest
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class TableUnbuilder:
|
|
185
|
+
def __init__(self, callbackTable=None):
|
|
186
|
+
if callbackTable is None:
|
|
187
|
+
callbackTable = {}
|
|
188
|
+
self._callbackTable = callbackTable
|
|
189
|
+
|
|
190
|
+
def unbuild(self, table):
|
|
191
|
+
assert isinstance(table, BaseTable)
|
|
192
|
+
|
|
193
|
+
source = {}
|
|
194
|
+
|
|
195
|
+
callbackKey = (type(table),)
|
|
196
|
+
if isinstance(table, FormatSwitchingBaseTable):
|
|
197
|
+
source["Format"] = int(table.Format)
|
|
198
|
+
callbackKey += (table.Format,)
|
|
199
|
+
|
|
200
|
+
for converter in table.getConverters():
|
|
201
|
+
if isinstance(converter, ComputedInt):
|
|
202
|
+
continue
|
|
203
|
+
value = getattr(table, converter.name)
|
|
204
|
+
|
|
205
|
+
enumClass = getattr(converter, "enumClass", None)
|
|
206
|
+
if enumClass:
|
|
207
|
+
source[converter.name] = value.name.lower()
|
|
208
|
+
elif isinstance(converter, Struct):
|
|
209
|
+
if converter.repeat:
|
|
210
|
+
source[converter.name] = [self.unbuild(v) for v in value]
|
|
211
|
+
else:
|
|
212
|
+
source[converter.name] = self.unbuild(value)
|
|
213
|
+
elif isinstance(converter, SimpleValue):
|
|
214
|
+
# "simple" values (e.g. int, float, str) need no further un-building
|
|
215
|
+
source[converter.name] = value
|
|
216
|
+
else:
|
|
217
|
+
raise NotImplementedError(
|
|
218
|
+
"Don't know how unbuild {value!r} with {converter!r}"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
source = self._callbackTable.get(callbackKey, lambda s: s)(source)
|
|
222
|
+
|
|
223
|
+
return source
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from fontTools.ttLib.tables import otTables as ot
|
|
2
|
+
from .table_builder import TableUnbuilder
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def unbuildColrV1(layerList, baseGlyphList):
|
|
6
|
+
layers = []
|
|
7
|
+
if layerList:
|
|
8
|
+
layers = layerList.Paint
|
|
9
|
+
unbuilder = LayerListUnbuilder(layers)
|
|
10
|
+
return {
|
|
11
|
+
rec.BaseGlyph: unbuilder.unbuildPaint(rec.Paint)
|
|
12
|
+
for rec in baseGlyphList.BaseGlyphPaintRecord
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _flatten_layers(lst):
|
|
17
|
+
for paint in lst:
|
|
18
|
+
if paint["Format"] == ot.PaintFormat.PaintColrLayers:
|
|
19
|
+
yield from _flatten_layers(paint["Layers"])
|
|
20
|
+
else:
|
|
21
|
+
yield paint
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LayerListUnbuilder:
|
|
25
|
+
def __init__(self, layers):
|
|
26
|
+
self.layers = layers
|
|
27
|
+
|
|
28
|
+
callbacks = {
|
|
29
|
+
(
|
|
30
|
+
ot.Paint,
|
|
31
|
+
ot.PaintFormat.PaintColrLayers,
|
|
32
|
+
): self._unbuildPaintColrLayers,
|
|
33
|
+
}
|
|
34
|
+
self.tableUnbuilder = TableUnbuilder(callbacks)
|
|
35
|
+
|
|
36
|
+
def unbuildPaint(self, paint):
|
|
37
|
+
assert isinstance(paint, ot.Paint)
|
|
38
|
+
return self.tableUnbuilder.unbuild(paint)
|
|
39
|
+
|
|
40
|
+
def _unbuildPaintColrLayers(self, source):
|
|
41
|
+
assert source["Format"] == ot.PaintFormat.PaintColrLayers
|
|
42
|
+
|
|
43
|
+
layers = list(
|
|
44
|
+
_flatten_layers(
|
|
45
|
+
[
|
|
46
|
+
self.unbuildPaint(childPaint)
|
|
47
|
+
for childPaint in self.layers[
|
|
48
|
+
source["FirstLayerIndex"] : source["FirstLayerIndex"]
|
|
49
|
+
+ source["NumLayers"]
|
|
50
|
+
]
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if len(layers) == 1:
|
|
56
|
+
return layers[0]
|
|
57
|
+
|
|
58
|
+
return {"Format": source["Format"], "Layers": layers}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
from pprint import pprint
|
|
63
|
+
import sys
|
|
64
|
+
from fontTools.ttLib import TTFont
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
fontfile = sys.argv[1]
|
|
68
|
+
except IndexError:
|
|
69
|
+
sys.exit("usage: fonttools colorLib.unbuilder FONTFILE")
|
|
70
|
+
|
|
71
|
+
font = TTFont(fontfile)
|
|
72
|
+
colr = font["COLR"]
|
|
73
|
+
if colr.version < 1:
|
|
74
|
+
sys.exit(f"error: No COLR table version=1 found in {fontfile}")
|
|
75
|
+
|
|
76
|
+
colorGlyphs = unbuildColrV1(
|
|
77
|
+
colr.table.LayerList,
|
|
78
|
+
colr.table.BaseGlyphList,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
pprint(colorGlyphs)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Define all configuration options that can affect the working of fontTools
|
|
3
|
+
modules. E.g. optimization levels of varLib IUP, otlLib GPOS compression level,
|
|
4
|
+
etc. If this file gets too big, split it into smaller files per-module.
|
|
5
|
+
|
|
6
|
+
An instance of the Config class can be attached to a TTFont object, so that
|
|
7
|
+
the various modules can access their configuration options from it.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from textwrap import dedent
|
|
11
|
+
|
|
12
|
+
from fontTools.misc.configTools import *
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Config(AbstractConfig):
|
|
16
|
+
options = Options()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
OPTIONS = Config.options
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Config.register_option(
|
|
23
|
+
name="fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL",
|
|
24
|
+
help=dedent(
|
|
25
|
+
"""\
|
|
26
|
+
GPOS Lookup type 2 (PairPos) compression level:
|
|
27
|
+
0 = do not attempt to compact PairPos lookups;
|
|
28
|
+
1 to 8 = create at most 1 to 8 new subtables for each existing
|
|
29
|
+
subtable, provided that it would yield a 50%% file size saving;
|
|
30
|
+
9 = create as many new subtables as needed to yield a file size saving.
|
|
31
|
+
Default: 0.
|
|
32
|
+
|
|
33
|
+
This compaction aims to save file size, by splitting large class
|
|
34
|
+
kerning subtables (Format 2) that contain many zero values into
|
|
35
|
+
smaller and denser subtables. It's a trade-off between the overhead
|
|
36
|
+
of several subtables versus the sparseness of one big subtable.
|
|
37
|
+
|
|
38
|
+
See the pull request: https://github.com/fonttools/fonttools/pull/2326
|
|
39
|
+
"""
|
|
40
|
+
),
|
|
41
|
+
default=0,
|
|
42
|
+
parse=int,
|
|
43
|
+
validate=lambda v: v in range(10),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
Config.register_option(
|
|
47
|
+
name="fontTools.ttLib.tables.otBase:USE_HARFBUZZ_REPACKER",
|
|
48
|
+
help=dedent(
|
|
49
|
+
"""\
|
|
50
|
+
FontTools tries to use the HarfBuzz Repacker to serialize GPOS/GSUB tables
|
|
51
|
+
if the uharfbuzz python bindings are importable, otherwise falls back to its
|
|
52
|
+
slower, less efficient serializer. Set to False to always use the latter.
|
|
53
|
+
Set to True to explicitly request the HarfBuzz Repacker (will raise an
|
|
54
|
+
error if uharfbuzz cannot be imported).
|
|
55
|
+
"""
|
|
56
|
+
),
|
|
57
|
+
default=None,
|
|
58
|
+
parse=Option.parse_optional_bool,
|
|
59
|
+
validate=Option.validate_optional_bool,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
Config.register_option(
|
|
63
|
+
name="fontTools.otlLib.builder:WRITE_GPOS7",
|
|
64
|
+
help=dedent(
|
|
65
|
+
"""\
|
|
66
|
+
macOS before 13.2 didn’t support GPOS LookupType 7 (non-chaining
|
|
67
|
+
ContextPos lookups), so FontTools.otlLib.builder disables a file size
|
|
68
|
+
optimisation that would use LookupType 7 instead of 8 when there is no
|
|
69
|
+
chaining (no prefix or suffix). Set to True to enable the optimization.
|
|
70
|
+
"""
|
|
71
|
+
),
|
|
72
|
+
default=False,
|
|
73
|
+
parse=Option.parse_optional_bool,
|
|
74
|
+
validate=Option.validate_optional_bool,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
Config.register_option(
|
|
78
|
+
name="fontTools.ttLib:OPTIMIZE_FONT_SPEED",
|
|
79
|
+
help=dedent(
|
|
80
|
+
"""\
|
|
81
|
+
Enable optimizations that prioritize speed over file size. This
|
|
82
|
+
mainly affects how glyf table and gvar / VARC tables are compiled.
|
|
83
|
+
The produced fonts will be larger, but rendering performance will
|
|
84
|
+
be improved with HarfBuzz and other text layout engines.
|
|
85
|
+
"""
|
|
86
|
+
),
|
|
87
|
+
default=False,
|
|
88
|
+
parse=Option.parse_optional_bool,
|
|
89
|
+
validate=Option.validate_optional_bool,
|
|
90
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright 2016 Google Inc. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from .cu2qu import *
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Benchmark the cu2qu algorithm performance."""
|
|
2
|
+
|
|
3
|
+
from .cu2qu import *
|
|
4
|
+
import random
|
|
5
|
+
import timeit
|
|
6
|
+
|
|
7
|
+
MAX_ERR = 0.05
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_curve():
|
|
11
|
+
return [
|
|
12
|
+
tuple(float(random.randint(0, 2048)) for coord in range(2))
|
|
13
|
+
for point in range(4)
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def setup_curve_to_quadratic():
|
|
18
|
+
return generate_curve(), MAX_ERR
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def setup_curves_to_quadratic():
|
|
22
|
+
num_curves = 3
|
|
23
|
+
return ([generate_curve() for curve in range(num_curves)], [MAX_ERR] * num_curves)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run_benchmark(module, function, setup_suffix="", repeat=5, number=1000):
|
|
27
|
+
setup_func = "setup_" + function
|
|
28
|
+
if setup_suffix:
|
|
29
|
+
print("%s with %s:" % (function, setup_suffix), end="")
|
|
30
|
+
setup_func += "_" + setup_suffix
|
|
31
|
+
else:
|
|
32
|
+
print("%s:" % function, end="")
|
|
33
|
+
|
|
34
|
+
def wrapper(function, setup_func):
|
|
35
|
+
function = globals()[function]
|
|
36
|
+
setup_func = globals()[setup_func]
|
|
37
|
+
|
|
38
|
+
def wrapped():
|
|
39
|
+
return function(*setup_func())
|
|
40
|
+
|
|
41
|
+
return wrapped
|
|
42
|
+
|
|
43
|
+
results = timeit.repeat(wrapper(function, setup_func), repeat=repeat, number=number)
|
|
44
|
+
print("\t%5.1fus" % (min(results) * 1000000.0 / number))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main():
|
|
48
|
+
run_benchmark("cu2qu", "curve_to_quadratic")
|
|
49
|
+
run_benchmark("cu2qu", "curves_to_quadratic")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
random.seed(1)
|
|
54
|
+
main()
|