self.assertRaises(tkinter.TclError, font.Font, root=self.root,
name='testfont', font=('Times', 10))
+ 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')
+ # Explicit options still override the copied settings.
+ f = font.Font(root=self.root, font=named, size=30)
+ self.assertEqual(f.cget('size'), sizetype(30))
+
+ 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
+ # a Font wrapping a description, as a tuple and as a string
+ font.Font(root=self.root, font=('Times', -20), exists=True),
+ font.Font(root=self.root, font='Times -20', exists=True),
+ ]
+ for desc in descriptions:
+ with self.subTest(font=desc):
+ f = font.Font(root=self.root, font=desc)
+ # resolved as if the description were wrapped by exists=True
+ wrapped = font.Font(root=self.root, font=desc, exists=True)
+ self.assertEqual(f.actual(), wrapped.actual())
+ self.assertGreater(int(f.cget('size')), 0) # pixels -> points
+
def test_existing(self):
sizetype = int if self.wantobjects else str
self.assertRaises(TypeError, font.Font, root=self.root, exists=True)
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')
self.name = font
else:
if font:
- # start from the actual settings of 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))
if options:
# explicit options override the corresponding settings
settings = self._mkdict(font)
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"