Esempio n. 1
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

			self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(numTables, 16)

		self.nextTableOffset = 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()
Esempio n. 2
0
	def _calcMasterChecksum(self, directory):
		# calculate checkSumAdjustment
		tags = list(self.tables.keys())
		checksums = []
		for i in range(len(tags)):
			checksums.append(self.tables[tags[i]].checkSum)

		if self.DirectoryEntry != SFNTDirectoryEntry:
			# Create a SFNT directory for checksum calculation purposes
			self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables, 16)
			directory = sstruct.pack(sfntDirectoryFormat, self)
			tables = sorted(self.tables.items())
			for tag, entry in tables:
				sfntEntry = SFNTDirectoryEntry()
				sfntEntry.tag = entry.tag
				sfntEntry.checkSum = entry.checkSum
				sfntEntry.offset = entry.origOffset
				sfntEntry.length = entry.origLength
				directory = directory + sfntEntry.toString()

		directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
		assert directory_end == len(directory)

		checksums.append(calcChecksum(directory))
		checksum = sum(checksums) & 0xffffffff
		# BiboAfba!
		checksumadjustment = (0xB1B0AFBA - checksum) & 0xffffffff
		return checksumadjustment
Esempio n. 3
0
	def _calcMasterChecksum(self, directory):
		# calculate checkSumAdjustment
		tags = list(self.tables.keys())
		checksums = []
		for i in range(len(tags)):
			checksums.append(self.tables[tags[i]].checkSum)

		# TODO(behdad) I'm fairly sure the checksum for woff is not working correctly.
		# Haven't debugged.
		if self.DirectoryEntry != SFNTDirectoryEntry:
			# Create a SFNT directory for checksum calculation purposes
			self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables, 16)
			directory = sstruct.pack(sfntDirectoryFormat, self)
			tables = sorted(self.tables.items())
			for tag, entry in tables:
				sfntEntry = SFNTDirectoryEntry()
				for item in ['tag', 'checkSum', 'offset', 'length']:
					setattr(sfntEntry, item, getattr(entry, item))
				directory = directory + sfntEntry.toString()

		directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
		assert directory_end == len(directory)

		checksums.append(calcChecksum(directory))
		checksum = sum(checksums) & 0xffffffff
		# BiboAfba!
		checksumadjustment = (0xB1B0AFBA - checksum) & 0xffffffff
		return checksumadjustment
Esempio n. 4
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()
Esempio n. 5
0
def _testJunkAtTheBeginningOfTheFile(header):
    """
    >>> test = dict(numTables=5, searchRange=64, entrySelector=2, rangeShift=16)
    >>> bool(_testJunkAtTheBeginningOfTheFile(test))
    False
    >>> test = dict(numTables=5, searchRange=0, entrySelector=2, rangeShift=16)
    >>> bool(_testJunkAtTheBeginningOfTheFile(test))
    True
    >>> test = dict(numTables=5, searchRange=64, entrySelector=0, rangeShift=16)
    >>> bool(_testJunkAtTheBeginningOfTheFile(test))
    True
    >>> test = dict(numTables=5, searchRange=64, entrySelector=2, rangeShift=0)
    >>> bool(_testJunkAtTheBeginningOfTheFile(test))
    True
    """
    errors = []
    numTables = header["numTables"]
    searchRange, entrySelector, rangeShift = getSearchRange(numTables)
    if header["searchRange"] != searchRange:
        errors.append("The searchRange value is incorrect.")
    if header["entrySelector"] != entrySelector:
        errors.append("The entrySelector value is incorrect.")
    if header["rangeShift"] != rangeShift:
        errors.append("The rangeShift value is incorrect.")
    return errors
