Example #1
0
def analyze_attach(glyphTuples, effectTuples, nmbf, hmtxObj):
    """
    """

    ct = analyze_attach_makeClasses(glyphTuples, effectTuples, nmbf, hmtxObj)
    r = format4.Format4({}, classTable=ct, coverage=coverage.Coverage())
    Entry = entry4.Entry
    SR = staterow.StateRow
    r['Start of text'] = SR()

    for gt, et in zip(glyphTuples, effectTuples):
        if len(gt) > 2:
            vGT, vET = [], []

            for g, e in zip(gt, et):
                if g is None:
                    if e:
                        raise ValueError("Bad mark-to-ligature data!")

                else:
                    vGT.append(g)
                    vET.append(e)

            gt = tuple(vGT)
            et = tuple(vET)

        gBase, gMark = gt
        eBase, eMark = et
        baseRow = r['Start of text']
        baseClass = ct[gBase]
        secondState = "Saw %s" % (nmbf(gBase), )

        if baseClass not in baseRow:
            baseRow[baseClass] = Entry(mark=True, newState=secondState)

        if secondState not in r:
            r[secondState] = SR()

        markRow = r[secondState]
        markClass = ct[gMark]

        if markClass not in markRow:
            aw = hmtxObj[gBase].advance
            bx = aw + eMark.xAttach
            by = eMark.yAttach
            action = coordentry.CoordEntry(bx, by, 0, 0)
            markRow[markClass] = Entry(action=action, newState='Start of text')

    r.normalize()
    return [r]
Example #2
0
    def _makeCoordExample():
        cv = coverage.Coverage(crossStream=False)
        ct = classtable.ClassTable({30: "x", 96: "acute", 97: "grave"})

        pe1 = coordentry.CoordEntry(markedX=200,
                                    markedY=1600,
                                    currentX=450,
                                    currentY=1100)

        pe2 = coordentry.CoordEntry(markedX=200,
                                    markedY=1600,
                                    currentX=300,
                                    currentY=1000)

        e1 = entry4.Entry(newState="Start of text")
        e2 = entry4.Entry(newState="Saw x", mark=True)
        e3 = entry4.Entry(newState="Start of text", action=pe1)
        e4 = entry4.Entry(newState="Start of text", action=pe2)

        row1 = staterow.StateRow({
            "End of text": e1,
            "Out of bounds": e1,
            "Deleted glyph": e1,
            "End of line": e1,
            "x": e2,
            "acute": e1,
            "grave": e1
        })

        row2 = staterow.StateRow({
            "End of text": e1,
            "Out of bounds": e1,
            "Deleted glyph": e1,
            "End of line": e1,
            "x": e2,
            "acute": e3,
            "grave": e4
        })

        return Format4(
            {
                "Start of text": row1,
                "Start of line": row1,
                "Saw x": row2
            },
            coverage=cv,
            classTable=ct)
Example #3
0
def analyze_kern_with(glyphTuples, effectTuples, agf, vert):
    """
    """

    r = format0.Format0()

    for tG, tE in zip(glyphTuples, effectTuples):
        key = glyphpair.GlyphPair(abs(n) for n in tG)
        r[key] = agf(tE[1])

    rSize = len(r.binaryString())
    f2 = format2.Format2.fromformat0(r)

    if len(f2.binaryString()) < rSize:
        r = f2

    r.coverage = coverage.Coverage(crossStream=False, vertical=vert)
    return [r]
Example #4
0
    def _makePointExample():
        cv = coverage.Coverage(crossStream=False)
        ct = classtable.ClassTable({30: "x", 96: "acute", 97: "grave"})
        pe1 = pointentry.PointEntry(markedPoint=19, currentPoint=12)
        pe2 = pointentry.PointEntry(markedPoint=19, currentPoint=4)
        e1 = entry4.Entry(newState="Start of text")
        e2 = entry4.Entry(newState="Saw x", mark=True)
        e3 = entry4.Entry(newState="Start of text", action=pe1)
        e4 = entry4.Entry(newState="Start of text", action=pe2)

        row1 = staterow.StateRow({
            "End of text": e1,
            "Out of bounds": e1,
            "Deleted glyph": e1,
            "End of line": e1,
            "x": e2,
            "acute": e1,
            "grave": e1
        })

        row2 = staterow.StateRow({
            "End of text": e1,
            "Out of bounds": e1,
            "Deleted glyph": e1,
            "End of line": e1,
            "x": e2,
            "acute": e3,
            "grave": e4
        })

        return Format4(
            {
                "Start of text": row1,
                "Start of line": row1,
                "Saw x": row2
            },
            coverage=cv,
            classTable=ct)
