示例#1
0
    def _writeTable(self, tag, writer, done, tableCache=None):
        """Internal helper function for self.save(). Keeps track of
		inter-table dependencies.
		"""
        if tag in done:
            return
        tableClass = getTableClass(tag)
        for masterTable in tableClass.dependencies:
            if masterTable not in done:
                if masterTable in self:
                    self._writeTable(masterTable, writer, done, tableCache)
                else:
                    done.append(masterTable)
        done.append(tag)
        tabledata = self.getTableData(tag)
        if tableCache is not None:
            entry = tableCache.get((Tag(tag), tabledata))
            if entry is not None:
                log.debug("reusing '%s' table", tag)
                writer.setEntry(tag, entry)
                return
        log.debug("Writing '%s' table to disk", tag)
        writer[tag] = tabledata
        if tableCache is not None:
            tableCache[(Tag(tag), tabledata)] = writer[tag]
	def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
		         flavor=None, flavorData=None):
		if not haveBrotli:
			log.error(
				'The WOFF2 encoder requires the Brotli Python extension, available at: '
				'https://github.com/google/brotli')
			raise ImportError("No module named brotli")

		self.file = file
		self.numTables = numTables
		self.sfntVersion = Tag(sfntVersion)
		self.flavorData = WOFF2FlavorData(data=flavorData)

		self.directoryFormat = woff2DirectoryFormat
		self.directorySize = woff2DirectorySize
		self.DirectoryEntry = WOFF2DirectoryEntry

		self.signature = Tag("wOF2")

		self.nextTableOffset = 0
		self.transformBuffer = BytesIO()

		self.tables = OrderedDict()

		# make empty TTFont to store data while normalising and transforming tables
		self.ttFont = TTFont(recalcBBoxes=False, recalcTimestamp=False)
示例#3
0
def xmlToTag(tag):
    """The opposite of tagToXML()"""
    if tag == "OS_2":
        return Tag("OS/2")
    if len(tag) == 8:
        return identifierToTag(tag)
    else:
        return Tag(tag + " " * (4 - len(tag)))
示例#4
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)
示例#5
0
def tagToXML(tag):
    """Similarly to tagToIdentifier(), this converts a TT tag
	to a valid XML element name. Since XML element names are
	case sensitive, this is a fairly simple/readable translation.
	"""
    import re
    tag = Tag(tag)
    if tag == "OS/2":
        return "OS_2"
    elif tag == "GlyphOrder":
        return tag
    if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
        return tag.strip()
    else:
        return tagToIdentifier(tag)
示例#6
0
def guessFileType(fileName):
	base, ext = os.path.splitext(fileName)
	try:
		with open(fileName, "rb") as f:
			header = f.read(256)
	except IOError:
		return None

	if header.startswith(b'\xef\xbb\xbf<?xml'):
		header = header.lstrip(b'\xef\xbb\xbf')
	cr, tp = getMacCreatorAndType(fileName)
	if tp in ("sfnt", "FFIL"):
		return "TTF"
	if ext == ".dfont":
		return "TTF"
	head = Tag(header[:4])
	if head == "OTTO":
		return "OTF"
	elif head == "ttcf":
		return "TTC"
	elif head in ("\0\1\0\0", "true"):
		return "TTF"
	elif head == "wOFF":
		return "WOFF"
	elif head == "wOF2":
		return "WOFF2"
	elif head == "<?xm":
		# Use 'latin1' because that can't fail.
		header = tostr(header, 'latin1')
		if opentypeheaderRE.search(header):
			return "OTX"
		else:
			return "TTX"
	return None
示例#7
0
def tagToIdentifier(tag):
    """Convert a table tag to a valid (but UGLY) python identifier,
	as well as a filename that's guaranteed to be unique even on a
	caseless file system. Each character is mapped to two characters.
	Lowercase letters get an underscore before the letter, uppercase
	letters get an underscore after the letter. Trailing spaces are
	trimmed. Illegal characters are escaped as two hex bytes. If the
	result starts with a number (as the result of a hex escape), an
	extra underscore is prepended. Examples::

		>>> tagToIdentifier('glyf')
		'_g_l_y_f'
		>>> tagToIdentifier('cvt ')
		'_c_v_t'
		>>> tagToIdentifier('OS/2')
		'O_S_2f_2'
	"""
    import re
    tag = Tag(tag)
    if tag == "GlyphOrder":
        return tag
    assert len(tag) == 4, "tag should be 4 characters long"
    while len(tag) > 1 and tag[-1] == ' ':
        tag = tag[:-1]
    ident = ""
    for c in tag:
        ident = ident + _escapechar(c)
    if re.match("[0-9]", ident):
        ident = "_" + ident
    return ident
示例#8
0
	def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
			flavor=None, flavorData=None):
		self.file = file
		self.numTables = numTables
		self.sfntVersion = Tag(sfntVersion)
		self.flavor = flavor
		self.flavorData = flavorData

		if self.flavor == "woff":
			self.directoryFormat = woffDirectoryFormat
			self.directorySize = woffDirectorySize
			self.DirectoryEntry = WOFFDirectoryEntry

			self.signature = "wOFF"

			# to calculate WOFF checksum adjustment, we also need the original SFNT offsets
			self.origNextTableOffset = sfntDirectorySize + numTables * sfntDirectoryEntrySize
		else:
			assert not self.flavor, "Unknown flavor '%s'" % self.flavor
			self.directoryFormat = sfntDirectoryFormat
			self.directorySize = sfntDirectorySize
			self.DirectoryEntry = SFNTDirectoryEntry

			from fontTools.ttLib import getSearchRange
			self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(numTables, 16)

		self.directoryOffset = self.file.tell()
		self.nextTableOffset = self.directoryOffset + self.directorySize + numTables * self.DirectoryEntry.formatSize
		# clear out directory area
		self.file.seek(self.nextTableOffset)
		# make sure we're actually where we want to be. (old cStringIO bug)
		self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
		self.tables = OrderedDict()
示例#9
0
 def readTag(self):
     pos = self.pos
     newpos = pos + 4
     value = Tag(self.data[pos:newpos])
     assert len(value) == 4, value
     self.pos = newpos
     return value
示例#10
0
	def __init__(self, file, checkChecksums=0, fontNumber=-1):
		if not haveBrotli:
			log.error(
				'The WOFF2 decoder requires the Brotli Python extension, available at: '
				'https://github.com/google/brotli')
			raise ImportError("No module named brotli")

		self.file = file

		signature = Tag(self.file.read(4))
		if signature != b"wOF2":
			raise TTLibError("Not a WOFF2 font (bad signature)")

		self.file.seek(0)
		self.DirectoryEntry = WOFF2DirectoryEntry
		data = self.file.read(woff2DirectorySize)
		if len(data) != woff2DirectorySize:
			raise TTLibError('Not a WOFF2 font (not enough data)')
		sstruct.unpack(woff2DirectoryFormat, data, self)

		self.tables = OrderedDict()
		offset = 0
		for i in range(self.numTables):
			entry = self.DirectoryEntry()
			entry.fromFile(self.file)
			tag = Tag(entry.tag)
			self.tables[tag] = entry
			entry.offset = offset
			offset += entry.length

		totalUncompressedSize = offset
		compressedData = self.file.read(self.totalCompressedSize)
		decompressedData = brotli.decompress(compressedData)
		if len(decompressedData) != totalUncompressedSize:
			raise TTLibError(
				'unexpected size for decompressed font data: expected %d, found %d'
				% (totalUncompressedSize, len(decompressedData)))
		self.transformBuffer = BytesIO(decompressedData)

		self.file.seek(0, 2)
		if self.length != self.file.tell():
			raise TTLibError("reported 'length' doesn't match the actual file size")

		self.flavorData = WOFF2FlavorData(self)

		# make empty TTFont to store data while reconstructing tables
		self.ttFont = TTFont(recalcBBoxes=False, recalcTimestamp=False)
示例#11
0
	def __getitem__(self, tag):
		"""Fetch the raw table data. Reconstruct transformed tables."""
		entry = self.tables[Tag(tag)]
		if not hasattr(entry, 'data'):
			if entry.transformed:
				entry.data = self.reconstructTable(tag)
			else:
				entry.data = entry.loadData(self.transformBuffer)
		return entry.data
示例#12
0
    def getTableData(self, tag):
        """Returns raw table data, whether compiled or directly read from disk.
		"""
        tag = Tag(tag)
        if self.isLoaded(tag):
            log.debug("Compiling '%s' table", tag)
            return self.tables[tag].compile(self)
        elif self.reader and tag in self.reader:
            log.debug("Reading '%s' table from disk", tag)
            return self.reader[tag]
        else:
            raise KeyError(tag)
def getMacCreatorAndType(path):
	"""Returns file creator and file type codes for a path.

	Args:
		path (str): A file path.

	Returns:
		A tuple of two :py:class:`fontTools.textTools.Tag` objects, the first
		representing the file creator and the second representing the
		file type.
	"""
	if xattr is not None:
		try:
			finderInfo = xattr.getxattr(path, 'com.apple.FinderInfo')
		except (KeyError, IOError):
			pass
		else:
			fileType = Tag(finderInfo[:4])
			fileCreator = Tag(finderInfo[4:8])
			return fileCreator, fileType
	return None, None
