How to automate Glyphs export?

Is there a headless version of Glyphsapp to export fonts with Glyphs using a shell script?

I just replied to Marc by email. There is no headless version (yet? The app depends on many resources like GlyphData and plugins. Loading all of them each time you call it, would be quite slow).

But you can control the normal app with AppleScript. I just had a look at it and improved a few minor things. I’ll post some sample code later. And it is possibel to run apple script from Python, too.

That means you need an apple server. It might be possibel to build a version that runs on Linux but that would mean a few weeks of work that I can’t spare at the moment.

Requiring the operating system to be OS X is no trouble.

The startup time issue is a valid concern, but in itself its just nice-to-have and is what the daemon model is for. The way Mac OS X works, running Glyphs on a desktop is running it as a daemon, which, as you say, can be controlled via the AppleScript API using a Python shell script. Would be great if you can post your AppleScript code in https://github.com/schriftgestalt/GlyphsSDK :slight_smile:

My question is actually about running Glyphs ‘headless,’ on a Mac OS X machine without a GUI, such as Travis CI

When the operating system is GNU, then there’s a virtual X Window System server for machines without physical displays which can be used on Travis CI for rendering X GUIs, and xdotool to fake X mouse/keyboard input events.

But, I’m not aware of anything like that for Mac OS X, and, lacking such generic tooling, I wonder if there is or could be a ‘headless’ version.

Perhaps this could be similar to FontForge’s headless version - when you install FF on OS X with brew install fontforge then run fontforge you get the embedded Python interpreter’s >>> prompt with the fontforge module pre-loaded (no need to run import fontforge) so that you can do things like fontforge compile-my-project.py source-file.sfd that don’t involve a GUI.