Example #5
0
    _testingValues = (Format3(),
                      Format3(
                          {
                              ClassPair([1, 1]): -25,
                              ClassPair([1, 2]): -10,
                              ClassPair([2, 1]): 15
                          },
                          leftClassDef=classdef.ClassDef({
                              15: 1,
                              25: 1,
                              35: 2
                          }),
                          rightClassDef=classdef.ClassDef({
                              9: 1,
                              12: 1,
                              15: 1,
                              40: 2
                          }),
                          coverage=coverage.Coverage()))


def _test():
    import doctest
    doctest.testmod()


if __name__ == "__main__":
    if __debug__:
        _test()
Example #6
0
 def fromgpospairs(cls, pgObj, **kwArgs):
     """
     Creates and returns a new Format0 object from the specified PairGlyphs
     object. If there are any keys whose associated Values cannot be
     converted, they will be shown in a ValueError raised by this method.
     
     The following keyword arguments are used:
     
         coverage        A Coverage object. If not provided, a default
                         Coverage will be created and used.
         
         tupleIndex      The variations tuple index. Default is 0.
     """
     
     if 'coverage' not in kwArgs:
         kwArgs['coverage'] = coverage.Coverage()
     
     if 'tupleIndex' not in kwArgs:
         kwArgs['tupleIndex'] = 0
     
     r = cls({}, **utilities.filterKWArgs(cls, kwArgs))
     couldNotProcess = set()
     horizontal = not kwArgs['coverage'].vertical
     GP = glyphpair.GlyphPair
     
     for gposKey, gposValue in pgObj.items():
         fmt0Key = GP(gposKey)
         gposFirst = gposValue.first
         gposMask1 = (0 if gposFirst is None else gposFirst.getMask())
         gposSecond = gposValue.second
         gposMask2 = (0 if gposSecond is None else gposSecond.getMask())
         delta = 0
         
         # We only process effects that move the glyphs internally. That
         # means any Values that use anything other than an advance shift
         # on the first glyph or an origin shift on the second are not
         # processed, and their keys will be added to couldNotProcess.
         
         if horizontal:
             if gposMask1 == 4:  # xAdvance
                 delta += gposFirst.xAdvance
             
             elif gposMask1:
                 couldNotProcess.add(gposKey)
                 continue
             
             if gposMask2 == 1:  # xPlacement
                 delta += gposSecond.xPlacement
             
             elif gposMask2:
                 couldNotProcess.add(gposKey)
                 continue
         
         else:
             if gposMask1 == 8:  # yAdvance
                 delta += gposFirst.yAdvance
             
             elif gposMask1:
                 couldNotProcess.add(gposKey)
                 continue
             
             if gposMask2 == 2:  # yPlacement
                 delta += gposSecond.yPlacement
             
             elif gposMask2:
                 couldNotProcess.add(gposKey)
                 continue
         
         if delta:
             r[fmt0Key] = delta
     
     if couldNotProcess:
         v = sorted(couldNotProcess)
         raise ValueError("Could not process these GPOS keys: %s" % (v,))
     
     return r
