Exemple #1
0
    def _makeGPOS():
        from fontio3.GPOS import (GPOS, pairglyphs, pairglyphs_key, pairvalues,
                                  value)

        from fontio3.opentype import (featuredict, featuretable, langsys,
                                      langsys_optfeatset, langsysdict, lookup,
                                      scriptdict)

        # Return a GPOS with (15, 32) kerned -18, and (20, 30) kerned +14
        v1 = value.Value(xPlacement=-18)
        v2 = value.Value(xPlacement=14)
        pv1 = pairvalues.PairValues(second=v1)
        pv2 = pairvalues.PairValues(second=v2)
        k1 = pairglyphs_key.Key((15, 32))
        k2 = pairglyphs_key.Key((20, 30))
        pairObj = pairglyphs.PairGlyphs({k1: pv1, k2: pv2})
        lkObj = lookup.Lookup([pairObj])
        ftObj = featuretable.FeatureTable([lkObj])
        featObj = featuredict.FeatureDict({b'kern0001': ftObj})

        optSetObj = langsys_optfeatset.OptFeatSet({b'kern0001'})
        lsObj = langsys.LangSys(optionalFeatures=optSetObj)
        lsdObj = langsysdict.LangSysDict({}, defaultLangSys=lsObj)
        scptObj = scriptdict.ScriptDict({b'latn': lsdObj})
        return GPOS.GPOS(features=featObj, scripts=scptObj)
Exemple #2
0
    def _makeTest():
        """
        The test case here recognizes the glyph sequence 25-80-26, where 25 is
        the backtrack and 26 the lookahead. The position of glyph 80 is
        adjusted.
        """

        from fontio3 import hmtx, utilities
        from fontio3.GPOS import single, value

        from fontio3.opentype import (lookup, pschainglyph_glyphtuple,
                                      pschainglyph_key, pslookupgroup,
                                      pslookuprecord)

        s1 = single.Single({80: value.Value(xPlacement=-25)})
        lk1 = lookup.Lookup([s1])
        psr1 = pslookuprecord.PSLookupRecord(0, lk1)
        psg1 = pslookupgroup.PSLookupGroup([psr1])
        backPart = pschainglyph_glyphtuple.GlyphTuple([25])
        inPart = pschainglyph_glyphtuple.GlyphTuple([80])
        lookPart = pschainglyph_glyphtuple.GlyphTuple([26])
        key1 = pschainglyph_key.Key([backPart, inPart, lookPart])
        r = ChainGlyph({key1: psg1})
        e = utilities.fakeEditor(0x10000)
        e.hmtx = hmtx.Hmtx()
        e.hmtx[25] = hmtx.MtxEntry(900, 50)
        e.hmtx[26] = hmtx.MtxEntry(970, 40)
        e.hmtx[80] = hmtx.MtxEntry(1020, 55)

        return r, e
    def _makeTest():
        """
        The test case here recognizes the glyph sequence 1-2-3, and adjusts the
        positioning of class 2.
        """

        from fontio3 import hmtx, utilities
        from fontio3.GPOS import single, value

        from fontio3.opentype import (classdef, lookup,
                                      pschainclass_classtuple,
                                      pschainclass_key, pslookupgroup,
                                      pslookuprecord)

        v1 = value.Value(xPlacement=-25)
        v2 = value.Value(xPlacement=-29)
        s1 = single.Single({80: v1, 81: v2, 82: v1})
        lk1 = lookup.Lookup([s1])
        psr1 = pslookuprecord.PSLookupRecord(0, lk1)
        psg = pslookupgroup.PSLookupGroup([psr1])
        backPart = pschainclass_classtuple.ClassTuple([1])
        inPart = pschainclass_classtuple.ClassTuple([1])
        lookPart = pschainclass_classtuple.ClassTuple([1])
        key = pschainclass_key.Key([backPart, inPart, lookPart])
        cdBack = classdef.ClassDef({25: 1})
        cdIn = classdef.ClassDef({80: 1, 81: 1, 82: 1})
        cdLook = classdef.ClassDef({26: 1})

        r = ChainClass({key: psg},
                       classDefBacktrack=cdBack,
                       classDefInput=cdIn,
                       classDefLookahead=cdLook)

        e = utilities.fakeEditor(0x10000)
        e.hmtx = hmtx.Hmtx()
        e.hmtx[25] = hmtx.MtxEntry(900, 50)
        e.hmtx[26] = hmtx.MtxEntry(970, 40)
        e.hmtx[80] = hmtx.MtxEntry(1020, 55)
        e.hmtx[81] = hmtx.MtxEntry(1090, 85)
        e.hmtx[82] = hmtx.MtxEntry(1050, 70)

        return r, e
    def _makeTest():
        """
        The test case here recognizes the glyph sequence 25-80-26, and adjusts
        the positioning of glyphs 25 and 26.
        """

        from fontio3 import hmtx
        from fontio3.GPOS import single, value

        from fontio3.opentype import (lookup, pscontextglyph_key,
                                      pslookupgroup, pslookuprecord)

        s1 = single.Single({25: value.Value(xPlacement=-25)})
        s2 = single.Single({26: value.Value(xPlacement=25)})
        s3 = single.Single({12: value.Value(xPlacement=-15)})
        s4 = single.Single({12: value.Value(xPlacement=15)})

        lk1 = lookup.Lookup([s1])
        lk2 = lookup.Lookup([s2])
        lk3 = lookup.Lookup([s3])
        lk4 = lookup.Lookup([s4])

        psr1 = pslookuprecord.PSLookupRecord(0, lk1)
        psr2 = pslookuprecord.PSLookupRecord(2, lk2)
        psr3 = pslookuprecord.PSLookupRecord(0, lk3)
        psr4 = pslookuprecord.PSLookupRecord(2, lk4)

        psg1 = pslookupgroup.PSLookupGroup([psr1, psr2])
        psg2 = pslookupgroup.PSLookupGroup([psr3, psr4])
        key1 = pscontextglyph_key.Key([25, 80, 26], ruleOrder=0)
        key2 = pscontextglyph_key.Key([12, 15, 12], ruleOrder=1)

        r = ContextGlyph({key1: psg1, key2: psg2})
        e = utilities.fakeEditor(0x10000)
        e.hmtx = hmtx.Hmtx()
        e.hmtx[12] = hmtx.MtxEntry(700, 50)
        e.hmtx[15] = hmtx.MtxEntry(690, 50)
        e.hmtx[25] = hmtx.MtxEntry(900, 50)
        e.hmtx[26] = hmtx.MtxEntry(970, 40)
        e.hmtx[80] = hmtx.MtxEntry(1020, 55)

        return r, e