Esempio n. 6
0
def calcHeadCheckSumAdjustment(flavor, tables):
    numTables = len(tables)
    # build the sfnt header
    searchRange, entrySelector, rangeShift = getSearchRange(numTables)
    sfntDirectoryData = dict(
        sfntVersion=flavor,
        numTables=numTables,
        searchRange=searchRange,
        entrySelector=entrySelector,
        rangeShift=rangeShift
    )
    # build the sfnt directory
    directory = sstruct.pack(sfntDirectoryFormat, sfntDirectoryData)
    for tag, entry in sorted(tables.items()):
        entry = tables[tag]
        sfntEntry = SFNTDirectoryEntry()
        sfntEntry.tag = tag
        sfntEntry.checkSum = entry["checkSum"]
        sfntEntry.offset = entry["offset"]
        sfntEntry.length = entry["length"]
        directory += sfntEntry.toString()
    # calculate the checkSumAdjustment
    checkSums = [entry["checkSum"] for entry in tables.values()]
    checkSums.append(calcChecksum(directory))
    checkSumAdjustment = sum(checkSums)
    checkSumAdjustment = (0xB1B0AFBA - checkSumAdjustment) & 0xffffffff
    # done
    return checkSumAdjustment
	def compile(self, ttFont):
		nPairs = len(self.kernTable)
		searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
		data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)

		# yeehee! (I mean, turn names into indices)
		getGlyphID = ttFont.getGlyphID
		kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items())
		for left, right, value in kernTable:
			data = data + struct.pack(">HHh", left, right, value)
		return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data
Esempio n. 8
0
def packSFNT(header, directory, tableData, flavor="cff",
    calcCheckSum=True, applyPadding=True, sortDirectory=True,
    searchRange=None, entrySelector=None, rangeShift=None):
    # update the checkSum
    if calcCheckSum:
        if flavor == "cff":
            f = "OTTO"
        else:
            f = "\000\001\000\000"
        calcHeadCheckSumAdjustmentSFNT(directory, tableData, flavor=f)
    # update the header
    cSearchRange, cEntrySelector, cRangeShift = getSearchRange(len(directory), 16)
    if searchRange is None:
        searchRange = cSearchRange
    if entrySelector is None:
        entrySelector = cEntrySelector
    if rangeShift is None:
        rangeShift = cRangeShift
    if flavor == "cff":
        header["sfntVersion"] = "OTTO"
    else:
        header["sfntVersion"] = "\000\001\000\000"
    header["searchRange"] = searchRange
    header["entrySelector"] = entrySelector
    header["rangeShift"] = rangeShift
    # version and num tables should already be set
    sfntData = sstruct.pack(sfntDirectoryFormat, header)
    # compile the directory
    sfntDirectoryEntries = {}
    entryOrder = []
    for entry in directory:
        sfntEntry = SFNTDirectoryEntry()
        sfntEntry.tag = entry["tag"]
        sfntEntry.checkSum = entry["checksum"]
        sfntEntry.offset = entry["offset"]
        sfntEntry.length = entry["length"]
        sfntDirectoryEntries[entry["tag"]] = sfntEntry
        entryOrder.append(entry["tag"])
    if sortDirectory:
        entryOrder = sorted(entryOrder)
    for tag in entryOrder:
        entry = sfntDirectoryEntries[tag]
        sfntData += entry.toString()
    # compile the data
    directory = [(entry["offset"], entry["tag"]) for entry in directory]
    for o, tag in sorted(directory):
        data = tableData[tag]
        if applyPadding:
            data = padData(data)
        sfntData += data
    # done
    return sfntData
Esempio n. 9
0
	def compile(self, ttFont):
		nPairs = len(self.kernTable)
		searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
		data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)

		# yeehee! (I mean, turn names into indices)
		try:
			reverseOrder = ttFont.getReverseGlyphMap()
			kernTable = sorted((reverseOrder[left], reverseOrder[right], value) for ((left,right),value) in self.kernTable.items())
		except KeyError:
			# Slower, but will not throw KeyError on invalid glyph id.
			getGlyphID = ttFont.getGlyphID
			kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items())

		for left, right, value in kernTable:
			data = data + struct.pack(">HHh", left, right, value)
		return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data
