示例#1
0
    def decompile(self, ttFont):
        self.glyphName = ttFont.getGlyphName(self.gid)
        if self.rawdata is None:
            from fontemon_blender_addon.fontTools import ttLib
            raise ttLib.TTLibError("No table data to decompile")
        if len(self.rawdata) > 0:
            if len(self.rawdata) < sbixGlyphHeaderFormatSize:
                from fontemon_blender_addon.fontTools import ttLib
                #print "Glyph %i header too short: Expected %x, got %x." % (self.gid, sbixGlyphHeaderFormatSize, len(self.rawdata))
                raise ttLib.TTLibError("Glyph header too short.")

            sstruct.unpack(sbixGlyphHeaderFormat,
                           self.rawdata[:sbixGlyphHeaderFormatSize], self)

            if self.graphicType == "dupe":
                # this glyph is a reference to another glyph's image data
                gid, = struct.unpack(">H",
                                     self.rawdata[sbixGlyphHeaderFormatSize:])
                self.referenceGlyphName = ttFont.getGlyphName(gid)
            else:
                self.imageData = self.rawdata[sbixGlyphHeaderFormatSize:]
                self.referenceGlyphName = None
        # clean up
        del self.rawdata
        del self.gid
示例#2
0
    def __init__(self,
                 glyphName=None,
                 referenceGlyphName=None,
                 originOffsetX=0,
                 originOffsetY=0,
                 graphicType=None,
                 imageData=None,
                 rawdata=None,
                 gid=0):
        self.gid = gid
        self.glyphName = glyphName
        self.referenceGlyphName = referenceGlyphName
        self.originOffsetX = originOffsetX
        self.originOffsetY = originOffsetY
        self.rawdata = rawdata
        self.graphicType = graphicType
        self.imageData = imageData

        # fix self.graphicType if it is null terminated or too short
        if self.graphicType is not None:
            if self.graphicType[-1] == "\0":
                self.graphicType = self.graphicType[:-1]
            if len(self.graphicType) > 4:
                from fontemon_blender_addon.fontTools import ttLib
                raise ttLib.TTLibError(
                    "Glyph.graphicType must not be longer than 4 characters.")
            elif len(self.graphicType) < 4:
                # pad with spaces
                self.graphicType += "    "[:(4 - len(self.graphicType))]
示例#3
0
	def decompile(self, data, ttFont):
		# read table header
		sstruct.unpack(sbixHeaderFormat, data[ : sbixHeaderFormatSize], self)
		# collect offsets to individual strikes in self.strikeOffsets
		for i in range(self.numStrikes):
			current_offset = sbixHeaderFormatSize + i * sbixStrikeOffsetFormatSize
			offset_entry = sbixStrikeOffset()
			sstruct.unpack(sbixStrikeOffsetFormat, \
				data[current_offset:current_offset+sbixStrikeOffsetFormatSize], \
				offset_entry)
			self.strikeOffsets.append(offset_entry.strikeOffset)

		# decompile Strikes
		for i in range(self.numStrikes-1, -1, -1):
			current_strike = Strike(rawdata=data[self.strikeOffsets[i]:])
			data = data[:self.strikeOffsets[i]]
			current_strike.decompile(ttFont)
			#print "  Strike length: %xh" % len(bitmapSetData)
			#print "Number of Glyph entries:", len(current_strike.glyphs)
			if current_strike.ppem in self.strikes:
				from fontemon_blender_addon.fontTools import ttLib
				raise ttLib.TTLibError("Pixel 'ppem' must be unique for each Strike")
			self.strikes[current_strike.ppem] = current_strike

		# after the glyph data records have been extracted, we don't need the offsets anymore
		del self.strikeOffsets
		del self.numStrikes
示例#4
0
	def compile(self, ttFont):
		self.updateFirstAndLastCharIndex(ttFont)
		panose = self.panose
		head = ttFont["head"]
		if (self.fsSelection & 1) and not (head.macStyle & 1<<1):
			log.warning("fsSelection bit 0 (italic) and "
				"head table macStyle bit 1 (italic) should match")
		if (self.fsSelection & 1<<5) and not (head.macStyle & 1):
			log.warning("fsSelection bit 5 (bold) and "
				"head table macStyle bit 0 (bold) should match")
		if (self.fsSelection & 1<<6) and (self.fsSelection & 1 + (1<<5)):
			log.warning("fsSelection bit 6 (regular) is set, "
				"bits 0 (italic) and 5 (bold) must be clear")
		if self.version < 4 and self.fsSelection & 0b1110000000:
			log.warning("fsSelection bits 7, 8 and 9 are only defined in "
				"OS/2 table version 4 and up: version %s", self.version)
		self.panose = sstruct.pack(panoseFormat, self.panose)
		if self.version == 0:
			data = sstruct.pack(OS2_format_0, self)
		elif self.version == 1:
			data = sstruct.pack(OS2_format_1, self)
		elif self.version in (2, 3, 4):
			data = sstruct.pack(OS2_format_2, self)
		elif self.version == 5:
			d = self.__dict__.copy()
			d['usLowerOpticalPointSize'] = round(self.usLowerOpticalPointSize * 20)
			d['usUpperOpticalPointSize'] = round(self.usUpperOpticalPointSize * 20)
			data = sstruct.pack(OS2_format_5, d)
		else:
			from fontemon_blender_addon.fontTools import ttLib
			raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
		self.panose = panose
		return data
