Ejemplo n.º 1
0
 def buildBinary(self, w, **kwArgs):
     """
     Adds the binary data to the specified LinkedWriter.
     
     >>> h = utilities.hexdump
     >>> h(_testingValues[0].binaryString())
            0 | 0002 0000                                |....            |
     
     >>> h(_testingValues[1].binaryString())
            0 | 0001 0004 0004 0002  0002 0002 0001      |..............  |
     
     >>> h(_testingValues[2].binaryString())
            0 | 0002 0004 0004 0006  0002 0007 0007 0001 |................|
           10 | 000A 000B 0002 000F  000F 0002           |............    |
     """
     
     if 'stakeValue' in kwArgs:
         stakeValue = kwArgs.pop('stakeValue')
         w.stakeCurrentWithValue(stakeValue)
     else:
         stakeValue = w.stakeCurrent()
     
     if self:
         v = sorted(self)
         sv = []
         
         if len(v) == (v[-1] - v[0] + 1):
             # ok to try format 1
             w1 = writer.LinkedWriter()
             w1.add("3H", 1, v[0], len(v))
             w1.addGroup("H", [self[i] for i in v])
             sv.append(w1.binaryString())
         
         w2 = writer.LinkedWriter()
         groups = []
         
         for start, stop, skip in utilities.monotonicGroupsGenerator(v):
             cumulCount = 0
             it = (self[i] for i in range(start, stop, skip))
             
             for k, g in itertools.groupby(it):
                 subCount = len(list(g))
                 
                 groups.append((
                   start + cumulCount,
                   start + cumulCount + subCount - 1,
                   k))
                 
                 cumulCount += subCount
         
         w2.add("HH", 2, len(groups))
         w2.addGroup("3H", groups)
         sv.append(w2.binaryString())
         sv.sort(key=len)
         w.addString(sv[0])  # shortest
     
     else:
         w.add("HH", 2, 0)  # format 0, no ranges
Ejemplo n.º 2
0
def _binaryString(self, **kwArgs):
    """
    Returns a string with the binary data for the object. This is just a thin
    wrapper around a call to buildBinary(), which is what you should be using,
    if possible.
    
    %start
    %kind
    protocol method
    %return
    A string containing the binary data for the object.
    %note
    This method, while occasionally useful, should generally not be used by
    your code; use buildBinary instead, which aggregates all the binary content
    together, and thus drastically reduces memory use compared to the old
    fontio1-based binaryString() approach.
    %end
    """

    try:
        self.buildBinary
    except AttributeError:
        raise NotImplementedError()

    w = writer.LinkedWriter()
    self.buildBinary(w, **kwArgs)
    return w.binaryString()
Ejemplo n.º 3
0
    def _makeLookup2(self, keySpan):
        """
        Creates and returns a bytestring for the lookup-specific portions of
        the output data, for lookup format 2.
        
        >>> s1 = splits_distance.Splits_Distance([10, 500, 800])
        >>> s2 = splits_distance.Splits_Distance([250, 500])
        >>> s3 = s1.__deepcopy__()
        >>> d = Lcar({5: s1, 6: s1, 15: s2, 25: s3})
        >>> ks = span.Span(d)
        >>> utilities.hexdump(d._makeLookup2(ks))
               0 | 0002 0006 0003 000C  0001 0006 0006 0005 |................|
              10 | 002A 000F 000F 003A  0019 0019 0032 FFFF |.*.....:.....2..|
              20 | FFFF 0000 0003 000A  01F4 0320 0003 000A |........... ....|
              30 | 01F4 0320 0002 00FA  01F4                |... ......      |
        """

        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()
        w.add("H", 2)
        bStake = w.getNewStake()
        w.addReplaceableString(bStake, b' ' * 10)  # don't know nUnits yet...
        pool = {}  # an id-pool; safe because scope is limited to this method
        nUnits = 0

        for spanStart, spanEnd in keySpan:
            firstGlyph = spanStart

            it = ((id(self[i]), self[i])
                  for i in range(spanStart, spanEnd + 1))

            for k, g in itertools.groupby(it, operator.itemgetter(0)):
                v = list(g)
                if k not in pool:
                    pool[k] = (w.getNewStake(), v[0][1])

                n = len(v)
                w.add("2H", firstGlyph + n - 1, firstGlyph)

                w.addUnresolvedOffset("H",
                                      stakeStart,
                                      pool[k][0],
                                      offsetByteDelta=6)

                firstGlyph += n
                nUnits += 1

        # add the sentinel (doesn't count toward nUnits)
        w.add("3H", 0xFFFF, 0xFFFF, 0)

        # we now know nUnits, so retrofit the BSH
        w.setReplaceableString(
            bStake,
            bsh.BSH(nUnits=nUnits, unitSize=6).binaryString())

        # Now add the deferred values
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 4
0
    def _makeLookup6(self, keySpan):
        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()
        w.add("H", 6)
        bsh.BSH(nUnits=len(self), unitSize=4).buildBinary(w)
        pool = {}  # an id-pool

        for spanStart, spanEnd in keySpan:  # a convenient already-sorted source...
            for glyphIndex in range(spanStart, spanEnd + 1):
                w.add("H", glyphIndex)
                obj = self[glyphIndex]
                thisID = id(obj)

                if thisID not in pool:
                    pool[thisID] = (w.getNewStake(), obj)

                w.addUnresolvedOffset("H",
                                      stakeStart,
                                      pool[thisID][0],
                                      offsetByteDelta=6)

        # add the sentinel (doesn't count toward nUnits)
        w.add("2H", 0xFFFF, 0)

        # Now add the deferred values
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 5
0
    def _makeLookup8(self, keySpan, emptyObj):
        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()
        w.add("H", 8)
        firstGlyph = keySpan[0][0]
        count = keySpan[-1][1] - firstGlyph + 1
        w.add("2H", firstGlyph, count)
        pool = {}

        for i in range(firstGlyph, firstGlyph + count):
            obj = self.get(i, emptyObj)
            objID = id(obj)

            if objID not in pool:
                pool[objID] = (w.getNewStake(), obj)

            w.addUnresolvedOffset("H",
                                  stakeStart,
                                  pool[objID][0],
                                  offsetByteDelta=6)  # table-relative

        # Now add the deferred values
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 6
0
def bestPUSHString(inputList):
    """
    Return a binary string representing the best compression for the specified
    list.
    
    >>> [c for c in bestPUSHString([12])]
    [176, 12]
    >>> [c for c in bestPUSHString([512, 513])]
    [185, 2, 0, 2, 1]
    >>> [c for c in bestPUSHString([12, 512, 513, 13])]
    [176, 12, 185, 2, 0, 2, 1, 176, 13]
    """
    
    w = writer.LinkedWriter()
    
    for useWordForm, g in itertools.groupby(inputList, lambda x: x < 0 or x > 255):
        fullList = list(g)
        
        while fullList:
            v = fullList[:255]
            useNForm = len(v) > 8
            
            if useWordForm:
                data = ([0x41, len(v)] if useNForm else [0xB7 + len(v)])
            else:
                data = ([0x40, len(v)] if useNForm else [0xAF + len(v)])
            
            w.addGroup("B", data)
            w.addGroup(("h" if useWordForm else "B"), v)
            fullList = fullList[255:]
    
    return w.binaryString()