Exemple #5
0
    def _makeTest():
        from fontio3 import hmtx, utilities
        from fontio3.GPOS import single, value

        from fontio3.opentype import (coverageset, lookup,
                                      pschaincoverage_coveragetuple,
                                      pschaincoverage_key, pslookupgroup,
                                      pslookuprecord)

        v1 = value.Value(xPlacement=-25)
        v2 = value.Value(xPlacement=-29)
        s1 = single.Single({80: v1, 81: v2, 82: v1})
        lk1 = lookup.Lookup([s1])
        psr1 = pslookuprecord.PSLookupRecord(0, lk1)
        psg = pslookupgroup.PSLookupGroup([psr1])

        cs1 = coverageset.CoverageSet([20, 22])
        cs2 = coverageset.CoverageSet([23])
        cs3 = coverageset.CoverageSet([80, 81, 82])
        cs4 = coverageset.CoverageSet([22])
        cs5 = coverageset.CoverageSet([20, 40])
        ct1 = pschaincoverage_coveragetuple.CoverageTuple([cs1, cs2])
        ct2 = pschaincoverage_coveragetuple.CoverageTuple([cs3])
        ct3 = pschaincoverage_coveragetuple.CoverageTuple([cs4, cs5])
        key = pschaincoverage_key.Key([ct1, ct2, ct3])

        r = ChainCoverage({key: psg})
        e = utilities.fakeEditor(0x10000)
        e.hmtx = hmtx.Hmtx()
        e.hmtx[20] = hmtx.MtxEntry(910, 42)
        e.hmtx[22] = hmtx.MtxEntry(900, 50)
        e.hmtx[23] = hmtx.MtxEntry(970, 40)
        e.hmtx[40] = hmtx.MtxEntry(890, 8)
        e.hmtx[80] = hmtx.MtxEntry(1020, 55)
        e.hmtx[81] = hmtx.MtxEntry(1090, 85)
        e.hmtx[82] = hmtx.MtxEntry(1020, 55)

        return r, e
