def parseGDEF(lines, font): container = ttLib.getTableClass('GDEF')() log.debug("Parsing GDEF") self = ot.GDEF() fields = { 'class definition begin': ('GlyphClassDef', lambda lines, font: parseClassDef(lines, font, klass=ot.GlyphClassDef) ), 'attachment list begin': ('AttachList', parseAttachList), 'carets begin': ('LigCaretList', parseCaretList), 'mark attachment class definition begin': ('MarkAttachClassDef', lambda lines, font: parseClassDef( lines, font, klass=ot.MarkAttachClassDef)), 'markfilter set definition begin': ('MarkGlyphSetsDef', parseMarkFilteringSets), } for attr, parser in fields.values(): setattr(self, attr, None) while lines.peek() is not None: typ = lines.peek()[0].lower() if typ not in fields: log.debug('Skipping %s', typ) next(lines) continue attr, parser = fields[typ] assert getattr(self, attr) is None, attr setattr(self, attr, parser(lines, font)) self.Version = 0x00010000 if self.MarkGlyphSetsDef is None else 0x00010002 container.table = self return container
def parseGSUBGPOS(lines, font, tableTag): container = ttLib.getTableClass(tableTag)() lookupMap = DeferredMapping() featureMap = DeferredMapping() assert tableTag in ('GSUB', 'GPOS') log.debug("Parsing %s", tableTag) self = getattr(ot, tableTag)() self.Version = 0x00010000 fields = { 'script table begin': ('ScriptList', lambda lines: parseScriptList(lines, featureMap)), 'feature table begin': ('FeatureList', lambda lines: parseFeatureList(lines, lookupMap, featureMap)), 'lookup': ('LookupList', None), } for attr, parser in fields.values(): setattr(self, attr, None) while lines.peek() is not None: typ = lines.peek()[0].lower() if typ not in fields: log.debug('Skipping %s', lines.peek()) next(lines) continue attr, parser = fields[typ] if typ == 'lookup': if self.LookupList is None: self.LookupList = ot.LookupList() self.LookupList.Lookup = [] _, name, _ = lines.peek() lookup = parseLookup(lines, tableTag, font, lookupMap) if lookupMap is not None: assert name not in lookupMap, "Duplicate lookup name: %s" % name lookupMap[name] = len(self.LookupList.Lookup) else: assert int(name) == len( self.LookupList.Lookup), "%d %d" % (name, len(self.Lookup)) self.LookupList.Lookup.append(lookup) else: assert getattr(self, attr) is None, attr setattr(self, attr, parser(lines)) if self.LookupList: self.LookupList.LookupCount = len(self.LookupList.Lookup) if lookupMap is not None: lookupMap.applyDeferredMappings() if featureMap is not None: featureMap.applyDeferredMappings() container.table = self return container
def parseCmap(lines, font): container = ttLib.getTableClass('cmap')() log.debug("Parsing cmap") tables = [] while lines.peek() is not None: lines.expect('cmap subtable %d' % len(tables)) platId, encId, fmt, lang = [ parseCmapId(lines, field) for field in ('platformID', 'encodingID', 'format', 'language') ] table = cmap_classes[fmt](fmt) table.platformID = platId table.platEncID = encId table.language = lang table.cmap = {} line = next(lines) while line[0] != 'end subtable': table.cmap[int(line[0], 16)] = line[1] line = next(lines) tables.append(table) container.tableVersion = 0 container.tables = tables return container
def merge(self, fontfiles): """Merges fonts together. Args: fontfiles: A list of file names to be merged Returns: A :class:`fontemon_blender_addon.fontTools.ttLib.TTFont` object. Call the ``save`` method on this to write it out to an OTF file. """ mega = ttLib.TTFont() # # Settle on a mega glyph order. # fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles] glyphOrders = [font.getGlyphOrder() for font in fonts] megaGlyphOrder = self._mergeGlyphOrders(glyphOrders) # Reload fonts and set new glyph names on them. # TODO Is it necessary to reload font? I think it is. At least # it's safer, in case tables were loaded to provide glyph names. fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles] for font, glyphOrder in zip(fonts, glyphOrders): font.setGlyphOrder(glyphOrder) mega.setGlyphOrder(megaGlyphOrder) for font in fonts: self._preMerge(font) self.fonts = fonts self.duplicateGlyphsPerFont = [{} for _ in fonts] allTags = reduce(set.union, (list(font.keys()) for font in fonts), set()) allTags.remove('GlyphOrder') # Make sure we process cmap before GSUB as we have a dependency there. if 'GSUB' in allTags: allTags.remove('GSUB') allTags = ['GSUB'] + list(allTags) if 'cmap' in allTags: allTags.remove('cmap') allTags = ['cmap'] + list(allTags) for tag in allTags: with timer("merge '%s'" % tag): tables = [font.get(tag, NotImplemented) for font in fonts] log.info("Merging '%s'.", tag) clazz = ttLib.getTableClass(tag) table = clazz(tag).merge(self, tables) # XXX Clean this up and use: table = mergeObjects(tables) if table is not NotImplemented and table is not False: mega[tag] = table log.info("Merged '%s'.", tag) else: log.info("Dropped '%s'.", tag) del self.duplicateGlyphsPerFont del self.fonts self._postMerge(mega) return mega
@_add_method(DefaultTable, allowDefaultTable=True) def merge(self, m, tables): if not hasattr(self, 'mergeMap'): log.info("Don't know how to merge '%s'.", self.tableTag) return NotImplemented logic = self.mergeMap if isinstance(logic, dict): return m.mergeObjects(self, self.mergeMap, tables) else: return logic(tables) ttLib.getTableClass('maxp').mergeMap = { '*': max, 'tableTag': equal, 'tableVersion': equal, 'numGlyphs': sum, 'maxStorage': first, 'maxFunctionDefs': first, 'maxInstructionDefs': first, # TODO When we correctly merge hinting data, update these values: # maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions } headFlagsMergeBitMap = { 'size': 16, '*': bitwise_or, 1: bitwise_and, # Baseline at y = 0
from fontemon_blender_addon.fontTools.misc.py23 import * from fontemon_blender_addon.fontTools import ttLib superclass = ttLib.getTableClass("fpgm") class table__p_r_e_p(superclass): pass
self.components = components def op_endchar(self, index): args = self.popall() if len(args) >= 4: from fontemon_blender_addon.fontTools.encodings.StandardEncoding import StandardEncoding # endchar can do seac accent bulding; The T2 spec says it's deprecated, # but recent software that shall remain nameless does output it. adx, ady, bchar, achar = args[-4:] baseGlyph = StandardEncoding[bchar] accentGlyph = StandardEncoding[achar] self.components.add(baseGlyph) self.components.add(accentGlyph) @_add_method(ttLib.getTableClass('CFF ')) def closure_glyphs(self, s): cff = self.cff assert len(cff) == 1 font = cff[cff.keys()[0]] glyphSet = font.CharStrings decompose = s.glyphs while decompose: components = set() for g in decompose: if g not in glyphSet: continue gl = glyphSet[g] subrs = getattr(gl.private, "Subrs", [])
from fontemon_blender_addon.fontTools.misc.py23 import * from fontemon_blender_addon.fontTools import ttLib superclass = ttLib.getTableClass("hmtx") class table__v_m_t_x(superclass): headerTag = 'vhea' advanceName = 'height' sideBearingName = 'tsb' numberOfMetricsName = 'numberOfVMetrics'
""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT) tool to store its hinting source data. TSI3 contains the text of the glyph programs in the form of 'VTTTalk' code. """ from fontemon_blender_addon.fontTools.misc.py23 import * from fontemon_blender_addon.fontTools import ttLib superclass = ttLib.getTableClass("TSI1") class table_T_S_I__3(superclass): extras = { 0xfffa: "reserved0", 0xfffb: "reserved1", 0xfffc: "reserved2", 0xfffd: "reserved3" } indextable = "TSI2"
def _startElementHandler(self, name, attrs): if self.stackSize == 1 and self.contentOnly: # We already know the table we're parsing, skip # parsing the table tag and continue to # stack '2' which begins parsing content self.contentStack.append([]) self.stackSize = 2 return stackSize = self.stackSize self.stackSize = stackSize + 1 subFile = attrs.get("src") if subFile is not None: if hasattr(self.file, 'name'): # if file has a name, get its parent directory dirname = os.path.dirname(self.file.name) else: # else fall back to using the current working directory dirname = os.getcwd() subFile = os.path.join(dirname, subFile) if not stackSize: if name != "ttFont": raise TTXParseError("illegal root tag: %s" % name) sfntVersion = attrs.get("sfntVersion") if sfntVersion is not None: if len(sfntVersion) != 4: sfntVersion = safeEval('"' + sfntVersion + '"') self.ttFont.sfntVersion = sfntVersion self.contentStack.append([]) elif stackSize == 1: if subFile is not None: subReader = XMLReader(subFile, self.ttFont, self.progress) subReader.read() self.contentStack.append([]) return tag = ttLib.xmlToTag(name) msg = "Parsing '%s' table..." % tag if self.progress: self.progress.setLabel(msg) log.info(msg) if tag == "GlyphOrder": tableClass = ttLib.GlyphOrder elif "ERROR" in attrs or ('raw' in attrs and safeEval(attrs['raw'])): tableClass = DefaultTable else: tableClass = ttLib.getTableClass(tag) if tableClass is None: tableClass = DefaultTable if tag == 'loca' and tag in self.ttFont: # Special-case the 'loca' table as we need the # original if the 'glyf' table isn't recompiled. self.currentTable = self.ttFont[tag] else: self.currentTable = tableClass(tag) self.ttFont[tag] = self.currentTable self.contentStack.append([]) elif stackSize == 2 and subFile is not None: subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True) subReader.read() self.contentStack.append([]) self.root = subReader.root elif stackSize == 2: self.contentStack.append([]) self.root = (name, attrs, self.contentStack[-1]) else: l = [] self.contentStack[-1].append((name, attrs, l)) self.contentStack.append(l)