Esempio n. 10
0
def calcHeadCheckSumAdjustmentSFNT(directory, tableData, flavor=None):
    """
    Set the checkSumAdjustment in the head table data.
    Grumble.
    """
    # if the flavor is None, guess.
    if flavor is None:
        flavor = "\000\001\000\000"
        for entry in directory:
            if entry["tag"] == "CFF ":
                flavor = "OTTO"
                break
    assert flavor in ("OTTO", "\000\001\000\000")
    # make the sfnt header
    searchRange, entrySelector, rangeShift = getSearchRange(len(directory), 16)
    sfntHeaderData = dict(
        sfntVersion=flavor,
        numTables=len(directory),
        searchRange=searchRange,
        entrySelector=entrySelector,
        rangeShift=rangeShift,
    )
    sfntData = sstruct.pack(sfntDirectoryFormat, sfntHeaderData)
    # make a SFNT table directory
    directory = [(entry["tag"], entry) for entry in directory]
    for tag, entry in sorted(directory):
        sfntEntry = SFNTDirectoryEntry()
        sfntEntry.tag = entry["tag"]
        sfntEntry.checkSum = entry["checksum"]
        sfntEntry.offset = entry["offset"]
        sfntEntry.length = entry["length"]
        sfntData += sfntEntry.toString()
    # calculate the checksum
    sfntDataChecksum = calcChecksum(sfntData)
    # gather all of the checksums
    checksums = [entry["checksum"] for o, entry in directory]
    checksums.append(sfntDataChecksum)
    # calculate the checksum
    checkSumAdjustment = sum(checksums)
    checkSumAdjustment = (0xB1B0AFBA - checkSumAdjustment) & 0xFFFFFFFF
    # set the value in the head table
    headTableData = tableData["head"]
    newHeadTableData = headTableData[:8]
    newHeadTableData += struct.pack(">L", checkSumAdjustment)
    newHeadTableData += headTableData[12:]
    tableData["head"] = newHeadTableData
Esempio n. 11
0
def calcHeadCheckSumAdjustmentSFNT(directory, tableData, flavor=None):
    """
    Set the checkSumAdjustment in the head table data.
    Grumble.
    """
    # if the flavor is None, guess.
    if flavor is None:
        flavor = "\000\001\000\000"
        for entry in directory:
            if entry["tag"] == "CFF ":
                flavor = "OTTO"
                break
    assert flavor in ("OTTO", "\000\001\000\000")
    # make the sfnt header
    searchRange, entrySelector, rangeShift = getSearchRange(len(directory), 16)
    sfntHeaderData = dict(
        sfntVersion=flavor,
        numTables=len(directory),
        searchRange=searchRange,
        entrySelector=entrySelector,
        rangeShift=rangeShift
    )
    sfntData = sstruct.pack(sfntDirectoryFormat, sfntHeaderData)
    # make a SFNT table directory
    directory = [(entry["tag"], entry) for entry in directory]
    for tag, entry in sorted(directory):
        sfntEntry = SFNTDirectoryEntry()
        sfntEntry.tag = entry["tag"]
        sfntEntry.checkSum = entry["checksum"]
        sfntEntry.offset = entry["offset"]
        sfntEntry.length = entry["length"]
        sfntData += sfntEntry.toString()
    # calculate the checksum
    sfntDataChecksum = calcChecksum(sfntData)
    # gather all of the checksums
    checksums = [entry["checksum"] for o, entry in directory]
    checksums.append(sfntDataChecksum)
    # calculate the checksum
    checkSumAdjustment = sum(checksums)
    checkSumAdjustment = (0xB1B0AFBA - checkSumAdjustment) & 0xffffffff
    # set the value in the head table
    headTableData = tableData["head"]
    newHeadTableData = headTableData[:8]
    newHeadTableData += struct.pack(">L", checkSumAdjustment)
    newHeadTableData += headTableData[12:]
    tableData["head"] = newHeadTableData
