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,206 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from io import StringIO
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from fontTools import configLogger
|
|
8
|
+
from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
|
|
9
|
+
from fontTools.feaLib.error import FeatureLibError
|
|
10
|
+
from fontTools.feaLib.lexer import Lexer
|
|
11
|
+
from fontTools.misc.cliTools import makeOutputFileName
|
|
12
|
+
from fontTools.ttLib import TTFont, TTLibError
|
|
13
|
+
from fontTools.voltLib.parser import Parser
|
|
14
|
+
from fontTools.voltLib.voltToFea import TABLES, VoltToFea
|
|
15
|
+
|
|
16
|
+
log = logging.getLogger("fontTools.feaLib")
|
|
17
|
+
|
|
18
|
+
SUPPORTED_TABLES = TABLES + ["cmap"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def invalid_fea_glyph_name(name):
|
|
22
|
+
"""Check if the glyph name is valid according to FEA syntax."""
|
|
23
|
+
if name[0] not in Lexer.CHAR_NAME_START_:
|
|
24
|
+
return True
|
|
25
|
+
if any(c not in Lexer.CHAR_NAME_CONTINUATION_ for c in name[1:]):
|
|
26
|
+
return True
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def sanitize_glyph_name(name):
|
|
31
|
+
"""Sanitize the glyph name to ensure it is valid according to FEA syntax."""
|
|
32
|
+
sanitized = ""
|
|
33
|
+
for i, c in enumerate(name):
|
|
34
|
+
if i == 0 and c not in Lexer.CHAR_NAME_START_:
|
|
35
|
+
sanitized += "a" + c
|
|
36
|
+
elif c not in Lexer.CHAR_NAME_CONTINUATION_:
|
|
37
|
+
sanitized += "_"
|
|
38
|
+
else:
|
|
39
|
+
sanitized += c
|
|
40
|
+
|
|
41
|
+
return sanitized
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main(args=None):
|
|
45
|
+
"""Build tables from a MS VOLT project into an OTF font"""
|
|
46
|
+
parser = argparse.ArgumentParser(
|
|
47
|
+
description="Use fontTools to compile MS VOLT projects."
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"input",
|
|
51
|
+
metavar="INPUT",
|
|
52
|
+
help="Path to the input font/VTP file to process",
|
|
53
|
+
type=Path,
|
|
54
|
+
)
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"-f",
|
|
57
|
+
"--font",
|
|
58
|
+
metavar="INPUT_FONT",
|
|
59
|
+
help="Path to the input font (if INPUT is a VTP file)",
|
|
60
|
+
type=Path,
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"-o",
|
|
64
|
+
"--output",
|
|
65
|
+
dest="output",
|
|
66
|
+
metavar="OUTPUT",
|
|
67
|
+
help="Path to the output font.",
|
|
68
|
+
type=Path,
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"-t",
|
|
72
|
+
"--tables",
|
|
73
|
+
metavar="TABLE_TAG",
|
|
74
|
+
choices=SUPPORTED_TABLES,
|
|
75
|
+
nargs="+",
|
|
76
|
+
help="Specify the table(s) to be built.",
|
|
77
|
+
)
|
|
78
|
+
parser.add_argument(
|
|
79
|
+
"-F",
|
|
80
|
+
"--debug-feature-file",
|
|
81
|
+
help="Write the generated feature file to disk.",
|
|
82
|
+
action="store_true",
|
|
83
|
+
)
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"--ship",
|
|
86
|
+
help="Remove source VOLT tables from output font.",
|
|
87
|
+
action="store_true",
|
|
88
|
+
)
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
"-v",
|
|
91
|
+
"--verbose",
|
|
92
|
+
help="Increase the logger verbosity. Multiple -v options are allowed.",
|
|
93
|
+
action="count",
|
|
94
|
+
default=0,
|
|
95
|
+
)
|
|
96
|
+
parser.add_argument(
|
|
97
|
+
"-T",
|
|
98
|
+
"--traceback",
|
|
99
|
+
help="show traceback for exceptions.",
|
|
100
|
+
action="store_true",
|
|
101
|
+
)
|
|
102
|
+
options = parser.parse_args(args)
|
|
103
|
+
|
|
104
|
+
levels = ["WARNING", "INFO", "DEBUG"]
|
|
105
|
+
configLogger(level=levels[min(len(levels) - 1, options.verbose)])
|
|
106
|
+
|
|
107
|
+
output_font = options.output or Path(
|
|
108
|
+
makeOutputFileName(options.font or options.input)
|
|
109
|
+
)
|
|
110
|
+
log.info(f"Compiling MS VOLT to '{output_font}'")
|
|
111
|
+
|
|
112
|
+
file_or_path = options.input
|
|
113
|
+
font = None
|
|
114
|
+
|
|
115
|
+
# If the input is a font file, extract the VOLT data from the "TSIV" table
|
|
116
|
+
try:
|
|
117
|
+
font = TTFont(file_or_path)
|
|
118
|
+
if "TSIV" in font:
|
|
119
|
+
file_or_path = StringIO(font["TSIV"].data.decode("utf-8"))
|
|
120
|
+
else:
|
|
121
|
+
log.error('"TSIV" table is missing')
|
|
122
|
+
return 1
|
|
123
|
+
except TTLibError:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
# If input is not a font file, the font must be provided
|
|
127
|
+
if font is None:
|
|
128
|
+
if not options.font:
|
|
129
|
+
log.error("Please provide an input font")
|
|
130
|
+
return 1
|
|
131
|
+
font = TTFont(options.font)
|
|
132
|
+
|
|
133
|
+
# FEA syntax does not allow some glyph names that VOLT accepts, so if we
|
|
134
|
+
# found such glyph name we will temporarily rename such glyphs.
|
|
135
|
+
glyphOrder = font.getGlyphOrder()
|
|
136
|
+
tempGlyphOrder = None
|
|
137
|
+
if any(invalid_fea_glyph_name(n) for n in glyphOrder):
|
|
138
|
+
tempGlyphOrder = []
|
|
139
|
+
for n in glyphOrder:
|
|
140
|
+
if invalid_fea_glyph_name(n):
|
|
141
|
+
n = sanitize_glyph_name(n)
|
|
142
|
+
existing = set(tempGlyphOrder) | set(glyphOrder)
|
|
143
|
+
while n in existing:
|
|
144
|
+
n = "a" + n
|
|
145
|
+
tempGlyphOrder.append(n)
|
|
146
|
+
font.setGlyphOrder(tempGlyphOrder)
|
|
147
|
+
|
|
148
|
+
doc = Parser(file_or_path).parse()
|
|
149
|
+
|
|
150
|
+
log.info("Converting VTP data to FEA")
|
|
151
|
+
converter = VoltToFea(doc, font)
|
|
152
|
+
try:
|
|
153
|
+
fea = converter.convert(options.tables, ignore_unsupported_settings=True)
|
|
154
|
+
except NotImplementedError as e:
|
|
155
|
+
if options.traceback:
|
|
156
|
+
raise
|
|
157
|
+
location = getattr(e.args[0], "location", None)
|
|
158
|
+
message = f'"{e}" is not supported'
|
|
159
|
+
if location:
|
|
160
|
+
path, line, column = location
|
|
161
|
+
log.error(f"{path}:{line}:{column}: {message}")
|
|
162
|
+
else:
|
|
163
|
+
log.error(message)
|
|
164
|
+
return 1
|
|
165
|
+
|
|
166
|
+
fea_filename = options.input
|
|
167
|
+
if options.debug_feature_file:
|
|
168
|
+
fea_filename = output_font.with_suffix(".fea")
|
|
169
|
+
log.info(f"Writing FEA to '{fea_filename}'")
|
|
170
|
+
with open(fea_filename, "w") as fp:
|
|
171
|
+
fp.write(fea)
|
|
172
|
+
|
|
173
|
+
log.info("Compiling FEA to OpenType tables")
|
|
174
|
+
try:
|
|
175
|
+
addOpenTypeFeaturesFromString(
|
|
176
|
+
font,
|
|
177
|
+
fea,
|
|
178
|
+
filename=fea_filename,
|
|
179
|
+
tables=options.tables,
|
|
180
|
+
)
|
|
181
|
+
except FeatureLibError as e:
|
|
182
|
+
if options.traceback:
|
|
183
|
+
raise
|
|
184
|
+
log.error(e)
|
|
185
|
+
return 1
|
|
186
|
+
|
|
187
|
+
if options.ship:
|
|
188
|
+
for tag in ["TSIV", "TSIS", "TSIP", "TSID"]:
|
|
189
|
+
if tag in font:
|
|
190
|
+
del font[tag]
|
|
191
|
+
|
|
192
|
+
# Restore original glyph names.
|
|
193
|
+
if tempGlyphOrder:
|
|
194
|
+
import io
|
|
195
|
+
|
|
196
|
+
f = io.BytesIO()
|
|
197
|
+
font.save(f)
|
|
198
|
+
font = TTFont(f)
|
|
199
|
+
font.setGlyphOrder(glyphOrder)
|
|
200
|
+
font["post"].extraNames = []
|
|
201
|
+
|
|
202
|
+
font.save(output_font)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
if __name__ == "__main__":
|
|
206
|
+
sys.exit(main())
|
fontTools/voltLib/ast.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
from fontTools.voltLib.error import VoltLibError
|
|
2
|
+
from typing import NamedTuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Pos(NamedTuple):
|
|
6
|
+
adv: int
|
|
7
|
+
dx: int
|
|
8
|
+
dy: int
|
|
9
|
+
adv_adjust_by: dict
|
|
10
|
+
dx_adjust_by: dict
|
|
11
|
+
dy_adjust_by: dict
|
|
12
|
+
|
|
13
|
+
def __str__(self):
|
|
14
|
+
res = " POS"
|
|
15
|
+
for attr in ("adv", "dx", "dy"):
|
|
16
|
+
value = getattr(self, attr)
|
|
17
|
+
if value is not None:
|
|
18
|
+
res += f" {attr.upper()} {value}"
|
|
19
|
+
adjust_by = getattr(self, f"{attr}_adjust_by", {})
|
|
20
|
+
for size, adjustment in adjust_by.items():
|
|
21
|
+
res += f" ADJUST_BY {adjustment} AT {size}"
|
|
22
|
+
res += " END_POS"
|
|
23
|
+
return res
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Element(object):
|
|
27
|
+
def __init__(self, location=None):
|
|
28
|
+
self.location = location
|
|
29
|
+
|
|
30
|
+
def build(self, builder):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
raise NotImplementedError
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Statement(Element):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Expression(Element):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class VoltFile(Statement):
|
|
46
|
+
def __init__(self):
|
|
47
|
+
Statement.__init__(self, location=None)
|
|
48
|
+
self.statements = []
|
|
49
|
+
|
|
50
|
+
def build(self, builder):
|
|
51
|
+
for s in self.statements:
|
|
52
|
+
s.build(builder)
|
|
53
|
+
|
|
54
|
+
def __str__(self):
|
|
55
|
+
return "\n" + "\n".join(str(s) for s in self.statements) + " END\n"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class GlyphDefinition(Statement):
|
|
59
|
+
def __init__(self, name, gid, gunicode, gtype, components, location=None):
|
|
60
|
+
Statement.__init__(self, location)
|
|
61
|
+
self.name = name
|
|
62
|
+
self.id = gid
|
|
63
|
+
self.unicode = gunicode
|
|
64
|
+
self.type = gtype
|
|
65
|
+
self.components = components
|
|
66
|
+
|
|
67
|
+
def __str__(self):
|
|
68
|
+
res = f'DEF_GLYPH "{self.name}" ID {self.id}'
|
|
69
|
+
if self.unicode is not None:
|
|
70
|
+
if len(self.unicode) > 1:
|
|
71
|
+
unicodes = ",".join(f"U+{u:04X}" for u in self.unicode)
|
|
72
|
+
res += f' UNICODEVALUES "{unicodes}"'
|
|
73
|
+
else:
|
|
74
|
+
res += f" UNICODE {self.unicode[0]}"
|
|
75
|
+
if self.type is not None:
|
|
76
|
+
res += f" TYPE {self.type}"
|
|
77
|
+
if self.components is not None:
|
|
78
|
+
res += f" COMPONENTS {self.components}"
|
|
79
|
+
res += " END_GLYPH"
|
|
80
|
+
return res
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class GroupDefinition(Statement):
|
|
84
|
+
def __init__(self, name, enum, location=None):
|
|
85
|
+
Statement.__init__(self, location)
|
|
86
|
+
self.name = name
|
|
87
|
+
self.enum = enum
|
|
88
|
+
self.glyphs_ = None
|
|
89
|
+
|
|
90
|
+
def glyphSet(self, groups=None):
|
|
91
|
+
if groups is not None and self.name in groups:
|
|
92
|
+
raise VoltLibError(
|
|
93
|
+
'Group "%s" contains itself.' % (self.name), self.location
|
|
94
|
+
)
|
|
95
|
+
if self.glyphs_ is None:
|
|
96
|
+
if groups is None:
|
|
97
|
+
groups = set({self.name})
|
|
98
|
+
else:
|
|
99
|
+
groups.add(self.name)
|
|
100
|
+
self.glyphs_ = self.enum.glyphSet(groups)
|
|
101
|
+
return self.glyphs_
|
|
102
|
+
|
|
103
|
+
def __str__(self):
|
|
104
|
+
enum = self.enum and str(self.enum) or ""
|
|
105
|
+
return f'DEF_GROUP "{self.name}"\n{enum}\nEND_GROUP'
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class GlyphName(Expression):
|
|
109
|
+
"""A single glyph name, such as cedilla."""
|
|
110
|
+
|
|
111
|
+
def __init__(self, glyph, location=None):
|
|
112
|
+
Expression.__init__(self, location)
|
|
113
|
+
self.glyph = glyph
|
|
114
|
+
|
|
115
|
+
def glyphSet(self):
|
|
116
|
+
return (self.glyph,)
|
|
117
|
+
|
|
118
|
+
def __str__(self):
|
|
119
|
+
return f' GLYPH "{self.glyph}"'
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class Enum(Expression):
|
|
123
|
+
"""An enum"""
|
|
124
|
+
|
|
125
|
+
def __init__(self, enum, location=None):
|
|
126
|
+
Expression.__init__(self, location)
|
|
127
|
+
self.enum = enum
|
|
128
|
+
|
|
129
|
+
def __iter__(self):
|
|
130
|
+
for e in self.glyphSet():
|
|
131
|
+
yield e
|
|
132
|
+
|
|
133
|
+
def glyphSet(self, groups=None):
|
|
134
|
+
glyphs = []
|
|
135
|
+
for element in self.enum:
|
|
136
|
+
if isinstance(element, (GroupName, Enum)):
|
|
137
|
+
glyphs.extend(element.glyphSet(groups))
|
|
138
|
+
else:
|
|
139
|
+
glyphs.extend(element.glyphSet())
|
|
140
|
+
return tuple(glyphs)
|
|
141
|
+
|
|
142
|
+
def __str__(self):
|
|
143
|
+
enum = "".join(str(e) for e in self.enum)
|
|
144
|
+
return f" ENUM{enum} END_ENUM"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class GroupName(Expression):
|
|
148
|
+
"""A glyph group"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, group, parser, location=None):
|
|
151
|
+
Expression.__init__(self, location)
|
|
152
|
+
self.group = group
|
|
153
|
+
self.parser_ = parser
|
|
154
|
+
|
|
155
|
+
def glyphSet(self, groups=None):
|
|
156
|
+
group = self.parser_.resolve_group(self.group)
|
|
157
|
+
if group is not None:
|
|
158
|
+
self.glyphs_ = group.glyphSet(groups)
|
|
159
|
+
return self.glyphs_
|
|
160
|
+
else:
|
|
161
|
+
raise VoltLibError(
|
|
162
|
+
'Group "%s" is used but undefined.' % (self.group), self.location
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def __str__(self):
|
|
166
|
+
return f' GROUP "{self.group}"'
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class Range(Expression):
|
|
170
|
+
"""A glyph range"""
|
|
171
|
+
|
|
172
|
+
def __init__(self, start, end, parser, location=None):
|
|
173
|
+
Expression.__init__(self, location)
|
|
174
|
+
self.start = start
|
|
175
|
+
self.end = end
|
|
176
|
+
self.parser = parser
|
|
177
|
+
|
|
178
|
+
def glyphSet(self):
|
|
179
|
+
return tuple(self.parser.glyph_range(self.start, self.end))
|
|
180
|
+
|
|
181
|
+
def __str__(self):
|
|
182
|
+
return f' RANGE "{self.start}" TO "{self.end}"'
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class ScriptDefinition(Statement):
|
|
186
|
+
def __init__(self, name, tag, langs, location=None):
|
|
187
|
+
Statement.__init__(self, location)
|
|
188
|
+
self.name = name
|
|
189
|
+
self.tag = tag
|
|
190
|
+
self.langs = langs
|
|
191
|
+
|
|
192
|
+
def __str__(self):
|
|
193
|
+
res = "DEF_SCRIPT"
|
|
194
|
+
if self.name is not None:
|
|
195
|
+
res += f' NAME "{self.name}"'
|
|
196
|
+
res += f' TAG "{self.tag}"\n\n'
|
|
197
|
+
for lang in self.langs:
|
|
198
|
+
res += f"{lang}"
|
|
199
|
+
res += "END_SCRIPT"
|
|
200
|
+
return res
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class LangSysDefinition(Statement):
|
|
204
|
+
def __init__(self, name, tag, features, location=None):
|
|
205
|
+
Statement.__init__(self, location)
|
|
206
|
+
self.name = name
|
|
207
|
+
self.tag = tag
|
|
208
|
+
self.features = features
|
|
209
|
+
|
|
210
|
+
def __str__(self):
|
|
211
|
+
res = "DEF_LANGSYS"
|
|
212
|
+
if self.name is not None:
|
|
213
|
+
res += f' NAME "{self.name}"'
|
|
214
|
+
res += f' TAG "{self.tag}"\n\n'
|
|
215
|
+
for feature in self.features:
|
|
216
|
+
res += f"{feature}"
|
|
217
|
+
res += "END_LANGSYS\n"
|
|
218
|
+
return res
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class FeatureDefinition(Statement):
|
|
222
|
+
def __init__(self, name, tag, lookups, location=None):
|
|
223
|
+
Statement.__init__(self, location)
|
|
224
|
+
self.name = name
|
|
225
|
+
self.tag = tag
|
|
226
|
+
self.lookups = lookups
|
|
227
|
+
|
|
228
|
+
def __str__(self):
|
|
229
|
+
res = f'DEF_FEATURE NAME "{self.name}" TAG "{self.tag}"\n'
|
|
230
|
+
res += " " + " ".join(f'LOOKUP "{l}"' for l in self.lookups) + "\n"
|
|
231
|
+
res += "END_FEATURE\n"
|
|
232
|
+
return res
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class LookupDefinition(Statement):
|
|
236
|
+
def __init__(
|
|
237
|
+
self,
|
|
238
|
+
name,
|
|
239
|
+
process_base,
|
|
240
|
+
process_marks,
|
|
241
|
+
mark_glyph_set,
|
|
242
|
+
direction,
|
|
243
|
+
reversal,
|
|
244
|
+
comments,
|
|
245
|
+
context,
|
|
246
|
+
sub,
|
|
247
|
+
pos,
|
|
248
|
+
location=None,
|
|
249
|
+
):
|
|
250
|
+
Statement.__init__(self, location)
|
|
251
|
+
self.name = name
|
|
252
|
+
self.process_base = process_base
|
|
253
|
+
self.process_marks = process_marks
|
|
254
|
+
self.mark_glyph_set = mark_glyph_set
|
|
255
|
+
self.direction = direction
|
|
256
|
+
self.reversal = reversal
|
|
257
|
+
self.comments = comments
|
|
258
|
+
self.context = context
|
|
259
|
+
self.sub = sub
|
|
260
|
+
self.pos = pos
|
|
261
|
+
|
|
262
|
+
def __str__(self):
|
|
263
|
+
res = f'DEF_LOOKUP "{self.name}"'
|
|
264
|
+
res += f' {self.process_base and "PROCESS_BASE" or "SKIP_BASE"}'
|
|
265
|
+
if self.process_marks:
|
|
266
|
+
res += " PROCESS_MARKS "
|
|
267
|
+
if self.mark_glyph_set:
|
|
268
|
+
res += f'MARK_GLYPH_SET "{self.mark_glyph_set}"'
|
|
269
|
+
elif isinstance(self.process_marks, str):
|
|
270
|
+
res += f'"{self.process_marks}"'
|
|
271
|
+
else:
|
|
272
|
+
res += "ALL"
|
|
273
|
+
else:
|
|
274
|
+
res += " SKIP_MARKS"
|
|
275
|
+
if self.direction is not None:
|
|
276
|
+
res += f" DIRECTION {self.direction}"
|
|
277
|
+
if self.reversal:
|
|
278
|
+
res += " REVERSAL"
|
|
279
|
+
if self.comments is not None:
|
|
280
|
+
comments = self.comments.replace("\n", r"\n")
|
|
281
|
+
res += f'\nCOMMENTS "{comments}"'
|
|
282
|
+
if self.context:
|
|
283
|
+
res += "\n" + "\n".join(str(c) for c in self.context)
|
|
284
|
+
else:
|
|
285
|
+
res += "\nIN_CONTEXT\nEND_CONTEXT"
|
|
286
|
+
if self.sub:
|
|
287
|
+
res += f"\n{self.sub}"
|
|
288
|
+
if self.pos:
|
|
289
|
+
res += f"\n{self.pos}"
|
|
290
|
+
return res
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class SubstitutionDefinition(Statement):
|
|
294
|
+
def __init__(self, mapping, location=None):
|
|
295
|
+
Statement.__init__(self, location)
|
|
296
|
+
self.mapping = mapping
|
|
297
|
+
|
|
298
|
+
def __str__(self):
|
|
299
|
+
res = "AS_SUBSTITUTION\n"
|
|
300
|
+
for src, dst in self.mapping.items():
|
|
301
|
+
src = "".join(str(s) for s in src)
|
|
302
|
+
dst = "".join(str(d) for d in dst)
|
|
303
|
+
res += f"SUB{src}\nWITH{dst}\nEND_SUB\n"
|
|
304
|
+
res += "END_SUBSTITUTION"
|
|
305
|
+
return res
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class SubstitutionSingleDefinition(SubstitutionDefinition):
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class SubstitutionMultipleDefinition(SubstitutionDefinition):
|
|
313
|
+
pass
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class SubstitutionLigatureDefinition(SubstitutionDefinition):
|
|
317
|
+
pass
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class SubstitutionAlternateDefinition(SubstitutionDefinition):
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class SubstitutionReverseChainingSingleDefinition(SubstitutionDefinition):
|
|
325
|
+
pass
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class PositionAttachDefinition(Statement):
|
|
329
|
+
def __init__(self, coverage, coverage_to, location=None):
|
|
330
|
+
Statement.__init__(self, location)
|
|
331
|
+
self.coverage = coverage
|
|
332
|
+
self.coverage_to = coverage_to
|
|
333
|
+
|
|
334
|
+
def __str__(self):
|
|
335
|
+
coverage = "".join(str(c) for c in self.coverage)
|
|
336
|
+
res = f"AS_POSITION\nATTACH{coverage}\nTO"
|
|
337
|
+
for coverage, anchor in self.coverage_to:
|
|
338
|
+
coverage = "".join(str(c) for c in coverage)
|
|
339
|
+
res += f'{coverage} AT ANCHOR "{anchor}"'
|
|
340
|
+
res += "\nEND_ATTACH\nEND_POSITION"
|
|
341
|
+
return res
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class PositionAttachCursiveDefinition(Statement):
|
|
345
|
+
def __init__(self, coverages_exit, coverages_enter, location=None):
|
|
346
|
+
Statement.__init__(self, location)
|
|
347
|
+
self.coverages_exit = coverages_exit
|
|
348
|
+
self.coverages_enter = coverages_enter
|
|
349
|
+
|
|
350
|
+
def __str__(self):
|
|
351
|
+
res = "AS_POSITION\nATTACH_CURSIVE"
|
|
352
|
+
for coverage in self.coverages_exit:
|
|
353
|
+
coverage = "".join(str(c) for c in coverage)
|
|
354
|
+
res += f"\nEXIT {coverage}"
|
|
355
|
+
for coverage in self.coverages_enter:
|
|
356
|
+
coverage = "".join(str(c) for c in coverage)
|
|
357
|
+
res += f"\nENTER {coverage}"
|
|
358
|
+
res += "\nEND_ATTACH\nEND_POSITION"
|
|
359
|
+
return res
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class PositionAdjustPairDefinition(Statement):
|
|
363
|
+
def __init__(self, coverages_1, coverages_2, adjust_pair, location=None):
|
|
364
|
+
Statement.__init__(self, location)
|
|
365
|
+
self.coverages_1 = coverages_1
|
|
366
|
+
self.coverages_2 = coverages_2
|
|
367
|
+
self.adjust_pair = adjust_pair
|
|
368
|
+
|
|
369
|
+
def __str__(self):
|
|
370
|
+
res = "AS_POSITION\nADJUST_PAIR\n"
|
|
371
|
+
for coverage in self.coverages_1:
|
|
372
|
+
coverage = " ".join(str(c) for c in coverage)
|
|
373
|
+
res += f" FIRST {coverage}"
|
|
374
|
+
res += "\n"
|
|
375
|
+
for coverage in self.coverages_2:
|
|
376
|
+
coverage = " ".join(str(c) for c in coverage)
|
|
377
|
+
res += f" SECOND {coverage}"
|
|
378
|
+
res += "\n"
|
|
379
|
+
for (id_1, id_2), (pos_1, pos_2) in self.adjust_pair.items():
|
|
380
|
+
res += f" {id_1} {id_2} BY{pos_1}{pos_2}\n"
|
|
381
|
+
res += "\nEND_ADJUST\nEND_POSITION"
|
|
382
|
+
return res
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class PositionAdjustSingleDefinition(Statement):
|
|
386
|
+
def __init__(self, adjust_single, location=None):
|
|
387
|
+
Statement.__init__(self, location)
|
|
388
|
+
self.adjust_single = adjust_single
|
|
389
|
+
|
|
390
|
+
def __str__(self):
|
|
391
|
+
res = "AS_POSITION\nADJUST_SINGLE"
|
|
392
|
+
for coverage, pos in self.adjust_single:
|
|
393
|
+
coverage = "".join(str(c) for c in coverage)
|
|
394
|
+
res += f"{coverage} BY{pos}"
|
|
395
|
+
res += "\nEND_ADJUST\nEND_POSITION"
|
|
396
|
+
return res
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class ContextDefinition(Statement):
|
|
400
|
+
def __init__(self, ex_or_in, left=None, right=None, location=None):
|
|
401
|
+
Statement.__init__(self, location)
|
|
402
|
+
self.ex_or_in = ex_or_in
|
|
403
|
+
self.left = left if left is not None else []
|
|
404
|
+
self.right = right if right is not None else []
|
|
405
|
+
|
|
406
|
+
def __str__(self):
|
|
407
|
+
res = self.ex_or_in + "\n"
|
|
408
|
+
for coverage in self.left:
|
|
409
|
+
coverage = "".join(str(c) for c in coverage)
|
|
410
|
+
res += f" LEFT{coverage}\n"
|
|
411
|
+
for coverage in self.right:
|
|
412
|
+
coverage = "".join(str(c) for c in coverage)
|
|
413
|
+
res += f" RIGHT{coverage}\n"
|
|
414
|
+
res += "END_CONTEXT"
|
|
415
|
+
return res
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class AnchorDefinition(Statement):
|
|
419
|
+
def __init__(self, name, gid, glyph_name, component, locked, pos, location=None):
|
|
420
|
+
Statement.__init__(self, location)
|
|
421
|
+
self.name = name
|
|
422
|
+
self.gid = gid
|
|
423
|
+
self.glyph_name = glyph_name
|
|
424
|
+
self.component = component
|
|
425
|
+
self.locked = locked
|
|
426
|
+
self.pos = pos
|
|
427
|
+
|
|
428
|
+
def __str__(self):
|
|
429
|
+
locked = self.locked and " LOCKED" or ""
|
|
430
|
+
return (
|
|
431
|
+
f'DEF_ANCHOR "{self.name}"'
|
|
432
|
+
f" ON {self.gid}"
|
|
433
|
+
f" GLYPH {self.glyph_name}"
|
|
434
|
+
f" COMPONENT {self.component}"
|
|
435
|
+
f"{locked}"
|
|
436
|
+
f" AT {self.pos} END_ANCHOR"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class SettingDefinition(Statement):
|
|
441
|
+
def __init__(self, name, value, location=None):
|
|
442
|
+
Statement.__init__(self, location)
|
|
443
|
+
self.name = name
|
|
444
|
+
self.value = value
|
|
445
|
+
|
|
446
|
+
def __str__(self):
|
|
447
|
+
if self.value is True:
|
|
448
|
+
return f"{self.name}"
|
|
449
|
+
if isinstance(self.value, (tuple, list)):
|
|
450
|
+
value = " ".join(str(v) for v in self.value)
|
|
451
|
+
return f"{self.name} {value}"
|
|
452
|
+
return f"{self.name} {self.value}"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class VoltLibError(Exception):
|
|
2
|
+
def __init__(self, message, location):
|
|
3
|
+
Exception.__init__(self, message)
|
|
4
|
+
self.location = location
|
|
5
|
+
|
|
6
|
+
def __str__(self):
|
|
7
|
+
message = Exception.__str__(self)
|
|
8
|
+
if self.location:
|
|
9
|
+
path, line, column = self.location
|
|
10
|
+
return "%s:%d:%d: %s" % (path, line, column, message)
|
|
11
|
+
else:
|
|
12
|
+
return message
|