Change UnderlineThickness in compiled font via fontTools

yanone's picture

Hi everyone,

I'd like to change the underline thickness in a compiled OT font automatically, since the AFDKO seems to have no keywords for this.
My tool of choice is fontTools.

When decompiling the font to XML via ttx, this is setting that at least InDesign reads the value from:

<CFFFont name="Antithesis-Regular">
<UnderlineThickness value="90"/>

How to reach this setting via Python+fontTools?
I've come as far as this:

ttf = TTFont(path)
ttf['CFF '].cff

Ant now what? I can't find out how to reach the said setting.
Thank you.

eigi's picture

I found it here:

ttf['CFF '].cff.topDictIndex.items[0].UnderlineThickness


yanone's picture

Eigi, you’re the man!

It worked immediately on my interactive python session that was still open.

But when I run this as a script from the beginning, unfortunately I can’t reproduce this funtionality.
Then, ttf['CFF '].cff.topDictIndex.items[0] is None, and therefore also has no attributes.
There must be some intermediate step to load/decompile something that I unknowingly typed and now can't reproduce. And now that previous session is gone, so I can't look up the steps that I made.
I tried to load just ttf['CFF '] into a variable, to no effect.

Thank you!

Michel Boyer's picture

You must just have made a typo or inadvertently switched from one installation of python to another. The version of fonttools that comes with AFDKO 2.5 works just fine for me. With the following bash script made executable and in your path, which I'll call chthickns (for short)

#!/usr/bin/env AFDKOPython

from fontTools.ttLib import TTFont
import sys

d=f['CFF '].cff.topDictIndex[0]
d.UnderlineThickness = int(sys.argv[2])[3])

the line command, after the % prompt,

% chthickns Myfont.otf 90 newfont.otf

replaces UnderlineThickness of MyFont.otf by 90 and saves the result in newfont.otf. For in situ modification, cf mutatis mutandis.

blokland's picture

Editing this kind of stuff (and a lot of other things too) in .otf and .ttf fonts is IMHO much simpler with DTL OTMaster. And no, it's not for free, but it will save you a lot of time.


yanone's picture

Thank you, Frank.
But it can’t be automated, or can it, and is therefore not saving any time.

So the .items was superfluous in Eigi’s line.
It works with just ttf['CFF '].cff.topDictIndex[0].UnderlineThickness.

Thanks, everyone.

Michel Boyer's picture

So the .items was superfluous [yanone]

Oh, my bad, I had not even seen it (weird, since you repeat it in your post!). I should have read [more carefully].

Michel Boyer's picture

@blokland       Can you batch process with DTL OTMaster?

yanone's picture

Should anyone be interested, here are all the values I’d like to see changed in compiled fonts that are impossible to influence through makeOTF. I didn’t find that any application would ever care about the first four values, which is a shame. But hey, they’re there.
The last two lines overwrite this dreadful version string that the recent FDK creates.
Are there any more suggestions?

Lines may be soft-wrapped.

from fontTools.ttLib import TTFont
ttf = TTFont(ttpath)
ttf['post'].underlinePosition = int(instance.customParameters['underlinePosition'])
ttf['post'].underlineThickness = int(instance.customParameters['underlineThickness'])
ttf['OS/2'].yStrikeoutPosition = int(instance.customParameters['strikeoutPosition'])
ttf['OS/2'].yStrikeoutSize = int(instance.customParameters['strikeoutThickness'])
ttf['CFF '].cff.topDictIndex[0].UnderlineThickness = int(instance.customParameters['underlineThickness'])
ttf['name'].getName(5, 1, 0).string = "Version %s.%s" % (f.versionMajor, f.versionMinor)
ttf['name'].getName(5, 3, 1).string = "Version %s.%s" % (f.versionMajor, f.versionMinor)

Michel Boyer's picture

Using DTL OTM Light I see, for ArnoPro-Regular.otf version 1.000, a value of -100 for float UnderlinePosition in the CFF table and the comment is "Underline stroke center y coordinate"; I see nothing similar in the ttx output. I also see in the post table, with DTL OTM Light, a value of -75 for FWORD underlinePosition with comment "Distance top of underline from baseline (negative = below baseline)". Is the value in the CFF table a calculated value derived from other parameters?

Michel Boyer's picture

This text from the Microsoft spec of the post table appears to answer my question

FWord underlinePosition
This is the suggested distance of the top of the underline from the baseline (negative values indicate below baseline).
The PostScript definition of this FontInfo dictionary key (the y coordinate of the center of the stroke) is not used for historical reasons. The value of the PostScript key may be calculated by subtracting half the underlineThickness from the value of this field.

The value -100 is indeed ttf['post'].underlinePosition - ttf['post'].underlineThickness /2.0

Michel Boyer's picture

I didn’t find that any application would ever care about the first four values, which is a shame.   [yanone]

The fact is that FontForge cares about those values but 'underlinePosition' is interpreted differently. Here is the correspondence

import fontforge
f.os2_strikeypos     % ttf['OS/2'].yStrikeoutPosition
f.os2_strikeysize    % ttf['OS/2'].yStrikeoutSize
f.uwidth             % ttf['post'].underlineThickness

The additional parameter handled by FontForge and corresponding to [Uu]nderlinePosition (I did not look at the source, I just experimented with values), is

f.upos   % ttf['post'].underlinePosition + ttf['post'].underlineThickness/2

in the cases I have tested (and the updates are correctly made with that interpretation). The interpretation of the Microsoft spec above may not, indeed, be crystal clear. The value of f.upos given by FontForge for ArnoPro-Regular.otf is neither -100 nor -75 but -50. (I am running the July 2012 version)

yanone's picture

I meant applications as in word processing, text setting applications. Only few seem to care to read the values from the fonts and apply them.
Thanks for your investigations. Will need a few days to experiment and implement.

blokland's picture

Michel: Can you batch process with DTL OTMaster?
Not really. OTM is basically a highly convenient tool for editing single fonts. The batch-stuff is in our production tools.
At the coming ATypI conference in Amsterdam DTL/URW++ will release a new edition of DTL OTMaster. Also there will be a new OTM Light version. For € 49.95 this version will allow the editing of OpenType and TrueType fonts, but the ‘import’/‘export’ (OT Layout features, Character Layout files, et cetera) and the glyph-editing functions will be only available in the full version.

Syndicate content Syndicate content