Ejemplo n.º 7
0
    def _makeFormat1(sortedKeys):
        """
        Returns a format 1 binary string.
        """

        w = writer.LinkedWriter()
        w.add("HH", 1, len(sortedKeys))
        w.addGroup("H", sortedKeys)  # already checked density and monotonicity
        return w.binaryString()
Ejemplo n.º 8
0
    def _buildBinary_sub(self, contigRanges):
        """
        Does the actual work for binary string building. This method returns
        None if so many idRangeOffset values get accumulated that the offsets
        go over 64K; in this case the caller will redo the contigRanges via
        subdivision, which will always succeed.
        """

        w = writer.LinkedWriter()
        startLength = w.byteLength
        w.add("H", 4)  # format
        lengthStake = w.addDeferredValue("H")
        w.add("H", (self.language or 0))
        segCount = len(contigRanges) + 1
        idDeltas = [0] * len(contigRanges)
        idRangeOffsets = [0] * len(contigRanges)
        currentGIA = []
        f = self._fmt4Analyze

        for i, x in enumerate(contigRanges):
            glyphs = [self.get(k, 0) for k in range(x[0], x[1] + 1)]
            idDelta = f(glyphs, x[0])
            idDeltas[i] = idDelta % 65536

            if idDelta == 0:
                ro = 2 * (len(currentGIA) + segCount - i)

                if ro >= 0x10000:
                    return None

                idRangeOffsets[i] = ro
                currentGIA.extend(glyphs)

            else:
                idRangeOffsets[i] = 0

        idDeltas.append(1)
        idRangeOffsets.append(0)
        w.add("H", 2 * segCount)
        bshObj = bsh.BSH(nUnits=segCount, unitSize=2)
        w.addString(bshObj.binaryString(skipUnitSize=True)[2:])
        w.addGroup("H", (x[1] for x in contigRanges))
        w.add("hH", -1, 0)
        w.addGroup("H", (x[0] for x in contigRanges))
        w.add("h", -1)
        w.addGroup("H", idDeltas)
        w.addGroup("H", idRangeOffsets)

        if currentGIA:
            w.addGroup("H", currentGIA)

        # We pin the length field to FFFF in case it's larger.
        byteSize = min(0xFFFF, w.byteLength - startLength)
        w.setDeferredValue(lengthStake, "H", byteSize)
        return w.binaryString()
