Merge pull request #1460 from anthrotype/fix-null-valuerecord1-format-a

[feaLib] distinguish missing value and explicit '<NULL>' for PairPos2 format A
This commit is contained in:
Cosimo Lupo 2019-01-19 15:08:56 +00:00 committed by GitHub
commit c0f4f21e0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 6 deletions

View File

@ -1115,6 +1115,9 @@ class ValueRecord(Expression):
hash(self.xAdvDevice) ^ hash(self.yAdvDevice)) hash(self.xAdvDevice) ^ hash(self.yAdvDevice))
def makeString(self, vertical=None): def makeString(self, vertical=None):
if not self:
return "<NULL>"
x, y = self.xPlacement, self.yPlacement x, y = self.xPlacement, self.yPlacement
xAdvance, yAdvance = self.xAdvance, self.yAdvance xAdvance, yAdvance = self.xAdvance, self.yAdvance
xPlaDevice, yPlaDevice = self.xPlaDevice, self.yPlaDevice xPlaDevice, yPlaDevice = self.xPlaDevice, self.yPlaDevice
@ -1140,6 +1143,23 @@ class ValueRecord(Expression):
deviceToString(xPlaDevice), deviceToString(yPlaDevice), deviceToString(xPlaDevice), deviceToString(yPlaDevice),
deviceToString(xAdvDevice), deviceToString(yAdvDevice)) deviceToString(xAdvDevice), deviceToString(yAdvDevice))
def __bool__(self):
return any(
getattr(self, v) is not None
for v in [
"xPlacement",
"yPlacement",
"xAdvance",
"yAdvance",
"xPlaDevice",
"yPlaDevice",
"xAdvDevice",
"yAdvDevice",
]
)
__nonzero__ = __bool__
class ValueRecordDefinition(Statement): class ValueRecordDefinition(Statement):
def __init__(self, name, value, location=None): def __init__(self, name, value, location=None):

View File

