Saving to .glyphspackage format?
arrowtype opened this issue · 10 comments
It is awesome that glyphsLib (and fontmake) now support .glyphspackage files, as they are really nice for version control.
However, it looks like so far, glyphsLib only supports reading from .glyphspackage files (thanks to #803), but not writing back to them.
I am hoping to increment the version number of a glyphspackage source during a build process, so I would love it if glyphsLib could write to the new format, too.
As an example, here’s some Python I’m trying to use:
import sys
from glyphsLib import GSFont
glyphsSource = sys.argv[1]
versionMinor = sys.argv[2]
font = GSFont(glyphsSource)
font.versionMinor = int(versionMinor)
font.save(glyphsSource)
Unfortunately, running it currently gives this result:
▶ python source/01-build-scripts/helpers/update-glyphs-source-version.py source/familyname.glyphspackage 2
Traceback (most recent call last):
File "/Users/stephennixon/type-repos/familyname/source/01-build-scripts/helpers/update-glyphs-source-version.py", line 11, in <module>
font.save(glyphsSource)
File "/Users/stephennixon/type-repos/familyname/venv/lib/python3.11/site-packages/glyphsLib/classes.py", line 4533, in save
with open(path, "w", encoding="utf-8") as fp:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IsADirectoryError: [Errno 21] Is a directory: 'source/familyname.glyphspackage'
So, partly I am just filing this issue as a feature request, in hopes that saving to glyphspackage might get support soonish.
I do also have two questions:
- Have I just missed something, and is this possible already?
- If this isn’t a feature yet, realistically, is this likely to be supported soonish? Or, is it a larger scope of work / not really a priority right now? (It’s all good either way, I’m just trying to plan for a couple of projects.)
- If it isn’t likely to happen soon, what might be a good way to handle this basic goal (incrementing versions for each build)? I could just use FontTools ttLib or font-v to write versions to font binaries only, or generate UFOs as a step of my build and edit the version number there... but is there another option that I might be missing?
Thanks so much for any insights and/or ideas!
Re: question 3... I guess I could save to .glyphs format, then convert that to a glyphspackage. 🤔 I wonder if I’d lose any data that way. There’s one way to find out, I guess!
We haven't implemented writing .glyphspackage yet (though it should not be too hard in theory).
If you're only interested in changing one field in the global fontinfo.plist just do that, loading the .glyphspackage in glyphsLib is expensive as it loads all the glyphs as well.
Use openstep_plist (which glyphsLib itself uses) to load the glyphspackage's fontinfo.plist, change the version and save it back
Use openstep_plist (which glyphsLib itself uses) to load the glyphspackage's fontinfo.plist, change the version and save it back
Ah, awesome! I hoped someone might give a smart suggestion like this. Will try this out!
In the long run, yeah, I would love to be able to interact with glyphspackage files more fully via glyphsLib, but for now my need is very simple. Thanks for sharing that insight!
In case it helps anyone, here’s the code I ended up with. It seems to work well!
"""
USAGE:
python3 <path_to_script>/update-glyphs-source-version.py <path_to_glyphspackage>/familyname.glyphspackage <integer>
"""
import sys
import os
import openstep_plist
glyphsSource = sys.argv[1] # pass this in as first arg
versionMinor = sys.argv[2] # pass this in as second arg
# make path for fontinfo
fontinfoPath = os.path.join(glyphsSource, "fontinfo.plist")
# read the fontinfo.plist
with open(fontinfoPath, "r", encoding="utf-8") as fontinfo:
data = openstep_plist.load(fontinfo, use_numbers=True)
# update the data from passed-in arg
data['versionMinor'] = int(versionMinor)
# write to the fontinfo.plist
with open(fontinfoPath, "w", encoding="utf-8") as fontinfo:
# the extra args keep things closer to the formatting from GlyphsApp
openstep_plist.dump(data, fontinfo, unicode_escape=False, indent=0, single_line_tuples=True)
# print the update
print(f'Updated {data["familyName"]} with version {data["versionMajor"]}.{str(data["versionMinor"]).zfill(3)}')
hm seems like openstep-plist writer always escapes newline characters and writes multi-line strings as a single line. There seems to be no option to pass through literal newlines:
I think glyphsLib only uses openstep-plist to parse, but not also to dump.
Anyway, apart from the git noise, both representation should be indentical and work the same.
Sorry about that
Okay, thanks for confirming! I had explored the options, but wasn't getting closer.
I appreciate your help here!
If I remember correctly, once upon a time Glyphs.app would write multiline strings (e.g. features) with the newline escaped, even using octal codes \012
if I am not mistaken) then it got changed to write out the newlines as literal characters unescaped.
Anyway, openstep-plist will never match Glyphs.app 100% because it does not know about the quirks of the .glyphs format, it just treats everything the same way, cannot write e.g. unicodes one way or custom parameters antother way etc.
For simple changes like this, you could just read the file as a string and search and replace on it. That would keep the rest of the file unaffected.
Only adding new things might be a bit tricky.
Ah, yeah, I guess I'm basically doing search and replace to fix things after changing them, so I could simplify it and just add the data the first time in glyphs, then search and replace only that.