예제 #1
0
def hint_NOT(self, **kwArgs):
    """
    NOT: Logical NOT, opcode 0x5C
    
    >>> logger = utilities.makeDoctestLogger("NOT_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(Collection([Triple(0, 8, 4)]), 0, 5)
    >>> h.append(opcode_tt.Opcode_nonpush(nameToOpcodeMap["NOT"]))
    >>> hint_NOT(h, logger=logger)
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> pp.PP().sequence_deep(h.state.pushHistory)
    Extra index 0 in PUSH opcode index 0 in test
    Extra index 1 in PUSH opcode index 0 in test
    Result of opcode NOT at index 0 in test, with inputs:
      Extra index 2 in PUSH opcode index 0 in test
    >>> _popSync(h.state)
    0
    >>> h.state.assign('pc', 0)
    >>> hint_NOT(h, logger=logger)
    >>> _popSync(h.state)
    1
    >>> h.state.assign('pc', 0)
    >>> hint_NOT(h, logger=logger)
    >>> _popSync(h.state)
    Singles: [0, 1]
    >>> hint_NOT(h, logger=logger)
    NOT_test - CRITICAL - Stack underflow in test (PC 1).
    """

    state = self.state
    logger = self._getLogger(**kwArgs)
    n = self._popRemove(state, 'stack', coerceToCollection=True)

    if n is None:
        state.assign('pc', doNotProceedPC)
        return

    h = self._popRemove(state, 'pushHistory')

    if h is None:
        state.assign('pc', doNotProceedPC)
        return

    v = list(n.encompassedBooleans())

    if len(v) == 1:
        v = [1 - v[0]]

    state.append('stack', (v[0] if len(v) == 1 else toCollection(v)))

    state.append(
        'pushHistory',
        op.HistoryEntry_op(hintsObj=(id(self.ultParent), self.ultParent),
                           hintsPC=state.pc + self.ultDelta,
                           opcode=self[state.pc].opcode,
                           historyIterable=[h]))

    fatObj = kwArgs.get('fdefArgTracer', None)

    if fatObj is not None:
        fatObj.notePop(None, 'NOT')
        fatObj.notePush()

    state.assign('pc', state.pc + 1)
예제 #2
0
def hint_MIRP(self, **kwArgs):
    """
    MIRP: Move indirect relative point, opcodes 0xE0-0xFF
    
    >>> logger = utilities.makeDoctestLogger("MIRP_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(7, 11, 12, 3, 15)
    >>> m = h.state.statistics.maxima
    >>> hint_MIRP(h, logger=logger)
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> m.point, m.cvt
    (3, 15)
    >>> hint_srp0.hint_SRP0(h, logger=logger)
    >>> hint_MIRP(h, logger=logger)
    >>> m.point, m.cvt
    (12, 15)
    >>> h.state.statistics.pprint(keys=('cvt', 'point'))
    History for CVT values:
      11: Extra index 1 in PUSH opcode index 0 in test
      15: Extra index 4 in PUSH opcode index 0 in test
    History for outline points (glyph zone):
      0: Implicit zero for RP0 used at opcode index 0 in test
      3: Extra index 3 in PUSH opcode index 0 in test
      7: Extra index 0 in PUSH opcode index 0 in test
      12: Extra index 2 in PUSH opcode index 0 in test
    >>> hint_MIRP(h, logger=logger)
    MIRP_test - CRITICAL - Stack underflow in test (PC 3).
    """
    
    state = self.state
    stack = state.stack
    stats = state.statistics
    gs = state.graphicsState
    rph = state.refPtHistory
    logger = self._getLogger(**kwArgs)
    t = self._popRemove(state, 'stack', 2, coerceToCollection=True)
    
    if t is None:
        state.assign('pc', doNotProceedPC)
        return
    
    point, cvtIndex = t
    t = self._popRemove(state, 'pushHistory', 2)
    
    if t is None:
        state.assign('pc', doNotProceedPC)
        return
    
    pointHist, cvtHist = t
    missing = sorted(n for n in cvtIndex if n not in state.cvt)
    
    if missing:
        logger.error((
          'E6005',
          (self.ultParent.infoString, state.pc + self.ultDelta, missing),
          "For MIRP opcode in %s (PC %d), "
          "CVTs not present in CVT table: %s"))
        
        state._validationFailed = True
    
    elif self._zoneCheck("MIRP", (0, 1), logger):
        zp0, zp1 = gs.zonePointer0, gs.zonePointer1
        
        v = [
          (zp1, point, True),
          (zp0, toCollection(gs.referencePoint0), False)]
        
        if self._pointCheck(
          "MIRP",
          v,
          logger,
          kwArgs.get('extraInfo', {})):
            
            stats.addHistory('cvt', cvtIndex, cvtHist)
            
            stats.noteEffect(
              'cvt',
              cvtIndex,
              self.ultParent.infoString,
              state.pc + self.ultDelta,
              -1)
            
            stats.addHistory('pointMoved', (zp1, point), pointHist)
            refHist = self._synthRefHistory(0)
            
            stats.addHistory(
              'point',
              (zp0, gs.referencePoint0),
              refHist)
            
            state.assignDeep(
              'graphicsState',
              'referencePoint1',
              gs.referencePoint0)
            
            state.assignDeep('graphicsState', 'referencePoint1', point)
            rph[1] = rph[0]
            rph[2] = pointHist
            state.changed('refPtHistory')
            
            if kwArgs.get('setRP0', False):
                state.assignDeep('graphicsState', 'referencePoint0', point)
                rph[0] = pointHist
                state.changed('refPtHistory')
            
            if 'fdefEntryStack' in kwArgs:
                # We only note graphicsState effects when a
                # FDEF stack is available
                
                state.statistics.noteGSEffect(
                  tuple(kwArgs['fdefEntryStack']),
                  state.pc + self.ultDelta)
    
    fatObj = kwArgs.get('fdefArgTracer', None)
    
    if fatObj is not None:
        fatObj.notePop('cvtIndex', 'MIRP')
        fatObj.notePop('pointIndex', 'MIRP')
    
    state.assign('pc', state.pc + 1)
