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,52 @@
|
|
|
1
|
+
from fontTools.misc import sstruct
|
|
2
|
+
from fontTools.misc.textTools import safeEval
|
|
3
|
+
from fontTools.misc.timeTools import timestampFromString, timestampToString
|
|
4
|
+
from . import DefaultTable
|
|
5
|
+
|
|
6
|
+
FFTMFormat = """
|
|
7
|
+
> # big endian
|
|
8
|
+
version: I
|
|
9
|
+
FFTimeStamp: Q
|
|
10
|
+
sourceCreated: Q
|
|
11
|
+
sourceModified: Q
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class table_F_F_T_M_(DefaultTable.DefaultTable):
|
|
16
|
+
"""FontForge Time Stamp table
|
|
17
|
+
|
|
18
|
+
The ``FFTM`` table is used by the free-software font editor
|
|
19
|
+
FontForge to record timestamps for the creation and modification
|
|
20
|
+
of font source (.sfd) files and a timestamp for FontForge's
|
|
21
|
+
own source code.
|
|
22
|
+
|
|
23
|
+
See also https://fontforge.org/docs/techref/non-standard.html
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def decompile(self, data, ttFont):
|
|
27
|
+
dummy, rest = sstruct.unpack2(FFTMFormat, data, self)
|
|
28
|
+
|
|
29
|
+
def compile(self, ttFont):
|
|
30
|
+
data = sstruct.pack(FFTMFormat, self)
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
def toXML(self, writer, ttFont):
|
|
34
|
+
writer.comment(
|
|
35
|
+
"FontForge's timestamp, font source creation and modification dates"
|
|
36
|
+
)
|
|
37
|
+
writer.newline()
|
|
38
|
+
formatstring, names, fixes = sstruct.getformat(FFTMFormat)
|
|
39
|
+
for name in names:
|
|
40
|
+
value = getattr(self, name)
|
|
41
|
+
if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
|
|
42
|
+
value = timestampToString(value)
|
|
43
|
+
writer.simpletag(name, value=value)
|
|
44
|
+
writer.newline()
|
|
45
|
+
|
|
46
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
47
|
+
value = attrs["value"]
|
|
48
|
+
if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
|
|
49
|
+
value = timestampFromString(value)
|
|
50
|
+
else:
|
|
51
|
+
value = safeEval(value)
|
|
52
|
+
setattr(self, name, value)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from fontTools.misc import sstruct
|
|
2
|
+
from fontTools.misc.fixedTools import floatToFixedToStr
|
|
3
|
+
from fontTools.misc.textTools import safeEval
|
|
4
|
+
from . import DefaultTable
|
|
5
|
+
from . import grUtils
|
|
6
|
+
import struct
|
|
7
|
+
|
|
8
|
+
Feat_hdr_format = """
|
|
9
|
+
>
|
|
10
|
+
version: 16.16F
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class table_F__e_a_t(DefaultTable.DefaultTable):
|
|
15
|
+
"""Feature table
|
|
16
|
+
|
|
17
|
+
The ``Feat`` table is used exclusively by the Graphite shaping engine
|
|
18
|
+
to store features and possible settings specified in GDL. Graphite features
|
|
19
|
+
determine what rules are applied to transform a glyph stream.
|
|
20
|
+
|
|
21
|
+
Not to be confused with ``feat``, or the OpenType Layout tables
|
|
22
|
+
``GSUB``/``GPOS``.
|
|
23
|
+
|
|
24
|
+
See also https://graphite.sil.org/graphite_techAbout#graphite-font-tables
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, tag=None):
|
|
28
|
+
DefaultTable.DefaultTable.__init__(self, tag)
|
|
29
|
+
self.features = {}
|
|
30
|
+
|
|
31
|
+
def decompile(self, data, ttFont):
|
|
32
|
+
(_, data) = sstruct.unpack2(Feat_hdr_format, data, self)
|
|
33
|
+
self.version = float(floatToFixedToStr(self.version, precisionBits=16))
|
|
34
|
+
(numFeats,) = struct.unpack(">H", data[:2])
|
|
35
|
+
data = data[8:]
|
|
36
|
+
allfeats = []
|
|
37
|
+
maxsetting = 0
|
|
38
|
+
for i in range(numFeats):
|
|
39
|
+
if self.version >= 2.0:
|
|
40
|
+
(fid, nums, _, offset, flags, lid) = struct.unpack(
|
|
41
|
+
">LHHLHH", data[16 * i : 16 * (i + 1)]
|
|
42
|
+
)
|
|
43
|
+
offset = int((offset - 12 - 16 * numFeats) / 4)
|
|
44
|
+
else:
|
|
45
|
+
(fid, nums, offset, flags, lid) = struct.unpack(
|
|
46
|
+
">HHLHH", data[12 * i : 12 * (i + 1)]
|
|
47
|
+
)
|
|
48
|
+
offset = int((offset - 12 - 12 * numFeats) / 4)
|
|
49
|
+
allfeats.append((fid, nums, offset, flags, lid))
|
|
50
|
+
maxsetting = max(maxsetting, offset + nums)
|
|
51
|
+
data = data[16 * numFeats :]
|
|
52
|
+
allsettings = []
|
|
53
|
+
for i in range(maxsetting):
|
|
54
|
+
if len(data) >= 4 * (i + 1):
|
|
55
|
+
(val, lid) = struct.unpack(">HH", data[4 * i : 4 * (i + 1)])
|
|
56
|
+
allsettings.append((val, lid))
|
|
57
|
+
for i, f in enumerate(allfeats):
|
|
58
|
+
(fid, nums, offset, flags, lid) = f
|
|
59
|
+
fobj = Feature()
|
|
60
|
+
fobj.flags = flags
|
|
61
|
+
fobj.label = lid
|
|
62
|
+
self.features[grUtils.num2tag(fid)] = fobj
|
|
63
|
+
fobj.settings = {}
|
|
64
|
+
fobj.default = None
|
|
65
|
+
fobj.index = i
|
|
66
|
+
for i in range(offset, offset + nums):
|
|
67
|
+
if i >= len(allsettings):
|
|
68
|
+
continue
|
|
69
|
+
(vid, vlid) = allsettings[i]
|
|
70
|
+
fobj.settings[vid] = vlid
|
|
71
|
+
if fobj.default is None:
|
|
72
|
+
fobj.default = vid
|
|
73
|
+
|
|
74
|
+
def compile(self, ttFont):
|
|
75
|
+
fdat = b""
|
|
76
|
+
vdat = b""
|
|
77
|
+
offset = 0
|
|
78
|
+
for f, v in sorted(self.features.items(), key=lambda x: x[1].index):
|
|
79
|
+
fnum = grUtils.tag2num(f)
|
|
80
|
+
if self.version >= 2.0:
|
|
81
|
+
fdat += struct.pack(
|
|
82
|
+
">LHHLHH",
|
|
83
|
+
grUtils.tag2num(f),
|
|
84
|
+
len(v.settings),
|
|
85
|
+
0,
|
|
86
|
+
offset * 4 + 12 + 16 * len(self.features),
|
|
87
|
+
v.flags,
|
|
88
|
+
v.label,
|
|
89
|
+
)
|
|
90
|
+
elif fnum > 65535: # self healing for alphabetic ids
|
|
91
|
+
self.version = 2.0
|
|
92
|
+
return self.compile(ttFont)
|
|
93
|
+
else:
|
|
94
|
+
fdat += struct.pack(
|
|
95
|
+
">HHLHH",
|
|
96
|
+
grUtils.tag2num(f),
|
|
97
|
+
len(v.settings),
|
|
98
|
+
offset * 4 + 12 + 12 * len(self.features),
|
|
99
|
+
v.flags,
|
|
100
|
+
v.label,
|
|
101
|
+
)
|
|
102
|
+
for s, l in sorted(
|
|
103
|
+
v.settings.items(), key=lambda x: (-1, x[1]) if x[0] == v.default else x
|
|
104
|
+
):
|
|
105
|
+
vdat += struct.pack(">HH", s, l)
|
|
106
|
+
offset += len(v.settings)
|
|
107
|
+
hdr = sstruct.pack(Feat_hdr_format, self)
|
|
108
|
+
return hdr + struct.pack(">HHL", len(self.features), 0, 0) + fdat + vdat
|
|
109
|
+
|
|
110
|
+
def toXML(self, writer, ttFont):
|
|
111
|
+
writer.simpletag("version", version=self.version)
|
|
112
|
+
writer.newline()
|
|
113
|
+
for f, v in sorted(self.features.items(), key=lambda x: x[1].index):
|
|
114
|
+
writer.begintag(
|
|
115
|
+
"feature",
|
|
116
|
+
fid=f,
|
|
117
|
+
label=v.label,
|
|
118
|
+
flags=v.flags,
|
|
119
|
+
default=(v.default if v.default else 0),
|
|
120
|
+
)
|
|
121
|
+
writer.newline()
|
|
122
|
+
for s, l in sorted(v.settings.items()):
|
|
123
|
+
writer.simpletag("setting", value=s, label=l)
|
|
124
|
+
writer.newline()
|
|
125
|
+
writer.endtag("feature")
|
|
126
|
+
writer.newline()
|
|
127
|
+
|
|
128
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
129
|
+
if name == "version":
|
|
130
|
+
self.version = float(safeEval(attrs["version"]))
|
|
131
|
+
elif name == "feature":
|
|
132
|
+
fid = attrs["fid"]
|
|
133
|
+
fobj = Feature()
|
|
134
|
+
fobj.flags = int(safeEval(attrs["flags"]))
|
|
135
|
+
fobj.label = int(safeEval(attrs["label"]))
|
|
136
|
+
fobj.default = int(safeEval(attrs.get("default", "0")))
|
|
137
|
+
fobj.index = len(self.features)
|
|
138
|
+
self.features[fid] = fobj
|
|
139
|
+
fobj.settings = {}
|
|
140
|
+
for element in content:
|
|
141
|
+
if not isinstance(element, tuple):
|
|
142
|
+
continue
|
|
143
|
+
tag, a, c = element
|
|
144
|
+
if tag == "setting":
|
|
145
|
+
fobj.settings[int(safeEval(a["value"]))] = int(safeEval(a["label"]))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class Feature(object):
|
|
149
|
+
pass
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .otBase import BaseTTXConverter
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class table_G_D_E_F_(BaseTTXConverter):
|
|
5
|
+
"""Glyph Definition table
|
|
6
|
+
|
|
7
|
+
The ``GDEF`` table stores various glyph properties that are used
|
|
8
|
+
by OpenType Layout.
|
|
9
|
+
|
|
10
|
+
See also https://learn.microsoft.com/en-us/typography/opentype/spec/gdef
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
pass
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from fontTools.misc import sstruct
|
|
2
|
+
from fontTools.misc.textTools import tobytes, tostr, safeEval
|
|
3
|
+
from . import DefaultTable
|
|
4
|
+
|
|
5
|
+
GMAPFormat = """
|
|
6
|
+
> # big endian
|
|
7
|
+
tableVersionMajor: H
|
|
8
|
+
tableVersionMinor: H
|
|
9
|
+
flags: H
|
|
10
|
+
recordsCount: H
|
|
11
|
+
recordsOffset: H
|
|
12
|
+
fontNameLength: H
|
|
13
|
+
"""
|
|
14
|
+
# psFontName is a byte string which follows the record above. This is zero padded
|
|
15
|
+
# to the beginning of the records array. The recordsOffsst is 32 bit aligned.
|
|
16
|
+
|
|
17
|
+
GMAPRecordFormat1 = """
|
|
18
|
+
> # big endian
|
|
19
|
+
UV: L
|
|
20
|
+
cid: H
|
|
21
|
+
gid: H
|
|
22
|
+
ggid: H
|
|
23
|
+
name: 32s
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GMAPRecord(object):
|
|
28
|
+
def __init__(self, uv=0, cid=0, gid=0, ggid=0, name=""):
|
|
29
|
+
self.UV = uv
|
|
30
|
+
self.cid = cid
|
|
31
|
+
self.gid = gid
|
|
32
|
+
self.ggid = ggid
|
|
33
|
+
self.name = name
|
|
34
|
+
|
|
35
|
+
def toXML(self, writer, ttFont):
|
|
36
|
+
writer.begintag("GMAPRecord")
|
|
37
|
+
writer.newline()
|
|
38
|
+
writer.simpletag("UV", value=self.UV)
|
|
39
|
+
writer.newline()
|
|
40
|
+
writer.simpletag("cid", value=self.cid)
|
|
41
|
+
writer.newline()
|
|
42
|
+
writer.simpletag("gid", value=self.gid)
|
|
43
|
+
writer.newline()
|
|
44
|
+
writer.simpletag("glyphletGid", value=self.gid)
|
|
45
|
+
writer.newline()
|
|
46
|
+
writer.simpletag("GlyphletName", value=self.name)
|
|
47
|
+
writer.newline()
|
|
48
|
+
writer.endtag("GMAPRecord")
|
|
49
|
+
writer.newline()
|
|
50
|
+
|
|
51
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
52
|
+
value = attrs["value"]
|
|
53
|
+
if name == "GlyphletName":
|
|
54
|
+
self.name = value
|
|
55
|
+
else:
|
|
56
|
+
setattr(self, name, safeEval(value))
|
|
57
|
+
|
|
58
|
+
def compile(self, ttFont):
|
|
59
|
+
if self.UV is None:
|
|
60
|
+
self.UV = 0
|
|
61
|
+
nameLen = len(self.name)
|
|
62
|
+
if nameLen < 32:
|
|
63
|
+
self.name = self.name + "\0" * (32 - nameLen)
|
|
64
|
+
data = sstruct.pack(GMAPRecordFormat1, self)
|
|
65
|
+
return data
|
|
66
|
+
|
|
67
|
+
def __repr__(self):
|
|
68
|
+
return (
|
|
69
|
+
"GMAPRecord[ UV: "
|
|
70
|
+
+ str(self.UV)
|
|
71
|
+
+ ", cid: "
|
|
72
|
+
+ str(self.cid)
|
|
73
|
+
+ ", gid: "
|
|
74
|
+
+ str(self.gid)
|
|
75
|
+
+ ", ggid: "
|
|
76
|
+
+ str(self.ggid)
|
|
77
|
+
+ ", Glyphlet Name: "
|
|
78
|
+
+ str(self.name)
|
|
79
|
+
+ " ]"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class table_G_M_A_P_(DefaultTable.DefaultTable):
|
|
84
|
+
"""Glyphlets GMAP table
|
|
85
|
+
|
|
86
|
+
The ``GMAP`` table is used by Adobe's SING Glyphlets.
|
|
87
|
+
|
|
88
|
+
See also https://web.archive.org/web/20080627183635/http://www.adobe.com/devnet/opentype/gdk/topic.html
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
dependencies = []
|
|
92
|
+
|
|
93
|
+
def decompile(self, data, ttFont):
|
|
94
|
+
dummy, newData = sstruct.unpack2(GMAPFormat, data, self)
|
|
95
|
+
self.psFontName = tostr(newData[: self.fontNameLength])
|
|
96
|
+
assert (
|
|
97
|
+
self.recordsOffset % 4
|
|
98
|
+
) == 0, "GMAP error: recordsOffset is not 32 bit aligned."
|
|
99
|
+
newData = data[self.recordsOffset :]
|
|
100
|
+
self.gmapRecords = []
|
|
101
|
+
for i in range(self.recordsCount):
|
|
102
|
+
gmapRecord, newData = sstruct.unpack2(
|
|
103
|
+
GMAPRecordFormat1, newData, GMAPRecord()
|
|
104
|
+
)
|
|
105
|
+
gmapRecord.name = gmapRecord.name.strip("\0")
|
|
106
|
+
self.gmapRecords.append(gmapRecord)
|
|
107
|
+
|
|
108
|
+
def compile(self, ttFont):
|
|
109
|
+
self.recordsCount = len(self.gmapRecords)
|
|
110
|
+
self.fontNameLength = len(self.psFontName)
|
|
111
|
+
self.recordsOffset = 4 * (((self.fontNameLength + 12) + 3) // 4)
|
|
112
|
+
data = sstruct.pack(GMAPFormat, self)
|
|
113
|
+
data = data + tobytes(self.psFontName)
|
|
114
|
+
data = data + b"\0" * (self.recordsOffset - len(data))
|
|
115
|
+
for record in self.gmapRecords:
|
|
116
|
+
data = data + record.compile(ttFont)
|
|
117
|
+
return data
|
|
118
|
+
|
|
119
|
+
def toXML(self, writer, ttFont):
|
|
120
|
+
writer.comment("Most of this table will be recalculated by the compiler")
|
|
121
|
+
writer.newline()
|
|
122
|
+
formatstring, names, fixes = sstruct.getformat(GMAPFormat)
|
|
123
|
+
for name in names:
|
|
124
|
+
value = getattr(self, name)
|
|
125
|
+
writer.simpletag(name, value=value)
|
|
126
|
+
writer.newline()
|
|
127
|
+
writer.simpletag("PSFontName", value=self.psFontName)
|
|
128
|
+
writer.newline()
|
|
129
|
+
for gmapRecord in self.gmapRecords:
|
|
130
|
+
gmapRecord.toXML(writer, ttFont)
|
|
131
|
+
|
|
132
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
133
|
+
if name == "GMAPRecord":
|
|
134
|
+
if not hasattr(self, "gmapRecords"):
|
|
135
|
+
self.gmapRecords = []
|
|
136
|
+
gmapRecord = GMAPRecord()
|
|
137
|
+
self.gmapRecords.append(gmapRecord)
|
|
138
|
+
for element in content:
|
|
139
|
+
if isinstance(element, str):
|
|
140
|
+
continue
|
|
141
|
+
name, attrs, content = element
|
|
142
|
+
gmapRecord.fromXML(name, attrs, content, ttFont)
|
|
143
|
+
else:
|
|
144
|
+
value = attrs["value"]
|
|
145
|
+
if name == "PSFontName":
|
|
146
|
+
self.psFontName = value
|
|
147
|
+
else:
|
|
148
|
+
setattr(self, name, safeEval(value))
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from fontTools.misc import sstruct
|
|
2
|
+
from fontTools.misc.textTools import bytesjoin, safeEval, readHex
|
|
3
|
+
from . import DefaultTable
|
|
4
|
+
import sys
|
|
5
|
+
import array
|
|
6
|
+
|
|
7
|
+
GPKGFormat = """
|
|
8
|
+
> # big endian
|
|
9
|
+
version: H
|
|
10
|
+
flags: H
|
|
11
|
+
numGMAPs: H
|
|
12
|
+
numGlyplets: H
|
|
13
|
+
"""
|
|
14
|
+
# psFontName is a byte string which follows the record above. This is zero padded
|
|
15
|
+
# to the beginning of the records array. The recordsOffsst is 32 bit aligned.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class table_G_P_K_G_(DefaultTable.DefaultTable):
|
|
19
|
+
"""Glyphlets GPKG table
|
|
20
|
+
|
|
21
|
+
The ``GPKG`` table is used by Adobe's SING Glyphlets.
|
|
22
|
+
|
|
23
|
+
See also https://web.archive.org/web/20080627183635/http://www.adobe.com/devnet/opentype/gdk/topic.html
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def decompile(self, data, ttFont):
|
|
27
|
+
dummy, newData = sstruct.unpack2(GPKGFormat, data, self)
|
|
28
|
+
|
|
29
|
+
GMAPoffsets = array.array("I")
|
|
30
|
+
endPos = (self.numGMAPs + 1) * 4
|
|
31
|
+
GMAPoffsets.frombytes(newData[:endPos])
|
|
32
|
+
if sys.byteorder != "big":
|
|
33
|
+
GMAPoffsets.byteswap()
|
|
34
|
+
self.GMAPs = []
|
|
35
|
+
for i in range(self.numGMAPs):
|
|
36
|
+
start = GMAPoffsets[i]
|
|
37
|
+
end = GMAPoffsets[i + 1]
|
|
38
|
+
self.GMAPs.append(data[start:end])
|
|
39
|
+
pos = endPos
|
|
40
|
+
endPos = pos + (self.numGlyplets + 1) * 4
|
|
41
|
+
glyphletOffsets = array.array("I")
|
|
42
|
+
glyphletOffsets.frombytes(newData[pos:endPos])
|
|
43
|
+
if sys.byteorder != "big":
|
|
44
|
+
glyphletOffsets.byteswap()
|
|
45
|
+
self.glyphlets = []
|
|
46
|
+
for i in range(self.numGlyplets):
|
|
47
|
+
start = glyphletOffsets[i]
|
|
48
|
+
end = glyphletOffsets[i + 1]
|
|
49
|
+
self.glyphlets.append(data[start:end])
|
|
50
|
+
|
|
51
|
+
def compile(self, ttFont):
|
|
52
|
+
self.numGMAPs = len(self.GMAPs)
|
|
53
|
+
self.numGlyplets = len(self.glyphlets)
|
|
54
|
+
GMAPoffsets = [0] * (self.numGMAPs + 1)
|
|
55
|
+
glyphletOffsets = [0] * (self.numGlyplets + 1)
|
|
56
|
+
|
|
57
|
+
dataList = [sstruct.pack(GPKGFormat, self)]
|
|
58
|
+
|
|
59
|
+
pos = len(dataList[0]) + (self.numGMAPs + 1) * 4 + (self.numGlyplets + 1) * 4
|
|
60
|
+
GMAPoffsets[0] = pos
|
|
61
|
+
for i in range(1, self.numGMAPs + 1):
|
|
62
|
+
pos += len(self.GMAPs[i - 1])
|
|
63
|
+
GMAPoffsets[i] = pos
|
|
64
|
+
gmapArray = array.array("I", GMAPoffsets)
|
|
65
|
+
if sys.byteorder != "big":
|
|
66
|
+
gmapArray.byteswap()
|
|
67
|
+
dataList.append(gmapArray.tobytes())
|
|
68
|
+
|
|
69
|
+
glyphletOffsets[0] = pos
|
|
70
|
+
for i in range(1, self.numGlyplets + 1):
|
|
71
|
+
pos += len(self.glyphlets[i - 1])
|
|
72
|
+
glyphletOffsets[i] = pos
|
|
73
|
+
glyphletArray = array.array("I", glyphletOffsets)
|
|
74
|
+
if sys.byteorder != "big":
|
|
75
|
+
glyphletArray.byteswap()
|
|
76
|
+
dataList.append(glyphletArray.tobytes())
|
|
77
|
+
dataList += self.GMAPs
|
|
78
|
+
dataList += self.glyphlets
|
|
79
|
+
data = bytesjoin(dataList)
|
|
80
|
+
return data
|
|
81
|
+
|
|
82
|
+
def toXML(self, writer, ttFont):
|
|
83
|
+
writer.comment("Most of this table will be recalculated by the compiler")
|
|
84
|
+
writer.newline()
|
|
85
|
+
formatstring, names, fixes = sstruct.getformat(GPKGFormat)
|
|
86
|
+
for name in names:
|
|
87
|
+
value = getattr(self, name)
|
|
88
|
+
writer.simpletag(name, value=value)
|
|
89
|
+
writer.newline()
|
|
90
|
+
|
|
91
|
+
writer.begintag("GMAPs")
|
|
92
|
+
writer.newline()
|
|
93
|
+
for gmapData in self.GMAPs:
|
|
94
|
+
writer.begintag("hexdata")
|
|
95
|
+
writer.newline()
|
|
96
|
+
writer.dumphex(gmapData)
|
|
97
|
+
writer.endtag("hexdata")
|
|
98
|
+
writer.newline()
|
|
99
|
+
writer.endtag("GMAPs")
|
|
100
|
+
writer.newline()
|
|
101
|
+
|
|
102
|
+
writer.begintag("glyphlets")
|
|
103
|
+
writer.newline()
|
|
104
|
+
for glyphletData in self.glyphlets:
|
|
105
|
+
writer.begintag("hexdata")
|
|
106
|
+
writer.newline()
|
|
107
|
+
writer.dumphex(glyphletData)
|
|
108
|
+
writer.endtag("hexdata")
|
|
109
|
+
writer.newline()
|
|
110
|
+
writer.endtag("glyphlets")
|
|
111
|
+
writer.newline()
|
|
112
|
+
|
|
113
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
114
|
+
if name == "GMAPs":
|
|
115
|
+
if not hasattr(self, "GMAPs"):
|
|
116
|
+
self.GMAPs = []
|
|
117
|
+
for element in content:
|
|
118
|
+
if isinstance(element, str):
|
|
119
|
+
continue
|
|
120
|
+
itemName, itemAttrs, itemContent = element
|
|
121
|
+
if itemName == "hexdata":
|
|
122
|
+
self.GMAPs.append(readHex(itemContent))
|
|
123
|
+
elif name == "glyphlets":
|
|
124
|
+
if not hasattr(self, "glyphlets"):
|
|
125
|
+
self.glyphlets = []
|
|
126
|
+
for element in content:
|
|
127
|
+
if isinstance(element, str):
|
|
128
|
+
continue
|
|
129
|
+
itemName, itemAttrs, itemContent = element
|
|
130
|
+
if itemName == "hexdata":
|
|
131
|
+
self.glyphlets.append(readHex(itemContent))
|
|
132
|
+
else:
|
|
133
|
+
setattr(self, name, safeEval(attrs["value"]))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .otBase import BaseTTXConverter
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class table_G_P_O_S_(BaseTTXConverter):
|
|
5
|
+
"""Glyph Positioning table
|
|
6
|
+
|
|
7
|
+
The ``GPOS`` table stores advanced glyph-positioning data
|
|
8
|
+
used in OpenType Layout features, such as mark attachment,
|
|
9
|
+
cursive attachment, kerning, and other position adjustments.
|
|
10
|
+
|
|
11
|
+
See also https://learn.microsoft.com/en-us/typography/opentype/spec/gpos
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
pass
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .otBase import BaseTTXConverter
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class table_G_S_U_B_(BaseTTXConverter):
|
|
5
|
+
"""Glyph Substitution table
|
|
6
|
+
|
|
7
|
+
The ``GSUB`` table contains glyph-substitution rules used in
|
|
8
|
+
OpenType Layout.
|
|
9
|
+
|
|
10
|
+
See also https://learn.microsoft.com/en-us/typography/opentype/spec/gsub
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
pass
|