Tk Source Code

View Ticket
Login
Ticket UUID: 434d294df8b053246ee86e7898d06bc3a6d1d771
Title: fix type mismatch in unix/tkUnixRFont.c
Type: Patch Version: 8.6
Submitter: yorwba Created on: 2017-02-22 08:45:46
Subsystem: 46. Unix Fonts Assigned To: jan.nijtmans
Priority: 5 Medium Severity: Critical
Status: Closed Last Modified: 2017-07-03 07:46:39
Resolution: Fixed Closed By: jan.nijtmans
    Closed on: 2017-07-03 07:46:39
Description:
The code in unix/tkUnixRFont.c uses XftPattern{Get,Add}Integer to
manipulate the XFT_PIXEL_SIZE property. This is incorrect, since
fontconfig expects it to be a double (see the comment on
https://cgit.freedesktop.org/fontconfig/tree/fontconfig/fontconfig.h#n83
Line 83, XFT_PIXEL_SIZE is an alias for FC_PIXEL_SIZE).

This bug leads to incorrect reporting of font sizes in some cases, e.g.
the test case "font-44.1 TkFontGetPixels: size < 0" fails with output 0
on my machine.

For context: that test case is
    tk scaling 0.5
    font actual {times -12} -size
and expected output is 24.

After changing the code to call XftPattern{Get,Add}Double instead, the
test case still fails, but outputs 9 instead. I believe this is
related to [tk scaling] being ignored and 9 being the number of points
in 12 pixels on my display.

Patch:

