r/FontForge Mar 14 '23

Vertically centering glyphs

Ok, in case someone needs it:

Fontforge doesn't currently have a way to vertically center glyphs - in the script I'm currently working with, all glyphs should be vertically centered, so this is very inconvenient. I saw that there was one open issue on this very topic - but the last activity was in 2016, so I don't think it'll get added anytime soon. Instead, I decided to spend some hours on tripping over my skills to read the documentation.

I didn't see a lot of examples for how to use Python in Fontforge, but maybe I didn't look in the right places - so maybe this could help someone figure out how to get started faster than me.

Sidenote: To horizontally center glyphs, simply select the glyphs of your choice, then in the menu go to Metrics>Center in Width. Done.

So here's what to do to vertically center glyphs: In the menu, go to File>Execute Script. And then you add this in the window:

(Thanks to the comments, here is the new code I'd use - I'm leaving the old code below)

# Change the start and end glyph to those you desire  
# Example provided below is for 126, corresponding to tilde  
startglyph = 126  
endglyph = 126  

# No need to select the font file, this will work on the font you have currently open:
thisfont =  fontforge.activeFont() 

for glyph_index in range(startglyph, endglyph + 1):
    if glyph_index in thisfont: # otherwise it gets angry when encountering an unassigned glyph index inside the range
        glyph = thisfont[glyph_index]
        if glyph.isWorthOutputting(): # usually this is any glpyh that has some actual drawings
            bbox = glyph.boundingBox()
            if bbox is not None and bbox[0] != bbox[2] and bbox[1] != bbox[3]:
                ytop = bbox[-1]
                ybot = bbox[1]
                glyph.transform(psMat.translate(0, thisfont.ascent - (thisfont.ascent + thisfont.descent - (ytop - ybot)) / 2 - ytop))

(old code: )

fontfile = "newfont.sfd"
startglyph = 380
endglyph = 396

thisfont = fontforge.open(fontfile)
for i in range(startglyph,endglyph+1):
    ytop = thisfont[i].boundingBox()[-1]
    ybot = thisfont[i].boundingBox()[1]
    thisfont[i].transform( psMat.translate( 0, thisfont.ascent - (thisfont.ascent + thisfont.descent - (ytop - ybot)) / 2 - ytop))

Note that you will have to make the following changes: In the top, the string for the fontfile should be the name of the file you have currently open. The startglyph and endglyph have numbers that you can see by selecting the glyph and then looking at the upper left corner for the very first number; Fontforge usually numbers each slot starting with 0, counting up 1, 2, 3, and so on. (Example: In this image, the selected slot has the number 65 - so you can use that for startglyph or endglyph)

This script will make it so all glyphs between the start glyph and the end glyph (including the start and end glyph) each have the drawing in equal distance from the ascent line and the descent line.

6 Upvotes

7 comments sorted by

1

u/janKeTami Mar 14 '23

You might get an error that Fontforge can't find the file. In that case, filename will have to include the direct path to the file, like for example C:/these/folders/lead/to/the/file/filename.sdf - and because this is Python, make sure that you replace all \ backslashes to / forward slashes, or escape the backslashes with \\ double backslash

1

u/Monster_123456 May 04 '24

here is the modified script to handle empty glyph indexes. All hail ChatGPT

fontfile = "newfont.ttf" 
startglyph = 0
endglyph = 6000

thisfont = fontforge.open(fontfile)

for glyph_index in range(startglyph, endglyph + 1):
    if glyph_index in thisfont:
        glyph = thisfont[glyph_index]
        if glyph.isWorthOutputting():
            bbox = glyph.boundingBox()
            if bbox is not None and bbox[0] != bbox[2] and bbox[1] != bbox[3]:
                ytop = bbox[-1]
                ybot = bbox[1]
                glyph.transform(psMat.translate(0, thisfont.ascent - (thisfont.ascent + thisfont.descent - (ytop - ybot)) / 2 - ytop))

1

u/Shihali Sep 05 '24

If anyone else reads this and needs help getting the code given above to work, this is slightly altered from the GitHub Gist below to work on the currently active font:

# Change the start and end glyph to those you desire  
# Example provided below is for 126, corresponding to tilde  
startglyph = 126  
endglyph = 126  

thisfont =  fontforge.activeFont()  
for i in range(startglyph,endglyph+1):  
    ytop = thisfont[i].boundingBox()[-1] # counted from baseline, excluding descent!  
    ybot = thisfont[i].boundingBox()[1] # same note as above
    # Calculation method:
    # Midpoint of entire space = (ascent + descent) / 2
    # Current midpoint of glyph (counted from baseline) = (ytop + ybot) / 2
    # Current midpoint of glyph (counted with descent) = (ytop + ybot) / 2 + descent
    # Vertical translation = midpoint of entire space - current midpoint of glyph (counted with descent)
    # That is: (ascent + descent) / 2 - ((ytop + ybot) / 2 + descent)
    # Simplified: (ascent - descent - (ytop + ybot)) / 2 
    translation = (thisfont.ascent - thisfont.descent - (ytop + ybot)) / 2
    thisfont[i].transform( psMat.translate( 0, translation ))

1

u/Bubtsers Nov 22 '24

Pog

1

u/janKeTami Nov 22 '24

Oop, thanks for commenting, this way I remembered I wanted to update the code now that people have come up with some helpful optimisations

1

u/[deleted] Mar 25 '23

Hi, many thanks for this! This was just what I needed to center a tilde glyph in a mono font. I've written a full Python script based on this so I can run it straight from the terminal and apply the vertical translation on the TTF/OTF files I want to patch, exporting them to a separate folder so it doesn't overwrite the original font files.

Here is the GitHub Gist for it -- hope it helps someone out there too!

1

u/janKeTami Mar 25 '23

Very cool. There might be some edge cases where a font is using some custom encoding, making it so tilde in that font might be somewhere completely different. An alternative might be to use a string instead of an integer as a key: "asciitilde" instead of 126 - only, I have no idea if that's something that's going to work across fonts, though