Ejemplo n.º 9
0
    def _makeLookup4(self, keySpan):
        """
        Creates and returns a bytestring for the lookup-specific portions of
        the output data, for lookup format 4.
        
        >>> s1 = splits_distance.Splits_Distance([10, 500, 800])
        >>> s2 = splits_distance.Splits_Distance([250, 500])
        >>> s3 = s1.__deepcopy__()
        >>> d = Lcar({5: s1, 6: s1, 15: s2, 25: s3})
        >>> ks = span.Span(d)
        >>> utilities.hexdump(d._makeLookup4(ks))
               0 | 0004 0006 0003 000C  0001 0006 0006 0005 |................|
              10 | 002A 000F 000F 002E  0019 0019 0030 FFFF |.*...........0..|
              20 | FFFF 0000 0032 0032  0042 003A 0003 000A |.....2.2.B.:....|
              30 | 01F4 0320 0003 000A  01F4 0320 0002 00FA |... ....... ....|
              40 | 01F4                                     |..              |
        """

        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()
        w.add("H", 4)
        bsh.BSH(nUnits=len(keySpan), unitSize=6).buildBinary(w)
        pool = {}  # an id-pool
        offsetOffsets = [w.getNewStake() for obj in keySpan]

        for (spanStart, spanEnd), stake in zip(keySpan, offsetOffsets):
            w.add("2H", spanEnd, spanStart)
            w.addUnresolvedOffset("H", stakeStart, stake, offsetByteDelta=6)

        # add the sentinel (doesn't count toward nUnits)
        w.add("3H", 0xFFFF, 0xFFFF, 0)

        for (spanStart, spanEnd), stake in zip(keySpan, offsetOffsets):
            w.stakeCurrentWithValue(stake)

            for glyphIndex in range(spanStart, spanEnd + 1):
                obj = self[glyphIndex]
                thisID = id(obj)

                if thisID not in pool:
                    pool[thisID] = (w.getNewStake(), obj)

                w.addUnresolvedOffset("H",
                                      stakeStart,
                                      pool[thisID][0],
                                      offsetByteDelta=6)

        # Now add the deferred values
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 10
0
    def _makeFormat2(sortedGlyphs):
        groups = []
        currIndex = 0
        it = utilities.monotonicGroupsGenerator(sortedGlyphs)

        for start, stop, skip in it:
            groups.append((start, stop - 1, currIndex))
            currIndex += (stop - start)

        w = writer.LinkedWriter()
        w.add("HH", 2, len(groups))
        w.addGroup("3H", groups)
        return w.binaryString()
Ejemplo n.º 11
0
    def binaryString(self, **kwArgs):
        """
        Returns the binary string for the object. If the "live" object has not
        yet been created, this method simply returns the string directly. If
        the "live" object has been created, its buildBinary() method is called,
        as usual.
        """

        if self.cachedObj is None:
            self.w.reset()
            return self.w.rest()

        w = writer.LinkedWriter()
        self.buildBinary(w, **kwArgs)
        return w.binaryString()
Ejemplo n.º 12
0
    def _makeLookup8(self, keySpan):
        """
        Creates and returns a bytestring for the lookup-specific portions of
        the output data, for lookup format 8.
        
        >>> s1 = splits_distance.Splits_Distance([10, 500, 800])
        >>> s2 = splits_distance.Splits_Distance([250, 500])
        >>> s3 = s1.__deepcopy__()
        >>> d = Lcar({5: s1, 6: s1, 15: s2, 25: s3})
        >>> ks = span.Span(d)
        >>> utilities.hexdump(d._makeLookup8(ks))
               0 | 0008 0005 0015 0036  0036 0000 0000 0000 |.......6.6......|
              10 | 0000 0000 0000 0000  0000 0046 0000 0000 |...........F....|
              20 | 0000 0000 0000 0000  0000 0000 0000 003E |...............>|
              30 | 0003 000A 01F4 0320  0003 000A 01F4 0320 |....... ....... |
              40 | 0002 00FA 01F4                           |......          |
        """

        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()
        w.add("H", 8)
        firstGlyph = keySpan[0][0]
        lastGlyph = keySpan[-1][1]
        w.add("2H", firstGlyph, lastGlyph - firstGlyph + 1)
        pool = {}  # an id-pool

        for glyphIndex in range(firstGlyph, lastGlyph + 1):
            if glyphIndex in self:
                obj = self[glyphIndex]
                thisID = id(obj)

                if thisID not in pool:
                    pool[thisID] = (w.getNewStake(), obj)

                w.addUnresolvedOffset("H",
                                      stakeStart,
                                      pool[thisID][0],
                                      offsetByteDelta=6)

            else:
                w.add("H", 0)  # spec is unclear whether this is 0 or 0xFFFF

        # Now add the deferred values
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 13
0
    def _makeLookup6(self, keySpan):
        """
        Creates and returns a bytestring for the lookup-specific portions of
        the output data, for lookup format 6.
        
        >>> s1 = splits_distance.Splits_Distance([10, 500, 800])
        >>> s2 = splits_distance.Splits_Distance([250, 500])
        >>> s3 = s1.__deepcopy__()
        >>> d = Lcar({5: s1, 6: s1, 15: s2, 25: s3})
        >>> ks = span.Span(d)
        >>> utilities.hexdump(d._makeLookup6(ks))
               0 | 0006 0004 0004 0010  0002 0000 0005 0026 |...............&|
              10 | 0006 0026 000F 0036  0019 002E FFFF 0000 |...&...6........|
              20 | 0003 000A 01F4 0320  0003 000A 01F4 0320 |....... ....... |
              30 | 0002 00FA 01F4                           |......          |
        """

        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()
        w.add("H", 6)
        bsh.BSH(nUnits=len(self), unitSize=4).buildBinary(w)
        pool = {}  # an id-pool

        for spanStart, spanEnd in keySpan:  # a nice already-sorted source...
            for glyphIndex in range(spanStart, spanEnd + 1):
                w.add("H", glyphIndex)
                obj = self[glyphIndex]
                thisID = id(obj)

                if thisID not in pool:
                    pool[thisID] = (w.getNewStake(), obj)

                w.addUnresolvedOffset("H",
                                      stakeStart,
                                      pool[thisID][0],
                                      offsetByteDelta=6)

        # add the sentinel (doesn't count toward nUnits)
        w.add("2H", 0xFFFF, 0)

        # Now add the deferred values
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 14
0
    def _makeFormat2(sortedKeys):
        """
        Returns a format 2 binary string.
        """

        # already checked density and monotonicity
        groups = []
        currIndex = 0
        it = utilities.monotonicGroupsGenerator(sortedKeys)

        for start, stop, skip in it:
            groups.append((start, stop - 1, currIndex))
            currIndex += (stop - start)

        w = writer.LinkedWriter()
        w.add("HH", 2, len(groups))
        w.addGroup("3H", groups)
        return w.binaryString()
