From c31d7ac59933dbdd61da649ca6fd91dacbb1c971 Mon Sep 17 00:00:00 2001 From: iAlturki <120382911+iAlturki@users.noreply.github.com> Date: Thu, 15 Oct 2026 15:19:43 +0300 Subject: [PATCH 1/3] Respect explicit BASIC layout in font_variant() --- Tests/test_imagefont.py | 20 ++++++++++++++++++++ docs/releasenotes/13.0.0.rst | 9 +++++++++ src/PIL/ImageFont.py | 4 +++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index c7a111b63c9..0ee5e363b24 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -66,6 +66,7 @@ def test_font_properties(font: ImageFont.FreeTypeFont) -> None: font_copy = font.font_variant() assert font_copy.path == FONT_PATH assert font_copy.size == FONT_SIZE + assert font_copy.layout_engine == font.layout_engine font_copy = font.font_variant(size=FONT_SIZE + 1) assert font_copy.size == FONT_SIZE + 1 @@ -75,6 +76,25 @@ def test_font_properties(font: ImageFont.FreeTypeFont) -> None: assert font_copy.path == second_font_path +@pytest.mark.parametrize( + "variant_layout_engine", + ( + ImageFont.Layout.BASIC, + pytest.param(ImageFont.Layout.RAQM, marks=skip_unless_feature("raqm")), + ), +) +def test_font_variant_layout_engine( + font: ImageFont.FreeTypeFont, variant_layout_engine: ImageFont.Layout +) -> None: + font_copy = font.font_variant(layout_engine=variant_layout_engine) + assert font_copy.layout_engine == variant_layout_engine + + expected = ImageFont.truetype( + FONT_PATH, FONT_SIZE, layout_engine=variant_layout_engine + ) + assert font_copy.getlength("AV") == expected.getlength("AV") + + def _render( font: StrOrBytesPath | BinaryIO, layout_engine: ImageFont.Layout ) -> Image.Image: diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index a9fcc0dc510..cfefc72b348 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -134,6 +134,15 @@ escape sequences. Other changes ============= +Fixed font_variant() layout engine override +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:py:meth:`~PIL.ImageFont.FreeTypeFont.font_variant` now respects an explicit +:py:attr:`~PIL.ImageFont.Layout.BASIC` layout engine override when copying a font +that uses :py:attr:`~PIL.ImageFont.Layout.RAQM`. Previously, the original layout +engine was retained because ``BASIC`` has the value zero. Omitting the argument +or passing ``None`` still retains the original layout engine. + Python 3.15 ^^^^^^^^^^^ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index d066759660c..9d8a73667c3 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -695,7 +695,9 @@ def font_variant( size=self.size if size is None else size, index=self.index if index is None else index, encoding=self.encoding if encoding is None else encoding, - layout_engine=layout_engine or self.layout_engine, + layout_engine=( + self.layout_engine if layout_engine is None else layout_engine + ), ) def get_variation_names(self) -> list[bytes]: From 92c1459ae0e0b6273c94cb72e1543feffda26f5b Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Fri, 16 Oct 2026 07:11:19 +1100 Subject: [PATCH 2/3] Fix title underline --- docs/releasenotes/13.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index cfefc72b348..2d16d9512b2 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -135,7 +135,7 @@ Other changes ============= Fixed font_variant() layout engine override -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :py:meth:`~PIL.ImageFont.FreeTypeFont.font_variant` now respects an explicit :py:attr:`~PIL.ImageFont.Layout.BASIC` layout engine override when copying a font From 0887df31be6e424d065451427488979c660c3206 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 16 Oct 2026 09:12:10 +1100 Subject: [PATCH 3/3] Remove test code that passes before change --- Tests/test_imagefont.py | 5 ----- docs/releasenotes/13.0.0.rst | 9 --------- src/PIL/ImageFont.py | 2 +- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0ee5e363b24..4a06cd90edb 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -89,11 +89,6 @@ def test_font_variant_layout_engine( font_copy = font.font_variant(layout_engine=variant_layout_engine) assert font_copy.layout_engine == variant_layout_engine - expected = ImageFont.truetype( - FONT_PATH, FONT_SIZE, layout_engine=variant_layout_engine - ) - assert font_copy.getlength("AV") == expected.getlength("AV") - def _render( font: StrOrBytesPath | BinaryIO, layout_engine: ImageFont.Layout diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index 2d16d9512b2..a9fcc0dc510 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -134,15 +134,6 @@ escape sequences. Other changes ============= -Fixed font_variant() layout engine override -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:py:meth:`~PIL.ImageFont.FreeTypeFont.font_variant` now respects an explicit -:py:attr:`~PIL.ImageFont.Layout.BASIC` layout engine override when copying a font -that uses :py:attr:`~PIL.ImageFont.Layout.RAQM`. Previously, the original layout -engine was retained because ``BASIC`` has the value zero. Omitting the argument -or passing ``None`` still retains the original layout engine. - Python 3.15 ^^^^^^^^^^^ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 9d8a73667c3..611a8c4f2d3 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -696,7 +696,7 @@ def font_variant( index=self.index if index is None else index, encoding=self.encoding if encoding is None else encoding, layout_engine=( - self.layout_engine if layout_engine is None else layout_engine + layout_engine if layout_engine is not None else self.layout_engine ), )