示例#14
0
 def __getitem__(self, tag):
     tag = Tag(tag)
     table = self.tables.get(tag)
     if table is None:
         if tag == "GlyphOrder":
             table = GlyphOrder(tag)
             self.tables[tag] = table
         elif self.reader is not None:
             table = self._readTable(tag)
         else:
             raise KeyError("'%s' table not found" % tag)
     return table
示例#15
0
	def fromString(self, data):
		if len(data) < 1:
			raise TTLibError("can't read table 'flags': not enough data")
		dummy, data = sstruct.unpack2(woff2FlagsFormat, data, self)
		if self.flags & 0x3F == 0x3F:
			# if bits [0..5] of the flags byte == 63, read a 4-byte arbitrary tag value
			if len(data) < woff2UnknownTagSize:
				raise TTLibError("can't read table 'tag': not enough data")
			dummy, data = sstruct.unpack2(woff2UnknownTagFormat, data, self)
		else:
			# otherwise, tag is derived from a fixed 'Known Tags' table
			self.tag = woff2KnownTags[self.flags & 0x3F]
		self.tag = Tag(self.tag)
		self.origLength, data = unpackBase128(data)
		self.length = self.origLength
		if self.transformed:
			self.length, data = unpackBase128(data)
			if self.tag == 'loca' and self.length != 0:
				raise TTLibError(
					"the transformLength of the 'loca' table must be 0")
		# return left over data
		return data
示例#16
0
 def fromXML(self, name, _attrs, content, ttFont):
     assert (name == "Axis")
     for tag, _, value in filter(lambda t: type(t) is tuple, content):
         value = ''.join(value)
         if tag == "AxisTag":
             self.axisTag = Tag(value)
         elif tag in {
                 "Flags", "MinValue", "DefaultValue", "MaxValue",
                 "AxisNameID"
         }:
             setattr(
                 self, tag[0].lower() + tag[1:],
                 str2fl(value, 16)
                 if tag.endswith("Value") else safeEval(value))
示例#17
0
	def reconstructTable(self, tag):
		"""Reconstruct table named 'tag' from transformed data."""
		entry = self.tables[Tag(tag)]
		rawData = entry.loadData(self.transformBuffer)
		if tag == 'glyf':
			# no need to pad glyph data when reconstructing
			padding = self.padding if hasattr(self, 'padding') else None
			data = self._reconstructGlyf(rawData, padding)
		elif tag == 'loca':
			data = self._reconstructLoca()
		elif tag == 'hmtx':
			data = self._reconstructHmtx(rawData)
		else:
			raise TTLibError("transform for table '%s' is unknown" % tag)
		return data
示例#18
0
    def __new__(cls, *args, **kwargs):
        """ Return an instance of the SFNTReader sub-class which is compatible
		with the input file type.
		"""
        if args and cls is SFNTReader:
            infile = args[0]
            infile.seek(0)
            sfntVersion = Tag(infile.read(4))
            infile.seek(0)
            if sfntVersion == "wOF2":
                # return new WOFF2Reader object
                from fontTools.ttLib.woff2 import WOFF2Reader
                return object.__new__(WOFF2Reader)
        # return default object
        return object.__new__(cls)
示例#19
0
    def getTableData(self, tag):
        """Returns the binary representation of a table.

		If the table is currently loaded and in memory, the data is compiled to
		binary and returned; if it is not currently loaded, the binary data is
		read from the font file and returned.
		"""
        tag = Tag(tag)
        if self.isLoaded(tag):
            log.debug("Compiling '%s' table", tag)
            return self.tables[tag].compile(self)
        elif self.reader and tag in self.reader:
            log.debug("Reading '%s' table from disk", tag)
            return self.reader[tag]
        else:
            raise KeyError(tag)
示例#20
0
 def __getitem__(self, tag):
     """Fetch the raw table data."""
     entry = self.tables[Tag(tag)]
     data = entry.loadData(self.file)
     if self.checkChecksums:
         if tag == 'head':
             # Beh: we have to special-case the 'head' table.
             checksum = calcChecksum(data[:8] + b'\0\0\0\0' + data[12:])
         else:
             checksum = calcChecksum(data)
         if self.checkChecksums > 1:
             # Be obnoxious, and barf when it's wrong
             assert checksum == entry.checkSum, "bad checksum for '%s' table" % tag
         elif checksum != entry.checkSum:
             # Be friendly, and just log a warning.
             log.warning("bad checksum for '%s' table", tag)
     return data