Ejemplo n.º 15
0
    def _makeLookup0(self, fontGlyphCount):
        """
        Creates and returns a bytestring for the lookup-specific portions of
        the output data, for lookup format 0.
        
        >>> s1 = splits_distance.Splits_Distance([10, 500, 800])
        >>> s2 = splits_distance.Splits_Distance([250, 500])
        >>> s3 = s1.__deepcopy__()
        >>> d = Lcar({5: s1, 6: s1, 15: s2, 25: s3})
        >>> utilities.hexdump(d._makeLookup0(30))
               0 | 0000 0000 0000 0000  0000 0000 0044 0044 |.............D.D|
              10 | 0000 0000 0000 0000  0000 0000 0000 0000 |................|
              20 | 0054 0000 0000 0000  0000 0000 0000 0000 |.T..............|
              30 | 0000 0000 004C 0000  0000 0000 0000 0003 |.....L..........|
              40 | 000A 01F4 0320 0003  000A 01F4 0320 0002 |..... ....... ..|
              50 | 00FA 01F4                                |....            |
        """

        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()
        w.add("H", 0)
        pool = {}  # an id-pool; safe because scope is limited to this method

        for glyphIndex in range(fontGlyphCount):
            if glyphIndex in self:
                obj = self[glyphIndex]
                thisID = id(obj)

                if thisID not in pool:
                    pool[thisID] = (w.getNewStake(), obj)

                w.addUnresolvedOffset("H",
                                      stakeStart,
                                      pool[thisID][0],
                                      offsetByteDelta=6)

            else:
                w.add("H", 0)  # spec is unclear whether this is 0 or 0xFFFF

        # Now add the deferred values
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 16
0
    def buildBinary(self, wOrig, **kwArgs):
        """
        Return a binary string representing the best compression. Note this may
        change the length of the hints.

        Note that it's perfectly OK for a PUSH vector to have an arbitrarily
        large number of entries; this method will break them up into groups of
        255, as needed.
        
        >>> [c for c in Opcode_push([65, 66, 68, 67]).binaryString()]
        [179, 65, 66, 68, 67]
        >>> [c for c in Opcode_push([300, 200, 100, 0, -100]).binaryString()]
        [184, 1, 44, 178, 200, 100, 0, 184, 255, 156]
        """

        # We include the following test in case subsequent edits (via calls to
        # the deleteDataItem method) remove all content from the push.

        if not self.data:
            return

        w = writer.LinkedWriter()

        for useWordForm, g in itertools.groupby(self.data,
                                                lambda x: x < 0 or x > 255):
            fullList = list(g)

            while fullList:
                v = fullList[:255]
                useNForm = len(v) > 8

                if useWordForm:
                    data = ([0x41, len(v)] if useNForm else [0xB7 + len(v)])
                else:
                    data = ([0x40, len(v)] if useNForm else [0xAF + len(v)])

                w.addGroup("B", data)
                w.addGroup(("h" if useWordForm else "B"), v)
                fullList = fullList[255:]

        wOrig.addString(w.binaryString())