Esempio n. 12
0
	def compile(self, ttFont):
		nPairs = len(self.kernTable)
		searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
		searchRange &= 0xFFFF
		data = struct.pack(
			">HHHH", nPairs, searchRange, entrySelector, rangeShift)

		# yeehee! (I mean, turn names into indices)
		try:
			reverseOrder = ttFont.getReverseGlyphMap()
			kernTable = sorted(
				(reverseOrder[left], reverseOrder[right], value)
				for ((left, right), value) in self.kernTable.items())
		except KeyError:
			# Slower, but will not throw KeyError on invalid glyph id.
			getGlyphID = ttFont.getGlyphID
			kernTable = sorted(
				(getGlyphID(left), getGlyphID(right), value)
				for ((left, right), value) in self.kernTable.items())

		for left, right, value in kernTable:
			data = data + struct.pack(">HHh", left, right, value)

		if not self.apple:
			version = 0
			length = len(data) + 6
			if length >= 0x10000:
				log.warning('"kern" subtable overflow, '
							'truncating length value while preserving pairs.')
				length &= 0xFFFF
			header = struct.pack(
				">HHBB", version, length, self.format, self.coverage)
		else:
			if self.tupleIndex is None:
				# sensible default when compiling a TTX from an old fonttools
				# or when inserting a Windows-style format 0 subtable into an
				# Apple version=1.0 kern table
				log.warning("'tupleIndex' is None; default to 0")
				self.tupleIndex = 0
			length = len(data) + 8
			header = struct.pack(
				">LBBH", length, self.coverage, self.format, self.tupleIndex)
		return header + data
def _cmap_format_4_compile(self, ttFont):
  if self.data:
    return struct.pack(">HHH", self.format, self.length, self.language) + self.data

  charCodes = list(self.cmap.keys())
  charCodes.sort()
  charCodes = charCodes[:256]
  lenCharCodes = len(charCodes)
  if lenCharCodes == 0:
    startCode = [0xffff]
    endCode = [0xffff]
  else:
    charCodes.sort()
    names = list(map(operator.getitem, [self.cmap]*lenCharCodes, charCodes))
    nameMap = ttFont.getReverseGlyphMap()
    try:
      gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
    except KeyError:
      nameMap = ttFont.getReverseGlyphMap(rebuild=True)
      try:
        gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
      except KeyError:
        # allow virtual GIDs in format 4 tables
        gids = []
        for name in names:
          try:
            gid = nameMap[name]
          except KeyError:
            try:
              if (name[:3] == 'gid'):
                gid = eval(name[3:])
              else:
                gid = ttFont.getGlyphID(name)
            except:
              raise KeyError(name)

          gids.append(gid)
    cmap = {}  # code:glyphID mapping
    list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))

    # Build startCode and endCode lists.
    # Split the char codes in ranges of consecutive char codes, then split
    # each range in more ranges of consecutive/not consecutive glyph IDs.
    # See splitRange().
    lastCode = charCodes[0]
    endCode = []
    startCode = [lastCode]
    for charCode in charCodes[1:]:  # skip the first code, it's the first start code
      if charCode == lastCode + 1:
        lastCode = charCode
        continue
      start, end = splitRange(startCode[-1], lastCode, cmap)
      startCode.extend(start)
      endCode.extend(end)
      startCode.append(charCode)
      lastCode = charCode
    start, end = splitRange(startCode[-1], lastCode, cmap)
    startCode.extend(start)
    endCode.extend(end)
    startCode.append(0xffff)
    endCode.append(0xffff)

  #debug_len = len(endCode)
  #debug_pos = 10
  #debug_start = min(debug_pos, debug_len)
  #debug_end = min(debug_pos+10, debug_len)
