Skip to content

Instantly share code, notes, and snippets.

@ryanbugden
Last active August 3, 2023 02:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanbugden/326ba693da025634e8f80c22e4d53eb9 to your computer and use it in GitHub Desktop.
Save ryanbugden/326ba693da025634e8f80c22e4d53eb9 to your computer and use it in GitHub Desktop.
RF start-up scripts for moving SVGs to and from Figma (or other apps) via pasteboard.
# menuTitle: Accept SVGs from Figma (Start-up script)
'''
An RF start-up script that allows inbound SVG data from Figma via pasteboard.
Should work on simple shapes, might not work for others.
Ryan Bugden
'''
import AppKit
from mojo.events import addObserver, removeObserver
from fontTools.svgLib.path import SVGPath
class AcceptFigmaSVG():
def __init__(self):
addObserver(self, "convert_svg", "paste")
def convert_svg(self, notification):
pasteboard = AppKit.NSPasteboard.generalPasteboard()
data = pasteboard.dataForType_(AppKit.NSPasteboardTypeString)
text = data.decode()
if text[0:4] == "<svg":
## Add preceding tag that currently works for RoboFont (doesn't seem to be necessarily with fontTools)
# text = '<?xml version="1.0" encoding="UTF-8"?>\n' + text
# Paste the new svg content...
# Instantiate an empty glyph object, and draw the svg to it.
hold_g = RGlyph()
pen = hold_g.getPen()
svg = SVGPath.fromstring(text, transform=(1, 0, 0, -1, 0, 0))
svg.draw(pen)
# Move the glyph to (0,0), and add it to the current glyph
CurrentGlyph().appendGlyph(hold_g, offset=(-hold_g.bounds[0], -hold_g.bounds[1]))
print("Successfully pasted SVG!")
AcceptFigmaSVG()
# menuTitle: Copy Glyph as SVG, Menu Item (Start-up script)
'''
An RF start-up script that places Copy Glyph as SVG in your Edit menu.
(For pasting your glyph into applications that really prefer SVGs, like Figma)
Ryan Bugden
2023.05.16
'''
from AppKit import NSApp, NSMenuItem, NSAlternateKeyMask, NSCommandKeyMask, NSPasteboard, NSPasteboardTypeString
from fontTools.pens.basePen import BasePen
from mojo.tools import CallbackWrapper
from mojo.subscriber import Subscriber, registerGlyphEditorSubscriber
class SVGpen(BasePen):
'''
SVG pen implementation (C) 2012 by Andreas Eigendorf
MIT license - https://github.com/fontfont/RoboChrome/blob/master/RoboChrome.roboFontExt/lib/colorfont/svgPen.py
'''
def __init__(self, glyphSet):
BasePen.__init__(self, glyphSet)
self.d = ""
def _moveTo(self, pt):
x, y = pt
self.d += "M%s %s" % (int(round(x)), int(round(y)))
def _lineTo(self, pt):
x, y = pt
self.d += "L%s %s" % (int(round(x)), int(round(y)))
def _curveToOne(self, pt1, pt2, pt3):
x1, y1 = pt1
x2, y2 = pt2
x3, y3 = pt3
self.d += "C%d %d %d %d %d %d" % (
int(round(x1)),
int(round(y1)),
int(round(x2)),
int(round(y2)),
int(round(x3)),
int(round(y3)),
)
def _closePath(self):
self.d += "Z"
def glyph_to_svg(glyph, color="#000", group=False, scale=1):
'''
Returns an SVG document for a glyph
Edit of a demo by Jens Kutilek - TypoLabs 2016:
https://github.com/jenskutilek/TypoLabs2016/blob/master/17%20SVG%20Export.py
'''
font = glyph.font.copy() # Copy to handle components (decompose).
upm = font.info.unitsPerEm
glyph = font[glyph.name]
glyph.decompose() # Handle components (decompose).
glyph_width = glyph.bounds[2] - glyph.bounds[0]
glyph_height = glyph.bounds[3] - glyph.bounds[1]
bottom_left = glyph.bounds[0], glyph.bounds[1]
svg_doctype = """<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>"""
svg_doc = (
"""<svg enable-background="new 0 0 64 64" id="%s" width="%d" height="%d" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">"""
% (glyph.name, glyph_width * scale, glyph_height * scale)
)
pen = SVGpen(glyph)
glyph.draw(pen)
if pen.d:
if group:
svg_doc += '<g fill="%s">' % (color)
svg_doc += (
'<path transform="scale(%s -%s) translate(%d, %d)" d="%s"/>'
% (
scale,
scale,
-bottom_left[0] * scale,
(-bottom_left[1] - glyph_height) * scale,
pen.d,
)
)
if group:
svg_doc += "</g>"
svg_doc += "</svg>"
return svg_doc
class copy_glyph_svg_menu(Subscriber):
'''
Places “Copy Glyph as SVG” in the Edit menu,
and carries out the action when clicked.
'''
def build(self):
# Put in the menu item
title = "Copy Glyph as SVG"
edit_menu = NSApp().mainMenu().itemWithTitle_("Edit")
if edit_menu:
edit_menu = edit_menu.submenu()
if not edit_menu.itemWithTitle_(title):
index = edit_menu.indexOfItemWithTitle_("Copy")
self.target = CallbackWrapper(self.copy_glyph_svg)
new_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(title, "action:", "")
new_item.setKeyEquivalentModifierMask_(NSAlternateKeyMask | NSCommandKeyMask)
new_item.setTarget_(self.target)
edit_menu.insertItem_atIndex_(new_item, index+1)
else:
print("Copy Glyph as SVG Menu Item - Error")
def copy_glyph_svg(self, sender):
svg_doc = glyph_to_svg(CurrentGlyph())
pasteboard = NSPasteboard.generalPasteboard()
pasteboard.clearContents()
output = pasteboard.setString_forType_(svg_doc, NSPasteboardTypeString)
print('Copy Glyph as SVG - Output:\n', svg_doc)
registerGlyphEditorSubscriber(copy_glyph_svg_menu)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment