1*e1fe3e4aSElliott Hughesimport io 2*e1fe3e4aSElliott Hughesimport struct 3*e1fe3e4aSElliott Hughesfrom fontTools.misc.fixedTools import floatToFixed, fixedToFloat 4*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import getXML 5*e1fe3e4aSElliott Hughesfrom fontTools.otlLib import builder, error 6*e1fe3e4aSElliott Hughesfrom fontTools import ttLib 7*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables import otTables 8*e1fe3e4aSElliott Hughesimport pytest 9*e1fe3e4aSElliott Hughes 10*e1fe3e4aSElliott Hughes 11*e1fe3e4aSElliott Hughesclass BuilderTest(object): 12*e1fe3e4aSElliott Hughes GLYPHS = ( 13*e1fe3e4aSElliott Hughes ".notdef space zero one two three four five six " 14*e1fe3e4aSElliott Hughes "A B C a b c grave acute cedilla f_f_i f_i c_t" 15*e1fe3e4aSElliott Hughes ).split() 16*e1fe3e4aSElliott Hughes GLYPHMAP = {name: num for num, name in enumerate(GLYPHS)} 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughes ANCHOR1 = builder.buildAnchor(11, -11) 19*e1fe3e4aSElliott Hughes ANCHOR2 = builder.buildAnchor(22, -22) 20*e1fe3e4aSElliott Hughes ANCHOR3 = builder.buildAnchor(33, -33) 21*e1fe3e4aSElliott Hughes 22*e1fe3e4aSElliott Hughes def test_buildAnchor_format1(self): 23*e1fe3e4aSElliott Hughes anchor = builder.buildAnchor(23, 42) 24*e1fe3e4aSElliott Hughes assert getXML(anchor.toXML) == [ 25*e1fe3e4aSElliott Hughes '<Anchor Format="1">', 26*e1fe3e4aSElliott Hughes ' <XCoordinate value="23"/>', 27*e1fe3e4aSElliott Hughes ' <YCoordinate value="42"/>', 28*e1fe3e4aSElliott Hughes "</Anchor>", 29*e1fe3e4aSElliott Hughes ] 30*e1fe3e4aSElliott Hughes 31*e1fe3e4aSElliott Hughes def test_buildAnchor_format2(self): 32*e1fe3e4aSElliott Hughes anchor = builder.buildAnchor(23, 42, point=17) 33*e1fe3e4aSElliott Hughes assert getXML(anchor.toXML) == [ 34*e1fe3e4aSElliott Hughes '<Anchor Format="2">', 35*e1fe3e4aSElliott Hughes ' <XCoordinate value="23"/>', 36*e1fe3e4aSElliott Hughes ' <YCoordinate value="42"/>', 37*e1fe3e4aSElliott Hughes ' <AnchorPoint value="17"/>', 38*e1fe3e4aSElliott Hughes "</Anchor>", 39*e1fe3e4aSElliott Hughes ] 40*e1fe3e4aSElliott Hughes 41*e1fe3e4aSElliott Hughes def test_buildAnchor_format3(self): 42*e1fe3e4aSElliott Hughes anchor = builder.buildAnchor( 43*e1fe3e4aSElliott Hughes 23, 44*e1fe3e4aSElliott Hughes 42, 45*e1fe3e4aSElliott Hughes deviceX=builder.buildDevice({1: 1, 0: 0}), 46*e1fe3e4aSElliott Hughes deviceY=builder.buildDevice({7: 7}), 47*e1fe3e4aSElliott Hughes ) 48*e1fe3e4aSElliott Hughes assert getXML(anchor.toXML) == [ 49*e1fe3e4aSElliott Hughes '<Anchor Format="3">', 50*e1fe3e4aSElliott Hughes ' <XCoordinate value="23"/>', 51*e1fe3e4aSElliott Hughes ' <YCoordinate value="42"/>', 52*e1fe3e4aSElliott Hughes " <XDeviceTable>", 53*e1fe3e4aSElliott Hughes ' <StartSize value="0"/>', 54*e1fe3e4aSElliott Hughes ' <EndSize value="1"/>', 55*e1fe3e4aSElliott Hughes ' <DeltaFormat value="1"/>', 56*e1fe3e4aSElliott Hughes ' <DeltaValue value="[0, 1]"/>', 57*e1fe3e4aSElliott Hughes " </XDeviceTable>", 58*e1fe3e4aSElliott Hughes " <YDeviceTable>", 59*e1fe3e4aSElliott Hughes ' <StartSize value="7"/>', 60*e1fe3e4aSElliott Hughes ' <EndSize value="7"/>', 61*e1fe3e4aSElliott Hughes ' <DeltaFormat value="2"/>', 62*e1fe3e4aSElliott Hughes ' <DeltaValue value="[7]"/>', 63*e1fe3e4aSElliott Hughes " </YDeviceTable>", 64*e1fe3e4aSElliott Hughes "</Anchor>", 65*e1fe3e4aSElliott Hughes ] 66*e1fe3e4aSElliott Hughes 67*e1fe3e4aSElliott Hughes def test_buildAttachList(self): 68*e1fe3e4aSElliott Hughes attachList = builder.buildAttachList( 69*e1fe3e4aSElliott Hughes {"zero": [23, 7], "one": [1]}, self.GLYPHMAP 70*e1fe3e4aSElliott Hughes ) 71*e1fe3e4aSElliott Hughes assert getXML(attachList.toXML) == [ 72*e1fe3e4aSElliott Hughes "<AttachList>", 73*e1fe3e4aSElliott Hughes " <Coverage>", 74*e1fe3e4aSElliott Hughes ' <Glyph value="zero"/>', 75*e1fe3e4aSElliott Hughes ' <Glyph value="one"/>', 76*e1fe3e4aSElliott Hughes " </Coverage>", 77*e1fe3e4aSElliott Hughes " <!-- GlyphCount=2 -->", 78*e1fe3e4aSElliott Hughes ' <AttachPoint index="0">', 79*e1fe3e4aSElliott Hughes " <!-- PointCount=2 -->", 80*e1fe3e4aSElliott Hughes ' <PointIndex index="0" value="7"/>', 81*e1fe3e4aSElliott Hughes ' <PointIndex index="1" value="23"/>', 82*e1fe3e4aSElliott Hughes " </AttachPoint>", 83*e1fe3e4aSElliott Hughes ' <AttachPoint index="1">', 84*e1fe3e4aSElliott Hughes " <!-- PointCount=1 -->", 85*e1fe3e4aSElliott Hughes ' <PointIndex index="0" value="1"/>', 86*e1fe3e4aSElliott Hughes " </AttachPoint>", 87*e1fe3e4aSElliott Hughes "</AttachList>", 88*e1fe3e4aSElliott Hughes ] 89*e1fe3e4aSElliott Hughes 90*e1fe3e4aSElliott Hughes def test_buildAttachList_empty(self): 91*e1fe3e4aSElliott Hughes assert builder.buildAttachList({}, self.GLYPHMAP) is None 92*e1fe3e4aSElliott Hughes 93*e1fe3e4aSElliott Hughes def test_buildAttachPoint(self): 94*e1fe3e4aSElliott Hughes attachPoint = builder.buildAttachPoint([7, 3]) 95*e1fe3e4aSElliott Hughes assert getXML(attachPoint.toXML) == [ 96*e1fe3e4aSElliott Hughes "<AttachPoint>", 97*e1fe3e4aSElliott Hughes " <!-- PointCount=2 -->", 98*e1fe3e4aSElliott Hughes ' <PointIndex index="0" value="3"/>', 99*e1fe3e4aSElliott Hughes ' <PointIndex index="1" value="7"/>', 100*e1fe3e4aSElliott Hughes "</AttachPoint>", 101*e1fe3e4aSElliott Hughes ] 102*e1fe3e4aSElliott Hughes 103*e1fe3e4aSElliott Hughes def test_buildAttachPoint_empty(self): 104*e1fe3e4aSElliott Hughes assert builder.buildAttachPoint([]) is None 105*e1fe3e4aSElliott Hughes 106*e1fe3e4aSElliott Hughes def test_buildAttachPoint_duplicate(self): 107*e1fe3e4aSElliott Hughes attachPoint = builder.buildAttachPoint([7, 3, 7]) 108*e1fe3e4aSElliott Hughes assert getXML(attachPoint.toXML) == [ 109*e1fe3e4aSElliott Hughes "<AttachPoint>", 110*e1fe3e4aSElliott Hughes " <!-- PointCount=2 -->", 111*e1fe3e4aSElliott Hughes ' <PointIndex index="0" value="3"/>', 112*e1fe3e4aSElliott Hughes ' <PointIndex index="1" value="7"/>', 113*e1fe3e4aSElliott Hughes "</AttachPoint>", 114*e1fe3e4aSElliott Hughes ] 115*e1fe3e4aSElliott Hughes 116*e1fe3e4aSElliott Hughes def test_buildBaseArray(self): 117*e1fe3e4aSElliott Hughes anchor = builder.buildAnchor 118*e1fe3e4aSElliott Hughes baseArray = builder.buildBaseArray( 119*e1fe3e4aSElliott Hughes {"a": {2: anchor(300, 80)}, "c": {1: anchor(300, 80), 2: anchor(300, -20)}}, 120*e1fe3e4aSElliott Hughes numMarkClasses=4, 121*e1fe3e4aSElliott Hughes glyphMap=self.GLYPHMAP, 122*e1fe3e4aSElliott Hughes ) 123*e1fe3e4aSElliott Hughes assert getXML(baseArray.toXML) == [ 124*e1fe3e4aSElliott Hughes "<BaseArray>", 125*e1fe3e4aSElliott Hughes " <!-- BaseCount=2 -->", 126*e1fe3e4aSElliott Hughes ' <BaseRecord index="0">', 127*e1fe3e4aSElliott Hughes ' <BaseAnchor index="0" empty="1"/>', 128*e1fe3e4aSElliott Hughes ' <BaseAnchor index="1" empty="1"/>', 129*e1fe3e4aSElliott Hughes ' <BaseAnchor index="2" Format="1">', 130*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 131*e1fe3e4aSElliott Hughes ' <YCoordinate value="80"/>', 132*e1fe3e4aSElliott Hughes " </BaseAnchor>", 133*e1fe3e4aSElliott Hughes ' <BaseAnchor index="3" empty="1"/>', 134*e1fe3e4aSElliott Hughes " </BaseRecord>", 135*e1fe3e4aSElliott Hughes ' <BaseRecord index="1">', 136*e1fe3e4aSElliott Hughes ' <BaseAnchor index="0" empty="1"/>', 137*e1fe3e4aSElliott Hughes ' <BaseAnchor index="1" Format="1">', 138*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 139*e1fe3e4aSElliott Hughes ' <YCoordinate value="80"/>', 140*e1fe3e4aSElliott Hughes " </BaseAnchor>", 141*e1fe3e4aSElliott Hughes ' <BaseAnchor index="2" Format="1">', 142*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 143*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 144*e1fe3e4aSElliott Hughes " </BaseAnchor>", 145*e1fe3e4aSElliott Hughes ' <BaseAnchor index="3" empty="1"/>', 146*e1fe3e4aSElliott Hughes " </BaseRecord>", 147*e1fe3e4aSElliott Hughes "</BaseArray>", 148*e1fe3e4aSElliott Hughes ] 149*e1fe3e4aSElliott Hughes 150*e1fe3e4aSElliott Hughes def test_buildBaseRecord(self): 151*e1fe3e4aSElliott Hughes a = builder.buildAnchor 152*e1fe3e4aSElliott Hughes rec = builder.buildBaseRecord([a(500, -20), None, a(300, -15)]) 153*e1fe3e4aSElliott Hughes assert getXML(rec.toXML) == [ 154*e1fe3e4aSElliott Hughes "<BaseRecord>", 155*e1fe3e4aSElliott Hughes ' <BaseAnchor index="0" Format="1">', 156*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 157*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 158*e1fe3e4aSElliott Hughes " </BaseAnchor>", 159*e1fe3e4aSElliott Hughes ' <BaseAnchor index="1" empty="1"/>', 160*e1fe3e4aSElliott Hughes ' <BaseAnchor index="2" Format="1">', 161*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 162*e1fe3e4aSElliott Hughes ' <YCoordinate value="-15"/>', 163*e1fe3e4aSElliott Hughes " </BaseAnchor>", 164*e1fe3e4aSElliott Hughes "</BaseRecord>", 165*e1fe3e4aSElliott Hughes ] 166*e1fe3e4aSElliott Hughes 167*e1fe3e4aSElliott Hughes def test_buildCaretValueForCoord(self): 168*e1fe3e4aSElliott Hughes caret = builder.buildCaretValueForCoord(500) 169*e1fe3e4aSElliott Hughes assert getXML(caret.toXML) == [ 170*e1fe3e4aSElliott Hughes '<CaretValue Format="1">', 171*e1fe3e4aSElliott Hughes ' <Coordinate value="500"/>', 172*e1fe3e4aSElliott Hughes "</CaretValue>", 173*e1fe3e4aSElliott Hughes ] 174*e1fe3e4aSElliott Hughes 175*e1fe3e4aSElliott Hughes def test_buildCaretValueForPoint(self): 176*e1fe3e4aSElliott Hughes caret = builder.buildCaretValueForPoint(23) 177*e1fe3e4aSElliott Hughes assert getXML(caret.toXML) == [ 178*e1fe3e4aSElliott Hughes '<CaretValue Format="2">', 179*e1fe3e4aSElliott Hughes ' <CaretValuePoint value="23"/>', 180*e1fe3e4aSElliott Hughes "</CaretValue>", 181*e1fe3e4aSElliott Hughes ] 182*e1fe3e4aSElliott Hughes 183*e1fe3e4aSElliott Hughes def test_buildComponentRecord(self): 184*e1fe3e4aSElliott Hughes a = builder.buildAnchor 185*e1fe3e4aSElliott Hughes rec = builder.buildComponentRecord([a(500, -20), None, a(300, -15)]) 186*e1fe3e4aSElliott Hughes assert getXML(rec.toXML) == [ 187*e1fe3e4aSElliott Hughes "<ComponentRecord>", 188*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" Format="1">', 189*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 190*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 191*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 192*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" empty="1"/>', 193*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="2" Format="1">', 194*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 195*e1fe3e4aSElliott Hughes ' <YCoordinate value="-15"/>', 196*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 197*e1fe3e4aSElliott Hughes "</ComponentRecord>", 198*e1fe3e4aSElliott Hughes ] 199*e1fe3e4aSElliott Hughes 200*e1fe3e4aSElliott Hughes def test_buildComponentRecord_empty(self): 201*e1fe3e4aSElliott Hughes assert builder.buildComponentRecord([]) is None 202*e1fe3e4aSElliott Hughes 203*e1fe3e4aSElliott Hughes def test_buildComponentRecord_None(self): 204*e1fe3e4aSElliott Hughes assert builder.buildComponentRecord(None) is None 205*e1fe3e4aSElliott Hughes 206*e1fe3e4aSElliott Hughes def test_buildCoverage(self): 207*e1fe3e4aSElliott Hughes cov = builder.buildCoverage(("two", "four", "two"), {"two": 2, "four": 4}) 208*e1fe3e4aSElliott Hughes assert getXML(cov.toXML) == [ 209*e1fe3e4aSElliott Hughes "<Coverage>", 210*e1fe3e4aSElliott Hughes ' <Glyph value="two"/>', 211*e1fe3e4aSElliott Hughes ' <Glyph value="four"/>', 212*e1fe3e4aSElliott Hughes "</Coverage>", 213*e1fe3e4aSElliott Hughes ] 214*e1fe3e4aSElliott Hughes 215*e1fe3e4aSElliott Hughes def test_buildCursivePos(self): 216*e1fe3e4aSElliott Hughes pos = builder.buildCursivePosSubtable( 217*e1fe3e4aSElliott Hughes {"two": (self.ANCHOR1, self.ANCHOR2), "four": (self.ANCHOR3, self.ANCHOR1)}, 218*e1fe3e4aSElliott Hughes self.GLYPHMAP, 219*e1fe3e4aSElliott Hughes ) 220*e1fe3e4aSElliott Hughes assert getXML(pos.toXML) == [ 221*e1fe3e4aSElliott Hughes '<CursivePos Format="1">', 222*e1fe3e4aSElliott Hughes " <Coverage>", 223*e1fe3e4aSElliott Hughes ' <Glyph value="two"/>', 224*e1fe3e4aSElliott Hughes ' <Glyph value="four"/>', 225*e1fe3e4aSElliott Hughes " </Coverage>", 226*e1fe3e4aSElliott Hughes " <!-- EntryExitCount=2 -->", 227*e1fe3e4aSElliott Hughes ' <EntryExitRecord index="0">', 228*e1fe3e4aSElliott Hughes ' <EntryAnchor Format="1">', 229*e1fe3e4aSElliott Hughes ' <XCoordinate value="11"/>', 230*e1fe3e4aSElliott Hughes ' <YCoordinate value="-11"/>', 231*e1fe3e4aSElliott Hughes " </EntryAnchor>", 232*e1fe3e4aSElliott Hughes ' <ExitAnchor Format="1">', 233*e1fe3e4aSElliott Hughes ' <XCoordinate value="22"/>', 234*e1fe3e4aSElliott Hughes ' <YCoordinate value="-22"/>', 235*e1fe3e4aSElliott Hughes " </ExitAnchor>", 236*e1fe3e4aSElliott Hughes " </EntryExitRecord>", 237*e1fe3e4aSElliott Hughes ' <EntryExitRecord index="1">', 238*e1fe3e4aSElliott Hughes ' <EntryAnchor Format="1">', 239*e1fe3e4aSElliott Hughes ' <XCoordinate value="33"/>', 240*e1fe3e4aSElliott Hughes ' <YCoordinate value="-33"/>', 241*e1fe3e4aSElliott Hughes " </EntryAnchor>", 242*e1fe3e4aSElliott Hughes ' <ExitAnchor Format="1">', 243*e1fe3e4aSElliott Hughes ' <XCoordinate value="11"/>', 244*e1fe3e4aSElliott Hughes ' <YCoordinate value="-11"/>', 245*e1fe3e4aSElliott Hughes " </ExitAnchor>", 246*e1fe3e4aSElliott Hughes " </EntryExitRecord>", 247*e1fe3e4aSElliott Hughes "</CursivePos>", 248*e1fe3e4aSElliott Hughes ] 249*e1fe3e4aSElliott Hughes 250*e1fe3e4aSElliott Hughes def test_buildDevice_format1(self): 251*e1fe3e4aSElliott Hughes device = builder.buildDevice({1: 1, 0: 0}) 252*e1fe3e4aSElliott Hughes assert getXML(device.toXML) == [ 253*e1fe3e4aSElliott Hughes "<Device>", 254*e1fe3e4aSElliott Hughes ' <StartSize value="0"/>', 255*e1fe3e4aSElliott Hughes ' <EndSize value="1"/>', 256*e1fe3e4aSElliott Hughes ' <DeltaFormat value="1"/>', 257*e1fe3e4aSElliott Hughes ' <DeltaValue value="[0, 1]"/>', 258*e1fe3e4aSElliott Hughes "</Device>", 259*e1fe3e4aSElliott Hughes ] 260*e1fe3e4aSElliott Hughes 261*e1fe3e4aSElliott Hughes def test_buildDevice_format2(self): 262*e1fe3e4aSElliott Hughes device = builder.buildDevice({2: 2, 0: 1, 1: 0}) 263*e1fe3e4aSElliott Hughes assert getXML(device.toXML) == [ 264*e1fe3e4aSElliott Hughes "<Device>", 265*e1fe3e4aSElliott Hughes ' <StartSize value="0"/>', 266*e1fe3e4aSElliott Hughes ' <EndSize value="2"/>', 267*e1fe3e4aSElliott Hughes ' <DeltaFormat value="2"/>', 268*e1fe3e4aSElliott Hughes ' <DeltaValue value="[1, 0, 2]"/>', 269*e1fe3e4aSElliott Hughes "</Device>", 270*e1fe3e4aSElliott Hughes ] 271*e1fe3e4aSElliott Hughes 272*e1fe3e4aSElliott Hughes def test_buildDevice_format3(self): 273*e1fe3e4aSElliott Hughes device = builder.buildDevice({5: 3, 1: 77}) 274*e1fe3e4aSElliott Hughes assert getXML(device.toXML) == [ 275*e1fe3e4aSElliott Hughes "<Device>", 276*e1fe3e4aSElliott Hughes ' <StartSize value="1"/>', 277*e1fe3e4aSElliott Hughes ' <EndSize value="5"/>', 278*e1fe3e4aSElliott Hughes ' <DeltaFormat value="3"/>', 279*e1fe3e4aSElliott Hughes ' <DeltaValue value="[77, 0, 0, 0, 3]"/>', 280*e1fe3e4aSElliott Hughes "</Device>", 281*e1fe3e4aSElliott Hughes ] 282*e1fe3e4aSElliott Hughes 283*e1fe3e4aSElliott Hughes def test_buildLigatureArray(self): 284*e1fe3e4aSElliott Hughes anchor = builder.buildAnchor 285*e1fe3e4aSElliott Hughes ligatureArray = builder.buildLigatureArray( 286*e1fe3e4aSElliott Hughes { 287*e1fe3e4aSElliott Hughes "f_i": [{2: anchor(300, -20)}, {}], 288*e1fe3e4aSElliott Hughes "c_t": [{}, {1: anchor(500, 350), 2: anchor(1300, -20)}], 289*e1fe3e4aSElliott Hughes }, 290*e1fe3e4aSElliott Hughes numMarkClasses=4, 291*e1fe3e4aSElliott Hughes glyphMap=self.GLYPHMAP, 292*e1fe3e4aSElliott Hughes ) 293*e1fe3e4aSElliott Hughes assert getXML(ligatureArray.toXML) == [ 294*e1fe3e4aSElliott Hughes "<LigatureArray>", 295*e1fe3e4aSElliott Hughes " <!-- LigatureCount=2 -->", 296*e1fe3e4aSElliott Hughes ' <LigatureAttach index="0">', # f_i 297*e1fe3e4aSElliott Hughes " <!-- ComponentCount=2 -->", 298*e1fe3e4aSElliott Hughes ' <ComponentRecord index="0">', 299*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" empty="1"/>', 300*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" empty="1"/>', 301*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="2" Format="1">', 302*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 303*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 304*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 305*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="3" empty="1"/>', 306*e1fe3e4aSElliott Hughes " </ComponentRecord>", 307*e1fe3e4aSElliott Hughes ' <ComponentRecord index="1">', 308*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" empty="1"/>', 309*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" empty="1"/>', 310*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="2" empty="1"/>', 311*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="3" empty="1"/>', 312*e1fe3e4aSElliott Hughes " </ComponentRecord>", 313*e1fe3e4aSElliott Hughes " </LigatureAttach>", 314*e1fe3e4aSElliott Hughes ' <LigatureAttach index="1">', 315*e1fe3e4aSElliott Hughes " <!-- ComponentCount=2 -->", 316*e1fe3e4aSElliott Hughes ' <ComponentRecord index="0">', 317*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" empty="1"/>', 318*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" empty="1"/>', 319*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="2" empty="1"/>', 320*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="3" empty="1"/>', 321*e1fe3e4aSElliott Hughes " </ComponentRecord>", 322*e1fe3e4aSElliott Hughes ' <ComponentRecord index="1">', 323*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" empty="1"/>', 324*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" Format="1">', 325*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 326*e1fe3e4aSElliott Hughes ' <YCoordinate value="350"/>', 327*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 328*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="2" Format="1">', 329*e1fe3e4aSElliott Hughes ' <XCoordinate value="1300"/>', 330*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 331*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 332*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="3" empty="1"/>', 333*e1fe3e4aSElliott Hughes " </ComponentRecord>", 334*e1fe3e4aSElliott Hughes " </LigatureAttach>", 335*e1fe3e4aSElliott Hughes "</LigatureArray>", 336*e1fe3e4aSElliott Hughes ] 337*e1fe3e4aSElliott Hughes 338*e1fe3e4aSElliott Hughes def test_buildLigatureAttach(self): 339*e1fe3e4aSElliott Hughes anchor = builder.buildAnchor 340*e1fe3e4aSElliott Hughes attach = builder.buildLigatureAttach( 341*e1fe3e4aSElliott Hughes [[anchor(500, -10), None], [None, anchor(300, -20), None]] 342*e1fe3e4aSElliott Hughes ) 343*e1fe3e4aSElliott Hughes assert getXML(attach.toXML) == [ 344*e1fe3e4aSElliott Hughes "<LigatureAttach>", 345*e1fe3e4aSElliott Hughes " <!-- ComponentCount=2 -->", 346*e1fe3e4aSElliott Hughes ' <ComponentRecord index="0">', 347*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" Format="1">', 348*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 349*e1fe3e4aSElliott Hughes ' <YCoordinate value="-10"/>', 350*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 351*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" empty="1"/>', 352*e1fe3e4aSElliott Hughes " </ComponentRecord>", 353*e1fe3e4aSElliott Hughes ' <ComponentRecord index="1">', 354*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" empty="1"/>', 355*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" Format="1">', 356*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 357*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 358*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 359*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="2" empty="1"/>', 360*e1fe3e4aSElliott Hughes " </ComponentRecord>", 361*e1fe3e4aSElliott Hughes "</LigatureAttach>", 362*e1fe3e4aSElliott Hughes ] 363*e1fe3e4aSElliott Hughes 364*e1fe3e4aSElliott Hughes def test_buildLigatureAttach_emptyComponents(self): 365*e1fe3e4aSElliott Hughes attach = builder.buildLigatureAttach([[], None]) 366*e1fe3e4aSElliott Hughes assert getXML(attach.toXML) == [ 367*e1fe3e4aSElliott Hughes "<LigatureAttach>", 368*e1fe3e4aSElliott Hughes " <!-- ComponentCount=2 -->", 369*e1fe3e4aSElliott Hughes ' <ComponentRecord index="0" empty="1"/>', 370*e1fe3e4aSElliott Hughes ' <ComponentRecord index="1" empty="1"/>', 371*e1fe3e4aSElliott Hughes "</LigatureAttach>", 372*e1fe3e4aSElliott Hughes ] 373*e1fe3e4aSElliott Hughes 374*e1fe3e4aSElliott Hughes def test_buildLigatureAttach_noComponents(self): 375*e1fe3e4aSElliott Hughes attach = builder.buildLigatureAttach([]) 376*e1fe3e4aSElliott Hughes assert getXML(attach.toXML) == [ 377*e1fe3e4aSElliott Hughes "<LigatureAttach>", 378*e1fe3e4aSElliott Hughes " <!-- ComponentCount=0 -->", 379*e1fe3e4aSElliott Hughes "</LigatureAttach>", 380*e1fe3e4aSElliott Hughes ] 381*e1fe3e4aSElliott Hughes 382*e1fe3e4aSElliott Hughes def test_buildLigCaretList(self): 383*e1fe3e4aSElliott Hughes carets = builder.buildLigCaretList( 384*e1fe3e4aSElliott Hughes {"f_f_i": [300, 600]}, {"c_t": [42]}, self.GLYPHMAP 385*e1fe3e4aSElliott Hughes ) 386*e1fe3e4aSElliott Hughes assert getXML(carets.toXML) == [ 387*e1fe3e4aSElliott Hughes "<LigCaretList>", 388*e1fe3e4aSElliott Hughes " <Coverage>", 389*e1fe3e4aSElliott Hughes ' <Glyph value="f_f_i"/>', 390*e1fe3e4aSElliott Hughes ' <Glyph value="c_t"/>', 391*e1fe3e4aSElliott Hughes " </Coverage>", 392*e1fe3e4aSElliott Hughes " <!-- LigGlyphCount=2 -->", 393*e1fe3e4aSElliott Hughes ' <LigGlyph index="0">', 394*e1fe3e4aSElliott Hughes " <!-- CaretCount=2 -->", 395*e1fe3e4aSElliott Hughes ' <CaretValue index="0" Format="1">', 396*e1fe3e4aSElliott Hughes ' <Coordinate value="300"/>', 397*e1fe3e4aSElliott Hughes " </CaretValue>", 398*e1fe3e4aSElliott Hughes ' <CaretValue index="1" Format="1">', 399*e1fe3e4aSElliott Hughes ' <Coordinate value="600"/>', 400*e1fe3e4aSElliott Hughes " </CaretValue>", 401*e1fe3e4aSElliott Hughes " </LigGlyph>", 402*e1fe3e4aSElliott Hughes ' <LigGlyph index="1">', 403*e1fe3e4aSElliott Hughes " <!-- CaretCount=1 -->", 404*e1fe3e4aSElliott Hughes ' <CaretValue index="0" Format="2">', 405*e1fe3e4aSElliott Hughes ' <CaretValuePoint value="42"/>', 406*e1fe3e4aSElliott Hughes " </CaretValue>", 407*e1fe3e4aSElliott Hughes " </LigGlyph>", 408*e1fe3e4aSElliott Hughes "</LigCaretList>", 409*e1fe3e4aSElliott Hughes ] 410*e1fe3e4aSElliott Hughes 411*e1fe3e4aSElliott Hughes def test_buildLigCaretList_bothCoordsAndPointsForSameGlyph(self): 412*e1fe3e4aSElliott Hughes carets = builder.buildLigCaretList( 413*e1fe3e4aSElliott Hughes {"f_f_i": [300]}, {"f_f_i": [7]}, self.GLYPHMAP 414*e1fe3e4aSElliott Hughes ) 415*e1fe3e4aSElliott Hughes assert getXML(carets.toXML) == [ 416*e1fe3e4aSElliott Hughes "<LigCaretList>", 417*e1fe3e4aSElliott Hughes " <Coverage>", 418*e1fe3e4aSElliott Hughes ' <Glyph value="f_f_i"/>', 419*e1fe3e4aSElliott Hughes " </Coverage>", 420*e1fe3e4aSElliott Hughes " <!-- LigGlyphCount=1 -->", 421*e1fe3e4aSElliott Hughes ' <LigGlyph index="0">', 422*e1fe3e4aSElliott Hughes " <!-- CaretCount=2 -->", 423*e1fe3e4aSElliott Hughes ' <CaretValue index="0" Format="1">', 424*e1fe3e4aSElliott Hughes ' <Coordinate value="300"/>', 425*e1fe3e4aSElliott Hughes " </CaretValue>", 426*e1fe3e4aSElliott Hughes ' <CaretValue index="1" Format="2">', 427*e1fe3e4aSElliott Hughes ' <CaretValuePoint value="7"/>', 428*e1fe3e4aSElliott Hughes " </CaretValue>", 429*e1fe3e4aSElliott Hughes " </LigGlyph>", 430*e1fe3e4aSElliott Hughes "</LigCaretList>", 431*e1fe3e4aSElliott Hughes ] 432*e1fe3e4aSElliott Hughes 433*e1fe3e4aSElliott Hughes def test_buildLigCaretList_empty(self): 434*e1fe3e4aSElliott Hughes assert builder.buildLigCaretList({}, {}, self.GLYPHMAP) is None 435*e1fe3e4aSElliott Hughes 436*e1fe3e4aSElliott Hughes def test_buildLigCaretList_None(self): 437*e1fe3e4aSElliott Hughes assert builder.buildLigCaretList(None, None, self.GLYPHMAP) is None 438*e1fe3e4aSElliott Hughes 439*e1fe3e4aSElliott Hughes def test_buildLigGlyph_coords(self): 440*e1fe3e4aSElliott Hughes lig = builder.buildLigGlyph([500, 800], None) 441*e1fe3e4aSElliott Hughes assert getXML(lig.toXML) == [ 442*e1fe3e4aSElliott Hughes "<LigGlyph>", 443*e1fe3e4aSElliott Hughes " <!-- CaretCount=2 -->", 444*e1fe3e4aSElliott Hughes ' <CaretValue index="0" Format="1">', 445*e1fe3e4aSElliott Hughes ' <Coordinate value="500"/>', 446*e1fe3e4aSElliott Hughes " </CaretValue>", 447*e1fe3e4aSElliott Hughes ' <CaretValue index="1" Format="1">', 448*e1fe3e4aSElliott Hughes ' <Coordinate value="800"/>', 449*e1fe3e4aSElliott Hughes " </CaretValue>", 450*e1fe3e4aSElliott Hughes "</LigGlyph>", 451*e1fe3e4aSElliott Hughes ] 452*e1fe3e4aSElliott Hughes 453*e1fe3e4aSElliott Hughes def test_buildLigGlyph_empty(self): 454*e1fe3e4aSElliott Hughes assert builder.buildLigGlyph([], []) is None 455*e1fe3e4aSElliott Hughes 456*e1fe3e4aSElliott Hughes def test_buildLigGlyph_None(self): 457*e1fe3e4aSElliott Hughes assert builder.buildLigGlyph(None, None) is None 458*e1fe3e4aSElliott Hughes 459*e1fe3e4aSElliott Hughes def test_buildLigGlyph_points(self): 460*e1fe3e4aSElliott Hughes lig = builder.buildLigGlyph(None, [2]) 461*e1fe3e4aSElliott Hughes assert getXML(lig.toXML) == [ 462*e1fe3e4aSElliott Hughes "<LigGlyph>", 463*e1fe3e4aSElliott Hughes " <!-- CaretCount=1 -->", 464*e1fe3e4aSElliott Hughes ' <CaretValue index="0" Format="2">', 465*e1fe3e4aSElliott Hughes ' <CaretValuePoint value="2"/>', 466*e1fe3e4aSElliott Hughes " </CaretValue>", 467*e1fe3e4aSElliott Hughes "</LigGlyph>", 468*e1fe3e4aSElliott Hughes ] 469*e1fe3e4aSElliott Hughes 470*e1fe3e4aSElliott Hughes def test_buildLookup(self): 471*e1fe3e4aSElliott Hughes s1 = builder.buildSingleSubstSubtable({"one": "two"}) 472*e1fe3e4aSElliott Hughes s2 = builder.buildSingleSubstSubtable({"three": "four"}) 473*e1fe3e4aSElliott Hughes lookup = builder.buildLookup([s1, s2], flags=7) 474*e1fe3e4aSElliott Hughes assert getXML(lookup.toXML) == [ 475*e1fe3e4aSElliott Hughes "<Lookup>", 476*e1fe3e4aSElliott Hughes ' <LookupType value="1"/>', 477*e1fe3e4aSElliott Hughes ' <LookupFlag value="7"/><!-- rightToLeft ignoreBaseGlyphs ignoreLigatures -->', 478*e1fe3e4aSElliott Hughes " <!-- SubTableCount=2 -->", 479*e1fe3e4aSElliott Hughes ' <SingleSubst index="0">', 480*e1fe3e4aSElliott Hughes ' <Substitution in="one" out="two"/>', 481*e1fe3e4aSElliott Hughes " </SingleSubst>", 482*e1fe3e4aSElliott Hughes ' <SingleSubst index="1">', 483*e1fe3e4aSElliott Hughes ' <Substitution in="three" out="four"/>', 484*e1fe3e4aSElliott Hughes " </SingleSubst>", 485*e1fe3e4aSElliott Hughes "</Lookup>", 486*e1fe3e4aSElliott Hughes ] 487*e1fe3e4aSElliott Hughes 488*e1fe3e4aSElliott Hughes def test_buildLookup_badFlags(self): 489*e1fe3e4aSElliott Hughes s = builder.buildSingleSubstSubtable({"one": "two"}) 490*e1fe3e4aSElliott Hughes with pytest.raises( 491*e1fe3e4aSElliott Hughes AssertionError, 492*e1fe3e4aSElliott Hughes match=( 493*e1fe3e4aSElliott Hughes "if markFilterSet is None, flags must not set " 494*e1fe3e4aSElliott Hughes "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x0010" 495*e1fe3e4aSElliott Hughes ), 496*e1fe3e4aSElliott Hughes ) as excinfo: 497*e1fe3e4aSElliott Hughes builder.buildLookup([s], builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET, None) 498*e1fe3e4aSElliott Hughes 499*e1fe3e4aSElliott Hughes def test_buildLookup_conflictingSubtableTypes(self): 500*e1fe3e4aSElliott Hughes s1 = builder.buildSingleSubstSubtable({"one": "two"}) 501*e1fe3e4aSElliott Hughes s2 = builder.buildAlternateSubstSubtable({"one": ["two", "three"]}) 502*e1fe3e4aSElliott Hughes with pytest.raises( 503*e1fe3e4aSElliott Hughes AssertionError, match="all subtables must have the same LookupType" 504*e1fe3e4aSElliott Hughes ) as excinfo: 505*e1fe3e4aSElliott Hughes builder.buildLookup([s1, s2]) 506*e1fe3e4aSElliott Hughes 507*e1fe3e4aSElliott Hughes def test_buildLookup_noSubtables(self): 508*e1fe3e4aSElliott Hughes assert builder.buildLookup([]) is None 509*e1fe3e4aSElliott Hughes assert builder.buildLookup(None) is None 510*e1fe3e4aSElliott Hughes assert builder.buildLookup([None]) is None 511*e1fe3e4aSElliott Hughes assert builder.buildLookup([None, None]) is None 512*e1fe3e4aSElliott Hughes 513*e1fe3e4aSElliott Hughes def test_buildLookup_markFilterSet(self): 514*e1fe3e4aSElliott Hughes s = builder.buildSingleSubstSubtable({"one": "two"}) 515*e1fe3e4aSElliott Hughes flags = ( 516*e1fe3e4aSElliott Hughes builder.LOOKUP_FLAG_RIGHT_TO_LEFT 517*e1fe3e4aSElliott Hughes | builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET 518*e1fe3e4aSElliott Hughes ) 519*e1fe3e4aSElliott Hughes lookup = builder.buildLookup([s], flags, markFilterSet=999) 520*e1fe3e4aSElliott Hughes assert getXML(lookup.toXML) == [ 521*e1fe3e4aSElliott Hughes "<Lookup>", 522*e1fe3e4aSElliott Hughes ' <LookupType value="1"/>', 523*e1fe3e4aSElliott Hughes ' <LookupFlag value="17"/><!-- rightToLeft useMarkFilteringSet -->', 524*e1fe3e4aSElliott Hughes " <!-- SubTableCount=1 -->", 525*e1fe3e4aSElliott Hughes ' <SingleSubst index="0">', 526*e1fe3e4aSElliott Hughes ' <Substitution in="one" out="two"/>', 527*e1fe3e4aSElliott Hughes " </SingleSubst>", 528*e1fe3e4aSElliott Hughes ' <MarkFilteringSet value="999"/>', 529*e1fe3e4aSElliott Hughes "</Lookup>", 530*e1fe3e4aSElliott Hughes ] 531*e1fe3e4aSElliott Hughes 532*e1fe3e4aSElliott Hughes def test_buildMarkArray(self): 533*e1fe3e4aSElliott Hughes markArray = builder.buildMarkArray( 534*e1fe3e4aSElliott Hughes { 535*e1fe3e4aSElliott Hughes "acute": (7, builder.buildAnchor(300, 800)), 536*e1fe3e4aSElliott Hughes "grave": (2, builder.buildAnchor(10, 80)), 537*e1fe3e4aSElliott Hughes }, 538*e1fe3e4aSElliott Hughes self.GLYPHMAP, 539*e1fe3e4aSElliott Hughes ) 540*e1fe3e4aSElliott Hughes assert self.GLYPHMAP["grave"] < self.GLYPHMAP["acute"] 541*e1fe3e4aSElliott Hughes assert getXML(markArray.toXML) == [ 542*e1fe3e4aSElliott Hughes "<MarkArray>", 543*e1fe3e4aSElliott Hughes " <!-- MarkCount=2 -->", 544*e1fe3e4aSElliott Hughes ' <MarkRecord index="0">', 545*e1fe3e4aSElliott Hughes ' <Class value="2"/>', 546*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 547*e1fe3e4aSElliott Hughes ' <XCoordinate value="10"/>', 548*e1fe3e4aSElliott Hughes ' <YCoordinate value="80"/>', 549*e1fe3e4aSElliott Hughes " </MarkAnchor>", 550*e1fe3e4aSElliott Hughes " </MarkRecord>", 551*e1fe3e4aSElliott Hughes ' <MarkRecord index="1">', 552*e1fe3e4aSElliott Hughes ' <Class value="7"/>', 553*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 554*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 555*e1fe3e4aSElliott Hughes ' <YCoordinate value="800"/>', 556*e1fe3e4aSElliott Hughes " </MarkAnchor>", 557*e1fe3e4aSElliott Hughes " </MarkRecord>", 558*e1fe3e4aSElliott Hughes "</MarkArray>", 559*e1fe3e4aSElliott Hughes ] 560*e1fe3e4aSElliott Hughes 561*e1fe3e4aSElliott Hughes def test_buildMarkBasePosSubtable(self): 562*e1fe3e4aSElliott Hughes anchor = builder.buildAnchor 563*e1fe3e4aSElliott Hughes marks = { 564*e1fe3e4aSElliott Hughes "acute": (0, anchor(300, 700)), 565*e1fe3e4aSElliott Hughes "cedilla": (1, anchor(300, -100)), 566*e1fe3e4aSElliott Hughes "grave": (0, anchor(300, 700)), 567*e1fe3e4aSElliott Hughes } 568*e1fe3e4aSElliott Hughes bases = { 569*e1fe3e4aSElliott Hughes # Make sure we can handle missing entries. 570*e1fe3e4aSElliott Hughes "A": {}, # no entry for any markClass 571*e1fe3e4aSElliott Hughes "B": {0: anchor(500, 900)}, # only markClass 0 specified 572*e1fe3e4aSElliott Hughes "C": {1: anchor(500, -10)}, # only markClass 1 specified 573*e1fe3e4aSElliott Hughes "a": {0: anchor(500, 400), 1: anchor(500, -20)}, 574*e1fe3e4aSElliott Hughes "b": {0: anchor(500, 800), 1: anchor(500, -20)}, 575*e1fe3e4aSElliott Hughes } 576*e1fe3e4aSElliott Hughes table = builder.buildMarkBasePosSubtable(marks, bases, self.GLYPHMAP) 577*e1fe3e4aSElliott Hughes assert getXML(table.toXML) == [ 578*e1fe3e4aSElliott Hughes '<MarkBasePos Format="1">', 579*e1fe3e4aSElliott Hughes " <MarkCoverage>", 580*e1fe3e4aSElliott Hughes ' <Glyph value="grave"/>', 581*e1fe3e4aSElliott Hughes ' <Glyph value="acute"/>', 582*e1fe3e4aSElliott Hughes ' <Glyph value="cedilla"/>', 583*e1fe3e4aSElliott Hughes " </MarkCoverage>", 584*e1fe3e4aSElliott Hughes " <BaseCoverage>", 585*e1fe3e4aSElliott Hughes ' <Glyph value="A"/>', 586*e1fe3e4aSElliott Hughes ' <Glyph value="B"/>', 587*e1fe3e4aSElliott Hughes ' <Glyph value="C"/>', 588*e1fe3e4aSElliott Hughes ' <Glyph value="a"/>', 589*e1fe3e4aSElliott Hughes ' <Glyph value="b"/>', 590*e1fe3e4aSElliott Hughes " </BaseCoverage>", 591*e1fe3e4aSElliott Hughes " <!-- ClassCount=2 -->", 592*e1fe3e4aSElliott Hughes " <MarkArray>", 593*e1fe3e4aSElliott Hughes " <!-- MarkCount=3 -->", 594*e1fe3e4aSElliott Hughes ' <MarkRecord index="0">', # grave 595*e1fe3e4aSElliott Hughes ' <Class value="0"/>', 596*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 597*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 598*e1fe3e4aSElliott Hughes ' <YCoordinate value="700"/>', 599*e1fe3e4aSElliott Hughes " </MarkAnchor>", 600*e1fe3e4aSElliott Hughes " </MarkRecord>", 601*e1fe3e4aSElliott Hughes ' <MarkRecord index="1">', # acute 602*e1fe3e4aSElliott Hughes ' <Class value="0"/>', 603*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 604*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 605*e1fe3e4aSElliott Hughes ' <YCoordinate value="700"/>', 606*e1fe3e4aSElliott Hughes " </MarkAnchor>", 607*e1fe3e4aSElliott Hughes " </MarkRecord>", 608*e1fe3e4aSElliott Hughes ' <MarkRecord index="2">', # cedilla 609*e1fe3e4aSElliott Hughes ' <Class value="1"/>', 610*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 611*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 612*e1fe3e4aSElliott Hughes ' <YCoordinate value="-100"/>', 613*e1fe3e4aSElliott Hughes " </MarkAnchor>", 614*e1fe3e4aSElliott Hughes " </MarkRecord>", 615*e1fe3e4aSElliott Hughes " </MarkArray>", 616*e1fe3e4aSElliott Hughes " <BaseArray>", 617*e1fe3e4aSElliott Hughes " <!-- BaseCount=5 -->", 618*e1fe3e4aSElliott Hughes ' <BaseRecord index="0">', # A 619*e1fe3e4aSElliott Hughes ' <BaseAnchor index="0" empty="1"/>', 620*e1fe3e4aSElliott Hughes ' <BaseAnchor index="1" empty="1"/>', 621*e1fe3e4aSElliott Hughes " </BaseRecord>", 622*e1fe3e4aSElliott Hughes ' <BaseRecord index="1">', # B 623*e1fe3e4aSElliott Hughes ' <BaseAnchor index="0" Format="1">', 624*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 625*e1fe3e4aSElliott Hughes ' <YCoordinate value="900"/>', 626*e1fe3e4aSElliott Hughes " </BaseAnchor>", 627*e1fe3e4aSElliott Hughes ' <BaseAnchor index="1" empty="1"/>', 628*e1fe3e4aSElliott Hughes " </BaseRecord>", 629*e1fe3e4aSElliott Hughes ' <BaseRecord index="2">', # C 630*e1fe3e4aSElliott Hughes ' <BaseAnchor index="0" empty="1"/>', 631*e1fe3e4aSElliott Hughes ' <BaseAnchor index="1" Format="1">', 632*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 633*e1fe3e4aSElliott Hughes ' <YCoordinate value="-10"/>', 634*e1fe3e4aSElliott Hughes " </BaseAnchor>", 635*e1fe3e4aSElliott Hughes " </BaseRecord>", 636*e1fe3e4aSElliott Hughes ' <BaseRecord index="3">', # a 637*e1fe3e4aSElliott Hughes ' <BaseAnchor index="0" Format="1">', 638*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 639*e1fe3e4aSElliott Hughes ' <YCoordinate value="400"/>', 640*e1fe3e4aSElliott Hughes " </BaseAnchor>", 641*e1fe3e4aSElliott Hughes ' <BaseAnchor index="1" Format="1">', 642*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 643*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 644*e1fe3e4aSElliott Hughes " </BaseAnchor>", 645*e1fe3e4aSElliott Hughes " </BaseRecord>", 646*e1fe3e4aSElliott Hughes ' <BaseRecord index="4">', # b 647*e1fe3e4aSElliott Hughes ' <BaseAnchor index="0" Format="1">', 648*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 649*e1fe3e4aSElliott Hughes ' <YCoordinate value="800"/>', 650*e1fe3e4aSElliott Hughes " </BaseAnchor>", 651*e1fe3e4aSElliott Hughes ' <BaseAnchor index="1" Format="1">', 652*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 653*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 654*e1fe3e4aSElliott Hughes " </BaseAnchor>", 655*e1fe3e4aSElliott Hughes " </BaseRecord>", 656*e1fe3e4aSElliott Hughes " </BaseArray>", 657*e1fe3e4aSElliott Hughes "</MarkBasePos>", 658*e1fe3e4aSElliott Hughes ] 659*e1fe3e4aSElliott Hughes 660*e1fe3e4aSElliott Hughes def test_buildMarkGlyphSetsDef(self): 661*e1fe3e4aSElliott Hughes marksets = builder.buildMarkGlyphSetsDef( 662*e1fe3e4aSElliott Hughes [{"acute", "grave"}, {"cedilla", "grave"}], self.GLYPHMAP 663*e1fe3e4aSElliott Hughes ) 664*e1fe3e4aSElliott Hughes assert getXML(marksets.toXML) == [ 665*e1fe3e4aSElliott Hughes "<MarkGlyphSetsDef>", 666*e1fe3e4aSElliott Hughes ' <MarkSetTableFormat value="1"/>', 667*e1fe3e4aSElliott Hughes " <!-- MarkSetCount=2 -->", 668*e1fe3e4aSElliott Hughes ' <Coverage index="0">', 669*e1fe3e4aSElliott Hughes ' <Glyph value="grave"/>', 670*e1fe3e4aSElliott Hughes ' <Glyph value="acute"/>', 671*e1fe3e4aSElliott Hughes " </Coverage>", 672*e1fe3e4aSElliott Hughes ' <Coverage index="1">', 673*e1fe3e4aSElliott Hughes ' <Glyph value="grave"/>', 674*e1fe3e4aSElliott Hughes ' <Glyph value="cedilla"/>', 675*e1fe3e4aSElliott Hughes " </Coverage>", 676*e1fe3e4aSElliott Hughes "</MarkGlyphSetsDef>", 677*e1fe3e4aSElliott Hughes ] 678*e1fe3e4aSElliott Hughes 679*e1fe3e4aSElliott Hughes def test_buildMarkGlyphSetsDef_empty(self): 680*e1fe3e4aSElliott Hughes assert builder.buildMarkGlyphSetsDef([], self.GLYPHMAP) is None 681*e1fe3e4aSElliott Hughes 682*e1fe3e4aSElliott Hughes def test_buildMarkGlyphSetsDef_None(self): 683*e1fe3e4aSElliott Hughes assert builder.buildMarkGlyphSetsDef(None, self.GLYPHMAP) is None 684*e1fe3e4aSElliott Hughes 685*e1fe3e4aSElliott Hughes def test_buildMarkLigPosSubtable(self): 686*e1fe3e4aSElliott Hughes anchor = builder.buildAnchor 687*e1fe3e4aSElliott Hughes marks = { 688*e1fe3e4aSElliott Hughes "acute": (0, anchor(300, 700)), 689*e1fe3e4aSElliott Hughes "cedilla": (1, anchor(300, -100)), 690*e1fe3e4aSElliott Hughes "grave": (0, anchor(300, 700)), 691*e1fe3e4aSElliott Hughes } 692*e1fe3e4aSElliott Hughes bases = { 693*e1fe3e4aSElliott Hughes "f_i": [{}, {0: anchor(200, 400)}], # nothing on f; only 1 on i 694*e1fe3e4aSElliott Hughes "c_t": [ 695*e1fe3e4aSElliott Hughes {0: anchor(500, 600), 1: anchor(500, -20)}, # c 696*e1fe3e4aSElliott Hughes {0: anchor(1300, 800), 1: anchor(1300, -20)}, # t 697*e1fe3e4aSElliott Hughes ], 698*e1fe3e4aSElliott Hughes } 699*e1fe3e4aSElliott Hughes table = builder.buildMarkLigPosSubtable(marks, bases, self.GLYPHMAP) 700*e1fe3e4aSElliott Hughes assert getXML(table.toXML) == [ 701*e1fe3e4aSElliott Hughes '<MarkLigPos Format="1">', 702*e1fe3e4aSElliott Hughes " <MarkCoverage>", 703*e1fe3e4aSElliott Hughes ' <Glyph value="grave"/>', 704*e1fe3e4aSElliott Hughes ' <Glyph value="acute"/>', 705*e1fe3e4aSElliott Hughes ' <Glyph value="cedilla"/>', 706*e1fe3e4aSElliott Hughes " </MarkCoverage>", 707*e1fe3e4aSElliott Hughes " <LigatureCoverage>", 708*e1fe3e4aSElliott Hughes ' <Glyph value="f_i"/>', 709*e1fe3e4aSElliott Hughes ' <Glyph value="c_t"/>', 710*e1fe3e4aSElliott Hughes " </LigatureCoverage>", 711*e1fe3e4aSElliott Hughes " <!-- ClassCount=2 -->", 712*e1fe3e4aSElliott Hughes " <MarkArray>", 713*e1fe3e4aSElliott Hughes " <!-- MarkCount=3 -->", 714*e1fe3e4aSElliott Hughes ' <MarkRecord index="0">', 715*e1fe3e4aSElliott Hughes ' <Class value="0"/>', 716*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 717*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 718*e1fe3e4aSElliott Hughes ' <YCoordinate value="700"/>', 719*e1fe3e4aSElliott Hughes " </MarkAnchor>", 720*e1fe3e4aSElliott Hughes " </MarkRecord>", 721*e1fe3e4aSElliott Hughes ' <MarkRecord index="1">', 722*e1fe3e4aSElliott Hughes ' <Class value="0"/>', 723*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 724*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 725*e1fe3e4aSElliott Hughes ' <YCoordinate value="700"/>', 726*e1fe3e4aSElliott Hughes " </MarkAnchor>", 727*e1fe3e4aSElliott Hughes " </MarkRecord>", 728*e1fe3e4aSElliott Hughes ' <MarkRecord index="2">', 729*e1fe3e4aSElliott Hughes ' <Class value="1"/>', 730*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 731*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 732*e1fe3e4aSElliott Hughes ' <YCoordinate value="-100"/>', 733*e1fe3e4aSElliott Hughes " </MarkAnchor>", 734*e1fe3e4aSElliott Hughes " </MarkRecord>", 735*e1fe3e4aSElliott Hughes " </MarkArray>", 736*e1fe3e4aSElliott Hughes " <LigatureArray>", 737*e1fe3e4aSElliott Hughes " <!-- LigatureCount=2 -->", 738*e1fe3e4aSElliott Hughes ' <LigatureAttach index="0">', 739*e1fe3e4aSElliott Hughes " <!-- ComponentCount=2 -->", 740*e1fe3e4aSElliott Hughes ' <ComponentRecord index="0">', 741*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" empty="1"/>', 742*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" empty="1"/>', 743*e1fe3e4aSElliott Hughes " </ComponentRecord>", 744*e1fe3e4aSElliott Hughes ' <ComponentRecord index="1">', 745*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" Format="1">', 746*e1fe3e4aSElliott Hughes ' <XCoordinate value="200"/>', 747*e1fe3e4aSElliott Hughes ' <YCoordinate value="400"/>', 748*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 749*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" empty="1"/>', 750*e1fe3e4aSElliott Hughes " </ComponentRecord>", 751*e1fe3e4aSElliott Hughes " </LigatureAttach>", 752*e1fe3e4aSElliott Hughes ' <LigatureAttach index="1">', 753*e1fe3e4aSElliott Hughes " <!-- ComponentCount=2 -->", 754*e1fe3e4aSElliott Hughes ' <ComponentRecord index="0">', 755*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" Format="1">', 756*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 757*e1fe3e4aSElliott Hughes ' <YCoordinate value="600"/>', 758*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 759*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" Format="1">', 760*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 761*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 762*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 763*e1fe3e4aSElliott Hughes " </ComponentRecord>", 764*e1fe3e4aSElliott Hughes ' <ComponentRecord index="1">', 765*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="0" Format="1">', 766*e1fe3e4aSElliott Hughes ' <XCoordinate value="1300"/>', 767*e1fe3e4aSElliott Hughes ' <YCoordinate value="800"/>', 768*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 769*e1fe3e4aSElliott Hughes ' <LigatureAnchor index="1" Format="1">', 770*e1fe3e4aSElliott Hughes ' <XCoordinate value="1300"/>', 771*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 772*e1fe3e4aSElliott Hughes " </LigatureAnchor>", 773*e1fe3e4aSElliott Hughes " </ComponentRecord>", 774*e1fe3e4aSElliott Hughes " </LigatureAttach>", 775*e1fe3e4aSElliott Hughes " </LigatureArray>", 776*e1fe3e4aSElliott Hughes "</MarkLigPos>", 777*e1fe3e4aSElliott Hughes ] 778*e1fe3e4aSElliott Hughes 779*e1fe3e4aSElliott Hughes def test_buildMarkRecord(self): 780*e1fe3e4aSElliott Hughes rec = builder.buildMarkRecord(17, builder.buildAnchor(500, -20)) 781*e1fe3e4aSElliott Hughes assert getXML(rec.toXML) == [ 782*e1fe3e4aSElliott Hughes "<MarkRecord>", 783*e1fe3e4aSElliott Hughes ' <Class value="17"/>', 784*e1fe3e4aSElliott Hughes ' <MarkAnchor Format="1">', 785*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 786*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 787*e1fe3e4aSElliott Hughes " </MarkAnchor>", 788*e1fe3e4aSElliott Hughes "</MarkRecord>", 789*e1fe3e4aSElliott Hughes ] 790*e1fe3e4aSElliott Hughes 791*e1fe3e4aSElliott Hughes def test_buildMark2Record(self): 792*e1fe3e4aSElliott Hughes a = builder.buildAnchor 793*e1fe3e4aSElliott Hughes rec = builder.buildMark2Record([a(500, -20), None, a(300, -15)]) 794*e1fe3e4aSElliott Hughes assert getXML(rec.toXML) == [ 795*e1fe3e4aSElliott Hughes "<Mark2Record>", 796*e1fe3e4aSElliott Hughes ' <Mark2Anchor index="0" Format="1">', 797*e1fe3e4aSElliott Hughes ' <XCoordinate value="500"/>', 798*e1fe3e4aSElliott Hughes ' <YCoordinate value="-20"/>', 799*e1fe3e4aSElliott Hughes " </Mark2Anchor>", 800*e1fe3e4aSElliott Hughes ' <Mark2Anchor index="1" empty="1"/>', 801*e1fe3e4aSElliott Hughes ' <Mark2Anchor index="2" Format="1">', 802*e1fe3e4aSElliott Hughes ' <XCoordinate value="300"/>', 803*e1fe3e4aSElliott Hughes ' <YCoordinate value="-15"/>', 804*e1fe3e4aSElliott Hughes " </Mark2Anchor>", 805*e1fe3e4aSElliott Hughes "</Mark2Record>", 806*e1fe3e4aSElliott Hughes ] 807*e1fe3e4aSElliott Hughes 808*e1fe3e4aSElliott Hughes def test_buildPairPosClassesSubtable(self): 809*e1fe3e4aSElliott Hughes d20 = builder.buildValue({"XPlacement": -20}) 810*e1fe3e4aSElliott Hughes d50 = builder.buildValue({"XPlacement": -50}) 811*e1fe3e4aSElliott Hughes d0 = builder.buildValue({}) 812*e1fe3e4aSElliott Hughes d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) 813*e1fe3e4aSElliott Hughes subtable = builder.buildPairPosClassesSubtable( 814*e1fe3e4aSElliott Hughes { 815*e1fe3e4aSElliott Hughes (tuple("A"), tuple(["zero"])): (d0, d50), 816*e1fe3e4aSElliott Hughes (tuple("A"), tuple(["one", "two"])): (None, d20), 817*e1fe3e4aSElliott Hughes (tuple(["B", "C"]), tuple(["zero"])): (d8020, d50), 818*e1fe3e4aSElliott Hughes }, 819*e1fe3e4aSElliott Hughes self.GLYPHMAP, 820*e1fe3e4aSElliott Hughes ) 821*e1fe3e4aSElliott Hughes assert getXML(subtable.toXML) == [ 822*e1fe3e4aSElliott Hughes '<PairPos Format="2">', 823*e1fe3e4aSElliott Hughes " <Coverage>", 824*e1fe3e4aSElliott Hughes ' <Glyph value="A"/>', 825*e1fe3e4aSElliott Hughes ' <Glyph value="B"/>', 826*e1fe3e4aSElliott Hughes ' <Glyph value="C"/>', 827*e1fe3e4aSElliott Hughes " </Coverage>", 828*e1fe3e4aSElliott Hughes ' <ValueFormat1 value="3"/>', 829*e1fe3e4aSElliott Hughes ' <ValueFormat2 value="1"/>', 830*e1fe3e4aSElliott Hughes " <ClassDef1>", 831*e1fe3e4aSElliott Hughes ' <ClassDef glyph="A" class="1"/>', 832*e1fe3e4aSElliott Hughes " </ClassDef1>", 833*e1fe3e4aSElliott Hughes " <ClassDef2>", 834*e1fe3e4aSElliott Hughes ' <ClassDef glyph="one" class="1"/>', 835*e1fe3e4aSElliott Hughes ' <ClassDef glyph="two" class="1"/>', 836*e1fe3e4aSElliott Hughes ' <ClassDef glyph="zero" class="2"/>', 837*e1fe3e4aSElliott Hughes " </ClassDef2>", 838*e1fe3e4aSElliott Hughes " <!-- Class1Count=2 -->", 839*e1fe3e4aSElliott Hughes " <!-- Class2Count=3 -->", 840*e1fe3e4aSElliott Hughes ' <Class1Record index="0">', 841*e1fe3e4aSElliott Hughes ' <Class2Record index="0">', 842*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="0" YPlacement="0"/>', 843*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="0"/>', 844*e1fe3e4aSElliott Hughes " </Class2Record>", 845*e1fe3e4aSElliott Hughes ' <Class2Record index="1">', 846*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="0" YPlacement="0"/>', 847*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="0"/>', 848*e1fe3e4aSElliott Hughes " </Class2Record>", 849*e1fe3e4aSElliott Hughes ' <Class2Record index="2">', 850*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="-80" YPlacement="-20"/>', 851*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="-50"/>', 852*e1fe3e4aSElliott Hughes " </Class2Record>", 853*e1fe3e4aSElliott Hughes " </Class1Record>", 854*e1fe3e4aSElliott Hughes ' <Class1Record index="1">', 855*e1fe3e4aSElliott Hughes ' <Class2Record index="0">', 856*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="0" YPlacement="0"/>', 857*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="0"/>', 858*e1fe3e4aSElliott Hughes " </Class2Record>", 859*e1fe3e4aSElliott Hughes ' <Class2Record index="1">', 860*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="0" YPlacement="0"/>', 861*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="-20"/>', 862*e1fe3e4aSElliott Hughes " </Class2Record>", 863*e1fe3e4aSElliott Hughes ' <Class2Record index="2">', 864*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="0" YPlacement="0"/>', 865*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="-50"/>', 866*e1fe3e4aSElliott Hughes " </Class2Record>", 867*e1fe3e4aSElliott Hughes " </Class1Record>", 868*e1fe3e4aSElliott Hughes "</PairPos>", 869*e1fe3e4aSElliott Hughes ] 870*e1fe3e4aSElliott Hughes 871*e1fe3e4aSElliott Hughes def test_buildPairPosGlyphs(self): 872*e1fe3e4aSElliott Hughes d50 = builder.buildValue({"XPlacement": -50}) 873*e1fe3e4aSElliott Hughes d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) 874*e1fe3e4aSElliott Hughes subtables = builder.buildPairPosGlyphs( 875*e1fe3e4aSElliott Hughes {("A", "zero"): (None, d50), ("A", "one"): (d8020, d50)}, self.GLYPHMAP 876*e1fe3e4aSElliott Hughes ) 877*e1fe3e4aSElliott Hughes assert sum([getXML(t.toXML) for t in subtables], []) == [ 878*e1fe3e4aSElliott Hughes '<PairPos Format="1">', 879*e1fe3e4aSElliott Hughes " <Coverage>", 880*e1fe3e4aSElliott Hughes ' <Glyph value="A"/>', 881*e1fe3e4aSElliott Hughes " </Coverage>", 882*e1fe3e4aSElliott Hughes ' <ValueFormat1 value="0"/>', 883*e1fe3e4aSElliott Hughes ' <ValueFormat2 value="1"/>', 884*e1fe3e4aSElliott Hughes " <!-- PairSetCount=1 -->", 885*e1fe3e4aSElliott Hughes ' <PairSet index="0">', 886*e1fe3e4aSElliott Hughes " <!-- PairValueCount=1 -->", 887*e1fe3e4aSElliott Hughes ' <PairValueRecord index="0">', 888*e1fe3e4aSElliott Hughes ' <SecondGlyph value="zero"/>', 889*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="-50"/>', 890*e1fe3e4aSElliott Hughes " </PairValueRecord>", 891*e1fe3e4aSElliott Hughes " </PairSet>", 892*e1fe3e4aSElliott Hughes "</PairPos>", 893*e1fe3e4aSElliott Hughes '<PairPos Format="1">', 894*e1fe3e4aSElliott Hughes " <Coverage>", 895*e1fe3e4aSElliott Hughes ' <Glyph value="A"/>', 896*e1fe3e4aSElliott Hughes " </Coverage>", 897*e1fe3e4aSElliott Hughes ' <ValueFormat1 value="3"/>', 898*e1fe3e4aSElliott Hughes ' <ValueFormat2 value="1"/>', 899*e1fe3e4aSElliott Hughes " <!-- PairSetCount=1 -->", 900*e1fe3e4aSElliott Hughes ' <PairSet index="0">', 901*e1fe3e4aSElliott Hughes " <!-- PairValueCount=1 -->", 902*e1fe3e4aSElliott Hughes ' <PairValueRecord index="0">', 903*e1fe3e4aSElliott Hughes ' <SecondGlyph value="one"/>', 904*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="-80" YPlacement="-20"/>', 905*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="-50"/>', 906*e1fe3e4aSElliott Hughes " </PairValueRecord>", 907*e1fe3e4aSElliott Hughes " </PairSet>", 908*e1fe3e4aSElliott Hughes "</PairPos>", 909*e1fe3e4aSElliott Hughes ] 910*e1fe3e4aSElliott Hughes 911*e1fe3e4aSElliott Hughes def test_buildPairPosGlyphsSubtable(self): 912*e1fe3e4aSElliott Hughes d20 = builder.buildValue({"XPlacement": -20}) 913*e1fe3e4aSElliott Hughes d50 = builder.buildValue({"XPlacement": -50}) 914*e1fe3e4aSElliott Hughes d0 = builder.buildValue({}) 915*e1fe3e4aSElliott Hughes d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) 916*e1fe3e4aSElliott Hughes subtable = builder.buildPairPosGlyphsSubtable( 917*e1fe3e4aSElliott Hughes { 918*e1fe3e4aSElliott Hughes ("A", "zero"): (d0, d50), 919*e1fe3e4aSElliott Hughes ("A", "one"): (None, d20), 920*e1fe3e4aSElliott Hughes ("B", "five"): (d8020, d50), 921*e1fe3e4aSElliott Hughes }, 922*e1fe3e4aSElliott Hughes self.GLYPHMAP, 923*e1fe3e4aSElliott Hughes ) 924*e1fe3e4aSElliott Hughes 925*e1fe3e4aSElliott Hughes assert getXML(subtable.toXML) == [ 926*e1fe3e4aSElliott Hughes '<PairPos Format="1">', 927*e1fe3e4aSElliott Hughes " <Coverage>", 928*e1fe3e4aSElliott Hughes ' <Glyph value="A"/>', 929*e1fe3e4aSElliott Hughes ' <Glyph value="B"/>', 930*e1fe3e4aSElliott Hughes " </Coverage>", 931*e1fe3e4aSElliott Hughes ' <ValueFormat1 value="3"/>', 932*e1fe3e4aSElliott Hughes ' <ValueFormat2 value="1"/>', 933*e1fe3e4aSElliott Hughes " <!-- PairSetCount=2 -->", 934*e1fe3e4aSElliott Hughes ' <PairSet index="0">', 935*e1fe3e4aSElliott Hughes " <!-- PairValueCount=2 -->", 936*e1fe3e4aSElliott Hughes ' <PairValueRecord index="0">', 937*e1fe3e4aSElliott Hughes ' <SecondGlyph value="zero"/>', 938*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="0" YPlacement="0"/>', 939*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="-50"/>', 940*e1fe3e4aSElliott Hughes " </PairValueRecord>", 941*e1fe3e4aSElliott Hughes ' <PairValueRecord index="1">', 942*e1fe3e4aSElliott Hughes ' <SecondGlyph value="one"/>', 943*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="0" YPlacement="0"/>', 944*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="-20"/>', 945*e1fe3e4aSElliott Hughes " </PairValueRecord>", 946*e1fe3e4aSElliott Hughes " </PairSet>", 947*e1fe3e4aSElliott Hughes ' <PairSet index="1">', 948*e1fe3e4aSElliott Hughes " <!-- PairValueCount=1 -->", 949*e1fe3e4aSElliott Hughes ' <PairValueRecord index="0">', 950*e1fe3e4aSElliott Hughes ' <SecondGlyph value="five"/>', 951*e1fe3e4aSElliott Hughes ' <Value1 XPlacement="-80" YPlacement="-20"/>', 952*e1fe3e4aSElliott Hughes ' <Value2 XPlacement="-50"/>', 953*e1fe3e4aSElliott Hughes " </PairValueRecord>", 954*e1fe3e4aSElliott Hughes " </PairSet>", 955*e1fe3e4aSElliott Hughes "</PairPos>", 956*e1fe3e4aSElliott Hughes ] 957*e1fe3e4aSElliott Hughes 958*e1fe3e4aSElliott Hughes def test_buildSinglePos(self): 959*e1fe3e4aSElliott Hughes subtables = builder.buildSinglePos( 960*e1fe3e4aSElliott Hughes { 961*e1fe3e4aSElliott Hughes "one": builder.buildValue({"XPlacement": 500}), 962*e1fe3e4aSElliott Hughes "two": builder.buildValue({"XPlacement": 500}), 963*e1fe3e4aSElliott Hughes "three": builder.buildValue({"XPlacement": 200}), 964*e1fe3e4aSElliott Hughes "four": builder.buildValue({"XPlacement": 400}), 965*e1fe3e4aSElliott Hughes "five": builder.buildValue({"XPlacement": 500}), 966*e1fe3e4aSElliott Hughes "six": builder.buildValue({"YPlacement": -6}), 967*e1fe3e4aSElliott Hughes }, 968*e1fe3e4aSElliott Hughes self.GLYPHMAP, 969*e1fe3e4aSElliott Hughes ) 970*e1fe3e4aSElliott Hughes assert sum([getXML(t.toXML) for t in subtables], []) == [ 971*e1fe3e4aSElliott Hughes '<SinglePos Format="2">', 972*e1fe3e4aSElliott Hughes " <Coverage>", 973*e1fe3e4aSElliott Hughes ' <Glyph value="one"/>', 974*e1fe3e4aSElliott Hughes ' <Glyph value="two"/>', 975*e1fe3e4aSElliott Hughes ' <Glyph value="three"/>', 976*e1fe3e4aSElliott Hughes ' <Glyph value="four"/>', 977*e1fe3e4aSElliott Hughes ' <Glyph value="five"/>', 978*e1fe3e4aSElliott Hughes " </Coverage>", 979*e1fe3e4aSElliott Hughes ' <ValueFormat value="1"/>', 980*e1fe3e4aSElliott Hughes " <!-- ValueCount=5 -->", 981*e1fe3e4aSElliott Hughes ' <Value index="0" XPlacement="500"/>', 982*e1fe3e4aSElliott Hughes ' <Value index="1" XPlacement="500"/>', 983*e1fe3e4aSElliott Hughes ' <Value index="2" XPlacement="200"/>', 984*e1fe3e4aSElliott Hughes ' <Value index="3" XPlacement="400"/>', 985*e1fe3e4aSElliott Hughes ' <Value index="4" XPlacement="500"/>', 986*e1fe3e4aSElliott Hughes "</SinglePos>", 987*e1fe3e4aSElliott Hughes '<SinglePos Format="1">', 988*e1fe3e4aSElliott Hughes " <Coverage>", 989*e1fe3e4aSElliott Hughes ' <Glyph value="six"/>', 990*e1fe3e4aSElliott Hughes " </Coverage>", 991*e1fe3e4aSElliott Hughes ' <ValueFormat value="2"/>', 992*e1fe3e4aSElliott Hughes ' <Value YPlacement="-6"/>', 993*e1fe3e4aSElliott Hughes "</SinglePos>", 994*e1fe3e4aSElliott Hughes ] 995*e1fe3e4aSElliott Hughes 996*e1fe3e4aSElliott Hughes def test_buildSinglePos_ValueFormat0(self): 997*e1fe3e4aSElliott Hughes subtables = builder.buildSinglePos( 998*e1fe3e4aSElliott Hughes {"zero": builder.buildValue({})}, self.GLYPHMAP 999*e1fe3e4aSElliott Hughes ) 1000*e1fe3e4aSElliott Hughes assert sum([getXML(t.toXML) for t in subtables], []) == [ 1001*e1fe3e4aSElliott Hughes '<SinglePos Format="1">', 1002*e1fe3e4aSElliott Hughes " <Coverage>", 1003*e1fe3e4aSElliott Hughes ' <Glyph value="zero"/>', 1004*e1fe3e4aSElliott Hughes " </Coverage>", 1005*e1fe3e4aSElliott Hughes ' <ValueFormat value="0"/>', 1006*e1fe3e4aSElliott Hughes "</SinglePos>", 1007*e1fe3e4aSElliott Hughes ] 1008*e1fe3e4aSElliott Hughes 1009*e1fe3e4aSElliott Hughes def test_buildSinglePosSubtable_format1(self): 1010*e1fe3e4aSElliott Hughes subtable = builder.buildSinglePosSubtable( 1011*e1fe3e4aSElliott Hughes { 1012*e1fe3e4aSElliott Hughes "one": builder.buildValue({"XPlacement": 777}), 1013*e1fe3e4aSElliott Hughes "two": builder.buildValue({"XPlacement": 777}), 1014*e1fe3e4aSElliott Hughes }, 1015*e1fe3e4aSElliott Hughes self.GLYPHMAP, 1016*e1fe3e4aSElliott Hughes ) 1017*e1fe3e4aSElliott Hughes assert getXML(subtable.toXML) == [ 1018*e1fe3e4aSElliott Hughes '<SinglePos Format="1">', 1019*e1fe3e4aSElliott Hughes " <Coverage>", 1020*e1fe3e4aSElliott Hughes ' <Glyph value="one"/>', 1021*e1fe3e4aSElliott Hughes ' <Glyph value="two"/>', 1022*e1fe3e4aSElliott Hughes " </Coverage>", 1023*e1fe3e4aSElliott Hughes ' <ValueFormat value="1"/>', 1024*e1fe3e4aSElliott Hughes ' <Value XPlacement="777"/>', 1025*e1fe3e4aSElliott Hughes "</SinglePos>", 1026*e1fe3e4aSElliott Hughes ] 1027*e1fe3e4aSElliott Hughes 1028*e1fe3e4aSElliott Hughes def test_buildSinglePosSubtable_format2(self): 1029*e1fe3e4aSElliott Hughes subtable = builder.buildSinglePosSubtable( 1030*e1fe3e4aSElliott Hughes { 1031*e1fe3e4aSElliott Hughes "one": builder.buildValue({"XPlacement": 777}), 1032*e1fe3e4aSElliott Hughes "two": builder.buildValue({"YPlacement": -888}), 1033*e1fe3e4aSElliott Hughes }, 1034*e1fe3e4aSElliott Hughes self.GLYPHMAP, 1035*e1fe3e4aSElliott Hughes ) 1036*e1fe3e4aSElliott Hughes assert getXML(subtable.toXML) == [ 1037*e1fe3e4aSElliott Hughes '<SinglePos Format="2">', 1038*e1fe3e4aSElliott Hughes " <Coverage>", 1039*e1fe3e4aSElliott Hughes ' <Glyph value="one"/>', 1040*e1fe3e4aSElliott Hughes ' <Glyph value="two"/>', 1041*e1fe3e4aSElliott Hughes " </Coverage>", 1042*e1fe3e4aSElliott Hughes ' <ValueFormat value="3"/>', 1043*e1fe3e4aSElliott Hughes " <!-- ValueCount=2 -->", 1044*e1fe3e4aSElliott Hughes ' <Value index="0" XPlacement="777" YPlacement="0"/>', 1045*e1fe3e4aSElliott Hughes ' <Value index="1" XPlacement="0" YPlacement="-888"/>', 1046*e1fe3e4aSElliott Hughes "</SinglePos>", 1047*e1fe3e4aSElliott Hughes ] 1048*e1fe3e4aSElliott Hughes 1049*e1fe3e4aSElliott Hughes def test_buildValue(self): 1050*e1fe3e4aSElliott Hughes value = builder.buildValue({"XPlacement": 7, "YPlacement": 23}) 1051*e1fe3e4aSElliott Hughes func = lambda writer, font: value.toXML(writer, font, valueName="Val") 1052*e1fe3e4aSElliott Hughes assert getXML(func) == ['<Val XPlacement="7" YPlacement="23"/>'] 1053*e1fe3e4aSElliott Hughes 1054*e1fe3e4aSElliott Hughes def test_getLigatureSortKey(self): 1055*e1fe3e4aSElliott Hughes components = lambda s: [tuple(word) for word in s.split()] 1056*e1fe3e4aSElliott Hughes c = components("fi fl ff ffi fff") 1057*e1fe3e4aSElliott Hughes c.sort(key=otTables.LigatureSubst._getLigatureSortKey) 1058*e1fe3e4aSElliott Hughes assert c == components("ffi fff fi fl ff") 1059*e1fe3e4aSElliott Hughes 1060*e1fe3e4aSElliott Hughes def test_getSinglePosValueKey(self): 1061*e1fe3e4aSElliott Hughes device = builder.buildDevice({10: 1, 11: 3}) 1062*e1fe3e4aSElliott Hughes a1 = builder.buildValue({"XPlacement": 500, "XPlaDevice": device}) 1063*e1fe3e4aSElliott Hughes a2 = builder.buildValue({"XPlacement": 500, "XPlaDevice": device}) 1064*e1fe3e4aSElliott Hughes b = builder.buildValue({"XPlacement": 500}) 1065*e1fe3e4aSElliott Hughes keyA1 = builder._getSinglePosValueKey(a1) 1066*e1fe3e4aSElliott Hughes keyA2 = builder._getSinglePosValueKey(a1) 1067*e1fe3e4aSElliott Hughes keyB = builder._getSinglePosValueKey(b) 1068*e1fe3e4aSElliott Hughes assert keyA1 == keyA2 1069*e1fe3e4aSElliott Hughes assert hash(keyA1) == hash(keyA2) 1070*e1fe3e4aSElliott Hughes assert keyA1 != keyB 1071*e1fe3e4aSElliott Hughes assert hash(keyA1) != hash(keyB) 1072*e1fe3e4aSElliott Hughes 1073*e1fe3e4aSElliott Hughes 1074*e1fe3e4aSElliott Hughesclass ClassDefBuilderTest(object): 1075*e1fe3e4aSElliott Hughes def test_build_usingClass0(self): 1076*e1fe3e4aSElliott Hughes b = builder.ClassDefBuilder(useClass0=True) 1077*e1fe3e4aSElliott Hughes b.add({"aa", "bb"}) 1078*e1fe3e4aSElliott Hughes b.add({"a", "b"}) 1079*e1fe3e4aSElliott Hughes b.add({"c"}) 1080*e1fe3e4aSElliott Hughes b.add({"e", "f", "g", "h"}) 1081*e1fe3e4aSElliott Hughes cdef = b.build() 1082*e1fe3e4aSElliott Hughes assert isinstance(cdef, otTables.ClassDef) 1083*e1fe3e4aSElliott Hughes assert cdef.classDefs == {"a": 1, "b": 1, "c": 3, "aa": 2, "bb": 2} 1084*e1fe3e4aSElliott Hughes 1085*e1fe3e4aSElliott Hughes def test_build_notUsingClass0(self): 1086*e1fe3e4aSElliott Hughes b = builder.ClassDefBuilder(useClass0=False) 1087*e1fe3e4aSElliott Hughes b.add({"a", "b"}) 1088*e1fe3e4aSElliott Hughes b.add({"c"}) 1089*e1fe3e4aSElliott Hughes b.add({"e", "f", "g", "h"}) 1090*e1fe3e4aSElliott Hughes cdef = b.build() 1091*e1fe3e4aSElliott Hughes assert isinstance(cdef, otTables.ClassDef) 1092*e1fe3e4aSElliott Hughes assert cdef.classDefs == { 1093*e1fe3e4aSElliott Hughes "a": 2, 1094*e1fe3e4aSElliott Hughes "b": 2, 1095*e1fe3e4aSElliott Hughes "c": 3, 1096*e1fe3e4aSElliott Hughes "e": 1, 1097*e1fe3e4aSElliott Hughes "f": 1, 1098*e1fe3e4aSElliott Hughes "g": 1, 1099*e1fe3e4aSElliott Hughes "h": 1, 1100*e1fe3e4aSElliott Hughes } 1101*e1fe3e4aSElliott Hughes 1102*e1fe3e4aSElliott Hughes def test_canAdd(self): 1103*e1fe3e4aSElliott Hughes b = builder.ClassDefBuilder(useClass0=True) 1104*e1fe3e4aSElliott Hughes b.add({"a", "b", "c", "d"}) 1105*e1fe3e4aSElliott Hughes b.add({"e", "f"}) 1106*e1fe3e4aSElliott Hughes assert b.canAdd({"a", "b", "c", "d"}) 1107*e1fe3e4aSElliott Hughes assert b.canAdd({"e", "f"}) 1108*e1fe3e4aSElliott Hughes assert b.canAdd({"g", "h", "i"}) 1109*e1fe3e4aSElliott Hughes assert not b.canAdd({"b", "c", "d"}) 1110*e1fe3e4aSElliott Hughes assert not b.canAdd({"a", "b", "c", "d", "e", "f"}) 1111*e1fe3e4aSElliott Hughes assert not b.canAdd({"d", "e", "f"}) 1112*e1fe3e4aSElliott Hughes assert not b.canAdd({"f"}) 1113*e1fe3e4aSElliott Hughes 1114*e1fe3e4aSElliott Hughes def test_add_exception(self): 1115*e1fe3e4aSElliott Hughes b = builder.ClassDefBuilder(useClass0=True) 1116*e1fe3e4aSElliott Hughes b.add({"a", "b", "c"}) 1117*e1fe3e4aSElliott Hughes with pytest.raises(error.OpenTypeLibError): 1118*e1fe3e4aSElliott Hughes b.add({"a", "d"}) 1119*e1fe3e4aSElliott Hughes 1120*e1fe3e4aSElliott Hughes 1121*e1fe3e4aSElliott HughesbuildStatTable_test_data = [ 1122*e1fe3e4aSElliott Hughes ( 1123*e1fe3e4aSElliott Hughes [ 1124*e1fe3e4aSElliott Hughes dict( 1125*e1fe3e4aSElliott Hughes tag="wght", 1126*e1fe3e4aSElliott Hughes name="Weight", 1127*e1fe3e4aSElliott Hughes values=[ 1128*e1fe3e4aSElliott Hughes dict(value=100, name="Thin"), 1129*e1fe3e4aSElliott Hughes dict(value=400, name="Regular", flags=0x2), 1130*e1fe3e4aSElliott Hughes dict(value=900, name="Black"), 1131*e1fe3e4aSElliott Hughes ], 1132*e1fe3e4aSElliott Hughes ) 1133*e1fe3e4aSElliott Hughes ], 1134*e1fe3e4aSElliott Hughes None, 1135*e1fe3e4aSElliott Hughes "Regular", 1136*e1fe3e4aSElliott Hughes [ 1137*e1fe3e4aSElliott Hughes " <STAT>", 1138*e1fe3e4aSElliott Hughes ' <Version value="0x00010001"/>', 1139*e1fe3e4aSElliott Hughes ' <DesignAxisRecordSize value="8"/>', 1140*e1fe3e4aSElliott Hughes " <!-- DesignAxisCount=1 -->", 1141*e1fe3e4aSElliott Hughes " <DesignAxisRecord>", 1142*e1fe3e4aSElliott Hughes ' <Axis index="0">', 1143*e1fe3e4aSElliott Hughes ' <AxisTag value="wght"/>', 1144*e1fe3e4aSElliott Hughes ' <AxisNameID value="257"/> <!-- Weight -->', 1145*e1fe3e4aSElliott Hughes ' <AxisOrdering value="0"/>', 1146*e1fe3e4aSElliott Hughes " </Axis>", 1147*e1fe3e4aSElliott Hughes " </DesignAxisRecord>", 1148*e1fe3e4aSElliott Hughes " <!-- AxisValueCount=3 -->", 1149*e1fe3e4aSElliott Hughes " <AxisValueArray>", 1150*e1fe3e4aSElliott Hughes ' <AxisValue index="0" Format="1">', 1151*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1152*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1153*e1fe3e4aSElliott Hughes ' <ValueNameID value="258"/> <!-- Thin -->', 1154*e1fe3e4aSElliott Hughes ' <Value value="100.0"/>', 1155*e1fe3e4aSElliott Hughes " </AxisValue>", 1156*e1fe3e4aSElliott Hughes ' <AxisValue index="1" Format="1">', 1157*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1158*e1fe3e4aSElliott Hughes ' <Flags value="2"/> <!-- ElidableAxisValueName -->', 1159*e1fe3e4aSElliott Hughes ' <ValueNameID value="256"/> <!-- Regular -->', 1160*e1fe3e4aSElliott Hughes ' <Value value="400.0"/>', 1161*e1fe3e4aSElliott Hughes " </AxisValue>", 1162*e1fe3e4aSElliott Hughes ' <AxisValue index="2" Format="1">', 1163*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1164*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1165*e1fe3e4aSElliott Hughes ' <ValueNameID value="259"/> <!-- Black -->', 1166*e1fe3e4aSElliott Hughes ' <Value value="900.0"/>', 1167*e1fe3e4aSElliott Hughes " </AxisValue>", 1168*e1fe3e4aSElliott Hughes " </AxisValueArray>", 1169*e1fe3e4aSElliott Hughes ' <ElidedFallbackNameID value="256"/> <!-- Regular -->', 1170*e1fe3e4aSElliott Hughes " </STAT>", 1171*e1fe3e4aSElliott Hughes ], 1172*e1fe3e4aSElliott Hughes ), 1173*e1fe3e4aSElliott Hughes ( 1174*e1fe3e4aSElliott Hughes [ 1175*e1fe3e4aSElliott Hughes dict( 1176*e1fe3e4aSElliott Hughes tag="wght", 1177*e1fe3e4aSElliott Hughes name=dict(en="Weight", nl="Gewicht"), 1178*e1fe3e4aSElliott Hughes values=[ 1179*e1fe3e4aSElliott Hughes dict(value=100, name=dict(en="Thin", nl="Dun")), 1180*e1fe3e4aSElliott Hughes dict(value=400, name="Regular", flags=0x2), 1181*e1fe3e4aSElliott Hughes dict(value=900, name="Black"), 1182*e1fe3e4aSElliott Hughes ], 1183*e1fe3e4aSElliott Hughes ), 1184*e1fe3e4aSElliott Hughes dict( 1185*e1fe3e4aSElliott Hughes tag="wdth", 1186*e1fe3e4aSElliott Hughes name="Width", 1187*e1fe3e4aSElliott Hughes values=[ 1188*e1fe3e4aSElliott Hughes dict(value=50, name="Condensed"), 1189*e1fe3e4aSElliott Hughes dict(value=100, name="Regular", flags=0x2), 1190*e1fe3e4aSElliott Hughes dict(value=200, name="Extended"), 1191*e1fe3e4aSElliott Hughes ], 1192*e1fe3e4aSElliott Hughes ), 1193*e1fe3e4aSElliott Hughes ], 1194*e1fe3e4aSElliott Hughes None, 1195*e1fe3e4aSElliott Hughes 2, 1196*e1fe3e4aSElliott Hughes [ 1197*e1fe3e4aSElliott Hughes " <STAT>", 1198*e1fe3e4aSElliott Hughes ' <Version value="0x00010001"/>', 1199*e1fe3e4aSElliott Hughes ' <DesignAxisRecordSize value="8"/>', 1200*e1fe3e4aSElliott Hughes " <!-- DesignAxisCount=2 -->", 1201*e1fe3e4aSElliott Hughes " <DesignAxisRecord>", 1202*e1fe3e4aSElliott Hughes ' <Axis index="0">', 1203*e1fe3e4aSElliott Hughes ' <AxisTag value="wght"/>', 1204*e1fe3e4aSElliott Hughes ' <AxisNameID value="256"/> <!-- Weight -->', 1205*e1fe3e4aSElliott Hughes ' <AxisOrdering value="0"/>', 1206*e1fe3e4aSElliott Hughes " </Axis>", 1207*e1fe3e4aSElliott Hughes ' <Axis index="1">', 1208*e1fe3e4aSElliott Hughes ' <AxisTag value="wdth"/>', 1209*e1fe3e4aSElliott Hughes ' <AxisNameID value="260"/> <!-- Width -->', 1210*e1fe3e4aSElliott Hughes ' <AxisOrdering value="1"/>', 1211*e1fe3e4aSElliott Hughes " </Axis>", 1212*e1fe3e4aSElliott Hughes " </DesignAxisRecord>", 1213*e1fe3e4aSElliott Hughes " <!-- AxisValueCount=6 -->", 1214*e1fe3e4aSElliott Hughes " <AxisValueArray>", 1215*e1fe3e4aSElliott Hughes ' <AxisValue index="0" Format="1">', 1216*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1217*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1218*e1fe3e4aSElliott Hughes ' <ValueNameID value="257"/> <!-- Thin -->', 1219*e1fe3e4aSElliott Hughes ' <Value value="100.0"/>', 1220*e1fe3e4aSElliott Hughes " </AxisValue>", 1221*e1fe3e4aSElliott Hughes ' <AxisValue index="1" Format="1">', 1222*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1223*e1fe3e4aSElliott Hughes ' <Flags value="2"/> <!-- ElidableAxisValueName -->', 1224*e1fe3e4aSElliott Hughes ' <ValueNameID value="258"/> <!-- Regular -->', 1225*e1fe3e4aSElliott Hughes ' <Value value="400.0"/>', 1226*e1fe3e4aSElliott Hughes " </AxisValue>", 1227*e1fe3e4aSElliott Hughes ' <AxisValue index="2" Format="1">', 1228*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1229*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1230*e1fe3e4aSElliott Hughes ' <ValueNameID value="259"/> <!-- Black -->', 1231*e1fe3e4aSElliott Hughes ' <Value value="900.0"/>', 1232*e1fe3e4aSElliott Hughes " </AxisValue>", 1233*e1fe3e4aSElliott Hughes ' <AxisValue index="3" Format="1">', 1234*e1fe3e4aSElliott Hughes ' <AxisIndex value="1"/>', 1235*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1236*e1fe3e4aSElliott Hughes ' <ValueNameID value="261"/> <!-- Condensed -->', 1237*e1fe3e4aSElliott Hughes ' <Value value="50.0"/>', 1238*e1fe3e4aSElliott Hughes " </AxisValue>", 1239*e1fe3e4aSElliott Hughes ' <AxisValue index="4" Format="1">', 1240*e1fe3e4aSElliott Hughes ' <AxisIndex value="1"/>', 1241*e1fe3e4aSElliott Hughes ' <Flags value="2"/> <!-- ElidableAxisValueName -->', 1242*e1fe3e4aSElliott Hughes ' <ValueNameID value="258"/> <!-- Regular -->', 1243*e1fe3e4aSElliott Hughes ' <Value value="100.0"/>', 1244*e1fe3e4aSElliott Hughes " </AxisValue>", 1245*e1fe3e4aSElliott Hughes ' <AxisValue index="5" Format="1">', 1246*e1fe3e4aSElliott Hughes ' <AxisIndex value="1"/>', 1247*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1248*e1fe3e4aSElliott Hughes ' <ValueNameID value="262"/> <!-- Extended -->', 1249*e1fe3e4aSElliott Hughes ' <Value value="200.0"/>', 1250*e1fe3e4aSElliott Hughes " </AxisValue>", 1251*e1fe3e4aSElliott Hughes " </AxisValueArray>", 1252*e1fe3e4aSElliott Hughes ' <ElidedFallbackNameID value="2"/> <!-- missing from name table -->', 1253*e1fe3e4aSElliott Hughes " </STAT>", 1254*e1fe3e4aSElliott Hughes ], 1255*e1fe3e4aSElliott Hughes ), 1256*e1fe3e4aSElliott Hughes ( 1257*e1fe3e4aSElliott Hughes [ 1258*e1fe3e4aSElliott Hughes dict( 1259*e1fe3e4aSElliott Hughes tag="wght", 1260*e1fe3e4aSElliott Hughes name="Weight", 1261*e1fe3e4aSElliott Hughes values=[ 1262*e1fe3e4aSElliott Hughes dict(value=400, name="Regular", flags=0x2), 1263*e1fe3e4aSElliott Hughes dict(value=600, linkedValue=650, name="Bold"), 1264*e1fe3e4aSElliott Hughes ], 1265*e1fe3e4aSElliott Hughes ) 1266*e1fe3e4aSElliott Hughes ], 1267*e1fe3e4aSElliott Hughes None, 1268*e1fe3e4aSElliott Hughes 18, 1269*e1fe3e4aSElliott Hughes [ 1270*e1fe3e4aSElliott Hughes " <STAT>", 1271*e1fe3e4aSElliott Hughes ' <Version value="0x00010001"/>', 1272*e1fe3e4aSElliott Hughes ' <DesignAxisRecordSize value="8"/>', 1273*e1fe3e4aSElliott Hughes " <!-- DesignAxisCount=1 -->", 1274*e1fe3e4aSElliott Hughes " <DesignAxisRecord>", 1275*e1fe3e4aSElliott Hughes ' <Axis index="0">', 1276*e1fe3e4aSElliott Hughes ' <AxisTag value="wght"/>', 1277*e1fe3e4aSElliott Hughes ' <AxisNameID value="256"/> <!-- Weight -->', 1278*e1fe3e4aSElliott Hughes ' <AxisOrdering value="0"/>', 1279*e1fe3e4aSElliott Hughes " </Axis>", 1280*e1fe3e4aSElliott Hughes " </DesignAxisRecord>", 1281*e1fe3e4aSElliott Hughes " <!-- AxisValueCount=2 -->", 1282*e1fe3e4aSElliott Hughes " <AxisValueArray>", 1283*e1fe3e4aSElliott Hughes ' <AxisValue index="0" Format="1">', 1284*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1285*e1fe3e4aSElliott Hughes ' <Flags value="2"/> <!-- ElidableAxisValueName -->', 1286*e1fe3e4aSElliott Hughes ' <ValueNameID value="257"/> <!-- Regular -->', 1287*e1fe3e4aSElliott Hughes ' <Value value="400.0"/>', 1288*e1fe3e4aSElliott Hughes " </AxisValue>", 1289*e1fe3e4aSElliott Hughes ' <AxisValue index="1" Format="3">', 1290*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1291*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1292*e1fe3e4aSElliott Hughes ' <ValueNameID value="258"/> <!-- Bold -->', 1293*e1fe3e4aSElliott Hughes ' <Value value="600.0"/>', 1294*e1fe3e4aSElliott Hughes ' <LinkedValue value="650.0"/>', 1295*e1fe3e4aSElliott Hughes " </AxisValue>", 1296*e1fe3e4aSElliott Hughes " </AxisValueArray>", 1297*e1fe3e4aSElliott Hughes ' <ElidedFallbackNameID value="18"/> <!-- missing from name table -->', 1298*e1fe3e4aSElliott Hughes " </STAT>", 1299*e1fe3e4aSElliott Hughes ], 1300*e1fe3e4aSElliott Hughes ), 1301*e1fe3e4aSElliott Hughes ( 1302*e1fe3e4aSElliott Hughes [ 1303*e1fe3e4aSElliott Hughes dict( 1304*e1fe3e4aSElliott Hughes tag="opsz", 1305*e1fe3e4aSElliott Hughes name="Optical Size", 1306*e1fe3e4aSElliott Hughes values=[ 1307*e1fe3e4aSElliott Hughes dict(nominalValue=6, rangeMaxValue=10, name="Small"), 1308*e1fe3e4aSElliott Hughes dict( 1309*e1fe3e4aSElliott Hughes rangeMinValue=10, 1310*e1fe3e4aSElliott Hughes nominalValue=14, 1311*e1fe3e4aSElliott Hughes rangeMaxValue=24, 1312*e1fe3e4aSElliott Hughes name="Text", 1313*e1fe3e4aSElliott Hughes flags=0x2, 1314*e1fe3e4aSElliott Hughes ), 1315*e1fe3e4aSElliott Hughes dict(rangeMinValue=24, nominalValue=600, name="Display"), 1316*e1fe3e4aSElliott Hughes ], 1317*e1fe3e4aSElliott Hughes ) 1318*e1fe3e4aSElliott Hughes ], 1319*e1fe3e4aSElliott Hughes None, 1320*e1fe3e4aSElliott Hughes 2, 1321*e1fe3e4aSElliott Hughes [ 1322*e1fe3e4aSElliott Hughes " <STAT>", 1323*e1fe3e4aSElliott Hughes ' <Version value="0x00010001"/>', 1324*e1fe3e4aSElliott Hughes ' <DesignAxisRecordSize value="8"/>', 1325*e1fe3e4aSElliott Hughes " <!-- DesignAxisCount=1 -->", 1326*e1fe3e4aSElliott Hughes " <DesignAxisRecord>", 1327*e1fe3e4aSElliott Hughes ' <Axis index="0">', 1328*e1fe3e4aSElliott Hughes ' <AxisTag value="opsz"/>', 1329*e1fe3e4aSElliott Hughes ' <AxisNameID value="256"/> <!-- Optical Size -->', 1330*e1fe3e4aSElliott Hughes ' <AxisOrdering value="0"/>', 1331*e1fe3e4aSElliott Hughes " </Axis>", 1332*e1fe3e4aSElliott Hughes " </DesignAxisRecord>", 1333*e1fe3e4aSElliott Hughes " <!-- AxisValueCount=3 -->", 1334*e1fe3e4aSElliott Hughes " <AxisValueArray>", 1335*e1fe3e4aSElliott Hughes ' <AxisValue index="0" Format="2">', 1336*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1337*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1338*e1fe3e4aSElliott Hughes ' <ValueNameID value="257"/> <!-- Small -->', 1339*e1fe3e4aSElliott Hughes ' <NominalValue value="6.0"/>', 1340*e1fe3e4aSElliott Hughes ' <RangeMinValue value="-32768.0"/>', 1341*e1fe3e4aSElliott Hughes ' <RangeMaxValue value="10.0"/>', 1342*e1fe3e4aSElliott Hughes " </AxisValue>", 1343*e1fe3e4aSElliott Hughes ' <AxisValue index="1" Format="2">', 1344*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1345*e1fe3e4aSElliott Hughes ' <Flags value="2"/> <!-- ElidableAxisValueName -->', 1346*e1fe3e4aSElliott Hughes ' <ValueNameID value="258"/> <!-- Text -->', 1347*e1fe3e4aSElliott Hughes ' <NominalValue value="14.0"/>', 1348*e1fe3e4aSElliott Hughes ' <RangeMinValue value="10.0"/>', 1349*e1fe3e4aSElliott Hughes ' <RangeMaxValue value="24.0"/>', 1350*e1fe3e4aSElliott Hughes " </AxisValue>", 1351*e1fe3e4aSElliott Hughes ' <AxisValue index="2" Format="2">', 1352*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1353*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1354*e1fe3e4aSElliott Hughes ' <ValueNameID value="259"/> <!-- Display -->', 1355*e1fe3e4aSElliott Hughes ' <NominalValue value="600.0"/>', 1356*e1fe3e4aSElliott Hughes ' <RangeMinValue value="24.0"/>', 1357*e1fe3e4aSElliott Hughes ' <RangeMaxValue value="32767.99998"/>', 1358*e1fe3e4aSElliott Hughes " </AxisValue>", 1359*e1fe3e4aSElliott Hughes " </AxisValueArray>", 1360*e1fe3e4aSElliott Hughes ' <ElidedFallbackNameID value="2"/> <!-- missing from name table -->', 1361*e1fe3e4aSElliott Hughes " </STAT>", 1362*e1fe3e4aSElliott Hughes ], 1363*e1fe3e4aSElliott Hughes ), 1364*e1fe3e4aSElliott Hughes ( 1365*e1fe3e4aSElliott Hughes [ 1366*e1fe3e4aSElliott Hughes dict(tag="wght", name="Weight", ordering=1, values=[]), 1367*e1fe3e4aSElliott Hughes dict( 1368*e1fe3e4aSElliott Hughes tag="ABCD", 1369*e1fe3e4aSElliott Hughes name="ABCDTest", 1370*e1fe3e4aSElliott Hughes ordering=0, 1371*e1fe3e4aSElliott Hughes values=[dict(value=100, name="Regular", flags=0x2)], 1372*e1fe3e4aSElliott Hughes ), 1373*e1fe3e4aSElliott Hughes ], 1374*e1fe3e4aSElliott Hughes [dict(location=dict(wght=300, ABCD=100), name="Regular ABCD")], 1375*e1fe3e4aSElliott Hughes 18, 1376*e1fe3e4aSElliott Hughes [ 1377*e1fe3e4aSElliott Hughes " <STAT>", 1378*e1fe3e4aSElliott Hughes ' <Version value="0x00010002"/>', 1379*e1fe3e4aSElliott Hughes ' <DesignAxisRecordSize value="8"/>', 1380*e1fe3e4aSElliott Hughes " <!-- DesignAxisCount=2 -->", 1381*e1fe3e4aSElliott Hughes " <DesignAxisRecord>", 1382*e1fe3e4aSElliott Hughes ' <Axis index="0">', 1383*e1fe3e4aSElliott Hughes ' <AxisTag value="wght"/>', 1384*e1fe3e4aSElliott Hughes ' <AxisNameID value="256"/> <!-- Weight -->', 1385*e1fe3e4aSElliott Hughes ' <AxisOrdering value="1"/>', 1386*e1fe3e4aSElliott Hughes " </Axis>", 1387*e1fe3e4aSElliott Hughes ' <Axis index="1">', 1388*e1fe3e4aSElliott Hughes ' <AxisTag value="ABCD"/>', 1389*e1fe3e4aSElliott Hughes ' <AxisNameID value="257"/> <!-- ABCDTest -->', 1390*e1fe3e4aSElliott Hughes ' <AxisOrdering value="0"/>', 1391*e1fe3e4aSElliott Hughes " </Axis>", 1392*e1fe3e4aSElliott Hughes " </DesignAxisRecord>", 1393*e1fe3e4aSElliott Hughes " <!-- AxisValueCount=2 -->", 1394*e1fe3e4aSElliott Hughes " <AxisValueArray>", 1395*e1fe3e4aSElliott Hughes ' <AxisValue index="0" Format="4">', 1396*e1fe3e4aSElliott Hughes " <!-- AxisCount=2 -->", 1397*e1fe3e4aSElliott Hughes ' <Flags value="0"/>', 1398*e1fe3e4aSElliott Hughes ' <ValueNameID value="259"/> <!-- Regular ABCD -->', 1399*e1fe3e4aSElliott Hughes ' <AxisValueRecord index="0">', 1400*e1fe3e4aSElliott Hughes ' <AxisIndex value="0"/>', 1401*e1fe3e4aSElliott Hughes ' <Value value="300.0"/>', 1402*e1fe3e4aSElliott Hughes " </AxisValueRecord>", 1403*e1fe3e4aSElliott Hughes ' <AxisValueRecord index="1">', 1404*e1fe3e4aSElliott Hughes ' <AxisIndex value="1"/>', 1405*e1fe3e4aSElliott Hughes ' <Value value="100.0"/>', 1406*e1fe3e4aSElliott Hughes " </AxisValueRecord>", 1407*e1fe3e4aSElliott Hughes " </AxisValue>", 1408*e1fe3e4aSElliott Hughes ' <AxisValue index="1" Format="1">', 1409*e1fe3e4aSElliott Hughes ' <AxisIndex value="1"/>', 1410*e1fe3e4aSElliott Hughes ' <Flags value="2"/> <!-- ElidableAxisValueName -->', 1411*e1fe3e4aSElliott Hughes ' <ValueNameID value="258"/> <!-- Regular -->', 1412*e1fe3e4aSElliott Hughes ' <Value value="100.0"/>', 1413*e1fe3e4aSElliott Hughes " </AxisValue>", 1414*e1fe3e4aSElliott Hughes " </AxisValueArray>", 1415*e1fe3e4aSElliott Hughes ' <ElidedFallbackNameID value="18"/> <!-- missing from name table -->', 1416*e1fe3e4aSElliott Hughes " </STAT>", 1417*e1fe3e4aSElliott Hughes ], 1418*e1fe3e4aSElliott Hughes ), 1419*e1fe3e4aSElliott Hughes] 1420*e1fe3e4aSElliott Hughes 1421*e1fe3e4aSElliott Hughes 1422*e1fe3e4aSElliott Hughes@pytest.mark.parametrize( 1423*e1fe3e4aSElliott Hughes "axes, axisValues, elidedFallbackName, expected_ttx", buildStatTable_test_data 1424*e1fe3e4aSElliott Hughes) 1425*e1fe3e4aSElliott Hughesdef test_buildStatTable(axes, axisValues, elidedFallbackName, expected_ttx): 1426*e1fe3e4aSElliott Hughes font = ttLib.TTFont() 1427*e1fe3e4aSElliott Hughes font["name"] = ttLib.newTable("name") 1428*e1fe3e4aSElliott Hughes font["name"].names = [] 1429*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/issues/1985 1430*e1fe3e4aSElliott Hughes # Add nameID < 256 that matches a test axis name, to test whether 1431*e1fe3e4aSElliott Hughes # the nameID is not reused: AxisNameIDs must be > 255 according 1432*e1fe3e4aSElliott Hughes # to the spec. 1433*e1fe3e4aSElliott Hughes font["name"].addMultilingualName(dict(en="ABCDTest"), nameID=6) 1434*e1fe3e4aSElliott Hughes builder.buildStatTable(font, axes, axisValues, elidedFallbackName) 1435*e1fe3e4aSElliott Hughes f = io.StringIO() 1436*e1fe3e4aSElliott Hughes font.saveXML(f, tables=["STAT"]) 1437*e1fe3e4aSElliott Hughes ttx = f.getvalue().splitlines() 1438*e1fe3e4aSElliott Hughes ttx = ttx[3:-2] # strip XML header and <ttFont> element 1439*e1fe3e4aSElliott Hughes assert expected_ttx == ttx 1440*e1fe3e4aSElliott Hughes # Compile and round-trip 1441*e1fe3e4aSElliott Hughes f = io.BytesIO() 1442*e1fe3e4aSElliott Hughes font.save(f) 1443*e1fe3e4aSElliott Hughes font = ttLib.TTFont(f) 1444*e1fe3e4aSElliott Hughes f = io.StringIO() 1445*e1fe3e4aSElliott Hughes font.saveXML(f, tables=["STAT"]) 1446*e1fe3e4aSElliott Hughes ttx = f.getvalue().splitlines() 1447*e1fe3e4aSElliott Hughes ttx = ttx[3:-2] # strip XML header and <ttFont> element 1448*e1fe3e4aSElliott Hughes assert expected_ttx == ttx 1449*e1fe3e4aSElliott Hughes 1450*e1fe3e4aSElliott Hughes 1451*e1fe3e4aSElliott Hughesdef test_buildStatTable_platform_specific_names(): 1452*e1fe3e4aSElliott Hughes # PR: https://github.com/fonttools/fonttools/pull/2528 1453*e1fe3e4aSElliott Hughes # Introduce new 'platform' feature for creating a STAT table. 1454*e1fe3e4aSElliott Hughes # Set windowsNames and or macNames to create name table entries 1455*e1fe3e4aSElliott Hughes # in the specified platforms 1456*e1fe3e4aSElliott Hughes font_obj = ttLib.TTFont() 1457*e1fe3e4aSElliott Hughes font_obj["name"] = ttLib.newTable("name") 1458*e1fe3e4aSElliott Hughes font_obj["name"].names = [] 1459*e1fe3e4aSElliott Hughes 1460*e1fe3e4aSElliott Hughes wght_values = [ 1461*e1fe3e4aSElliott Hughes dict(nominalValue=200, rangeMinValue=200, rangeMaxValue=250, name="ExtraLight"), 1462*e1fe3e4aSElliott Hughes dict(nominalValue=300, rangeMinValue=250, rangeMaxValue=350, name="Light"), 1463*e1fe3e4aSElliott Hughes dict( 1464*e1fe3e4aSElliott Hughes nominalValue=400, 1465*e1fe3e4aSElliott Hughes rangeMinValue=350, 1466*e1fe3e4aSElliott Hughes rangeMaxValue=450, 1467*e1fe3e4aSElliott Hughes name="Regular", 1468*e1fe3e4aSElliott Hughes flags=0x2, 1469*e1fe3e4aSElliott Hughes ), 1470*e1fe3e4aSElliott Hughes dict(nominalValue=500, rangeMinValue=450, rangeMaxValue=650, name="Medium"), 1471*e1fe3e4aSElliott Hughes dict(nominalValue=700, rangeMinValue=650, rangeMaxValue=750, name="Bold"), 1472*e1fe3e4aSElliott Hughes dict(nominalValue=800, rangeMinValue=750, rangeMaxValue=850, name="ExtraBold"), 1473*e1fe3e4aSElliott Hughes dict(nominalValue=900, rangeMinValue=850, rangeMaxValue=900, name="Black"), 1474*e1fe3e4aSElliott Hughes ] 1475*e1fe3e4aSElliott Hughes 1476*e1fe3e4aSElliott Hughes AXES = [ 1477*e1fe3e4aSElliott Hughes dict( 1478*e1fe3e4aSElliott Hughes tag="wght", 1479*e1fe3e4aSElliott Hughes name="Weight", 1480*e1fe3e4aSElliott Hughes ordering=1, 1481*e1fe3e4aSElliott Hughes values=wght_values, 1482*e1fe3e4aSElliott Hughes ), 1483*e1fe3e4aSElliott Hughes ] 1484*e1fe3e4aSElliott Hughes 1485*e1fe3e4aSElliott Hughes font_obj["name"].setName("ExtraLight", 260, 3, 1, 0x409) 1486*e1fe3e4aSElliott Hughes font_obj["name"].setName("Light", 261, 3, 1, 0x409) 1487*e1fe3e4aSElliott Hughes font_obj["name"].setName("Regular", 262, 3, 1, 0x409) 1488*e1fe3e4aSElliott Hughes font_obj["name"].setName("Medium", 263, 3, 1, 0x409) 1489*e1fe3e4aSElliott Hughes font_obj["name"].setName("Bold", 264, 3, 1, 0x409) 1490*e1fe3e4aSElliott Hughes font_obj["name"].setName("ExtraBold", 265, 3, 1, 0x409) 1491*e1fe3e4aSElliott Hughes font_obj["name"].setName("Black", 266, 3, 1, 0x409) 1492*e1fe3e4aSElliott Hughes 1493*e1fe3e4aSElliott Hughes font_obj["name"].setName("Weight", 270, 3, 1, 0x409) 1494*e1fe3e4aSElliott Hughes 1495*e1fe3e4aSElliott Hughes expected_names = [x.string for x in font_obj["name"].names] 1496*e1fe3e4aSElliott Hughes 1497*e1fe3e4aSElliott Hughes builder.buildStatTable(font_obj, AXES, windowsNames=True, macNames=False) 1498*e1fe3e4aSElliott Hughes actual_names = [x.string for x in font_obj["name"].names] 1499*e1fe3e4aSElliott Hughes 1500*e1fe3e4aSElliott Hughes # no new name records were added by buildStatTable 1501*e1fe3e4aSElliott Hughes # because windows-only names with the same strings were already present 1502*e1fe3e4aSElliott Hughes assert expected_names == actual_names 1503*e1fe3e4aSElliott Hughes 1504*e1fe3e4aSElliott Hughes font_obj["name"].removeNames(nameID=270) 1505*e1fe3e4aSElliott Hughes expected_names = [x.string for x in font_obj["name"].names] + ["Weight"] 1506*e1fe3e4aSElliott Hughes 1507*e1fe3e4aSElliott Hughes builder.buildStatTable(font_obj, AXES, windowsNames=True, macNames=False) 1508*e1fe3e4aSElliott Hughes actual_names = [x.string for x in font_obj["name"].names] 1509*e1fe3e4aSElliott Hughes # One new name records 'Weight' were added by buildStatTable 1510*e1fe3e4aSElliott Hughes assert expected_names == actual_names 1511*e1fe3e4aSElliott Hughes 1512*e1fe3e4aSElliott Hughes builder.buildStatTable(font_obj, AXES, windowsNames=True, macNames=True) 1513*e1fe3e4aSElliott Hughes actual_names = [x.string for x in font_obj["name"].names] 1514*e1fe3e4aSElliott Hughes expected_names = [ 1515*e1fe3e4aSElliott Hughes "Weight", 1516*e1fe3e4aSElliott Hughes "Weight", 1517*e1fe3e4aSElliott Hughes "Weight", 1518*e1fe3e4aSElliott Hughes "ExtraLight", 1519*e1fe3e4aSElliott Hughes "ExtraLight", 1520*e1fe3e4aSElliott Hughes "ExtraLight", 1521*e1fe3e4aSElliott Hughes "Light", 1522*e1fe3e4aSElliott Hughes "Light", 1523*e1fe3e4aSElliott Hughes "Light", 1524*e1fe3e4aSElliott Hughes "Regular", 1525*e1fe3e4aSElliott Hughes "Regular", 1526*e1fe3e4aSElliott Hughes "Regular", 1527*e1fe3e4aSElliott Hughes "Medium", 1528*e1fe3e4aSElliott Hughes "Medium", 1529*e1fe3e4aSElliott Hughes "Medium", 1530*e1fe3e4aSElliott Hughes "Bold", 1531*e1fe3e4aSElliott Hughes "Bold", 1532*e1fe3e4aSElliott Hughes "Bold", 1533*e1fe3e4aSElliott Hughes "ExtraBold", 1534*e1fe3e4aSElliott Hughes "ExtraBold", 1535*e1fe3e4aSElliott Hughes "ExtraBold", 1536*e1fe3e4aSElliott Hughes "Black", 1537*e1fe3e4aSElliott Hughes "Black", 1538*e1fe3e4aSElliott Hughes "Black", 1539*e1fe3e4aSElliott Hughes ] 1540*e1fe3e4aSElliott Hughes # Because there is an inconsistency in the names add new name IDs 1541*e1fe3e4aSElliott Hughes # for each platform -> windowsNames=True, macNames=True 1542*e1fe3e4aSElliott Hughes assert sorted(expected_names) == sorted(actual_names) 1543*e1fe3e4aSElliott Hughes 1544*e1fe3e4aSElliott Hughes 1545*e1fe3e4aSElliott Hughesdef test_stat_infinities(): 1546*e1fe3e4aSElliott Hughes negInf = floatToFixed(builder.AXIS_VALUE_NEGATIVE_INFINITY, 16) 1547*e1fe3e4aSElliott Hughes assert struct.pack(">l", negInf) == b"\x80\x00\x00\x00" 1548*e1fe3e4aSElliott Hughes posInf = floatToFixed(builder.AXIS_VALUE_POSITIVE_INFINITY, 16) 1549*e1fe3e4aSElliott Hughes assert struct.pack(">l", posInf) == b"\x7f\xff\xff\xff" 1550*e1fe3e4aSElliott Hughes 1551*e1fe3e4aSElliott Hughes 1552*e1fe3e4aSElliott Hughesdef test_buildMathTable_empty(): 1553*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1554*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder([]) 1555*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont) 1556*e1fe3e4aSElliott Hughes 1557*e1fe3e4aSElliott Hughes assert "MATH" in ttFont 1558*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1559*e1fe3e4aSElliott Hughes assert mathTable.Version == 0x00010000 1560*e1fe3e4aSElliott Hughes 1561*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1562*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo is None 1563*e1fe3e4aSElliott Hughes assert mathTable.MathVariants is None 1564*e1fe3e4aSElliott Hughes 1565*e1fe3e4aSElliott Hughes 1566*e1fe3e4aSElliott Hughesdef test_buildMathTable_constants(): 1567*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1568*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder([]) 1569*e1fe3e4aSElliott Hughes constants = { 1570*e1fe3e4aSElliott Hughes "AccentBaseHeight": 516, 1571*e1fe3e4aSElliott Hughes "AxisHeight": 262, 1572*e1fe3e4aSElliott Hughes "DelimitedSubFormulaMinHeight": 1500, 1573*e1fe3e4aSElliott Hughes "DisplayOperatorMinHeight": 2339, 1574*e1fe3e4aSElliott Hughes "FlattenedAccentBaseHeight": 698, 1575*e1fe3e4aSElliott Hughes "FractionDenomDisplayStyleGapMin": 198, 1576*e1fe3e4aSElliott Hughes "FractionDenominatorDisplayStyleShiftDown": 698, 1577*e1fe3e4aSElliott Hughes "FractionDenominatorGapMin": 66, 1578*e1fe3e4aSElliott Hughes "FractionDenominatorShiftDown": 465, 1579*e1fe3e4aSElliott Hughes "FractionNumDisplayStyleGapMin": 198, 1580*e1fe3e4aSElliott Hughes "FractionNumeratorDisplayStyleShiftUp": 774, 1581*e1fe3e4aSElliott Hughes "FractionNumeratorGapMin": 66, 1582*e1fe3e4aSElliott Hughes "FractionNumeratorShiftUp": 516, 1583*e1fe3e4aSElliott Hughes "FractionRuleThickness": 66, 1584*e1fe3e4aSElliott Hughes "LowerLimitBaselineDropMin": 585, 1585*e1fe3e4aSElliott Hughes "LowerLimitGapMin": 132, 1586*e1fe3e4aSElliott Hughes "MathLeading": 300, 1587*e1fe3e4aSElliott Hughes "OverbarExtraAscender": 66, 1588*e1fe3e4aSElliott Hughes "OverbarRuleThickness": 66, 1589*e1fe3e4aSElliott Hughes "OverbarVerticalGap": 198, 1590*e1fe3e4aSElliott Hughes "RadicalDegreeBottomRaisePercent": 75, 1591*e1fe3e4aSElliott Hughes "RadicalDisplayStyleVerticalGap": 195, 1592*e1fe3e4aSElliott Hughes "RadicalExtraAscender": 66, 1593*e1fe3e4aSElliott Hughes "RadicalKernAfterDegree": -556, 1594*e1fe3e4aSElliott Hughes "RadicalKernBeforeDegree": 278, 1595*e1fe3e4aSElliott Hughes "RadicalRuleThickness": 66, 1596*e1fe3e4aSElliott Hughes "RadicalVerticalGap": 82, 1597*e1fe3e4aSElliott Hughes "ScriptPercentScaleDown": 70, 1598*e1fe3e4aSElliott Hughes "ScriptScriptPercentScaleDown": 55, 1599*e1fe3e4aSElliott Hughes "SkewedFractionHorizontalGap": 66, 1600*e1fe3e4aSElliott Hughes "SkewedFractionVerticalGap": 77, 1601*e1fe3e4aSElliott Hughes "SpaceAfterScript": 42, 1602*e1fe3e4aSElliott Hughes "StackBottomDisplayStyleShiftDown": 698, 1603*e1fe3e4aSElliott Hughes "StackBottomShiftDown": 465, 1604*e1fe3e4aSElliott Hughes "StackDisplayStyleGapMin": 462, 1605*e1fe3e4aSElliott Hughes "StackGapMin": 198, 1606*e1fe3e4aSElliott Hughes "StackTopDisplayStyleShiftUp": 774, 1607*e1fe3e4aSElliott Hughes "StackTopShiftUp": 516, 1608*e1fe3e4aSElliott Hughes "StretchStackBottomShiftDown": 585, 1609*e1fe3e4aSElliott Hughes "StretchStackGapAboveMin": 132, 1610*e1fe3e4aSElliott Hughes "StretchStackGapBelowMin": 132, 1611*e1fe3e4aSElliott Hughes "StretchStackTopShiftUp": 165, 1612*e1fe3e4aSElliott Hughes "SubSuperscriptGapMin": 264, 1613*e1fe3e4aSElliott Hughes "SubscriptBaselineDropMin": 105, 1614*e1fe3e4aSElliott Hughes "SubscriptShiftDown": 140, 1615*e1fe3e4aSElliott Hughes "SubscriptTopMax": 413, 1616*e1fe3e4aSElliott Hughes "SuperscriptBaselineDropMax": 221, 1617*e1fe3e4aSElliott Hughes "SuperscriptBottomMaxWithSubscript": 413, 1618*e1fe3e4aSElliott Hughes "SuperscriptBottomMin": 129, 1619*e1fe3e4aSElliott Hughes "SuperscriptShiftUp": 477, 1620*e1fe3e4aSElliott Hughes "SuperscriptShiftUpCramped": 358, 1621*e1fe3e4aSElliott Hughes "UnderbarExtraDescender": 66, 1622*e1fe3e4aSElliott Hughes "UnderbarRuleThickness": 66, 1623*e1fe3e4aSElliott Hughes "UnderbarVerticalGap": 198, 1624*e1fe3e4aSElliott Hughes "UpperLimitBaselineRiseMin": 165, 1625*e1fe3e4aSElliott Hughes "UpperLimitGapMin": 132, 1626*e1fe3e4aSElliott Hughes } 1627*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, constants=constants) 1628*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1629*e1fe3e4aSElliott Hughes assert mathTable.MathConstants 1630*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo is None 1631*e1fe3e4aSElliott Hughes assert mathTable.MathVariants is None 1632*e1fe3e4aSElliott Hughes for k, v in constants.items(): 1633*e1fe3e4aSElliott Hughes r = getattr(mathTable.MathConstants, k) 1634*e1fe3e4aSElliott Hughes try: 1635*e1fe3e4aSElliott Hughes r = r.Value 1636*e1fe3e4aSElliott Hughes except AttributeError: 1637*e1fe3e4aSElliott Hughes pass 1638*e1fe3e4aSElliott Hughes assert r == v 1639*e1fe3e4aSElliott Hughes 1640*e1fe3e4aSElliott Hughes 1641*e1fe3e4aSElliott Hughesdef test_buildMathTable_italicsCorrection(): 1642*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1643*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]) 1644*e1fe3e4aSElliott Hughes italicsCorrections = {"A": 100, "C": 300, "D": 400, "E": 500} 1645*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, italicsCorrections=italicsCorrections) 1646*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1647*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1648*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo 1649*e1fe3e4aSElliott Hughes assert mathTable.MathVariants is None 1650*e1fe3e4aSElliott Hughes assert set( 1651*e1fe3e4aSElliott Hughes mathTable.MathGlyphInfo.MathItalicsCorrectionInfo.Coverage.glyphs 1652*e1fe3e4aSElliott Hughes ) == set(italicsCorrections.keys()) 1653*e1fe3e4aSElliott Hughes for glyph, correction in zip( 1654*e1fe3e4aSElliott Hughes mathTable.MathGlyphInfo.MathItalicsCorrectionInfo.Coverage.glyphs, 1655*e1fe3e4aSElliott Hughes mathTable.MathGlyphInfo.MathItalicsCorrectionInfo.ItalicsCorrection, 1656*e1fe3e4aSElliott Hughes ): 1657*e1fe3e4aSElliott Hughes assert correction.Value == italicsCorrections[glyph] 1658*e1fe3e4aSElliott Hughes 1659*e1fe3e4aSElliott Hughes 1660*e1fe3e4aSElliott Hughesdef test_buildMathTable_topAccentAttachment(): 1661*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1662*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]) 1663*e1fe3e4aSElliott Hughes topAccentAttachments = {"A": 10, "B": 20, "C": 30, "E": 50} 1664*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, topAccentAttachments=topAccentAttachments) 1665*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1666*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1667*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo 1668*e1fe3e4aSElliott Hughes assert mathTable.MathVariants is None 1669*e1fe3e4aSElliott Hughes assert set( 1670*e1fe3e4aSElliott Hughes mathTable.MathGlyphInfo.MathTopAccentAttachment.TopAccentCoverage.glyphs 1671*e1fe3e4aSElliott Hughes ) == set(topAccentAttachments.keys()) 1672*e1fe3e4aSElliott Hughes for glyph, attachment in zip( 1673*e1fe3e4aSElliott Hughes mathTable.MathGlyphInfo.MathTopAccentAttachment.TopAccentCoverage.glyphs, 1674*e1fe3e4aSElliott Hughes mathTable.MathGlyphInfo.MathTopAccentAttachment.TopAccentAttachment, 1675*e1fe3e4aSElliott Hughes ): 1676*e1fe3e4aSElliott Hughes assert attachment.Value == topAccentAttachments[glyph] 1677*e1fe3e4aSElliott Hughes 1678*e1fe3e4aSElliott Hughes 1679*e1fe3e4aSElliott Hughesdef test_buildMathTable_extendedShape(): 1680*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1681*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]) 1682*e1fe3e4aSElliott Hughes extendedShapes = {"A", "C", "E", "F"} 1683*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, extendedShapes=extendedShapes) 1684*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1685*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1686*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo 1687*e1fe3e4aSElliott Hughes assert mathTable.MathVariants is None 1688*e1fe3e4aSElliott Hughes assert set(mathTable.MathGlyphInfo.ExtendedShapeCoverage.glyphs) == extendedShapes 1689*e1fe3e4aSElliott Hughes 1690*e1fe3e4aSElliott Hughes 1691*e1fe3e4aSElliott Hughesdef test_buildMathTable_mathKern(): 1692*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1693*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder(["A", "B"]) 1694*e1fe3e4aSElliott Hughes mathKerns = { 1695*e1fe3e4aSElliott Hughes "A": { 1696*e1fe3e4aSElliott Hughes "TopRight": ([10, 20], [10, 20, 30]), 1697*e1fe3e4aSElliott Hughes "BottomRight": ([], [10]), 1698*e1fe3e4aSElliott Hughes "TopLeft": ([10], [0, 20]), 1699*e1fe3e4aSElliott Hughes "BottomLeft": ([-10, 0], [0, 10, 20]), 1700*e1fe3e4aSElliott Hughes }, 1701*e1fe3e4aSElliott Hughes } 1702*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, mathKerns=mathKerns) 1703*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1704*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1705*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo 1706*e1fe3e4aSElliott Hughes assert mathTable.MathVariants is None 1707*e1fe3e4aSElliott Hughes assert set(mathTable.MathGlyphInfo.MathKernInfo.MathKernCoverage.glyphs) == set( 1708*e1fe3e4aSElliott Hughes mathKerns.keys() 1709*e1fe3e4aSElliott Hughes ) 1710*e1fe3e4aSElliott Hughes for glyph, record in zip( 1711*e1fe3e4aSElliott Hughes mathTable.MathGlyphInfo.MathKernInfo.MathKernCoverage.glyphs, 1712*e1fe3e4aSElliott Hughes mathTable.MathGlyphInfo.MathKernInfo.MathKernInfoRecords, 1713*e1fe3e4aSElliott Hughes ): 1714*e1fe3e4aSElliott Hughes h, k = mathKerns[glyph]["TopRight"] 1715*e1fe3e4aSElliott Hughes assert [v.Value for v in record.TopRightMathKern.CorrectionHeight] == h 1716*e1fe3e4aSElliott Hughes assert [v.Value for v in record.TopRightMathKern.KernValue] == k 1717*e1fe3e4aSElliott Hughes h, k = mathKerns[glyph]["BottomRight"] 1718*e1fe3e4aSElliott Hughes assert [v.Value for v in record.BottomRightMathKern.CorrectionHeight] == h 1719*e1fe3e4aSElliott Hughes assert [v.Value for v in record.BottomRightMathKern.KernValue] == k 1720*e1fe3e4aSElliott Hughes h, k = mathKerns[glyph]["TopLeft"] 1721*e1fe3e4aSElliott Hughes assert [v.Value for v in record.TopLeftMathKern.CorrectionHeight] == h 1722*e1fe3e4aSElliott Hughes assert [v.Value for v in record.TopLeftMathKern.KernValue] == k 1723*e1fe3e4aSElliott Hughes h, k = mathKerns[glyph]["BottomLeft"] 1724*e1fe3e4aSElliott Hughes assert [v.Value for v in record.BottomLeftMathKern.CorrectionHeight] == h 1725*e1fe3e4aSElliott Hughes assert [v.Value for v in record.BottomLeftMathKern.KernValue] == k 1726*e1fe3e4aSElliott Hughes 1727*e1fe3e4aSElliott Hughes 1728*e1fe3e4aSElliott Hughesdef test_buildMathTable_vertVariants(): 1729*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1730*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder(["A", "A.size1", "A.size2"]) 1731*e1fe3e4aSElliott Hughes vertGlyphVariants = {"A": [("A.size1", 100), ("A.size2", 200)]} 1732*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, vertGlyphVariants=vertGlyphVariants) 1733*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1734*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1735*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo is None 1736*e1fe3e4aSElliott Hughes assert mathTable.MathVariants 1737*e1fe3e4aSElliott Hughes assert set(mathTable.MathVariants.VertGlyphCoverage.glyphs) == set( 1738*e1fe3e4aSElliott Hughes vertGlyphVariants.keys() 1739*e1fe3e4aSElliott Hughes ) 1740*e1fe3e4aSElliott Hughes for glyph, construction in zip( 1741*e1fe3e4aSElliott Hughes mathTable.MathVariants.VertGlyphCoverage.glyphs, 1742*e1fe3e4aSElliott Hughes mathTable.MathVariants.VertGlyphConstruction, 1743*e1fe3e4aSElliott Hughes ): 1744*e1fe3e4aSElliott Hughes assert [ 1745*e1fe3e4aSElliott Hughes (r.VariantGlyph, r.AdvanceMeasurement) 1746*e1fe3e4aSElliott Hughes for r in construction.MathGlyphVariantRecord 1747*e1fe3e4aSElliott Hughes ] == vertGlyphVariants[glyph] 1748*e1fe3e4aSElliott Hughes 1749*e1fe3e4aSElliott Hughes 1750*e1fe3e4aSElliott Hughesdef test_buildMathTable_horizVariants(): 1751*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1752*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder(["A", "A.size1", "A.size2"]) 1753*e1fe3e4aSElliott Hughes horizGlyphVariants = {"A": [("A.size1", 100), ("A.size2", 200)]} 1754*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, horizGlyphVariants=horizGlyphVariants) 1755*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1756*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1757*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo is None 1758*e1fe3e4aSElliott Hughes assert mathTable.MathVariants 1759*e1fe3e4aSElliott Hughes assert set(mathTable.MathVariants.HorizGlyphCoverage.glyphs) == set( 1760*e1fe3e4aSElliott Hughes horizGlyphVariants.keys() 1761*e1fe3e4aSElliott Hughes ) 1762*e1fe3e4aSElliott Hughes for glyph, construction in zip( 1763*e1fe3e4aSElliott Hughes mathTable.MathVariants.HorizGlyphCoverage.glyphs, 1764*e1fe3e4aSElliott Hughes mathTable.MathVariants.HorizGlyphConstruction, 1765*e1fe3e4aSElliott Hughes ): 1766*e1fe3e4aSElliott Hughes assert [ 1767*e1fe3e4aSElliott Hughes (r.VariantGlyph, r.AdvanceMeasurement) 1768*e1fe3e4aSElliott Hughes for r in construction.MathGlyphVariantRecord 1769*e1fe3e4aSElliott Hughes ] == horizGlyphVariants[glyph] 1770*e1fe3e4aSElliott Hughes 1771*e1fe3e4aSElliott Hughes 1772*e1fe3e4aSElliott Hughesdef test_buildMathTable_vertAssembly(): 1773*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1774*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder(["A", "A.top", "A.middle", "A.bottom", "A.extender"]) 1775*e1fe3e4aSElliott Hughes vertGlyphAssembly = { 1776*e1fe3e4aSElliott Hughes "A": [ 1777*e1fe3e4aSElliott Hughes [ 1778*e1fe3e4aSElliott Hughes ("A.bottom", 0, 0, 100, 200), 1779*e1fe3e4aSElliott Hughes ("A.extender", 1, 50, 50, 100), 1780*e1fe3e4aSElliott Hughes ("A.middle", 0, 100, 100, 200), 1781*e1fe3e4aSElliott Hughes ("A.extender", 1, 50, 50, 100), 1782*e1fe3e4aSElliott Hughes ("A.top", 0, 100, 0, 200), 1783*e1fe3e4aSElliott Hughes ], 1784*e1fe3e4aSElliott Hughes 10, 1785*e1fe3e4aSElliott Hughes ], 1786*e1fe3e4aSElliott Hughes } 1787*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, vertGlyphAssembly=vertGlyphAssembly) 1788*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1789*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1790*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo is None 1791*e1fe3e4aSElliott Hughes assert mathTable.MathVariants 1792*e1fe3e4aSElliott Hughes assert set(mathTable.MathVariants.VertGlyphCoverage.glyphs) == set( 1793*e1fe3e4aSElliott Hughes vertGlyphAssembly.keys() 1794*e1fe3e4aSElliott Hughes ) 1795*e1fe3e4aSElliott Hughes for glyph, construction in zip( 1796*e1fe3e4aSElliott Hughes mathTable.MathVariants.VertGlyphCoverage.glyphs, 1797*e1fe3e4aSElliott Hughes mathTable.MathVariants.VertGlyphConstruction, 1798*e1fe3e4aSElliott Hughes ): 1799*e1fe3e4aSElliott Hughes assert [ 1800*e1fe3e4aSElliott Hughes [ 1801*e1fe3e4aSElliott Hughes ( 1802*e1fe3e4aSElliott Hughes r.glyph, 1803*e1fe3e4aSElliott Hughes r.PartFlags, 1804*e1fe3e4aSElliott Hughes r.StartConnectorLength, 1805*e1fe3e4aSElliott Hughes r.EndConnectorLength, 1806*e1fe3e4aSElliott Hughes r.FullAdvance, 1807*e1fe3e4aSElliott Hughes ) 1808*e1fe3e4aSElliott Hughes for r in construction.GlyphAssembly.PartRecords 1809*e1fe3e4aSElliott Hughes ], 1810*e1fe3e4aSElliott Hughes construction.GlyphAssembly.ItalicsCorrection.Value, 1811*e1fe3e4aSElliott Hughes ] == vertGlyphAssembly[glyph] 1812*e1fe3e4aSElliott Hughes 1813*e1fe3e4aSElliott Hughes 1814*e1fe3e4aSElliott Hughesdef test_buildMathTable_horizAssembly(): 1815*e1fe3e4aSElliott Hughes ttFont = ttLib.TTFont() 1816*e1fe3e4aSElliott Hughes ttFont.setGlyphOrder(["A", "A.top", "A.middle", "A.bottom", "A.extender"]) 1817*e1fe3e4aSElliott Hughes horizGlyphAssembly = { 1818*e1fe3e4aSElliott Hughes "A": [ 1819*e1fe3e4aSElliott Hughes [ 1820*e1fe3e4aSElliott Hughes ("A.bottom", 0, 0, 100, 200), 1821*e1fe3e4aSElliott Hughes ("A.extender", 1, 50, 50, 100), 1822*e1fe3e4aSElliott Hughes ("A.middle", 0, 100, 100, 200), 1823*e1fe3e4aSElliott Hughes ("A.extender", 1, 50, 50, 100), 1824*e1fe3e4aSElliott Hughes ("A.top", 0, 100, 0, 200), 1825*e1fe3e4aSElliott Hughes ], 1826*e1fe3e4aSElliott Hughes 10, 1827*e1fe3e4aSElliott Hughes ], 1828*e1fe3e4aSElliott Hughes } 1829*e1fe3e4aSElliott Hughes builder.buildMathTable(ttFont, horizGlyphAssembly=horizGlyphAssembly) 1830*e1fe3e4aSElliott Hughes mathTable = ttFont["MATH"].table 1831*e1fe3e4aSElliott Hughes assert mathTable.MathConstants is None 1832*e1fe3e4aSElliott Hughes assert mathTable.MathGlyphInfo is None 1833*e1fe3e4aSElliott Hughes assert mathTable.MathVariants 1834*e1fe3e4aSElliott Hughes assert set(mathTable.MathVariants.HorizGlyphCoverage.glyphs) == set( 1835*e1fe3e4aSElliott Hughes horizGlyphAssembly.keys() 1836*e1fe3e4aSElliott Hughes ) 1837*e1fe3e4aSElliott Hughes for glyph, construction in zip( 1838*e1fe3e4aSElliott Hughes mathTable.MathVariants.HorizGlyphCoverage.glyphs, 1839*e1fe3e4aSElliott Hughes mathTable.MathVariants.HorizGlyphConstruction, 1840*e1fe3e4aSElliott Hughes ): 1841*e1fe3e4aSElliott Hughes assert [ 1842*e1fe3e4aSElliott Hughes [ 1843*e1fe3e4aSElliott Hughes ( 1844*e1fe3e4aSElliott Hughes r.glyph, 1845*e1fe3e4aSElliott Hughes r.PartFlags, 1846*e1fe3e4aSElliott Hughes r.StartConnectorLength, 1847*e1fe3e4aSElliott Hughes r.EndConnectorLength, 1848*e1fe3e4aSElliott Hughes r.FullAdvance, 1849*e1fe3e4aSElliott Hughes ) 1850*e1fe3e4aSElliott Hughes for r in construction.GlyphAssembly.PartRecords 1851*e1fe3e4aSElliott Hughes ], 1852*e1fe3e4aSElliott Hughes construction.GlyphAssembly.ItalicsCorrection.Value, 1853*e1fe3e4aSElliott Hughes ] == horizGlyphAssembly[glyph] 1854*e1fe3e4aSElliott Hughes 1855*e1fe3e4aSElliott Hughes 1856*e1fe3e4aSElliott Hughesclass ChainContextualRulesetTest(object): 1857*e1fe3e4aSElliott Hughes def test_makeRulesets(self): 1858*e1fe3e4aSElliott Hughes font = ttLib.TTFont() 1859*e1fe3e4aSElliott Hughes font.setGlyphOrder(["a", "b", "c", "d", "A", "B", "C", "D", "E"]) 1860*e1fe3e4aSElliott Hughes sb = builder.ChainContextSubstBuilder(font, None) 1861*e1fe3e4aSElliott Hughes prefix, input_, suffix, lookups = [["a"], ["b"]], [["c"]], [], [None] 1862*e1fe3e4aSElliott Hughes sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups)) 1863*e1fe3e4aSElliott Hughes 1864*e1fe3e4aSElliott Hughes prefix, input_, suffix, lookups = [["a"], ["d"]], [["c"]], [], [None] 1865*e1fe3e4aSElliott Hughes sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups)) 1866*e1fe3e4aSElliott Hughes 1867*e1fe3e4aSElliott Hughes sb.add_subtable_break(None) 1868*e1fe3e4aSElliott Hughes 1869*e1fe3e4aSElliott Hughes # Second subtable has some glyph classes 1870*e1fe3e4aSElliott Hughes prefix, input_, suffix, lookups = [["A"]], [["E"]], [], [None] 1871*e1fe3e4aSElliott Hughes sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups)) 1872*e1fe3e4aSElliott Hughes prefix, input_, suffix, lookups = [["A"]], [["C", "D"]], [], [None] 1873*e1fe3e4aSElliott Hughes sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups)) 1874*e1fe3e4aSElliott Hughes prefix, input_, suffix, lookups = [["A", "B"]], [["E"]], [], [None] 1875*e1fe3e4aSElliott Hughes sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups)) 1876*e1fe3e4aSElliott Hughes 1877*e1fe3e4aSElliott Hughes sb.add_subtable_break(None) 1878*e1fe3e4aSElliott Hughes 1879*e1fe3e4aSElliott Hughes # Third subtable has no pre/post context 1880*e1fe3e4aSElliott Hughes prefix, input_, suffix, lookups = [], [["E"]], [], [None] 1881*e1fe3e4aSElliott Hughes sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups)) 1882*e1fe3e4aSElliott Hughes prefix, input_, suffix, lookups = [], [["C", "D"]], [], [None] 1883*e1fe3e4aSElliott Hughes sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups)) 1884*e1fe3e4aSElliott Hughes 1885*e1fe3e4aSElliott Hughes rulesets = sb.rulesets() 1886*e1fe3e4aSElliott Hughes assert len(rulesets) == 3 1887*e1fe3e4aSElliott Hughes assert rulesets[0].hasPrefixOrSuffix 1888*e1fe3e4aSElliott Hughes assert not rulesets[0].hasAnyGlyphClasses 1889*e1fe3e4aSElliott Hughes cd = rulesets[0].format2ClassDefs() 1890*e1fe3e4aSElliott Hughes assert set(cd[0].classes()[1:]) == set([("d",), ("b",), ("a",)]) 1891*e1fe3e4aSElliott Hughes assert set(cd[1].classes()[1:]) == set([("c",)]) 1892*e1fe3e4aSElliott Hughes assert set(cd[2].classes()[1:]) == set() 1893*e1fe3e4aSElliott Hughes 1894*e1fe3e4aSElliott Hughes assert rulesets[1].hasPrefixOrSuffix 1895*e1fe3e4aSElliott Hughes assert rulesets[1].hasAnyGlyphClasses 1896*e1fe3e4aSElliott Hughes assert not rulesets[1].format2ClassDefs() 1897*e1fe3e4aSElliott Hughes 1898*e1fe3e4aSElliott Hughes assert not rulesets[2].hasPrefixOrSuffix 1899*e1fe3e4aSElliott Hughes assert rulesets[2].hasAnyGlyphClasses 1900*e1fe3e4aSElliott Hughes assert rulesets[2].format2ClassDefs() 1901*e1fe3e4aSElliott Hughes cd = rulesets[2].format2ClassDefs() 1902*e1fe3e4aSElliott Hughes assert set(cd[0].classes()[1:]) == set() 1903*e1fe3e4aSElliott Hughes assert set(cd[1].classes()[1:]) == set([("C", "D"), ("E",)]) 1904*e1fe3e4aSElliott Hughes assert set(cd[2].classes()[1:]) == set() 1905*e1fe3e4aSElliott Hughes 1906*e1fe3e4aSElliott Hughes 1907*e1fe3e4aSElliott Hughesif __name__ == "__main__": 1908*e1fe3e4aSElliott Hughes import sys 1909*e1fe3e4aSElliott Hughes 1910*e1fe3e4aSElliott Hughes sys.exit(pytest.main(sys.argv)) 1911