def build(instance, opts): font = instance.parent master = font.masters[0] glyphOrder = [] advanceWidths = {} characterMap = {} charStrings = {} colorLayers = {} for glyph in font.glyphs: if not glyph.export: continue name = glyph.name for layer in glyph.layers: if layer.name.startswith("Color "): _, index = layer.name.split(" ") if name not in colorLayers: colorLayers[name] = [] colorLayers[name].append((name, int(index))) glyphOrder.append(name) if glyph.unicode: characterMap[int(glyph.unicode, 16)] = name layer = getLayer(glyph, instance) charStrings[name] = draw(layer, instance).getCharString() advanceWidths[name] = layer.width # XXX glyphOrder.pop(glyphOrder.index(".notdef")) glyphOrder.pop(glyphOrder.index("space")) glyphOrder.insert(0, ".notdef") glyphOrder.insert(1, "space") version = float(opts.version) vendor = font.customParameters["vendorID"] names = { "copyright": font.copyright, "familyName": instance.familyName, "styleName": instance.name, "uniqueFontIdentifier": f"{version:.03f};{vendor};{instance.fontName}", "fullName": instance.fullName, "version": f"Version {version:.03f}", "psName": instance.fontName, "manufacturer": font.manufacturer, "designer": font.designer, "vendorURL": font.manufacturerURL, "designerURL": font.designerURL, "licenseDescription": font.customParameters["license"], "licenseInfoURL": font.customParameters["licenseURL"], "sampleText": font.customParameters["sampleText"], } fb = FontBuilder(font.upm, isTTF=False) fb.updateHead(fontRevision=version) fb.setupGlyphOrder(glyphOrder) fb.setupCharacterMap(characterMap) fb.setupNameTable(names, mac=False) fb.setupHorizontalHeader(ascent=master.ascender, descent=master.descender, lineGap=master.customParameters['hheaLineGap']) if opts.debug: fb.setupCFF(names["psName"], {}, charStrings, {}) else: fb.setupCFF2(charStrings) metrics = {} for name, width in advanceWidths.items(): bounds = charStrings[name].calcBounds(None) or [0] metrics[name] = (width, bounds[0]) fb.setupHorizontalMetrics(metrics) fb.setupPost() fea = makeFeatures(instance, master, opts) fb.addOpenTypeFeatures(fea) palettes = master.customParameters["Color Palettes"] palettes = [[tuple(int(v) / 255 for v in c.split(",")) for c in p] for p in palettes] fb.setupCPAL(palettes) fb.setupCOLR(colorLayers) instance.font = fb.font axes = [ instance.weightValue, instance.widthValue, instance.customValue, instance.customValue1, instance.customValue2, instance.customValue3, ] instance.axes = {} for i, axis in enumerate(font.customParameters["Axes"]): instance.axes[axis["Tag"]] = axes[i] if opts.debug: fb.font.save(f"{instance.fontName}.otf") fb.font.saveXML(f"{instance.fontName}.ttx") return fb.font
def build(instance, opts, glyphOrder): font = instance.parent master = font.masters[0] advanceWidths = {} characterMap = {} charStrings = {} colorLayers = {} for name in glyphOrder: glyph = font.glyphs[name] if not glyph.export: continue for layer in glyph.layers: if "colorPalette" in layer.attributes: index = layer.attributes["colorPalette"] if name not in colorLayers: colorLayers[name] = [] if layer.layerId == layer.associatedMasterId: # master layer colorLayers[name].append((name, int(index))) else: assert False, "can’t handle non-master color layers" if glyph.unicode: characterMap[int(glyph.unicode, 16)] = name layer = getLayer(glyph, instance) charStrings[name] = draw(layer, instance).getCharString() advanceWidths[name] = layer.width # XXX glyphOrder.pop(glyphOrder.index(".notdef")) glyphOrder.pop(glyphOrder.index("space")) glyphOrder.insert(0, ".notdef") glyphOrder.insert(1, "space") version = float(opts.version) vendor = get_property(font, "vendorID") names = { "copyright": font.copyright, "familyName": instance.familyName, "styleName": instance.name, "uniqueFontIdentifier": f"{version:.03f};{vendor};{instance.fontName}", "fullName": instance.fullName, "version": f"Version {version:.03f}", "psName": instance.fontName, "manufacturer": font.manufacturer, "designer": font.designer, "vendorURL": font.manufacturerURL, "designerURL": font.designerURL, "licenseDescription": get_property(font, "licenses"), "licenseInfoURL": get_property(font, "licenseURL"), "sampleText": get_property(font, "sampleTexts"), } fb = FontBuilder(font.upm, isTTF=False) date = font.date.replace(tzinfo=datetime.timezone.utc) stat = opts.glyphs.stat() fb.updateHead( fontRevision=version, created=int(date.timestamp()) - mac_epoch_diff, modified=int(stat.st_mtime) - mac_epoch_diff, ) fb.setupGlyphOrder(glyphOrder) fb.setupCharacterMap(characterMap) fb.setupNameTable(names, mac=False) fb.setupHorizontalHeader( ascent=master.ascender, descent=master.descender, lineGap=master.customParameters["hheaLineGap"], ) if opts.debug: fb.setupCFF(names["psName"], {}, charStrings, {}) fb.font["CFF "].compile(fb.font) else: fb.setupCFF2(charStrings) metrics = {} for name, width in advanceWidths.items(): bounds = charStrings[name].calcBounds(None) or [0] metrics[name] = (width, bounds[0]) fb.setupHorizontalMetrics(metrics) fb.setupPost( underlinePosition=master.customParameters["underlinePosition"], underlineThickness=master.customParameters["underlineThickness"], ) # Compile to get font bbox fb.font["head"].compile(fb.font) codePages = [ CODEPAGE_RANGES[v] for v in font.customParameters["codePageRanges"] ] fb.setupOS2( version=4, sTypoAscender=master.ascender, sTypoDescender=master.descender, sTypoLineGap=master.customParameters["typoLineGap"], usWinAscent=fb.font["head"].yMax, usWinDescent=-fb.font["head"].yMin, sxHeight=master.xHeight, sCapHeight=master.capHeight, achVendID=vendor, fsType=calcBits(font.customParameters["fsType"], 0, 16), fsSelection=calcFsSelection(instance), ulUnicodeRange1=calcBits(font.customParameters["unicodeRanges"], 0, 32), ulCodePageRange1=calcBits(codePages, 0, 32), ) fea = makeFeatures(instance, master, opts, glyphOrder) fb.addOpenTypeFeatures(fea) palettes = master.customParameters["Color Palettes"] palettes = [[tuple(v / 255 for v in c) for c in p] for p in palettes] fb.setupCPAL(palettes) fb.setupCOLR(colorLayers) instance.font = fb.font if opts.debug: fb.font.save(f"{instance.fontName}.otf") return fb.font