def run(self, destDir, progress): paths = self.controller.get(["ufo"]) report = Report() tempDir = os.path.join(destDir, "temp") if not os.path.exists(tempDir): os.makedirs(tempDir) tempExportPaths = self._generateCallback(tempDir, progress, report) progress.update("Merging Tables...") report.writeTitle("Merged Fonts:") report.newLine() tableNames = [item["tableName"] for item in self.tableList if item["add"]] for fontIndex, path in enumerate(paths): font = RFont(path, document=False, showInterface=False) binarySourcepath = font.lib.get("com.typemytype.robofont.binarySource") tempExportPath = tempExportPaths[fontIndex] if binarySourcepath: binaryIs Type = os.path.splitext(binarySourcepath)[1].lower() in [".ttf", ".otf"] tempIsOpenType = os.path.splitext(tempExportPath)[1].lower() in [".ttf", ".otf"] if binaryIsOpenType and tempIsOpenType: if os.path.exists(binarySourcepath) and os.path.exists(tempExportPath): binarySource = TTFont(binarySourcepath) tempFont = TTFont(tempExportPath) fileName = os.path.basename(tempExportPath) if not self.controller.keepFileNames(): fileName = "%s-%s%s" % (font.info.familyName, font.info.styleName, os.path.splitext(tempExportPath)[1]) path = os.path.join(destDir, fileName) report.writeTitle(os.path.basename(path), "'") report.write("source: %s" % tempExportPath) report.write("binary source: %s" % binarySourcepath) report.newLine() report.indent() for table in tableNames: if table in binarySource: report.write("merge %s table" % table) tempFont[table] = binarySource[table] report.write("save to %s" % path) tempFont.save(path) report.dedent() report.newLine() tempFont.close() binarySource.close() if not font.hasInterface(): font.close() reportPath = os.path.join(destDir, "Binary Merge Report.txt") report.save(reportPath) if not getDefault("Batch.Debug", False): if os.path.exists(tempDir): shutil.rmtree(tempDir)
def drawPoints(self, info): newPoints = self.getValues(None) if newPoints != None: glyph = CurrentGlyph() onCurveSize = getDefault("glyphViewOncurvePointsSize") * 5 offCurveSize = getDefault("glyphViewOncurvePointsSize") * 3 fillColor = tuple([i for i in getDefault("glyphViewCurvePointsFill")]) upmScale = (glyph.font.info.unitsPerEm/1000) scale = info["scale"] oPtSize = onCurveSize * scale fPtSize = offCurveSize * scale d.save() # thanks Erik! textLoc = newPoints[0][3][0] + math.cos(self.returnAngle(newPoints)) * (scale*.25*120) * 2, newPoints[0][3][1] + math.sin(self.returnAngle(newPoints)) * (scale*.25*120) * 2 for b in newPoints: for a in b: if a == newPoints[1][0] or a == newPoints[0][-1]: d.oval(a[0]-oPtSize/2,a[1]-oPtSize/2, oPtSize, oPtSize) else: d.oval(a[0]-fPtSize/2,a[1]-fPtSize/2, fPtSize, fPtSize) d.fill(fillColor[0],fillColor[1],fillColor[2],fillColor[3]) d.stroke(fillColor[0],fillColor[1],fillColor[2],fillColor[3]) d.strokeWidth(scale) d.line(newPoints[0][2],newPoints[1][1]) d.restore() # https://robofont.com/documentation/building-tools/toolspace/observers/draw-info-text-in-glyph-view/?highlight=draw%20text glyphWindow = CurrentGlyphWindow() if not glyphWindow: return glyphView = glyphWindow.getGlyphView() textAttributes = { AppKit.NSFontAttributeName: AppKit.NSFont.userFixedPitchFontOfSize_(11), } glyphView.drawTextAtPoint( f'{round(abs(math.degrees(self.returnAngle(newPoints)))%180,4)}°\n{str(round(self.returnRatio(newPoints),4))}', textAttributes, textLoc, yOffset=0, drawBackground=True, centerX=True, centerY=True, roundBackground=False,) UpdateCurrentGlyphView()
def newThemeCallback(self, sender): if self.debug: print("newThemeCallback") # Callback from the "New" button # Read all of the current preferences into a new theme dictionary and update the Vanilla list theme = {} for key, name, dataType in THEMEKEYS: data = getDefault(key) data = dataType(data) # Save the data in the theme theme[key] = data # Give the theme a default name new = [] name = "★ New Theme" for themes in self.themeNames: if name in themes: new.append(themes) length = len(new) + 1 if name in self.themeNames: newName = name.replace("★ ", "") themeName = newName + " (%s)" % length theme["themeName"] = themeName theme["themeType"] = "User" # User or Default else: theme["themeName"] = "New Theme" theme["themeType"] = "User" # User or Default self.themes += [theme] self.rebuildThemeList() self.editName((len(self.themes) - 1), theme["themeName"])
def makeBackupTheme(self): if self.debug: print("makeBackupTheme") # Make a backup of the current user prefs self.backupTheme = {} for key, name, dataType in THEMEKEYS: data = getDefault(key) data = dataType(data) self.backupTheme[key] = data
def drawSolidPreview(self, info): outline = self.getRotatedGlyph() pen = CocoaPen(None) outline.draw(pen) defaultPreviewColor = getDefault('glyphViewPreviewFillColor') fillColor = NSColor.colorWithCalibratedRed_green_blue_alpha_( *defaultPreviewColor) fillColor.set() pen.path.fill()
def __init__(self, parentWindow): data = getExtensionDefault(self.identifier, dict()) updateWithDefaultValues(data, defaultOptions) data["debug"] = getDefault("Batch.Debug", False) width = 380 height = 1000 self.w = Sheet((width, height), parentWindow=parentWindow) y = 10 self.w.threaded = CheckBox((10, y, -10, 22), "Threaded", value=data["threaded"]) y += 30 self.w.exportInFolders = CheckBox((10, y, -10, 22), "Export in Sub Folders", value=data["exportInFolders"]) y += 30 self.w.keepFileNames = CheckBox( (10, y, -10, 22), "Keep file names (otherwise use familyName-styleName)", value=data["keepFileNames"]) y += 30 self.w.debug = CheckBox((10, y, -10, 22), "Debug", value=data["debug"]) y += 35 self.w.saveButton = Button((-100, y, -10, 20), "Save settings", callback=self.saveCallback, sizeStyle="small") self.w.setDefaultButton(self.w.saveButton) self.w.closeButton = Button((-190, y, -110, 20), "Cancel", callback=self.closeCallback, sizeStyle="small") self.w.closeButton.bind(".", ["command"]) self.w.closeButton.bind(chr(27), []) self.w.resetButton = Button((-280, y, -200, 20), "Reset", callback=self.resetCallback, sizeStyle="small") y += 30 self.w.resize(width, y, False) self.w.open()
def setup(self): self._rin = None self._rout = None self.markerWidth = 12 self.snapThreshold = .5 self.dot_size = int(getDefault('glyphViewOffCurvePointsSize')) * 3 self.snap_size = self.dot_size + 6 self.font_size = int(getDefault('textFontSize')) + 2 foregroundLayer = self.extensionContainer( identifier="com.letterror.angleRatioTool.fg", location="foreground", clear=True ) self.outgoingLayer = foregroundLayer.appendPathSublayer( fillColor=None, strokeColor=self.outgoingColor ) self.incomingLayer = foregroundLayer.appendPathSublayer( fillColor=None, strokeColor=self.incomingColor ) self.captionTextLayer = foregroundLayer.appendTextLineSublayer( #position=(0, 0), #size=(400, 100), backgroundColor=self.outgoingColor, text="", fillColor=(1, 1, 1, 1), horizontalAlignment="center" ) self.outgoingLayer.setVisible(True) self.incomingLayer.setVisible(True) self.captionTextLayer.setVisible(True) self.update()
def spaceCenterDraw(self, notification): glyph = notification["glyph"] spaceCenter = notification["spaceCenter"] scale = notification["scale"] attrValues = self.getAttributes() outGlyph = self.getGlyph(glyph, *attrValues) inverse = spaceCenter.glyphLineView.getDisplayStates()['Inverse'] foreground = tuple(getDefault('spaceCenterGlyphColor')) if not inverse else tuple(getDefault('spaceCenterBackgroundColor')) background = tuple(getDefault('spaceCenterBackgroundColor')) if not inverse else tuple(getDefault('spaceCenterGlyphColor')) # cover current glyph drawingTools.fill(*background) drawingTools.stroke(*background) drawingTools.strokeWidth(2*scale) drawingTools.drawGlyph(glyph) drawingTools.stroke(None) # draw glyph preview drawingTools.fill(*foreground) drawingTools.drawGlyph(outGlyph)
def resize(self, sender): SWM = getDefault("singleWindowMode") fw = CurrentFontWindow() fo = fw.fontOverview gc = fo.getGlyphCollection() v = gc.getGlyphCellView() # make cells small first starter = 10 v.setCellSize_([starter, starter]) # get the width of the whole window x, y, w, h = fw.window().getPosSize() # get the width of overall font overview fo_w = fo.getNSView().frameSize().width # get the width of the cell view vw, vh = v.frameSize().width, v.frameSize().height # get the width of the sets menu to the left of the font overview sets_w = fo_w - vw # get number of glyphs num_g = len(gc.getGlyphNames()) cells_across = 1 cw = int(vw / cells_across) while ((num_g) / cells_across) * cw > vh: cells_across += 1 cw = ch = int(vw / cells_across) vw = cw * cells_across fo_total_w = vw + sets_w # set frame size in single window mode if SWM == 1: fw.editor.splitView.setDimension('fontOverview', fo_total_w) fw.editor.splitView.setDimension('glyphView', w - fo_total_w) fw.centerGlyphInView() # set frame size in multi-window mode else: windows = NSApp().orderedWindows() (x, y), (w, h) = windows[0].frame() x_diff = w - fo_total_w windows[0].setFrame_display_animate_( ((x + x_diff, y), (fo_total_w, h)), True, False) # change the cell size once and for all, update the slider to reflect the change v.setCellSize_([cw, ch]) fo.views.sizeSlider.set(cw) # set this as the new default cell size (this happens when you use the native slide too) setDefault("fontCollectionViewGlyphSize", int(cw))
def generateVariationFont(self, destPath, autohint=False, fitToExtremes=False, releaseMode=True, glyphOrder=None, report=None): """ Generate a variation font based on a desingSpace. """ if report is None: report = Report() self.generateReport = report self.compileSettingAutohint = autohint self.compileSettingReleaseMode = releaseMode self.compileGlyphOrder = glyphOrder self.loadFonts() self.loadLocations() self.loadMasters() self._generatedFiles = set() self.makeMasterGlyphsCompatible() self.decomposedMixedGlyphs() self.makeMasterGlyphsQuadractic() self.makeMasterKerningCompatible() self.makeMasterOnDefaultLocation() self.makeLayerMaster() try: self._generateVariationFont(destPath) except Exception: import traceback result = traceback.format_exc() print(result) finally: if not getDefault("Batch.Debug", False): # remove generated files for path in self._generatedFiles: if os.path.exists(path): if os.path.isdir(path): shutil.rmtree(path) else: os.remove(path) return report
def drawActionPreview(self, data): if not self.previewObjects: return pen = CocoaPen(glyphSet=self.glyph.layer) for obj in self.previewObjects: if isinstance(obj, BaseBPoint): pass elif isinstance(obj, BaseContour): obj.draw(pen) elif isinstance(obj, BaseComponent): obj.draw(pen) pixel = 1.0 / data["scale"] r, g, b, a = getDefault("glyphViewEchoStrokeColor") with bot.savedState(): bot.fill(None) bot.stroke(r, g, b, a) bot.strokeWidth(pixel) bot.drawPath(pen.path)
def makeLayerMaster(self): """ If there a layer name in a source description add it as seperate master in the source description. """ for sourceDescriptor in self.sources: if sourceDescriptor.layerName is not None: path, ext = os.path.splitext(sourceDescriptor.path) sourceDescriptor.path = "%s-%s%s" % ( path, sourceDescriptor.layerName, ext) sourceDescriptor.styleName = "%s %s" % ( sourceDescriptor.styleName, sourceDescriptor.layerName) sourceDescriptor.filename = None sourceDescriptor.layerName = None if getDefault("Batch.Debug", False): masterFont = self.fonts[sourceDescriptor.name] layerPath = os.path.join(os.path.dirname(self.path), sourceDescriptor.path) masterFont.save(layerPath)
def makeMasterGlyphsCompatible(self): """ Update all masters with missing glyphs. All Masters must have the same glyphs. """ self.generateReport.writeTitle("Making master glyphs compatible", "'") self.generateReport.indent() # collect all possible glyph names glyphNames = [] for master in self.masters.values(): glyphNames.extend(master.keys()) glyphNames = set(glyphNames) # get the default master defaultMaster = self.masters[self.default.name] # loop over all glyphName for glyphName in glyphNames: # first check if the default master has this glyph if glyphName not in defaultMaster: # the default does not have the glyph # build a repair mutator to generate a glyph. glyphItems = [] for sourceDescriptor in self.sources: master = self.masters[sourceDescriptor.name] if glyphName in sourceDescriptor.mutedGlyphNames: continue if glyphName in master: sourceGlyph = self.mathGlyphClass(master[glyphName]) sourceGlyphLocation = self.locations[ sourceDescriptor.name] glyphItems.append((sourceGlyphLocation, sourceGlyph)) # Note: this needs to be a mutatormath mutator, not a varlib.model. # A varlib model can't work without in the missing default. # Filling in the default is a bit of a hack: it will make the font work, # but it is a bit of a guess. _, mutator = buildMutator(glyphItems) # use the repair mutator to generate an instance at the default location result = mutator.makeInstance( Location(self.newDefaultLocation())) # round if necessary if self.roundGeometry: result.round() self.generateReport.write( "Adding missing glyph '%s' in the default master '%s %s (%s)'" % (glyphName, defaultMaster.font.info.familyName, defaultMaster.font.info.styleName, defaultMaster.name)) # add the glyph to the default master defaultMaster.newGlyph(glyphName) glyph = defaultMaster[glyphName] result.extractGlyph(glyph, onlyGeometry=True) glyphs = [] # fill all masters with missing glyphs # and collect all glyphs from all masters # to send them to optimize contour data for sourceDescriptor in self.sources: master = self.masters[sourceDescriptor.name] hasGlyph = False if glyphName in master: # Glyph is present in the master. # This checks for points, components and so on. if not checkGlyphIsEmpty(master[glyphName], allowWhiteSpace=True): glyphs.append(master[glyphName]) hasGlyph = True if not hasGlyph: # Get the varlibmodel to generate a filler glyph. # These is probably a support with just a few glyphs. try: self.useVarlib = True mutator = self.getGlyphMutator(glyphName) if mutator is None: self.useVarlib = False mutator = self.getGlyphMutator(glyphName) self.useVarlib = True # generate an instance result = mutator.makeInstance( Location(sourceDescriptor.location)) except Exception as e: print("Problem in %s" % glyphName) print("\n".join(self.problems)) raise e # round if necessary if self.roundGeometry: result.round() self.generateReport.write( "Adding missing glyph '%s' in master '%s %s (%s)'" % (glyphName, master.font.info.familyName, master.font.info.styleName, master.name)) # add the glyph to the master master.newGlyph(glyphName) result.extractGlyph(master[glyphName], onlyGeometry=True) glyphs.append(master[glyphName]) # optimize glyph contour data from all masters self.makeGlyphOutlinesCompatible(glyphs) if getDefault("Batch.Debug", False): for k, m in self.masters.items(): tempPath = os.path.join( os.path.dirname(m.font.path), "%s_%s" % (k, os.path.basename(m.font.path))) m.font.save(tempPath) if self.compileGlyphOrder is None: self.compileGlyphOrder = defaultMaster.font.lib.get( "public.glyphOrder", []) for glyphName in sorted(glyphNames): if glyphName not in self.compileGlyphOrder: self.compileGlyphOrder.append(glyphName) self.generateReport.dedent() self.generateReport.newLine()
except ImportError: hasMojo = False try: CurrentFont except NameError: class CurrentFont(dict): def save(self, path=None): pass f = CurrentFont() if hasMojo: glyphViewRoundValues = getDefault("glyphViewRoundValues") setDefault("glyphViewRoundValues", 0) for g in f: g.leftMargin = 0 g.rightMargin = 0 n = g.naked() d = g.getLayer("union") d.clear() d.appendGlyph(g) d.removeOverlap(round=0) if len(g) > 1: for method in "xor", "difference", "intersection": d = g.getLayer(method) d.clear()
except ImportError: hasMojo = False try: CurrentFont except NameError: class CurrentFont(dict): def save(self, path=None): pass f = CurrentFont() if hasMojo: glyphViewRoundValues = getDefault("glyphViewRoundValues") setDefault("glyphViewRoundValues", 0) for g in f: g.leftMargin = 0 g.rightMargin = 0 n = g.naked() d = g.getLayer("union") d.clear() d.appendGlyph(g) d.removeOverlap(round=0) if len(g) > 1: for method in "xor", "difference", "intersection": d = g.getLayer(method)
def getColor(): curColor = getDefault("glyphViewLocalGuidesColor") curColor = map(lambda x: x - 0.001 if x > 0.5 else x + 0.001, curColor) return tuple(curColor)
def drawGlyphsInGroup(self, notification): '''Display all glyphs belonging to the same spacing group in the background.''' glyph = notification['glyph'] font = glyph.font if font is None: return siblings = getSiblings(glyph, self.side) if not siblings: return if not notification['selected']: return S = CurrentSpaceCenter() if not S: return inverse = S.glyphLineView.getDisplayStates()['Inverse'] # hide solid color glyph R, G, B, A = getDefault( "spaceCenterBackgroundColor") if not inverse else getDefault( "spaceCenterGlyphColor") bounds = glyph.bounds if bounds: save() fill(R, G, B, A) stroke(R, G, B, A) drawGlyph(glyph) restore() # draw side indicator save() stroke(1, 0, 0) strokeWidth(10) xPos = 0 if self.side == 'left' else glyph.width yMin = font.info.descender yMax = yMin + font.info.unitsPerEm line((xPos, yMin), (xPos, yMax)) restore() # draw glyph and siblings R, G, B, A = getDefault( "spaceCenterGlyphColor") if not inverse else getDefault( "spaceCenterBackgroundColor") alpha = (1.0 / len(siblings) + self.opacity) / 2 stroke(None) for glyphName in siblings: if glyphName not in glyph.layer: continue g = font[glyphName].getLayer(glyph.layer.name) save() if self.side == 'right': dx = glyph.width - g.width translate(dx, 0) color = (R, G, B, 0.4) if glyphName == glyph.name else (R, G, B, alpha) fill(*color) drawGlyph(g) restore()
def _generateVariationFont(self, outPutPath): """ Generate a variation font. """ dirname = os.path.dirname(outPutPath) # fontCompiler settings options = FontCompilerOptions() options.saveFDKPartsNextToUFO = getDefault("saveFDKPartsNextToUFO") options.shouldDecomposeWithCheckOutlines = False options.generateCheckComponentMatrix = True options.defaultDrawingSegmentType = "qcurve" options.format = "ttf" options.decompose = False options.checkOutlines = False options.autohint = self.compileSettingAutohint options.releaseMode = self.compileSettingReleaseMode options.glyphOrder = self.compileGlyphOrder options.useMacRoman = False options.fdk = CurrentFDK() options.generateFeaturesWithFontTools = True self.generateReport.newLine() self.generateReport.writeTitle("Generate TTF", "'") self.generateReport.indent() # map all master ufo paths to generated binaries masterBinaryPaths = VarLibMasterFinder() masterCount = 0 for sourceDescriptor in self.sources: master = self.masters[sourceDescriptor.name] # get the output path outputPath = os.path.join( dirname, "temp_%02d_%s-%s-%s.ttf" % (masterCount, master.font.info.familyName, master.font.info.styleName, master.name)) masterBinaryPaths[sourceDescriptor.path] = outputPath self._generatedFiles.add(outputPath) masterCount += 1 # set the output path options.outputPath = outputPath options.layerName = master.name try: # generate the font result = generateFont(master.font, options=options) if getDefault("Batch.Debug", False): tempSavePath = os.path.join( dirname, "temp_%s-%s-%s.ufo" % (master.font.info.familyName, master.font.info.styleName, master.name)) font = master.font font.save(tempSavePath) if font.layers.defaultLayer.name != master.name: tempFont = defcon.Font(tempSavePath) tempFont.layers.defaultLayer = tempFont.layers[ master.name] tempFont.save() except Exception: import traceback result = traceback.format_exc() print(result) self.generateReport.newLine() self.generateReport.write( "Generate %s %s (%s)" % (master.font.info.familyName, master.font.info.styleName, master.name)) self.generateReport.indent() self.generateReport.write(result) self.generateReport.dedent() self.generateReport.dedent() # optimize the design space for varlib designSpacePath = os.path.join(os.path.dirname(self.path), "temp_%s" % os.path.basename(self.path)) self.write(designSpacePath) self._generatedFiles.add(designSpacePath) try: # let varLib build the variation font varFont, _, _ = varLib.build(designSpacePath, master_finder=masterBinaryPaths) # save the variation font varFont.save(outPutPath) except Exception: if getDefault("Batch.Debug", False): print("masterBinaryPaths:", masterBinaryPaths) import traceback result = traceback.format_exc() print(result)
class ShowGlyphPalette: bcColor = AppKit.NSColor.grayColor() fill = getDefault("glyphViewFillColor") attr = { AppKit.NSFontAttributeName: AppKit.NSFont.fontWithName_size_("Monaco", 8.0), AppKit.NSForegroundColorAttributeName: AppKit.NSColor.whiteColor() } key = "com.rafalbuchner.ShowGlyphPalette" def __init__(self): # self.debugW = vanilla.FloatingWindow((0,0,100,100)) # self.debugW.bind("close",self.closingDebug) # self.debugW.open() self.isCursorAbove = False self.glyphList = [] self.methodsToDraw = [] self.methodsToDrawBackground = [] self.itemsCallbacks = { 'show related cluster': self.showGlyphsWithCurrentCB, 'show related in back': self.showRelatedInBackCB } self.items = { 'show related cluster': True, 'show related in back': True } self.showOnCursorGlyphInSpaceCenterItem = { 'show this glyph in SC': self.showThisGlyphInSC_CB } self.activeContextualOptions = [] self.loadSettings() addObserver(self, "glyphAdditionContextualMenuItemsCB", "glyphAdditionContextualMenuItems") addObserver(self, "currentGlyphChangedCB", "currentGlyphChanged") addObserver(self, "drawCB", "draw") addObserver(self, "drawBackgroundCB", "drawBackground") addObserver(self, "mouseMovedCB", "mouseMoved") addObserver(self, "mouseDownCB", "mouseDown") # def closingDebug(self, info): # removeObserver(self, "glyphAdditionContextualMenuItems") # removeObserver(self, "currentGlyphChanged") # removeObserver(self, "draw") # removeObserver(self, "drawBackground") # removeObserver(self, "mouseMoved") # removeObserver(self, "mouseDown") def showThisGlyphInSC_CB(self, sender): if CurrentSpaceCenter() is None: spaceCenter = OpenSpaceCenter(self.glyph.font) spaceCenter.set([self.glyphBelowName]) else: scGlyphs = CurrentSpaceCenter().get() spaceCenter = OpenSpaceCenter(self.glyph.font) spaceCenter.set(scGlyphs + [self.glyphBelowName]) def drawRelatedGlyphWindow(self, cursor, glyph, view): view._drawTextInRect(f"name: {glyph.name}\nwidth: {glyph.width}", self.attr, cursor, yOffset=10, xOffset=10, drawBackground=True, position="left", backgroundColor=self.bcColor) def mouseDownCB(self, point): if point['clickCount'] == 2: if self.isCursorAbove: CurrentGlyphWindow().setGlyphByName(self.glyphBelowName) def mouseMovedCB(self, info): self.cursor = (info['point'].x, info['point'].y) self.isCursorAbove = False self.activeContextualOptions = [] if self.items['show related cluster']: for glyphRepr in self.glyphList: if glyphRepr.isInside(self.cursor): self.view = info["view"] self.glyphBelowName = glyphRepr.name self.isCursorAbove = True self.activeContextualOptions += list( self.showOnCursorGlyphInSpaceCenterItem.items()) break UpdateCurrentGlyphView() def currentGlyphChangedCB(self, sender): self.glyph = CurrentGlyph() if self.glyph is None: return font = self.glyph.font self.glyphList = [] self.clusterWidth = [] for glyph in font: try: for component in glyph.components: if component.baseGlyph == self.glyph.name: # glyphRepr = GlyphRepr(font[glyph.name],origin=(self.glyph.width/2, self.glyph.bounds[-1]/2),fillColor=self.fill,shift=None,offset=font.info.ascender*1) glyphRepr = GlyphRepr(font[glyph.name], origin=(0, 0), fillColor=self.fill, shift=None, offset=font.info.ascender * 1) self.glyphList += [glyphRepr] self.clusterWidth += [font[glyph.name].width] except: print(f"glyph <{glyph.name}> contains corrupted component") if len(self.glyphList) > 0: scale = 3 / len(self.glyphList) # for i, glyphRepr in enumerate(self.glyphList): # glyphRepr.origin = (self.glyph.width/2-sum(self.clusterWidth)*scale/2+sum(self.clusterWidth[:i])*scale, # self.glyph.bounds[-1]/2) if scale > .4: scale = .4 for glyph in self.glyphList: glyph.scale = scale @staticmethod def appendMethodToDrawingMethods(sender, method, drawingMethods): if sender.get() == 1: if method not in drawingMethods: drawingMethods.append(method) UpdateCurrentGlyphView() else: if method in drawingMethods: drawingMethods.remove(method) UpdateCurrentGlyphView() def showRelatedInBackCB(self, sender): ShowGlyphPalette.appendMethodToDrawingMethods( sender, self.showRelatedInBackDraw, self.methodsToDrawBackground) def showGlyphsWithCurrentCB(self, sender): ShowGlyphPalette.appendMethodToDrawingMethods( sender, self.showGlyphsWithCurrentDraw, self.methodsToDrawBackground) def showRelatedInBackDraw(self, scale): for glyphRepr in self.glyphList: glyph = self.glyph.font[glyphRepr.name] shift = (self.glyph.width - glyph.width) / 2 glyphReprBack = GlyphRepr(glyph, origin=(shift, 0), fillColor=self.fill, shift=shift, offset=0) stroke(None) glyphReprBack.draw() def drawPreviewCB(self, scale): pass def showGlyphsWithCurrentDraw(self, scale): def _getGlyphWidth(glyphRepr): return glyphRepr.glyph.width if self.glyphList: # for i, glyphRepr in enumerate(self.glyphList): for i, glyphRepr in enumerate(self.glyphList): glyphRepr.origin = ( self.glyph.width / 2 - sum(self.clusterWidth) * glyphRepr.scale / 2 + sum(self.clusterWidth[:i]) * glyphRepr.scale, self.glyph.bounds[-1] / 2) stroke(None) glyphRepr.draw() def drawCB(self, scale): if self.isCursorAbove: glyph = self.glyph.font[self.glyphBelowName] self.drawRelatedGlyphWindow(self.cursor, glyph, self.view) for method in self.methodsToDraw: method(scale) def drawBackgroundCB(self, scale): for method in self.methodsToDrawBackground: method(scale) def loadSettings(self): # loading settings zero_settings = self.items settings = getExtensionDefault(self.key, fallback=self.items) if settings: for name in settings: self.items[name] = settings[name] else: self.items = zero_settings else: self.items = zero_settings def savingSettings(self): setExtensionDefault(self.key, self.items) def glyphAdditionContextualMenuItemsCB(self, info): menuItems = info['additionContextualMenuItems'] showGlyphPaletteMenuItems = self.activeContextualOptions tool = info["tool"] if isinstance(tool, EditingTool): for title in self.items: temp = title.replace(" ", "_") value = self.items[title] checkboxMenuItem = AppKit.NSMenuItem.alloc( ).initWithTitle_action_keyEquivalent_("regular", '', '') MenuTextAttributes = { AppKit.NSFontAttributeName: AppKit.NSFont.menuFontOfSize_(14) } t = AppKit.NSAttributedString.alloc( ).initWithString_attributes_(title, MenuTextAttributes) checkbox = vanilla.CheckBox((0, 0, 100, 22), t, value=value, callback=self.checkboxCallback) setattr(self, temp, checkbox) view = checkbox.getNSButton() view.setFrame_(((0, 0), (200, 30))) checkboxMenuItem.setView_(view) showGlyphPaletteMenuItems += [checkboxMenuItem] cb = self.itemsCallbacks.get(title) if cb is not None: cb(checkbox) showGlyphPaletteMenuItems += [ ("print out palette", self.printOutClusterCallback), ("open palette in SpaceCenter", self.openClusterInSpaceCenterCallback), ("select palette in font", self.selectClusterCallback) ] menuItems += [("Glyph Palette", showGlyphPaletteMenuItems)] def selectClusterCallback(self, sender): self.glyph.font.selectedGlyphNames = [ glyph.glyph.name for glyph in self.glyphList ] def openClusterInSpaceCenterCallback(self, sender): spaceCenter = OpenSpaceCenter(self.glyph.font) spaceCenter.set([glyph.glyph.name for glyph in self.glyphList]) def printOutClusterCallback(self, sender): print([glyph.glyph.name for glyph in self.glyphList]) def checkboxCallback(self, obj): value = False if obj.get() == 1: value = True self.items[obj.getTitle()] = value self.savingSettings() self.itemsCallbacks[obj.getTitle()](obj)
def _generateVariationFont(self, outPutPath): """ Generate a variation font. """ dirname = os.path.dirname(outPutPath) # fontCompiler settings options = FontCompilerOptions() options.saveFDKPartsNextToUFO = getDefault("saveFDKPartsNextToUFO") options.shouldDecomposeWithCheckOutlines = False options.generateCheckComponentMatrix = True options.defaultDrawingSegmentType = "qcurve" options.format = "ttf" options.decompose = False options.checkOutlines = False options.autohint = self.compileSettingAutohint options.releaseMode = self.compileSettingReleaseMode options.glyphOrder = self.compileGlyphOrder options.useMacRoman = False options.fdk = CurrentFDK() options.generateFeaturesWithFontTools = False self.generateReport.newLine() self.generateReport.writeTitle("Generate TTF", "'") self.generateReport.indent() # map all master ufo paths to generated binaries masterBinaryPaths = VarLibMasterFinder() masterCount = 0 for sourceDescriptor in self.sources: master = self.masters[sourceDescriptor.name] # get the output path outputPath = os.path.join(dirname, "temp_%02d_%s-%s-%s.ttf" % (masterCount, master.font.info.familyName, master.font.info.styleName, master.name)) masterBinaryPaths[sourceDescriptor.path] = outputPath self._generatedFiles.add(outputPath) masterCount += 1 # set the output path options.outputPath = outputPath options.layerName = master.name try: # generate the font result = generateFont(master.font, options=options) if getDefault("Batch.Debug", False): tempSavePath = os.path.join(dirname, "temp_%s-%s-%s.ufo" % (master.font.info.familyName, master.font.info.styleName, master.name)) font = master.font font.save(tempSavePath) if font.layers.defaultLayer.name != master.name: tempFont = defcon.Font(tempSavePath) tempFont.layers.defaultLayer = tempFont.layers[master.name] tempFont.save() self._generatedFiles.add(tempSavePath) except Exception: import traceback result = traceback.format_exc() print(result) self.generateReport.newLine() self.generateReport.write("Generate %s %s (%s)" % (master.font.info.familyName, master.font.info.styleName, master.name)) self.generateReport.indent() self.generateReport.write(result) self.generateReport.dedent() self.generateReport.dedent() # optimize the design space for varlib designSpacePath = os.path.join(os.path.dirname(self.path), "temp_%s" % os.path.basename(self.path)) self.write(designSpacePath) self._generatedFiles.add(designSpacePath) try: # let varLib build the variation font varFont, _, _ = varLib.build(designSpacePath, master_finder=masterBinaryPaths) # save the variation font varFont.save(outPutPath) except Exception: import traceback result = traceback.format_exc() print(result)
def makeMasterGlyphsCompatible(self): """ Update all masters with missing glyphs. All Masters must have the same glyphs. """ self.generateReport.writeTitle("Making master glyphs compatible", "'") self.generateReport.indent() # collect all possible glyph names glyphNames = [] for master in self.masters.values(): glyphNames.extend(master.keys()) glyphNames = set(glyphNames) # get the default master defaultMaster = self.masters[self.default.name] # loop over all glyphName for glyphName in glyphNames: # first check if the default master has this glyph if glyphName not in defaultMaster: # the default does not have the glyph # build a repair mutator to generate a glyph. glyphItems = [] for sourceDescriptor in self.sources: master = self.masters[sourceDescriptor.name] if glyphName in sourceDescriptor.mutedGlyphNames: continue if glyphName in master: sourceGlyph = self.mathGlyphClass(master[glyphName]) sourceGlyphLocation = self.locations[sourceDescriptor.name] glyphItems.append((sourceGlyphLocation, sourceGlyph)) # Note: this needs to be a mutatormath mutator, not a varlib.model. # A varlib model can't work without in the missing default. # Filling in the default is a bit of a hack: it will make the font work, # but it is a bit of a guess. _, mutator = buildMutator(glyphItems) # use the repair mutator to generate an instance at the default location result = mutator.makeInstance(Location(self.defaultLoc)) # round if necessary if self.roundGeometry: result.round() self.generateReport.write("Adding missing glyph '%s' in the default master '%s %s (%s)'" % (glyphName, defaultMaster.font.info.familyName, defaultMaster.font.info.styleName, defaultMaster.name)) # add the glyph to the default master defaultMaster.newGlyph(glyphName) glyph = defaultMaster[glyphName] result.extractGlyph(glyph, onlyGeometry=True) glyphs = [] # fill all masters with missing glyphs # and collect all glyphs from all masters # to send them to optimize contour data for sourceDescriptor in self.sources: master = self.masters[sourceDescriptor.name] hasGlyph = False if glyphName in master: # Glyph is present in the master. # This checks for points, components and so on. if not checkGlyphIsEmpty(master[glyphName], allowWhiteSpace=True): glyphs.append(master[glyphName]) hasGlyph = True if not hasGlyph: # Get the varlibmodel to generate a filler glyph. # These is probably a support with just a few glyphs. try: self.useVarlib = True mutator = self.getGlyphMutator(glyphName) if mutator is None: self.useVarlib = False mutator = self.getGlyphMutator(glyphName) self.useVarlib = True # generate an instance result = mutator.makeInstance(Location(sourceDescriptor.location)) except Exception as e: print("Problem in %s" % glyphName) print("\n".join(self.problems)) raise e # round if necessary if self.roundGeometry: result.round() self.generateReport.write("Adding missing glyph '%s' in master '%s %s (%s)'" % (glyphName, master.font.info.familyName, master.font.info.styleName, master.name)) # add the glyph to the master master.newGlyph(glyphName) result.extractGlyph(master[glyphName], onlyGeometry=True) glyphs.append(master[glyphName]) # optimize glyph contour data from all masters self.makeGlyphOutlinesCompatible(glyphs) if getDefault("Batch.Debug", False): for k, m in self.masters.items(): tempPath = m.font.path.replace(".ufo", "_%s.ufo" % k) m.font.save(tempPath) if self.compileGlyphOrder is None: self.compileGlyphOrder = defaultMaster.font.lib.get("public.glyphOrder", []) for glyphName in sorted(glyphNames): if glyphName not in self.compileGlyphOrder: self.compileGlyphOrder.append(glyphName) self.generateReport.dedent() self.generateReport.newLine()