Example #7
0
    def fromformat2(cls, fmt2Obj, **kwArgs):
        """
        Creates and returns a new Format1 object from the specified 'kerx'
        Format2 object.
        """

        leftMap = {}

        unionCT = cls._combinedClasses(fmt2Obj.leftClassDef,
                                       fmt2Obj.rightClassDef, leftMap)

        r = cls({},
                classTable=unionCT,
                coverage=kwArgs.pop('coverage', coverage.Coverage()),
                tupleIndex=kwArgs.pop('tupleIndex', 0))

        addedClassNames = tuple(sorted(set(unionCT.values())))
        classNames = namestash.fixedClassNames + addedClassNames

        addedStateNames = tuple(
            "Saw_L_%d" % (n, )
            for n in sorted(set(fmt2Obj.leftClassDef.values())))

        stateNames = namestash.fixedStateNames + addedStateNames
        E = entry.Entry
        defaultEntry = E()

        for stateName in stateNames:
            newRow = staterow.StateRow()

            for className in classNames:
                if className == 'Deleted glyph':
                    newRow[className] = E(newState=stateName)
                else:
                    newRow[className] = defaultEntry

            r[stateName] = newRow

        for cp, dist in fmt2Obj.items():
            if not dist:
                continue

            # For the SOT and SOL states, each cell belonging to either
            # class_L_cp[0] or class_LR_cp[0]* gets a link to the Saw_L_cp[0]
            # state.

            f2LeftClass, f2RightClass = cp
            firstCell = E(newState="Saw_L_%d" % (f2LeftClass, ))
            match1 = "class_L_%d" % (f2LeftClass, )
            match2 = "class_LR_%d_" % (f2LeftClass, )

            for className in classNames[4:]:
                if className == match1 or className.startswith(match2):
                    for sn in ("Start of text", "Start of line"):
                        if r[sn][className] == defaultEntry:
                            r[sn][className] = firstCell

            match3 = "class_R_%d" % (f2RightClass, )
            match4 = "class_LR_"
            match5 = "_%d" % (f2RightClass, )

            for className in classNames[4:]:
                isLR = (className.startswith(match4)
                        and className.endswith(match5))

                if (className == match3) or isLR:
                    sn = "Saw_L_%d" % (f2LeftClass, )
                    assert r[sn][className] == defaultEntry
                    val = valuetuple.ValueTuple([dist])

                    if className == match3:
                        r[sn][className] = E(newState="Start of text",
                                             push=True,
                                             values=val)

                    else:
                        r[sn][className] = E(newState="Saw_L_%s" %
                                             (className.split('_')[2], ),
                                             push=True,
                                             values=val)

        # Now that the nonzero kerning entries are there, go through and fill
        # in the links for any cells which are still empty but have a LR class.

        for stateName in stateNames[2:]:
            for className in classNames[4:]:
                if className.startswith("class_R"):
                    continue

                cell = r[stateName][className]

                if cell != defaultEntry:
                    continue

                r[stateName][className] = E(newState="Saw_L_%s" %
                                            (className.split('_')[2], ))

        return r
Example #8
0
#
# Test code
#

if 0:

    def __________________():
        pass


if __debug__:
    from fontio3.kerx import coverage
    from fontio3.utilities import namer

    _srtv = staterow._testingValues
    _cv = coverage.Coverage(crossStream=True)

    _ct = classtable.ClassTable({
        3: "Space",
        4: "Punctuation",
        5: "Punctuation",
        6: "Punctuation",
        12: "Punctuation",
        13: "Punctuation",
        30: "Letter",
        31: "Letter",
        51: "Letter",
        96: "Letter",
        97: "Letter"
    })