Ejemplo n.º 17
0
    def _makeLookup2(self, keySpan):
        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()
        w.add("H", 2)
        bStake = w.getNewStake()
        w.addReplaceableString(bStake, ' ' * 10)  # we don't know nUnits yet...
        pool = {}  # an id-pool; safe because scope is limited to this method
        nUnits = 0

        for spanStart, spanEnd in keySpan:
            firstGlyph = spanStart
            it = ((id(self[i]), self[i])
                  for i in range(spanStart, spanEnd + 1))

            for k, g in itertools.groupby(it, operator.itemgetter(0)):
                v = list(g)
                if k not in pool:
                    pool[k] = (w.getNewStake(), v[0][1])

                n = len(v)
                w.add("2H", firstGlyph + n - 1, firstGlyph)
                w.addUnresolvedOffset(
                    "H", stakeStart, pool[k][0],
                    offsetByteDelta=6)  # table offset, not lookup offset
                firstGlyph += n
                nUnits += 1

        # add the sentinel (doesn't count toward nUnits)
        w.add("3H", 0xFFFF, 0xFFFF, 0)

        # we now know nUnits, so retrofit the BSH
        w.setReplaceableString(
            bStake,
            bsh.BSH(nUnits=nUnits, unitSize=6).binaryString())

        # Now add the deferred values
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 18
0
    def buildBinary(self, w, **kwArgs):
        """
        Adds the binary data for the Cvar object to the specified writer.
        """

        if 'stakeValue' in kwArgs:
            stakeValue = kwArgs.pop('stakeValue')
            w.stakeCurrentWithValue(stakeValue)
        else:
            stakeValue = w.stakeCurrent()

        w.add("L", self.VERSION)
        invDict = self.makeInvertDict()
        sortedCoords = sorted(invDict)
        w.add("H", len(invDict))
        dataStake = w.getNewStake()
        w.addUnresolvedOffset("H", stakeValue, dataStake)
        sizeStakes = []

        for coord in sortedCoords:
            sizeStakes.append(w.addDeferredValue("H"))
            # we hardwire the flags to 0xA000.
            w.add("H", 0xA000)
            coord.buildBinary(w, axisOrder=self.axisOrder)

        # headers are done; now output the data
        w.stakeCurrentWithValue(dataStake)

        for coord, stake in zip(sortedCoords, sizeStakes):
            wWork = writer.LinkedWriter()
            dCVTtoDelta = invDict[coord]
            sortedCVTs = sorted(dCVTtoDelta)
            ppObj = packed_points.PackedPoints(sortedCVTs)
            ppObj.buildBinary(wWork, pointCount=32767)
            deltas = [dCVTtoDelta[c] for c in sortedCVTs]
            self._packDeltas(wWork, deltas)
            bs = wWork.binaryString()
            w.setDeferredValue(stake, "H", len(bs))
            w.addString(bs)
Ejemplo n.º 19
0
 def binaryString(self, **kwArgs):
     """
     Returns the binary string. In the simplest case, this is a one-byte
     string containing the opcode. If it pops some fixed number of
     arguments, it contains first a PUSH for those arguments and then the
     opcode. If it pops a variable number of arguments (based on loop), it
     pushes the arguments, the count, and then includes a SLOOP before the
     specified opcode.
     
     >>> [c for c in OpcodeWithArgs_nonpush(0x01).binaryString()]
     [1]
     >>> [c for c in OpcodeWithArgs_nonpush(0x5D, [204, 6, 204, 7, 2]).binaryString()]
     [180, 204, 6, 204, 7, 2, 93]
     >>> [c for c in OpcodeWithArgs_nonpush(0x3C, [30, 31, 33, 39]).binaryString()]
     [180, 30, 31, 33, 39, 4, 23, 60]
     >>> [c for c in OpcodeWithArgs_nonpush(0x38, [7, 8, 9, 128]).binaryString()]
     [180, 7, 8, 9, 128, 3, 23, 56]
     """
     
     w = writer.LinkedWriter()
     
     if self.data:
         isLoop = self.opcode in loopOpcodes
         
         v = list(self.data)
         
         if isLoop:
             v.append(len(v) - (self.opcode == 0x38))  # SHPIX adjust if needed
         
         maxStack = kwArgs.get('maxStack', [0])
         maxStack[0] = max(maxStack[0], len(v))
         w.addString(bestPUSHString(v))
         
         if isLoop:
             w.add("B", nameToOpcodeMap["SLOOP"])
     
     w.add("B", self.opcode)
     return w.binaryString()
Ejemplo n.º 20
0
    def _makeLookup4(self, keySpan):
        w = writer.LinkedWriter()
        stakeStart = w.stakeCurrent()  # start of lookup
        w.add("H", 4)
        bsh.BSH(nUnits=len(keySpan), unitSize=6).buildBinary(w)
        pool = {}  # id(obj) -> (objStake, obj)
        ooStakes = [w.getNewStake() for obj in keySpan]

        for (spanStart, spanEnd), stake in zip(keySpan, ooStakes):
            w.add("2H", spanEnd, spanStart)
            w.addUnresolvedOffset("H", stakeStart,
                                  stake)  # from lookup, not table!!

        # add the sentinel
        w.add("3H", 0xFFFF, 0xFFFF, 0)

        # add the offset vectors
        for (spanStart, spanEnd), stake in zip(keySpan, ooStakes):
            w.stakeCurrentWithValue(stake)

            for glyphIndex in range(spanStart, spanEnd + 1):
                obj = self[glyphIndex]
                thisID = id(obj)

                if thisID not in pool:
                    pool[thisID] = (w.getNewStake(), obj)

                w.addUnresolvedOffset(
                    "H", stakeStart, pool[thisID][0],
                    offsetByteDelta=6)  # from table, not lookup!!

        # add the actual objects
        for stake, obj in sorted(pool.values(), key=operator.itemgetter(1)):
            obj.buildBinary(w, stakeValue=stake)

        return w.binaryString()