--- tkUnixRFont.c
+++ tkUnixRFont.c
@@ -170,16 +170,16 @@
 {
     const char *family = "Unknown";
     const char *const *familyPtr = &family;
-    int weight, slant, size, pxsize;
-    double ptsize;
+    int weight, slant, size;
+    double ptsize, pxsize;
 
     (void) XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr);
     if (XftPatternGetDouble(ftFont->pattern, XFT_SIZE, 0,
 	    &ptsize) == XftResultMatch) {
 	size = (int) ptsize;
-    } else if (XftPatternGetInteger(ftFont->pattern, XFT_PIXEL_SIZE, 0,
+    } else if (XftPatternGetDouble(ftFont->pattern, XFT_PIXEL_SIZE, 0,
 	    &pxsize) == XftResultMatch) {
-	size = -pxsize;
+	size = -(int) pxsize;
     } else {
 	size = 12;
     }
@@ -444,7 +444,7 @@
     if (faPtr->size > 0) {
 	XftPatternAddDouble(pattern, XFT_SIZE, (double)faPtr->size);
     } else if (faPtr->size < 0) {
-	XftPatternAddInteger(pattern, XFT_PIXEL_SIZE, -faPtr->size);
+	XftPatternAddDouble(pattern, XFT_PIXEL_SIZE, -(double)faPtr->size);
     } else {
 	XftPatternAddDouble(pattern, XFT_SIZE, 12.0);
     }
User Comments: jan.nijtmans added on 2017-07-03 07:46:39:
Thanks, François, for reminding me. Yes, I think everything is handled now. Closing.

fvogel added on 2017-07-02 19:46:45:

You didn't report here, Jan, but I believe you took Christian's comment into account in [9ab129aa0d372250].

Can we close this bug then? Or what is it that should still be done?


chw added on 2017-05-20 03:08:04:
Check-in [6c1859a0] has the wrong logic in dealing with XLFD_PIXEL_SIZE, see
https://www.androwish.org/index.html/artifact?name=5cef6e6b3903ef42&ln=4003-4008
for a fix.

jan.nijtmans added on 2017-05-19 13:51:34:
I'm confident that this bug is now fixed in core-8-6-branch. Tested on Ubuntu, where there indeed were some fonts which returned 0 for "font actial -size", after this fix the font size returns 9.

Please confirm, then I'll close this issue.

gcramer added on 2017-05-16 16:50:09:
> bug-434d294df with --enable-xft:  [font actual $x -size] always gives 9.0.

This might be an issue, for compatibility reasons it may return "9", and the user normally specifies "size 9", and not -size "9.0".

aspect added on 2017-05-16 14:08:47:
I don't really follow what's going on here, but an observation from Fedora 24: Xft seems to play a role.

trunk with --enable-xft:  [font actual $x -size] always gives 0.

bug-434d294df with --enable-xft:  [font actual $x -size] always gives 9.0.

trunk with --disable-xft looks more reasonable:

% pack req Tk
8.7a0
% font actual TkFixedFont
-family courier -size 9 -weight normal -slant roman -underline 0 -overstrike 0
% font actual {times -12} -size
9
% tk scaling
1.3333333333333333
% tk scaling 0.5
% font actual {times -12} -size
24


bug-434d294df with --disable-xft:

% pack req Tk
8.7a0
% font actual TkFixedFont
-family courier -size 9.0 -weight normal -slant roman -underline 0 -overstrike 0
% font actual {times -12} -size
9.0
% tk scaling
1.3333333333333333
% tk scaling 0.5
% font actual {times -12} -size
24.005905511811026

dkf added on 2017-05-15 12:25:00:

Is there a particular reason for not using ((int) round(VALUE)) or lround(VALUE) for converting floating point values to integers? That'll give much less surprising results for tricky edge cases.


fvogel added on 2017-03-26 12:39:41:

Aside from Gregor's remark, which looks sensible, the bugfix branch does not compile for me on Vista due to:

tkWinDialog.c
C:\Users\francois\Documents\Development\tcltk-fossil\tk\win\..\win\tkWinDialog.c(3454) : error C2220: warning treated as error - no 'object' file generated
C:\Users\francois\Documents\Development\tcltk-fossil\tk\win\..\win\tkWinDialog.c(3454) : warning C4244: 'function' : conversion from 'double' to 'int', possible loss of data

I didn't dive into the details of the problem and of the proposed fix, and have no plans to do so, at least in the immediate future due to lack of time...


gcramer added on 2017-03-22 14:09:11:

I've stumbled over this (changed) line in TkFontGetPixels.c [f9348cf1]:

1231: fontPtr->underlineHeight = TkFontGetPixels(tkwin, fontPtr->fa.size) / 10;
So far as I see fontPtr->underlineHeight is still an integer type. Now TkFontGetPixels() returns an un-rounded double. This means that the value TkFontGetPixels()/10 now has the tendency to become smaller, example:
(int)(19.9/10)         = 1 // new computation
((int)(19.9 + 0.5))/10 = 2 // old computation
because in old implementation TkFontGetPixels() has returned the rounded value (int)(19.9 + 0.5). So this change will in general produce underlines with less height. I'm not sure that this is intended. (But probably I have overseen anything else.)


jan.nijtmans added on 2017-03-21 14:34:21:

Reading the documentation on XFT_PIXEL_SIZE, indeed this is expected to be a double not an integer.

Suggested fix in [f9348cf17680c53049245ed7510677ce2faefb3e|f9348cf176]. Test-cases not adapted yet. Since this change means that in more places double's are used in stead of integers, not sure this should be done for Tk <= 8.6.

François, how is your feeling on this, since you are deeper in Tk than I am? Please review.


yorwba added on 2017-02-24 06:37:31:
Apparently the conversion from point size to pixel size was introduced in commit
3cd573fc of fontconfig, which is identified as Version 2.11.1, 2014-03-24 in the
corresponding README. I have

> apt-cache policy libfontconfig1 | grep Installed
  Installed: 2.11.94-0ubuntu1.1

Since your Mint system is based on Ubuntu 14.04, the change is probably not
included in your version of fontconfig.

bll added on 2017-02-23 18:04:37:
My system always returns -12 for the font size, whether the display size is configured in X11.conf or not.

bll added on 2017-02-23 17:57:06:
Some notes:

test 44.1 as written assumes that the point size will be returned.

On my system, where the display size is configured (and thus tk scaling is ignored), the code returns the pixel size (and therefore test 44.1 is incorrect).

# linux mint 17.3 (ubuntu 14.04)
% font actual {{Nimbus Roman No9 L} -12} -size  # patched code
-12

On a pristine ubuntu 16.10, the font size is returned in points (where the display size is not configured).

# ubuntu 16.10
% font actual {{Nimbus Roman No9 L} -12} -size  # patched code
9

The unpatched 8.6.6 that comes with ubuntu 16.10 always returns a size of 0.

I don't know if the font size reporting difference is due to changes in X11, the display size being configured, the font configuration or something else.

I will turn off my display size config and retest.

bll added on 2017-02-23 16:37:34:
The conversions of ...GetInteger are not necessary.
The change at line 447 is correct.
New patch attached.

00051 Bool
00062 XftPatternAddInteger (XftPattern *p, const char *object, int i)
00063 {
00064     XftValue  v;
00065 
00066     v.type = XftTypeInteger;
00067     v.u.i = i;
00068     return XftPatternAdd (p, object, v, True);
00069 }

00119 XftResult
00120 XftPatternGetInteger (XftPattern *p, const char *object, int id, int *i)
00121 {
00122     XftValue  v;
00123     XftResult r;
00124 
00125     r = XftPatternGet (p, object, id, &v);
00126     if (r != XftResultMatch)
00127        return r;
00128     switch (v.type) {
00129     case XftTypeDouble:
00130        *i = (int) v.u.d;
00131        break;
00132     case XftTypeInteger:
00133        *i = v.u.i;
00134        break;
00135     default:
00136         return XftResultTypeMismatch;
00137     }
00138     return XftResultMatch;
00139 }

yorwba added on 2017-02-23 07:55:48:
I noticed the bug using Tkinter from Python, but then grabbed the Tk 8.6.6 tarball to reproduce.

Output of unpatched wish:
% font actual {times -12}
-family {TeX Gyre Termes} -size 0 -weight normal -slant roman -underline 0 -overstrike 0

Patched:
% font actual {times -12}
-family {TeX Gyre Termes} -size 9 -weight normal -slant roman -underline 0 -overstrike 0

While multiple functions of the XftPattern* interface can be used,
each property must be set with the correct type.
If you look at the table under
https://keithp.com/~keithp/talks/xtc2001/paper/xft.html#sec-xft-name
you can see that both size and pixelsize are supposed to be doubles.

Xft (actually fontconfig) doesn't do any type conversions at the boundary
when you try to set the pixel size to the integer value 12.
Instead an integer-valued union field is set and when fontconfig converts
the pixel size into the point size, it reads the double field, which has
value 5.9287877500949585e-323. During conversion this value underflows,
yielding a point size of 0.

bll added on 2017-02-22 16:24:25:
What is the output of [font actual {times -12}] on your machine?
What version of Tk are you using?

There are various different XftPattern* interfaces that can be used.

References:
https://keithp.com/~keithp/talks/xtc2001/paper/

Attachments: