Exemplo n.º 1
0
def readTTCHeader(file):
	file.seek(0)
	data = file.read(ttcHeaderSize)
	if len(data) != ttcHeaderSize:
		raise TTLibError("Not a Font Collection (not enough data)")
	self = SimpleNamespace()
	sstruct.unpack(ttcHeaderFormat, data, self)
	if self.TTCTag != "ttcf":
		raise TTLibError("Not a Font Collection")
	assert self.Version == 0x00010000 or self.Version == 0x00020000, "unrecognized TTC version 0x%08x" % self.Version
	self.offsetTable = struct.unpack(">%dL" % self.numFonts, file.read(self.numFonts * 4))
	if self.Version == 0x00020000:
		pass # ignoring version 2.0 signatures
	return self
Exemplo n.º 2
0
	def __init__(self, file, checkChecksums=0, fontNumber=-1):
		self.file = file
		self.checkChecksums = checkChecksums

		self.flavor = None
		self.flavorData = None
		self.DirectoryEntry = SFNTDirectoryEntry
		self.file.seek(0)
		self.sfntVersion = self.file.read(4)
		self.file.seek(0)
		if self.sfntVersion == b"ttcf":
			header = readTTCHeader(self.file)
			numFonts = header.numFonts
			if not 0 <= fontNumber < numFonts:
				raise TTLibError("specify a font number between 0 and %d (inclusive)" % (numFonts - 1))
			self.numFonts = numFonts
			self.file.seek(header.offsetTable[fontNumber])
			data = self.file.read(sfntDirectorySize)
			if len(data) != sfntDirectorySize:
				raise TTLibError("Not a Font Collection (not enough data)")
			sstruct.unpack(sfntDirectoryFormat, data, self)
		elif self.sfntVersion == b"wOFF":
			self.flavor = "woff"
			self.DirectoryEntry = WOFFDirectoryEntry
			data = self.file.read(woffDirectorySize)
			if len(data) != woffDirectorySize:
				raise TTLibError("Not a WOFF font (not enough data)")
			sstruct.unpack(woffDirectoryFormat, data, self)
		else:
			data = self.file.read(sfntDirectorySize)
			if len(data) != sfntDirectorySize:
				raise TTLibError("Not a TrueType or OpenType font (not enough data)")
			sstruct.unpack(sfntDirectoryFormat, data, self)
		self.sfntVersion = Tag(self.sfntVersion)

		if self.sfntVersion not in ("\x00\x01\x00\x00", "OTTO", "true"):
			raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
		tables = {}
		for i in range(self.numTables):
			entry = self.DirectoryEntry()
			entry.fromFile(self.file)
			tag = Tag(entry.tag)
			tables[tag] = entry
		self.tables = OrderedDict(sorted(tables.items(), key=lambda i: i[1].offset))

		# Load flavor data if any
		if self.flavor == "woff":
			self.flavorData = WOFFFlavorData(self)
Exemplo n.º 3
0
    def getGlyphSet(self, preferCFF=True):
        """Return a generic GlyphSet, which is a dict-like object
		mapping glyph names to glyph objects. The returned glyph objects
		have a .draw() method that supports the Pen protocol, and will
		have an attribute named 'width'.

		If the font is CFF-based, the outlines will be taken from the 'CFF ' or
		'CFF2' tables. Otherwise the outlines will be taken from the 'glyf' table.
		If the font contains both a 'CFF '/'CFF2' and a 'glyf' table, you can use
		the 'preferCFF' argument to specify which one should be taken. If the
		font contains both a 'CFF ' and a 'CFF2' table, the latter is taken.
		"""
        glyphs = None
        if (preferCFF and any(tb in self for tb in ["CFF ", "CFF2"])
                or ("glyf" not in self and any(tb in self
                                               for tb in ["CFF ", "CFF2"]))):
            table_tag = "CFF2" if "CFF2" in self else "CFF "
            glyphs = _TTGlyphSet(
                self,
                list(self[table_tag].cff.values())[0].CharStrings, _TTGlyphCFF)

        if glyphs is None and "glyf" in self:
            glyphs = _TTGlyphSet(self, self["glyf"], _TTGlyphGlyf)

        if glyphs is None:
            raise TTLibError("Font contains no outlines")

        return glyphs
