Is there a way to look into the fonts?

Is there a way to "look" into the fonts? Either by reading the cft files in the SDK (listing their content about which characters it contains, the size of the characters, etc) or by displaying them (and I hope you won't tell me to look at them by displaying text in the devices in the simulator)

Also: how much the cft fonts used by the simulator can be "trusted" to make decisions regarding the real devices' fonts?

  • I keep looking for solutions to this too. By looking at the compiler.json and simulator.json files, you can map the system fonts back to an actual .cft file, but then there's nothing you can do with it to determine its characteristics.

    So I think the only answer is to do what you are trying to avoid; write a test that spits out the details you care about for each font, and run it for each device you care about, and save the results. At least, you can get text dimensions that way - I'm not sure about figuring out which characters exist; but I seem to remember thats actually documented somewhere (... but I didn't find it via a quick search)

    You could do slightly better by finding a 'minimum cover'; ie a set of devices that between them cover every .cft file.

    And if you write it as a test, you can automate running the whole thing from the command line. It will take a while, but you only have to do it once (and again whenever a new font is added).

    But even then, there's your final question: how much can they be trusted. And the answer is not much, I'm afraid.

    For my data field, for example, on a fenix5xplus, there's no way (on the simulator) to fit a FONT_MEDIUM row below a FONT_XTINY row (label and data) in 80 vertical pixels without them overlapping. But when I run it on the device, there's no overlap.

  • Ok, so all I've been able to find was other people asking the same question... so I decided to take a look.

    I got all the details for the fonts for a single device (ascent, descent, line height, widths of various characters) by running a small program in the simulator, then looked at the corresponding .cft files. It wasn't too hard to find most values. The tricky part was finding the mapping from char code to glyphs to get the widths (and the existence of characters). But looking at some of the number-only fonts helped with that (since there are so few glyphs present). So I've ended up with a small program that takes a cft file name, and returns all the metrics relating to it. Here's an example output:

    [
      {
        "name": "FNT_NOTO_SANS_BOLD_102",
        "height": 102,
        "ascent": 80,
        "internalLeading": 24,
        "kerning": 0,
        "externalLeading": 1,
        "charInfo": [
          {
            "code": 37,
            "char": "%",
            "width": 67
          },
          {
            "code": 43,
            "char": "+",
            "width": 43
          },
          {
            "code": 45,
            "char": "-",
            "width": 24
          },
          {
            "code": 46,
            "char": ".",
            "width": 21
          },
          {
            "code": 47,
            "char": "/",
            "width": 31
          },
          {
            "code": 48,
            "char": "0",
            "width": 43
          },
          {
            "code": 49,
            "char": "1",
            "width": 43
          },
          {
            "code": 50,
            "char": "2",
            "width": 43
          },
          {
            "code": 51,
            "char": "3",
            "width": 43
          },
          {
            "code": 52,
            "char": "4",
            "width": 43
          },
          {
            "code": 53,
            "char": "5",
            "width": 43
          },
          {
            "code": 54,
            "char": "6",
            "width": 43
          },
          {
            "code": 55,
            "char": "7",
            "width": 43
          },
          {
            "code": 56,
            "char": "8",
            "width": 43
          },
          {
            "code": 57,
            "char": "9",
            "width": 43
          },
          {
            "code": 58,
            "char": ":",
            "width": 21
          },
          {
            "code": 59,
            "char": ";",
            "width": 21
          },
          {
            "code": 65,
            "char": "A",
            "width": 52
          },
          {
            "code": 80,
            "char": "P",
            "width": 47
          },
          {
            "code": 95,
            "char": "_",
            "width": 31
          },
          {
            "code": 175,
            "char": "¯",
            "width": 37
          },
          {
            "code": 176,
            "char": "°",
            "width": 32
          }
        ]
      }
    ]
    

    I'll include the program in the next release of @markw65/monkeyc-optimizer - but it will be command line only; I don't plan to add support to @markw65/prettier-extension-monkeyc

  • Thank you!  You rock!!  :-)

    I don't need the character width information, or the Asian fonts, so I played with 'jq' to strip out all that stuff, which made the json orders of magnitude smaller.  The command I came up with probably isn't the most efficient, but it works...

    jq 'del(.fonts[] | .charInfo) | del(.devices[] | .langMap) | delpaths([paths | select(.[-1] | strings | startswith("apac"))]) | delpaths([paths | select(.[-1] | strings | startswith("FONT_SYSTEM_"))]) | delpaths([paths | select(.[-1] | strings | startswith("FONT_GLANCE_"))]) | delpaths([paths | select(.[-1] | strings | startswith("FONT_SIM_EXT_"))]) | delpaths([paths | select(.[-1] | strings | contains("_CJK_"))]) | delpaths([paths | select(.[-1] | strings | contains("_VIET_"))])'

    Just getting rid of the charInfo nodes made it much more manageable (which is the very first part of the command).

    Now, digging into the actual font metrics, is "internalLeading" the amount of space above the rendered character?

  • Well, after some very limited testing, it appears that the font metrics from the CFT files are *close*, but do not exactly match what is rendered (at least in the simulator).  I wrote a quick app to render the 5 text fonts on the screen, which you can see below.  (Screen capture is from running as Fenix 6X in the simulator.)

    One interesting thing that I hadn't noticed before:  I used the same "y" position for drawText, drawLine, and fillRect. The rendered text from drawText is rendered 1-pixel lower than I would expect.  drawLine and fillRect line up exactly.  But the top of the background from drawText does not line up.  I'm basically doing this:

    dc.setColor(Graphics.COLOR_DK_RED, Graphics.COLOR_DK_RED);
    dc.clear();
    
    dispString = fontName + " " + fontHeight + " " + ascent + " " + descent;
    
    dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_WHITE);
    dc.fillRect(0, y, dc.getWidth(), fontHeight);
    dc.drawText(dc.getWidth() / 2, y, font, dispString, Graphics.TEXT_JUSTIFY_CENTER);
    
    dc.setColor(Graphics.COLOR_DK_GREEN, Graphics.COLOR_DK_GREEN);
    dc.drawLine(0, y, dc.getWidth() / 2, y);

    Regardless of the weird "off by 1" issue, the space I'm seeing above the text is slightly more than what the internalLeading value indicates in the CFT file.

    From smallest to biggest, the space above the rendered fonts are:  3, 6, 7, 8, 8.  (Note that these might actually need to increase by one - I wasn't sure if I should count the shaded pixels from the anti-aliasing as part of the space, or part of the rendered text.  I counted them as part of the text.)

    The corresponding internalLeading values from the CFT file are:  2, 5, 5, 6, 7.

    Anyway - great work on the CFT parsing tool.  I just wanted to share my findings in case anyone else was interested.  I think the internalLeading values could be used to get tighter spacing in the UI.  They're just not the exact amount of whitespace that appears above the text.

      

  • Now, digging into the actual font metrics, is "internalLeading" the amount of space above the rendered character?

    I'm not 100% sure. I think it's supposed to be the space at the top reserved for diacritical marks - but it's whatever the font designer set it to be (also note that there isn't a Toybox call to get this info). In particular I think fonts that don't include diacriticals on upper case letters could let the upper case letters encroach on the internalLeading space - or it could go the other way, and the space above the upper case letters could be bigger than the internalLeading.

  • Nice test! And interesting finding with the off-by-one.

    I think the internalLeading values could be used to get tighter spacing in the UI

    Up to a point, yes. Unfortunately, I think you'd have to test each font you wanted to try this with (or be prepared for some overlap).

  • played with 'jq' to strip out all that stuff, which made the json orders of magnitude smaller

    I did consider adding command line options to say which bits you want, but I figured its easy enough to strip them out, or just ignore the bits you don't want. But it certainly generates a lot of output!

    For my own use case, I do need the character widths; but only to determine how I need to format the data. eg "00:00" vs "00:00:00" for times, or how many decimal places to include for distance/speed. So I only really need a few characters.

  • I just saw this bug report.

    Looking at the screen shot vs simulator, it looks like for some of the fonts, the simulator is printing the characters 1 pixel lower than the actual device does. So it might be worth re-running your test case on a real device and see if that off-by-one is only in the simulator...

  •  , regarding https://github.com/markw65/monkeyc-optimizer/wiki/Garmin-Font-Analyzer can you clarify (there in the README or wiki) what information of the json output is true for what? I have a feeling that there are 3 different things:

    1. actual fonts on the devices (probably we don't have much info about them)

    2. actual fonts in the simulator (the fonts in the SDK) that is taken from the binary font files

    3. font information that is taken from the simulator.json

  • Well, the assumption is that the fonts on the devices are the same as the fonts in the simulator; although we know there are exceptions.

    Obviously all the information about the fonts comes from the .cdf files in <ConnectIQ>/Fonts. You can also just give it the path to a .cdf file, if you happen to have one, and it will tell you about that. But I don't know where else you would get one from.

    If you give it a device name, it will use the device's compiler.json to map languages to font sets, and simulator.json to map those font sets to lists of fonts.

    So given the output of "npx cft-font-info fr230", devices.fr230.langMap.fin will tell you that Finnish uses the "ww" fontSet (which came from partNumbers.languages in compiler.json), and then devices.fr230.fontSets.ww.FONT_SMALL will tell you that in Finnish, FONT_SMALL is FNT_ROBOTO_12B (it got that from simulator.json), and then fonts.FNT_ROBOTO_12B will tell you everything you ever wanted to know about FNT_ROBOTO_12B.

    There is also a problem at present in that I wrote this before they started using true type fonts (or at least, before I noticed that they started using them). So it won't work for devices that use true type fonts. On the other hand, there are lots of tools that will tell you all about true type fonts.