Allow for duplicate font variation styles by radarhere · Pull Request #9362 · python-pillow/Pillow
Resolves #9361
Within font_getvarnames(), our code goes through the names, and once the ID of a name matches the ID of a style, it breaks. It presumes that each master->namedstyle would have a unique ID.
| if (master->namedstyle[j].strid == name.name_id) { | |
| list_name = Py_BuildValue("y#", name.string, name.string_len); | |
| if (list_name == NULL) { | |
| PyMem_Free(list_names_filled); | |
| Py_DECREF(list_names); | |
| FT_Done_MM_Var(library, master); | |
| return NULL; | |
| } | |
| PyList_SetItem(list_names, j, list_name); | |
| list_names_filled[j] = 1; | |
| break; | |
| } |
The font found in the issue has two styles with the same ID. To allow for this, we should no longer break and continue to go through the loop. Once the list has been populated in C, Python can remove the duplicate result.
I created a test font here from our existing AdobeVFPrototype.ttf using ttx.
Hmmm, I've worked with the TTF file a bit now, and I think the variants are different - even though they have a same name - and it might be necessary to number them (#1, #2, ...) similar to how Gimp does it or so, otherwise selecting them becomes possible only by using the index number, which isn't returned either. That font is file is properly horrible. :-/
The idea of adding variation name suffixes is a bit concerning from a theoretical standpoint - there's nothing stopping the font from already having both XXtended Regular and XXtended Regular #1 as variation names, and then things become more complicated. You might say that's ridiculous, but it's kind of ridiculous to have identical variation names in the first place.
Would it help if I provided a modified version of the font file, that uses XXtended Regular #1 and XXtended Regular #2 instead? Kario39C3Var-Roman-unique.ttf.zip
Thanks, a bit - but finding a solution working generically would be better, I think. sigh .. what a beepshow.
I've scanned the font with
#!/usr/bin/env python3 from PIL import ImageFont import pprint ttf = ImageFont.truetype(font='Kario39C3Var-Roman.ttf', index=0) #pprint.pp(ttf.font.getvarnames()) for i in range(4294967296+1): try: print("{} (face={}): {}".format('Kario39C3Var-Roman.ttf', i, ttf.font_variant(index=i).getname()), flush=True) except IOError as e: pass
(yeah yeah I know about the const being ridiculous, and I cancelled early)
this way, I could identify the right fonts and use the indicies in a hardcoded for what I need.
While I do still think that this font is flawed, I've created #9376 to offer a solution. It would show the duplicates in font.get_variation_names(), and add a new method to let you choose between them - font.set_variation_by_name_index(i).
from PIL import ImageFont font = ImageFont.truetype('Kario39C3Var-Roman.ttf') for i, name in enumerate(font.get_variation_names()): print("Index", i, "Name", name) font.set_variation_by_name_index(i)
Yupp, that font is horribly flawed, but said fix would help me and potential other running problems with similar fonts.
Thanks for the super quick and helpful interactions, and a happy, healthy new year! 😍
If you're willing to accept a solution using our private APIs (with the disclaimer that we feel the freedom to change them in the future without warning), then with Pillow 12.1.0, you could use the following code.
from PIL import ImageFont font = ImageFont.truetype('Kario39C3Var-Roman.ttf') names = [name.replace(b"\x00", b"") for name in font.font.getvarnames()] for i, name in enumerate(names): print("Index", i, "Name", name) font.font.setvarname(i+1)
luketainton pushed a commit to luketainton/repos_webexmemebot that referenced this pull request
Jan 5, 2026This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters