Esempio n. 1
0
	def compileActions(font, states):
		result, actions, actionIndex = b"", set(), {}
		for state in states:
			for _glyphClass, trans in state.Transitions.items():
				actions.add(trans.compileLigActions())
		# Sort the compiled actions in decreasing order of
		# length, so that the longer sequence come before the
		# shorter ones.  For each compiled action ABCD, its
		# suffixes BCD, CD, and D do not be encoded separately
		# (in case they occur); instead, we can just store an
		# index that points into the middle of the longer
		# sequence. Every compiled AAT ligature sequence is
		# terminated with an end-of-sequence flag, which can
		# only be set on the last element of the sequence.
		# Therefore, it is sufficient to consider just the
		# suffixes.
		for a in sorted(actions, key=lambda x:(-len(x), x)):
			if a not in actionIndex:
				for i in range(0, len(a), 4):
					suffix = a[i:]
					suffixIndex = (len(result) + i) // 4
					actionIndex.setdefault(
						suffix, suffixIndex)
				result += a
		result = pad(result, 4)
		return (result, actionIndex)
Esempio n. 2
0
	def compileActions(font, states):
		result, actions, actionIndex = b"", set(), {}
		for state in states:
			for _glyphClass, trans in state.Transitions.items():
				actions.add(trans.compileLigActions())
		# Sort the compiled actions in decreasing order of
		# length, so that the longer sequence come before the
		# shorter ones.  For each compiled action ABCD, its
		# suffixes BCD, CD, and D do not be encoded separately
		# (in case they occur); instead, we can just store an
		# index that points into the middle of the longer
		# sequence. Every compiled AAT ligature sequence is
		# terminated with an end-of-sequence flag, which can
		# only be set on the last element of the sequence.
		# Therefore, it is sufficient to consider just the
		# suffixes.
		for a in sorted(actions, key=lambda x:(-len(x), x)):
			if a not in actionIndex:
				for i in range(0, len(a), 4):
					suffix = a[i:]
					suffixIndex = (len(result) + i) // 4
					actionIndex.setdefault(
						suffix, suffixIndex)
				result += a
		result = pad(result, 4)
		return (result, actionIndex)
def setMacCreatorAndType(path, fileCreator, fileType):
    if xattr is not None:
        from fontTools.misc.textTools import pad
        if not all(len(s) == 4 for s in (fileCreator, fileType)):
            raise TypeError('arg must be string of 4 chars')
        finderInfo = pad(bytesjoin([fileType, fileCreator]), 32)
        xattr.setxattr(path, 'com.apple.FinderInfo', finderInfo)
    if MacOS is not None:
        MacOS.SetCreatorAndType(path, fileCreator, fileType)
Esempio n. 4
0
def setMacCreatorAndType(path, fileCreator, fileType):
	if xattr is not None:
		from fontTools.misc.textTools import pad
		if not all(len(s) == 4 for s in (fileCreator, fileType)):
			raise TypeError('arg must be string of 4 chars')
		finderInfo = pad(bytesjoin([fileType, fileCreator]), 32)
		xattr.setxattr(path, 'com.apple.FinderInfo', finderInfo)
	if MacOS is not None:
		MacOS.SetCreatorAndType(path, fileCreator, fileType)
Esempio n. 5
0
 def _writeFlavorData(self):
     """Write metadata and/or private data using appropiate padding."""
     compressedMetaData = self.compressedMetaData
     privData = self.flavorData.privData
     if compressedMetaData and privData:
         compressedMetaData = pad(compressedMetaData, size=4)
     if compressedMetaData:
         self.file.seek(self.metaOffset)
         assert self.file.tell() == self.metaOffset
         self.file.write(compressedMetaData)
     if privData:
         self.file.seek(self.privOffset)
         assert self.file.tell() == self.privOffset
         self.file.write(privData)
Esempio n. 6
0
	def _writeFlavorData(self):
		"""Write metadata and/or private data using appropiate padding."""
		compressedMetaData = self.compressedMetaData
		privData = self.flavorData.privData
		if compressedMetaData and privData:
			compressedMetaData = pad(compressedMetaData, size=4)
		if compressedMetaData:
			self.file.seek(self.metaOffset)
			assert self.file.tell() == self.metaOffset
			self.file.write(compressedMetaData)
		if privData:
			self.file.seek(self.privOffset)
			assert self.file.tell() == self.privOffset
			self.file.write(privData)