예제 #3
0
def hint_WS(self, **kwArgs):
    """
    WS: Write to storage, opcode 0x42
    
    >>> logger = utilities.makeDoctestLogger("WS_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(toCollection([8, 15]), 64, 15, -128)
    >>> h.state.storage[8] = 32
    >>> h.state.changed('storage')
    >>> hint_WS(h, logger=logger)
    >>> h.state.storage[15]
    -128
    >>> hint_WS(h, logger=logger)
    >>> h.state.storage[8]
    Singles: [32, 64]
    >>> h.state.storage[15]
    Singles: [-128, 64]
    >>> h.state.statistics.pprint(keys=('storage',))
    History for storage locations:
      8: Extra index 0 in PUSH opcode index 0 in test
      15:
        Extra index 0 in PUSH opcode index 0 in test
        Extra index 2 in PUSH opcode index 0 in test
    >>> hint_WS(h, logger=logger)
    WS_test - CRITICAL - Stack underflow in test (PC 2).
    """
    
    state = self.state
    stats = state.statistics
    logger = self._getLogger(**kwArgs)
    t = self._popRemove(state, 'stack', 2, coerceToCollection=True)
    
    if t is None:
        state.assign('pc', doNotProceedPC)
        return
    
    storageIndex, value = t
    t = self._popRemove(state, 'pushHistory', 2)
    
    if t is None:
        state.assign('pc', doNotProceedPC)
        return
    
    indexHistory, valueHistory = t
    stats.addHistory('storage', storageIndex, indexHistory)
    
    stats.noteEffect(
      kind = 'storage',
      value = storageIndex,
      infoString = self.ultParent.infoString,
      pc = state.pc + self.ultDelta,
      relStackIndex = -2)
    
    if len(storageIndex) == 1:
        indivIndex = int(storageIndex)
        n2 = value.toNumber()
        
        if n2 is not None:
            value = n2
        
        state.storage[indivIndex] = value
        state.storageHistory[indivIndex] = valueHistory
    
    else:
        for indivIndex in storageIndex:
            orig = toCollection(state.storage[indivIndex])
            orig = orig.addToCollection(value)
            n2 = orig.toNumber()
            
            if n2 is not None:
                orig = n2
            
            origHistory = self._synthStorageHistory(indivIndex)
            state.storage[indivIndex] = orig
            
            state.storageHistory[indivIndex] = (
              HG([origHistory, valueHistory]))
    
    fatObj = kwArgs.get('fdefArgTracer', None)
    
    if fatObj is not None:
        fatObj.notePop(None, 'WS')
        fatObj.notePop('storageIndex', 'WS')
    
    state.changed('storage')
    state.changed('storageHistory')
    state.assign('pc', state.pc + 1)
예제 #4
0
def hint_IP(self, **kwArgs):
    """
    IP: Interpolate point, opcode 0x39
    
    >>> logger = utilities.makeDoctestLogger("IP_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(3, 4, 6, 7, 2, 12, 3, 5)
    >>> m = h.state.statistics.maxima
    >>> h.state.assignDeep('graphicsState', 'loop', 4)
    >>> hint_IP(h, logger=logger)
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> m.point
    12
    >>> h.state.graphicsState.loop
    1
    >>> h.state.assignDeep('graphicsState', 'loop', 2)
    >>> h.state.assignDeep('graphicsState', 'referencePoint2', 14)
    >>> hint_IP(h, logger=logger)
    >>> m.point
    14
    >>> h.state.statistics.pprint(keys=('point',))
    History for outline points (glyph zone):
      0:
        Implicit zero for RP1 used at opcode index 0 in test
        Implicit zero for RP2 used at opcode index 0 in test
        Implicit zero for RP1 used at opcode index 1 in test
      2: Extra index 4 in PUSH opcode index 0 in test
      3: Extra index 6 in PUSH opcode index 0 in test
      5: Extra index 7 in PUSH opcode index 0 in test
      6: Extra index 2 in PUSH opcode index 0 in test
      7: Extra index 3 in PUSH opcode index 0 in test
      12: Extra index 5 in PUSH opcode index 0 in test
      14: Implicit zero for RP2 used at opcode index 1 in test
    >>> h.state.assignDeep('graphicsState', 'loop', 3)
    >>> hint_IP(h, logger=logger)
    IP_test - CRITICAL - Stack underflow in test (PC 2).
    """

    state = self.state
    gs = state.graphicsState
    logger = self._getLogger(**kwArgs)

    points = self._popRemove(state,
                             'stack',
                             gs.loop,
                             coerceToCollection=True,
                             coerceToList=True)

    if points is None:
        state.assign('pc', doNotProceedPC)
        return

    histories = self._popRemove(state,
                                'pushHistory',
                                gs.loop,
                                coerceToList=True)

    if histories is None:
        state.assign('pc', doNotProceedPC)
        return

    if self._zoneCheck("IP", (0, 1, 2), logger):
        zp0 = gs.zonePointer0
        zp1 = gs.zonePointer1
        zp2 = gs.zonePointer2
        v = [(zp2, p, True) for p in points]
        v.append((zp0, toCollection(gs.referencePoint1), False))
        v.append((zp1, toCollection(gs.referencePoint2), False))

        if self._pointCheck("IP", v, logger, kwArgs.get('extraInfo', {})):
            for i, p in enumerate(points):
                state.statistics.addHistory('pointMoved', (gs.zonePointer2, p),
                                            histories[i])

            rh1 = self._synthRefHistory(1)

            state.statistics.addHistory('point',
                                        (gs.zonePointer0, gs.referencePoint1),
                                        rh1)

            rh2 = self._synthRefHistory(2)

            state.statistics.addHistory('point',
                                        (gs.zonePointer1, gs.referencePoint2),
                                        rh2)

    state.assignDeep('graphicsState', 'loop', 1)
    state.assign('pc', state.pc + 1)

    fatObj = kwArgs.get('fdefArgTracer', None)

    if fatObj is not None:
        for p in points:
            fatObj.notePop('pointIndex', 'IP')