Example #9
0
def analyze_shift_with(glyphTuples, effectTuples, nmbf, agf):
    """
    Do non-kerning generic with-stream shifts.
    
    ### E = effect.Effect
    ### e0 = E()
    ### e1 = E(xShift=-40)
    ### e2 = E(xShift=25)
    ### gt = ((2, 4, 6, 8),)
    ### et = ((e0, e1, e0, e2),)
    ### r = analyze_shift_with(gt, et, str, operator.attrgetter('xShift'))
    ### len(r)
    1
    ### r[0].pprint(onlySignificant=True)
    State 'Start of text':
      Class '2':
        Go to state 'Saw 2'
    State 'Start of line':
      Class '2':
        Go to state 'Saw 2'
    State 'Saw 2':
      Class 'Deleted glyph':
        Go to state 'Saw 2'
      Class '2':
        Go to state 'Saw 2'
      Class '4':
        Push this glyph, then go to state 'Saw 2_4'
    State 'Saw 2_4':
      Class 'Deleted glyph':
        Go to state 'Saw 2_4'
      Class '2':
        Go to state 'Saw 2'
      Class '6':
        Go to state 'Saw 2_4_6'
    State 'Saw 2_4_6':
      Class 'Deleted glyph':
        Go to state 'Saw 2_4_6'
      Class '2':
        Go to state 'Saw 2'
      Class '8':
        Push this glyph, then go to state 'Start of text' after applying these kerning shifts to the popped glyphs:
          Pop #1: 25
          Pop #2: -40
    Class table:
      2: 2
      4: 4
      6: 6
      8: 8
    Header information:
      Horizontal
      With-stream
      No variation kerning
    
    ### gt = ((2, 4, 6, 8, None),)
    ### et = ((e0, e0, e1, e0, e2),)
    ### r = analyze_shift_with(gt, et, str, operator.attrgetter('xShift'))
    ### len(r)
    1
    ### r[0].pprint(onlySignificant=True)
    State 'Start of text':
      Class '2':
        Go to state 'Saw 2'
    State 'Start of line':
      Class '2':
        Go to state 'Saw 2'
    State 'Saw 2':
      Class 'Deleted glyph':
        Go to state 'Saw 2'
      Class '2':
        Go to state 'Saw 2'
      Class '4':
        Go to state 'Saw 2_4'
    State 'Saw 2_4':
      Class 'Deleted glyph':
        Go to state 'Saw 2_4'
      Class '2':
        Go to state 'Saw 2'
      Class '6':
        Push this glyph, then go to state 'Saw 2_4_6'
    State 'Saw 2_4_6':
      Class 'Deleted glyph':
        Go to state 'Saw 2_4_6'
      Class '2':
        Go to state 'Saw 2'
      Class '8':
        Go to state 'Shift 25'
    State 'Shift 25':
      Class 'Out of bounds':
        Push this glyph (without advancing), then go to state 'Start of text' after applying these kerning shifts to the popped glyphs:
          Pop #1: 25
      Class 'Deleted glyph':
        Push this glyph (without advancing), then go to state 'Start of text' after applying these kerning shifts to the popped glyphs:
          Pop #1: 25
      Class '2':
        Push this glyph (without advancing), then go to state 'Start of text' after applying these kerning shifts to the popped glyphs:
          Pop #1: 25
      Class '4':
        Push this glyph (without advancing), then go to state 'Start of text' after applying these kerning shifts to the popped glyphs:
          Pop #1: 25
      Class '6':
        Push this glyph (without advancing), then go to state 'Start of text' after applying these kerning shifts to the popped glyphs:
          Pop #1: 25
      Class '8':
        Push this glyph (without advancing), then go to state 'Start of text' after applying these kerning shifts to the popped glyphs:
          Pop #1: 25
    Class table:
      2: 2
      4: 4
      6: 6
      8: 8
    Header information:
      Horizontal
      With-stream
      No variation kerning
    """

    ct = analyze_shift_makeClasses(glyphTuples, effectTuples, nmbf)

    r = format1.Format1({}, classTable=ct, coverage=coverage.Coverage())

    SR = staterow.StateRow
    Entry = entry.Entry
    specialShifts = set()

    for tG, tE in zip(glyphTuples, effectTuples):
        currState = 'Start of text'
        vtv = []
        isSpecial = 0

        if tG[-1] is None:
            tG = tG[:-1]

            if agf(tE[-1]):
                isSpecial = agf(tE[-1])
                specialShifts.add(isSpecial)

            tE = tE[:-1]

        for i, (g, e) in enumerate(zip(tG, tE)):
            currClass = ct[g]

            if i == len(tG) - 1:
                if isSpecial:
                    nextState = "Shift %d" % (isSpecial, )
                else:
                    nextState = 'Start of text'

            elif i:
                nextState = '%s_%s' % (currState, currClass)

            else:
                nextState = 'Saw %s' % (currClass, )

            if currState not in r:
                r[currState] = SR()

            thisRow = r[currState]

            if currClass not in thisRow:
                if not e:
                    thisRow[currClass] = Entry(newState=nextState)

                elif i < len(tG) - 1:
                    thisRow[currClass] = Entry(newState=nextState, push=True)

                    vtv.append(agf(e))

                else:
                    thisRow[currClass] = Entry(push=True,
                                               values=valuetuple.ValueTuple(
                                                   reversed(vtv + [agf(e)])),
                                               newState=nextState)

                    break

            currState = nextState

    # Now add the special shifts (if any)

    r.normalize()
    allClasses = set(r['Start of text'])
    ignore = {'End of text', 'End of line'}

    for specialShift in sorted(specialShifts):
        cell = Entry(push=True,
                     newState='Start of text',
                     noAdvance=True,
                     values=valuetuple.ValueTuple([specialShift]))

        currState = "Shift %d" % (specialShift, )
        r[currState] = SR()

        for c in allClasses:
            r[currState][c] = (Entry() if c in ignore else cell)

    return [r]
