Skip to content

Garmin Font Analyzer

markw65 edited this page Feb 19, 2024 · 2 revisions

If you install @markw65/monkeyc-optimizer, you can use it to analyze Garmin's .cft files.

Installation

If you don't already have it, you will first need to install nodejs/npm. Once that's done, you can install @markw65/monkeyc-optimizer.

Local installation (ie per project) is usually the best way to work with npm packages. To do so, switch to the root of your project directory, and execute the following command:

npm install --save-dev @markw65/monkeyc-optimizer

If you prefer a global installation, you can instead run

npm install -g @markw65/monkeyc-optimizer

You can now run the font file analyzer via npx cft-font-info if you installed locally, or just cft-font-info if you installed globally. The rest of the instructions will assume you installed locally.

Command line arguments

  • --chars=<characters> - The characters to return information about. If omitted, all characters defined by the font are listed.
  • --char-info-as-array - Changes the format of the charInfo array from an array of objects, to an array of arrays (and adds a charInfoNames field describing the elements of each sub-array.

Each remaining argument is treated as a glob pattern. If the pattern ends in .cft (case insensitive) it will match font files in your ConnectIQ/Fonts directory, otherwise it will match devices in the ConnectIQ/Devices directory. For each device listed, all the fonts used by that device will be added to the list of fonts to analyze.

The output is a JSON object (jq can be very useful for filtering the bits you need).

The top level entries are fonts and devices.

  • fonts is an object whose keys are font names, and whose values are objects with keys:
    • height integer height of font
    • ascent integer ascent of font
    • internalLeading space reserved above the font for diacriticals
    • charInfo an array of character descriptors, each in the format
      • code integer value of the unicode character
      • char the character value as a unicode string
      • width integer width of the character
      • glyphAscent integer ascent for this character
      • glyphDescent integer descent for this character
  • devices is an object whose keys are device names, and whose values are objects with keys:
    • langMap an object whose keys are language abbreviations, and whose values are fontSet names
    • fontSets an object whose keys are fontSet names, and whose values are objects mapping Graphics.FONT_* names to font names

For example, npx cft-font-info fr235 --chars="01" would produce the following (formatted via jq)

{
  "FNT_BEBAS_NEUE_20B": {
    "height": 29,
    "ascent": 25,
    "internalLeading": 5,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 11,
        "glyphAscent": 20,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 11,
        "glyphAscent": 20,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_BEBAS_NEUE_32B": {
    "height": 47,
    "ascent": 40,
    "internalLeading": 8,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 18,
        "glyphAscent": 32,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 18,
        "glyphAscent": 32,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_BEBAS_NEUE_40B": {
    "height": 59,
    "ascent": 51,
    "internalLeading": 11,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 23,
        "glyphAscent": 40,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 23,
        "glyphAscent": 40,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_BEBAS_NEUE_72B": {
    "height": 104,
    "ascent": 89,
    "internalLeading": 17,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 40,
        "glyphAscent": 71,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 40,
        "glyphAscent": 70,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_NOTO_SANS_CJK_KR_MEDIUM_18": {
    "height": 18,
    "ascent": 14,
    "internalLeading": 4,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 7,
        "glyphAscent": 9,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 7,
        "glyphAscent": 9,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_NOTO_SANS_CJK_KR_MEDIUM_21": {
    "height": 21,
    "ascent": 16,
    "internalLeading": 4,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 8,
        "glyphAscent": 11,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 8,
        "glyphAscent": 11,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_NOTO_SANS_CJK_KR_REGULAR_26": {
    "height": 26,
    "ascent": 20,
    "internalLeading": 5,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 10,
        "glyphAscent": 14,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 10,
        "glyphAscent": 14,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_ROBOTO_12B": {
    "height": 19,
    "ascent": 15,
    "internalLeading": 3,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 8,
        "glyphAscent": 11,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 8,
        "glyphAscent": 11,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_ROBOTO_15": {
    "height": 24,
    "ascent": 19,
    "internalLeading": 4,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 9,
        "glyphAscent": 14,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 9,
        "glyphAscent": 14,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_ROBOTO_20B": {
    "height": 32,
    "ascent": 25,
    "internalLeading": 5,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 13,
        "glyphAscent": 19,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 13,
        "glyphAscent": 19,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_CN_BOLD_15": {
    "height": 14,
    "ascent": 11,
    "internalLeading": 3,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 6,
        "glyphAscent": 8,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 6,
        "glyphAscent": 8,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_CN_BOLD_21": {
    "height": 22,
    "ascent": 17,
    "internalLeading": 6,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 9,
        "glyphAscent": 11,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 9,
        "glyphAscent": 11,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_CN_BOLD_26": {
    "height": 26,
    "ascent": 20,
    "internalLeading": 6,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 11,
        "glyphAscent": 14,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 11,
        "glyphAscent": 14,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_JP_BOLD_15": {
    "height": 14,
    "ascent": 11,
    "internalLeading": 3,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 6,
        "glyphAscent": 8,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 6,
        "glyphAscent": 8,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_JP_BOLD_21": {
    "height": 22,
    "ascent": 17,
    "internalLeading": 6,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 9,
        "glyphAscent": 11,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 9,
        "glyphAscent": 11,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_JP_BOLD_26": {
    "height": 26,
    "ascent": 20,
    "internalLeading": 6,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 11,
        "glyphAscent": 14,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 11,
        "glyphAscent": 14,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_TWHK_BOLD_15": {
    "height": 14,
    "ascent": 11,
    "internalLeading": 3,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 6,
        "glyphAscent": 8,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 6,
        "glyphAscent": 8,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_TWHK_BOLD_21": {
    "height": 22,
    "ascent": 17,
    "internalLeading": 6,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 9,
        "glyphAscent": 11,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 9,
        "glyphAscent": 11,
        "glyphDescent": 0
      }
    ]
  },
  "FNT_SOURCE_HAN_SANS_TWHK_BOLD_26": {
    "height": 26,
    "ascent": 20,
    "internalLeading": 6,
    "charInfo": [
      {
        "code": 48,
        "char": "0",
        "width": 11,
        "glyphAscent": 14,
        "glyphDescent": 0
      },
      {
        "code": 49,
        "char": "1",
        "width": 11,
        "glyphAscent": 14,
        "glyphDescent": 0
      }
    ]
  },
  "devices": {
    "fr235": {
      "fontSets": {
        "ww": {
          "FONT_XTINY": "FNT_ROBOTO_12B",
          "FONT_TINY": "FNT_ROBOTO_12B",
          "FONT_SMALL": "FNT_ROBOTO_12B",
          "FONT_MEDIUM": "FNT_ROBOTO_15",
          "FONT_LARGE": "FNT_ROBOTO_20B",
          "FONT_NUMBER_MILD": "FNT_BEBAS_NEUE_20B",
          "FONT_NUMBER_MEDIUM": "FNT_BEBAS_NEUE_32B",
          "FONT_NUMBER_HOT": "FNT_BEBAS_NEUE_40B",
          "FONT_NUMBER_THAI_HOT": "FNT_BEBAS_NEUE_72B"
        },
        "apac_chn": {
          "FONT_XTINY": "FNT_SOURCE_HAN_SANS_CN_BOLD_15",
          "FONT_TINY": "FNT_SOURCE_HAN_SANS_CN_BOLD_15",
          "FONT_SMALL": "FNT_SOURCE_HAN_SANS_CN_BOLD_15",
          "FONT_MEDIUM": "FNT_SOURCE_HAN_SANS_CN_BOLD_21",
          "FONT_LARGE": "FNT_SOURCE_HAN_SANS_CN_BOLD_26",
          "FONT_NUMBER_MILD": "FNT_BEBAS_NEUE_20B",
          "FONT_NUMBER_MEDIUM": "FNT_BEBAS_NEUE_32B",
          "FONT_NUMBER_HOT": "FNT_BEBAS_NEUE_40B",
          "FONT_NUMBER_THAI_HOT": "FNT_BEBAS_NEUE_72B"
        },
        "apac_twn": {
          "FONT_XTINY": "FNT_SOURCE_HAN_SANS_TWHK_BOLD_15",
          "FONT_TINY": "FNT_SOURCE_HAN_SANS_TWHK_BOLD_15",
          "FONT_SMALL": "FNT_SOURCE_HAN_SANS_TWHK_BOLD_15",
          "FONT_MEDIUM": "FNT_SOURCE_HAN_SANS_TWHK_BOLD_21",
          "FONT_LARGE": "FNT_SOURCE_HAN_SANS_TWHK_BOLD_26",
          "FONT_NUMBER_MILD": "FNT_BEBAS_NEUE_20B",
          "FONT_NUMBER_MEDIUM": "FNT_BEBAS_NEUE_32B",
          "FONT_NUMBER_HOT": "FNT_BEBAS_NEUE_40B",
          "FONT_NUMBER_THAI_HOT": "FNT_BEBAS_NEUE_72B"
        },
        "apac_jpn": {
          "FONT_XTINY": "FNT_SOURCE_HAN_SANS_JP_BOLD_15",
          "FONT_TINY": "FNT_SOURCE_HAN_SANS_JP_BOLD_15",
          "FONT_SMALL": "FNT_SOURCE_HAN_SANS_JP_BOLD_15",
          "FONT_MEDIUM": "FNT_SOURCE_HAN_SANS_JP_BOLD_21",
          "FONT_LARGE": "FNT_SOURCE_HAN_SANS_JP_BOLD_26",
          "FONT_NUMBER_MILD": "FNT_BEBAS_NEUE_20B",
          "FONT_NUMBER_MEDIUM": "FNT_BEBAS_NEUE_32B",
          "FONT_NUMBER_HOT": "FNT_BEBAS_NEUE_40B",
          "FONT_NUMBER_THAI_HOT": "FNT_BEBAS_NEUE_72B"
        },
        "apac_tha": {
          "FONT_XTINY": "bitstreamVeraSans 16",
          "FONT_TINY": "bitstreamVeraSans 16",
          "FONT_SMALL": "bitstreamVeraSans 16",
          "FONT_MEDIUM": "bitstreamVeraSans 21",
          "FONT_LARGE": "bitstreamVeraSans 27",
          "FONT_NUMBER_MILD": "FNT_BEBAS_NEUE_20B",
          "FONT_NUMBER_MEDIUM": "FNT_BEBAS_NEUE_32B",
          "FONT_NUMBER_HOT": "FNT_BEBAS_NEUE_40B",
          "FONT_NUMBER_THAI_HOT": "FNT_BEBAS_NEUE_72B"
        },
        "apac_kor": {
          "FONT_XTINY": "FNT_NOTO_SANS_CJK_KR_MEDIUM_18",
          "FONT_TINY": "FNT_NOTO_SANS_CJK_KR_MEDIUM_18",
          "FONT_SMALL": "FNT_NOTO_SANS_CJK_KR_MEDIUM_18",
          "FONT_MEDIUM": "FNT_NOTO_SANS_CJK_KR_MEDIUM_21",
          "FONT_LARGE": "FNT_NOTO_SANS_CJK_KR_REGULAR_26",
          "FONT_NUMBER_MILD": "FNT_BEBAS_NEUE_20B",
          "FONT_NUMBER_MEDIUM": "FNT_BEBAS_NEUE_32B",
          "FONT_NUMBER_HOT": "FNT_BEBAS_NEUE_40B",
          "FONT_NUMBER_THAI_HOT": "FNT_BEBAS_NEUE_72B"
        }
      },
      "langMap": {
        "hrv": "ww",
        "ces": "ww",
        "dan": "ww",
        "dut": "ww",
        "eng": "ww",
        "fin": "ww",
        "fre": "ww",
        "deu": "ww",
        "gre": "ww",
        "hun": "ww",
        "ita": "ww",
        "nob": "ww",
        "pol": "ww",
        "por": "ww",
        "rus": "ww",
        "slo": "ww",
        "slv": "ww",
        "spa": "ww",
        "swe": "ww",
        "ind": "ww",
        "zhs": "apac_chn",
        "zht": "apac_twn",
        "kor": "apac_kor",
        "tha": "apac_tha",
        "jpn": "apac_jpn"
      }
    }
  }
}