#   for i in range(debug_start, debug_end):
#     print('a: start/end {0}/{1}'.format(startCode[i], endCode[i] + 1))
  # build up rest of cruft
  idDelta = []
  idRangeOffset = []
  glyphIndexArray = []
  for i in range(len(endCode)-1):  # skip the closing codes (0xffff)
    indices = []
    for charCode in range(startCode[i], endCode[i] + 1):
      indices.append(cmap[charCode])
    if  (indices == list(range(indices[0], indices[0] + len(indices)))):
      idDelta.append((indices[0] - startCode[i]) % 0x10000)
      idRangeOffset.append(0)
    else:
      # someone *definitely* needs to get killed.
      idDelta.append(0)
      idRangeOffset.append(2 * (len(endCode) + len(glyphIndexArray) - i))
      glyphIndexArray.extend(indices)
  idDelta.append(1)  # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef
  idRangeOffset.append(0)

  # Insane.
  segCount = len(endCode)
  segCountX2 = segCount * 2
  searchRange, entrySelector, rangeShift = getSearchRange(segCount, 2)

  charCodeArray = array.array("H", endCode + [0] + startCode)
  idDeltaArray = array.array("H", idDelta)
  restArray = array.array("H", idRangeOffset + glyphIndexArray)
  if sys.byteorder != "big":
    charCodeArray.byteswap()
    idDeltaArray.byteswap()
    restArray.byteswap()
  data = charCodeArray.tostring() + idDeltaArray.tostring() + restArray.tostring()

  length = struct.calcsize(_c_m_a_p.cmap_format_4_format) + len(data)
  header = struct.pack(_c_m_a_p.cmap_format_4_format, self.format, length, self.language,
      segCountX2, searchRange, entrySelector, rangeShift)
  return header + data
Esempio n. 14
0
def getSFNTCollectionData(pathOrFiles, modifyNames=True, reverseNames=False, DSIG=False, duplicates=[], shared=[]):
    tables = []
    offsets = {}

    fonts = [TTFont(pathOrFile) for pathOrFile in pathOrFiles]
    numFonts = len(fonts)

    header = dict(
        TTCTag="ttcf",
        Version=0x00010000,
        numFonts=numFonts,
    )

    if DSIG:
        header["version"] = 0x00020000

    fontData = sstruct.pack(ttcHeaderFormat, header)
    offset = ttcHeaderSize + (numFonts * struct.calcsize(">L"))
    if DSIG:
        offset += 3 * struct.calcsize(">L")

    for font in fonts:
        fontData += struct.pack(">L", offset)
        tags = [i for i in sorted(font.keys()) if len(i) == 4]
        offset += sfntDirectorySize + (len(tags) * sfntDirectoryEntrySize)

    if DSIG:
        data = "\0" * 4
        tables.append(data)
        offset += len(data)
        fontData += struct.pack(">4s", "DSIG")
        fontData += struct.pack(">L", len(data))
        fontData += struct.pack(">L", offset)

    for i, font in enumerate(fonts):
        # Make the name table unique
        if modifyNames:
            index = i
            if reverseNames:
                index = len(fonts) - i - 1
            name = font["name"]
            for namerecord in name.names:
                nameID = namerecord.nameID
                string = namerecord.toUnicode()
                if nameID == 1:
                    namerecord.string = "%s %d" % (string, index)
                elif nameID == 4:
                    namerecord.string = string.replace("Regular", "%d Regular" % index)
                elif nameID == 6:
                    namerecord.string = string.replace("-", "%d-" % index)

        tags = [i for i in sorted(font.keys()) if len(i) == 4]

        searchRange, entrySelector, rangeShift = getSearchRange(len(tags), 16)
        offsetTable = dict(
            sfntVersion=font.sfntVersion,
            numTables=len(tags),
            searchRange=searchRange,
            entrySelector=entrySelector,
            rangeShift=rangeShift,
        )

        fontData += sstruct.pack(sfntDirectoryFormat, offsetTable)

        for tag in tags:
            data = font.getTableData(tag)
            checksum = calcTableChecksum(tag, data)
            entry = dict(
                tag=tag,
                offset=offset,
                length=len(data),
                checkSum=checksum,
            )

            if (shared and tag not in shared) or tag in duplicates or data not in tables:
                tables.append(data)
                offsets[checksum] = offset
                offset += len(data) + calcPaddingLength(len(data))
            else:
                entry["offset"] = offsets[checksum]

            fontData += sstruct.pack(sfntDirectoryEntryFormat, entry)

    for table in tables:
        fontData += padData(table)

    for font in fonts:
        font.close()

    return fontData