def encodeFloat(f):
    # For CFF only, used in cffLib
    if f == 0.0:  # 0.0 == +0.0 == -0.0
        return realZeroBytes
    # Note: 14 decimal digits seems to be the limitation for CFF real numbers
    # in macOS. However, we use 8 here to match the implementation of AFDKO.
    s = "%.8G" % f
    if s[:2] == "0.":
        s = s[1:]
    elif s[:3] == "-0.":
        s = "-" + s[2:]
    nibbles = []
    while s:
        c = s[0]
        s = s[1:]
        if c == "E":
            c2 = s[:1]
            if c2 == "-":
                s = s[1:]
                c = "E-"
            elif c2 == "+":
                s = s[1:]
        nibbles.append(realNibblesDict[c])
    nibbles.append(0xf)
    if len(nibbles) % 2:
        nibbles.append(0xf)
    d = bytechr(30)
    for i in range(0, len(nibbles), 2):
        d = d + bytechr(nibbles[i] << 4 | nibbles[i + 1])
    return d
示例#2
0
def writePFB(path, data):
    chunks = findEncryptedChunks(data)
    with open(path, "wb") as f:
        for isEncrypted, chunk in chunks:
            if isEncrypted:
                code = 2
            else:
                code = 1
            f.write(bytechr(128) + bytechr(code))
            f.write(longToString(len(chunk)))
            f.write(chunk)
        f.write(bytechr(128) + bytechr(3))
 def compile(self, ttFont):
     d = self.__dict__.copy()
     d["nameLength"] = bytechr(len(self.baseGlyphName))
     d["uniqueName"] = self.compilecompileUniqueName(self.uniqueName, 28)
     METAMD5List = eval(self.METAMD5)
     d["METAMD5"] = b""
     for val in METAMD5List:
         d["METAMD5"] += bytechr(val)
     assert (len(d["METAMD5"]) == 16
             ), "Failed to pack 16 byte MD5 hash in SING table"
     data = sstruct.pack(SINGFormat, d)
     data = data + tobytes(self.baseGlyphName)
     return data
	def compile(self, ttFont):
		self.version = 0
		numGlyphs = ttFont['maxp'].numGlyphs
		glyphOrder = ttFont.getGlyphOrder()
		self.recordSize = 4 * ((2 + numGlyphs + 3) // 4)
		pad = (self.recordSize - 2 - numGlyphs) * b"\0"
		self.numRecords = len(self.hdmx)
		data = sstruct.pack(hdmxHeaderFormat, self)
		items = sorted(self.hdmx.items())
		for ppem, widths in items:
			data = data + bytechr(ppem) + bytechr(max(widths.values()))
			for glyphID in range(len(glyphOrder)):
				width = widths[glyphOrder[glyphID]]
				data = data + bytechr(width)
			data = data + pad
		return data
 def fromXML(self, name, attrs, content):
     from fontTools.misc.textTools import binary2num, readHex
     if attrs.get("raw"):
         self.setBytecode(readHex(content))
         return
     content = strjoin(content)
     content = content.split()
     program = []
     end = len(content)
     i = 0
     while i < end:
         token = content[i]
         i = i + 1
         try:
             token = int(token)
         except ValueError:
             try:
                 token = strToFixedToFloat(token, precisionBits=16)
             except ValueError:
                 program.append(token)
                 if token in ('hintmask', 'cntrmask'):
                     mask = content[i]
                     maskBytes = b""
                     for j in range(0, len(mask), 8):
                         maskBytes = maskBytes + bytechr(
                             binary2num(mask[j:j + 8]))
                     program.append(maskBytes)
                     i = i + 1
             else:
                 program.append(token)
         else:
             program.append(token)
     self.setProgram(program)
示例#6
0
    def toUnicode(self, errors='strict'):
        """
		If self.string is a Unicode string, return it; otherwise try decoding the
		bytes in self.string to a Unicode string using the encoding of this
		entry as returned by self.getEncoding(); Note that  self.getEncoding()
		returns 'ascii' if the encoding is unknown to the library.

		Certain heuristics are performed to recover data from bytes that are
		ill-formed in the chosen encoding, or that otherwise look misencoded
		(mostly around bad UTF-16BE encoded bytes, or bytes that look like UTF-16BE
		but marked otherwise).  If the bytes are ill-formed and the heuristics fail,
		the error is handled according to the errors parameter to this function, which is
		passed to the underlying decode() function; by default it throws a
		UnicodeDecodeError exception.

		Note: The mentioned heuristics mean that roundtripping a font to XML and back
		to binary might recover some misencoded data whereas just loading the font
		and saving it back will not change them.
		"""
        def isascii(b):
            return (b >= 0x20 and b <= 0x7E) or b in [0x09, 0x0A, 0x0D]

        encoding = self.getEncoding()
        string = self.string

        if isinstance(
                string,
                bytes) and encoding == 'utf_16_be' and len(string) % 2 == 1:
            # Recover badly encoded UTF-16 strings that have an odd number of bytes:
            # - If the last byte is zero, drop it.  Otherwise,
            # - If all the odd bytes are zero and all the even bytes are ASCII,
            #   prepend one zero byte.  Otherwise,
            # - If first byte is zero and all other bytes are ASCII, insert zero
            #   bytes between consecutive ASCII bytes.
            #
            # (Yes, I've seen all of these in the wild... sigh)
            if byteord(string[-1]) == 0:
                string = string[:-1]
            elif all(
                    byteord(b) == 0 if i % 2 else isascii(byteord(b))
                    for i, b in enumerate(string)):
                string = b'\0' + string
            elif byteord(string[0]) == 0 and all(
                    isascii(byteord(b)) for b in string[1:]):
                string = bytesjoin(b'\0' + bytechr(byteord(b))
                                   for b in string[1:])

        string = tostr(string, encoding=encoding, errors=errors)

        # If decoded strings still looks like UTF-16BE, it suggests a double-encoding.
        # Fix it up.
        if all(
                ord(c) == 0 if i % 2 == 0 else isascii(ord(c))
                for i, c in enumerate(string)):
            # If string claims to be Mac encoding, but looks like UTF-16BE with ASCII text,
            # narrow it down.
            string = ''.join(c for c in string[1::2])

        return string
	def toString(self):
		data = bytechr(self.flags)
		if (self.flags & 0x3F) == 0x3F:
			data += struct.pack('>4s', self.tag.tobytes())
		data += packBase128(self.origLength)
		if self.transformed:
			data += packBase128(self.length)
		return data
 def encodeInt(value,
               fourByteOp=fourByteOp,
               bytechr=bytechr,
               pack=struct.pack,
               unpack=struct.unpack):
     if -107 <= value <= 107:
         code = bytechr(value + 139)
     elif 108 <= value <= 1131:
         value = value - 108
         code = bytechr((value >> 8) + 247) + bytechr(value & 0xFF)
     elif -1131 <= value <= -108:
         value = -value - 108
         code = bytechr((value >> 8) + 251) + bytechr(value & 0xFF)
     elif fourByteOp is None:
         # T2 only supports 2 byte ints
         if -32768 <= value <= 32767:
             code = bytechr(28) + pack(">h", value)
         else:
             # Backwards compatible hack: due to a previous bug in FontTools,
             # 16.16 fixed numbers were written out as 4-byte ints. When
             # these numbers were small, they were wrongly written back as
             # small ints instead of 4-byte ints, breaking round-tripping.
             # This here workaround doesn't do it any better, since we can't
             # distinguish anymore between small ints that were supposed to
             # be small fixed numbers and small ints that were just small
             # ints. Hence the warning.
             log.warning(
                 "4-byte T2 number got passed to the "
                 "IntType handler. This should happen only when reading in "
                 "old XML files.\n")
             code = bytechr(255) + pack(">l", value)
     else:
         code = fourByteOp + pack(">l", value)
     return code
示例#9
0
 def test_unsupportedLookupType(self):
     data = bytesjoin([
         MORX_NONCONTEXTUAL_DATA[:67],
         bytechr(66), MORX_NONCONTEXTUAL_DATA[69:]
     ])
     with self.assertRaisesRegex(AssertionError,
                                 r"unsupported 'morx' lookup type 66"):
         morx = newTable('morx')
         morx.decompile(data, FakeFont(['.notdef']))
示例#10
0
def _reverseBytes(data):
    if len(data) != 1:
        return bytesjoin(map(_reverseBytes, data))
    byte = byteord(data)
    result = 0
    for i in range(8):
        result = result << 1
        result |= byte & 1
        byte = byte >> 1
    return bytechr(result)
示例#11
0
    def getnexttoken(
            self,
            # localize some stuff, for performance
            len=len,
            ps_special=ps_special,
            stringmatch=stringRE.match,
            hexstringmatch=hexstringRE.match,
            commentmatch=commentRE.match,
            endmatch=endofthingRE.match):

        self.skipwhite()
        if self.pos >= self.len:
            return None, None
        pos = self.pos
        buf = self.buf
        char = bytechr(byteord(buf[pos]))
        if char in ps_special:
            if char in b'{}[]':
                tokentype = 'do_special'
                token = char
            elif char == b'%':
                tokentype = 'do_comment'
                _, nextpos = commentmatch(buf, pos).span()
                token = buf[pos:nextpos]
            elif char == b'(':
                tokentype = 'do_string'
                m = stringmatch(buf, pos)
                if m is None:
                    raise PSTokenError('bad string at character %d' % pos)
                _, nextpos = m.span()
                token = buf[pos:nextpos]
            elif char == b'<':
                tokentype = 'do_hexstring'
                m = hexstringmatch(buf, pos)
                if m is None:
                    raise PSTokenError('bad hexstring at character %d' % pos)
                _, nextpos = m.span()
                token = buf[pos:nextpos]
            else:
                raise PSTokenError('bad token at character %d' % pos)
        else:
            if char == b'/':
                tokentype = 'do_literal'
                m = endmatch(buf, pos + 1)
            else:
                tokentype = ''
                m = endmatch(buf, pos)
            if m is None:
                raise PSTokenError('bad token at character %d' % pos)
            _, nextpos = m.span()
            token = buf[pos:nextpos]
        self.pos = pos + len(token)
        token = tostr(token, encoding=self.encoding)
        return tokentype, token
示例#12
0
def _binary2data(binary):
    byteList = []
    for bitLoc in range(0, len(binary), 8):
        byteString = binary[bitLoc:bitLoc + 8]
        curByte = 0
        for curBit in reversed(byteString):
            curByte = curByte << 1
            if curBit == '1':
                curByte |= 1
        byteList.append(bytechr(curByte))
    return bytesjoin(byteList)
示例#13
0
 def test_ReservedCoverageFlags(self):
     # 8A BC DE = TextDirection=Vertical, Reserved=0xABCDE
     # Note that the lower 4 bits of the first byte are already
     # part of the Reserved value. We test the full round-trip
     # to encoding and decoding is quite hairy.
     data = bytesjoin([
         MORX_REARRANGEMENT_DATA[:28],
         bytechr(0x8A),
         bytechr(0xBC),
         bytechr(0xDE), MORX_REARRANGEMENT_DATA[31:]
     ])
     table = newTable('morx')
     table.decompile(data, self.font)
     subtable = table.table.MorphChain[0].MorphSubtable[0]
     self.assertEqual(subtable.Reserved, 0xABCDE)
     xml = getXML(table.toXML)
     self.assertIn('    <Reserved value="0xabcde"/>', xml)
     table2 = newTable('morx')
     for name, attrs, content in parseXML(xml):
         table2.fromXML(name, attrs, content, font=self.font)
     self.assertEqual(hexStr(table2.compile(self.font)[28:31]), "8abcde")
示例#14
0
def writeLWFN(path, data):
    # Res.FSpCreateResFile was deprecated in OS X 10.5
    Res.FSpCreateResFile(path, "just", "LWFN", 0)
    resRef = Res.FSOpenResFile(path, 2)  # write-only
    try:
        Res.UseResFile(resRef)
        resID = 501
        chunks = findEncryptedChunks(data)
        for isEncrypted, chunk in chunks:
            if isEncrypted:
                code = 2
            else:
                code = 1
            while chunk:
                res = Res.Resource(
                    bytechr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
                res.AddResource('POST', resID, '')
                chunk = chunk[LWFNCHUNKSIZE - 2:]
                resID = resID + 1
        res = Res.Resource(bytechr(5) + '\0')
        res.AddResource('POST', resID, '')
    finally:
        Res.CloseResFile(resRef)
示例#15
0
    def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
        if metrics is None:
            metrics = self.metrics
        assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"

        # Loop through each byte. This can cover two bytes in the original data or
        # a single byte if things happen to be aligned. The very last entry might
        # not be aligned so take care to trim the binary data to size and pad with
        # zeros in the row data. Bit aligned data is somewhat tricky.
        #
        # Example of data cut. Data cut represented in x's.
        # '|' represents byte boundary.
        # data = ...0XX|XXXXXX00|000... => XXXXXXXX
        #		or
        # data = ...0XX|XXXX0000|000... => XXXXXX00
        #   or
        # data = ...000|XXXXXXXX|000... => XXXXXXXX
        #   or
        # data = ...000|00XXXX00|000... => XXXX0000
        #
        dataList = []
        bitRange = self._getBitRange(row, bitDepth, metrics)
        stepRange = bitRange + (8, )
        for curBit in range(*stepRange):
            endBit = min(curBit + 8, bitRange[1])
            numBits = endBit - curBit
            cutPoint = curBit % 8
            firstByteLoc = curBit // 8
            secondByteLoc = endBit // 8
            if firstByteLoc < secondByteLoc:
                numBitsCut = 8 - cutPoint
            else:
                numBitsCut = endBit - curBit
            curByte = _reverseBytes(self.imageData[firstByteLoc])
            firstHalf = byteord(curByte) >> cutPoint
            firstHalf = ((1 << numBitsCut) - 1) & firstHalf
            newByte = firstHalf
            if firstByteLoc < secondByteLoc and secondByteLoc < len(
                    self.imageData):
                curByte = _reverseBytes(self.imageData[secondByteLoc])
                secondHalf = byteord(curByte) << numBitsCut
                newByte = (firstHalf | secondHalf) & ((1 << numBits) - 1)
            dataList.append(bytechr(newByte))

        # The way the data is kept is opposite the algorithm used.
        data = bytesjoin(dataList)
        if not reverseBytes:
            data = _reverseBytes(data)
        return data
    def compile(self, isCFF2=False):
        if self.bytecode is not None:
            return
        opcodes = self.opcodes
        program = self.program

        if isCFF2:
            # If present, remove return and endchar operators.
            if program and program[-1] in ("return", "endchar"):
                program = program[:-1]
        elif program and not isinstance(program[-1], str):
            raise CharStringCompileError(
                "T2CharString or Subr has items on the stack after last operator."
            )

        bytecode = []
        encodeInt = self.getIntEncoder()
        encodeFixed = self.getFixedEncoder()
        i = 0
        end = len(program)
        while i < end:
            token = program[i]
            i = i + 1
            if isinstance(token, str):
                try:
                    bytecode.extend(bytechr(b) for b in opcodes[token])
                except KeyError:
                    raise CharStringCompileError("illegal operator: %s" %
                                                 token)
                if token in ('hintmask', 'cntrmask'):
                    bytecode.append(program[i])  # hint mask
                    i = i + 1
            elif isinstance(token, int):
                bytecode.append(encodeInt(token))
            elif isinstance(token, float):
                bytecode.append(encodeFixed(token))
            else:
                assert 0, "unsupported type: %s" % type(token)
        try:
            bytecode = bytesjoin(bytecode)
        except TypeError:
            log.error(bytecode)
            raise
        self.setBytecode(bytecode)
示例#17
0
def readPFB(path, onlyHeader=False):
    """reads a PFB font file, returns raw data"""
    data = []
    with open(path, "rb") as f:
        while True:
            if f.read(1) != bytechr(128):
                raise T1Error('corrupt PFB file')
            code = byteord(f.read(1))
            if code in [1, 2]:
                chunklen = stringToLong(f.read(4))
                chunk = f.read(chunklen)
                assert len(chunk) == chunklen
                data.append(chunk)
            elif code == 3:
                break
            else:
                raise T1Error('bad chunk code: ' + repr(code))
            if onlyHeader:
                break
    data = bytesjoin(data)
    assertType1(data)
    return data
示例#18
0
 def checkFlags(self,
                flags,
                textDirection,
                processingOrder,
                checkCompile=True):
     data = bytesjoin([
         MORX_REARRANGEMENT_DATA[:28],
         bytechr(flags << 4), MORX_REARRANGEMENT_DATA[29:]
     ])
     xml = []
     for line in MORX_REARRANGEMENT_XML:
         if line.startswith('    <TextDirection '):
             line = '    <TextDirection value="%s"/>' % textDirection
         elif line.startswith('    <ProcessingOrder '):
             line = '    <ProcessingOrder value="%s"/>' % processingOrder
         xml.append(line)
     table1 = newTable('morx')
     table1.decompile(data, self.font)
     self.assertEqual(getXML(table1.toXML), xml)
     if checkCompile:
         table2 = newTable('morx')
         for name, attrs, content in parseXML(xml):
             table2.fromXML(name, attrs, content, font=self.font)
         self.assertEqual(hexStr(table2.compile(self.font)), hexStr(data))
示例#19
0
def _encryptChar(plain, R):
    plain = byteord(plain)
    cipher = ((plain ^ (R >> 8))) & 0xFF
    R = ((cipher + R) * 52845 + 22719) & 0xFFFF
    return bytechr(cipher), R
encodeIntCFF = getIntEncoder("cff")
encodeIntT1 = getIntEncoder("t1")
encodeIntT2 = getIntEncoder("t2")


def encodeFixed(f, pack=struct.pack):
    """For T2 only"""
    value = floatToFixed(f, precisionBits=16)
    if value & 0xFFFF == 0:  # check if the fractional part is zero
        return encodeIntT2(value >> 16)  # encode only the integer part
    else:
        return b"\xff" + pack(">l",
                              value)  # encode the entire fixed point value


realZeroBytes = bytechr(30) + bytechr(0xf)


def encodeFloat(f):
    # For CFF only, used in cffLib
    if f == 0.0:  # 0.0 == +0.0 == -0.0
        return realZeroBytes
    # Note: 14 decimal digits seems to be the limitation for CFF real numbers
    # in macOS. However, we use 8 here to match the implementation of AFDKO.
    s = "%.8G" % f
    if s[:2] == "0.":
        s = s[1:]
    elif s[:3] == "-0.":
        s = "-" + s[2:]
    nibbles = []
    while s:
示例#21
0
def packPStrings(strings):
    data = b""
    for s in strings:
        data = data + bytechr(len(s)) + tobytes(s, encoding="latin1")
    return data
示例#22
0
def longToString(long):
    s = b""
    for i in range(4):
        s += bytechr((long & (0xff << (i * 8))) >> i * 8)
    return s
示例#23
0
def _decryptChar(cipher, R):
    cipher = byteord(cipher)
    plain = ((cipher ^ (R >> 8))) & 0xFF
    R = ((cipher + R) * 52845 + 22719) & 0xFFFF
    return bytechr(plain), R