Standardize Font Appearance

Neville Smythe neville.smythe at optusnet.com.au
Mon Sep 12 01:24:57 EDT 2022


I have enough datapoints to report some findings from my comparison of the appearance of fonts across platforms, in particular on the length of strings in fields and text baselines, and to correct some of my previous statements. My intention is to compile a database for some 7000+ fonts in 7 different font sizes ranging from 9pt to 18pt on Mac Windows and Linux system; but for now (for reasons given below) I looked at four basic fonts in common use: Arial, Times New Roman, Noto Sans and Noto Serif.

The good news is that the horrible problem of different strings lengths is no longer a problem, as long as your target platforms are just Mac or Windows 10. For each of these four fonts, strings in (right or left justified) fields have the same rendered length (they differ between fonts of course). I would be confident that holds for all TrueType fonts installed on these platforms, starting from the advent of Windows 10. My former claim about a difference between High Sierra and Monterey on the Mac is incorrect, the result of a coding error. Of course the field or stack must have a font assigned: leaving the font to empty will result in the system using its own font, which will be consistent with the look-and-feel of the platform/system version, but which will certainly be quite different between platforms.

On LInux (Ubuntu 18.x) however, string lengths for all the fonts in all sizes are quite different, sometimes longer and sometimes shorter than on the other platforms. The biggest difference was for Noto Sans 9pt, the sample Linux string being 14% longer; this was an outlier, mostly the Linux strings were less than 8% longer. In the other directions was Arial 15pt, where Linux was 5% shorter. So if Linux is a target platform, allow some extra length for your fields (on the right  for a left justified field, on the left for a right justified label).

A more difficult problem is the vertical placement of text. With the default settings for fields, strings in Windows will typically appear 2 or 3 pixels lower than on a Mac, which can mess with your layout — text carefully positioned on one platform will appear too close to nearby objects on the other, or will not align with text in adjacent fields. This can be fixed by adjusting the topMargin of the field, but the details are not straightforward..

The baseline for rendered strings is determined by the font metrics and the font size; unfortunately Mac Windows and Linux all use different metrics from each other. It would be nice if LC had a function which returned the baseline for the rendered string, but I couldn’t find such a function. The closest formula I can get is (for text using the textFont and textSize of the owner field, and consigning to oblivion the formula I had previously proposed which was caused by the aforementioned error plus a glitch in the data processing unit, ie my brain.)

     first baseline relative to the top of the field = the topMargin of the field + fontAscent - 6
 where
    fontAscent = - (item 2 of measureText(fld <fldname>,fld <fldname>,”bounds”)

Why the magic number -6? It seems this places text in a common font such as Time New Roman or Arial in medium range textSizes such as 11 to 15 so that the top of the lower case character “x” is at ...approximately... the topMargin of the field. This would be the ideal placement for text at all fonts and sizes, but of course in general 6 is nothing like the difference between the fontAscent (the height of the capital “A” above the baseline) and the height of “x”, and anyway the fontAscent is not the actual height of the font, so for other sizes and fonts the placement is wildly different from the ideal. The simplification is probably a legacy of a decision originally made for speed given the complications of dealing with different font formats; a correction now would break all existing stacks.

Actually while my formula fits precisely most of the time, it occasionally is out by 1 pixel. And that precision holds for all fonts I have looked at, even for the gorgeously extravagant Zapfino at 64pt. So the discrepancy is probably some floating point rounding from the correct formula. If anyone (LC engineer’s?) can provide the correct formula that would be nice, but I have the feeling it may involve a font metric not directly exposed by the LC api.

The formula can be used to adjust the topMargin so that the baseline is fixed, to within 1 pixel anyway The adjustment would have to be done at runtime, or applied to a stack before building a standalone for a particular platform, using a pre-cooked fontAscent. Hence my database project. Just at the moment this has hit a bit of snag: as far as I can see, fonts have to be added to Windows 10 one at a time -- on a Mac you just dump a folder of fonts onto Font Book. Does anyone know how to do this in Windows and Linux? If necessary, following Ralph’s post I believe I could work out where the Mac and Windows 10 systems pick up their ascent parameters by parsing the ttf metadata (horrors!), but the Linux values are another mystery. 

The apparently obvious solution to the problem is to set fixedLineHeight to true, but unfortunately this does not work. The bottom of each character glyph will I guess be placed on a fixed line independently of the font, size or platform, but that is not the baseline, which will still be dependent on font metrics. The result is that even with fixedLineHeight true baselines are different for different sizes of a given font, and for different fonts for a given size even on the same platform (so adjacent fields with different fonts or sizes will not align baselines), and also different for a given font and size on different platforms so the layout looks sloppy.

 My formula for the first baseline for a field with fixed line height is

        baseline =  the topMargin of the field + the textHeight of the field - item 2 of measureText(the field,the text,"size”) + fontAscent - 7

which again seems to be accurate except for a few (different!) cases where it is 1 pixel out. Again, if anyone knows the exact formula I would be grateful to hear it. 

Possibly an enhancement to LC could provide a fixedBaseline,baseline pair of properties for fields, which would set the first baseline at a user value, bleeding the text into the top margin, thus eliminating this problem.


Neville






More information about the use-livecode mailing list