示例#5
0
 def decompile(self, data, ttFont):
     pos = 0  # track current position from to start of VDMX table
     dummy, data = sstruct.unpack2(VDMX_HeaderFmt, data, self)
     pos += sstruct.calcsize(VDMX_HeaderFmt)
     self.ratRanges = []
     for i in range(self.numRatios):
         ratio, data = sstruct.unpack2(VDMX_RatRangeFmt, data)
         pos += sstruct.calcsize(VDMX_RatRangeFmt)
         # the mapping between a ratio and a group is defined further below
         ratio['groupIndex'] = None
         self.ratRanges.append(ratio)
     lenOffset = struct.calcsize('>H')
     _offsets = []  # temporarily store offsets to groups
     for i in range(self.numRatios):
         offset = struct.unpack('>H', data[0:lenOffset])[0]
         data = data[lenOffset:]
         pos += lenOffset
         _offsets.append(offset)
     self.groups = []
     for groupIndex in range(self.numRecs):
         # the offset to this group from beginning of the VDMX table
         currOffset = pos
         group, data = sstruct.unpack2(VDMX_GroupFmt, data)
         # the group lenght and bounding sizes are re-calculated on compile
         recs = group.pop('recs')
         startsz = group.pop('startsz')
         endsz = group.pop('endsz')
         pos += sstruct.calcsize(VDMX_GroupFmt)
         for j in range(recs):
             vTable, data = sstruct.unpack2(VDMX_vTableFmt, data)
             vTableLength = sstruct.calcsize(VDMX_vTableFmt)
             pos += vTableLength
             # group is a dict of (yMax, yMin) tuples keyed by yPelHeight
             group[vTable['yPelHeight']] = (vTable['yMax'], vTable['yMin'])
         # make sure startsz and endsz match the calculated values
         minSize = min(group.keys())
         maxSize = max(group.keys())
         assert startsz == minSize, \
          "startsz (%s) must equal min yPelHeight (%s): group %d" % \
          (group.startsz, minSize, groupIndex)
         assert endsz == maxSize, \
          "endsz (%s) must equal max yPelHeight (%s): group %d" % \
          (group.endsz, maxSize, groupIndex)
         self.groups.append(group)
         # match the defined offsets with the current group's offset
         for offsetIndex, offsetValue in enumerate(_offsets):
             # when numRecs < numRatios there can more than one ratio range
             # sharing the same VDMX group
             if currOffset == offsetValue:
                 # map the group with the ratio range thas has the same
                 # index as the offset to that group (it took me a while..)
                 self.ratRanges[offsetIndex]['groupIndex'] = groupIndex
     # check that all ratio ranges have a group
     for i in range(self.numRatios):
         ratio = self.ratRanges[i]
         if ratio['groupIndex'] is None:
             from fontemon_blender_addon.fontTools import ttLib
             raise ttLib.TTLibError("no group defined for ratRange %d" % i)
示例#6
0
    def getGlyphOrder(self):
        """This function will get called by a ttLib.TTFont instance.
		Do not call this function yourself, use TTFont().getGlyphOrder()
		or its relatives instead!
		"""
        if not hasattr(self, "glyphOrder"):
            raise ttLib.TTLibError("illegal use of getGlyphOrder()")
        glyphOrder = self.glyphOrder
        del self.glyphOrder
        return glyphOrder
示例#7
0
 def compile(self, ttFont):
     if self.glyphName is None:
         from fontemon_blender_addon.fontTools import ttLib
         raise ttLib.TTLibError("Can't compile Glyph without glyph name")
         # TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index?
         # (needed if you just want to compile the sbix table on its own)
     self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName))
     if self.graphicType is None:
         self.rawdata = b""
     else:
         self.rawdata = sstruct.pack(sbixGlyphHeaderFormat,
                                     self) + self.imageData