예제 #5
0
def hint_SHZ(self, **kwArgs):
    """
    SHZ: Shift zone, opcodes 0x36-0x37
    
    >>> logger = utilities.makeDoctestLogger("SHZ_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(1, 0, 12, 8)
    >>> hint_srp1.hint_SRP1(h, logger=logger)
    >>> hint_srp2.hint_SRP2(h, logger=logger)
    >>> hint_SHZ(h, refPt=1, logger=logger)
    >>> hint_SHZ(h, refPt=2, logger=logger)
    >>> h.state.statistics.maxima.pprint(keys=('point', 'stack'))
    Highest point index in the glyph zone: 12
    Deepest stack attained: 4
    >>> h.state.statistics.pprint(keys=('point', 'zone'))
    History for outline points (glyph zone):
      8: Extra index 3 in PUSH opcode index 0 in test
      12: Extra index 2 in PUSH opcode index 0 in test
    History for SHZ zones:
      0: Extra index 1 in PUSH opcode index 0 in test
      1: Extra index 0 in PUSH opcode index 0 in test
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> hint_SHZ(h, logger=logger)
    SHZ_test - CRITICAL - Stack underflow in test (PC 4).
    """

    state = self.state
    gs = state.graphicsState
    logger = self._getLogger(**kwArgs)
    zone = self._popRemove(state, 'stack')

    if zone is None:
        state.assign('pc', doNotProceedPC)
        return

    history = self._popRemove(state, 'pushHistory')

    if history is None:
        state.assign('pc', doNotProceedPC)
        return

    if self._zoneCheck("SHZ", (zone, ), logger):
        state.statistics.addHistory('zone', zone, history)

        if kwArgs.get('refPt', 1) == 1:
            if self._pointCheck(
                    "SHZ",
                [(gs.zonePointer0, toCollection(gs.referencePoint1), False)],
                    logger, kwArgs.get('extraInfo', {})):

                state.statistics.addHistory(
                    'point', (gs.zonePointer0, gs.referencePoint1),
                    self._synthRefHistory(1))

        else:
            if self._pointCheck(
                    "SHZ",
                [(gs.zonePointer1, toCollection(gs.referencePoint2), False)],
                    logger, kwArgs.get('extraInfo', {})):

                state.statistics.addHistory(
                    'point', (gs.zonePointer1, gs.referencePoint2),
                    self._synthRefHistory(2))

    fatObj = kwArgs.get('fdefArgTracer', None)

    if fatObj is not None:
        fatObj.notePop('zoneIndex', 'SHZ')

    state.assign('pc', state.pc + 1)
예제 #6
0
def hint_MDRP(self, **kwArgs):
    """
    MDRP: Move direct relative point, opcodes 0xC0-0xDF
    
    >>> logger = utilities.makeDoctestLogger("MDRP_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(6, 12)
    >>> pp.PP().mapping_deep_smart(h.state.refPtHistory, lambda x: False)
    0: (no data)
    1: (no data)
    2: (no data)
    >>> m = h.state.statistics.maxima
    >>> hint_MDRP(h, logger=logger)
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> m.point
    12
    >>> h.state.assignDeep('graphicsState', 'referencePoint0', 16)
    >>> hint_MDRP(h, logger=logger)
    >>> m.point
    16
    >>> pp.PP().mapping_deep_smart(h.state.refPtHistory, lambda x: False)
    0: (no data)
    1: (no data)
    2: Extra index 0 in PUSH opcode index 0 in test
    >>> h.state.statistics.pprint(keys=('point',))
    History for outline points (glyph zone):
      0: Implicit zero for RP0 used at opcode index 0 in test
      6: Extra index 0 in PUSH opcode index 0 in test
      12: Extra index 1 in PUSH opcode index 0 in test
      16: Implicit zero for RP0 used at opcode index 1 in test
    >>> hint_MDRP(h, logger=logger)
    MDRP_test - CRITICAL - Stack underflow in test (PC 2).
    """
    
    state = self.state
    gs = state.graphicsState
    rph = state.refPtHistory
    logger = self._getLogger(**kwArgs)
    p = self._popRemove(state, 'stack', coerceToCollection=True)
    
    if p is None:
        state.assign('pc', doNotProceedPC)
        return
    
    history = self._popRemove(state, 'pushHistory')
    
    if history is None:
        state.assign('pc', doNotProceedPC)
        return
    
    if self._zoneCheck("MDRP", (0, 1), logger):
        zp1 = gs.zonePointer1
        
        v = [
          (zp1, p, True),
          (gs.zonePointer0, toCollection(gs.referencePoint0), False)]
        
        if self._pointCheck(
          "MDRP",
          v,
          logger,
          kwArgs.get('extraInfo', {})):
            
            state.statistics.addHistory('pointMoved', (zp1, p), history)
            refHist = self._synthRefHistory(0)
            
            state.statistics.addHistory(
              'point',
              (gs.zonePointer0, gs.referencePoint0),
              refHist)
            
            state.assignDeep(
              'graphicsState',
              'referencePoint1',
              gs.referencePoint0)
            
            state.assignDeep('graphicsState', 'referencePoint2', p)
            rph[1] = rph[0]
            rph[2] = history
            state.changed('refPtHistory')
            
            if kwArgs.get('setRP0', False):
                state.assignDeep('graphicsState', 'referencePoint0', p)
                rph[0] = history
                state.changed('refPtHistory')
            
            if 'fdefEntryStack' in kwArgs:
                # We only note graphicsState effects when a
                # FDEF stack is available
                
                state.statistics.noteGSEffect(
                  tuple(kwArgs['fdefEntryStack']),
                  state.pc + self.ultDelta)
    
    fatObj = kwArgs.get('fdefArgTracer', None)
    
    if fatObj is not None:
        fatObj.notePop('pointIndex', 'MDRP')
    
    state.assign('pc', state.pc + 1)
