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
def decompile(self, ttFont): if self.data is None: from fontemon_blender_addon.fontTools import ttLib raise ttLib.TTLibError if len(self.data) < sbixStrikeHeaderFormatSize: from fontemon_blender_addon.fontTools import ttLib raise(ttLib.TTLibError, "Strike header too short: Expected %x, got %x.") \ % (sbixStrikeHeaderFormatSize, len(self.data)) # read Strike header from raw data sstruct.unpack(sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self) # calculate number of glyphs firstGlyphDataOffset, = struct.unpack(">L", \ self.data[sbixStrikeHeaderFormatSize:sbixStrikeHeaderFormatSize + sbixGlyphDataOffsetFormatSize]) self.numGlyphs = (firstGlyphDataOffset - sbixStrikeHeaderFormatSize) // sbixGlyphDataOffsetFormatSize - 1 # ^ -1 because there's one more offset than glyphs # build offset list for single glyph data offsets self.glyphDataOffsets = [] for i in range(self.numGlyphs + 1): # + 1 because there's one more offset than glyphs start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize current_offset, = struct.unpack(">L", self.data[start:start + sbixGlyphDataOffsetFormatSize]) self.glyphDataOffsets.append(current_offset) # iterate through offset list and slice raw data into glyph data records for i in range(self.numGlyphs): current_glyph = Glyph(rawdata=self.data[self.glyphDataOffsets[i]:self.glyphDataOffsets[i+1]], gid=i) current_glyph.decompile(ttFont) self.glyphs[current_glyph.glyphName] = current_glyph del self.glyphDataOffsets del self.numGlyphs del self.data
def decompile(self, data, ttFont): axisTags = [axis.axisTag for axis in ttFont["fvar"].axes] glyphs = ttFont.getGlyphOrder() sstruct.unpack(GVAR_HEADER_FORMAT, data[0:GVAR_HEADER_SIZE], self) assert len(glyphs) == self.glyphCount assert len(axisTags) == self.axisCount offsets = self.decompileOffsets_(data[GVAR_HEADER_SIZE:], tableFormat=(self.flags & 1), glyphCount=self.glyphCount) sharedCoords = tv.decompileSharedTuples(axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples) self.variations = {} offsetToData = self.offsetToGlyphVariationData for i in range(self.glyphCount): glyphName = glyphs[i] glyph = ttFont["glyf"][glyphName] numPointsInGlyph = self.getNumPoints_(glyph) gvarData = data[offsetToData + offsets[i]:offsetToData + offsets[i + 1]] try: self.variations[glyphName] = decompileGlyph_( numPointsInGlyph, sharedCoords, axisTags, gvarData) except Exception: log.error( "Failed to decompile deltas for glyph '%s' (%d points)", glyphName, numPointsInGlyph, ) raise
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
def decompile(self, data, ttFont): sstruct.unpack(TRAK_HEADER_FORMAT, data[:TRAK_HEADER_FORMAT_SIZE], self) for direction in ('horiz', 'vert'): trackData = TrackData() offset = getattr(self, direction + 'Offset') if offset != 0: trackData.decompile(data, offset) setattr(self, direction + 'Data', trackData)
def _readHeaderAndMap(self): self.file.seek(0) headerData = self._read(ResourceForkHeaderSize) sstruct.unpack(ResourceForkHeader, headerData, self) # seek to resource map, skip reserved mapOffset = self.mapOffset + 22 resourceMapData = self._read(ResourceMapHeaderSize, mapOffset) sstruct.unpack(ResourceMapHeader, resourceMapData, self) self.absTypeListOffset = self.mapOffset + self.typeListOffset self.absNameListOffset = self.mapOffset + self.nameListOffset
def decompile(self, data, ttFont): # Save the original data because offsets are from the start of the table. origData = data i = 0 dummy = sstruct.unpack(eblcHeaderFormat, data[:8], self) i += 8 self.strikes = [] for curStrikeIndex in range(self.numSizes): curStrike = Strike() self.strikes.append(curStrike) curTable = curStrike.bitmapSizeTable dummy = sstruct.unpack2(bitmapSizeTableFormatPart1, data[i:i + 16], curTable) i += 16 for metric in ('hori', 'vert'): metricObj = SbitLineMetrics() vars(curTable)[metric] = metricObj dummy = sstruct.unpack2(sbitLineMetricsFormat, data[i:i + 12], metricObj) i += 12 dummy = sstruct.unpack(bitmapSizeTableFormatPart2, data[i:i + 8], curTable) i += 8 for curStrike in self.strikes: curTable = curStrike.bitmapSizeTable for subtableIndex in range(curTable.numberOfIndexSubTables): i = curTable.indexSubTableArrayOffset + subtableIndex * indexSubTableArraySize tup = struct.unpack(indexSubTableArrayFormat, data[i:i + indexSubTableArraySize]) (firstGlyphIndex, lastGlyphIndex, additionalOffsetToIndexSubtable) = tup i = curTable.indexSubTableArrayOffset + additionalOffsetToIndexSubtable tup = struct.unpack(indexSubHeaderFormat, data[i:i + indexSubHeaderSize]) (indexFormat, imageFormat, imageDataOffset) = tup indexFormatClass = self.getIndexFormatClass(indexFormat) indexSubTable = indexFormatClass(data[i + indexSubHeaderSize:], ttFont) indexSubTable.firstGlyphIndex = firstGlyphIndex indexSubTable.lastGlyphIndex = lastGlyphIndex indexSubTable.additionalOffsetToIndexSubtable = additionalOffsetToIndexSubtable indexSubTable.indexFormat = indexFormat indexSubTable.imageFormat = imageFormat indexSubTable.imageDataOffset = imageDataOffset indexSubTable.decompile( ) # https://github.com/fontemon_blender_addon.fontTools/fontemon_blender_addon.fontTools/issues/317 curStrike.indexSubTables.append(indexSubTable)
def decompile(self, data, ttFont, version=2.0): if version >= 3.0: _, data = sstruct.unpack2(Silf_part1_format_v3, data, self) self.ruleVersion = float( floatToFixedToStr(self.ruleVersion, precisionBits=16)) _, data = sstruct.unpack2(Silf_part1_format, data, self) for jlevel in range(self.numJLevels): j, data = sstruct.unpack2(Silf_justify_format, data, _Object()) self.jLevels.append(j) _, data = sstruct.unpack2(Silf_part2_format, data, self) if self.numCritFeatures: self.critFeatures = struct.unpack_from( ('>%dH' % self.numCritFeatures), data) data = data[self.numCritFeatures * 2 + 1:] (numScriptTag, ) = struct.unpack_from('B', data) if numScriptTag: self.scriptTags = [ struct.unpack("4s", data[x:x + 4])[0].decode("ascii") for x in range(1, 1 + 4 * numScriptTag, 4) ] data = data[1 + 4 * numScriptTag:] (self.lbGID, ) = struct.unpack('>H', data[:2]) if self.numPasses: self.oPasses = struct.unpack(('>%dL' % (self.numPasses + 1)), data[2:6 + 4 * self.numPasses]) data = data[6 + 4 * self.numPasses:] (numPseudo, ) = struct.unpack(">H", data[:2]) for i in range(numPseudo): if version >= 3.0: pseudo = sstruct.unpack(Silf_pseudomap_format, data[8 + 6 * i:14 + 6 * i], _Object()) else: pseudo = sstruct.unpack(Silf_pseudomap_format_h, data[8 + 4 * i:12 + 4 * i], _Object()) self.pMap[pseudo.unicode] = ttFont.getGlyphName(pseudo.nPseudo) data = data[8 + 6 * numPseudo:] currpos = (sstruct.calcsize(Silf_part1_format) + sstruct.calcsize(Silf_justify_format) * self.numJLevels + sstruct.calcsize(Silf_part2_format) + 2 * self.numCritFeatures + 1 + 1 + 4 * numScriptTag + 6 + 4 * self.numPasses + 8 + 6 * numPseudo) if version >= 3.0: currpos += sstruct.calcsize(Silf_part1_format_v3) self.classes = Classes() self.classes.decompile(data, ttFont, version) for i in range(self.numPasses): p = Pass() self.passes.append(p) p.decompile( data[self.oPasses[i] - currpos:self.oPasses[i + 1] - currpos], ttFont, version)
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
def decompile(self, refData, reader): sstruct.unpack(ResourceRefItem, refData, self) # interpret 3-byte dataOffset as (padded) ULONG to unpack it with struct self.dataOffset, = struct.unpack('>L', bytesjoin([b"\0", self.dataOffset])) absDataOffset = reader.dataOffset + self.dataOffset dataLength, = struct.unpack(">L", reader._read(4, absDataOffset)) self.data = reader._read(dataLength) if self.nameOffset == -1: return absNameOffset = reader.absNameListOffset + self.nameOffset nameLength, = struct.unpack('B', reader._read(1, absNameOffset)) name, = struct.unpack('>%ss' % nameLength, reader._read(nameLength)) self.name = tostr(name, encoding='mac-roman')
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)
def decompile(self, data, ttFont): axisTags = [axis.axisTag for axis in ttFont["fvar"].axes] header = {} sstruct.unpack(CVAR_HEADER_FORMAT, data[0:CVAR_HEADER_SIZE], header) self.majorVersion = header["majorVersion"] self.minorVersion = header["minorVersion"] assert self.majorVersion == 1, self.majorVersion self.variations = decompileTupleVariationStore( tableTag=self.tableTag, axisTags=axisTags, tupleVariationCount=header["tupleVariationCount"], pointCount=len(ttFont["cvt "].values), sharedTuples=None, data=data, pos=CVAR_HEADER_SIZE, dataPos=header["offsetToData"])
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")
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
def _readTypeList(self): absTypeListOffset = self.absTypeListOffset numTypesData = self._read(2, absTypeListOffset) self.numTypes, = struct.unpack('>H', numTypesData) absTypeListOffset2 = absTypeListOffset + 2 for i in range(self.numTypes + 1): resTypeItemOffset = absTypeListOffset2 + ResourceTypeItemSize * i resTypeItemData = self._read(ResourceTypeItemSize, resTypeItemOffset) item = sstruct.unpack(ResourceTypeItem, resTypeItemData) resType = tostr(item['type'], encoding='mac-roman') refListOffset = absTypeListOffset + item['refListOffset'] numRes = item['numRes'] + 1 resources = self._readReferenceList(resType, refListOffset, numRes) self._resources[resType] = resources
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): 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())
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
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)
def decompile(self, data, ttFont): sstruct.unpack(hheaFormat, data, self)
def fromFile(self, file): sstruct.unpack(self.format, file.read(self.formatSize), self)
def fromString(self, str): sstruct.unpack(self.format, str, self)