In writing this I’m also reminded of how TextMate (https://github.com/textmate/textmate) has, since 2005, had a little shell utility tool to go with its GUI (http://blog.macromates.com/2005/textmate-shell-utility-tmmate/)

I seem to remember there was some “Glyphs Project File” stuff, and it seems natural to me that project files would have a headless program to operate them with.

If you run on macOS, you could just ignore the UI and tread the app as a headless daemon.

here is a sample script:

tell application "Glyphs"
	set doc to document 1
	set aFont to font of doc
	repeat with aInstance in instances of aFont
		if active of aInstance then
			generate aInstance
		end if
	end repeat
end tell

Thanks Georg!

Here is a different example:

tell application "Glyphs"
	set doc to document 1
	set aFont to font of doc
	set aInstance to instance 1 of aFont
	
	set ResultFolder to POSIX file (do shell script "dirname " & quoted form of POSIX path of ((file of doc) as alias)) as alias
	
	log name of aInstance as string
	generate aInstance to ResultFolder as OTF
end tell

There are a few options to the generate command. They are mentioned in Glyphs Scripting Dictionary.

Hey Georg,

My knowledge of Applescript is flakey at best. I keep recieving Syntax Errors.

I’m running El Capitan and your scripts are being run through ‘Script Editor’.

Cheers
Marc

I’m having some success with the first sample. I’ll get back to you if I run into further problems. Once again, thank you for this.

I wrote the script in 10.10.5 and it works on 10.11.6, too. Maybe you copied some hidden chars with it.

click on the square button in the lower left and then on Replies. Then run or compile the script again. But if it is not even compiling, then there is some typo somewhere.

Script Editor usually helps you find the problematic line. Try the Compile button first.

1 Like

@Marc_Foley what is the latest status of this?

I have no issues with generating an instance via an applescript but is there any documentation on generating a variable font with an appleScript?

Additionally, how would one run the scripts in the top bar (i.e. Script > Insert Instances, etc.) from an appleScript or is there a better way to do this?

I managed to cobble together a script that can do this through clicks using “system services”, but I have been unable to find a way to set an export destination

tell application "Finder"
	activate
	open (POSIX path of ((path to me as text) & "::")) & "HeptaSlab-Build.glyphs" as POSIX file using application file "Glyphs Cutting Edge.app" of folder "Applications" of startup disk
	tell application "System Events" to tell process "Glyphs" to ÂŹ
		tell menu bar item 3 of menu bar 1
			click
			tell menu item 15 of menu 1
				click
			end tell
			keystroke return
		end tell
end tell

Just to get some context: Why are you doing it in AppleScript?

I am not tied to using AppleScript, in fact I would prefer not to use it at all. I wanted to automate the export process for a project I’m working on, so that if I make any edits to the source file I can run something like a build.sh to get my final .ttf

Ideally I wanted to make a build script to:

  • duplicate my file
  • run one of my scripts from the Glyphs scripts folder on the new file
  • export the variable font
  • run some google fonts scripts
  • convert to ttx, add an xml patch
  • convert to ttf

I was thinking I needed AppleScript for the 3rd step and maybe the 2nd step

Then you should not use Apple Script. Have a look at the folder Glyphs remote scripts in the GlyphsSDK repo. The following script should get you started.

#!/usr/bin/env python
# encoding: utf-8

import sys
import os
from Glyphs import *
from Foundation import NSURL

def main():
	font = currentDocument.font()
	fontCopy = font.copy()
	
	# run the script with exec(), make sure that the script can get to the right font object. Depends what the script is doing. 
	
	variationFontPlugin = Glyphs.objectWithClassName_("GlyphsFileFormatVariationFonts") #.fileFormatInstances()
	variationFontPlugin.setFont_(fontCopy)
	destination = NSURL.fileURLWithPath_("/Users/georg/Stuff/")
	print variationFontPlugin.writeVARTables_error_(destination, None)
	
	# you know the rest

if __name__ == '__main__':
	main()
2 Likes

Great, thanks for this!

I will take a look at the repo

I am trying to do sth similar in Objective-C. I seem to get the exporter via [[NSClassFromString(@"GlyphsFileFormatVariationFonts") alloc] init]; but the GlyphsFileFormatVariationFonts that it returns is not a known object to the compiler and hence I cannot access its methods. I cannot find it in the GlyphsCore headers either.

Basically I try to make it run writeVARTables:error: or _exportToURL:error (as written in the GlyphsApp python wrapper (the leading underscore there confuses me, I tried with and without when reforming to obj-c)).

You need to add a @interface defining the method. This would replace the header (that you don’t have) and tell the compiler what the object can do.

@interface GlyphsFileFormatVariationFonts (private)
- (BOOL)writeVARTables:(NSString *)TTFPath destination:(NSURL *)DestinationURL instance:(GSInstance *)instance error:(NSError **)error;
@end

(the API seems to have changed since I posted the code)

if that is not working because it doesn’t know the GlyphsFileFormatVariationFonts class, use NSObject instead.

@interface NSObject (private)



Oh, I was pretty close, I tried that before:

// Whoever reads this: don't use it :D
@interface GlyphsFileFormatVariationFonts : NSObject
-(id)exportToURL:(NSURL*)destination error:(NSError*)error;
-(void)setFont:(GSFont*)font;
@end

Thank you!

What does TTFPath take? The filename like @"afont.ttf"
and what/why does it need a GSInstance (I tried to just use the first one)?

GlyphsFileFormatVariationFonts *exporter = [[NSClassFromString(@"GlyphsFileFormatVariationFonts") alloc] init];
[exporter setFont:[thisFont copy]];
NSURL *destination = [NSURL fileURLWithPath:(@"/Users/Mark/Desktop/")];
NSError *error = [NSError new];
BOOL result = [exporter writeVARTables:@"testste.ttf" destination:destination instance:[thisFont.instances firstObject] error:&error];

What I notice:

  1. the compiler only shuts up if I declare it like so: @interface GlyphsFileFormatVariationFonts : NSObject or like you said with @interface NSObject (private)
  2. It looks like I also must define the setFont: method
  3. When I set the font, it only works partially* when I create the copy directly in the argument of setFont:, not when I create a font copy as a variable before and use that
    *) partially means, that it doesn’t work yet, but at least in that case I get the Error as such: `Domain=GSGlyphsDomain Code=987 “Please define an ‘Axes’ parameter in Font Info > Font” UserInfo={NSLocalizedDescription=Please define an ‘Axes’ parameter in Font Info > Font}``
    (The font has an “Axes” parameter, so somewhere it gets lost.