Glyphs.font.currentTab.layers broken?

here’s what I get:

Traceback (most recent call last):
  File "<string>", line 174, in <module>
  File "GlyphsApp/__init__.py", line 124, in __repr__
    
  File "GlyphsApp/__init__.py", line 140, in __iter__
    
  File "GlyphsApp/__init__.py", line 7295, in values
    
  File "GlyphsApp/__init__.py", line 7257, in deactivateFeatures
    
NameError: global name 'copy' is not defined

I noticed this a few cutting edge versions ago, but have been busy and didn’t get around to reporting it. I compared the GlyphsApp __init__.py to a previous version and use of copy was added, but there’s no import copy. To work around for the moment I just added to my scripts:

import copy import GlyphsApp GlyphsApp.copy = copy

1 Like

I fixed the import.

I’m not sure the current implementation is what I would expect (I didn’t implement it). tab.layers returns a list of the layers that you have typed. It does not change if you activate features. What do you think?

1 Like

Thanks @zakkuri and @GeorgSeifert!

Well, that’s a tricky question, Georg. I can imagine that returning the actual layers might be more logical in some cases. On the other hand: for the potential use I can think of just now it’s already fine with the typed layers. It would not return all the layers in cases like when you hit that Show All Masters command as well?

Can I ask what’s the function that applies the features in a tab?

I think you can use these on the tab:

.selectedFeatures().append(featureTag) and/or .selectedFeatures().addObject_(featureTag)

Thank you! I phrased it poorly, I mean I’d like to get the changed list of layers/glyphs after features applied :slight_smile:

Then Font.currentTab.allLayers() will show the “changed” or “feature-applied” layers

1 Like

Perfect, thank you!

1 Like

Hi! Is there a way to get matching layers before and after features in a tab? I see that tab.selectedLayers() somehow keeps track of it (when you select ff it switches to f_f with features on and vice versa). Here’s roughly what I’m trying to get:

{
f_f: (f, f),
a.ss01: (a)
}

There are two levels of information. The string that you entered and a list of layer that are visible. That is all that you should need.

From the string you can get to the glyphs/layers. Does this help?

tab = Font.currentTab
print(tab.string)
print(tab.layers)
string = tab.string
for c in string:
	print(c, Font.glyphForCharacter_(ord©))

Sorry, not sure I get that. Or perhaps my explanation was poor.

If in a tab I have these layers:
a, f, f, n — so that’s 4 layers

With features on, they turn into
a, f_f, n — meaning 4 layers turn into 3, changing indexing

And for that I want to get corresponding pairs:

a = a
f, f = f_f
n = n

That info is not directly available. And often it is much more complicated. Think of one to many substitutions …

What do you need that info for?

The API that Glyphs uses internally is

from Foundation import NSMakeRange
tab = Font.currentTab
layoutManager = tab.graphicView().layoutManager()
print(layoutManager.characterIndexForLayerIndex_(2))
print(layoutManager.layerIndexForCharacterIndex_(3))
print(layoutManager.characterRangeForLayerRange_(NSMakeRange(3, 1)))
print(layoutManager.layerRangeForCharacterRange_(NSMakeRange(3, 1)))

With this you might be able to build the mapping you need.

1 Like

That works, thank you!
Is that a bug with how escaped characters are counted as 2? Shouldn’t it be either 1 or length of the actually typed out characters?

tab.text = 'o/f_f n'
for which characterIndexForLayerIndex returns:

['o', 0]
['f_f', 1]
['n', 3] # shouldn’t this be either 2 or 6 though?

UPD:
Seems like counting actual characters makes more sense because now indexing after escaped characters is messed up:

tab.text = "o/f_f n"
layerIndex = 2 # which is n
charIndex = layoutManager.characterIndexForLayerIndex_(layerIndex) # that’s underscore
print(tab.allLayers()[layerIndex])
print(tab.text[charIndex])

Prints:

<GSLayer "Regular” (n)>
_

Not sure why/how Glyphs’ selection works fine, I have to add a counter of the length of all escaped characters to work around that.

Don’t use .text. Use .string.

1 Like

Oh, I see! However, in this case it also seems a bit off, because escaped characters add 2 to indexing:

tab.text = "o/f_f n"
layerIndex = 2 # which is n
charIndex = layoutManager.characterIndexForLayerIndex_(layerIndex)
print(tab.allLayers()[layerIndex]) # correct
print(tab.string[charIndex]) # out of range, charIndex-1 would be correct

Prints:

<GSLayer "Regular” (n)>
Traceback (most recent call last):
  File "<macro panel>", line 5
IndexError: string index out of range

Yes. I was about it write this. In objectiveC, a string is UTF16. So an upper plane (anything above 0xFFFF) char is two bytes. Python seems to smooth over this. I’ll see what is the best way to deal with this. But maybe just adding on to your index for upper plane chars is enough?

2 Likes

Yes, I got it working in this case, just wasn’t sure if it’s a bug that will change or should I proceed with the workaround.

That is not to change.