It would be great to have the ability to write some python (or link a python file) from within a class or feature for this kind of situation (the python being built on build).
I usually feel like tokens are too limiting and complex at the same time.
They only allow to get information from the API, but do not allow string modification.
They also use a simple, yet complex enough syntax so that I have to look up the documentation or prior example every time.
But I really like the idea to have everything set tidy and auto-built from within the glyphs UI.
I would love to have the ability to do something like:
# Python Start
for g in Glyphs.glyphs:
print('sub'+g+' by '+g+'.ss01');
# Python Stop
You can have a Python script that generates the code and writes that to the prefixes/classes/features. The benefit then is that you don’t need to run Python when exporting fonts (faster exports, easier to hand a file to someone else, no need to debug Python errors when exporting files).
You can have a python script, it’s true, but to my knowledge at least, it is not possible to automaticallty run a defined python script on export. (tell me if i’m wrong, I would love to)
Something like Update Features, but with python script(s) attached.
For the faster export, i agree with you, but this is a tradeoff that I would be happy to make, rather than having to run the python(s) script manually myself.
Otherwise, I don’t see how linking an external file (or embedding the code) within a feature would make things harder to share. To me it would be quite the opposite actually.
Also having a python script in the context of a feature or class and returning a string that would automatically fill in that class (or feature) would make the python script even simpler to write.
There is no callback for just before export, but there is the DOCUMENTWASSAVED callback, which might be good enough.
The other person might not have Python installed. Or a different version of Python. Or different modules than you.
That’s certainly true. I myself would also like to have more capabilities for generating feature code with custom programming, but there is no one single simple solution. Many proposals have come and gone with different tradeoffs. Generating FEA code is simple enough, but at that point you might as well have a system more geared towards the underlying concepts of feature code like FEZ or FeaComposer.
Thanks for the DOCUMENTWASSAVED` callback, I didn’t think about that option.
A run python on export plugin would be amazing.
For sure it would be great to have a standard for complex feature code.
I hear you on the issue of Python versions, that’s annoying… But I feel like Glyphs has been pushing toward embedding its own python for a while now. Also the function that would be run in this context would be very standard string manipulation, so maybe not a issue?
Anyway having either Python, or a library like FEZ or FeaComposer (even better, both!) directly embedded in Glyphs would be a game changer. Looking forward to that!
Well, that’s how it might start out, but I’m sure it would not take long until people start importing Unicode data base modules and doing all kinds of complex stuff.
All that is possible today, too, just not something for which Glyphs offers a dedicated UI. But I think a DOCUMENTWILLEXPORT callback or similar should be doable.
DOCUMENTWILLEXPORT would be amazing.
Please let me know if you do it. I started writing a small plugin to do exactly that. For now using DOCUMENTWASSAVED, but it would only really make sense if DOCUMENTWILLEXPORT existed.
import re, io
from itertools import chain
from contextlib import redirect_stdout
def extract_code(source):
match = re.search(r"#start python(.*?)#end python", source, re.DOTALL)
if match:
extracted_code = match.group(1).strip()
if(extracted_code):
cleaned_code = re.sub(r'^([ \t]*)#', r'\1', extracted_code, flags=re.MULTILINE)
return cleaned_code
else:
return
def generate_code(original, replacement_code):
new_code = re.sub(
r'(#start python\n)(.*?)(\n#end python)',
rf'\1{replacement_code}\3',
original,
flags=re.DOTALL
)
return new_code
def restore_code(source, new_code):
print('Run this one after export to restore the original')
#Replace Python code by generated code
def exportCallback(info):
try:
#print(info.object())
for f_c in chain(Glyphs.font.features, Glyphs.font.classes):
if(not f_c.automatic):
#print(f_c.name)
code = extract_code(f_c.code)
if(code):
buffer = io.StringIO()
with redirect_stdout(buffer):
exec(code)
built_code = buffer.getvalue()
#Backup Python Code in notes
f_c.notes = '\n'.join(re.sub(r'^(\s*)', r'\1#', line) for line in code.splitlines())
#Replace code by generated code
f_c.code = generate_code(f_c.code, built_code)
Glyphs.font.compileFeatures()
except:
# Error. Print exception.
import traceback
print(traceback.format_exc())
#When Export is finished restore Python code
def endExportCallback(info):
try:
for f_c in chain(Glyphs.font.features, Glyphs.font.classes):
if(not f_c.automatic):
f_c.code = generate_code(f_c.code, f_c.notes)
f_c.notes = ''
except:
# Error. Print exception.
import traceback
print(traceback.format_exc())
# add your function to the hook
Glyphs.addCallback(exportCallback, DOCUMENTWASSAVED) #future DOCUMENTWILLEXPORT
Glyphs.addCallback(endExportCallback, DOCUMENTEXPORTED)