示例#8
0
	def fromXML(self, name, attrs, content, ttFont):
		if name in ["ppem", "resolution"]:
			setattr(self, name, safeEval(attrs["value"]))
		elif name == "glyph":
			if "graphicType" in attrs:
				myFormat = safeEval("'''" + attrs["graphicType"] + "'''")
			else:
				myFormat = None
			if "glyphname" in attrs:
				myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''")
			elif "name" in attrs:
				myGlyphName = safeEval("'''" + attrs["name"] + "'''")
			else:
				from fontemon_blender_addon.fontTools import ttLib
				raise ttLib.TTLibError("Glyph must have a glyph name.")
			if "originOffsetX" in attrs:
				myOffsetX = safeEval(attrs["originOffsetX"])
			else:
				myOffsetX = 0
			if "originOffsetY" in attrs:
				myOffsetY = safeEval(attrs["originOffsetY"])
			else:
				myOffsetY = 0
			current_glyph = Glyph(
				glyphName=myGlyphName,
				graphicType=myFormat,
				originOffsetX=myOffsetX,
				originOffsetY=myOffsetY,
			)
			for element in content:
				if isinstance(element, tuple):
					name, attrs, content = element
					current_glyph.fromXML(name, attrs, content, ttFont)
					current_glyph.compile(ttFont)
			self.glyphs[current_glyph.glyphName] = current_glyph
		else:
			from fontemon_blender_addon.fontTools import ttLib
			raise ttLib.TTLibError("can't handle '%s' element" % name)
 def __init__(self, path, res_name_or_index):
     from fontemon_blender_addon.fontTools import ttLib
     reader = ResourceReader(path)
     if isinstance(res_name_or_index, basestring):
         rsrc = reader.getNamedResource('sfnt', res_name_or_index)
     else:
         rsrc = reader.getIndResource('sfnt', res_name_or_index)
     if rsrc is None:
         raise ttLib.TTLibError("sfnt resource not found: %s" %
                                res_name_or_index)
     reader.close()
     self.rsrc = rsrc
     super(SFNTResourceReader, self).__init__(rsrc.data)
     self.name = path
示例#10
0
 def fromXML(self, name, attrs, content, ttFont):
     if name == "ref":
         # glyph is a "dupe", i.e. a reference to another glyph's image data.
         # in this case imageData contains the glyph id of the reference glyph
         # get glyph id from glyphname
         self.imageData = struct.pack(
             ">H",
             ttFont.getGlyphID(safeEval("'''" + attrs["glyphname"] +
                                        "'''")))
     elif name == "hexdata":
         self.imageData = readHex(content)
     else:
         from fontemon_blender_addon.fontTools import ttLib
         raise ttLib.TTLibError("can't handle '%s' element" % name)
示例#11
0
 def decompile(self, data, ttFont):
     sstruct.unpack(postFormat, data[:postFormatSize], self)
     data = data[postFormatSize:]
     if self.formatType == 1.0:
         self.decode_format_1_0(data, ttFont)
     elif self.formatType == 2.0:
         self.decode_format_2_0(data, ttFont)
     elif self.formatType == 3.0:
         self.decode_format_3_0(data, ttFont)
     elif self.formatType == 4.0:
         self.decode_format_4_0(data, ttFont)
     else:
         # supported format
         raise ttLib.TTLibError("'post' table format %f not supported" %
                                self.formatType)
示例#12
0
 def compile(self, ttFont):
     data = sstruct.pack(postFormat, self)
     if self.formatType == 1.0:
         pass  # we're done
     elif self.formatType == 2.0:
         data = data + self.encode_format_2_0(ttFont)
     elif self.formatType == 3.0:
         pass  # we're done
     elif self.formatType == 4.0:
         data = data + self.encode_format_4_0(ttFont)
     else:
         # supported format
         raise ttLib.TTLibError("'post' table format %f not supported" %
                                self.formatType)
     return data
示例#13
0
	def fromXML(self, name, attrs, content, ttFont):
		if name =="version":
			setattr(self, name, safeEval(attrs["value"]))
		elif name == "flags":
			setattr(self, name, binary2num(attrs["value"]))
		elif name == "strike":
			current_strike = Strike()
			for element in content:
				if isinstance(element, tuple):
					name, attrs, content = element
					current_strike.fromXML(name, attrs, content, ttFont)
			self.strikes[current_strike.ppem] = current_strike
		else:
			from fontemon_blender_addon.fontTools import ttLib
			raise ttLib.TTLibError("can't handle '%s' element" % name)