예제 #7
0
def hint_SHP(self, **kwArgs):
    """
    SHP: Shift points, opcodes 0x32-0x33
    
    >>> logger = utilities.makeDoctestLogger("SHP_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(11, 4, 2, 6, 5, 2, 10, 8)
    >>> hint_srp1.hint_SRP1(h, logger=logger)
    >>> hint_srp2.hint_SRP2(h, logger=logger)
    >>> hint_sloop.hint_SLOOP(h, logger=logger)
    >>> hint_SHP(h, refPt=1, logger=logger)
    >>> hint_sloop.hint_SLOOP(h, logger=logger)
    >>> hint_SHP(h, refPt=2, logger=logger)
    >>> h.state.statistics.maxima.pprint(keys=('point', 'stack'))
    Highest point index in the glyph zone: 11
    Deepest stack attained: 8
    >>> h.state.statistics.pprint(keys=('point',))
    History for outline points (glyph zone):
      4: Extra index 1 in PUSH opcode index 0 in test
      5: Extra index 4 in PUSH opcode index 0 in test
      6: Extra index 3 in PUSH opcode index 0 in test
      8: Extra index 7 in PUSH opcode index 0 in test
      10: Extra index 6 in PUSH opcode index 0 in test
      11: Extra index 0 in PUSH opcode index 0 in test
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> hint_SHP(h, logger=logger)
    SHP_test - CRITICAL - Stack underflow in test (PC 6).
    """
    
    state = self.state
    gs = state.graphicsState
    logger = self._getLogger(**kwArgs)
    
    points = self._popRemove(
      state,
      'stack',
      gs.loop,
      coerceToCollection = True,
      coerceToList = True)
    
    if points is None:
        state.assign('pc', doNotProceedPC)
        return
    
    histories = self._popRemove(
      state,
      'pushHistory',
      gs.loop,
      coerceToList = True)
    
    if histories is None:
        state.assign('pc', doNotProceedPC)
        return
    
    if self._zoneCheck("SHP", (0, 1, 2), logger):
        v = [(gs.zonePointer2, p, True) for p in points]
        v.append((gs.zonePointer0, toCollection(gs.referencePoint1), False))
        v.append((gs.zonePointer1, toCollection(gs.referencePoint2), False))
        
        if self._pointCheck(
          "SHP",
          v,
          logger,
          kwArgs.get('extraInfo', {})):
            
            for i, p in enumerate(points):
                
                state.statistics.addHistory(
                  'pointMoved',
                  (gs.zonePointer2, p),
                  histories[i])
            
            if kwArgs.get('refPt', 1) == 1:
                
                state.statistics.addHistory(
                  'point',
                  (gs.zonePointer0, gs.referencePoint1),
                  self._synthRefHistory(1))
            
            else:
                
                state.statistics.addHistory(
                  'point',
                  (gs.zonePointer1, gs.referencePoint2),
                  self._synthRefHistory(2))
    
    fatObj = kwArgs.get('fdefArgTracer', None)
    
    if fatObj is not None:
        for p in points:
            fatObj.notePop('pointIndex', 'SHP')
    
    state.assignDeep('graphicsState', 'loop', 1)
    state.assign('pc', state.pc + 1)
