Martin's Blog

Using FontAwesome 5 in QML

FontAwesome is a set of icon fonts making it easy to beef up your user interfaces. It is mostly used in web applications (aka HTML/CSS), however, leveraging it to your Qt applications is not difficult either.

First, download the latest release from their website. You unzip it and find the container *.ttf files in web-fonts-with-css/webfonts/. You should have (among others) the following files there:

  • fa-brands-400.ttf
  • fa-regular-400.ttf
  • fa-solid-900.ttf

Copy these into your application’s source directory. Next, we will need to edit the file fa-solid-900.ttf. At the time of writing this, when loading both the regular and solid variants into the application, there was no way to switch between the two, because basically Qt/QML sees both as the same font. Hence, we can use e.g. FontForge to edit the file and change its family name to something else. For this, install FontForge. For example, in Fedora Linux you just need to run

sudo yum install -y fontforge

Then, open the file fa-solid-900.ttf with the tool. In the Element menu, you will find an entry called Font Info:

fontforge-menu

Selecting it will open up a new dialog window which allows you to edit the font details. Make sure you set the font family to Font Awesome 5 Free Solid:

fontforge-details

Close the dialog by clicking the OK button and proceed by selecting Generate Fonts from the File menu. This should bring up the following dialog:

fontforge-generate

Make sure you select TrueType as font type and uncheck the option Validate Before Saving.

In QML, we can now easily access these fonts by writing a (singleton) component which makes them available:

// Fonts.qml

pragma Singleton

import QtQuick 2.0

Item {
    id: fonts

    readonly property FontLoader fontAwesomeRegular: FontLoader {
        source: "./fa-regular-400.ttf"
    }
    readonly property FontLoader fontAwesomeSolid: FontLoader {
        source: "./fa-solid-900.ttf"
    }
    readonly property FontLoader fontAwesomeBrands: FontLoader {
        source: "./fa-brands-400.ttf"
    }

    readonly property string icons: fonts.fontAwesomeBrands.name
    readonly property string solidIcons: fonts.fontAwesomeSolid.name
    readonly property string brands: fonts.fontAwesomeBrands.name
}

For the singleton to work, you must create a qmldir file next to it as well:

// qmldir
singleton Icons 1.0 Icons.qml

With this, you should be able to display some icons using e.g. a ToolButton:

// FontAwesomeToolButton.qml

import QtQuick 2.9
import QtQuick.Controls 2.2

// This must point to the directory where you places
// the 'Fonts.qml' file. If all your sources are in the
// same directory, this can be '.' as well.
import "../path/to/Fonts"

ToolButton {
    id: button

    property alias symbol: button.text

    // This can be set to one of the loaded fonts, either
    // 'Fonts.icons', 'Fonts.solidIcons' or
    // 'Fonts.brands'.
    font.family: Fonts.solidIcons
}

And finally we can use our tool button implementation:

// demo.qml

import QtQuick 2.9

// Adjust the paths accordingly. The latter import is where
// our tool button implementation is stored:
import "./path/to/Fonts"
import "./path/to/Components"

Row {
    FontAwesomeToolButton {
        symbol: "\uf13d"
    }
    FontAwesomeToolButton {
        symbol: "\uf101"
    }
    FontAwesomeToolButton {
        symbol: "\uf069"
    }
}

Run this e.g. via qmlscene:

qmlscene ./demo.qml

You should get something like this: FontAwesome Tool Buttons

While this approach works, you will have notices one bad thing: In order to select which icon to display, we inserted the unicode character codes as label names. You can get them easily from the FontAwesome Gallery, however, after inserting into the QML source code you do not quickly see which icon a code refers to. In HTML, you would use something like

<i class="fas fa-anchor"></i>
<i class="fas fa-angle-double-right"></i>
<i class="fas fa-asterisk"></i>

which is far more readable. To achieve something similar in QML, we can extract some meta information from the FontAwesome download package and generate another QML component from that one.

First, create a file fa_iconsjson2qml.py and put the following content in it:

#!/bin/env python3

"""
Convert FontAwesome's `icons.json` to QML.

This script creates a QML component which defines
constants for all FontAwesome icons listed in the file
`icons.json` in the FontAwesome package.
"""

import sys


def _dash2cap(name):
    try:
        while True:
            idx = name.index("-")
            pre = name[0:idx]
            suf = name[idx + 2:]
            cap = name[idx + 1:idx + 2].capitalize()
            name = pre + cap + suf
    except ValueError:
        pass
    return name


def _main(argv):
    import json
    if len(argv) != 3:
        print("Usage:")
        msg = "    {} path/to/icons.json path/to/Icons.qml"
        msg = msg.format(argv[0])
        print(msg)
        sys.exit(1)
    with open(argv[1], "r") as file:
        icons = json.load(file)
    lines = []
    lines.append("pragma Singleton")
    lines.append("import QtQuick 2.0")
    lines.append("")
    lines.append("QtObject {")
    prop = '    readonly property string {}: "{}"'
    for key in icons:
        name = "fa-" + key
        name = _dash2cap(name)
        code = "\\u" + icons[key]["unicode"]
        line = prop.format(name, code)
        lines.append(line)
    lines.append("}")
    with open(argv[2], "w") as file:
        file.write("\n".join(lines))


if __name__ == '__main__':
    _main(sys.argv)

Make the file executable and call it on the file icons.json which is contained in the FontAwesome zip:

chmod +x ./fa_iconsjson2qml.py
./fa_iconsjson2qml.py \
    ./path/to/FontAwesome/advanced-options/metadata/icons.json \
    ./path/to/qml/sources/Fonts/Icons.qml

This script basically extracts the available icons from the JSON file and creates another QML singleton component which looks like this:

pragma Singleton
import QtQuick 2.0

QtObject {
    readonly property string fa500px: "\uf26e"
    readonly property string faAccessibleIcon: "\uf368"
    readonly property string faAccusoft: "\uf369"
    readonly property string faAddressBook: "\uf2b9"
    readonly property string faAddressCard: "\uf2bb"
    // several hundred similar lines following...
}

Ideally, put the file next to the Fonts.qml file and edit the qmldir file to look like this:

singleton Icons 1.0 Icons.qml
singleton Fonts 1.0 Fonts.qml

Now, you can modify the demo from above to:

// demo.qml

import QtQuick 2.9

// Adjust the paths accordingly. The latter import is where
// our tool button implementation is stored:
import "./path/to/Fonts"
import "./path/to/Components"

Row {
    FontAwesomeToolButton {
        symbol: Icons.faAnchor
    }
    FontAwesomeToolButton {
        symbol: Icons.faAngleDoubleRight
    }
    FontAwesomeToolButton {
        symbol: Icons.faAsterisk
    }
}

… which is way more readable.

This project is maintained by mhoeher