示例#14
0
    def compile(self, ttFont):
        metrics = []
        hasNegativeAdvances = False
        for glyphName in ttFont.getGlyphOrder():
            advanceWidth, sideBearing = self.metrics[glyphName]
            if advanceWidth < 0:
                log.error("Glyph %r has negative advance %s" %
                          (glyphName, self.advanceName))
                hasNegativeAdvances = True
            metrics.append([advanceWidth, sideBearing])

        headerTable = ttFont.get(self.headerTag)
        if headerTable is not None:
            lastAdvance = metrics[-1][0]
            lastIndex = len(metrics)
            while metrics[lastIndex - 2][0] == lastAdvance:
                lastIndex -= 1
                if lastIndex <= 1:
                    # all advances are equal
                    lastIndex = 1
                    break
            additionalMetrics = metrics[lastIndex:]
            additionalMetrics = [otRound(sb) for _, sb in additionalMetrics]
            metrics = metrics[:lastIndex]
            numberOfMetrics = len(metrics)
            setattr(headerTable, self.numberOfMetricsName, numberOfMetrics)
        else:
            # no hhea/vhea, can't store numberOfMetrics; assume == numGlyphs
            numberOfMetrics = ttFont["maxp"].numGlyphs
            additionalMetrics = []

        allMetrics = []
        for advance, sb in metrics:
            allMetrics.extend([otRound(advance), otRound(sb)])
        metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
        try:
            data = struct.pack(metricsFmt, *allMetrics)
        except struct.error as e:
            if "out of range" in str(e) and hasNegativeAdvances:
                raise ttLib.TTLibError(
                    "'%s' table can't contain negative advance %ss" %
                    (self.tableTag, self.advanceName))
            else:
                raise
        additionalMetrics = array.array("h", additionalMetrics)
        if sys.byteorder != "big": additionalMetrics.byteswap()
        data = data + additionalMetrics.tobytes()
        return data
def openTTFonts(path):
    """Given a pathname, return a list of TTFont objects. In the case
	of a flat TTF/OTF file, the list will contain just one font object;
	but in the case of a Mac font suitcase it will contain as many
	font objects as there are sfnt resources in the file.
	"""
    from fontemon_blender_addon.fontTools import ttLib
    fonts = []
    sfnts = getSFNTResIndices(path)
    if not sfnts:
        fonts.append(ttLib.TTFont(path))
    else:
        for index in sfnts:
            fonts.append(ttLib.TTFont(path, index))
        if not fonts:
            raise ttLib.TTLibError("no fonts found in file '%s'" % path)
    return fonts
示例#16
0
	def decompile(self, data, ttFont):
		dummy, data = sstruct.unpack2(OS2_format_0, data, self)

		if self.version == 1:
			dummy, data = sstruct.unpack2(OS2_format_1_addition, data, self)
		elif self.version in (2, 3, 4):
			dummy, data = sstruct.unpack2(OS2_format_2_addition, data, self)
		elif self.version == 5:
			dummy, data = sstruct.unpack2(OS2_format_5_addition, data, self)
			self.usLowerOpticalPointSize /= 20
			self.usUpperOpticalPointSize /= 20
		elif self.version != 0:
			from fontemon_blender_addon.fontTools import ttLib
			raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
		if len(data):
			log.warning("too much 'OS/2' table data")

		self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
示例#17
0
    def decompile(self, data, ttFont):
        numGlyphs = ttFont['maxp'].numGlyphs
        headerTable = ttFont.get(self.headerTag)
        if headerTable is not None:
            numberOfMetrics = int(
                getattr(headerTable, self.numberOfMetricsName))
        else:
            numberOfMetrics = numGlyphs
        if numberOfMetrics > numGlyphs:
            log.warning("The %s.%s exceeds the maxp.numGlyphs" %
                        (self.headerTag, self.numberOfMetricsName))
            numberOfMetrics = numGlyphs
        if len(data) < 4 * numberOfMetrics:
            raise ttLib.TTLibError("not enough '%s' table data" %
                                   self.tableTag)
        # Note: advanceWidth is unsigned, but some font editors might
        # read/write as signed. We can't be sure whether it was a mistake
        # or not, so we read as unsigned but also issue a warning...
        metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
        metrics = struct.unpack(metricsFmt, data[:4 * numberOfMetrics])
        data = data[4 * numberOfMetrics:]
        numberOfSideBearings = numGlyphs - numberOfMetrics
        sideBearings = array.array("h", data[:2 * numberOfSideBearings])
        data = data[2 * numberOfSideBearings:]

        if sys.byteorder != "big": sideBearings.byteswap()
        if data:
            log.warning("too much '%s' table data" % self.tableTag)
        self.metrics = {}
        glyphOrder = ttFont.getGlyphOrder()
        for i in range(numberOfMetrics):
            glyphName = glyphOrder[i]
            advanceWidth, lsb = metrics[i * 2:i * 2 + 2]
            if advanceWidth > 32767:
                log.warning(
                    "Glyph %r has a huge advance %s (%d); is it intentional or "
                    "an (invalid) negative value?", glyphName,
                    self.advanceName, advanceWidth)
            self.metrics[glyphName] = (advanceWidth, lsb)
        lastAdvance = metrics[-2]
        for i in range(numberOfSideBearings):
            glyphName = glyphOrder[i + numberOfMetrics]
            self.metrics[glyphName] = (lastAdvance, sideBearings[i])