Exemple #6
0
 def _makeTest():
     from fontio3 import hmtx, utilities
     from fontio3.GPOS import single, value
     
     from fontio3.opentype import (
       coverageset,
       lookup,
       pscontextcoverage_key,
       pslookupgroup,
       pslookuprecord)
     
     v1 = value.Value(xPlacement=-25)
     v2 = value.Value(xPlacement=-29)
     s1 = single.Single({20: v1, 25: v2})
     lk1 = lookup.Lookup([s1])
     psr1 = pslookuprecord.PSLookupRecord(0, lk1)
     v1 = value.Value(xPlacement=-31)
     v2 = value.Value(xPlacement=-19)
     s2 = single.Single({25: v1, 26: v2})
     lk2 = lookup.Lookup([s2])
     psr2 = pslookuprecord.PSLookupRecord(2, lk2)
     psg = pslookupgroup.PSLookupGroup([psr1, psr2])
     
     key = pscontextcoverage_key.Key([
       coverageset.CoverageSet([20, 25]),
       coverageset.CoverageSet([81, 82]),
       coverageset.CoverageSet([25, 26])])
     
     r = ContextCoverage({key: psg})
     e = utilities.fakeEditor(0x10000)
     e.hmtx = hmtx.Hmtx()
     e.hmtx[20] = hmtx.MtxEntry(910, 42)
     e.hmtx[25] = hmtx.MtxEntry(900, 50)
     e.hmtx[26] = hmtx.MtxEntry(970, 40)
     e.hmtx[80] = hmtx.MtxEntry(1020, 55)
     e.hmtx[81] = hmtx.MtxEntry(1090, 85)
     
     return r, e
Exemple #7
0
 def _makeTest():
     """
     The test case here recognizes the glyph sequence 1-2-3, and adjusts the
     positioning of classes 1 and 3.
     """
     
     from fontio3 import hmtx, utilities
     from fontio3.GPOS import single, value
     
     from fontio3.opentype import (
       classdef,
       lookup,
       pscontextclass_key,
       pslookupgroup,
       pslookuprecord)
     
     v1 = value.Value(xPlacement=-25)
     v2 = value.Value(xPlacement=-29)
     s1 = single.Single({20: v1, 25: v2})
     lk1 = lookup.Lookup([s1])
     psr1 = pslookuprecord.PSLookupRecord(0, lk1)
     s2 = single.Single({26: value.Value(xPlacement=25)})
     lk2 = lookup.Lookup([s2])
     psr2 = pslookuprecord.PSLookupRecord(2, lk2)
     psg = pslookupgroup.PSLookupGroup([psr1, psr2])
     key = pscontextclass_key.Key([1, 2, 3])
     cd = classdef.ClassDef({20: 1, 25: 1, 80: 2, 81: 2, 26: 3})
     r = ContextClass({key: psg}, classDef=cd)
     e = utilities.fakeEditor(0x10000)
     e.hmtx = hmtx.Hmtx()
     e.hmtx[20] = hmtx.MtxEntry(910, 42)
     e.hmtx[25] = hmtx.MtxEntry(900, 50)
     e.hmtx[26] = hmtx.MtxEntry(970, 40)
     e.hmtx[80] = hmtx.MtxEntry(1020, 55)
     e.hmtx[81] = hmtx.MtxEntry(1090, 85)
     
     return r, e