Esempio n. 7
0
	def close(self):
		""" All tags must have been specified. Now write the table data and directory.
		"""
		if len(self.tables) != self.numTables:
			raise TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(self.tables)))

		if self.sfntVersion in ("\x00\x01\x00\x00", "true"):
			isTrueType = True
		elif self.sfntVersion == "OTTO":
			isTrueType = False
		else:
			raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")

		# The WOFF2 spec no longer requires the glyph offsets to be 4-byte aligned.
		# However, the reference WOFF2 implementation still fails to reconstruct
		# 'unpadded' glyf tables, therefore we need to 'normalise' them.
		# See:
		# https://github.com/khaledhosny/ots/issues/60
		# https://github.com/google/woff2/issues/15
		if (
			isTrueType
			and "glyf" in self.flavorData.transformedTables
			and "glyf" in self.tables
		):
			self._normaliseGlyfAndLoca(padding=4)
		self._setHeadTransformFlag()

		# To pass the legacy OpenType Sanitiser currently included in browsers,
		# we must sort the table directory and data alphabetically by tag.
		# See:
		# https://github.com/google/woff2/pull/3
		# https://lists.w3.org/Archives/Public/public-webfonts-wg/2015Mar/0000.html
		# TODO(user): remove to match spec once browsers are on newer OTS
		self.tables = OrderedDict(sorted(self.tables.items()))

		self.totalSfntSize = self._calcSFNTChecksumsLengthsAndOffsets()

		fontData = self._transformTables()
		compressedFont = brotli.compress(fontData, mode=brotli.MODE_FONT)

		self.totalCompressedSize = len(compressedFont)
		self.length = self._calcTotalSize()
		self.majorVersion, self.minorVersion = self._getVersion()
		self.reserved = 0

		directory = self._packTableDirectory()
		self.file.seek(0)
		self.file.write(pad(directory + compressedFont, size=4))
		self._writeFlavorData()
Esempio n. 8
0
	def close(self):
		""" All tags must have been specified. Now write the table data and directory.
		"""
		if len(self.tables) != self.numTables:
			raise TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(self.tables)))

		if self.sfntVersion in ("\x00\x01\x00\x00", "true"):
			isTrueType = True
		elif self.sfntVersion == "OTTO":
			isTrueType = False
		else:
			raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")

		# The WOFF2 spec no longer requires the glyph offsets to be 4-byte aligned.
		# However, the reference WOFF2 implementation still fails to reconstruct
		# 'unpadded' glyf tables, therefore we need to 'normalise' them.
		# See:
		# https://github.com/khaledhosny/ots/issues/60
		# https://github.com/google/woff2/issues/15
		if isTrueType:
			self._normaliseGlyfAndLoca(padding=4)
		self._setHeadTransformFlag()

		# To pass the legacy OpenType Sanitiser currently included in browsers,
		# we must sort the table directory and data alphabetically by tag.
		# See:
		# https://github.com/google/woff2/pull/3
		# https://lists.w3.org/Archives/Public/public-webfonts-wg/2015Mar/0000.html
		# TODO(user): remove to match spec once browsers are on newer OTS
		self.tables = OrderedDict(sorted(self.tables.items()))

		self.totalSfntSize = self._calcSFNTChecksumsLengthsAndOffsets()

		fontData = self._transformTables()
		compressedFont = brotli.compress(fontData, mode=brotli.MODE_FONT)

		self.totalCompressedSize = len(compressedFont)
		self.length = self._calcTotalSize()
		self.majorVersion, self.minorVersion = self._getVersion()
		self.reserved = 0

		directory = self._packTableDirectory()
		self.file.seek(0)
		self.file.write(pad(directory + compressedFont, size=4))
		self._writeFlavorData()
Esempio n. 9
0
	def compile(self, ttFont):
		if not hasattr(self, "glyphOrder"):
			self.glyphOrder = ttFont.getGlyphOrder()
		padding = self.padding
		assert padding in (0, 1, 2, 4)
		locations = []
		currentLocation = 0
		dataList = []
		recalcBBoxes = ttFont.recalcBBoxes
		for glyphName in self.glyphOrder:
			glyph = self.glyphs[glyphName]
			glyphData = glyph.compile(self, recalcBBoxes)
			if padding > 1:
				glyphData = pad(glyphData, size=padding)
			locations.append(currentLocation)
			currentLocation = currentLocation + len(glyphData)
			dataList.append(glyphData)
		locations.append(currentLocation)

		if padding == 1 and currentLocation < 0x20000:
			# See if we can pad any odd-lengthed glyphs to allow loca
			# table to use the short offsets.
			indices = [i for i,glyphData in enumerate(dataList) if len(glyphData) % 2 == 1]
			if indices and currentLocation + len(indices) < 0x20000:
				# It fits.  Do it.
				for i in indices:
					dataList[i] += b'\0'
				currentLocation = 0
				for i,glyphData in enumerate(dataList):
					locations[i] = currentLocation
					currentLocation += len(glyphData)
				locations[len(dataList)] = currentLocation

		data = b''.join(dataList)
		if 'loca' in ttFont:
			ttFont['loca'].set(locations)
		if 'maxp' in ttFont:
			ttFont['maxp'].numGlyphs = len(self.glyphs)
		if not data:
		# As a special case when all glyph in the font are empty, add a zero byte
		# to the table, so that OTS doesn’t reject it, and to make the table work
		# on Windows as well.
		# See https://github.com/khaledhosny/ots/issues/52
			data = b"\0"
		return data