예제 #8
0
def hint_SROUND(self, **kwArgs):
    """
    SROUND: Super round, opcode 0x76
    
    >>> logger = utilities.makeDoctestLogger("SROUND_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(toCollection([0x61, 0xD5]), 0x61)
    >>> hint_SROUND(h, logger=logger)
    >>> h.state.graphicsState.roundState
    [Singles: [1.0], Singles: [0.5], Singles: [-0.375]]
    >>> hint_SROUND(h, logger=logger)
    SROUND_test - ERROR - In test (PC 1) a Collection value was used, but is not supported in fontio.
    >>> h.state.assign('pc', 0)
    >>> hint_SROUND(h, logger=logger)
    SROUND_test - CRITICAL - Stack underflow in test (PC 0).
    """
    
    is45Case = kwArgs.get('is45Case', False)
    state = self.state
    logger = self._getLogger(**kwArgs)
    n = self._popRemove(state, 'stack', coerceToCollection=True)
    
    if n is None:
        state.assign('pc', doNotProceedPC)
        return
    
    if self._popRemove(state, 'pushHistory') is None:
        state.assign('pc', doNotProceedPC)
        return
    
    n = self._toNumber(n)
    
    if n is None:
        state.assign('pc', doNotProceedPC)
        return
    
    if self._8BitCheck(("S45ROUND" if is45Case else "SROUND"), n, logger):
        thresholdIndex = n & 15
        phaseIndex = (n // 16) & 3
        periodIndex = (n // 64) & 3
        
        if periodIndex == 3:
            
            logger.error((
              'E6000',
              (self.ultParent.infoString, state.pc + self.ultDelta),
              "Reserved value of 3 used for period in SROUND or "
              "S45ROUND hint in %s (PC %d)."))
            
            state._validationFailed = True
        
        else:
            if is45Case:
                sqrt2 = math.sqrt(2.0)
                period = [0.5 * sqrt2, sqrt2, 2.0 * sqrt2][periodIndex]
            
            else:
                period = [0.5, 1.0, 2.0][periodIndex]
            
            phase = [0.0, 0.25, 0.5, 0.75][phaseIndex]
            threshold = [x / 8.0 for x in range(-4, 12)]
            
            if thresholdIndex:
                threshold = threshold[thresholdIndex]
            else:
                threshold = -1.0
            
            need = [
              toCollection(period, 64),
              toCollection(phase, 64),
              toCollection(threshold, 64)]
            
            state.assignDeep('graphicsState', 'roundState', need)
            
            if 'fdefEntryStack' in kwArgs:
                # We only note graphicsState effects when a
                # FDEF stack is available
                
                state.statistics.noteGSEffect(
                  tuple(kwArgs['fdefEntryStack']),
                  state.pc + self.ultDelta)
    
    fatObj = kwArgs.get('fdefArgTracer', None)
    
    if fatObj is not None:
        fatObj.notePop('roundState', 'SROUND')
    
    state.assign('pc', state.pc + 1)
예제 #9
0
def hint_DELTAC(self, **kwArgs):
    """
    DELTAC[1-3]: CVT-based delta hint, opcodes 0x73-0x75
    
    Note that this current implementation ignores the bandDelta kwArg.
    
    >>> logger = utilities.makeDoctestLogger("DELTAC_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(98, 4, 114, 6, 2)
    >>> hint_DELTAC(h, logger=logger)
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> h.state.statistics.maxima.cvt
    6
    >>> h.state.stack[:] = [0]
    >>> h.state.changed('stack')
    >>> hint_DELTAC(h, logger=logger)
    DELTAC_test - ERROR - In test (PC 1) the value 0 is too low.
    >>> h.state.stack[:] = []
    >>> h.state.changed('stack')
    >>> h.state.assign('pc', 0)
    >>> hint_DELTAC(h, logger=logger)
    DELTAC_test - CRITICAL - Stack underflow in test (PC 0).
    >>> h = _testingState(98, 4, 114, 6, toCollection([2]))
    >>> hint_DELTAC(h, logger=logger)
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> h.state.statistics.maxima.cvt
    6
    >>> h = _testingState(98, 4, 114, 6, toCollection([2, 3]))
    >>> hint_DELTAC(h, logger=logger)
    DELTAC_test - ERROR - In test (PC 0) a Collection value was used, but is not supported in fontio.
    """
    
    state = self.state
    stats = state.statistics
    logger = self._getLogger(**kwArgs)
    n = self._popRemove(state, 'stack')
    
    if n is None:
        state.assign('pc', doNotProceedPC)
        return
    
    count = self._toNumber(n, doCheck=True)
    
    if count is None or self._popRemove(state, 'pushHistory') is None:
        state.assign('pc', doNotProceedPC)
        return
    
    if count:  # some fonts have a zero count as a pseudo-NOP, hmph.
        historyPiece = self._popRemove(state, 'pushHistory', 2 * count)
        
        if historyPiece is None:
            state.assign('pc', doNotProceedPC)
            return
        
        historyPiece = historyPiece[1::2]
        allPieces = self._popRemove(state, 'stack', 2 * count)
        
        if allPieces is None:
            state.assign('pc', doNotProceedPC)
            return
        
        argPiece = allPieces[0::2]
        okToProceed = True
        
        for arg in argPiece:
            okToProceed = (
              self._8BitCheck("DELTAC", arg, logger) and
              okToProceed)
        
        if okToProceed:
            if argPiece != sorted(argPiece):
                logger.warning((
                  'V0491',
                  (self.ultParent.infoString, state.pc + self.ultDelta),
                  "The DELTAC opcode in %s (PC %d) has args unsorted "
                  "by PPEM. They should be sorted if this font is "
                  "to be used with iType."))
            
            cvtPiece = allPieces[1::2]
            
            missing = sorted(
              n
              for x in cvtPiece
              for n in toCollection(x)
              if n not in state.cvt)
            
            if missing:
                logger.error((
                  'E6005',
                  (self.ultParent.infoString,
                   state.pc + self.ultDelta,
                   missing),
                  "For DELTAC opcode in %s (PC %d), these "
                  "CVT indices are not present in CVT table: %s"))
                
                state._validationFailed = True
            
            thisHint = self.ultParent
            thisPC = state.pc + self.ultDelta
            
            for i, cvtIndex in enumerate(cvtPiece):
                he = historyPiece[i]
                stats.addHistory('cvt', cvtIndex, he)
                
                stats.noteEffect(
                  'cvt',
                  cvtIndex,
                  thisHint.infoString,
                  thisPC,
                  2 * (i - count))
    
    else:
        logger.warning((
          'V0490',
          (self.ultParent.infoString, state.pc + self.ultDelta),
          "DELTAC opcode in %s (PC %d) has a specification count "
          "of zero, meaning it's being used as a pseudo-NOP."))
    
    fatObj = kwArgs.get('fdefArgTracer', None)
    
    if fatObj is not None:
        fatObj.notePop(None, 'DELTAC')
        
        for i in range(count):
            fatObj.notePop('cvtIndex', 'DELTAC')
            fatObj.notePop('deltaArg', 'DELTAC')
    
    state.assign('pc', state.pc + 1)
예제 #10
0
def hint_RS(self, **kwArgs):
    """
    RS: Read storage, opcode 0x43
    
    >>> logger = utilities.makeDoctestLogger("RS_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(toCollection([2, 5, 19]), 5)
    >>> h.state.storage[5] = 10
    >>> h.state.changed('storage')
    >>> hint_RS(h, logger=logger)
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> h.state.statistics.maxima.storage
    5
    >>> _popSync(h.state)
    10
    >>> h.state.statistics.pprint(keys=('storage',))
    History for storage locations:
      5: Extra index 1 in PUSH opcode index 0 in test
    >>> hint_RS(h, logger=logger)
    >>> h.state.statistics.pprint(keys=('storage',))
    History for storage locations:
      2: Extra index 0 in PUSH opcode index 0 in test
      5:
        Extra index 0 in PUSH opcode index 0 in test
        Extra index 1 in PUSH opcode index 0 in test
      19: Extra index 0 in PUSH opcode index 0 in test
    >>> _popSync(h.state)
    Singles: [0, 10]
    >>> hint_RS(h, logger=logger)
    RS_test - CRITICAL - Stack underflow in test (PC 2).
    """

    state = self.state
    stats = state.statistics
    store = state.storage
    logger = self._getLogger(**kwArgs)
    index = self._popRemove(state, 'stack', coerceToCollection=True)

    if index is None:
        state.assign('pc', doNotProceedPC)
        return

    history = self._popRemove(state, 'pushHistory')

    if history is None:
        state.assign('pc', doNotProceedPC)
        return

    it = iter(index)
    n = toCollection(store.get(next(it), 0))

    for i in it:
        n = n.addToCollection(store.get(i, 0))

    n2 = n.toNumber()

    if n2 is not None:
        n = n2

    state.append('stack', n)
    stats.addHistory('storage', index, history)

    stats.noteEffect('storage', index, self.ultParent.infoString,
                     state.pc + self.ultDelta, -1)

    state.append('pushHistory', self._synthStorageHistory(index))
    state.assign('pc', state.pc + 1)

    fatObj = kwArgs.get('fdefArgTracer', None)

    if fatObj is not None:
        fatObj.notePop('storageIndex', 'RS')
        fatObj.notePush()
예제 #11
0
def hint_MSIRP(self, **kwArgs):
    """
    MSIRP: Move stack indirect relative point, opcodes 0x3A-0x3B
    
    >>> logger = utilities.makeDoctestLogger("MSIRP_test")
    >>> _popSync, _testingState = _testFuncs()
    >>> h = _testingState(7, -32, 12, 3, 64)
    >>> m = h.state.statistics.maxima
    >>> hint_MSIRP(h, logger=logger)
    >>> len(h.state.stack) == len(h.state.pushHistory)
    True
    >>> m.point
    3
    >>> hint_srp0.hint_SRP0(h, logger=logger)
    >>> hint_MSIRP(h, logger=logger)
    >>> m.point
    12
    >>> h.state.statistics.pprint(keys=('point',))
    History for outline points (glyph zone):
      0: Implicit zero for RP0 used at opcode index 0 in test
      3: Extra index 3 in PUSH opcode index 0 in test
      7: Extra index 0 in PUSH opcode index 0 in test
      12: Extra index 2 in PUSH opcode index 0 in test
    >>> hint_MSIRP(h, logger=logger)
    MSIRP_test - CRITICAL - Stack underflow in test (PC 3).
    """
    
    state = self.state
    gs = state.graphicsState
    logger = self._getLogger(**kwArgs)
    distance = self._popRemove(state, 'stack', coerceToCollection=True)
    
    if distance is None:
        state.assign('pc', doNotProceedPC)
        return
    
    distance = distance.changedBasis(1)
    point = self._popRemove(state, 'stack', coerceToCollection=True)
    
    if point is None:
        state.assign('pc', doNotProceedPC)
        return
    
    history = self._popRemove(state, 'pushHistory', 2)
    
    if history is None:
        state.assign('pc', doNotProceedPC)
        return
    
    history = history[0]
    
    if (None not in distance) and any(n < -16384 or n >= 16384 for n in distance):
        logger.error((
          'E6019',
          (self.ultParent.infoString, state.pc + self.ultDelta),
          "MSIRP opcode in %s (PC %d) has out-of-range FUnit distance."))
        
        state._validationFailed = True
    
    elif self._zoneCheck("MSIRP", (0, 1), logger):
        zp0, zp1 = gs.zonePointer0, gs.zonePointer1
        
        v = [
          (zp1, point, True),
          (zp0, toCollection(gs.referencePoint0), False)]
        
        if self._pointCheck(
          "MSIRP",
          v,
          logger,
          kwArgs.get('extraInfo', {})):
            
            state.statistics.addHistory(
              'pointMoved',
              (zp1, point),
              history)
            
            refHist = self._synthRefHistory(0)
            
            state.statistics.addHistory(
              'point',
              (zp0, gs.referencePoint0),
              refHist)
            
            if kwArgs.get('setRP0', False):
                state.assignDeep('graphicsState', 'referencePoint0', point)
                state.refPtHistory[0] = history
                state.changed('refPtHistory')
                
                if 'fdefEntryStack' in kwArgs:
                    # We only note graphicsState effects when a
                    # FDEF stack is available
                    
                    state.statistics.noteGSEffect(
                      tuple(kwArgs['fdefEntryStack']),
                      state.pc + self.ultDelta)
    
    fatObj = kwArgs.get('fdefArgTracer', None)
    
    if fatObj is not None:
        fatObj.notePop(None, 'MSIRP')
        fatObj.notePop('pointIndex', 'MSIRP')
    
    state.assign('pc', state.pc + 1)
예제 #12
0
class GraphicsState(object):
    """
    Objects representing the graphics state used during interpretation of
    TrueType hints.
    """

    #
    # Initialization method
    #

    def __init__(self, setAllAttrs=True):
        """
        Creates a graphics state with all values at their default initial values.
        
        >>> gs = GraphicsState()
        >>> gs.roundState
        [Singles: [1.0], Singles: [0.0], Singles: [0.5]]
        >>> gs.scanControl
        0
        """

        if setAllAttrs:
            d = self.__dict__
            kd = self.keyData
            self._stamps = dict.fromkeys(kd, -1)
            self._stamper = stamp.Stamper()

            for key, t in kd.items():
                d[key] = t[KEYINIT]()

    #
    # Class methods
    #

    @classmethod
    def fromargs(cls, **kwArgs):
        """
        Class method that can be used as an alternative constructor when actual
        values (as opposed to defaults) are needed.
        
        >>> g = GraphicsState.fromargs(loop=19)
        >>> g.loop, g.autoFlip
        (19, True)
        """

        r = GraphicsState()
        r.__dict__.update(kwArgs)
        return r

    #
    # Special methods
    #

#     def __copy__(self):
#         r = GraphicsState(setAllAttrs=False)
#         r.__dict__ = self.__dict__.copy()
#         return r

    def __deepcopy__(self, memo=None):
        r = GraphicsState(setAllAttrs=False)
        r._stamps = self._stamps.copy()  # NOT DEEP
        r._stamper = self._stamper
        d = self.__dict__
        rd = r.__dict__

        for k, t in self.keyData.items():
            rd[k] = t[KEYDEEPCOPY](d[k])

        return r

    def __eq__(self, other):
        """
        Returns True if the two GraphicsState objects are equal. This method
        only compares items whose stamps differ, to gain speed.
        
        >>> t1 = GraphicsState()
        >>> t2 = t1.__deepcopy__()
        >>> t1 == t2
        True
        >>> t1.assign('deltaShift', 2)
        >>> t1 == t2
        False
        >>> t1.assign('deltaShift', 3)
        >>> t1 == t2
        True
        """

        if self is other:
            return True

        dSelf = self.__dict__
        dOther = other.__dict__

        if self._stamper is other._stamper:
            selfStamps = self._stamps
            otherStamps = other._stamps

            for k in dSelf:
                if k[0] != '_':
                    if selfStamps[k] != otherStamps[k]:
                        # Only do actual comparison if stamps differ
                        if dSelf[k] != dOther[k]:
                            return False

            return True

        return all(dSelf[k] == dOther[k] for k in self.keyData)

#     def __ne__(self, other): return self is not other and self.__dict__ != other.__dict__

#
# Public methods
#

#     def asDict(self):
#         """
#         Sometimes it's useful to have the contents of a GraphicsState object as
#         a dict. Remember, though, for regular processing the object and
#         attribute approach is much, much faster.
#
#         >>> g = GraphicsState()
#         >>> d = g.asDict()
#         >>> for key in sorted(d): print(key, d[key])
#         autoFlip True
#         cvtCutIn Singles: [1.0625]
#         deltaBase 9
#         deltaShift 3
#         dualVector (Singles: [1.0], Singles: [0.0])
#         freedomVector (Singles: [1.0], Singles: [0.0])
#         instructControl 0
#         loop 1
#         minimumDistance Singles: [1.0]
#         projectionVector (Singles: [1.0], Singles: [0.0])
#         referencePoint0 0
#         referencePoint1 0
#         referencePoint2 0
#         roundState [Singles: [1.0], Singles: [0.0], Singles: [0.5]]
#         scanControl 0
#         scanType 0
#         singleWidthCutIn Singles: [1.0]
#         singleWidthValue 0
#         zonePointer0 1
#         zonePointer1 1
#         zonePointer2 1
#         """
#
#         return self.__dict__.copy()

    def asImmutable(self):
        """
        Returns an immutable version of the object.
        """

        kd = self.keyData
        d = self.__dict__
        v = []

        for k in self.sortedKeys:
            t = kd[k]
            v.append((k, t[KEYASIMMUTABLE](d[k])))

        return tuple(v)

    def assign(self, key, value):
        """
        Sets the specified value, adjusting stamps if needed.
        """

        d = self.__dict__

        if d[key] != value:
            d[key] = value
            d['_stamps'][key] = d['_stamper'].stamp()

    def changed(self, key):
        """
        This method is called when a client changes one of the attributes; it
        updates the stamp for that attribute.
        """

        self._stamps[key] = self._stamper.stamp()

    def combine(self, other):
        """
        Merges other into self.
        
        >>> g1 = GraphicsState()
        >>> g2 = GraphicsState.fromargs(deltaBase=12, freedomVector=yAxis)
        >>> g1.combine(g2)
        >>> g1.pprint()
        Auto-flip: True
        CVT cut-in: Singles: [1.0625]
        DELTA base: Singles: [9, 12]
        DELTA shift: 3
        Dual projection vector: (Singles: [1], Singles: [0])
        Freedom vector: (Ranges: [(*, *, 1, phase=0)], Ranges: [(*, *, 1, phase=0)])
        Instruction control: 0
        Loop counter: 1
        Minimum distance: Singles: [1.0]
        Projection vector: (Singles: [1], Singles: [0])
        Reference point 0: 0
        Reference point 1: 0
        Reference point 2: 0
        Round state: [Singles: [1.0], Singles: [0.0], Singles: [0.5]]
        Scan control: 0
        Scan type: 0
        Single width cut-in: Singles: [1.0]
        Single width value: 0
        Zone pointer 0: 1
        Zone pointer 1: 1
        Zone pointer 2: 1
        """

        dSelf = self.__dict__
        dOther = other.__dict__
        selfStamps = dSelf['_stamps']
        kd = self.keyData

        if dSelf['_stamper'] is dOther['_stamper']:
            otherStamps = dOther['_stamps']

            for k in kd:
                if selfStamps[k] == otherStamps[k]:
                    continue

                valueSelf = dSelf[k]
                valueOther = dOther[k]

                if valueSelf is valueOther or valueSelf == valueOther:
                    continue

                dSelf[k] = kd[k][KEYCOMBINE](valueSelf, valueOther)
                selfStamps[k] = dSelf['_stamper'].stamp()

        else:
            for k in kd:
                valueSelf = dSelf[k]
                valueOther = dOther[k]

                if valueSelf is valueOther or valueSelf == valueOther:
                    continue

                dSelf[k] = kd[k][KEYCOMBINE](valueSelf, valueOther)
                selfStamps[k] = dSelf['_stamper'].stamp()

    def merged(self, other):
        """
        Returns a new GraphicsState object whose contents are the merger of the
        contents of the two input GraphicsState objects, creating Collections
        where needed.
        
        >>> g1 = GraphicsState()
        >>> g2 = GraphicsState.fromargs(deltaBase=12, freedomVector=yAxis)
        >>> g1.merged(g2).pprint()
        Auto-flip: True
        CVT cut-in: Singles: [1.0625]
        DELTA base: Singles: [9, 12]
        DELTA shift: 3
        Dual projection vector: (Singles: [1], Singles: [0])
        Freedom vector: (Ranges: [(*, *, 1, phase=0)], Ranges: [(*, *, 1, phase=0)])
        Instruction control: 0
        Loop counter: 1
        Minimum distance: Singles: [1.0]
        Projection vector: (Singles: [1], Singles: [0])
        Reference point 0: 0
        Reference point 1: 0
        Reference point 2: 0
        Round state: [Singles: [1.0], Singles: [0.0], Singles: [0.5]]
        Scan control: 0
        Scan type: 0
        Single width cut-in: Singles: [1.0]
        Single width value: 0
        Zone pointer 0: 1
        Zone pointer 1: 1
        Zone pointer 2: 1
        """

        r = self.__deepcopy__()
        r.combine(other)
        return r

    def pprint(self, **kwArgs):
        """
        Pretty-prints the object to the specified stream. Two keyword arguments
        are used:
        
            indent      How many spaces to indent on left (default 0)
            keys        Which keys to report (default all)
            stream      Stream to receive output (default sys.stdout)
        
        >>> g = GraphicsState()
        >>> g.pprint()
        Auto-flip: True
        CVT cut-in: Singles: [1.0625]
        DELTA base: 9
        DELTA shift: 3
        Dual projection vector: (Singles: [1], Singles: [0])
        Freedom vector: (Singles: [1], Singles: [0])
        Instruction control: 0
        Loop counter: 1
        Minimum distance: Singles: [1.0]
        Projection vector: (Singles: [1], Singles: [0])
        Reference point 0: 0
        Reference point 1: 0
        Reference point 2: 0
        Round state: [Singles: [1.0], Singles: [0.0], Singles: [0.5]]
        Scan control: 0
        Scan type: 0
        Single width cut-in: Singles: [1.0]
        Single width value: 0
        Zone pointer 0: 1
        Zone pointer 1: 1
        Zone pointer 2: 1
        >>> g.pprint(indent=3, keys=('loop', 'autoFlip'))
           Loop counter: 1
           Auto-flip: True
        """

        p = pp.PP(**kwArgs)
        f = p.simple
        d = self.__dict__
        kd = self.keyData

        for k in kwArgs.get('keys', self.sortedKeys):
            if k[0] != '_':
                f(d[k], kd[k][KEYLABEL])

    def pprint_changes(self, prior, **kwArgs):
        """
        Prints nothing if the two objects are equal. Otherwise prints a label
        (if specified) and what changed. Keyword arguments used are:
        
            indent          How many spaces to indent on left (default 0)
            indentDelta     Extra spaces per new indent (default 2)
            keys            Which keys to report (default all)
            label           Header label (no default)
            stream          Stream to receive output (default sys.stdout)
        
        >>> gs1 = GraphicsState()
        >>> gs2 = GraphicsState.fromargs(loop=4, freedomVector=yAxis, zonePointer1=0)
        >>> gs2.pprint_changes(gs1)
        Freedom vector changed from (Singles: [1], Singles: [0]) to (Singles: [0], Singles: [1])
        Loop counter changed from 1 to 4
        Zone pointer 1 changed from 1 to 0
        >>> gs2.pprint_changes(gs1, label="Graphics state changes", keys=('loop',))
        Graphics state changes:
          Loop counter changed from 1 to 4
        """

        p = pp.PP(**kwArgs)
        f = p.diff
        dSelf = self.__dict__
        dPrior = prior.__dict__
        kd = self.keyData

        for k in kwArgs.get('keys', self.sortedKeys):
            selfValue = dSelf[k]
            priorValue = dPrior[k]

            if selfValue != priorValue:
                f(selfValue, priorValue, kd[k][KEYLABEL])

    #
    # Dispatch table
    #

    # keyData is a dict of tuples, indexed by key:
    #   [0]  label
    #   [1]  item initialization function (no args)
    #   [2]  item combine function (two args)
    #   [3]  item deepcopy function (one arg)
    #   [4]  item asImmutable function (one arg)

    f = functools.partial
    c = collection.cluster
    idem = lambda x: x
    asImm = lambda x: collection.toCollection(x).asImmutable()

    asImmSeq = (lambda x: tuple(
        collection.toCollection(obj).asImmutable() for obj in x))

    keyData = {
        'autoFlip':
        ("Auto-flip", lambda: True,
         lambda x, y: collection.toCollection([0, 1]), idem, asImm),
        'cvtCutIn': ("CVT cut-in", lambda: cvtCutInDefault26Dot6,
                     f(c, coerceToNumber=False), idem, asImm),
        'deltaBase': ("DELTA base", lambda: 9, c, idem, asImm),
        'deltaShift': ("DELTA shift", lambda: 3, c, idem, asImm),
        'dualVector': ("Dual projection vector", lambda: xAxis,
                       _seqCombine_special, idem, asImmSeq),
        'freedomVector':
        ("Freedom vector", lambda: xAxis, _seqCombine_special, idem, asImmSeq),
        'instructControl': ("Instruction control", lambda: 0, c, idem, asImm),
        'loop': ("Loop counter", lambda: 1, c, idem, asImm),
        'minimumDistance':
        ("Minimum distance", lambda: one26Dot6, c, idem, asImm),
        'projectionVector': ("Projection vector", lambda: xAxis,
                             _seqCombine_special, idem, asImmSeq),
        'referencePoint0': ("Reference point 0", lambda: 0, c, idem, asImm),
        'referencePoint1': ("Reference point 1", lambda: 0, c, idem, asImm),
        'referencePoint2': ("Reference point 2", lambda: 0, c, idem, asImm),
        'roundState':
        ("Round state", lambda: [one26Dot6, zero26Dot6, half26Dot6],
         f(_seqCombine_list, n=3), list, asImmSeq),
        'scanControl': ("Scan control", lambda: 0, c, idem, asImm),
        'scanType': ("Scan type", lambda: 0, c, idem, asImm),
        'singleWidthCutIn': ("Single width cut-in", lambda: one26Dot6,
                             f(c, coerceToNumber=False), idem, asImm),
        'singleWidthValue': (
            "Single width value",
            lambda: 0,  # 0 is FUnits, not 26.6
            c,
            idem,
            asImm),
        'zonePointer0': ("Zone pointer 0", lambda: 1, c, idem, asImm),
        'zonePointer1': ("Zone pointer 1", lambda: 1, c, idem, asImm),
        'zonePointer2': ("Zone pointer 2", lambda: 1, c, idem, asImm)
    }

    sortedKeys = sorted(keyData)
    del f, c, idem, asImm, asImmSeq