Ejemplo n.º 21
0
    def buildBinary(self, w, **kwArgs):
        """
        Adds the binary data to the specified LinkedWriter. Note that this is
        the flags, xCoordinates, and yCoordinates portions of a simple glyph.
        
        >>> utilities.hexdump(_testingValues[0].binaryString())
               0 | 3711 2111 1401 680A  01E0 FE20           |7.!...h....     |
        
        >>> utilities.hexdump(_testingValues[1].binaryString())
               0 | 0111 2111 2716 3727  026C 0168 E664 6464 |..!.'.7'.l.h.ddd|
              10 | 0262 01E0 FE20 8C32  32FA                |.b... .22.      |
        
        >>> utilities.hexdump(_testingValues[2].binaryString())
               0 | 3711 2111 1311 2111  2716 3727 1401 68F0 |7.!...!.'.7'..h.|
              10 | 0168 E664 6464 0A01  E0FE 2002 5801 E0FE |.h.ddd.... .X...|
              20 | 208C 3232 FA                             | .22.           |
        """

        numPoints = sum(len(c) for c in self)
        vFlags = [0] * numPoints
        xw = writer.LinkedWriter()
        yw = writer.LinkedWriter()
        lastX = 0
        lastY = 0

        for i, p in enumerate(self.pointIterator()):
            f = (1 if p.onCurve else 0)

            if self.highBit:
                f += 0x80

            deltaX = p.x - lastX
            deltaY = p.y - lastY

            if deltaX:
                a = abs(deltaX)

                if a < 256:
                    f += 0x02

                    if deltaX >= 0:
                        f += 0x10

                    xw.add("B", a)

                else:
                    xw.add("h", deltaX)

            else:
                f += 0x10

            if deltaY:
                a = abs(deltaY)

                if a < 256:
                    f += 0x04

                    if deltaY >= 0:
                        f += 0x20

                    yw.add("B", a)

                else:
                    yw.add("h", deltaY)

            else:
                f += 0x20

            lastX = p.x
            lastY = p.y
            vFlags[i] = f

        self._combineRepeats(vFlags)  # works in-place
        w.addGroup("B", vFlags)
        w.addString(xw.binaryString())
        w.addString(yw.binaryString())
Ejemplo n.º 22
0
    def buildBinary(self, w, **kwArgs):
        """
        Add the binary data for the Gvar object to the specified writer.
        
        Apart from the usual keyword argument ('stakeValue'), this method also
        supports the 'sharedPointsAllowed' keyword argument (default True).
        """

        if 'stakeValue' in kwArgs:
            stakeValue = kwArgs.pop('stakeValue')
            w.stakeCurrentWithValue(stakeValue)
        else:
            stakeValue = w.stakeCurrent()

        sharingEnabled = kwArgs.pop('sharedPointsAllowed', True)
        e = kwArgs['editor']
        w.add("L", self.VERSION)
        w.add("H", len(self.axisOrder))
        dGlobalCoords = self._findGlobalCoords(**kwArgs)
        w.add("H", len(dGlobalCoords))

        if dGlobalCoords:
            globalCoordsStake = w.getNewStake()
            w.addUnresolvedOffset("L", stakeValue, globalCoordsStake)

        else:
            w.add("L", 0)

        w.add("H", e.maxp.numGlyphs)
        w.add("H", 1)  # hard-code for 32-bit offsets, for now
        dataStake = w.getNewStake()
        w.addUnresolvedOffset("L", stakeValue, dataStake)
        numGlyphs = utilities.getFontGlyphCount(**kwArgs)
        glyphStakes = [w.getNewStake() for i in range(numGlyphs + 1)]

        for stake in glyphStakes:
            w.addUnresolvedOffset("L", dataStake, stake)

        # Now emit the global coordinates:

        if dGlobalCoords:
            w.stakeCurrentWithValue(globalCoordsStake)
            it = sorted(iter(dGlobalCoords.items()),
                        key=operator.itemgetter(1))

            for coord, globalIndex in it:
                coord.buildBinary(w, axisOrder=self.axisOrder, **kwArgs)

        # Now emit the actual variation data

        w.stakeCurrentWithValue(dataStake)
        gd = self.glyphData
        glyfTable = e.glyf
        hmtxTable = e.hmtx

        for glyphIndex in range(numGlyphs):
            w.alignToByteMultiple(2)
            w.stakeCurrentWithValue(glyphStakes[glyphIndex])

            if glyphIndex not in gd:
                continue

            glyphObj = glyfTable[glyphIndex]

            # We only skip this glyph if it has neither outline nor advance.
            # Zero-advance accents can still vary, as can the width of a space
            # glyph, so both criteria must be met to be skipped.

            if not (glyphObj or hmtxTable[glyphIndex].advance):
                continue

            if glyphObj.isComposite:
                pointCount = len(glyphObj.components) + 4
            else:
                pointCount = glyphObj.pointCount(editor=e) + 4

            pd = gd[glyphIndex]

            if sharingEnabled:
                commonPoints = pd.findCommonPoints()
            else:
                commonPoints = None

            invDict = pd.makeInvertDict()
            tupleCount = len(invDict)

            if commonPoints is not None:
                tupleCount |= 0x8000

            w.add("H", tupleCount)
            sortedCoords = sorted(invDict)
            tupleDataStake = w.getNewStake()
            w.addUnresolvedOffset("H", glyphStakes[glyphIndex], tupleDataStake)
            tupleSizeStakes = []
            privatePoints = []  # bools

            for coord in sortedCoords:
                dPointToDelta = invDict[coord]
                tupleSizeStakes.append(w.addDeferredValue("H"))
                mask = 0

                if coord not in dGlobalCoords:
                    mask |= 0x8000
                else:
                    mask = dGlobalCoords[coord]

                if any(obj.effectiveDomain for obj in dPointToDelta.values()):
                    mask |= 0x4000

                if commonPoints is not None and frozenset(
                        dPointToDelta) == commonPoints:
                    #if commonPoints is not None and set(dPointToDelta) in commonPoints:
                    privatePoints.append(False)

                else:
                    mask |= 0x2000
                    privatePoints.append(True)

                w.add("H", mask)

                if mask & 0x8000:
                    coord.buildBinary(w)

                if mask & 0x4000:
                    # we assume there's one unique domain; check this?
                    domPts = [
                        p for p, delta in dPointToDelta.items()
                        if delta.effectiveDomain
                    ]

                    dom = dPointToDelta[domPts[0]]
                    dom.effectiveDomain.edge1.buildBinary(w)
                    dom.effectiveDomain.edge2.buildBinary(w)

            # The array header is now done, so put out the shared points (if
            # any), and then stake the start of the actual tuple data.

            w.stakeCurrentWithValue(tupleDataStake)

            if commonPoints is not None:
                packedCommons = packed_points.PackedPoints(commonPoints)
                packedCommons.buildBinary(w, pointCount=pointCount - 4)

            # Now that the header is done, emit the actual points/deltas

            for t in zip(sortedCoords, tupleSizeStakes, privatePoints):
                coord, stake, isPrivate = t
                wSub = writer.LinkedWriter()
                dPointToDelta = invDict[coord]
                sortedPoints = sorted(dPointToDelta)

                if isPrivate:
                    packed_points.PackedPoints(dPointToDelta).buildBinary(
                        wSub, pointCount=pointCount - 4)

                # Emit x-deltas, in order, to wSub

                vx = [dPointToDelta[i].x for i in sortedPoints]
                self._emitDeltaBand(wSub, vx)

                # Emit y-deltas, in order, to wSub

                vy = [dPointToDelta[i].y for i in sortedPoints]
                self._emitDeltaBand(wSub, vy)

                bs = wSub.binaryString()
                w.setDeferredValue(stake, "H", len(bs))
                w.addString(bs)

        w.alignToByteMultiple(2)
        w.stakeCurrentWithValue(glyphStakes[-1])
