https://github.com/python/cpython/commit/d55a9882a2d8f0b092244afd876bbcad889ce3a7
commit: d55a9882a2d8f0b092244afd876bbcad889ce3a7
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-07-08T13:29:05Z
summary:

[3.14] gh-143990: Preserve the size when creating a Font from a named font 
(GH-153267) (GH-153323) (GH-153341)

tkinter.font.Font now copies the options of a named font (via "font
configure") instead of the options resolved by "font actual", which
would resolve a size specified in pixels (a negative size) to points.
A font description is still resolved, as it cannot be parsed otherwise.

Font.copy(), which has always been equivalent to constructing a Font
from the original font, is updated to match and now preserves the size
too.

(cherry picked from commit 45010f441d392558f1be8f3cea7d86954ed2217d)
(cherry picked from commit a9d4473232f86d6dfc234756f633b8dc5836ad32)

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-07-07-17-50-54.gh-issue-143990.FoNtCf.rst
M Lib/test/test_tkinter/test_font.py
M Lib/tkinter/font.py

diff --git a/Lib/test/test_tkinter/test_font.py 
b/Lib/test/test_tkinter/test_font.py
index 8e1e3d56c07f176..00150ce83d07c83 100644
--- a/Lib/test/test_tkinter/test_font.py
+++ b/Lib/test/test_tkinter/test_font.py
@@ -43,17 +43,49 @@ def test_configure(self):
         self.assertRaises(TypeError, self.font.cget)
         self.assertRaises(TypeError, self.font.cget, 'size', 'weight')
 
+    def test_create_from_named_font(self):
+        # gh-143990: a font created from a named font copies its configured
+        # options, preserving a size specified in pixels (a negative size).
+        sizetype = int if self.wantobjects else str
+        named = font.Font(root=self.root, name='my named font',  # name with 
spaces
+                          family='Times', size=-20, weight='bold')
+        # The source is the name of a named font or a Font representing one.
+        for source in ['my named font', named]:
+            with self.subTest(source=source):
+                f = font.Font(root=self.root, font=source)
+                self.assertEqual(f.cget('size'), sizetype(-20))
+                self.assertEqual(f.actual('family'), named.actual('family'))
+                self.assertEqual(f.actual('weight'), 'bold')
+
+    def test_create_from_description(self):
+        # gh-143990: a font created from a font description is resolved via
+        # "font actual", so a size in pixels (negative) becomes a size in 
points.
+        descriptions = [
+            ('Times', -20),                     # tuple
+            ('Times', -20, 'bold'),             # tuple with a style
+            'Times -20',                        # string
+            'Times -20 bold',                   # string with a style
+            '{Times New Roman} -20',            # string, family with spaces
+        ]
+        for desc in descriptions:
+            with self.subTest(font=desc):
+                f = font.Font(root=self.root, font=desc)
+                self.assertGreater(int(f.cget('size')), 0)  # pixels -> points
+
     def test_copy(self):
-        f = font.Font(root=self.root, family='Times', size=10, weight='bold')
+        # size=-20 (pixels): copy() copies the configured options, so the
+        # size is preserved rather than resolved (gh-143990).
+        f = font.Font(root=self.root, family='Times', size=-20, weight='bold')
         copied = f.copy()
         self.assertIsInstance(copied, font.Font)
         self.assertIsNot(copied, f)
         self.assertNotEqual(copied.name, f.name)
         self.assertEqual(copied.actual(), f.actual())
-        # The copy is independent of the original.
         sizetype = int if self.wantobjects else str
+        self.assertEqual(copied.cget('size'), sizetype(-20))
+        # The copy is independent of the original.
         copied.configure(size=20)
-        self.assertEqual(f.cget('size'), sizetype(10))
+        self.assertEqual(f.cget('size'), sizetype(-20))
         self.assertEqual(copied.cget('size'), sizetype(20))
         self.assertRaises(TypeError, f.copy, 'x')
 
diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py
index 3e24e28ef58cde9..dcda8b554b74539 100644
--- a/Lib/tkinter/font.py
+++ b/Lib/tkinter/font.py
@@ -72,8 +72,15 @@ def __init__(self, root=None, font=None, name=None, 
exists=False,
             root = tkinter._get_default_root('use font')
         tk = getattr(root, 'tk', root)
         if font:
-            # get actual settings corresponding to the given font
-            font = tk.splitlist(tk.call("font", "actual", font))
+            # start from the settings of the given font
+            try:
+                # a named font: copy its options, preserving the size,
+                # which can be negative (specified in pixels)
+                font = tk.splitlist(tk.call("font", "configure", font))
+            except tkinter.TclError:
+                # a font description: resolve it ("font configure" only
+                # accepts a font name); this loses a size in pixels
+                font = tk.splitlist(tk.call("font", "actual", font))
         else:
             font = self._set(options)
         if not name:
@@ -124,7 +131,7 @@ def __del__(self):
 
     def copy(self):
         "Return a distinct copy of the current font"
-        return Font(self._tk, **self.actual())
+        return Font(self._tk, self.name)
 
     def actual(self, option=None, displayof=None):
         "Return actual font attributes"
diff --git 
a/Misc/NEWS.d/next/Library/2026-07-07-17-50-54.gh-issue-143990.FoNtCf.rst 
b/Misc/NEWS.d/next/Library/2026-07-07-17-50-54.gh-issue-143990.FoNtCf.rst
new file mode 100644
index 000000000000000..a78dcccf482ab59
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-07-17-50-54.gh-issue-143990.FoNtCf.rst
@@ -0,0 +1,4 @@
+A :class:`tkinter.font.Font` created from a named font,
+including by :meth:`~tkinter.font.Font.copy`,
+now copies its configured options rather than the options resolved by Tcl's 
``font actual``,
+preserving a size specified in pixels (a negative size).

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to