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,340 @@
|
|
|
1
|
+
from collections import deque
|
|
2
|
+
from functools import partial
|
|
3
|
+
from fontTools.misc import sstruct
|
|
4
|
+
from fontTools.misc.textTools import safeEval
|
|
5
|
+
from fontTools.misc.lazyTools import LazyDict
|
|
6
|
+
from fontTools.ttLib import OPTIMIZE_FONT_SPEED
|
|
7
|
+
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
|
8
|
+
from . import DefaultTable
|
|
9
|
+
import array
|
|
10
|
+
import itertools
|
|
11
|
+
import logging
|
|
12
|
+
import struct
|
|
13
|
+
import sys
|
|
14
|
+
import fontTools.ttLib.tables.TupleVariation as tv
|
|
15
|
+
|
|
16
|
+
log = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# https://www.microsoft.com/typography/otspec/gvar.htm
|
|
19
|
+
# https://www.microsoft.com/typography/otspec/otvarcommonformats.htm
|
|
20
|
+
#
|
|
21
|
+
# Apple's documentation of 'gvar':
|
|
22
|
+
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gvar.html
|
|
23
|
+
#
|
|
24
|
+
# FreeType2 source code for parsing 'gvar':
|
|
25
|
+
# http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/truetype/ttgxvar.c
|
|
26
|
+
|
|
27
|
+
GVAR_HEADER_FORMAT_HEAD = """
|
|
28
|
+
> # big endian
|
|
29
|
+
version: H
|
|
30
|
+
reserved: H
|
|
31
|
+
axisCount: H
|
|
32
|
+
sharedTupleCount: H
|
|
33
|
+
offsetToSharedTuples: I
|
|
34
|
+
"""
|
|
35
|
+
# In between the HEAD and TAIL lies the glyphCount, which is
|
|
36
|
+
# of different size: 2 bytes for gvar, and 3 bytes for GVAR.
|
|
37
|
+
GVAR_HEADER_FORMAT_TAIL = """
|
|
38
|
+
> # big endian
|
|
39
|
+
flags: H
|
|
40
|
+
offsetToGlyphVariationData: I
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
GVAR_HEADER_SIZE_HEAD = sstruct.calcsize(GVAR_HEADER_FORMAT_HEAD)
|
|
44
|
+
GVAR_HEADER_SIZE_TAIL = sstruct.calcsize(GVAR_HEADER_FORMAT_TAIL)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
48
|
+
"""Glyph Variations table
|
|
49
|
+
|
|
50
|
+
The ``gvar`` table provides the per-glyph variation data that
|
|
51
|
+
describe how glyph outlines in the ``glyf`` table change across
|
|
52
|
+
the variation space that is defined for the font in the ``fvar``
|
|
53
|
+
table.
|
|
54
|
+
|
|
55
|
+
See also https://learn.microsoft.com/en-us/typography/opentype/spec/gvar
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
dependencies = ["fvar", "glyf"]
|
|
59
|
+
gid_size = 2
|
|
60
|
+
|
|
61
|
+
def __init__(self, tag=None):
|
|
62
|
+
DefaultTable.DefaultTable.__init__(self, tag)
|
|
63
|
+
self.version, self.reserved = 1, 0
|
|
64
|
+
self.variations = {}
|
|
65
|
+
|
|
66
|
+
def compile(self, ttFont):
|
|
67
|
+
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
68
|
+
sharedTuples = tv.compileSharedTuples(
|
|
69
|
+
axisTags, itertools.chain(*self.variations.values())
|
|
70
|
+
)
|
|
71
|
+
sharedTupleIndices = {coord: i for i, coord in enumerate(sharedTuples)}
|
|
72
|
+
sharedTupleSize = sum([len(c) for c in sharedTuples])
|
|
73
|
+
compiledGlyphs = self.compileGlyphs_(ttFont, axisTags, sharedTupleIndices)
|
|
74
|
+
offset = 0
|
|
75
|
+
offsets = []
|
|
76
|
+
for glyph in compiledGlyphs:
|
|
77
|
+
offsets.append(offset)
|
|
78
|
+
offset += len(glyph)
|
|
79
|
+
offsets.append(offset)
|
|
80
|
+
compiledOffsets, tableFormat = self.compileOffsets_(offsets)
|
|
81
|
+
|
|
82
|
+
GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
|
|
83
|
+
header = {}
|
|
84
|
+
header["version"] = self.version
|
|
85
|
+
header["reserved"] = self.reserved
|
|
86
|
+
header["axisCount"] = len(axisTags)
|
|
87
|
+
header["sharedTupleCount"] = len(sharedTuples)
|
|
88
|
+
header["offsetToSharedTuples"] = GVAR_HEADER_SIZE + len(compiledOffsets)
|
|
89
|
+
header["flags"] = tableFormat
|
|
90
|
+
header["offsetToGlyphVariationData"] = (
|
|
91
|
+
header["offsetToSharedTuples"] + sharedTupleSize
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
result = [
|
|
95
|
+
sstruct.pack(GVAR_HEADER_FORMAT_HEAD, header),
|
|
96
|
+
len(compiledGlyphs).to_bytes(self.gid_size, "big"),
|
|
97
|
+
sstruct.pack(GVAR_HEADER_FORMAT_TAIL, header),
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
result.append(compiledOffsets)
|
|
101
|
+
result.extend(sharedTuples)
|
|
102
|
+
result.extend(compiledGlyphs)
|
|
103
|
+
return b"".join(result)
|
|
104
|
+
|
|
105
|
+
def compileGlyphs_(self, ttFont, axisTags, sharedCoordIndices):
|
|
106
|
+
optimizeSpeed = ttFont.cfg[OPTIMIZE_FONT_SPEED]
|
|
107
|
+
result = []
|
|
108
|
+
glyf = ttFont["glyf"]
|
|
109
|
+
for glyphName in ttFont.getGlyphOrder():
|
|
110
|
+
variations = self.variations.get(glyphName, [])
|
|
111
|
+
if not variations:
|
|
112
|
+
result.append(b"")
|
|
113
|
+
continue
|
|
114
|
+
pointCountUnused = 0 # pointCount is actually unused by compileGlyph
|
|
115
|
+
result.append(
|
|
116
|
+
compileGlyph_(
|
|
117
|
+
self.gid_size,
|
|
118
|
+
variations,
|
|
119
|
+
pointCountUnused,
|
|
120
|
+
axisTags,
|
|
121
|
+
sharedCoordIndices,
|
|
122
|
+
optimizeSize=not optimizeSpeed,
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
return result
|
|
126
|
+
|
|
127
|
+
def decompile(self, data, ttFont):
|
|
128
|
+
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
129
|
+
glyphs = ttFont.getGlyphOrder()
|
|
130
|
+
|
|
131
|
+
# Parse the header
|
|
132
|
+
GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
|
|
133
|
+
sstruct.unpack(GVAR_HEADER_FORMAT_HEAD, data[:GVAR_HEADER_SIZE_HEAD], self)
|
|
134
|
+
self.glyphCount = int.from_bytes(
|
|
135
|
+
data[GVAR_HEADER_SIZE_HEAD : GVAR_HEADER_SIZE_HEAD + self.gid_size], "big"
|
|
136
|
+
)
|
|
137
|
+
sstruct.unpack(
|
|
138
|
+
GVAR_HEADER_FORMAT_TAIL,
|
|
139
|
+
data[GVAR_HEADER_SIZE_HEAD + self.gid_size : GVAR_HEADER_SIZE],
|
|
140
|
+
self,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
assert len(glyphs) == self.glyphCount, (len(glyphs), self.glyphCount)
|
|
144
|
+
assert len(axisTags) == self.axisCount, (
|
|
145
|
+
len(axisTags),
|
|
146
|
+
self.axisCount,
|
|
147
|
+
axisTags,
|
|
148
|
+
)
|
|
149
|
+
sharedCoords = tv.decompileSharedTuples(
|
|
150
|
+
axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples
|
|
151
|
+
)
|
|
152
|
+
variations = {}
|
|
153
|
+
offsetToData = self.offsetToGlyphVariationData
|
|
154
|
+
glyf = ttFont["glyf"]
|
|
155
|
+
|
|
156
|
+
def get_read_item():
|
|
157
|
+
reverseGlyphMap = ttFont.getReverseGlyphMap()
|
|
158
|
+
tableFormat = self.flags & 1
|
|
159
|
+
|
|
160
|
+
def read_item(glyphName):
|
|
161
|
+
gid = reverseGlyphMap[glyphName]
|
|
162
|
+
offsetSize = 2 if tableFormat == 0 else 4
|
|
163
|
+
startOffset = GVAR_HEADER_SIZE + offsetSize * gid
|
|
164
|
+
endOffset = startOffset + offsetSize * 2
|
|
165
|
+
offsets = table__g_v_a_r.decompileOffsets_(
|
|
166
|
+
data[startOffset:endOffset],
|
|
167
|
+
tableFormat=tableFormat,
|
|
168
|
+
glyphCount=1,
|
|
169
|
+
)
|
|
170
|
+
gvarData = data[offsetToData + offsets[0] : offsetToData + offsets[1]]
|
|
171
|
+
if not gvarData:
|
|
172
|
+
return []
|
|
173
|
+
glyph = glyf[glyphName]
|
|
174
|
+
numPointsInGlyph = self.getNumPoints_(glyph)
|
|
175
|
+
return decompileGlyph_(
|
|
176
|
+
self.gid_size, numPointsInGlyph, sharedCoords, axisTags, gvarData
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return read_item
|
|
180
|
+
|
|
181
|
+
read_item = get_read_item()
|
|
182
|
+
l = LazyDict({glyphs[gid]: read_item for gid in range(self.glyphCount)})
|
|
183
|
+
|
|
184
|
+
self.variations = l
|
|
185
|
+
|
|
186
|
+
if ttFont.lazy is False: # Be lazy for None and True
|
|
187
|
+
self.ensureDecompiled()
|
|
188
|
+
|
|
189
|
+
def ensureDecompiled(self, recurse=False):
|
|
190
|
+
# The recurse argument is unused, but part of the signature of
|
|
191
|
+
# ensureDecompiled across the library.
|
|
192
|
+
# Use a zero-length deque to consume the lazy dict
|
|
193
|
+
deque(self.variations.values(), maxlen=0)
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def decompileOffsets_(data, tableFormat, glyphCount):
|
|
197
|
+
if tableFormat == 0:
|
|
198
|
+
# Short format: array of UInt16
|
|
199
|
+
offsets = array.array("H")
|
|
200
|
+
offsetsSize = (glyphCount + 1) * 2
|
|
201
|
+
else:
|
|
202
|
+
# Long format: array of UInt32
|
|
203
|
+
offsets = array.array("I")
|
|
204
|
+
offsetsSize = (glyphCount + 1) * 4
|
|
205
|
+
offsets.frombytes(data[0:offsetsSize])
|
|
206
|
+
if sys.byteorder != "big":
|
|
207
|
+
offsets.byteswap()
|
|
208
|
+
|
|
209
|
+
# In the short format, offsets need to be multiplied by 2.
|
|
210
|
+
# This is not documented in Apple's TrueType specification,
|
|
211
|
+
# but can be inferred from the FreeType implementation, and
|
|
212
|
+
# we could verify it with two sample GX fonts.
|
|
213
|
+
if tableFormat == 0:
|
|
214
|
+
offsets = [off * 2 for off in offsets]
|
|
215
|
+
|
|
216
|
+
return offsets
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def compileOffsets_(offsets):
|
|
220
|
+
"""Packs a list of offsets into a 'gvar' offset table.
|
|
221
|
+
|
|
222
|
+
Returns a pair (bytestring, tableFormat). Bytestring is the
|
|
223
|
+
packed offset table. Format indicates whether the table
|
|
224
|
+
uses short (tableFormat=0) or long (tableFormat=1) integers.
|
|
225
|
+
The returned tableFormat should get packed into the flags field
|
|
226
|
+
of the 'gvar' header.
|
|
227
|
+
"""
|
|
228
|
+
assert len(offsets) >= 2
|
|
229
|
+
for i in range(1, len(offsets)):
|
|
230
|
+
assert offsets[i - 1] <= offsets[i]
|
|
231
|
+
if max(offsets) <= 0xFFFF * 2:
|
|
232
|
+
packed = array.array("H", [n >> 1 for n in offsets])
|
|
233
|
+
tableFormat = 0
|
|
234
|
+
else:
|
|
235
|
+
packed = array.array("I", offsets)
|
|
236
|
+
tableFormat = 1
|
|
237
|
+
if sys.byteorder != "big":
|
|
238
|
+
packed.byteswap()
|
|
239
|
+
return (packed.tobytes(), tableFormat)
|
|
240
|
+
|
|
241
|
+
def toXML(self, writer, ttFont):
|
|
242
|
+
writer.simpletag("version", value=self.version)
|
|
243
|
+
writer.newline()
|
|
244
|
+
writer.simpletag("reserved", value=self.reserved)
|
|
245
|
+
writer.newline()
|
|
246
|
+
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
247
|
+
for glyphName in ttFont.getGlyphNames():
|
|
248
|
+
variations = self.variations.get(glyphName)
|
|
249
|
+
if not variations:
|
|
250
|
+
continue
|
|
251
|
+
writer.begintag("glyphVariations", glyph=glyphName)
|
|
252
|
+
writer.newline()
|
|
253
|
+
for gvar in variations:
|
|
254
|
+
gvar.toXML(writer, axisTags)
|
|
255
|
+
writer.endtag("glyphVariations")
|
|
256
|
+
writer.newline()
|
|
257
|
+
|
|
258
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
259
|
+
if name == "version":
|
|
260
|
+
self.version = safeEval(attrs["value"])
|
|
261
|
+
elif name == "reserved":
|
|
262
|
+
self.reserved = safeEval(attrs["value"])
|
|
263
|
+
elif name == "glyphVariations":
|
|
264
|
+
if not hasattr(self, "variations"):
|
|
265
|
+
self.variations = {}
|
|
266
|
+
glyphName = attrs["glyph"]
|
|
267
|
+
glyph = ttFont["glyf"][glyphName]
|
|
268
|
+
numPointsInGlyph = self.getNumPoints_(glyph)
|
|
269
|
+
glyphVariations = []
|
|
270
|
+
for element in content:
|
|
271
|
+
if isinstance(element, tuple):
|
|
272
|
+
name, attrs, content = element
|
|
273
|
+
if name == "tuple":
|
|
274
|
+
gvar = TupleVariation({}, [None] * numPointsInGlyph)
|
|
275
|
+
glyphVariations.append(gvar)
|
|
276
|
+
for tupleElement in content:
|
|
277
|
+
if isinstance(tupleElement, tuple):
|
|
278
|
+
tupleName, tupleAttrs, tupleContent = tupleElement
|
|
279
|
+
gvar.fromXML(tupleName, tupleAttrs, tupleContent)
|
|
280
|
+
self.variations[glyphName] = glyphVariations
|
|
281
|
+
|
|
282
|
+
@staticmethod
|
|
283
|
+
def getNumPoints_(glyph):
|
|
284
|
+
NUM_PHANTOM_POINTS = 4
|
|
285
|
+
|
|
286
|
+
if glyph.isComposite():
|
|
287
|
+
return len(glyph.components) + NUM_PHANTOM_POINTS
|
|
288
|
+
else:
|
|
289
|
+
# Empty glyphs (eg. space, nonmarkingreturn) have no "coordinates" attribute.
|
|
290
|
+
return len(getattr(glyph, "coordinates", [])) + NUM_PHANTOM_POINTS
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def compileGlyph_(
|
|
294
|
+
dataOffsetSize,
|
|
295
|
+
variations,
|
|
296
|
+
pointCount,
|
|
297
|
+
axisTags,
|
|
298
|
+
sharedCoordIndices,
|
|
299
|
+
*,
|
|
300
|
+
optimizeSize=True,
|
|
301
|
+
):
|
|
302
|
+
assert dataOffsetSize in (2, 3)
|
|
303
|
+
tupleVariationCount, tuples, data = tv.compileTupleVariationStore(
|
|
304
|
+
variations, pointCount, axisTags, sharedCoordIndices, optimizeSize=optimizeSize
|
|
305
|
+
)
|
|
306
|
+
if tupleVariationCount == 0:
|
|
307
|
+
return b""
|
|
308
|
+
|
|
309
|
+
offsetToData = 2 + dataOffsetSize + len(tuples)
|
|
310
|
+
|
|
311
|
+
result = [
|
|
312
|
+
tupleVariationCount.to_bytes(2, "big"),
|
|
313
|
+
offsetToData.to_bytes(dataOffsetSize, "big"),
|
|
314
|
+
tuples,
|
|
315
|
+
data,
|
|
316
|
+
]
|
|
317
|
+
if (offsetToData + len(data)) % 2 != 0:
|
|
318
|
+
result.append(b"\0") # padding
|
|
319
|
+
return b"".join(result)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def decompileGlyph_(dataOffsetSize, pointCount, sharedTuples, axisTags, data):
|
|
323
|
+
assert dataOffsetSize in (2, 3)
|
|
324
|
+
if len(data) < 2 + dataOffsetSize:
|
|
325
|
+
return []
|
|
326
|
+
|
|
327
|
+
tupleVariationCount = int.from_bytes(data[:2], "big")
|
|
328
|
+
offsetToData = int.from_bytes(data[2 : 2 + dataOffsetSize], "big")
|
|
329
|
+
|
|
330
|
+
dataPos = offsetToData
|
|
331
|
+
return tv.decompileTupleVariationStore(
|
|
332
|
+
"gvar",
|
|
333
|
+
axisTags,
|
|
334
|
+
tupleVariationCount,
|
|
335
|
+
pointCount,
|
|
336
|
+
sharedTuples,
|
|
337
|
+
data,
|
|
338
|
+
2 + dataOffsetSize,
|
|
339
|
+
offsetToData,
|
|
340
|
+
)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from fontTools.misc import sstruct
|
|
2
|
+
from fontTools.misc.textTools import bytechr, byteord, strjoin
|
|
3
|
+
from . import DefaultTable
|
|
4
|
+
import array
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
|
|
7
|
+
hdmxHeaderFormat = """
|
|
8
|
+
> # big endian!
|
|
9
|
+
version: H
|
|
10
|
+
numRecords: H
|
|
11
|
+
recordSize: l
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _GlyphnamedList(Mapping):
|
|
16
|
+
def __init__(self, reverseGlyphOrder, data):
|
|
17
|
+
self._array = data
|
|
18
|
+
self._map = dict(reverseGlyphOrder)
|
|
19
|
+
|
|
20
|
+
def __getitem__(self, k):
|
|
21
|
+
return self._array[self._map[k]]
|
|
22
|
+
|
|
23
|
+
def __len__(self):
|
|
24
|
+
return len(self._map)
|
|
25
|
+
|
|
26
|
+
def __iter__(self):
|
|
27
|
+
return iter(self._map)
|
|
28
|
+
|
|
29
|
+
def keys(self):
|
|
30
|
+
return self._map.keys()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class table__h_d_m_x(DefaultTable.DefaultTable):
|
|
34
|
+
"""Horizontal Device Metrics table
|
|
35
|
+
|
|
36
|
+
The ``hdmx`` table is an optional table that stores advance widths for
|
|
37
|
+
glyph outlines at specified pixel sizes.
|
|
38
|
+
|
|
39
|
+
See also https://learn.microsoft.com/en-us/typography/opentype/spec/hdmx
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def decompile(self, data, ttFont):
|
|
43
|
+
numGlyphs = ttFont["maxp"].numGlyphs
|
|
44
|
+
glyphOrder = ttFont.getGlyphOrder()
|
|
45
|
+
dummy, data = sstruct.unpack2(hdmxHeaderFormat, data, self)
|
|
46
|
+
self.hdmx = {}
|
|
47
|
+
for i in range(self.numRecords):
|
|
48
|
+
ppem = byteord(data[0])
|
|
49
|
+
maxSize = byteord(data[1])
|
|
50
|
+
widths = _GlyphnamedList(
|
|
51
|
+
ttFont.getReverseGlyphMap(), array.array("B", data[2 : 2 + numGlyphs])
|
|
52
|
+
)
|
|
53
|
+
self.hdmx[ppem] = widths
|
|
54
|
+
data = data[self.recordSize :]
|
|
55
|
+
assert len(data) == 0, "too much hdmx data"
|
|
56
|
+
|
|
57
|
+
def compile(self, ttFont):
|
|
58
|
+
self.version = 0
|
|
59
|
+
numGlyphs = ttFont["maxp"].numGlyphs
|
|
60
|
+
glyphOrder = ttFont.getGlyphOrder()
|
|
61
|
+
self.recordSize = 4 * ((2 + numGlyphs + 3) // 4)
|
|
62
|
+
pad = (self.recordSize - 2 - numGlyphs) * b"\0"
|
|
63
|
+
self.numRecords = len(self.hdmx)
|
|
64
|
+
data = sstruct.pack(hdmxHeaderFormat, self)
|
|
65
|
+
items = sorted(self.hdmx.items())
|
|
66
|
+
for ppem, widths in items:
|
|
67
|
+
data = data + bytechr(ppem) + bytechr(max(widths.values()))
|
|
68
|
+
for glyphName in glyphOrder:
|
|
69
|
+
width = widths[glyphName]
|
|
70
|
+
data = data + bytechr(width)
|
|
71
|
+
data = data + pad
|
|
72
|
+
return data
|
|
73
|
+
|
|
74
|
+
def toXML(self, writer, ttFont):
|
|
75
|
+
writer.begintag("hdmxData")
|
|
76
|
+
writer.newline()
|
|
77
|
+
ppems = sorted(self.hdmx.keys())
|
|
78
|
+
records = []
|
|
79
|
+
format = ""
|
|
80
|
+
for ppem in ppems:
|
|
81
|
+
widths = self.hdmx[ppem]
|
|
82
|
+
records.append(widths)
|
|
83
|
+
format = format + "%4d"
|
|
84
|
+
glyphNames = ttFont.getGlyphOrder()[:]
|
|
85
|
+
glyphNames.sort()
|
|
86
|
+
maxNameLen = max(map(len, glyphNames))
|
|
87
|
+
format = "%" + repr(maxNameLen) + "s:" + format + " ;"
|
|
88
|
+
writer.write(format % (("ppem",) + tuple(ppems)))
|
|
89
|
+
writer.newline()
|
|
90
|
+
writer.newline()
|
|
91
|
+
for glyphName in glyphNames:
|
|
92
|
+
row = []
|
|
93
|
+
for ppem in ppems:
|
|
94
|
+
widths = self.hdmx[ppem]
|
|
95
|
+
row.append(widths[glyphName])
|
|
96
|
+
if ";" in glyphName:
|
|
97
|
+
glyphName = "\\x3b".join(glyphName.split(";"))
|
|
98
|
+
writer.write(format % ((glyphName,) + tuple(row)))
|
|
99
|
+
writer.newline()
|
|
100
|
+
writer.endtag("hdmxData")
|
|
101
|
+
writer.newline()
|
|
102
|
+
|
|
103
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
104
|
+
if name != "hdmxData":
|
|
105
|
+
return
|
|
106
|
+
content = strjoin(content)
|
|
107
|
+
lines = content.split(";")
|
|
108
|
+
topRow = lines[0].split()
|
|
109
|
+
assert topRow[0] == "ppem:", "illegal hdmx format"
|
|
110
|
+
ppems = list(map(int, topRow[1:]))
|
|
111
|
+
self.hdmx = hdmx = {}
|
|
112
|
+
for ppem in ppems:
|
|
113
|
+
hdmx[ppem] = {}
|
|
114
|
+
lines = (line.split() for line in lines[1:])
|
|
115
|
+
for line in lines:
|
|
116
|
+
if not line:
|
|
117
|
+
continue
|
|
118
|
+
assert line[0][-1] == ":", "illegal hdmx format"
|
|
119
|
+
glyphName = line[0][:-1]
|
|
120
|
+
if "\\" in glyphName:
|
|
121
|
+
from fontTools.misc.textTools import safeEval
|
|
122
|
+
|
|
123
|
+
glyphName = safeEval('"""' + glyphName + '"""')
|
|
124
|
+
line = list(map(int, line[1:]))
|
|
125
|
+
assert len(line) == len(ppems), "illegal hdmx format"
|
|
126
|
+
for i, ppem in enumerate(ppems):
|
|
127
|
+
hdmx[ppem][glyphName] = line[i]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from fontTools.misc import sstruct
|
|
2
|
+
from fontTools.misc.fixedTools import floatToFixedToStr, strToFixedToFloat
|
|
3
|
+
from fontTools.misc.textTools import safeEval, num2binary, binary2num
|
|
4
|
+
from fontTools.misc.timeTools import (
|
|
5
|
+
timestampFromString,
|
|
6
|
+
timestampToString,
|
|
7
|
+
timestampNow,
|
|
8
|
+
)
|
|
9
|
+
from fontTools.misc.timeTools import epoch_diff as mac_epoch_diff # For backward compat
|
|
10
|
+
from fontTools.misc.arrayTools import intRect, unionRect
|
|
11
|
+
from . import DefaultTable
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
log = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
headFormat = """
|
|
18
|
+
> # big endian
|
|
19
|
+
tableVersion: 16.16F
|
|
20
|
+
fontRevision: 16.16F
|
|
21
|
+
checkSumAdjustment: I
|
|
22
|
+
magicNumber: I
|
|
23
|
+
flags: H
|
|
24
|
+
unitsPerEm: H
|
|
25
|
+
created: Q
|
|
26
|
+
modified: Q
|
|
27
|
+
xMin: h
|
|
28
|
+
yMin: h
|
|
29
|
+
xMax: h
|
|
30
|
+
yMax: h
|
|
31
|
+
macStyle: H
|
|
32
|
+
lowestRecPPEM: H
|
|
33
|
+
fontDirectionHint: h
|
|
34
|
+
indexToLocFormat: h
|
|
35
|
+
glyphDataFormat: h
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class table__h_e_a_d(DefaultTable.DefaultTable):
|
|
40
|
+
"""Font Header table
|
|
41
|
+
|
|
42
|
+
The ``head`` table contains a variety of font-wide information.
|
|
43
|
+
|
|
44
|
+
See also https://learn.microsoft.com/en-us/typography/opentype/spec/head
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
dependencies = ["maxp", "loca", "CFF ", "CFF2"]
|
|
48
|
+
|
|
49
|
+
def decompile(self, data, ttFont):
|
|
50
|
+
dummy, rest = sstruct.unpack2(headFormat, data, self)
|
|
51
|
+
if rest:
|
|
52
|
+
# this is quite illegal, but there seem to be fonts out there that do this
|
|
53
|
+
log.warning("extra bytes at the end of 'head' table")
|
|
54
|
+
assert rest == b"\0\0"
|
|
55
|
+
|
|
56
|
+
# For timestamp fields, ignore the top four bytes. Some fonts have
|
|
57
|
+
# bogus values there. Since till 2038 those bytes only can be zero,
|
|
58
|
+
# ignore them.
|
|
59
|
+
#
|
|
60
|
+
# https://github.com/fonttools/fonttools/issues/99#issuecomment-66776810
|
|
61
|
+
for stamp in "created", "modified":
|
|
62
|
+
value = getattr(self, stamp)
|
|
63
|
+
if value > 0xFFFFFFFF:
|
|
64
|
+
log.warning("'%s' timestamp out of range; ignoring top bytes", stamp)
|
|
65
|
+
value &= 0xFFFFFFFF
|
|
66
|
+
setattr(self, stamp, value)
|
|
67
|
+
if value < 0x7C259DC0: # January 1, 1970 00:00:00
|
|
68
|
+
log.warning(
|
|
69
|
+
"'%s' timestamp seems very low; regarding as unix timestamp", stamp
|
|
70
|
+
)
|
|
71
|
+
value += 0x7C259DC0
|
|
72
|
+
setattr(self, stamp, value)
|
|
73
|
+
|
|
74
|
+
def compile(self, ttFont):
|
|
75
|
+
if ttFont.recalcBBoxes:
|
|
76
|
+
# For TT-flavored fonts, xMin, yMin, xMax and yMax are set in table__m_a_x_p.recalc().
|
|
77
|
+
if "CFF " in ttFont:
|
|
78
|
+
topDict = ttFont["CFF "].cff.topDictIndex[0]
|
|
79
|
+
self.xMin, self.yMin, self.xMax, self.yMax = intRect(topDict.FontBBox)
|
|
80
|
+
elif "CFF2" in ttFont:
|
|
81
|
+
topDict = ttFont["CFF2"].cff.topDictIndex[0]
|
|
82
|
+
charStrings = topDict.CharStrings
|
|
83
|
+
fontBBox = None
|
|
84
|
+
for charString in charStrings.values():
|
|
85
|
+
bounds = charString.calcBounds(charStrings)
|
|
86
|
+
if bounds is not None:
|
|
87
|
+
if fontBBox is not None:
|
|
88
|
+
fontBBox = unionRect(fontBBox, bounds)
|
|
89
|
+
else:
|
|
90
|
+
fontBBox = bounds
|
|
91
|
+
if fontBBox is not None:
|
|
92
|
+
self.xMin, self.yMin, self.xMax, self.yMax = intRect(fontBBox)
|
|
93
|
+
if ttFont.recalcTimestamp:
|
|
94
|
+
self.modified = timestampNow()
|
|
95
|
+
data = sstruct.pack(headFormat, self)
|
|
96
|
+
return data
|
|
97
|
+
|
|
98
|
+
def toXML(self, writer, ttFont):
|
|
99
|
+
writer.comment("Most of this table will be recalculated by the compiler")
|
|
100
|
+
writer.newline()
|
|
101
|
+
_, names, fixes = sstruct.getformat(headFormat)
|
|
102
|
+
for name in names:
|
|
103
|
+
value = getattr(self, name)
|
|
104
|
+
if name in fixes:
|
|
105
|
+
value = floatToFixedToStr(value, precisionBits=fixes[name])
|
|
106
|
+
elif name in ("created", "modified"):
|
|
107
|
+
value = timestampToString(value)
|
|
108
|
+
elif name in ("magicNumber", "checkSumAdjustment"):
|
|
109
|
+
if value < 0:
|
|
110
|
+
value = value + 0x100000000
|
|
111
|
+
value = hex(value)
|
|
112
|
+
if value[-1:] == "L":
|
|
113
|
+
value = value[:-1]
|
|
114
|
+
elif name in ("macStyle", "flags"):
|
|
115
|
+
value = num2binary(value, 16)
|
|
116
|
+
writer.simpletag(name, value=value)
|
|
117
|
+
writer.newline()
|
|
118
|
+
|
|
119
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
120
|
+
value = attrs["value"]
|
|
121
|
+
fixes = sstruct.getformat(headFormat)[2]
|
|
122
|
+
if name in fixes:
|
|
123
|
+
value = strToFixedToFloat(value, precisionBits=fixes[name])
|
|
124
|
+
elif name in ("created", "modified"):
|
|
125
|
+
value = timestampFromString(value)
|
|
126
|
+
elif name in ("macStyle", "flags"):
|
|
127
|
+
value = binary2num(value)
|
|
128
|
+
else:
|
|
129
|
+
value = safeEval(value)
|
|
130
|
+
setattr(self, name, value)
|