Ejemplo n.º 23
0
    def buildBinary(self, w, **kwArgs):
        """
        Adds the binary content for ``self`` to the specified writer.
    
        :param w: A :py:class:`~fontio3.utilities.writer.LinkedWriter`
        :param kwArgs: Optional keyword arguments (there are none here)
        :return: None
        :raises ValueError: if one or more values cannot fit into two bytes
        
        >>> Format2({100000: 5}).binaryString()
        Traceback (most recent call last):
          ...
        ValueError: One or more keys out of range for format 2 subtable!
        
        >>> Format2({5: 90000}).binaryString()
        Traceback (most recent call last):
          ...
        ValueError: One or more values out of range for format 2 subtable!
        """

        self._preBuildValidate()
        wWork = writer.LinkedWriter()
        startLength = 0  # we just created the writer, so its length is zero
        wWork.add("H", 2)
        byHighByte = self._checkForOneTwoConflicts()
        lengthStake = wWork.addDeferredValue("H")
        wWork.add("H", (self.language or 0))
        rangeOffsetPool = {}  # idRangeTuple -> sequence number
        subHeaders = []

        for highByte in range(256):
            if highByte not in byHighByte:
                wWork.add("H", 0)  # no subHeaderKey for this high byte
                continue

            wWork.add("H", 8 * len(subHeaders))

            subHeaders.append(
                self._makeSubHeader(byHighByte[highByte], rangeOffsetPool,
                                    highByte == 0))

        roStakePool = dict(
            (key, wWork.getNewStake()) for key in rangeOffsetPool)

        for firstCode, entryCount, idDelta, idRangeTuple in subHeaders:
            wWork.add("3H", firstCode, entryCount, idDelta)
            here = wWork.stakeCurrent()
            # Wow, this is SO much easier with LinkedWriters!!
            wWork.addUnresolvedOffset("H", here, roStakePool[idRangeTuple])

        for t in sorted((n, tp) for tp, n in rangeOffsetPool.items()):
            idRangeTuple = t[1]
            wWork.stakeCurrentWithValue(roStakePool[idRangeTuple])
            wWork.addGroup("H", idRangeTuple)

        # We pin the length field to FFFF in case it's larger.
        byteSize = min(0xFFFF, wWork.byteLength - startLength)
        wWork.setDeferredValue(lengthStake, "H", byteSize)

        # It may be possible to prove this exception cannot arise (because of
        # the self-local nature of idRangeOffsets); if that proves to be the
        # case, the following can be removed from the try block, and the wWork
        # hack can be removed and content just added to w.

        try:
            w.addString(wWork.binaryString())

        except ValueError:
            raise ValueError(
                "Internal format 2 offsets too large for 'H' format!")
