“Reset” components in custom-defined glyphs, before sizing them? (in Python script)

I’m working on a script with custom GlyphData.xml, as explained in a previous post, Does `.addMissingAnchors()` work for custom GlyphData? - #3 by thundernixon

Some of the marks/accents of this script start as letters, and need to be scaled to be used as accents. For example, there is an accent that is the “a,” but at about the size and position of the typical /ringcomb accent.

I have a script that runs and sizes things nicely if run exactly once, but that isn’t great if I add more accents, or wish to tweak the script slightly. In such cases, I have to delete glyphs, re-add them, and then re-run the script. Obviously, this kind of repetitive work would be better handled in a script!

So, I have two main approaches I’m considering:

  1. In the script, reset the sizing of components before scaling them.
  2. In the script, delete and re-add glyphs, before scaling them.

I tried approach 2 first, but got stuck at a few spots:

  • font.glyphs.append([customglyphname]) doesn’t seem to use components described in GlyphData.xml, so that didn’t work
  • Trying to go through all components, then copy, delete, and add them seemed to just copy them at their current scaling, which didn’t reset anything.

So, I’m now trying to reset the component sizes, by comparing their bounds to those of the glyphs they reference. This is mostly working, but there are some strange side effects:

  • The first matched glyph moves higher, but the scaling works as expected
  • The second match glyph moves higher, but the scaling works as expected
  • The third matched glyph gets a bunch of duplicates, but the scaling works as expected
  • Strangely, the rest of the matched glyphs seem to work great!

I imagine there might be some kind of super-easy way to reset glyphs, but I haven’t found it.

Is there something I’m missing?

Thanks so much for any help or insights!

Glyphs version: 3.4 (cutting edge)


# for glyph in Glyphs
for glyph in Glyphs.font.glyphs:

    if "-xx" in glyph.name and glyph.category == "Letter":
        for layer in glyph.layers:
            for comp in layer.components:
                comp.automaticAlignment = True


    if "-xx" in glyph.name and glyph.category == "Mark":
        if glyph.name == "backslashcomb-xx":
            continue

        print('-----')
        print(glyph)

        glyph.leftMetricsKey = "o"
        glyph.rightMetricsKey = "o"

        for layer in glyph.layers:
            for comp in layer.components:
                # arrange hyphens
                comp.automaticAlignment = True

        for layer in glyph.layers:
            for comp in layer.components:
                # then turn off auto-alignment
                comp.automaticAlignment = False

            # reset scale
            for comp in layer.components:
                parent = font[comp.name].layers[layer.layerId]
                
                w, h = parent.bounds.size.width, parent.bounds.size.height
                currentW, currentH = comp.bounds.size.width, comp.bounds.size.height
                scaleW, scaleH = w/currentW, h/currentH

                x, y = parent.bounds.origin.x, parent.bounds.origin.y
                currentX, currentY = comp.bounds.origin.x, comp.bounds.origin.y
                moveX, moveY = x-currentX, y-currentY

                comp.applyTransform([scaleW,0,0,scaleH,moveX,moveY])
                
            # scale
            layer.applyTransform([
                0.4, # x scale factor
                0.0, # x skew factor
                0.0, # y skew factor
                0.4, # y scale factor
                0.0, # x position
                layer.master.ascender + 150  # y position
            ])
            
            # delete all anchors
            layer.anchors = []

            layer.syncMetrics()

        for layer in glyph.layers:
        
            layer.addMissingAnchors()

Instead of comp.applyTransform() set the scale and position directly.

Ah, nice! I hadn’t realized that components had directly settable .scale() and .postition() properties. Cool!

It took me a bit of experimentation, but I got to a place that is working pretty well.

Thanks!

for glyph in Glyphs.font.glyphs:

    if "-xx" in glyph.name and glyph.category == "Letter":
        for layer in glyph.layers:
            for comp in layer.components:
                comp.automaticAlignment = True


    if "-xx" in glyph.name and glyph.category == "Mark":
        if glyph.name == "backslashcomb-xx":
            continue

        print('-----')
        print(glyph)

        glyph.leftMetricsKey = "o"
        glyph.rightMetricsKey = "o"

        for layer in glyph.layers:
            for comp in layer.components:
                # arrange hyphens
                comp.automaticAlignment = True

        for layer in glyph.layers:
            for comp in layer.components:
                # then turn off auto-alignment
                comp.automaticAlignment = False

            compXShift = 0

            print(layer.name)

            # reset scale
            for comp in layer.components:
                parent = font[comp.name].layers[layer.layerId]

                comp.scale = (1, 1)
                comp.position = (0 + compXShift, 0)

                compXShift += parent.width

            factor = 0.5
            # scale and position as "accent"
            layer.applyTransform([
                factor, # x scale factor
                0.0, # x skew factor
                0.0, # y skew factor
                factor, # y scale factor
                0.0, # x position
                layer.master.ascender + 150  # y position
            ])
            
            # delete all anchors
            layer.anchors = []

            layer.syncMetrics()

            print()

        for layer in glyph.layers:
        
            layer.addMissingAnchors()

I didn’t run it (so there might be some errors) but this should do the same:

for glyph in Glyphs.font.glyphs:

    if "-xx" in glyph.name and glyph.category == "Letter":
        for layer in glyph.layers:
            for comp in layer.components:
                comp.automaticAlignment = True

    if "-xx" in glyph.name and glyph.category == "Mark":
        if glyph.name == "backslashcomb-xx":
            continue

        print('-----')
        print(glyph)

        glyph.leftMetricsKey = "o"
        glyph.rightMetricsKey = "o"

        for layer in glyph.layers:

            factor = 0.5
            compXShift = 0
            print(layer.name)

            # reset scale
            for comp in layer.components:
                parent = comp.componentLayer()

                comp.scale = (factor, factor)
                comp.position = (0 + compXShift, 0)

                compXShift += parent.width * 0.5

            # delete all anchors
            layer.anchors = []

            layer.syncMetrics()
            print()

        for layer in glyph.layers:
            layer.addMissingAnchors()

e.g. the enable/disable of the alignment is overwritten by setting the position directly.

1 Like

Ah, that makes sense! Thanks for the very helpful tips here, and thanks, as always, for making amazing software!

1 Like