parser: Lexing and parsing OpenType feature files

Overview

The primary interface for processing .fea files is fontTools.feaLib.parser.Parser. At a lower level, the fontTools.feaLib.lexer module implements feaLib’s lexical analysis of the .fea language syntax, augmented by several smaller utility modules.

Parsing

class fontTools.feaLib.parser.Parser(featurefile, glyphNames=(), followIncludes=True, includeDir=None, **kwargs)[source]

Bases: object

Initializes a Parser object.

Example

from fontTools.feaLib.parser import Parser
parser = Parser(file, font.getReverseGlyphMap())
parsetree = parser.parse()

Note: the glyphNames iterable serves a double role to help distinguish glyph names from ranges in the presence of hyphens and to ensure that glyph names referenced in a feature file are actually part of a font’s glyph set. If the iterable is left empty, no glyph name in glyph set checking takes place, and all glyph tokens containing hyphens are treated as literal glyph names, not as ranges. (Adding a space around the hyphen can, in any case, help to disambiguate ranges from glyph names containing hyphens.)

By default, the parser will follow include() statements in the feature file. To turn this off, pass followIncludes=False. Pass a directory string as includeDir to explicitly declare a directory to search included feature files in.

extensions = {}
ast = <module 'fontTools.feaLib.ast' from '/usr/lib/python3/dist-packages/fontTools/feaLib/ast.py'>
SS_FEATURE_TAGS = {'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'ss07', 'ss08', 'ss09', 'ss10', 'ss11', 'ss12', 'ss13', 'ss14', 'ss15', 'ss16', 'ss17', 'ss18', 'ss19', 'ss20'}
CV_FEATURE_TAGS = {'cv01', 'cv02', 'cv03', 'cv04', 'cv05', 'cv06', 'cv07', 'cv08', 'cv09', 'cv10', 'cv11', 'cv12', 'cv13', 'cv14', 'cv15', 'cv16', 'cv17', 'cv18', 'cv19', 'cv20', 'cv21', 'cv22', 'cv23', 'cv24', 'cv25', 'cv26', 'cv27', 'cv28', 'cv29', 'cv30', 'cv31', 'cv32', 'cv33', 'cv34', 'cv35', 'cv36', 'cv37', 'cv38', 'cv39', 'cv40', 'cv41', 'cv42', 'cv43', 'cv44', 'cv45', 'cv46', 'cv47', 'cv48', 'cv49', 'cv50', 'cv51', 'cv52', 'cv53', 'cv54', 'cv55', 'cv56', 'cv57', 'cv58', 'cv59', 'cv60', 'cv61', 'cv62', 'cv63', 'cv64', 'cv65', 'cv66', 'cv67', 'cv68', 'cv69', 'cv70', 'cv71', 'cv72', 'cv73', 'cv74', 'cv75', 'cv76', 'cv77', 'cv78', 'cv79', 'cv80', 'cv81', 'cv82', 'cv83', 'cv84', 'cv85', 'cv86', 'cv87', 'cv88', 'cv89', 'cv90', 'cv91', 'cv92', 'cv93', 'cv94', 'cv95', 'cv96', 'cv97', 'cv98', 'cv99'}
parse()[source]

Parse the file, and return a fontTools.feaLib.ast.FeatureFile object representing the root of the abstract syntax tree containing the parsed contents of the file.

parse_anchor_()[source]
parse_anchor_marks_()[source]
parse_anchordef_()[source]
parse_anonymous_()[source]
parse_attach_()[source]
parse_enumerate_(vertical)[source]
parse_GlyphClassDef_()[source]
parse_glyphclass_definition_()[source]
split_glyph_range_(name, location)[source]
parse_glyphclass_(accept_glyphname, accept_null=False)[source]
parse_glyph_pattern_(vertical)[source]
parse_ignore_glyph_pattern_(sub)[source]
parse_ignore_context_(sub)[source]
parse_ignore_()[source]
parse_include_()[source]
parse_language_()[source]
parse_ligatureCaretByIndex_()[source]
parse_ligatureCaretByPos_()[source]
parse_lookup_(vertical)[source]
parse_lookupflag_()[source]
parse_markClass_()[source]
parse_position_(enumerated, vertical)[source]
parse_position_cursive_(enumerated, vertical)[source]
parse_position_base_(enumerated, vertical)[source]
parse_position_ligature_(enumerated, vertical)[source]
parse_position_mark_(enumerated, vertical)[source]
parse_script_()[source]
parse_substitute_()[source]
parse_subtable_()[source]
parse_size_parameters_()[source]
parse_size_menuname_()[source]
parse_table_()[source]
parse_table_GDEF_(table)[source]
parse_table_head_(table)[source]
parse_table_hhea_(table)[source]
parse_table_vhea_(table)[source]
parse_table_name_(table)[source]
parse_name_()[source]

Parses a name record. See section 9.e.

parse_stat_name_()[source]
parse_nameid_()[source]
unescape_string_(string, encoding)[source]
static unescape_unichr_(match)[source]
static unescape_byte_(match, encoding)[source]
find_previous(statements, class_)[source]
parse_table_BASE_(table)[source]
parse_table_OS_2_(table)[source]
parse_STAT_ElidedFallbackName()[source]
parse_STAT_design_axis()[source]
parse_STAT_axis_value_()[source]
parse_STAT_location()[source]
parse_table_STAT_(table)[source]
parse_base_tag_list_()[source]
parse_base_script_list_(count)[source]
parse_base_script_record_(count)[source]
parse_base_minmax_()[source]
parse_device_()[source]
is_next_value_()[source]
parse_valuerecord_(vertical)[source]
parse_valuerecord_definition_(vertical)[source]
parse_languagesystem_()[source]
parse_feature_block_(variation=False)[source]
parse_feature_reference_()[source]
parse_featureNames_(tag)[source]

Parses a featureNames statement found in stylistic set features. See section 8.c.

parse_cvParameters_(tag)[source]
parse_cvNameIDs_(tag, block_name)[source]
parse_cvCharacter_(tag)[source]
parse_FontRevision_()[source]
parse_conditionset_()[source]
parse_block_(block, vertical, stylisticset=None, size_feature=False, cv_feature=None)[source]
is_cur_keyword_(k)[source]
expect_class_name_()[source]
expect_cid_()[source]
expect_filename_()[source]
expect_glyph_()[source]
check_glyph_name_in_glyph_set(*names)[source]

Adds a glyph name (just start) or glyph names of a range (start and end) which are not in the glyph set to the “missing list” for future error reporting.

If no glyph set is present, does nothing.

expect_markClass_reference_()[source]
expect_tag_()[source]
expect_script_tag_()[source]
expect_language_tag_()[source]
expect_symbol_(symbol)[source]
expect_keyword_(keyword)[source]
expect_name_()[source]
expect_number_(variable=False)[source]
expect_variable_scalar_()[source]
expect_master_()[source]
expect_any_number_()[source]
expect_float_()[source]
expect_integer_or_float_()[source]
expect_decipoint_()[source]
expect_stat_flags()[source]
expect_stat_values_()[source]
expect_string_()[source]
advance_lexer_(comments=False)[source]
static reverse_string_(s)[source]

‘abc’ –> ‘cba’

make_cid_range_(location, start, limit)[source]

(location, 999, 1001) –> [“cid00999”, “cid01000”, “cid01001”]

make_glyph_range_(location, start, limit)[source]

(location, “a.sc”, “d.sc”) –> [“a.sc”, “b.sc”, “c.sc”, “d.sc”]

class fontTools.feaLib.parser.SymbolTable[source]

Bases: object

enter_scope()[source]
exit_scope()[source]
define(name, item)[source]
resolve(name)[source]

Lexing

class fontTools.feaLib.lexer.IncludingLexer(featurefile, *, includeDir=None)

Bases: object

A Lexer that follows include statements.

The OpenType feature file specification states that due to historical reasons, relative imports should be resolved in this order:

  1. If the source font is UFO format, then relative to the UFO’s font directory

  2. relative to the top-level include file

  3. relative to the parent include file

We only support 1 (via includeDir) and 2.

static make_lexer_(file_or_path)
next(self)
scan_anonymous_block(self, tag)
class fontTools.feaLib.lexer.Lexer(text, filename)

Bases: object

ANONYMOUS_BLOCK = 'ANONYMOUS_BLOCK'
CHAR_DIGIT_ = '0123456789'
CHAR_HEXDIGIT_ = '0123456789ABCDEFabcdef'
CHAR_LETTER_ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
CHAR_NAME_CONTINUATION_ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.+*:^~!/-'
CHAR_NAME_START_ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_+*:.^~!\\'
CHAR_NEWLINE_ = '\r\n'
CHAR_SYMBOL_ = ",;:-+'{}[]<>()="
CHAR_WHITESPACE_ = ' \t'
CID = 'CID'
COMMENT = 'COMMENT'
FILENAME = 'FILENAME'
FLOAT = 'FLOAT'
GLYPHCLASS = 'GLYPHCLASS'
HEXADECIMAL = 'HEXADECIMAL'
MODE_FILENAME_ = 'FILENAME'
MODE_NORMAL_ = 'NORMAL'
NAME = 'NAME'
NEWLINE = 'NEWLINE'
NUMBER = 'NUMBER'
NUMBERS = ('NUMBER', 'HEXADECIMAL', 'OCTAL')
OCTAL = 'OCTAL'
RE_GLYPHCLASS = re.compile('^[A-Za-z_0-9.\\-]+$')
STRING = 'STRING'
SYMBOL = 'SYMBOL'
location_(self)
next(self)
next_(self)
scan_anonymous_block(self, tag)
scan_over_(self, valid)
scan_until_(self, stop_at)
class fontTools.feaLib.lexer.NonIncludingLexer(featurefile, *, includeDir=None)

Bases: IncludingLexer

Lexer that does not follow include statements, emits them as-is.

fontTools.feaLib.variableScalar

fontTools.feaLib.variableScalar.LocationTuple

A hashable location.

alias of tuple[tuple[str, float], …]

fontTools.feaLib.variableScalar.Location(location: Mapping[str, float]) tuple[tuple[str, float], ...][source]

Create a hashable location from a dictionary-like location.

class fontTools.feaLib.variableScalar.VariableScalar(location_value=None)[source]

Bases: object

A scalar with different values at different points in the designspace.

values: dict[tuple[tuple[str, float], ...], int]

The values across various user-locations. Must always include the default location by time of building.

property does_vary: bool
add_value(location: Mapping[str, float], value: int)[source]
add_to_variation_store(store_builder, model_cache=None, avar=None)[source]

Deprecated: use VariableScalarBuilder.add_to_variation_store() instead.

class fontTools.feaLib.variableScalar.VariableScalarBuilder(axis_triples: dict[str, tuple[float, float, float]], axis_mappings: dict[str, Mapping[float, float]], model_cache: dict[tuple[tuple[tuple[str, float], ...], ...], VariationModel])[source]

Bases: object

A helper class for building variable scalars, or otherwise interrogating their variation model for interpolation or similar.

axis_triples: dict[str, tuple[float, float, float]]

Minimum, default, and maximum for each axis in user-coordinates.

axis_mappings: dict[str, Mapping[float, float]]

Optional mappings from normalized user-coordinates to normalized design-coordinates.

model_cache: dict[tuple[tuple[tuple[str, float], ...], ...], VariationModel]

We often use the same exact locations (i.e. font sources) for a large number of variable scalars. Instead of creating a model for each, cache them. Cache by user-location to avoid repeated mapping computations.

classmethod from_ttf(ttf: TTFont) Self[source]
classmethod from_designspace(doc: DesignSpaceDocument) Self[source]
default_value(scalar: VariableScalar) int[source]

Get the default value of a variable scalar.

value_at_location(scalar: VariableScalar, location: tuple[tuple[str, float], ...]) float[source]

Interpolate the value of a scalar from a user-location.

model(scalar: VariableScalar) VariationModel[source]

Return a variation model based on a scalar’s values.

Variable scalars with the same fully-specified user-locations will use the same cached variation model.

get_deltas_and_supports(scalar: VariableScalar)[source]

Calculate deltas and supports from this scalar’s variation model.

add_to_variation_store(scalar: VariableScalar, store_builder) tuple[int, int][source]

Serialize this scalar’s variation model to a store, returning the default value and variation index.

fontTools.feaLib.location

class fontTools.feaLib.location.FeatureLibLocation(file: str, line: int, column: int)[source]

Bases: NamedTuple

A location in a feature file

file: str

Alias for field number 0

line: int

Alias for field number 1

column: int

Alias for field number 2