Ejemplo n.º 24
0
 def _makeFormat1(sortedGlyphs):
     w = writer.LinkedWriter()
     w.add("HH", 1, len(sortedGlyphs))
     w.addGroup("H", sortedGlyphs)
     return w.binaryString()
Ejemplo n.º 25
0
    def buildBinary(self, w, **kwArgs):
        """
        Adds the binary data for the Sbit object to two walkers: the specified
        walker (for the dat output), and another walker (for the loc output).
        This other walker will be converted to a binary string and then passed
        into the locCallback keyword argument, a function.
        """

        wDat = writer.LinkedWriter()
        wDat.add("L", FLAVOR_VERSIONS.get(self.flavor, 0))
        wLoc = writer.LinkedWriter()
        stakeValue = wLoc.stakeCurrent()
        wLoc.add("2L", FLAVOR_VERSIONS.get(self.flavor, 0), len(self))
        istaStartStake = {}
        istaLengthStake = {}
        istaCountStake = {}

        for ppemKey in sorted(self):
            stk = self[ppemKey]
            istaStartStake[ppemKey] = wLoc.getNewStake()
            wLoc.addUnresolvedOffset("L", stakeValue, istaStartStake[ppemKey])
            istaLengthStake[ppemKey] = wLoc.addDeferredValue("L")
            istaCountStake[ppemKey] = wLoc.addDeferredValue("L")
            wLoc.add("L", stk.colorRef)
            stk.horiLineMetrics.buildBinary(wLoc, **kwArgs)
            stk.vertLineMetrics.buildBinary(wLoc, **kwArgs)
            actuals = {n for n in stk if stk[n] is not None}
            wLoc.add("2H", min(actuals), max(actuals))
            wLoc.addGroup("B", ppemKey)
            wLoc.add("B", stk.flags)

        a = analysis.Analysis.fromSbit(self, **kwArgs)
        doWalk = [4]  # base offset is past the 0x20000 version
        datPool = {}

        if self.flavor != 'CBDT':
            ss = self.findSizeSharers()
        else:
            ss = {}

        writtenOffsets = set()

        for ppemKey in sorted(self):
            stk = self[ppemKey]
            wLoc.stakeCurrentWithValue(istaStartStake[ppemKey])
            istaStartLength = wLoc.byteLength

            v = list(
                a.iterateSize(ppemKey,
                              datOffset=doWalk,
                              datPool=datPool,
                              sizeSharers=ss,
                              **kwArgs))

            dp = datPool[ppemKey]  # key will be there now
            wLoc.setDeferredValue(istaCountStake[ppemKey], "L", len(v))
            headerStakes = [wLoc.getNewStake() for t in v]

            for t, stake in zip(v, headerStakes):
                wLoc.add("2H", *t[0:2])  # firstGlyph and lastGlyph
                wLoc.addUnresolvedOffset("L", istaStartStake[ppemKey], stake)

            for t, stake in zip(v, headerStakes):
                wLoc.stakeCurrentWithValue(stake)
                firstGlyph, lastGlyph, indexFormat, datOffset = t
                assert datOffset == dp[firstGlyph]
                actuals = set(stk.actualIterator(firstGlyph, lastGlyph))
                g = stk[firstGlyph]
                wLoc.add("2HL", indexFormat, g.imageFormat, datOffset)
                assert indexFormat != 1

                if indexFormat == 2:
                    wLoc.add("L", g.binarySize())
                    g.metrics.buildBinary(wLoc)

                elif indexFormat == 3:
                    v = [None] * (lastGlyph - firstGlyph + 1)

                    for i in range(len(v)):
                        if i + firstGlyph in actuals:
                            v[i] = dp[i + firstGlyph] - datOffset
                        else:
                            v[i] = v[i - 1]

                    wLoc.addGroup("H", v)

                    wLoc.add(
                        "H", dp[lastGlyph] + stk[lastGlyph].binarySize() -
                        datOffset)

                    wLoc.alignToByteMultiple(4)

                elif indexFormat == 4:
                    wLoc.add("L", len(actuals))

                    for i in sorted(actuals):
                        wLoc.add("2H", i, dp[i] - datOffset)

                elif indexFormat == 5:
                    wLoc.add("L", g.binarySize())
                    g.metrics.buildBinary(wLoc)
                    wLoc.add("L", len(actuals))
                    wLoc.addGroup("H", sorted(actuals))
                    wLoc.alignToByteMultiple(4)

                for offset, i in sorted((dp[i], i) for i in actuals):
                    if offset not in writtenOffsets:
                        assert wDat.byteLength == offset
                        stk[i].buildBinary(wDat)
                        writtenOffsets.add(offset)

            wLoc.setDeferredValue(istaLengthStake[ppemKey], "L",
                                  wLoc.byteLength - istaStartLength)

        if 'locCallback' in kwArgs:
            kwArgs['locCallback'](wLoc.binaryString())

        w.addString(wDat.binaryString())