Exemplo n.º 4
0
	def __setitem__(self, tag, data):
		"""Write raw table data to disk."""
		if tag in self.tables:
			raise TTLibError("cannot rewrite '%s' table" % tag)

		entry = self.DirectoryEntry()
		entry.tag = tag
		entry.offset = self.nextTableOffset
		if tag == 'head':
			entry.checkSum = calcChecksum(data[:8] + b'\0\0\0\0' + data[12:])
			self.headTable = data
			entry.uncompressed = True
		else:
			entry.checkSum = calcChecksum(data)
		entry.saveData(self.file, data)

		if self.flavor == "woff":
			entry.origOffset = self.origNextTableOffset
			self.origNextTableOffset += (entry.origLength + 3) & ~3

		self.nextTableOffset = self.nextTableOffset + ((entry.length + 3) & ~3)
		# Add NUL bytes to pad the table data to a 4-byte boundary.
		# Don't depend on f.seek() as we need to add the padding even if no
		# subsequent write follows (seek is lazy), ie. after the final table
		# in the font.
		self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
		assert self.nextTableOffset == self.file.tell()

		self.setEntry(tag, entry)
 def fromXML(self, name, attrs, content, ttFont):
     if name == "hexdata":
         self.data[attrs["tag"]] = readHex(content)
     elif name == "text" and attrs["tag"] in ["dlng", "slng"]:
         self.data[attrs["tag"]] = strjoin(content).strip()
     else:
         raise TTLibError("can't handle '%s' element" % name)
Exemplo n.º 6
0
    def decompile(self, data, offset):
        # initial offset is from the start of trak table to the current TrackData
        trackDataHeader = data[offset:offset + TRACK_DATA_FORMAT_SIZE]
        if len(trackDataHeader) != TRACK_DATA_FORMAT_SIZE:
            raise TTLibError('not enough data to decompile TrackData header')
        sstruct.unpack(TRACK_DATA_FORMAT, trackDataHeader, self)
        offset += TRACK_DATA_FORMAT_SIZE

        nSizes = self.nSizes
        sizeTableOffset = self.sizeTableOffset
        sizeTable = []
        for i in range(nSizes):
            sizeValueData = data[sizeTableOffset:sizeTableOffset +
                                 SIZE_VALUE_FORMAT_SIZE]
            if len(sizeValueData) < SIZE_VALUE_FORMAT_SIZE:
                raise TTLibError(
                    'not enough data to decompile TrackData size subtable')
            sizeValue, = struct.unpack(SIZE_VALUE_FORMAT, sizeValueData)
            sizeTable.append(fi2fl(sizeValue, 16))
            sizeTableOffset += SIZE_VALUE_FORMAT_SIZE

        for i in range(self.nTracks):
            entry = TrackTableEntry()
            entryData = data[offset:offset + TRACK_TABLE_ENTRY_FORMAT_SIZE]
            if len(entryData) < TRACK_TABLE_ENTRY_FORMAT_SIZE:
                raise TTLibError(
                    'not enough data to decompile TrackTableEntry record')
            sstruct.unpack(TRACK_TABLE_ENTRY_FORMAT, entryData, entry)
            perSizeOffset = entry.offset
            for j in range(nSizes):
                size = sizeTable[j]
                perSizeValueData = data[perSizeOffset:perSizeOffset +
                                        PER_SIZE_VALUE_FORMAT_SIZE]
                if len(perSizeValueData) < PER_SIZE_VALUE_FORMAT_SIZE:
                    raise TTLibError(
                        'not enough data to decompile per-size track values')
                perSizeValue, = struct.unpack(PER_SIZE_VALUE_FORMAT,
                                              perSizeValueData)
                entry[size] = perSizeValue
                perSizeOffset += PER_SIZE_VALUE_FORMAT_SIZE
            self[entry.track] = entry
            offset += TRACK_TABLE_ENTRY_FORMAT_SIZE