示例#18
0
 def compile(self, ttFont):
     if not (self.version == 0 or self.version == 1):
         from fontemon_blender_addon.fontTools import ttLib
         raise ttLib.TTLibError(
             "unknown format for VDMX table: version %s" % self.version)
     data = sstruct.pack(VDMX_HeaderFmt, self)
     for ratio in self.ratRanges:
         data += sstruct.pack(VDMX_RatRangeFmt, ratio)
     # recalculate offsets to VDMX groups
     for offset in self._getOffsets():
         data += struct.pack('>H', offset)
     for group in self.groups:
         recs = len(group)
         startsz = min(group.keys())
         endsz = max(group.keys())
         gHeader = {'recs': recs, 'startsz': startsz, 'endsz': endsz}
         data += sstruct.pack(VDMX_GroupFmt, gHeader)
         for yPelHeight, (yMax, yMin) in sorted(group.items()):
             vTable = {'yPelHeight': yPelHeight, 'yMax': yMax, 'yMin': yMin}
             data += sstruct.pack(VDMX_vTableFmt, vTable)
     return data
示例#19
0
 def fromXML(self, name, attrs, content, ttFont):
     from fontemon_blender_addon.fontTools.misc.textTools import readHex
     from fontemon_blender_addon.fontTools import ttLib
     if name != "hexdata":
         raise ttLib.TTLibError("can't handle '%s' element" % name)
     self.decompile(readHex(content), ttFont)
示例#20
0
 def decompile(self, data, ttFont):
     totalLength = len(data)
     indextable = ttFont[self.indextable]
     for indices, isExtra in zip(
         (indextable.indices, indextable.extra_indices), (False, True)):
         programs = {}
         for i, (glyphID, textLength, textOffset) in enumerate(indices):
             if isExtra:
                 name = self.extras[glyphID]
             else:
                 name = ttFont.getGlyphName(glyphID)
             if textOffset > totalLength:
                 self.log.warning("textOffset > totalLength; %r skipped" %
                                  name)
                 continue
             if textLength < 0x8000:
                 # If the length stored in the record is less than 32768, then use
                 # that as the length of the record.
                 pass
             elif textLength == 0x8000:
                 # If the length is 32768, compute the actual length as follows:
                 isLast = i == (len(indices) - 1)
                 if isLast:
                     if isExtra:
                         # For the last "extra" record (the very last record of the
                         # table), the length is the difference between the total
                         # length of the TSI1 table and the textOffset of the final
                         # record.
                         nextTextOffset = totalLength
                     else:
                         # For the last "normal" record (the last record just prior
                         # to the record containing the "magic number"), the length
                         # is the difference between the textOffset of the record
                         # following the "magic number" (0xFFFE) record (i.e. the
                         # first "extra" record), and the textOffset of the last
                         # "normal" record.
                         nextTextOffset = indextable.extra_indices[0][2]
                 else:
                     # For all other records with a length of 0x8000, the length is
                     # the difference between the textOffset of the record in
                     # question and the textOffset of the next record.
                     nextTextOffset = indices[i + 1][2]
                 assert nextTextOffset >= textOffset, "entries not sorted by offset"
                 if nextTextOffset > totalLength:
                     self.log.warning(
                         "nextTextOffset > totalLength; %r truncated" %
                         name)
                     nextTextOffset = totalLength
                 textLength = nextTextOffset - textOffset
             else:
                 from fontemon_blender_addon.fontTools import ttLib
                 raise ttLib.TTLibError(
                     "%r textLength (%d) must not be > 32768" %
                     (name, textLength))
             text = data[textOffset:textOffset + textLength]
             assert len(text) == textLength
             text = tounicode(text, encoding='utf-8')
             if text:
                 programs[name] = text
         if isExtra:
             self.extraPrograms = programs
         else:
             self.glyphPrograms = programs