Example #10
0
def analyze_shift_cross(glyphTuples, effectTuples, nmbf, agf):
    """
    Do non-kerning generic cross-stream shifts.
    
    ### E = effect.Effect
    ### e0 = E()
    ### e1 = E(yShift=250)
    ### e2 = E(yShift=120)
    ### gt = ((2, 4, 6, 8),)
    ### et = ((e0, e1, e2, e0),)
    ### r = analyze_shift_cross(gt, et, str, operator.attrgetter('yShift'))
    ### len(r)
    1
    ### r[0].pprint(onlySignificant=True)
    State 'Start of text':
      Class '2':
        Go to state 'Saw 2'
    State 'Start of line':
      Class '2':
        Go to state 'Saw 2'
    State 'Saw 2':
      Class 'Deleted glyph':
        Go to state 'Saw 2'
      Class '2':
        Go to state 'Saw 2'
      Class '4':
        Push this glyph, then go to state 'Saw 2_4'
    State 'Saw 2_4':
      Class 'Deleted glyph':
        Go to state 'Saw 2_4'
      Class '2':
        Go to state 'Saw 2'
      Class '6':
        Push this glyph, then go to state 'Saw 2_4_6'
    State 'Saw 2_4_6':
      Class 'Deleted glyph':
        Go to state 'Saw 2_4_6'
      Class '2':
        Go to state 'Saw 2'
      Class '8':
        Reset kerning, then go to state 'Start of text' after applying these kerning shifts to the popped glyphs:
          Pop #1: -130
          Pop #2: 250
    Class table:
      2: 2
      4: 4
      6: 6
      8: 8
    Header information:
      Horizontal
      Cross-stream
      No variation kerning
    """

    ct = analyze_shift_makeClasses(glyphTuples, effectTuples, nmbf)

    r = format1.Format1({},
                        classTable=ct,
                        coverage=coverage.Coverage(crossStream=True))

    SR = staterow.StateRow
    Entry = entry.Entry
    specialShifts = set()

    for tG, tE in zip(glyphTuples, effectTuples):
        currState = 'Start of text'
        vtv = []
        runningShift = 0

        for i, (g, e) in enumerate(zip(tG, tE)):
            currClass = ct[g]

            if i == len(tG) - 1:
                nextState = 'Start of text'
            elif i:
                nextState = '%s_%s' % (currState, currClass)
            else:
                nextState = 'Saw %s' % (currClass, )

            if currState not in r:
                r[currState] = SR()

            thisRow = r[currState]
            d = {'newState': nextState}

            if currClass not in thisRow:
                if not e:
                    if runningShift:
                        d['reset'] = True
                        runningShift = 0

                else:
                    delta = agf(e) - runningShift

                    if delta:
                        vtv.append(delta)
                        runningShift = agf(e)
                        d['push'] = True

            if i == len(tG) - 1:
                d['values'] = valuetuple.ValueTuple(reversed(vtv))

            thisRow[currClass] = Entry(**d)
            currState = nextState

    r.normalize()
    return [r]