From d62955031b593b6f993c655eb1e4332c15818df7 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Thu, 1 Jan 2026 08:53:04 +1100 Subject: [PATCH] Allow for duplicate font variation styles (#9362) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Tests/fonts/AdobeVFPrototypeDuplicates.ttf | Bin 0 -> 6696 bytes Tests/fonts/LICENSE.txt | 2 +- Tests/test_imagefont.py | 17 ++++++++++++++++- src/PIL/ImageFont.py | 8 ++++++-- src/_imagingft.c | 1 - 5 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 Tests/fonts/AdobeVFPrototypeDuplicates.ttf diff --git a/Tests/fonts/AdobeVFPrototypeDuplicates.ttf b/Tests/fonts/AdobeVFPrototypeDuplicates.ttf new file mode 100644 index 0000000000000000000000000000000000000000..acf0bc156c8bec3c79de96968766795951b3a8fb GIT binary patch literal 6696 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 None: font.get_variation_axes() font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf") - assert font.get_variation_names(), [ + assert font.get_variation_names() == [ b"ExtraLight", b"Light", b"Regular", @@ -742,6 +742,21 @@ def test_variation_get(font: ImageFont.FreeTypeFont) -> None: ] +def test_variation_duplicates() -> None: + font = ImageFont.truetype("Tests/fonts/AdobeVFPrototypeDuplicates.ttf") + assert font.get_variation_names() == [ + b"ExtraLight", + b"Light", + b"Regular", + b"Semibold", + b"Bold", + b"Black", + b"Black Medium Contrast", + b"Black High Contrast", + b"Default", + ] + + def _check_text(font: ImageFont.FreeTypeFont, path: str, epsilon: float) -> None: im = Image.new("RGB", (100, 75), "white") d = ImageDraw.Draw(im) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 2e8ace98d..d11f7bf01 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -675,8 +675,12 @@ class FreeTypeFont: :returns: A list of the named styles in a variation font. :exception OSError: If the font is not a variation font. """ - names = self.font.getvarnames() - return [name.replace(b"\x00", b"") for name in names] + names = [] + for name in self.font.getvarnames(): + name = name.replace(b"\x00", b"") + if name not in names: + names.append(name) + return names def set_variation_by_name(self, name: str | bytes) -> None: """ diff --git a/src/_imagingft.c b/src/_imagingft.c index d0af25b30..a371173d6 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -1287,7 +1287,6 @@ font_getvarnames(FontObject *self) { } PyList_SetItem(list_names, j, list_name); list_names_filled[j] = 1; - break; } } }