Image on a button from a glyph representation



  • Hi,

    This is probably easier than I think! Is it possible to add a glyph preview to a vanilla image button? This is what I wrote (mostly taken from the drawbot code), but is not displaying anything:

    from vanilla import Window, ImageButton
    import Quartz
    import AppKit
    
    f = CurrentFont()
    glyph = f['a']
    nspath = glyph.naked().getRepresentation("defconAppKit.NSBezierPath")
    def _NSPathToCGPath(nsPath):
    	cgPath = Quartz.CGPathCreateMutable()
    	for i in range(nsPath.elementCount()):
    		instruction, points = nsPath.elementAtIndex_associatedPoints_(i)
    		if instruction == AppKit.NSMoveToBezierPathElement:
    			Quartz.CGPathMoveToPoint(cgPath, None, points[0].x, points[0].y)
    		elif instruction == AppKit.NSLineToBezierPathElement:
    			Quartz.CGPathAddLineToPoint(cgPath, None, points[0].x, points[0].y)
    		elif instruction == AppKit.NSCurveToBezierPathElement:
    			Quartz.CGPathAddCurveToPoint(
    				cgPath, None,
    				points[0].x, points[0].y,
    				points[1].x, points[1].y,
    				points[2].x, points[2].y
    			)
    		elif instruction == AppKit.NSClosePathBezierPathElement:
    			Quartz.CGPathCloseSubpath(cgPath)
    	return cgPath
    
    mediaBox = Quartz.CGRectMake(0, 0, 300, 300)
    _pdfData = Quartz.CFDataCreateMutable(None, 0)
    dataConsumer = Quartz.CGDataConsumerCreateWithCFData(_pdfData)
    _pdfContext = Quartz.CGPDFContextCreate(dataConsumer, mediaBox, None)
    Quartz.CGContextBeginPage(_pdfContext, mediaBox)
    for i in range(nspath.elementCount()):
    	instruction, points = nspath.elementAtIndex_associatedPoints_(i)
    	if instruction == AppKit.NSMoveToBezierPathElement:
    		Quartz.CGContextMoveToPoint(_pdfContext, points[0].x, points[0].y)
    	elif instruction == AppKit.NSLineToBezierPathElement:
    		Quartz.CGContextAddLineToPoint(_pdfContext, points[0].x, points[0].y)
    	elif instruction == AppKit.NSCurveToBezierPathElement:
    		Quartz.CGContextAddCurveToPoint(_pdfContext, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y)
    	elif instruction == AppKit.NSClosePathBezierPathElement:
    		Quartz.CGContextClosePath(_pdfContext)
    
    doc = Quartz.PDFDocument.alloc().initWithData_(_pdfData)
    
    
    class ImageButtonDemo:
    
    	def __init__(self):
    		self.w = Window((500, 500))
    		self.w.button = ImageButton((10, 10, 300, 300), imageObject=doc,
    							callback=self.buttonCallback)
    		self.w.open()
    
    	def buttonCallback(self, sender):
    		print("button hit!")
    
    ImageButtonDemo()
    

    Thanks a million!


  • admin

    euh just use drawbot :)

    or easier, there is no need to create a pdf context and draw into a pdf:

    from fontTools.pens.cocoaPen import CocoaPen
    import AppKit
    
    g = CurrentGlyph()
    
    pen = CocoaPen(g.layer)
    
    g.draw(pen)
    
    image = AppKit.NSImage.alloc().initWithSize_((1000, 1000))
    image.lockFocus()
    AppKit.NSColor.blueColor().set()
    pen.path.fill()
    image.unlockFocus()
    
    print(image)
    


  • Hi!

    The reason I don't want to use drawbot is that I want to make the extension to work independently (it's a simple kerning tool). Although the solution you showed is neat, it doesn't work well for a button for two reasons:

    • The button doesn't change its color according to dark mode.
    • The image is a raster and gets pixelated on a retina screen.

    Regular tool buttons in the glyph view toolbar are more adaptive this way, I guess it's because they're PDF. Thank you Frederik. I will look more into it and will let you know if I found a solution. This could be a nice feature for people who want to make extensions that deal with multiple fonts and only want to use a glyph image as a reference/identifier for a font.



  • Actually it looks fine in terms of pixels. I just have to scale it and make it darkmode compatible. Thanks again! Will post the code here.



  • Here is a crude working version. The only drawback here is that it doesn't center the glyph on the button but it's not hard to figure out. I decomposed the glyph so I can scale it.

    from fontTools.pens.cocoaPen import CocoaPen
    from vanilla import *
    import AppKit
    from mojo.pens import DecomposePointPen
    
    
    g = CurrentGlyph()
    
    def decomposeGlyph(srcGlyph):
    	decomposedGlyph = RGlyph()
    	decomposedGlyph.width = srcGlyph.width
    	dstPen = decomposedGlyph.getPointPen()
    	decomposePen = DecomposePointPen(srcGlyph.font, dstPen)
    	srcGlyph.drawPoints(decomposePen)
    	return decomposedGlyph
    
    def glyphImage(g, size, margin):
        g = decomposeGlyph(g)
        bottomX, bottomY, topX, topY = g.bounds
        g.moveBy((-bottomX, -bottomY))
        w = topX - bottomX
        h = topY - bottomY
        maxDimensionGlyph = max([w, h])
        maxDimensionSize = max([d-(margin*2) for d in size])
        scaleFactor = maxDimensionSize / maxDimensionGlyph
        g.scaleBy(scaleFactor)
        g.moveBy((margin, margin))
        pen = CocoaPen(g.layer)
        g.draw(pen)
        image = AppKit.NSImage.alloc().initWithSize_(size)
        image.lockFocus()
        if AppKit.NSApp().appearance() == AppKit.NSAppearance.appearanceNamed_(AppKit.NSAppearanceNameDarkAqua):
            AppKit.NSColor.whiteColor().set()
        else:
            AppKit.NSColor.blackColor().set()
        pen.path.fill()
        image.unlockFocus()
        return image
    
    class ImageButtonDemo:
    
    	def __init__(self):
    		self.w = Window((1000, 1000))
    		self.w.button = ImageButton((10, 10, 50, 50), imageObject=glyphImage(g, (50, 50), 10),
    							callback=self.buttonCallback)
    		self.w.open()
    
    	def buttonCallback(self, sender):
    		print("button hit!")
    
    ImageButtonDemo()
    

Log in to reply