Skip to content

Conversation

@radarhere
Copy link
Member

@radarhere radarhere commented Dec 30, 2025

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.

Pillow/src/_imagingft.c

Lines 1280 to 1291 in 2ebb3e9

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.

Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
@hugovk hugovk mentioned this pull request Dec 31, 2025
@radarhere radarhere merged commit d629550 into python-pillow:main Dec 31, 2025
59 checks passed
@radarhere radarhere deleted the duplicate_name branch December 31, 2025 21:53
@berlincount
Copy link

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. :-/

@radarhere
Copy link
Member Author

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

@berlincount
Copy link

berlincount commented Jan 2, 2026

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.

@radarhere
Copy link
Member Author

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)

@berlincount
Copy link

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! 😍

@hugovk
Copy link
Member

hugovk commented Jan 3, 2026

I'm not sure if it's worth adding (and maintaining) extra APIs to support horribly flawed fonts.

@radarhere
Copy link
Member Author

radarhere commented Jan 4, 2026

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, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [pillow](https://github.com/python-pillow/Pillow) ([changelog](https://github.com/python-pillow/Pillow/releases)) | `<12.0.1,>=12.0.0` → `<12.1.1,>=12.1.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/pillow/12.1.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/pillow/12.0.0/12.1.0?slim=true) |

---

### Release Notes

<details>
<summary>python-pillow/Pillow (pillow)</summary>

### [`v12.1.0`](https://github.com/python-pillow/Pillow/releases/tag/12.1.0)

[Compare Source](python-pillow/Pillow@12.0.0...12.1.0)

<https://pillow.readthedocs.io/en/stable/releasenotes/12.1.0.html>

#### Deprecations

- Deprecate getdata(), in favour of new get\_flattened\_data() [#&#8203;9292](python-pillow/Pillow#9292) \[[@&#8203;radarhere](https://github.com/radarhere)]

#### Documentation

- Specify APNG duration type when opening [#&#8203;9368](python-pillow/Pillow#9368) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Added release notes for [#&#8203;9350](python-pillow/Pillow#9350) [#&#8203;9366](python-pillow/Pillow#9366) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update ImageMorph documentation [#&#8203;9349](python-pillow/Pillow#9349) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Docs: update major bump cadence [#&#8203;9334](python-pillow/Pillow#9334) \[[@&#8203;hugovk](https://github.com/hugovk)]
- Add release notes for [#&#8203;9070](python-pillow/Pillow#9070) [#&#8203;9320](python-pillow/Pillow#9320) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated Ubuntu version [#&#8203;9306](python-pillow/Pillow#9306) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update macOS tested Pillow versions [#&#8203;9265](python-pillow/Pillow#9265) \[[@&#8203;radarhere](https://github.com/radarhere)]

#### Dependencies

- Update harfbuzz to 12.3.0 [#&#8203;9355](python-pillow/Pillow#9355) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update xz to 5.8.2 [#&#8203;9343](python-pillow/Pillow#9343) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated libjpeg-turbo to 3.1.3 [#&#8203;9333](python-pillow/Pillow#9333) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated zlib-ng to 2.3.2 [#&#8203;9324](python-pillow/Pillow#9324) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated libpng to 1.6.53 [#&#8203;9325](python-pillow/Pillow#9325) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update actions/checkout action to v6 [#&#8203;9323](python-pillow/Pillow#9323) \[@&#8203;[renovate\[bot\]](https://github.com/apps/renovate)]
- Update dependency mypy to v1.19.0 [#&#8203;9322](python-pillow/Pillow#9322) \[@&#8203;[renovate\[bot\]](https://github.com/apps/renovate)]
- Updated libpng to 1.6.51 [#&#8203;9305](python-pillow/Pillow#9305) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated brotli to 1.2.0 [#&#8203;9284](python-pillow/Pillow#9284) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update libimagequant to 4.4.1 [#&#8203;9301](python-pillow/Pillow#9301) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update zlib-ng to 2.3.1, except on manylinux2014 aarch64 [#&#8203;9312](python-pillow/Pillow#9312) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated harfbuzz to 12.2.0 [#&#8203;9289](python-pillow/Pillow#9289) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update github-actions [#&#8203;9277](python-pillow/Pillow#9277) \[@&#8203;[renovate\[bot\]](https://github.com/apps/renovate)]

#### Testing

- Replace pre-commit with prek [#&#8203;9360](python-pillow/Pillow#9360) \[[@&#8203;hugovk](https://github.com/hugovk)]
- Test PyQt6 on Python 3.14 on Windows [#&#8203;9353](python-pillow/Pillow#9353) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Test 32-bit Windows on Windows Server 2022 [#&#8203;9345](python-pillow/Pillow#9345) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Correct variable type [#&#8203;9335](python-pillow/Pillow#9335) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Fix `ResourceWarning`s in `selftest.py` [#&#8203;9332](python-pillow/Pillow#9332) \[[@&#8203;hugovk](https://github.com/hugovk)]
- Fix testing good P mode BMP images [#&#8203;9319](python-pillow/Pillow#9319) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Test Python 3.15 pre-release [#&#8203;9331](python-pillow/Pillow#9331) \[[@&#8203;hugovk](https://github.com/hugovk)]
- Test ImageFont.ImageFont, in case freetype2 is not supported [#&#8203;9287](python-pillow/Pillow#9287) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Add Fedora 43 [#&#8203;9290](python-pillow/Pillow#9290) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Remove Fedora 41 [#&#8203;9260](python-pillow/Pillow#9260) \[[@&#8203;radarhere](https://github.com/radarhere)]

#### Type hints

- Add ImageFile context manager [#&#8203;9367](python-pillow/Pillow#9367) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Assert fp is not None [#&#8203;8617](python-pillow/Pillow#8617) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Added return type to ImageFile \_close\_fp() [#&#8203;9356](python-pillow/Pillow#9356) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Use different variables for Image and ImageFile instances [#&#8203;9316](python-pillow/Pillow#9316) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Correct variable type [#&#8203;9335](python-pillow/Pillow#9335) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Improve type hints [#&#8203;9317](python-pillow/Pillow#9317) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Use different variables for Image and ImageFile instances [#&#8203;9268](python-pillow/Pillow#9268) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Added type hints [#&#8203;9269](python-pillow/Pillow#9269) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Correct **getitem** return type [#&#8203;9264](python-pillow/Pillow#9264) \[[@&#8203;radarhere](https://github.com/radarhere)]

#### Other changes

- Simplify band splitting [#&#8203;9291](python-pillow/Pillow#9291) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Support saving APNG float durations [#&#8203;9365](python-pillow/Pillow#9365) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Allow 1 mode images in MorphOp [#&#8203;9348](python-pillow/Pillow#9348) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Use minimum supported Python version for Lint [#&#8203;9364](python-pillow/Pillow#9364) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Allow for duplicate font variation styles [#&#8203;9362](python-pillow/Pillow#9362) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Call parent verify method [#&#8203;9357](python-pillow/Pillow#9357) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Return LUT from LutBuilder build\_default\_lut() [#&#8203;9350](python-pillow/Pillow#9350) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Simplify WebP code [#&#8203;9329](python-pillow/Pillow#9329) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Use unsigned long for DWORD [#&#8203;9352](python-pillow/Pillow#9352) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Cast to UINT32 before shifting bits [#&#8203;9347](python-pillow/Pillow#9347) \[[@&#8203;radarhere](https://github.com/radarhere)]
- \[pre-commit.ci] pre-commit autoupdate [#&#8203;9318](python-pillow/Pillow#9318) \[@&#8203;[pre-commit-ci\[bot\]](https://github.com/apps/pre-commit-ci)]
- Allow window ID to be passed to ImageGrab.grab() on macOS [#&#8203;9070](python-pillow/Pillow#9070) \[[@&#8203;yankeguo](https://github.com/yankeguo)]
- Apply encoder options when saving multiple PNG frames [#&#8203;9300](python-pillow/Pillow#9300) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Read all non-zero transparency from mode 1 PNG images as 255 [#&#8203;9282](python-pillow/Pillow#9282) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Support writing IFD, SIGNED\_RATIONAL and InkNames TIFF tags [#&#8203;9276](python-pillow/Pillow#9276) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Remove unused modes [#&#8203;9275](python-pillow/Pillow#9275) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Correct allocating new color to RGBA palette [#&#8203;9313](python-pillow/Pillow#9313) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Close image on ImageFont exception [#&#8203;9304](python-pillow/Pillow#9304) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Reapply "Use macos-latest for iOS arm64 simulator" [#&#8203;9259](python-pillow/Pillow#9259) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Escape period in pre-commit-config [#&#8203;9036](python-pillow/Pillow#9036) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Add Apache-2.0 notice to IcoImagePlugin [#&#8203;8947](python-pillow/Pillow#8947) \[[@&#8203;stefan6419846](https://github.com/stefan6419846)]
- \[pre-commit.ci] pre-commit autoupdate [#&#8203;9288](python-pillow/Pillow#9288) \[@&#8203;[pre-commit-ci\[bot\]](https://github.com/apps/pre-commit-ci)]
- Simplify code now that I;16\* modes are the only IMAGING\_TYPE\_SPECIAL [#&#8203;9263](python-pillow/Pillow#9263) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Remove BytesIO from DdsImagePlugin [#&#8203;9273](python-pillow/Pillow#9273) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Fix ZeroDivisionError in DdsImagePlugin [#&#8203;9272](python-pillow/Pillow#9272) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Fix warnings [#&#8203;9257](python-pillow/Pillow#9257) \[[@&#8203;radarhere](https://github.com/radarhere)]

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi42OS4yIiwidXBkYXRlZEluVmVyIjoiNDIuNjkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsidHlwZS9kZXBlbmRlbmNpZXMiXX0=-->

Reviewed-on: https://git.tainton.uk/repos/webexmemebot/pulls/542
Co-authored-by: renovate[bot] <renovate-bot@git.tainton.uk>
Co-committed-by: renovate[bot] <renovate-bot@git.tainton.uk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Segfaults when using the 39C3 font from Chaos Communication congress

3 participants