Esempio n. 10
0
def setMacCreatorAndType(path, fileCreator, fileType):
    """Set file creator and file type codes for a path.

	Note that if the ``xattr`` module is not installed, no action is
	taken but no error is raised.

	Args:
		path (str): A file path.
		fileCreator: A four-character file creator tag.
		fileType: A four-character file type tag.

	"""
    if xattr is not None:
        from fontTools.misc.textTools import pad
        if not all(len(s) == 4 for s in (fileCreator, fileType)):
            raise TypeError('arg must be string of 4 chars')
        finderInfo = pad(bytesjoin([fileType, fileCreator]), 32)
        xattr.setxattr(path, 'com.apple.FinderInfo', finderInfo)
Esempio n. 11
0
    def compile(self, ttFont):
        if not hasattr(self, "glyphOrder"):
            self.glyphOrder = ttFont.getGlyphOrder()
        padding = self.padding
        assert padding in (0, 1, 2, 4)
        locations = []
        currentLocation = 0
        dataList = []
        recalcBBoxes = ttFont.recalcBBoxes
        for glyphName in self.glyphOrder:
            glyph = self.glyphs[glyphName]
            glyphData = glyph.compile(self, recalcBBoxes)
            if padding > 1:
                glyphData = pad(glyphData, size=padding)
            locations.append(currentLocation)
            currentLocation = currentLocation + len(glyphData)
            dataList.append(glyphData)
        locations.append(currentLocation)

        if padding == 1 and currentLocation < 0x20000:
            # See if we can pad any odd-lengthed glyphs to allow loca
            # table to use the short offsets.
            indices = [
                i for i, glyphData in enumerate(dataList)
                if len(glyphData) % 2 == 1
            ]
            if indices and currentLocation + len(indices) < 0x20000:
                # It fits.  Do it.
                for i in indices:
                    dataList[i] += b'\0'
                currentLocation = 0
                for i, glyphData in enumerate(dataList):
                    locations[i] = currentLocation
                    currentLocation += len(glyphData)
                locations[len(dataList)] = currentLocation

        data = bytesjoin(dataList)
        if 'loca' in ttFont:
            ttFont['loca'].set(locations)
        if 'maxp' in ttFont:
            ttFont['maxp'].numGlyphs = len(self.glyphs)
        return data
Esempio n. 12
0
	def compile(self, ttFont):
		if not hasattr(self, "glyphOrder"):
			self.glyphOrder = ttFont.getGlyphOrder()
		padding = self.padding
		assert padding in (0, 1, 2, 4)
		locations = []
		currentLocation = 0
		dataList = []
		recalcBBoxes = ttFont.recalcBBoxes
		for glyphName in self.glyphOrder:
			glyph = self.glyphs[glyphName]
			glyphData = glyph.compile(self, recalcBBoxes)
			if padding > 1:
				glyphData = pad(glyphData, size=padding)
			locations.append(currentLocation)
			currentLocation = currentLocation + len(glyphData)
			dataList.append(glyphData)
		locations.append(currentLocation)

		if padding == 1 and currentLocation < 0x20000:
			# See if we can pad any odd-lengthed glyphs to allow loca
			# table to use the short offsets.
			indices = [i for i,glyphData in enumerate(dataList) if len(glyphData) % 2 == 1]
			if indices and currentLocation + len(indices) < 0x20000:
				# It fits.  Do it.
				for i in indices:
					dataList[i] += b'\0'
				currentLocation = 0
				for i,glyphData in enumerate(dataList):
					locations[i] = currentLocation
					currentLocation += len(glyphData)
				locations[len(dataList)] = currentLocation

		data = bytesjoin(dataList)
		if 'loca' in ttFont:
			ttFont['loca'].set(locations)
		if 'maxp' in ttFont:
			ttFont['maxp'].numGlyphs = len(self.glyphs)
		return data
Esempio n. 13
0
def test_pad():
    assert len(pad(b'abcd', 4)) == 4
    assert len(pad(b'abcde', 2)) == 6
    assert len(pad(b'abcde', 4)) == 8
    assert pad(b'abcdef', 4) == b'abcdef\x00\x00'
    assert pad(b'abcdef', 1) == b'abcdef'
Esempio n. 14
0
def test_pad():
    assert len(pad(b'abcd', 4)) == 4
    assert len(pad(b'abcde', 2)) == 6
    assert len(pad(b'abcde', 4)) == 8
    assert pad(b'abcdef', 4) == b'abcdef\x00\x00'
    assert pad(b'abcdef', 1) == b'abcdef'