def show_dist_textbox(self, info): window = CurrentGlyphWindow() view = window.getGlyphView() vanillaView = ShowDistTextBox(view, (20, 22, 120, 22), "", alignment="left", sizeStyle="mini") window.addGlyphEditorSubview(vanillaView)
def getActiveGlyphWindow(): window = CurrentGlyphWindow() # there is no glyph window if window is None: return None # the editor is not the first responder if not window.getGlyphView().isFirstResponder(): return None return window
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 getImageForView(self, viewName): if viewName == "Glyph View": window = CurrentGlyphWindow() if window is None: return None view = window.getGlyphView().enclosingScrollView() elif viewName == "Space Center": window = CurrentSpaceCenter() if window is None: return None view = window.glyphLineView.getNSScrollView() data = self._getImageForView(view) data = data.bytes() if isinstance(data, memoryview): data = data.tobytes() return data
def updateComp(self, g, viewScale): if len(g.selectedComponents) == 1: cf = g.font cg = g selected_component = cg.selectedComponents[0] selected_component_name = cg.selectedComponents[0].baseGlyph constructions = self.constructions glyph_constructor = self.glyph_constructor glyphWindow = CurrentGlyphWindow() glyphView = glyphWindow.getGlyphView() if not glyphWindow: return for line in glyph_constructor.split("\n"): if len(line) > 0: composed_glyph = line.split("=")[0] recipee = line.split("=")[1] new_base_glyph = recipee.split("+")[0] if new_base_glyph == cg.components[ 0].baseGlyph and cg.name == composed_glyph: construction = f"{composed_glyph}={recipee}" constructionGlyph = GlyphConstructionBuilder( construction, cf) if constructionGlyph.name == cg.name: for component_index, c in enumerate( constructionGlyph.components): c = list(c)[0] if c == selected_component_name: baseGlyphName = constructionGlyph.components[ component_index - 1][0] baseGlyph = cf[baseGlyphName] recipee = construction.split("=")[1] for diacritic_and_anchor in recipee.split( "+")[1:]: diacritic = diacritic_and_anchor.split( "@")[0] anchor = diacritic_and_anchor.split( "@")[1] if diacritic == selected_component_name: selected_component_anchor_name = "_%s" % anchor for baseGlyph_anchor in baseGlyph.anchors: if baseGlyph_anchor.name == anchor: x_baseGlyph_anchor = baseGlyph_anchor.x y_baseGlyph_anchor = baseGlyph_anchor.y selected_comp_baseGlyph = cf[ selected_component_name] for selectedComponent_anchor in selected_comp_baseGlyph.anchors: if selected_component_anchor_name == selectedComponent_anchor.name: x_offset = 0 y_offset = 0 for previous_components in constructionGlyph.components[ 1: component_index]: for cg_component in cg.components: if cg_component.baseGlyph == previous_components[ 0]: x_offset += cg_component.offset[ 0] y_offset += cg_component.offset[ 1] new_x_baseGlyph_anchor = selectedComponent_anchor.x + selected_component.offset[ 0] - x_offset new_y_baseGlyph_anchor = selectedComponent_anchor.y + selected_component.offset[ 1] - y_offset self.drawInfos( new_x_baseGlyph_anchor, new_y_baseGlyph_anchor, viewScale, glyphView, baseGlyph_anchor ) ### Update baseGlyph anchor baseGlyph_anchor.x = new_x_baseGlyph_anchor baseGlyph_anchor.y = new_y_baseGlyph_anchor if self.SettingsWindow.w.updateComposites.get( ) == 1: self.updateRelatedComposites( glyph_constructor, cg, cf, new_base_glyph, baseGlyph_anchor, composed_glyph )
class InterpolationPreviewWindow(object): def __init__(self): self.currentGlyph = None self.window = None self.fonts = [] self.fontNames = [] self.glyph0 = RGlyph() #None self.glyph1 = RGlyph() #None self.compatibilityReport = None self.interpolatedGlyph = RGlyph() self.w = vanilla.FloatingWindow((250, 155), "Interpolation Slider") self.w.open() self.w.title = vanilla.TextBox((10, 10, -10, 25), "Masters:", sizeStyle="small") self.w.font0 = vanilla.PopUpButton((10, 25, -10, 25), [], callback=self.glyphChanged, sizeStyle="small") self.w.font1 = vanilla.PopUpButton((10, 50, -10, 25), [], callback=self.glyphChanged, sizeStyle="small") self.w.compatibilityText = vanilla.TextBox((-105, 83, 100, 35), u"Compatibility: ⚪️", sizeStyle="small") self.w.line = vanilla.HorizontalLine((5, 110, -5, 1)) self.w.interpValue = vanilla.Slider((10, 120, -10, 25), callback=self.optionsChanged, minValue=0, maxValue=1) self.w.interpValue.set(0.5) self.w.bind("close", self.closed) self.collectFonts() self.glyphChanged(None) addObserver(self, "glyphChanged", "currentGlyphChanged") addObserver(self, "fontsChanged", "newFontDidOpen") addObserver(self, "fontsChanged", "fontDidOpen") addObserver(self, "fontsChanged", "fontDidClose") addObserver(self, "drawBkgnd", "drawBackground") addObserver(self, "drawPreview", "drawPreview") def closed(self, sender): if self.window: self.window.getGlyphView().refresh() if self.currentGlyph: self.currentGlyph.removeObserver(self, "Glyph.Changed") self.currentGlyph.removeObserver(self, "Glyph.ContoursChanged") removeObserver(self, "currentGlyphChanged") removeObserver(self, "newFontDidOpen") removeObserver(self, "fontDidOpen") removeObserver(self, "fontDidClose") removeObserver(self, "drawBackground") removeObserver(self, "drawPreview") def getFontName(self, font, fonts): # A helper to get the font name, starting with the preferred name and working back to the PostScript name # Make sure that it's not the same name as another font in the fonts list if font.info.openTypeNamePreferredFamilyName and font.info.openTypeNamePreferredSubfamilyName: name = "%s %s" % (font.info.openTypeNamePreferredFamilyName, font.info.openTypeNamePreferredSubfamilyName) elif font.info.familyName and font.info.styleName: name = "%s %s" % (font.info.familyName, font.info.styleName) elif font.info.fullName: name = font.info.fullName elif font.info.fullName: name = font.info.postscriptFontName else: name = "Untitled" # Add a number to the name if this name already exists if name in fonts: i = 2 while name + " (%s)" % i in fonts: i += 1 name = name + " (%s)" % i return name def collectFonts(self): # Hold aside the current font choices font0idx = self.w.font0.get() font1idx = self.w.font1.get() if not font0idx == -1: font0name = self.fontNames[font0idx] else: font0name = None if not font1idx == -1: font1name = self.fontNames[font1idx] else: font1name = None # Collect info on all open fonts self.fonts = AllFonts() self.fontNames = [] for font in self.fonts: self.fontNames.append(self.getFontName(font, self.fontNames)) # Update the popUpButtons self.w.font0.setItems(self.fontNames) self.w.font1.setItems(self.fontNames) # If there weren't any previous names, try to set the first and second items in the list if font0name == None: if len(self.fonts): self.w.font0.set(0) if font1name == None: if len(self.fonts) >= 1: self.w.font1.set(1) # Otherwise, if there had already been fonts choosen before new fonts were loaded, # try to set the index of the fonts that were already selected if font0name in self.fontNames: self.w.font0.set(self.fontNames.index(font0name)) if font1name in self.fontNames: self.w.font1.set(self.fontNames.index(font1name)) def fontsChanged(self, info): self.collectFonts() self.glyphChanged(None) def glyphChanged(self, info): # Reset the glyph info self.glyph0.clear() self.glyph1.clear() self.compatibilityReport = None self.window = CurrentGlyphWindow() self.interpolatedGlyph.clear() # Remove any observers on the older CurrentGLyph and add them to the new one if self.currentGlyph: self.currentGlyph.removeObserver(self, "Glyph.Changed") self.currentGlyph.removeObserver(self, "Glyph.ContoursChanged") self.currentGlyph = CurrentGlyph() if self.currentGlyph: self.currentGlyph.addObserver(self, "optionsChanged", "Glyph.Changed") self.currentGlyph.addObserver(self, "optionsChanged", "Glyph.ContoursChanged") if not self.currentGlyph == None: # Update the glyph info glyphName = self.currentGlyph.name master0idx = self.w.font0.get() master1idx = self.w.font1.get() master0 = self.fonts[master0idx] master1 = self.fonts[master1idx] if glyphName in master0: self.glyph0.clear() pen = DecomposingPen(master0, self.glyph0.getPen()) master0[glyphName].draw(pen) if glyphName in master1: self.glyph1.clear() pen = DecomposingPen(master1, self.glyph1.getPen()) master1[glyphName].draw(pen) # Update the interp compatibility report self.testCompatibility() # Adjust the frame of the window to fit the interpolation # (Thanks Frederik!) if self.window: widths = [] if self.glyph0: widths.append(self.glyph0.width) if self.glyph1: widths.append(self.glyph1.width) if len(widths): widths.sort() view = self.window.getGlyphView() scale = view.scale() (x, y), (w, h) = view.frame() ox, oy = view.offset() extraWidth = widths[-1] * scale view.setOffset((ox, oy)) view.setFrame_(((x, y), (w + extraWidth, h))) # Update the view self.optionsChanged(None) def testCompatibility(self): status = u"⚪️" if self.window: if self.glyph0 == self.glyph1: status = u"⚪️" elif len(self.interpolatedGlyph.contours) > 0: status = u"✅" else: status = u"❌" self.w.compatibilityText.set(u"Compatibility: %s" % status) def optionsChanged(self, sender): if self.glyph0 and self.glyph1: # Interpolate self.interpolatedGlyph.clear() self.interpolatedGlyph.interpolate(self.w.interpValue.get(), self.glyph0, self.glyph1) self.testCompatibility() # ...and refresh the window if self.window: self.window.getGlyphView().refresh() def addPoints(self, pt0, pt1): return (pt0[0] + pt1[0], pt0[1] + pt1[1]) def subtractPoints(self, pt0, pt1): return (pt0[0] - pt1[0], pt0[1] - pt1[1]) def drawBkgnd(self, info): # Draw the interpolated glyph outlines scale = info["scale"] ptSize = 7 * scale if self.interpolatedGlyph: # Draw the glyph outline pen = CocoaPen(None) self.interpolatedGlyph.draw(pen) dt.fill(r=None, g=None, b=None, a=1) dt.stroke(r=0, g=0, b=0, a=0.4) dt.strokeWidth(2 * scale) dt.save() dt.translate(self.currentGlyph.width) dt.drawPath(pen.path) dt.stroke(r=0, g=0, b=0, a=1) # Draw the points and handles for contour in self.interpolatedGlyph.contours: for bPoint in contour.bPoints: inLoc = self.addPoints(bPoint.anchor, bPoint.bcpIn) outLoc = self.addPoints(bPoint.anchor, bPoint.bcpOut) dt.line(inLoc, bPoint.anchor) dt.line(bPoint.anchor, outLoc) dt.fill(r=1, g=1, b=1, a=1) dt.oval(bPoint.anchor[0] - (ptSize * 0.5), bPoint.anchor[1] - (ptSize * 0.5), ptSize, ptSize) dt.fill(0) # Draw an "X" over each BCP if not bPoint.bcpIn == (0, 0): dt.oval(inLoc[0] - (ptSize * 0.5), inLoc[1] - (ptSize * 0.5), ptSize, ptSize) #dt.line((inLoc[0]-(ptSize*0.5), inLoc[1]-(ptSize*0.5)), (inLoc[0]+(ptSize*0.5), inLoc[1]+(ptSize*0.5))) #dt.line((inLoc[0]+(ptSize*0.5), inLoc[1]-(ptSize*0.5)), (inLoc[0]-(ptSize*0.5), inLoc[1]+(ptSize*0.5))) if not bPoint.bcpOut == (0, 0): dt.oval(outLoc[0] - (ptSize * 0.5), outLoc[1] - (ptSize * 0.5), ptSize, ptSize) #dt.line((outLoc[0]-(ptSize*0.5), outLoc[1]-(ptSize*0.5)), (outLoc[0]+(ptSize*0.5), outLoc[1]+(ptSize*0.5))) #dt.line((outLoc[0]+(ptSize*0.5), outLoc[1]-(ptSize*0.5)), (outLoc[0]-(ptSize*0.5), outLoc[1]+(ptSize*0.5))) dt.restore() def drawPreview(self, info): # Draw a filled in version of the interpolated glyph scale = info["scale"] if self.interpolatedGlyph: pen = CocoaPen(None) self.interpolatedGlyph.draw(pen) dt.fill(r=0, g=0, b=0, a=0.6) dt.stroke(r=None, g=None, b=None, a=1) dt.save() dt.translate(self.currentGlyph.width) dt.drawPath(pen.path) dt.restore()