Skip to content

Instantly share code, notes, and snippets.

@okay-type
Last active February 14, 2022 18:49
Show Gist options
  • Select an option

  • Save okay-type/e8e608926dcbcef6148cfaadddb2e959 to your computer and use it in GitHub Desktop.

Select an option

Save okay-type/e8e608926dcbcef6148cfaadddb2e959 to your computer and use it in GitHub Desktop.
rf4 version of the Ramsay Street Extension
# menuTitle : Mr Rogers
# shortCut : command+shift+k
import merz
from mojo.subscriber import Subscriber, registerGlyphEditorSubscriber, registerSubscriberEvent, getRegisteredSubscriberEvents, listRegisteredSubscribers, unregisterGlyphEditorSubscriber
from mojo.UI import SetCurrentGlyphByName
from fontTools.pens.basePen import BasePen
from importlib import reload
import MrRogersData
reload(MrRogersData)
from MrRogersData import neighborData
drawInPreview = True
'''
controls:
shortcuts are editable in glyphEditorDidKeyDown, fyi
⌘+⇪+K activate/deactivate tool entirely
⌘+K toggle tool visibility
^K rotate preview through AllFonts
⌥K rotate preview through the list of pairs
shift to rotate the other direction
x3☟ triple click a component to go to that glyph
tip: use previousCurrentGlyph.py to easily return
https://gist.github.com/okay-type/5ff7c9a24162884680b06461a1352abb
data:
data is stored in a second file as a dictionary of lists of tuples:
'glyph': [('leftA', 'rightA'), ('leftB', 'rightB'), ...]
updateNeighborData() automatically adds some control pairs to the list:
(CurrentGlyph, CurrentGlyph)
(H, H)
(O, O)
concept forked from typemytype's ramsayStreetRoboFontExtension
'''
class MrRogers(Subscriber):
debug = False
def build(self):
self.toggleOnOff = True
self.clickable = {}
self.glyphEditor = self.getGlyphEditor()
self.backgroundContainer = self.glyphEditor.extensionContainer(
identifier='com.robofont.drawNeighbors.background',
location='background',
clear=True
)
self.leftNeighborLayer = self.backgroundContainer.appendPathSublayer(
fillColor=(.3, 0, 1, .5),
name='leftNeighbourLayer')
self.rightNeighborLayer = self.backgroundContainer.appendPathSublayer(
fillColor=(.3, 0, 1, .5),
name='rightNeighborLayer')
self.previewContainer = self.glyphEditor.extensionContainer(
identifier='com.robofont.drawNeighbors.preview',
location='preview',
clear=True
)
self.leftPreviewLayer = self.previewContainer.appendPathSublayer(
fillColor=(0, 0, 0, 1),
name='leftPreviewLayer')
self.rightPreviewLayer = self.previewContainer.appendPathSublayer(
fillColor=(0, 0, 0, 1),
name='rightPreviewLayer')
# all fonts
self.neighborIndex = 0
self.updateFontList()
self.allFontsActive = self.allFonts.index(CurrentFont())
self.neighborData = self.updateNeighborData(neighborData.neighbors())
def updateNeighborData(self, neighborData):
f = CurrentFont()
fontglyphs = f.keys()
# add glyphs in font that aren't in dict
for glyphName in fontglyphs:
if glyphName not in neighborData:
neighborData[glyphName] = []
# if they're suffixed, try to add a base glyphs
if '.' in glyphName:
gbase, gsuffix = glyphName.split('.', 1)
if gbase in neighborData:
for pair in neighborData[gbase]:
spair = (pair[0]+'.'+gsuffix, pair[1]+'.'+gsuffix)
if spair[0] in fontglyphs and spair[1] in fontglyphs:
neighborData[glyphName].append(spair)
# add basic control pairs to every glyph
for glyphName in neighborData:
neighborData[glyphName].append((glyphName, glyphName))
pairs = [
('H', 'H'),
('O', 'O'),
('d', 'h'),
('o', 'o')
]
for pair in pairs:
if pair not in neighborData[glyphName]:
neighborData[glyphName].append(pair)
if '.' in glyphName:
gbase, gsuffix = glyphName.split('.', 1)
spair = (pair[0]+'.'+gsuffix, pair[1]+'.'+gsuffix)
if spair[0] in fontglyphs and spair[1] in fontglyphs:
neighborData[glyphName].append(spair)
return neighborData
def destroy(self):
self.backgroundContainer.clearSublayers()
self.previewContainer.clearSublayers()
def glyphEditorDidSetGlyph(self, info):
glyph = info['glyph']
if glyph is None:
return
if glyph.name in self.neighborData and self.neighborIndex > len(self.neighborData[glyph.name])-1:
self.neighborIndex = 0
self.drawNeighbors(glyph)
def glyphEditorGlyphDidChange(self, info):
glyph = info['glyph']
self.drawNeighbors(glyph)
def glyphEditorGlyphMetricsDidChange(self, info):
glyph = info['glyph']
self.drawNeighbors(glyph)
def glyphEditorDidKeyDown(self, info):
deviceState = info['iterations'][0]['deviceState']
keyDown = deviceState['keyDownWithoutModifiers']
controlDown = deviceState['controlDown']
optionDown = deviceState['optionDown']
commandDown = deviceState['commandDown']
shiftDown = deviceState['shiftDown']
if keyDown == 'k' or keyDown == 'K':
# toggle view on-off
if commandDown != 0:
self.toggleOnOff = not self.toggleOnOff
if CurrentGlyph():
self.drawNeighbors(CurrentGlyph())
# rotate fonts
if controlDown != 0:
if shiftDown == 0:
self.allFontsActive += 1
if self.allFontsActive > len(self.allFonts)-1:
self.allFontsActive = 0
else:
self.allFontsActive += -1
if self.allFontsActive < 0:
self.allFontsActive = len(self.allFonts)-1
if CurrentGlyph():
self.drawNeighbors(CurrentGlyph())
# rotate fonts
if optionDown != 0:
if CurrentGlyph():
if CurrentGlyph().name in self.neighborData:
if shiftDown == 0:
self.neighborIndex += 1
if self.neighborIndex > len(self.neighborData[CurrentGlyph().name])-1:
self.neighborIndex = 0
else:
self.neighborIndex += -1
if self.neighborIndex < 0:
self.neighborIndex = len(self.neighborData[CurrentGlyph().name])-1
self.drawNeighbors(CurrentGlyph())
def glyphEditorDidMouseDown(self, info):
lle = info['lowLevelEvents'][0]
point = lle['point']
cc = lle['clickCount']
if cc == 3:
for componentKey in self.clickable:
componentObject = self.clickable[componentKey]
path = componentObject.naked().getRepresentation('defconAppKit.NSBezierPath')
if path.containsPoint_(point):
SetCurrentGlyphByName(componentKey.replace('_LLLL', '').replace('_RRRR', ''))
break
def fontDocumentDidOpen(self, info):
self.updateFontList()
def fontDocumentDidClose(self, info):
self.updateFontList()
def updateFontList(self):
self.allFonts = AllFonts()
if len(self.allFonts) > 1:
weight_classes = [f.info.openTypeOS2WeightClass for f in self.allFonts]
width_classes = [f.info.openTypeOS2WidthClass for f in self.allFonts]
if all(weight_classes) and all(width_classes):
self.allFonts = sorted(self.allFonts, key=lambda f: (f.info.openTypeOS2WeightClass, f.info.openTypeOS2WidthClass))
else:
self.allFonts = AllFonts('styleName')
def drawNeighbors(self, glyph):
self.clickable = {}
if glyph is None:
return
if self.toggleOnOff is False:
self.leftNeighborLayer.setPath(None)
self.rightNeighborLayer.setPath(None)
self.leftPreviewLayer.setPath(None)
self.rightPreviewLayer.setPath(None)
return
font = self.allFonts[self.allFontsActive]
glyphName = glyph.name
neighborGlyphs = ['H', 'H']
if glyphName in self.neighborData:
neighborGlyphs = self.neighborData[glyphName][self.neighborIndex]
neighborGlyphs = (self.ifGlyphInFont(neighborGlyphs[0], glyphName, font), self.ifGlyphInFont(neighborGlyphs[1], glyphName, font))
leftGlyph = font[neighborGlyphs[0]]
rightGlyph = font[neighborGlyphs[1]]
self.leftNeighborLayer.setPath(leftGlyph.getRepresentation('merz.CGPath'))
self.leftNeighborLayer.setPosition((-leftGlyph.width, 0))
self.rightNeighborLayer.setPath(rightGlyph.getRepresentation('merz.CGPath'))
self.rightNeighborLayer.setPosition((glyph.width, 0))
self.leftPreviewLayer.setPath(leftGlyph.getRepresentation('merz.CGPath'))
self.leftPreviewLayer.setPosition((-leftGlyph.width, 0))
self.rightPreviewLayer.setPath(rightGlyph.getRepresentation('merz.CGPath'))
self.rightPreviewLayer.setPosition((glyph.width, 0))
leftGlyphCopy = leftGlyph.copy()
leftGlyphCopy.moveBy((-leftGlyph.width, 0))
self.clickable[leftGlyph.name+'_LLLL'] = leftGlyphCopy
rightGlyphCopy = rightGlyph.copy()
rightGlyphCopy.moveBy((glyph.width, 0))
self.clickable[rightGlyph.name+'_RRRR'] = rightGlyphCopy
def ifGlyphInFont(self, g, fallback, font):
if g in font.glyphOrder:
return g
else:
return fallback
if __name__ == '__main__':
registered_subscribers = listRegisteredSubscribers(subscriberClassName='MrRogers')
if len(registered_subscribers) > 0:
for target_subscriber in registered_subscribers:
unregisterGlyphEditorSubscriber(target_subscriber)
print('Toggle Mr. Rogers off')
else:
registerGlyphEditorSubscriber(MrRogers)
print('Toggle Mr. Rogers on')
class neighborData():
def neighbors():
return {
'A': [('H', 'V')],
'C': [('c', 'G')],
'B': [('P', 'D')],
'E': [('B', 'F')],
'D': [('B', 'P')],
'G': [('C', 'O')],
'F': [('P', 'E')],
'I': [('J', 'H')],
'H': [('I', 'P')],
'K': [('k', 'I')],
'J': [('j', 'I')],
'M': [('H', 'N')],
'L': [('I', 'H')],
'O': [('C', 'o')],
'N': [('M', 'V')],
'Q': [('O', 'G')],
'P': [('R', 'p')],
'S': [('C', 's')],
'R': [('B', 'P')],
'U': [('u', 'H')],
'T': [('I', 'H')],
'W': [('w', 'V')],
'V': [('v', 'W')],
'Y': [('y', 'V')],
'X': [('x', 'Y')],
'Z': [('z', 'X')],
'a': [('n', 'e')],
'c': [('e', 'C')],
'b': [('d', 'p')],
'e': [('o', 'c')],
'd': [('q', 'b')],
'g': [('o', 'q')],
'f': [('i', 't')],
'i': [('period', 'j')],
'h': [('l', 'n')],
'k': [('h', 'K')],
'j': [('i', 'period')],
'm': [('n', 'w')],
'l': [('h', 'k')],
'o': [('c', 'O')],
'n': [('h', 'm')],
'q': [('d', 'p')],
'p': [('q', 'P')],
's': [('e', 'S')],
'r': [('s', 'n')],
'u': [('v', 'n')],
't': [('s', 'f')],
'w': [('v', 'W')],
'v': [('u', 'w')],
'y': [('v', 'Y')],
'x': [('y', 'X')],
'z': [('x', 'Z')
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment