def compile(self, ttFont): keys = sorted(self.data.keys()) headerSize = sstruct.calcsize(META_HEADER_FORMAT) dataOffset = headerSize + len(keys) * sstruct.calcsize(DATA_MAP_FORMAT) header = sstruct.pack(META_HEADER_FORMAT, { "version": 1, "flags": 0, "dataOffset": dataOffset, "numDataMaps": len(keys) }) dataMaps = [] dataBlocks = [] for tag in keys: if tag in ["dlng", "slng"]: data = self.data[tag].encode("utf-8") else: data = self.data[tag] dataMaps.append(sstruct.pack(DATA_MAP_FORMAT, { "tag": tag, "dataOffset": dataOffset, "dataLength": len(data) })) dataBlocks.append(data) dataOffset += len(data) return bytesjoin([header] + dataMaps + dataBlocks)
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)
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 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, axisTags): sstruct.unpack2(FVAR_INSTANCE_FORMAT, data, self) pos = sstruct.calcsize(FVAR_INSTANCE_FORMAT) for axis in axisTags: value = struct.unpack(">l", data[pos:pos + 4])[0] self.coordinates[axis] = fi2fl(value, 16) pos += 4 if pos + 2 <= len(data): self.postscriptNameID = struct.unpack(">H", data[pos:pos + 2])[0] else: self.postscriptNameID = 0xFFFF
def decompile(self, data, ttFont): sstruct.unpack2(Silf_hdr_format, data, self) self.version = float(floatToFixedToStr(self.version, precisionBits=16)) if self.version >= 5.0: (data, self.scheme) = grUtils.decompress(data) sstruct.unpack2(Silf_hdr_format_3, data, self) base = sstruct.calcsize(Silf_hdr_format_3) elif self.version < 3.0: self.numSilf = struct.unpack('>H', data[4:6]) self.scheme = 0 self.compilerVersion = 0 base = 8 else: self.scheme = 0 sstruct.unpack2(Silf_hdr_format_3, data, self) base = sstruct.calcsize(Silf_hdr_format_3) silfoffsets = struct.unpack_from(('>%dL' % self.numSilf), data[base:]) for offset in silfoffsets: s = Silf() self.silfs.append(s) s.decompile(data[offset:], ttFont, self.version)
def compile(self, ttFont): instanceSize = sstruct.calcsize(FVAR_INSTANCE_FORMAT) + ( len(self.axes) * 4) includePostScriptNames = any(instance.postscriptNameID != 0xFFFF for instance in self.instances) if includePostScriptNames: instanceSize += 2 header = { "version": 0x00010000, "offsetToData": sstruct.calcsize(FVAR_HEADER_FORMAT), "countSizePairs": 2, "axisCount": len(self.axes), "axisSize": sstruct.calcsize(FVAR_AXIS_FORMAT), "instanceCount": len(self.instances), "instanceSize": instanceSize, } result = [sstruct.pack(FVAR_HEADER_FORMAT, header)] result.extend([axis.compile() for axis in self.axes]) axisTags = [axis.axisTag for axis in self.axes] for instance in self.instances: result.append(instance.compile(axisTags, includePostScriptNames)) return bytesjoin(result)
def compile(self, ttFont, base, version=2.0): # build it all up backwards oActions = reduce(lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.actions + [b""], (0, []))[1] oConstraints = reduce(lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.ruleConstraints + [b""], (1, []))[1] constraintCode = b"\000" + b"".join(self.ruleConstraints) transes = [] for t in self.stateTrans: if sys.byteorder != "big": t.byteswap() transes.append(t.tobytes()) if sys.byteorder != "big": t.byteswap() if not len(transes): self.startStates = [0] oRuleMap = reduce(lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.rules + [[]], (0, []))[1] passRanges = [] gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()]) for e in grUtils.entries(gidcolmap, sameval=True): if e[1]: passRanges.append((e[0], e[0] + e[1] - 1, e[2][0])) self.numRules = len(self.actions) self.fsmOffset = (sstruct.calcsize(Silf_pass_format) + 8 + len(passRanges) * 6 + len(oRuleMap) * 2 + 2 * oRuleMap[-1] + 2 + 2 * len(self.startStates) + 3 * self.numRules + 3 + 4 * self.numRules + 4) self.pcCode = self.fsmOffset + 2 * self.numTransitional * self.numColumns + 1 + base self.rcCode = self.pcCode + len(self.passConstraints) self.aCode = self.rcCode + len(constraintCode) self.oDebug = 0 # now generate output data = sstruct.pack(Silf_pass_format, self) data += grUtils.bininfo(len(passRanges), 6) data += b"".join(struct.pack(">3H", *p) for p in passRanges) data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap) flatrules = reduce(lambda a, x: a + x, self.rules, []) data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules) data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext) data += struct.pack((">%dH" % len(self.startStates)), *self.startStates) data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys) data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts) data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints)) data += struct.pack((">%dH" % (self.numRules + 1)), *oConstraints) data += struct.pack((">%dH" % (self.numRules + 1)), *oActions) return data + b"".join(transes) + struct.pack("B", 0) + \ self.passConstraints + constraintCode + b"".join(self.actions)
def _getOffsets(self): """ Calculate offsets to VDMX_Group records. For each ratRange return a list of offset values from the beginning of the VDMX table to a VDMX_Group. """ lenHeader = sstruct.calcsize(VDMX_HeaderFmt) lenRatRange = sstruct.calcsize(VDMX_RatRangeFmt) lenOffset = struct.calcsize('>H') lenGroupHeader = sstruct.calcsize(VDMX_GroupFmt) lenVTable = sstruct.calcsize(VDMX_vTableFmt) # offset to the first group pos = lenHeader + self.numRatios * lenRatRange + self.numRatios * lenOffset groupOffsets = [] for group in self.groups: groupOffsets.append(pos) lenGroup = lenGroupHeader + len(group) * lenVTable pos += lenGroup # offset to next group offsets = [] for ratio in self.ratRanges: groupIndex = ratio['groupIndex'] offsets.append(groupOffsets[groupIndex]) return offsets
def compile(self, ttFont, version=2.0): self.numPasses = len(self.passes) self.numJLevels = len(self.jLevels) self.numCritFeatures = len(self.critFeatures) numPseudo = len(self.pMap) data = b"" if version >= 3.0: hdroffset = sstruct.calcsize(Silf_part1_format_v3) else: hdroffset = 0 data += sstruct.pack(Silf_part1_format, self) for j in self.jLevels: data += sstruct.pack(Silf_justify_format, j) data += sstruct.pack(Silf_part2_format, self) if self.numCritFeatures: data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures) data += struct.pack("BB", 0, len(self.scriptTags)) if len(self.scriptTags): tdata = [ struct.pack("4s", x.encode("ascii")) for x in self.scriptTags ] data += b"".join(tdata) data += struct.pack(">H", self.lbGID) self.passOffset = len(data) data1 = grUtils.bininfo(numPseudo, 6) currpos = hdroffset + len(data) + 4 * (self.numPasses + 1) self.pseudosOffset = currpos + len(data1) for u, p in sorted(self.pMap.items()): data1 += struct.pack((">LH" if version >= 3.0 else ">HH"), u, ttFont.getGlyphID(p)) data1 += self.classes.compile(ttFont, version) currpos += len(data1) data2 = b"" datao = b"" for i, p in enumerate(self.passes): base = currpos + len(data2) datao += struct.pack(">L", base) data2 += p.compile(ttFont, base, version) datao += struct.pack(">L", currpos + len(data2)) if version >= 3.0: data3 = sstruct.pack(Silf_part1_format_v3, self) else: data3 = b"" return data3 + data + datao + data1 + data2
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): 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 compile(self, ttFont): packed = sstruct.pack(DSIG_HeaderFormat, self) headers = [packed] offset = len( packed) + self.usNumSigs * sstruct.calcsize(DSIG_SignatureFormat) data = [] for sigrec in self.signatureRecords: # first pack signature block sigrec.cbSignature = len(sigrec.pkcs7) packed = sstruct.pack(DSIG_SignatureBlockFormat, sigrec) + sigrec.pkcs7 data.append(packed) # update redundant length field sigrec.ulLength = len(packed) # update running table offset sigrec.ulOffset = offset headers.append(sstruct.pack(DSIG_SignatureFormat, sigrec)) offset += sigrec.ulLength if offset % 2: # Pad to even bytes data.append(b'\0') return bytesjoin(headers + data)
def compile(self, ttFont): if not hasattr(self, "names"): # only happens when there are NO name table entries read # from the TTX file self.names = [] names = self.names names.sort() # sort according to the spec; see NameRecord.__lt__() stringData = b"" format = 0 n = len(names) stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat) data = struct.pack(b">HHH", format, n, stringOffset) lastoffset = 0 done = {} # remember the data so we can reuse the "pointers" for name in names: string = name.toBytes() if string in done: name.offset, name.length = done[string] else: name.offset, name.length = done[string] = len(stringData), len(string) stringData = bytesjoin([stringData, string]) data = data + sstruct.pack(nameRecordFormat, name) return data + stringData
import array postFormat = """ > formatType: 16.16F italicAngle: 16.16F # italic angle in degrees underlinePosition: h underlineThickness: h isFixedPitch: L minMemType42: L # minimum memory if TrueType font is downloaded maxMemType42: L # maximum memory if TrueType font is downloaded minMemType1: L # minimum memory if Type1 font is downloaded maxMemType1: L # maximum memory if Type1 font is downloaded """ postFormatSize = sstruct.calcsize(postFormat) class table__p_o_s_t(DefaultTable.DefaultTable): 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:
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') ResourceForkHeader = """ > # big endian dataOffset: L mapOffset: L dataLen: L mapLen: L """ ResourceForkHeaderSize = sstruct.calcsize(ResourceForkHeader) ResourceMapHeader = """ > # big endian attr: H typeListOffset: H nameListOffset: H """ ResourceMapHeaderSize = sstruct.calcsize(ResourceMapHeader) ResourceTypeItem = """ > # big endian type: 4s numRes: H refListOffset: H
from fontemon_blender_addon.fontTools.ttLib.tables.TupleVariation import \ compileTupleVariationStore, decompileTupleVariationStore, TupleVariation # https://www.microsoft.com/typography/otspec/cvar.htm # https://www.microsoft.com/typography/otspec/otvarcommonformats.htm # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cvar.html CVAR_HEADER_FORMAT = """ > # big endian majorVersion: H minorVersion: H tupleVariationCount: H offsetToData: H """ CVAR_HEADER_SIZE = sstruct.calcsize(CVAR_HEADER_FORMAT) class table__c_v_a_r(DefaultTable.DefaultTable): dependencies = ["cvt ", "fvar"] def __init__(self, tag=None): DefaultTable.DefaultTable.__init__(self, tag) self.majorVersion, self.minorVersion = 1, 0 self.variations = [] def compile(self, ttFont, useSharedPoints=False): tupleVariationCount, tuples, data = compileTupleVariationStore( variations=[v for v in self.variations if v.hasImpact()], pointCount=len(ttFont["cvt "].values), axisTags=[axis.axisTag for axis in ttFont["fvar"].axes],
import logging log = logging.getLogger(__name__) nameRecordFormat = """ > # big endian platformID: H platEncID: H langID: H nameID: H length: H offset: H """ nameRecordSize = sstruct.calcsize(nameRecordFormat) class table__n_a_m_e(DefaultTable.DefaultTable): dependencies = ["ltag"] def decompile(self, data, ttFont): format, n, stringOffset = struct.unpack(b">HHH", data[:6]) expectedStringOffset = 6 + n * nameRecordSize if stringOffset != expectedStringOffset: log.error( "'name' table stringOffset incorrect. Expected: %s; Actual: %s", expectedStringOffset, stringOffset) stringData = data[stringOffset:] data = data[6:] self.names = []
the number of <colorParamUINameID> elements. """ XML = ET.XML XMLElement = ET.Element xmlToString = ET.tostring SVG_format_0 = """ > # big endian version: H offsetToSVGDocIndex: L offsetToColorPalettes: L """ SVG_format_0Size = sstruct.calcsize(SVG_format_0) SVG_format_1 = """ > # big endian version: H numIndicies: H """ SVG_format_1Size = sstruct.calcsize(SVG_format_1) doc_index_entry_format_0 = """ > # big endian startGlyphID: H endGlyphID: H svgDocOffset: L svgDocLength: L
# -- sfnt directory helpers and cruft ttcHeaderFormat = """ > # big endian TTCTag: 4s # "ttcf" Version: L # 0x00010000 or 0x00020000 numFonts: L # number of fonts # OffsetTable[numFonts]: L # array with offsets from beginning of file # ulDsigTag: L # version 2.0 only # ulDsigLength: L # version 2.0 only # ulDsigOffset: L # version 2.0 only """ ttcHeaderSize = sstruct.calcsize(ttcHeaderFormat) sfntDirectoryFormat = """ > # big endian sfntVersion: 4s numTables: H # number of tables searchRange: H # (max2 <= numTables)*16 entrySelector: H # log2(max2 <= numTables) rangeShift: H # numTables*16-searchRange """ sfntDirectorySize = sstruct.calcsize(sfntDirectoryFormat) sfntDirectoryEntryFormat = """ > # big endian tag: 4s
import logging log = logging.getLogger(__name__) # Apple's documentation of 'avar': # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html AVAR_HEADER_FORMAT = """ > # big endian majorVersion: H minorVersion: H reserved: H axisCount: H """ assert sstruct.calcsize(AVAR_HEADER_FORMAT) == 8, sstruct.calcsize(AVAR_HEADER_FORMAT) class table__a_v_a_r(DefaultTable.DefaultTable): dependencies = ["fvar"] def __init__(self, tag=None): DefaultTable.DefaultTable.__init__(self, tag) self.segments = {} def compile(self, ttFont): axisTags = [axis.axisTag for axis in ttFont["fvar"].axes] header = { "majorVersion": 1, "minorVersion": 0,
import struct from collections.abc import MutableMapping # Apple's documentation of 'trak': # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html TRAK_HEADER_FORMAT = """ > # big endian version: 16.16F format: H horizOffset: H vertOffset: H reserved: H """ TRAK_HEADER_FORMAT_SIZE = sstruct.calcsize(TRAK_HEADER_FORMAT) TRACK_DATA_FORMAT = """ > # big endian nTracks: H nSizes: H sizeTableOffset: L """ TRACK_DATA_FORMAT_SIZE = sstruct.calcsize(TRACK_DATA_FORMAT) TRACK_TABLE_ENTRY_FORMAT = """ > # big endian track: 16.16F nameIndex: H offset: H
sbixStrikeHeaderFormat = """ > ppem: H # The PPEM for which this strike was designed (e.g., 9, # 12, 24) resolution: H # The screen resolution (in dpi) for which this strike # was designed (e.g., 72) """ sbixGlyphDataOffsetFormat = """ > glyphDataOffset: L # Offset from the beginning of the strike data record # to data for the individual glyph """ sbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat) sbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat) class Strike(object): def __init__(self, rawdata=None, ppem=0, resolution=72): self.data = rawdata self.ppem = ppem self.resolution = resolution self.glyphs = {} def decompile(self, ttFont): if self.data is None: from fontemon_blender_addon.fontTools import ttLib raise ttLib.TTLibError if len(self.data) < sbixStrikeHeaderFormatSize:
import struct sbixGlyphHeaderFormat = """ > originOffsetX: h # The x-value of the point in the glyph relative to its # lower-left corner which corresponds to the origin of # the glyph on the screen, that is the point on the # baseline at the left edge of the glyph. originOffsetY: h # The y-value of the point in the glyph relative to its # lower-left corner which corresponds to the origin of # the glyph on the screen, that is the point on the # baseline at the left edge of the glyph. graphicType: 4s # e.g. "png " """ sbixGlyphHeaderFormatSize = sstruct.calcsize(sbixGlyphHeaderFormat) class Glyph(object): 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
def compile(self, ttFont): dataList = [] self.numSizes = len(self.strikes) dataList.append(sstruct.pack(eblcHeaderFormat, self)) # Data size of the header + bitmapSizeTable needs to be calculated # in order to form offsets. This value will hold the size of the data # in dataList after all the data is consolidated in dataList. dataSize = len(dataList[0]) # The table will be structured in the following order: # (0) header # (1) Each bitmapSizeTable [1 ... self.numSizes] # (2) Alternate between indexSubTableArray and indexSubTable # for each bitmapSizeTable present. # # The issue is maintaining the proper offsets when table information # gets moved around. All offsets and size information must be recalculated # when building the table to allow editing within ttLib and also allow easy # import/export to and from XML. All of this offset information is lost # when exporting to XML so everything must be calculated fresh so importing # from XML will work cleanly. Only byte offset and size information is # calculated fresh. Count information like numberOfIndexSubTables is # checked through assertions. If the information in this table was not # touched or was changed properly then these types of values should match. # # The table will be rebuilt the following way: # (0) Precompute the size of all the bitmapSizeTables. This is needed to # compute the offsets properly. # (1) For each bitmapSizeTable compute the indexSubTable and # indexSubTableArray pair. The indexSubTable must be computed first # so that the offset information in indexSubTableArray can be # calculated. Update the data size after each pairing. # (2) Build each bitmapSizeTable. # (3) Consolidate all the data into the main dataList in the correct order. for _ in self.strikes: dataSize += sstruct.calcsize(bitmapSizeTableFormatPart1) dataSize += len( ('hori', 'vert')) * sstruct.calcsize(sbitLineMetricsFormat) dataSize += sstruct.calcsize(bitmapSizeTableFormatPart2) indexSubTablePairDataList = [] for curStrike in self.strikes: curTable = curStrike.bitmapSizeTable curTable.numberOfIndexSubTables = len(curStrike.indexSubTables) curTable.indexSubTableArrayOffset = dataSize # Precompute the size of the indexSubTableArray. This information # is important for correctly calculating the new value for # additionalOffsetToIndexSubtable. sizeOfSubTableArray = curTable.numberOfIndexSubTables * indexSubTableArraySize lowerBound = dataSize dataSize += sizeOfSubTableArray upperBound = dataSize indexSubTableDataList = [] for indexSubTable in curStrike.indexSubTables: indexSubTable.additionalOffsetToIndexSubtable = dataSize - curTable.indexSubTableArrayOffset glyphIds = list(map(ttFont.getGlyphID, indexSubTable.names)) indexSubTable.firstGlyphIndex = min(glyphIds) indexSubTable.lastGlyphIndex = max(glyphIds) data = indexSubTable.compile(ttFont) indexSubTableDataList.append(data) dataSize += len(data) curTable.startGlyphIndex = min(ist.firstGlyphIndex for ist in curStrike.indexSubTables) curTable.endGlyphIndex = max(ist.lastGlyphIndex for ist in curStrike.indexSubTables) for i in curStrike.indexSubTables: data = struct.pack(indexSubHeaderFormat, i.firstGlyphIndex, i.lastGlyphIndex, i.additionalOffsetToIndexSubtable) indexSubTablePairDataList.append(data) indexSubTablePairDataList.extend(indexSubTableDataList) curTable.indexTablesSize = dataSize - curTable.indexSubTableArrayOffset for curStrike in self.strikes: curTable = curStrike.bitmapSizeTable data = sstruct.pack(bitmapSizeTableFormatPart1, curTable) dataList.append(data) for metric in ('hori', 'vert'): metricObj = vars(curTable)[metric] data = sstruct.pack(sbitLineMetricsFormat, metricObj) dataList.append(data) data = sstruct.pack(bitmapSizeTableFormatPart2, curTable) dataList.append(data) dataList.extend(indexSubTablePairDataList) return bytesjoin(dataList)
from .sbixStrike import * sbixHeaderFormat = """ > version: H # Version number (set to 1) flags: H # The only two bits used in the flags field are bits 0 # and 1. For historical reasons, bit 0 must always be 1. # Bit 1 is a sbixDrawOutlines flag and is interpreted as # follows: # 0: Draw only 'sbix' bitmaps # 1: Draw both 'sbix' bitmaps and outlines, in that # order numStrikes: L # Number of bitmap strikes to follow """ sbixHeaderFormatSize = sstruct.calcsize(sbixHeaderFormat) sbixStrikeOffsetFormat = """ > strikeOffset: L # Offset from begining of table to data for the # individual strike """ sbixStrikeOffsetFormatSize = sstruct.calcsize(sbixStrikeOffsetFormat) class table__s_b_i_x(DefaultTable.DefaultTable): def __init__(self, tag=None): DefaultTable.DefaultTable.__init__(self, tag) self.version = 1