示例#21
0
	def __setitem__(self, tag, data):
		"""Associate new entry named 'tag' with raw table data."""
		if tag in self.tables:
			raise TTLibError("cannot rewrite '%s' table" % tag)
		if tag == 'DSIG':
			# always drop DSIG table, since the encoding process can invalidate it
			self.numTables -= 1
			return

		entry = self.DirectoryEntry()
		entry.tag = Tag(tag)
		entry.flags = getKnownTagIndex(entry.tag)
		# WOFF2 table data are written to disk only on close(), after all tags
		# have been specified
		entry.data = data

		self.tables[tag] = entry
示例#22
0
def identifierToTag(ident):
    """the opposite of tagToIdentifier()"""
    if ident == "GlyphOrder":
        return ident
    if len(ident) % 2 and ident[0] == "_":
        ident = ident[1:]
    assert not (len(ident) % 2)
    tag = ""
    for i in range(0, len(ident), 2):
        if ident[i] == "_":
            tag = tag + ident[i + 1]
        elif ident[i + 1] == "_":
            tag = tag + ident[i]
        else:
            # assume hex
            tag = tag + chr(int(ident[i:i + 2], 16))
    # append trailing spaces
    tag = tag + (4 - len(tag)) * ' '
    return Tag(tag)
示例#23
0
	def _decompileTable(self, tag):
		""" Fetch table data, decompile it, and store it inside self.ttFont. """
		tag = Tag(tag)
		if tag not in self.tables:
			raise TTLibError("missing required table: %s" % tag)
		if self.ttFont.isLoaded(tag):
			return
		data = self.tables[tag].data
		if tag == 'loca':
			tableClass = WOFF2LocaTable
		elif tag == 'glyf':
			tableClass = WOFF2GlyfTable
		elif tag == 'hmtx':
			tableClass = WOFF2HmtxTable
		else:
			tableClass = getTableClass(tag)
		table = tableClass(tag)
		self.ttFont.tables[tag] = table
		table.decompile(data, self.ttFont)
示例#24
0
 def __delitem__(self, tag):
     del self.tables[Tag(tag)]
 def __init__(self, tag=None):
     if tag is None:
         tag = getClassTag(self.__class__)
     self.tableTag = Tag(tag)
示例#26
0
 def writeTag(self, tag):
     tag = Tag(tag).tobytes()
     assert len(tag) == 4, tag
     self.items.append(tag)
示例#27
0
 def __setitem__(self, tag, table):
     self.tables[Tag(tag)] = table
示例#28
0
	def __init__(self, tag=None):
		self.tableTag = Tag(tag or 'glyf')
def _add_fvar(font, axes, instances):
    """
	Add 'fvar' table to font.

	axes is an ordered dictionary of DesignspaceAxis objects.

	instances is list of dictionary objects with 'location', 'stylename',
	and possibly 'postscriptfontname' entries.
	"""

    assert axes
    assert isinstance(axes, OrderedDict)

    log.info("Generating fvar")

    fvar = newTable('fvar')
    nameTable = font['name']

    for a in axes.values():
        axis = Axis()
        axis.axisTag = Tag(a.tag)
        # TODO Skip axes that have no variation.
        axis.minValue, axis.defaultValue, axis.maxValue = a.minimum, a.default, a.maximum
        axis.axisNameID = nameTable.addMultilingualName(a.labelNames,
                                                        font,
                                                        minNameID=256)
        axis.flags = int(a.hidden)
        fvar.axes.append(axis)

    for instance in instances:
        coordinates = instance.location

        if "en" not in instance.localisedStyleName:
            if not instance.styleName:
                raise VarLibValidationError(
                    f"Instance at location '{coordinates}' must have a default English "
                    "style name ('stylename' attribute on the instance element or a "
                    "stylename element with an 'xml:lang=\"en\"' attribute).")
            localisedStyleName = dict(instance.localisedStyleName)
            localisedStyleName["en"] = tostr(instance.styleName)
        else:
            localisedStyleName = instance.localisedStyleName

        psname = instance.postScriptFontName

        inst = NamedInstance()
        inst.subfamilyNameID = nameTable.addMultilingualName(
            localisedStyleName)
        if psname is not None:
            psname = tostr(psname)
            inst.postscriptNameID = nameTable.addName(psname)
        inst.coordinates = {
            axes[k].tag: axes[k].map_backward(v)
            for k, v in coordinates.items()
        }
        #inst.coordinates = {axes[k].tag:v for k,v in coordinates.items()}
        fvar.instances.append(inst)

    assert "fvar" not in font
    font['fvar'] = fvar

    return fvar
示例#30
0
	def __init__(self, tag=None):
		self.tableTag = Tag(tag or 'hmtx')