@ -1114,7 +1114,7 @@ _VALUEREC_ATTRS = {
def makeOpenTypeValueRecord(v, pairPosContext): def makeOpenTypeValueRecord(v, pairPosContext):
"""ast.ValueRecord --> (otBase.ValueRecord, int ValueFormat)""" """ast.ValueRecord --> (otBase.ValueRecord, int ValueFormat)"""
if v is None: if not v:
return None, 0 return None, 0
vr = {} vr = {}

View File

@ -1153,7 +1153,7 @@ class Parser(object):
name = self.expect_name_() name = self.expect_name_()
if name == "NULL": if name == "NULL":
self.expect_symbol_(">") self.expect_symbol_(">")
return None return self.ast.ValueRecord()
vrd = self.valuerecords_.resolve(name) vrd = self.valuerecords_.resolve(name)
if vrd is None: if vrd is None:
raise FeatureLibError("Unknown valueRecordDef \"%s\"" % name, raise FeatureLibError("Unknown valueRecordDef \"%s\"" % name,

View File

@ -65,7 +65,7 @@ class BuilderTest(unittest.TestCase):
spec9a spec9b spec9c1 spec9c2 spec9c3 spec9d spec9e spec9f spec9g spec9a spec9b spec9c1 spec9c2 spec9c3 spec9d spec9e spec9f spec9g
spec10 spec10
bug453 bug457 bug463 bug501 bug502 bug504 bug505 bug506 bug509 bug453 bug457 bug463 bug501 bug502 bug504 bug505 bug506 bug509
bug512 bug514 bug568 bug633 bug1307 bug512 bug514 bug568 bug633 bug1307 bug1459
name size size2 multiple_feature_blocks omitted_GlyphClassDef name size size2 multiple_feature_blocks omitted_GlyphClassDef
ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical
ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical

View File

@ -0,0 +1,7 @@
# A pair position lookup where only the second glyph has a non-empty valuerecord
# while the first glyph has a NULL valuerecord. The ValueFormat1 for the first
# glyph is expected to be 0.
# https://github.com/fonttools/fonttools/issues/1459
feature kern {
pos A <NULL> V <-180 0 -90 0>;
} kern;

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<GPOS>
<Version value="0x00010000"/>
<ScriptList>
<!-- ScriptCount=1 -->
<ScriptRecord index="0">
<ScriptTag value="DFLT"/>
<Script>
<DefaultLangSys>
<ReqFeatureIndex value="65535"/>
<!-- FeatureCount=1 -->
<FeatureIndex index="0" value="0"/>
</DefaultLangSys>
<!-- LangSysCount=0 -->
</Script>
</ScriptRecord>
</ScriptList>
<FeatureList>
<!-- FeatureCount=1 -->
<FeatureRecord index="0">
<FeatureTag value="kern"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="0"/>
</Feature>
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=1 -->
<Lookup index="0">
<LookupType value="2"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<PairPos index="0" Format="1">
<Coverage>
<Glyph value="A"/>
</Coverage>
<ValueFormat1 value="0"/>
<ValueFormat2 value="5"/>
<!-- PairSetCount=1 -->
<PairSet index="0">
<!-- PairValueCount=1 -->
<PairValueRecord index="0">
<SecondGlyph value="V"/>
<Value2 XPlacement="-180" XAdvance="-90"/>
</PairValueRecord>
</PairSet>
</PairPos>
</Lookup>
</LookupList>
</GPOS>
</ttFont>

View File

@ -800,7 +800,22 @@ class ParserTest(unittest.TestCase):
self.assertEqual(pos.valuerecord2.makeString(vertical=False), self.assertEqual(pos.valuerecord2.makeString(vertical=False),
"<1 2 3 4>") "<1 2 3 4>")
def test_gpos_type_2_format_a_with_null(self): def test_gpos_type_2_format_a_with_null_first(self):
doc = self.parse("feature kern {"
" pos [T V] <NULL> [a b c] <1 2 3 4>;"
"} kern;")
pos = doc.statements[0].statements[0]
self.assertEqual(type(pos), ast.PairPosStatement)
self.assertFalse(pos.enumerated)
self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
self.assertFalse(pos.valuerecord1)
self.assertEqual(pos.valuerecord1.makeString(), "<NULL>")
self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
self.assertEqual(pos.valuerecord2.makeString(vertical=False),
"<1 2 3 4>")
self.assertEqual(pos.asFea(), "pos [T V] <NULL> [a b c] <1 2 3 4>;")
def test_gpos_type_2_format_a_with_null_second(self):
doc = self.parse("feature kern {" doc = self.parse("feature kern {"
" pos [T V] <1 2 3 4> [a b c] <NULL>;" " pos [T V] <1 2 3 4> [a b c] <NULL>;"
"} kern;") "} kern;")
@ -811,7 +826,8 @@ class ParserTest(unittest.TestCase):
self.assertEqual(pos.valuerecord1.makeString(vertical=False), self.assertEqual(pos.valuerecord1.makeString(vertical=False),
"<1 2 3 4>") "<1 2 3 4>")
self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]") self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
self.assertIsNone(pos.valuerecord2) self.assertFalse(pos.valuerecord2)
self.assertEqual(pos.asFea(), "pos [T V] [a b c] <1 2 3 4>;")
def test_gpos_type_2_format_b(self): def test_gpos_type_2_format_b(self):
doc = self.parse("feature kern {" doc = self.parse("feature kern {"
@ -1523,7 +1539,8 @@ class ParserTest(unittest.TestCase):
def test_valuerecord_format_d(self): def test_valuerecord_format_d(self):
doc = self.parse("feature test {valueRecordDef <NULL> foo;} test;") doc = self.parse("feature test {valueRecordDef <NULL> foo;} test;")
value = doc.statements[0].statements[0].value value = doc.statements[0].statements[0].value
self.assertIsNone(value) self.assertFalse(value)
self.assertEqual(value.makeString(), "<NULL>")
def test_valuerecord_named(self): def test_valuerecord_named(self):
doc = self.parse("valueRecordDef <1 2 3 4> foo;" doc = self.parse("valueRecordDef <1 2 3 4> foo;"