[feaLib] Implement GPOS type 3: Cursive Attachment Positioning
This commit is contained in:
parent
2fb1cc138c
commit
c6ee46f299
@ -102,6 +102,10 @@ class CursiveAttachmentPositioning(Statement):
|
||||
self.glyphclass = glyphclass
|
||||
self.entryAnchor, self.exitAnchor = entryAnchor, exitAnchor
|
||||
|
||||
def build(self, builder):
|
||||
builder.add_cursive_attachment_pos(
|
||||
self.location, self.glyphclass, self.entryAnchor, self.exitAnchor)
|
||||
|
||||
|
||||
class LanguageStatement(Statement):
|
||||
def __init__(self, location, language, include_default, required):
|
||||
|
@ -310,6 +310,14 @@ class Builder(object):
|
||||
location)
|
||||
lookup.mapping[from_glyph] = to_glyph
|
||||
|
||||
def add_cursive_attachment_pos(self, location, glyphclass,
|
||||
entryAnchor, exitAnchor):
|
||||
lookup = self.get_lookup_(location, CursiveAttachmentPosBuilder)
|
||||
lookup.add_attachment(
|
||||
location, glyphclass,
|
||||
makeOpenTypeAnchor(entryAnchor, otTables.EntryAnchor),
|
||||
makeOpenTypeAnchor(exitAnchor, otTables.ExitAnchor))
|
||||
|
||||
def add_pair_pos(self, location, enumerated,
|
||||
glyphclass1, value1, glyphclass2, value2):
|
||||
lookup = self.get_lookup_(location, PairPosBuilder)
|
||||
@ -350,6 +358,27 @@ def _makeOpenTypeDeviceTable(deviceTable, device):
|
||||
deviceTable.DeltaFormat = 3
|
||||
|
||||
|
||||
def makeOpenTypeAnchor(anchor, anchorClass):
|
||||
"""ast.Anchor --> otTables.Anchor"""
|
||||
if anchor is None:
|
||||
return None
|
||||
anch = anchorClass()
|
||||
anch.Format = 1
|
||||
anch.XCoordinate, anch.YCoordinate = anchor.x, anchor.y
|
||||
if anchor.contourpoint is not None:
|
||||
anch.AnchorPoint = anchor.contourpoint
|
||||
anch.Format = 2
|
||||
if anchor.xDeviceTable is not None:
|
||||
anch.XDeviceTable = otTables.XDeviceTable()
|
||||
_makeOpenTypeDeviceTable(anch.XDeviceTable, anchor.xDeviceTable)
|
||||
anch.Format = 3
|
||||
if anchor.yDeviceTable is not None:
|
||||
anch.YDeviceTable = otTables.YDeviceTable()
|
||||
_makeOpenTypeDeviceTable(anch.YDeviceTable, anchor.yDeviceTable)
|
||||
anch.Format = 3
|
||||
return anch
|
||||
|
||||
|
||||
def makeOpenTypeValueRecord(v):
|
||||
"""ast.ValueRecord --> (otBase.ValueRecord, int ValueFormat)"""
|
||||
if v is None:
|
||||
@ -611,6 +640,42 @@ class PairPosBuilder(LookupBuilder):
|
||||
return lookup
|
||||
|
||||
|
||||
class CursiveAttachmentPosBuilder(LookupBuilder):
|
||||
def __init__(self, font, location, lookup_flag):
|
||||
LookupBuilder.__init__(self, font, location, 'GPOS', 3, lookup_flag)
|
||||
self.attachments = {}
|
||||
|
||||
def equals(self, other):
|
||||
return (LookupBuilder.equals(self, other) and
|
||||
self.attachments == other.attachments)
|
||||
|
||||
def add_attachment(self, location, glyphs, entryAnchor, exitAnchor):
|
||||
for glyph in glyphs:
|
||||
self.attachments[glyph] = (location, entryAnchor, exitAnchor)
|
||||
|
||||
def build(self):
|
||||
st = otTables.CursivePos()
|
||||
st.Format = 1
|
||||
st.Coverage = otTables.Coverage()
|
||||
st.Coverage.glyphs = \
|
||||
sorted(self.attachments.keys(), key=self.font.getGlyphID)
|
||||
st.EntryExitCount = len(self.attachments)
|
||||
st.EntryExitRecord = []
|
||||
for glyph in st.Coverage.glyphs:
|
||||
location, entryAnchor, exitAnchor = self.attachments[glyph]
|
||||
rec = otTables.EntryExitRecord()
|
||||
st.EntryExitRecord.append(rec)
|
||||
rec.EntryAnchor = entryAnchor
|
||||
rec.ExitAnchor = exitAnchor
|
||||
subtables = [st]
|
||||
lookup = otTables.Lookup()
|
||||
lookup.SubTable = subtables
|
||||
lookup.LookupFlag = self.lookup_flag
|
||||
lookup.LookupType = self.lookup_type
|
||||
lookup.SubTableCount = len(lookup.SubTable)
|
||||
return lookup
|
||||
|
||||
|
||||
class ReverseChainSingleSubstBuilder(LookupBuilder):
|
||||
def __init__(self, font, location, lookup_flag):
|
||||
LookupBuilder.__init__(self, font, location, 'GSUB', 8, lookup_flag)
|
||||
|
@ -156,15 +156,11 @@ class BuilderTest(unittest.TestCase):
|
||||
"Already defined different position for glyph \"A\"",
|
||||
self.build, "feature test { pos A 123; pos A 456; } test;")
|
||||
|
||||
def test_GPOS_type1(self):
|
||||
font = makeTTFont()
|
||||
addOpenTypeFeatures(self.getpath("GPOS_1.fea"), font)
|
||||
self.expect_ttx(font, self.getpath("GPOS_1.ttx"))
|
||||
|
||||
def test_GPOS_type2(self):
|
||||
font = makeTTFont()
|
||||
addOpenTypeFeatures(self.getpath("GPOS_2.fea"), font)
|
||||
self.expect_ttx(font, self.getpath("GPOS_2.ttx"))
|
||||
def test_GPOS(self):
|
||||
for name in "1 2 3".split():
|
||||
font = makeTTFont()
|
||||
addOpenTypeFeatures(self.getpath("GPOS_%s.fea" % name), font)
|
||||
self.expect_ttx(font, self.getpath("GPOS_%s.ttx" % name))
|
||||
|
||||
def test_spec4h1(self):
|
||||
# OpenType Feature File specification, section 4.h, example 1.
|
||||
|
14
Lib/fontTools/feaLib/testdata/GPOS_3.fea
vendored
Normal file
14
Lib/fontTools/feaLib/testdata/GPOS_3.fea
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
languagesystem DFLT dflt;
|
||||
|
||||
anchorDef 3 4 contourpoint 2 ANCH342;
|
||||
|
||||
feature kern {
|
||||
pos cursive zero <anchor NULL> <anchor NULL>;
|
||||
pos cursive one <anchor 121 -1> <anchor ANCH342>;
|
||||
pos cursive two <anchor 122 -2> <anchor 3 4>;
|
||||
pos cursive three <anchor 123 -3> <anchor NULL>;
|
||||
pos cursive four <anchor 124 -4 contourpoint 7> <anchor 3 4>;
|
||||
pos cursive five
|
||||
<anchor 124 -4 <device 8 1, 9 2> <device 7 3>>
|
||||
<anchor ANCH342>;
|
||||
} kern;
|
114
Lib/fontTools/feaLib/testdata/GPOS_3.ttx
vendored
Normal file
114
Lib/fontTools/feaLib/testdata/GPOS_3.ttx
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont>
|
||||
|
||||
<GPOS>
|
||||
<Version value="1.0"/>
|
||||
<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=3 -->
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<CursivePos index="0" Format="1">
|
||||
<Coverage>
|
||||
<Glyph value="zero"/>
|
||||
<Glyph value="one"/>
|
||||
<Glyph value="two"/>
|
||||
<Glyph value="three"/>
|
||||
<Glyph value="four"/>
|
||||
<Glyph value="five"/>
|
||||
</Coverage>
|
||||
<!-- EntryExitCount=6 -->
|
||||
<EntryExitRecord index="0">
|
||||
</EntryExitRecord>
|
||||
<EntryExitRecord index="1">
|
||||
<EntryAnchor Format="1">
|
||||
<XCoordinate value="121"/>
|
||||
<YCoordinate value="-1"/>
|
||||
</EntryAnchor>
|
||||
<ExitAnchor Format="2">
|
||||
<XCoordinate value="3"/>
|
||||
<YCoordinate value="4"/>
|
||||
<AnchorPoint value="2"/>
|
||||
</ExitAnchor>
|
||||
</EntryExitRecord>
|
||||
<EntryExitRecord index="2">
|
||||
<EntryAnchor Format="1">
|
||||
<XCoordinate value="122"/>
|
||||
<YCoordinate value="-2"/>
|
||||
</EntryAnchor>
|
||||
<ExitAnchor Format="1">
|
||||
<XCoordinate value="3"/>
|
||||
<YCoordinate value="4"/>
|
||||
</ExitAnchor>
|
||||
</EntryExitRecord>
|
||||
<EntryExitRecord index="3">
|
||||
<EntryAnchor Format="1">
|
||||
<XCoordinate value="123"/>
|
||||
<YCoordinate value="-3"/>
|
||||
</EntryAnchor>
|
||||
</EntryExitRecord>
|
||||
<EntryExitRecord index="4">
|
||||
<EntryAnchor Format="2">
|
||||
<XCoordinate value="124"/>
|
||||
<YCoordinate value="-4"/>
|
||||
<AnchorPoint value="7"/>
|
||||
</EntryAnchor>
|
||||
<ExitAnchor Format="1">
|
||||
<XCoordinate value="3"/>
|
||||
<YCoordinate value="4"/>
|
||||
</ExitAnchor>
|
||||
</EntryExitRecord>
|
||||
<EntryExitRecord index="5">
|
||||
<EntryAnchor Format="3">
|
||||
<XCoordinate value="124"/>
|
||||
<YCoordinate value="-4"/>
|
||||
<XDeviceTable>
|
||||
<StartSize value="8"/>
|
||||
<EndSize value="9"/>
|
||||
<DeltaFormat value="2"/>
|
||||
<DeltaValue value="[1, 2]"/>
|
||||
</XDeviceTable>
|
||||
<YDeviceTable>
|
||||
<StartSize value="7"/>
|
||||
<EndSize value="7"/>
|
||||
<DeltaFormat value="2"/>
|
||||
<DeltaValue value="[3]"/>
|
||||
</YDeviceTable>
|
||||
</EntryAnchor>
|
||||
<ExitAnchor Format="2">
|
||||
<XCoordinate value="3"/>
|
||||
<YCoordinate value="4"/>
|
||||
<AnchorPoint value="2"/>
|
||||
</ExitAnchor>
|
||||
</EntryExitRecord>
|
||||
</CursivePos>
|
||||
</Lookup>
|
||||
</LookupList>
|
||||
</GPOS>
|
||||
|
||||
</ttFont>
|
Loading…
x
Reference in New Issue
Block a user