Exemplo n.º 7
0
 def sizes(self):
     if not self:
         return frozenset()
     tracks = list(self.tracks())
     sizes = self[tracks.pop(0)].sizes()
     for track in tracks:
         entrySizes = self[track].sizes()
         if sizes != entrySizes:
             raise TTLibError(
                 "'trak' table entries must specify the same sizes: "
                 "%s != %s" % (sorted(sizes), sorted(entrySizes)))
     return frozenset(sizes)
 def decompile(self, data, ttFont):
     axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
     header = {}
     headerSize = sstruct.calcsize(AVAR_HEADER_FORMAT)
     header = sstruct.unpack(AVAR_HEADER_FORMAT, data[0:headerSize])
     majorVersion = header["majorVersion"]
     if majorVersion != 1:
         raise TTLibError("unsupported 'avar' version %d" % majorVersion)
     pos = headerSize
     for axis in axisTags:
         segments = self.segments[axis] = {}
         numPairs = struct.unpack(">H", data[pos:pos+2])[0]
         pos = pos + 2
         for _ in range(numPairs):
             fromValue, toValue = struct.unpack(">hh", data[pos:pos+4])
             segments[fi2fl(fromValue, 14)] = fi2fl(toValue, 14)
             pos = pos + 4
 def decompile(self, data, ttFont):
     headerSize = sstruct.calcsize(META_HEADER_FORMAT)
     header = sstruct.unpack(META_HEADER_FORMAT, data[0 : headerSize])
     if header["version"] != 1:
         raise TTLibError("unsupported 'meta' version %d" %
                          header["version"])
     dataMapSize = sstruct.calcsize(DATA_MAP_FORMAT)
     for i in range(header["numDataMaps"]):
         dataMapOffset = headerSize + i * dataMapSize
         dataMap = sstruct.unpack(
             DATA_MAP_FORMAT,
             data[dataMapOffset : dataMapOffset + dataMapSize])
         tag = dataMap["tag"]
         offset = dataMap["dataOffset"]
         self.data[tag] = data[offset : offset + dataMap["dataLength"]]
         if tag in ["dlng", "slng"]:
             self.data[tag] = self.data[tag].decode("utf-8")
Exemplo n.º 10
0
    def decompile(self, data, ttFont):
        if not self.apple:
            version, length, subtableFormat, coverage = struct.unpack(
                ">HHBB", data[:6])
            if version != 0:
                from fontemon_blender_addon.fontTools.ttLib import TTLibError
                raise TTLibError("unsupported kern subtable version: %d" %
                                 version)
            tupleIndex = None
            # Should we also assert length == len(data)?
            data = data[6:]
        else:
            length, coverage, subtableFormat, tupleIndex = struct.unpack(
                ">LBBH", data[:8])
            data = data[8:]
        assert self.format == subtableFormat, "unsupported format"
        self.coverage = coverage
        self.tupleIndex = tupleIndex

        self.kernTable = kernTable = {}

        nPairs, searchRange, entrySelector, rangeShift = struct.unpack(
            ">HHHH", data[:8])
        data = data[8:]

        datas = array.array("H", data[:6 * nPairs])
        if sys.byteorder != "big": datas.byteswap()
        it = iter(datas)
        glyphOrder = ttFont.getGlyphOrder()
        for k in range(nPairs):
            left, right, value = next(it), next(it), next(it)
            if value >= 32768:
                value -= 65536
            try:
                kernTable[(glyphOrder[left], glyphOrder[right])] = value
            except IndexError:
                # Slower, but will not throw an IndexError on an invalid
                # glyph id.
                kernTable[(ttFont.getGlyphName(left),
                           ttFont.getGlyphName(right))] = value
        if len(data) > 6 * nPairs + 4:  # Ignore up to 4 bytes excess
            log.warning("excess data in 'kern' subtable: %d bytes",
                        len(data) - 6 * nPairs)
