[feaLib] Implement table head with FontRevision statement
This commit is contained in:
parent
2de6fc7744
commit
38f4ee7908
@ -302,6 +302,15 @@ class IgnoreSubstitutionRule(Statement):
|
||||
self.prefix, self.glyphs, self.suffix = (prefix, glyphs, suffix)
|
||||
|
||||
|
||||
class FontRevisionStatement(Statement):
|
||||
def __init__(self, location, revision):
|
||||
Statement.__init__(self, location)
|
||||
self.revision = revision
|
||||
|
||||
def build(self, builder):
|
||||
builder.set_font_revision(self.location, self.revision)
|
||||
|
||||
|
||||
class LigatureCaretByIndexStatement(Statement):
|
||||
def __init__(self, location, glyphs, carets):
|
||||
Statement.__init__(self, location)
|
||||
|
@ -33,6 +33,8 @@ class Builder(object):
|
||||
# for feature 'aalt'
|
||||
self.aalt_features_ = [] # [(location, featureName)*], for 'aalt'
|
||||
self.aalt_location_ = None
|
||||
# for table 'head'
|
||||
self.fontRevision_ = None # 2.71
|
||||
# for table 'GDEF'
|
||||
self.attachPoints_ = {} # "a" --> {3, 7}
|
||||
self.ligatureCaretByIndex_ = {} # "f_f_i" --> {3, 7}
|
||||
@ -46,6 +48,7 @@ class Builder(object):
|
||||
self.parseTree = Parser(self.featurefile_path).parse()
|
||||
self.parseTree.build(self)
|
||||
self.build_feature_aalt_()
|
||||
self.build_head()
|
||||
for tag in ('GPOS', 'GSUB'):
|
||||
table = self.makeTable(tag)
|
||||
if (table.ScriptList.ScriptCount > 0 or
|
||||
@ -142,6 +145,16 @@ class Builder(object):
|
||||
self.end_feature()
|
||||
self.lookups_.extend(old_lookups)
|
||||
|
||||
def build_head(self):
|
||||
if not self.fontRevision_:
|
||||
return
|
||||
table = self.font.get("head")
|
||||
if not table: # this only happens for unit tests
|
||||
table = self.font["head"] = getTableClass("head")()
|
||||
table.decompile(b"\0" * 54, self.font)
|
||||
table.tableVersion = 1.0
|
||||
table.fontRevision = self.fontRevision_
|
||||
|
||||
def buildGDEF(self):
|
||||
gdef = otTables.GDEF()
|
||||
gdef.GlyphClassDef = self.buildGDEFGlyphClassDef_()
|
||||
@ -408,6 +421,9 @@ class Builder(object):
|
||||
lookup = self.named_lookups_[lookup_name]
|
||||
self.add_lookup_to_feature_(lookup, self.cur_feature_name_)
|
||||
|
||||
def set_font_revision(self, location, revision):
|
||||
self.fontRevision_ = revision
|
||||
|
||||
def set_language(self, location, language, include_default, required):
|
||||
assert(len(language) == 4)
|
||||
if self.cur_lookup_name_:
|
||||
|
@ -80,7 +80,7 @@ class BuilderTest(unittest.TestCase):
|
||||
|
||||
def expect_ttx(self, font, expected_ttx):
|
||||
path = self.temp_path(suffix=".ttx")
|
||||
font.saveXML(path, quiet=True, tables=['GDEF', 'GSUB', 'GPOS'])
|
||||
font.saveXML(path, quiet=True, tables=['head', 'GDEF', 'GSUB', 'GPOS'])
|
||||
actual = self.read_ttx(path)
|
||||
expected = self.read_ttx(expected_ttx)
|
||||
if actual != expected:
|
||||
@ -168,7 +168,7 @@ class BuilderTest(unittest.TestCase):
|
||||
|
||||
def test_spec(self):
|
||||
for name in ("4h1 5d1 5d2 5fi1 5fi2 5fi3 5fi4 5h1 "
|
||||
"6d2 6e 6f 6h_ii 8a 9b").split():
|
||||
"6d2 6e 6f 6h_ii 8a 9b 9c1 9c2 9c3").split():
|
||||
font = makeTTFont()
|
||||
addOpenTypeFeatures(self.getpath("spec%s.fea" % name), font)
|
||||
self.expect_ttx(font, self.getpath("spec%s.ttx" % name))
|
||||
|
@ -8,6 +8,7 @@ import os
|
||||
|
||||
class Lexer(object):
|
||||
NUMBER = "NUMBER"
|
||||
FLOAT = "FLOAT"
|
||||
STRING = "STRING"
|
||||
NAME = "NAME"
|
||||
FILENAME = "FILENAME"
|
||||
@ -123,11 +124,19 @@ class Lexer(object):
|
||||
return (Lexer.NUMBER, int(text[start:self.pos_], 16), location)
|
||||
if cur_char in Lexer.CHAR_DIGIT_:
|
||||
self.scan_over_(Lexer.CHAR_DIGIT_)
|
||||
return (Lexer.NUMBER, int(text[start:self.pos_], 10), location)
|
||||
if next_char != ".":
|
||||
return (Lexer.NUMBER, int(text[start:self.pos_], 10), location)
|
||||
self.scan_over_(".")
|
||||
self.scan_over_(Lexer.CHAR_DIGIT_)
|
||||
return (Lexer.FLOAT, float(text[start:self.pos_]), location)
|
||||
if cur_char == "-" and next_char in Lexer.CHAR_DIGIT_:
|
||||
self.pos_ += 1
|
||||
self.scan_over_(Lexer.CHAR_DIGIT_)
|
||||
return (Lexer.NUMBER, int(text[start:self.pos_], 10), location)
|
||||
if self.pos_ >= limit or text[self.pos_] != ".":
|
||||
return (Lexer.NUMBER, int(text[start:self.pos_], 10), location)
|
||||
self.scan_over_(".")
|
||||
self.scan_over_(Lexer.CHAR_DIGIT_)
|
||||
return (Lexer.FLOAT, float(text[start:self.pos_]), location)
|
||||
if cur_char in Lexer.CHAR_SYMBOL_:
|
||||
self.pos_ += 1
|
||||
return (Lexer.SYMBOL, cur_char, location)
|
||||
|
@ -66,6 +66,10 @@ class LexerTest(unittest.TestCase):
|
||||
self.assertEqual(lex("0xCAFED00D"), [(Lexer.NUMBER, 0xCAFED00D)])
|
||||
self.assertEqual(lex("0xcafed00d"), [(Lexer.NUMBER, 0xCAFED00D)])
|
||||
|
||||
def test_float(self):
|
||||
self.assertEqual(lex("1.23 -4.5"),
|
||||
[(Lexer.FLOAT, 1.23), (Lexer.FLOAT, -4.5)])
|
||||
|
||||
def test_symbol(self):
|
||||
self.assertEqual(lex("a'"), [(Lexer.NAME, "a"), (Lexer.SYMBOL, "'")])
|
||||
self.assertEqual(
|
||||
|
@ -628,6 +628,7 @@ class Parser(object):
|
||||
self.expect_symbol_("{")
|
||||
handler = {
|
||||
"GDEF": self.parse_table_GDEF_,
|
||||
"head": self.parse_table_head_,
|
||||
}.get(name)
|
||||
if handler:
|
||||
handler(table)
|
||||
@ -660,6 +661,16 @@ class Parser(object):
|
||||
"or LigatureCaretByPos",
|
||||
self.cur_token_location_)
|
||||
|
||||
def parse_table_head_(self, table):
|
||||
statements = table.statements
|
||||
while self.next_token_ != "}":
|
||||
self.advance_lexer_()
|
||||
if self.is_cur_keyword_("FontRevision"):
|
||||
statements.append(self.parse_FontRevision_())
|
||||
else:
|
||||
raise FeatureLibError("Expected FontRevision",
|
||||
self.cur_token_location_)
|
||||
|
||||
def parse_device_(self):
|
||||
result = None
|
||||
self.expect_symbol_("<")
|
||||
@ -772,6 +783,15 @@ class Parser(object):
|
||||
self.expect_symbol_(";")
|
||||
return ast.FeatureReferenceStatement(location, featureName)
|
||||
|
||||
def parse_FontRevision_(self):
|
||||
assert self.cur_token_ == "FontRevision", self.cur_token_
|
||||
location, version = self.cur_token_location_, self.expect_float_()
|
||||
self.expect_symbol_(";")
|
||||
if version <= 0:
|
||||
raise FeatureLibError("Font revision numbers must be positive",
|
||||
location)
|
||||
return ast.FontRevisionStatement(location, version)
|
||||
|
||||
def parse_block_(self, block, vertical):
|
||||
self.expect_symbol_("{")
|
||||
for symtab in self.symbol_tables_:
|
||||
@ -901,6 +921,13 @@ class Parser(object):
|
||||
return self.cur_token_
|
||||
raise FeatureLibError("Expected a number", self.cur_token_location_)
|
||||
|
||||
def expect_float_(self):
|
||||
self.advance_lexer_()
|
||||
if self.cur_token_type_ is Lexer.FLOAT:
|
||||
return self.cur_token_
|
||||
raise FeatureLibError("Expected a floating-point number",
|
||||
self.cur_token_location_)
|
||||
|
||||
def advance_lexer_(self):
|
||||
self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
|
||||
self.next_token_type_, self.next_token_, self.next_token_location_)
|
||||
|
@ -138,6 +138,17 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertIsInstance(ref, ast.FeatureReferenceStatement)
|
||||
self.assertEqual(ref.featureName, "salt")
|
||||
|
||||
def test_FontRevision(self):
|
||||
doc = self.parse("table head {FontRevision 2.5;} head;")
|
||||
s = doc.statements[0].statements[0]
|
||||
self.assertIsInstance(s, ast.FontRevisionStatement)
|
||||
self.assertEqual(s.revision, 2.5)
|
||||
|
||||
def test_FontRevision_negative(self):
|
||||
self.assertRaisesRegex(
|
||||
FeatureLibError, "Font revision numbers must be positive",
|
||||
self.parse, "table head {FontRevision -17.2;} head;")
|
||||
|
||||
def test_glyphclass(self):
|
||||
[gc] = self.parse("@dash = [endash emdash figuredash];").statements
|
||||
self.assertEqual(gc.name, "dash")
|
||||
|
3
Lib/fontTools/feaLib/testdata/spec9c1.fea
vendored
Normal file
3
Lib/fontTools/feaLib/testdata/spec9c1.fea
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
table head {
|
||||
FontRevision 1.1;
|
||||
} head;
|
25
Lib/fontTools/feaLib/testdata/spec9c1.ttx
vendored
Normal file
25
Lib/fontTools/feaLib/testdata/spec9c1.ttx
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont>
|
||||
|
||||
<head>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="1.0"/>
|
||||
<fontRevision value="1.1"/>
|
||||
<checkSumAdjustment value="0x0"/>
|
||||
<magicNumber value="0x0"/>
|
||||
<flags value="00000000 00000000"/>
|
||||
<unitsPerEm value="0"/>
|
||||
<created value="Thu Jan 1 00:00:00 1970"/>
|
||||
<modified value="Thu Jan 1 00:00:00 1970"/>
|
||||
<xMin value="0"/>
|
||||
<yMin value="0"/>
|
||||
<xMax value="0"/>
|
||||
<yMax value="0"/>
|
||||
<macStyle value="00000000 00000000"/>
|
||||
<lowestRecPPEM value="0"/>
|
||||
<fontDirectionHint value="0"/>
|
||||
<indexToLocFormat value="0"/>
|
||||
<glyphDataFormat value="0"/>
|
||||
</head>
|
||||
|
||||
</ttFont>
|
3
Lib/fontTools/feaLib/testdata/spec9c2.fea
vendored
Normal file
3
Lib/fontTools/feaLib/testdata/spec9c2.fea
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
table head {
|
||||
FontRevision 1.001;
|
||||
} head;
|
25
Lib/fontTools/feaLib/testdata/spec9c2.ttx
vendored
Normal file
25
Lib/fontTools/feaLib/testdata/spec9c2.ttx
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont>
|
||||
|
||||
<head>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="1.0"/>
|
||||
<fontRevision value="1.001"/>
|
||||
<checkSumAdjustment value="0x0"/>
|
||||
<magicNumber value="0x0"/>
|
||||
<flags value="00000000 00000000"/>
|
||||
<unitsPerEm value="0"/>
|
||||
<created value="Thu Jan 1 00:00:00 1970"/>
|
||||
<modified value="Thu Jan 1 00:00:00 1970"/>
|
||||
<xMin value="0"/>
|
||||
<yMin value="0"/>
|
||||
<xMax value="0"/>
|
||||
<yMax value="0"/>
|
||||
<macStyle value="00000000 00000000"/>
|
||||
<lowestRecPPEM value="0"/>
|
||||
<fontDirectionHint value="0"/>
|
||||
<indexToLocFormat value="0"/>
|
||||
<glyphDataFormat value="0"/>
|
||||
</head>
|
||||
|
||||
</ttFont>
|
3
Lib/fontTools/feaLib/testdata/spec9c3.fea
vendored
Normal file
3
Lib/fontTools/feaLib/testdata/spec9c3.fea
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
table head {
|
||||
FontRevision 1.500;
|
||||
} head;
|
25
Lib/fontTools/feaLib/testdata/spec9c3.ttx
vendored
Normal file
25
Lib/fontTools/feaLib/testdata/spec9c3.ttx
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont>
|
||||
|
||||
<head>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="1.0"/>
|
||||
<fontRevision value="1.5"/>
|
||||
<checkSumAdjustment value="0x0"/>
|
||||
<magicNumber value="0x0"/>
|
||||
<flags value="00000000 00000000"/>
|
||||
<unitsPerEm value="0"/>
|
||||
<created value="Thu Jan 1 00:00:00 1970"/>
|
||||
<modified value="Thu Jan 1 00:00:00 1970"/>
|
||||
<xMin value="0"/>
|
||||
<yMin value="0"/>
|
||||
<xMax value="0"/>
|
||||
<yMax value="0"/>
|
||||
<macStyle value="00000000 00000000"/>
|
||||
<lowestRecPPEM value="0"/>
|
||||
<fontDirectionHint value="0"/>
|
||||
<indexToLocFormat value="0"/>
|
||||
<glyphDataFormat value="0"/>
|
||||
</head>
|
||||
|
||||
</ttFont>
|
Loading…
x
Reference in New Issue
Block a user