Exemple #8
0
    def buildBinary(self, w, **kwArgs):
        """
        Adds the binary data to the specified LinkedWriter. The following
        keyword arguments are supported:
        
            devicePool          A dict mapping device IDs to the Device
                                objects. This is optional; if not specified, a
                                local pool will be used. The devices will only
                                be actually added if devicePool is not
                                specified; if it is specified, a higher-level
                                client is responsible for this.
            
            posBase             A stake representing the base from which device
                                offsets will be computed. This is required.
            
            valueFormatFirst    The mask representing which values are to be
                                included for the second Value. This is
                                optional; if not present the getMasks() mask is
                                used instead.
            
            valueFormatSecond   The mask representing which values are to be
                                included for the second Value. This is
                                optional; if not present the getMask() mask is
                                used instead.
        
        >>> d = {
        ...   'posBase': "test stake",
        ...   'valueFormatFirst': 255,
        ...   'valueFormatSecond': 255}
        >>> w = writer.LinkedWriter()
        >>> w.stakeCurrentWithValue("test stake")
        >>> w.add("l", -1)
        >>> _testingValues[0].buildBinary(w, **d)
        >>> utilities.hexdump(w.binaryString())
               0 | FFFF FFFF 0000 0000  0000 0000 0000 0000 |................|
              10 | 0000 0000 FFF6 0000  0000 0000 0000 0000 |................|
              20 | 0000 0000                                |....            |
        
        >>> w.reset()
        >>> w.stakeCurrentWithValue("test stake")
        >>> w.add("l", -1)
        >>> _testingValues[1].buildBinary(w, **d)
        >>> utilities.hexdump(w.binaryString())
               0 | FFFF FFFF 0000 0000  0000 0000 0000 0000 |................|
              10 | 0000 0024 0000 0000  0000 0000 0000 0000 |...$............|
              20 | 0000 0000 000C 0012  0001 8C04           |............    |
        
        >>> w.reset()
        >>> w.stakeCurrentWithValue("test stake")
        >>> w.add("l", -1)
        >>> _testingValues[2].buildBinary(w, **d)
        >>> utilities.hexdump(w.binaryString())
               0 | FFFF FFFF 001E 0000  0000 0000 0000 0000 |................|
              10 | 0000 0030 0000 0000  0000 0000 0030 0024 |...0.........0.$|
              20 | 0000 0000 000C 0014  0002 BDF0 0020 3000 |............. 0.|
              30 | 000C 0012 0001 8C04                      |........        |
        """

        valueFormatFirst = kwArgs.pop('valueFormatFirst', None)
        valueFormatSecond = kwArgs.pop('valueFormatSecond', None)
        kwArgs.pop('valueFormat', None)

        if valueFormatFirst is None or valueFormatSecond is None:
            m1, m2 = self.getMasks()

            if valueFormatFirst is None:
                valueFormatFirst = m1

            if valueFormatSecond is None:
                valueFormatSecond = m2

        devicePool = kwArgs.pop('devicePool', None)

        if devicePool is None:
            devicePool = {}
            doLocal = True
        else:
            doLocal = False

        if valueFormatFirst:
            obj = self.first or value.Value()

            obj.buildBinary(w,
                            valueFormat=valueFormatFirst,
                            devicePool=devicePool,
                            **kwArgs)

        if valueFormatSecond:
            obj = self.second or value.Value()

            obj.buildBinary(w,
                            valueFormat=valueFormatSecond,
                            devicePool=devicePool,
                            **kwArgs)

        if doLocal:
            # we decorate-sort to ensure a repeatable, canonical ordering
            it = sorted((sorted(obj.asImmutable()[1]), obj, stake)
                        for obj, stake in devicePool.values())

            for t in it:
                t[1].buildBinary(w, stakeValue=t[2], **kwArgs)
    def fromValidatedFontWorkerSource(cls, fws, **kwArgs):
        """
        Creates and returns a new PairClasses object from the specified
        FontWorkerSource, with validation of the source data.
        
        >>> logger = utilities.makeDoctestLogger("FW_test")
        >>> pc = PairClasses.fromValidatedFontWorkerSource(_test_FW_fws2, namer=_test_FW_namer, logger=logger)
        FW_test.pairclasses - ERROR - line 13 -- unexpected token: foo
        >>> pc.pprint()
        (First class 1, Second class 2):
          First adjustment:
            FUnit adjustment to origin's x-coordinate: -123
        (First class 2, Second class 1):
          Second adjustment:
            FUnit adjustment to origin's x-coordinate: -456
        Class definition table for first glyph:
          2: 1
          3: 2
        Class definition table for second glyph:
          23: 1
          29: 2
        """

        terminalStrings = ('subtable end', 'lookup end')
        startingLineNumber=fws.lineNumber
        iskernset = kwArgs.get('iskernset', False)
        logger = kwArgs.pop('logger', logging.getLogger())
        logger = logger.getChild("pairclasses")
        fvfws = classdef.ClassDef.fromValidatedFontWorkerSource
        PV = pairvalues.PairValues
        V = value.Value
        
        tokenSet = frozenset({
          'left x advance',
          'left x placement',
          'left y advance',
          'left y placement',
          'right x advance',
          'right x placement',
          'right y advance',
          'right y placement'})

        # place-holders
        classDef1 = classdef.ClassDef()
        classDef2 = classdef.ClassDef()
        pairDict = defaultdict(lambda: [None, None])
        
        for line in fws:
            if line.lower().strip() in terminalStrings:
                if iskernset and line.lower() == 'subtable end':
                    continue
                
                else:
                    npd, nc1, nc2 = _reindexed(
                      pairDict,
                      classDef1,
                      classDef2,
                      logger = logger)
                    
                    return cls(npd, classDef1=nc1, classDef2=nc2)

            if len(line) > 0:
                tokens = [x.strip() for x in line.split('\t')]

                if tokens[0].lower() == 'firstclass definition begin':
                    classDef1 = fvfws(fws, logger=logger, **kwArgs)

                elif tokens[0].lower() == 'secondclass definition begin':
                    classDef2 = fvfws(fws, logger=logger, **kwArgs)

                elif tokens[0].lower() in tokenSet:
                    class1 = int(tokens[1])
                    class2 = int(tokens[2])
                    key = (class1, class2)
                    val= int(tokens[3])
                    
                    if val != 0:
                        pval = pairDict[key]
                        if tokens[0].lower() == 'left x advance':
                            if pval[0] and pval[0].xAdvance:
                                logger.warning((
                                  'Vxxxx',
                                  (fws.lineNumber, tokens[0], tokens[1], tokens[2]),
                                  "line %d -- ignoring duplicate %s for class "
                                  "pair %s,%s"))
                            else:
                                if pval[0] is None:
                                    pval[0] = value.Value()
                                pval[0].xAdvance=val

                        elif tokens[0].lower() == 'right x advance':
                            if pval[1] and pval[1].xAdvance:
                                logger.warning((
                                  'Vxxxx',
                                  (fws.lineNumber, tokens[0], tokens[1], tokens[2]),
                                  "line %d -- ignoring duplicate %s for class "
                                  "pair %s,%s"))
                            else:
                                if pval[1] is None:
                                    pval[1] = value.Value()
                                pval[1].xAdvance=val

                        elif tokens[0].lower() == 'left x placement':
                            if pval[0] and pval[0].xPlacement:
                                logger.warning((
                                  'Vxxxx',
                                  (fws.lineNumber, tokens[0], tokens[1], tokens[2]),
                                  "line %d -- ignoring duplicate %s for class "
                                  "pair %s,%s"))
                            else:
                                if pval[0] is None:
                                    pval[0] = value.Value()
                                pval[0].xPlacement = val

                        elif tokens[0].lower() == 'right x placement':
                            if pval[1] and pval[1].xPlacement:
                                logger.warning((
                                  'Vxxxx',
                                  (fws.lineNumber, tokens[0], tokens[1], tokens[2]),
                                  "line %d -- ignoring duplicate %s for class "
                                  "pair %s,%s"))
                            else:
                                if pval[1] is None:
                                    pval[1] = value.Value()
                                pval[1].xPlacement = val

                        elif tokens[0].lower() == 'left y advance':
                            if pval[0] and pval[0].yAdvance:
                                logger.warning((
                                  'Vxxxx',
                                  (fws.lineNumber, tokens[0], tokens[1], tokens[2]),
                                  "line %d -- ignoring duplicate %s for class "
                                  "pair %s,%s"))
                            else:
                                if pval[0] is None:
                                    pval[0] = value.Value()
                                pval[0].yAdvance=val

                        elif tokens[0].lower() == 'right y advance':
                            if pval[1] and pval[1].yAdvance:
                                logger.warning((
                                  'Vxxxx',
                                  (fws.lineNumber, tokens[0], tokens[1], tokens[2]),
                                  "line %d -- ignoring duplicate %s for class "
                                  "pair %s,%s"))
                            else:
                                if pval[1] is None:
                                    pval[1] = value.Value()
                                pval[1].yAdvance=val

                        elif tokens[0].lower() == 'left y placement':
                            if pval[0] and pval[0].yPlacement:
                                logger.warning((
                                  'Vxxxx',
                                  (fws.lineNumber, tokens[0], tokens[1], tokens[2]),
                                  "line %d -- ignoring duplicate %s for class "
                                  "pair %s,%s"))
                            else:
                                if pval[0] is None:
                                    pval[0] = value.Value()
                                pval[0].yPlacement=val

                        elif tokens[0].lower() == 'right y placement':
                            if pval[1] and pval[1].yPlacement:
                                logger.warning((
                                  'Vxxxx',
                                  (fws.lineNumber, tokens[0], tokens[1], tokens[2]),
                                  "line %d -- ignoring duplicate %s for class "
                                  "pair %s,%s"))
                            else:
                                if pval[1] is None:
                                    pval[1] = value.Value()
                                pval[1].yPlacement=val

                else:
                    logger.error((
                        'V0960',
                        (fws.lineNumber, tokens[0]),
                        'line %d -- unexpected token: %s'))
                
        logger.error((
            'V0958',
            (startingLineNumber, "/".join(terminalStrings)),
            'line %d -- did not find matching \'%s\''))

        if pairDict and classDef1 and classDef2:
            r = cls(pairDict, classDef1=classDef1, classDef2=classDef2)

        else:
            logger.error((
              'Vxxxx',
              (),
              "Incomplete or invalid lookup data for pair classes."))
            
            r = None

        return r
 def buildBinary(self, w, **kwArgs):
     """
     Adds the binary data for the PairClasses object to the specified
     LinkedWriter.
     
     >>> utilities.hexdump(_testingValues[0].binaryString())
            0 | 0002 004C 0081 0031  0058 006E 0003 0002 |...L...1.X.n....|
           10 | 0000 0000 0000 0000  0000 0000 0000 0000 |................|
           20 | 0000 0000 0000 0000  0000 0000 0000 0000 |................|
           30 | 0000 FFF6 0000 0000  0000 0084 0000 0000 |................|
           40 | 0000 001E 0084 0000  0084 0078 0001 0004 |...........x....|
           50 | 0005 0006 0007 000F  0002 0003 0005 0006 |................|
           60 | 0001 0007 0007 0002  000F 000F 0001 0002 |................|
           70 | 0001 0014 0016 0001  000C 0014 0002 BDF0 |................|
           80 | 0020 3000 000C 0012  0001 8C04           |. 0.........    |
     """
     
     if 'stakeValue' in kwArgs:
         stakeValue = kwArgs.pop('stakeValue')
         w.stakeCurrentWithValue(stakeValue)
     else:
         stakeValue = w.stakeCurrent()
     
     w.add("H", 2)  # format 2
     s = set(self.classDef1) | self.coverageExtras
     covTable = coverage.Coverage.fromglyphset(s)
     covStake = w.getNewStake()
     w.addUnresolvedOffset("H", stakeValue, covStake)
     vf1, vf2 = self.getMasks()
     w.add("HH", vf1, vf2)
     cd1Stake = w.getNewStake()
     cd2Stake = w.getNewStake()
     w.addUnresolvedOffset("H", stakeValue, cd1Stake)
     w.addUnresolvedOffset("H", stakeValue, cd2Stake)
     count1 = 1 + utilities.safeMax(self.classDef1.values())
     count2 = 1 + utilities.safeMax(self.classDef2.values())
     w.add("HH", count1, count2)
     emptyPV = pairvalues.PairValues(value.Value(), value.Value())
     devicePool = {}
     Key = pairclasses_key.Key
     
     for c1 in range(count1):
         for c2 in range(count2):
             obj = self.get(Key([c1, c2]), emptyPV)
             
             obj.buildBinary(
               w,
               devicePool = devicePool,
               posBase = stakeValue,
               valueFormatFirst = vf1,
               valueFormatSecond = vf2,
               **kwArgs)
     
     # Now add the deferred objects
     covTable.buildBinary(w, stakeValue=covStake)
     self.classDef1.buildBinary(w, stakeValue=cd1Stake)
     self.classDef2.buildBinary(w, stakeValue=cd2Stake)
     
     it = sorted(
       (sorted(obj.asImmutable()[1]), obj, stake)
       for obj, stake in devicePool.values())
     
     for t in it:
         t[1].buildBinary(w, stakeValue=t[2], **kwArgs)