Exemplo n.º 11
0
 def decompile(self, data, ttFont):
     header = {}
     headerSize = sstruct.calcsize(FVAR_HEADER_FORMAT)
     header = sstruct.unpack(FVAR_HEADER_FORMAT, data[0:headerSize])
     if header["version"] != 0x00010000:
         raise TTLibError("unsupported 'fvar' version %04x" %
                          header["version"])
     pos = header["offsetToData"]
     axisSize = header["axisSize"]
     for _ in range(header["axisCount"]):
         axis = Axis()
         axis.decompile(data[pos:pos + axisSize])
         self.axes.append(axis)
         pos += axisSize
     instanceSize = header["instanceSize"]
     axisTags = [axis.axisTag for axis in self.axes]
     for _ in range(header["instanceCount"]):
         instance = NamedInstance()
         instance.decompile(data[pos:pos + instanceSize], axisTags)
         self.instances.append(instance)
         pos += instanceSize
Exemplo n.º 12
0
    def save(self, file, reorderTables=True):
        """Save the font to disk. Similarly to the constructor,
		the 'file' argument can be either a pathname or a writable
		file object.
		"""
        if not hasattr(file, "write"):
            if self.lazy and self.reader.file.name == file:
                raise TTLibError(
                    "Can't overwrite TTFont when 'lazy' attribute is True")
            closeStream = True
            file = open(file, "wb")
        else:
            # assume "file" is a writable file object
            closeStream = False

        tmp = BytesIO()

        writer_reordersTables = self._save(tmp)

        if (reorderTables is None or writer_reordersTables
                or (reorderTables is False and self.reader is None)):
            # don't reorder tables and save as is
            file.write(tmp.getvalue())
            tmp.close()
        else:
            if reorderTables is False:
                # sort tables using the original font's order
                tableOrder = list(self.reader.keys())
            else:
                # use the recommended order from the OpenType specification
                tableOrder = None
            tmp.flush()
            tmp2 = BytesIO()
            reorderFontTables(tmp, tmp2, tableOrder)
            file.write(tmp2.getvalue())
            tmp.close()
            tmp2.close()

        if closeStream:
            file.close()
Exemplo n.º 13
0
	def close(self):
		"""All tables must have been written to disk. Now write the
		directory.
		"""
		tables = sorted(self.tables.items())
		if len(tables) != self.numTables:
			raise TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(tables)))

		if self.flavor == "woff":
			self.signature = b"wOFF"
			self.reserved = 0

			self.totalSfntSize = 12
			self.totalSfntSize += 16 * len(tables)
			for tag, entry in tables:
				self.totalSfntSize += (entry.origLength + 3) & ~3

			data = self.flavorData if self.flavorData else WOFFFlavorData()
			if data.majorVersion is not None and data.minorVersion is not None:
				self.majorVersion = data.majorVersion
				self.minorVersion = data.minorVersion
			else:
				if hasattr(self, 'headTable'):
					self.majorVersion, self.minorVersion = struct.unpack(">HH", self.headTable[4:8])
				else:
					self.majorVersion = self.minorVersion = 0
			if data.metaData:
				self.metaOrigLength = len(data.metaData)
				self.file.seek(0,2)
				self.metaOffset = self.file.tell()
				compressedMetaData = compress(data.metaData)
				self.metaLength = len(compressedMetaData)
				self.file.write(compressedMetaData)
			else:
				self.metaOffset = self.metaLength = self.metaOrigLength = 0
			if data.privData:
				self.file.seek(0,2)
				off = self.file.tell()
				paddedOff = (off + 3) & ~3
				self.file.write('\0' * (paddedOff - off))
				self.privOffset = self.file.tell()
				self.privLength = len(data.privData)
				self.file.write(data.privData)
			else:
				self.privOffset = self.privLength = 0

			self.file.seek(0,2)
			self.length = self.file.tell()

		else:
			assert not self.flavor, "Unknown flavor '%s'" % self.flavor
			pass

		directory = sstruct.pack(self.directoryFormat, self)

		self.file.seek(self.directoryOffset + self.directorySize)
		seenHead = 0
		for tag, entry in tables:
			if tag == "head":
				seenHead = 1
			directory = directory + entry.toString()
		if seenHead:
			self.writeMasterChecksum(directory)
		self.file.seek(self.directoryOffset)
		self.file.write(directory)
Exemplo n.º 14
0
	def setEntry(self, tag, entry):
		if tag in self.tables:
			raise TTLibError("cannot rewrite '%s' table" % tag)

		self.tables[tag] = entry