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?

  • OK, so what I do so far is that I have a python script that uses data from the compiler.json and simulator.json to generate constants per device and monkey.jungle so each device gets it's constants file.

    One piece of information that I am missing and I think is in your output is to decide which fonts (Graphics.FONT_...) I can use to display a given string. Until now it manually tweaked, so that each device knows for example (or thinks it knows) what non numeric characters are present in the font, so if the text is "3.14" or "27°" then it can decide if it can use the bigger numeric fonts or only the smaller generic fonts.

    Currently I have:

    const NUMBER_FONT_CHARS = "0123456789.-+:/#%°\n";
    or the same that also includes space, depending on the device.

    I see in your jsons I will be able to find the characters, though I guess it won't be feasible to include the whole set, 'cause then both the code size will grow too big and the search for the characters will take long... but maybe the developer could configure my script to include any character that he thinks can be of interest in terms of what characters could appear in the app, and then generate the above constant based on that configuration and your jsons.

    But, here comes the question: until now all the things in my generation were based on devices only. It looks like I might need to improve that to be on the part-number level. Is that even possible? I mean I see the data in compiler.json, and I know the compiler does treat the different part numbers differently because sometimes I see warnings about emitting certain part-numbers because it doesn't support a language or the minimum api level, but is it possible to do it from the monkey.json?

    And then there's also the language. So (at least in theory) it is possible that a certain device + part-number would have different characters depending on the language setting? For example it is possible that the numeric fonts will be different when the same physical device is set to Japanese vs English? So I guess that my script will need to check for that too, and at least give a warning (or better: automatically add constants based on language, but then again: this will take more code space...) if the whatever feature it is looking for ("do all the numeric fonts include space character", "what is the height, ascent of FONT_TINY", ...) is different based on the languages available on the device.

    Also do you know what does internalLeading mean?

    Here you can see some font and the baseline (I used the only available way to know where it is:  Graphics.getFontDescent), so would internalLeading mean the height of the "waste" above the numbers (I specifically write numbers, because that's a major use-case, and numbers will never have diacriticals)

  • If you just use CIQ to look at things, it might make it easier.  Number fonts are the maybe the harder to use.

    For example, here are the two largest on an f5,  The lines show the actual height of a font (dc.getFontHeight()), which is shown as a small number on the right:

    Same thing on a f6:

    Notice on the f5 there is no white space above or below the digits. while there is a fair amount on the f6.

    This all needs to be considered if you are looking for fitting a font given space available.  Also look at Graphics.TEXT_JUSTIFY_VCENTER to make it a bit easier to handle the white space

    Another thing with devices like the f5.  You see TINY and XTINY are the same height But TINY is bold (wider) than XTINY

    Custom fonts can make things a bit easier as you can use the same one on say, all 240x240 devices, and a different size on 260x260, but come with a memory cost. (which is lower on new devices with a graphics pool).  Then there are the scalable (vector) fonts avail on some of the new devices where you can say, "this font is x pixels high", but the font choice can differ between devices.

    Dealing with different fonts on different device has been a challenge starting with the 4 original CIQ devices

  • You're basically just restating the problem - which is that with the tools garmin provides all you can do is look at each font you're interested in, on each device you're interested in, and see what works.

    Look at the f5! Look at the f6! But there are nearly 150 different devices and 20ish fonts on each one to look at. How can you realistically do that?

    The point of my script is to actually figure out all the details of each font - ie which characters are present, and how big they are, including things like ascent, descent and internal leading.

    Its not perfect, but its much better than trying to eyeball every font on every device in the simulator...

  • Also do you know what does internalLeading mean

    Sort of. It's basically the space above the characters reserved for diacritical marks. The problem is that that isn't well defined, so it's whatever the font designer decides it is. But yes, generally, for numbers, or "ordinary" letters, it's likely to be empty space above the actual characters (and to the extent that I've tested it, thats exactly what I found with the Garmin fonts).

  • So (at least in theory) it is possible that a certain device + part-number would have different characters depending on the language setting?

    I believe so. eg Look at fr955.json. There's only one part number, and it has 6 different font sets. It looks like the number fonts are the same for all except ww. So there are only two sets of number fonts to deal with. Also, the number fonts are variants of Roboto in both cases...

  • It looks like I might need to improve that to be on the part-number level. Is that even possible?

    I'm not sure. If you look at the manifest in a .iq file, it lists part numbers, and the compiler builds one .prg for each part number. So clearly the tools are capable of differentiating. But I've never tried to see if you can do that outside of an .iq build - and its certainly not documented that you can.

  • though I guess it won't be feasible to include the whole set, 'cause then both the code size will grow too big and the search for the characters will take long

    I'm not quite sure exactly what you're using that variable for - but wouldn't you want to give it the desired list, and have the script compute which of the desired characters were actually available?

    But then, I'm not sure what you'd do when you wanted to display a ":" (for example) and it wasn't in the list...

    What i do is to give my prebuild script a list of characters that I plan to use with number fonts, and it fails (causing the whole build to fail) if they aren't all available. But I have a pretty short list, so no problems so far.

  • OK, so currently even if my monkey.jungle generator generates everything per device, it still means that if I want to support the widest possible audience then I need to support each font that can be used in each language, even if Japanese is only available in an APAC device, thus only selectable on APAC devices (so I wouldn't need to include the code for the fonts that are only available in the APAC devices for WW devices) because in the monkey.jungle I can only differentiate between devices but not part numbers.

  • Well my use case is that I'd like to find the biggest font I can use that fits the field's size. So I do something like:
    1. Check the text to be displayed. If it only contains characters that are present in the numeric font then start font from 8, otherwise start from 4.
    2. using getTextDimensions check if the text fits the space, if not then decrease the font, until it fits, or 0 is reached.

    What this does is that if there are "only numbers" and it's a layout with big fields then it will be displayed with a big numeric font. If there's not enough space OR there are "non-numeric" characters then a smaller general font will be used. Worst case: font 0 (XTINY) will be used, even if it's too big or if some of the characters are not displayed.

    As far as I can tell this is more or less what's happening in SimpleDataField, but I'd like to have more control (change label, switch between the label and the data, in case of multi-line data automatically switch the order of the lines based on their length in case the field geometry is such that there's place for shorter/longer text depending on the vertical position - think about "2 Fields" layout with 2 lines of data: ["zone: 2", "heart rate: 123"]. If the order doesn't matter then in the top semi-disk it will be displayed as that, but in the bottom semi-disk it will be reversed to: ["heart rate: 123", "zone: 2"])

  • What you're trying to do is something I think every dev has wanted to do since day one of CIQ.  The problem is, there is no way to do it programmatically.  The font metrics are inconsistent across devices and fonts.  Some have whitespace, some don't.  The values returned from getFontDescent, etc, don't always include all the whitespace.  It's dumb.

    If the SDK had a way to query the value of a pixel at a certain location in a bitmap, you could draw the text to an offscreen DC, get the bitmap, enumerate all the bits and find the first row that had a non-blank pixel.  Repeat from the bottom, then you have the actual height of the rendered text, as well as the offset from the top.  But Garmin doesn't have a way to do that, either.

    For one of my apps, I only support a handful of devices because the app relies on the navigation properties in Activity.Info.  I wanted to do something similar to what you're doing, so I rendered the text, took a screenshot in the simulator, and counted the pixels myself.  (I could have written a program to take the bitmaps and count the pixels.)  Honestly, I don't think it was worth the effort in my case.  I would have probably gotten the same results by just creating layouts for each device and loading a device-specific constants file for the field locations.

    I've seen many other different approaches over the years.  The fact is Garmin created a crappy SDK and doesn't care about making it useful for the developers.  As a result, we have to do all kinds of crazy hacky crap.  You can try filing an enhancement request, and it'll go on the giant mountain of requests that have been untouched for years...

    (okay, done ranting...)

    I don't know if it would be possible, but maybe it could be done by writing a script to launch the simulator for each device, render the text for a specific font on the screen, take a screenshot, then repeat for the remaining fonts.  Then, do all that for every device.  Then, have a program that would go through all the screenshots and determine the whitespace around the rendered text by enumerating the pixels.  It would take a long time, but at least it would be automated and less error-prone than counting the pixels by looking at them.  (Speaking from experience.)  I just don't know if you can trigger a screenshot from within your app.  I suppose there is automated testing code for Windows that you could add into the mix that could trigger the menu command to take a screenshot.

    If anyone ever does something like that and generates a CSV file of all the measurements for every font on every device, they'd be a hero amongst heroes.  If I had infinite free time, I'd give it a try.  But it's hard enough finding time these days to do anything on my apps.  Working for a living definitely takes time away from the important stuff!

    Good luck! I hope you're able to find a solution!  Please tell us about it if you do...