Magnum::Text::AbstractFont class

Base for font plugins.

Provides interface for opening fonts, filling a glyph cache and layouting the glyphs.

Usage

Fonts are most commonly implemented as plugins, which means the concrete font implementation is loaded and instantiated through a PluginManager::Manager. A font is opened using either openFile() or openData() together with specifying the size at which glyphs will be rasterized. Then it stays open until the font is destroyed, close() or another font is opened.

In the following example a font is loaded from the filesystem using the StbTrueTypeFont plugin, prerendering all needed glyphs, completely with all error handling:

PluginManager::Manager<Text::AbstractFont> manager;
Containers::Pointer<Text::AbstractFont> font =
    manager.loadAndInstantiate("StbTrueTypeFont");
if(!font->openFile("font.ttf", 12.0f))
    Fatal{} << "Can't open font.ttf with StbTrueTypeFont";

Text::GlyphCache cache{Vector2i{128}};
font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
                            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                            "0123456789?!:;,. ");

See Loading and using plugins for more information about general plugin usage and the list of derived classes for available font plugins. See GlyphCache for more information about glyph caches and Renderer for information about actual text rendering.

Font size

Font libraries specify font size in points, where 1 pt = ~1.333 px at 96 DPI, so in the above snippet a 12 pt font corresponds to 16 px on a 96 DPI display. The font size corresponds to the height of the EM quad which is defined as the distance between ascent and descent.

Upon opening the font, the size in points is exposed in size(). Derived properties are specified in pixels in lineHeight(), ascent() and descent().

The font size used when opening the font affects how large the glyphs will be when rendered into the GlyphCache. Actual text rendering with Renderer however uses its own font size, and the rendered size is then additionally depending on the actual projection used. This decoupling of font sizes is useful for example in case of DistanceFieldGlyphCache, where a single prerendered glyph size can be used to render arbitrarily large font sizes without becoming blurry or jaggy. When not using a distance field glyph cache, it's usually desirable to have the font size and the actual rendered size match. See the Renderer documentation for further information about picking font sizes.

Loading data from memory, using file callbacks

Besides loading data directly from the filesystem using openFile() like shown above, it's possible to use openData() to import data from memory. Note that the particular importer implementation must support FontFeature::OpenData for this method to work.

Utility::Resource rs{"data"};
Containers::ArrayView<const char> data = rs.getRaw("font.ttf");
if(!font->openData(data, 12.0f))
    Fatal{} << "Can't open font data with StbTrueTypeFont";

Some font formats consist of more than one file and in that case you may want to intercept those references and load them in a custom way as well. For font plugins that advertise support for this with FontFeature::FileCallback this is done by specifying a file loading callback using setFileCallback(). The callback gets a filename, InputFileCallbackPolicy and an user pointer as parameters; returns a non-owning view on the loaded data or a Containers::NullOpt to indicate the file loading failed. For example, loading a memory-mapped font could look like below. Note that the file loading callback affects openFile() as well — you don't have to load the top-level file manually and pass it to openData(), any font plugin supporting the callback feature handles that correctly.

struct Data {
    std::unordered_map<std::string, Containers::Optional<
        Containers::Array<const char, Utility::Path::MapDeleter>>> files;
} data;

font->setFileCallback([](const std::string& filename,
    InputFileCallbackPolicy policy, Data& data)
        -> Containers::Optional<Containers::ArrayView<const char>>
    {
        auto found = data.files.find(filename);

        /* Discard the memory mapping, if not needed anymore */
        if(policy == InputFileCallbackPolicy::Close) {
            if(found != data.files.end()) data.files.erase(found);
            return {};
        }

        /* Load if not there yet. If the mapping fails, remember that to not
           attempt to load the same file again next time. */
        if(found == data.files.end()) found = data.files.emplace(
            filename, Utility::Path::mapRead(filename)).first;

        if(!found->second) return {};
        return Containers::arrayView(*found->second);
    }, data);

font->openFile("magnum-font.conf", 13.0f);

For importers that don't support FontFeature::FileCallback directly, the base openFile() implementation will use the file callback to pass the loaded data through to openData(), in case the importer supports at least FontFeature::OpenData. If the importer supports neither FontFeature::FileCallback nor FontFeature::OpenData, setFileCallback() doesn't allow the callbacks to be set.

The input file callback signature is the same for Text::AbstractFont, ShaderTools::AbstractConverter and Trade::AbstractImporter to allow code reuse.

Data dependency

The AbstractShaper instances returned from createShaper() have a code and data dependency on the dynamic plugin module — since their implementation is in the plugin module itself, the plugin can't be unloaded until the returned instance is destroyed.

Subclassing

The plugin needs to implement the doFeatures(), doClose(), doCreateShaper() functions, either doCreateGlyphCache() or doFillGlyphCache() and one or more of doOpen*() functions. See also AbstractShaper for more information.

In order to support FontFeature::FileCallback, the font needs to properly use the callbacks to both load the top-level file in doOpenFile() and also load any external files when needed. The doOpenFile() can delegate back into the base implementation, but it should remember at least the base file path to pass correct paths to subsequent file callbacks. The doSetFileCallback() can be overridden in case it's desired to respond to file loading callback setup, but doesn't have to be.

You don't need to do most of the redundant sanity checks, these things are checked by the implementation:

Derived classes

class FreeTypeFont
FreeType font plugin.
class MagnumFont
Simple bitmap font plugin.
class StbTrueTypeFont
TrueType font plugin using stb_truetype.

Public types

using Feature = FontFeature deprecated in 2020.06
Features supported by a font implementation.
using Features = FontFeatures deprecated in 2020.06
Set of features supported by a font implementation.

Public static functions

static auto pluginInterface() -> Containers::StringView
Plugin interface.
static auto pluginSearchPaths() -> Containers::Array<Containers::String>
Plugin search paths.

Constructors, destructors, conversion operators

AbstractFont() explicit
Default constructor.
AbstractFont(PluginManager::AbstractManager& manager, const Containers::StringView& plugin) explicit
Plugin manager constructor.

Public functions

auto features() const -> FontFeatures
Features supported by this font.
auto fileCallback() -> auto new in 2019.10
File opening callback function.
auto fileCallbackUserData() const -> void* new in 2019.10
File opening callback user data.
void setFileCallback(Containers::Optional<Containers::ArrayView<const char>>(*)(const std::string&, InputFileCallbackPolicy, void*) callback, void* userData = nullptr) new in 2019.10
Set file opening callback.
template<class T>
void setFileCallback(Containers::Optional<Containers::ArrayView<const char>>(*)(const std::string&, InputFileCallbackPolicy, T&) callback, T& userData) new in 2019.10
Set file opening callback.
auto isOpened() const -> bool
Whether any file is opened.
auto openData(Containers::ArrayView<const void> data, Float size) -> bool
Open raw data.
auto openFile(Containers::StringView filename, Float size) -> bool
Open a file.
void close()
Close currently opened file.
auto size() const -> Float
Font size in points.
auto ascent() const -> Float
Font ascent in pixels.
auto descent() const -> Float
Font descent in pixels.
auto lineHeight() const -> Float
Line height in pixels.
auto glyphCount() const -> UnsignedInt new in Git master
Total count of glyphs in the font.
auto glyphId(char32_t character) -> UnsignedInt
Glyph ID for given character.
auto glyphSize(UnsignedInt glyph) -> Vector2 new in Git master
Glyph size in pixels.
auto glyphAdvance(UnsignedInt glyph) -> Vector2
Glyph advance in pixels.
void fillGlyphCache(AbstractGlyphCache& cache, Containers::StringView characters)
Fill glyph cache with given character set.
auto createGlyphCache() -> Containers::Pointer<AbstractGlyphCache>
Create glyph cache.
auto createShaper() -> Containers::Pointer<AbstractShaper> new in Git master
Create an instance of this font shaper implementation.
auto layout(const AbstractGlyphCache& cache, Float size, Containers::StringView text) -> Containers::Pointer<AbstractLayouter> deprecated in Git master
Layout the text using font's own layouter.

Protected types

struct Properties
Font properties.

Protected functions

auto doOpenFile(Containers::StringView filename, Float size) -> Properties virtual
Implementation for openFile()

Private functions

auto doFeatures() const -> FontFeatures pure virtual
Implementation for features()
void doSetFileCallback(Containers::Optional<Containers::ArrayView<const char>>(*)(const std::string&, InputFileCallbackPolicy, void*) callback, void* userData) virtual
Implementation for setFileCallback()
auto doIsOpened() const -> bool pure virtual
Implementation for isOpened()
auto doOpenData(Containers::ArrayView<const char> data, Float size) -> Properties virtual
Implementation for openData()
void doClose() pure virtual
Implementation for close()
auto doGlyphId(char32_t character) -> UnsignedInt pure virtual
Implementation for glyphId()
auto doGlyphSize(UnsignedInt glyph) -> Vector2 pure virtual new in Git master
Implementation for glyphSize()
auto doGlyphAdvance(UnsignedInt glyph) -> Vector2 pure virtual
Implementation for glyphAdvance()
void doFillGlyphCache(AbstractGlyphCache& cache, Containers::ArrayView<const char32_t> characters) virtual
Implementation for fillGlyphCache()
auto doCreateGlyphCache() -> Containers::Pointer<AbstractGlyphCache> virtual
Implementation for createGlyphCache()
auto doCreateShaper() -> Containers::Pointer<AbstractShaper> pure virtual new in Git master
Implementation for createShaper()

Typedef documentation

typedef FontFeature Magnum::Text::AbstractFont::Feature

Features supported by a font implementation.

typedef FontFeatures Magnum::Text::AbstractFont::Features

Set of features supported by a font implementation.

Function documentation

static Containers::StringView Magnum::Text::AbstractFont::pluginInterface()

Plugin interface.

"cz.mosra.magnum.Text.AbstractFont/0.3.5"

static Containers::Array<Containers::String> Magnum::Text::AbstractFont::pluginSearchPaths()

Plugin search paths.

Looks into magnum/fonts/ or magnum-d/fonts/ next to the dynamic Trade library, next to the executable and elsewhere according to the rules documented in Corrade::PluginManager::implicitPluginSearchPaths(). The search directory can be also hardcoded using the MAGNUM_PLUGINS_DIR CMake variables, see Downloading and building for more information.

Not defined on platforms without dynamic plugin support.

auto Magnum::Text::AbstractFont::fileCallback() new in 2019.10

File opening callback function.

void* Magnum::Text::AbstractFont::fileCallbackUserData() const new in 2019.10

File opening callback user data.

void Magnum::Text::AbstractFont::setFileCallback(Containers::Optional<Containers::ArrayView<const char>>(*)(const std::string&, InputFileCallbackPolicy, void*) callback, void* userData = nullptr) new in 2019.10

Set file opening callback.

In case the font plugin supports FontFeature::FileCallback, files opened through openFile() will be loaded through the provided callback. Besides that, all external files referenced by the top-level file will be loaded through the callback function as well, usually on demand. The callback function gets a filename, InputFileCallbackPolicy and the userData pointer as input and returns a non-owning view on the loaded data as output or a Corrade::Containers::NullOpt if loading failed — because empty files might also be valid in some circumstances, nullptr can't be used to indicate a failure.

In case the font plugin doesn't support FontFeature::FileCallback but supports at least FontFeature::OpenData, a file opened through openFile() will be internally loaded through the provided callback and then passed to openData(). First the file is loaded with InputFileCallbackPolicy::LoadTemporary passed to the callback, then the returned memory view is passed to openData() (sidestepping the potential openFile() implementation of that particular font plugin) and after that the callback is called again with InputFileCallbackPolicy::Close because the semantics of openData() don't require the data to be alive after. In case you need a different behavior, use openData() directly.

In case callback is nullptr, the current callback (if any) is reset. This function expects that the font plugin supports either FontFeature::FileCallback or FontFeature::OpenData. If a font plugin supports neither, callbacks can't be used.

It's expected that this function is called before a file is opened. It's also expected that the loaded data are kept in scope for as long as the font plugin needs them, based on the value of InputFileCallbackPolicy. Documentation of particular importers provides more information about the expected callback behavior.

Following is an example of setting up a file loading callback for fetching compiled-in resources from Corrade::Utility::Resource. See the overload below for a more convenient type-safe way to pass the user data pointer.

font->setFileCallback([](const std::string& filename,
    InputFileCallbackPolicy, void*) {
        Utility::Resource rs{"data"};
        return Containers::optional(rs.getRaw(filename));
    });

template<class T>
void Magnum::Text::AbstractFont::setFileCallback(Containers::Optional<Containers::ArrayView<const char>>(*)(const std::string&, InputFileCallbackPolicy, T&) callback, T& userData) new in 2019.10

Set file opening callback.

Equivalent to calling the above with a lambda wrapper that casts void* back to T* and dereferences it in order to pass it to callback. Example usage — this reuses an existing Corrade::Utility::Resource instance to avoid a potentially slow resource group lookup every time:

const Utility::Resource rs{"data"};
font->setFileCallback([](const std::string& filename,
    InputFileCallbackPolicy, const Utility::Resource& rs) {
        return Containers::optional(rs.getRaw(filename));
    }, rs);

bool Magnum::Text::AbstractFont::openData(Containers::ArrayView<const void> data, Float size)

Open raw data.

Parameters
data File data
size Font size in points

Closes previous file, if it was opened, and tries to open given raw data. Available only if FontFeature::OpenData is supported. On failure prints a message to Error and returns false.

bool Magnum::Text::AbstractFont::openFile(Containers::StringView filename, Float size)

Open a file.

Parameters
filename Font file
size Size to open the font in, in points

Closes previous file, if it was opened, and tries to open given file. On failure prints a message to Error and returns false. If file loading callbacks are set via setFileCallback() and FontFeature::OpenData is supported, this function uses the callback to load the file and passes the memory view to openData() instead. See setFileCallback() for more information.

void Magnum::Text::AbstractFont::close()

Close currently opened file.

On certain implementations an explicit call to this function when the file is no longer needed but the font instance is going to be reused further may result in freed memory. This call is also done automatically when the font instance gets destructed or when another file is opened. If no file is opened, does nothing. After this function is called, isOpened() is guaranteed to return false.

Float Magnum::Text::AbstractFont::size() const

Font size in points.

Font size is defined as the distance between ascent() and descent(), thus the value of (ascent - descent)*0.75f (i.e., converted from pixels) is equal to size().

Float Magnum::Text::AbstractFont::ascent() const

Font ascent in pixels.

Distance from baseline to top, positive value. Font size is defined as the distance between ascent() and descent(), thus the value of (ascent - descent)*0.75f (i.e., converted to points) is equal to size(). Expects that a font is opened.

Float Magnum::Text::AbstractFont::descent() const

Font descent in pixels.

Distance from baseline to bottom, negative value. Font size is defined as the distance between ascent() and descent(), thus the value of (ascent - descent)*0.75f (i.e., converted to points) is equal to size(). Expects that a font is opened.

Float Magnum::Text::AbstractFont::lineHeight() const

Line height in pixels.

Distance between baselines in consecutive text lines that corresponds to ascent() and descent(). Expects that a font is opened.

UnsignedInt Magnum::Text::AbstractFont::glyphCount() const new in Git master

Total count of glyphs in the font.

Expects that a font is opened.

UnsignedInt Magnum::Text::AbstractFont::glyphId(char32_t character)

Glyph ID for given character.

Expects that a font is opened.

Vector2 Magnum::Text::AbstractFont::glyphSize(UnsignedInt glyph) new in Git master

Glyph size in pixels.

Parameters
glyph Glyph ID

Size of the glyph image in pixels when rasterized. Some implementations may return fractional values, in which case Math::ceil() should be used to get the actual integer pixel size.

Vector2 Magnum::Text::AbstractFont::glyphAdvance(UnsignedInt glyph)

Glyph advance in pixels.

Parameters
glyph Glyph ID

Distance the cursor for the next glyph that follows glyph. Doesn't consider kerning or any other advanced shaping features. Expects that a font is opened and glyph is less than glyphCount().

void Magnum::Text::AbstractFont::fillGlyphCache(AbstractGlyphCache& cache, Containers::StringView characters)

Fill glyph cache with given character set.

Parameters
cache Glyph cache instance
characters UTF-8 characters to render

Fills the cache with given characters. Fonts having FontFeature::PreparedGlyphCache do not support partial glyph cache filling, use createGlyphCache() instead. Expects that a font is opened and characters is valid UTF-8.

Containers::Pointer<AbstractGlyphCache> Magnum::Text::AbstractFont::createGlyphCache()

Create glyph cache.

Configures and fills glyph cache with the contents of whole font. Available only if FontFeature::PreparedGlyphCache is supported. Other fonts support only partial glyph cache filling, see fillGlyphCache(). Expects that a font is opened.

Containers::Pointer<AbstractShaper> Magnum::Text::AbstractFont::createShaper() new in Git master

Create an instance of this font shaper implementation.

The returned class can be used to shape text using this font. See its documentation for more information. Note that the font has to stay in scope for as long as any AbstractShaper instances originating from the font exist. Expects that a font is opened. The returned instance is never nullptr.

Containers::Pointer<AbstractLayouter> Magnum::Text::AbstractFont::layout(const AbstractGlyphCache& cache, Float size, Containers::StringView text)

Layout the text using font's own layouter.

Parameters
cache Glyph cache
size Size to layout the text in, in pooints
text Text to layout

Note that the layouters support rendering of single-line text only. See Renderer class for more advanced text layouting. Expects that a font is opened.

Properties Magnum::Text::AbstractFont::doOpenFile(Containers::StringView filename, Float size) virtual protected

Implementation for openFile()

If doIsOpened() returns true after calling this function, it's assumed that opening was successful and the Properties are expected to contain valid values. If doIsOpened() returns false, the returned values are ignored. If FontFeature::OpenData is supported, default implementation opens the file and calls doOpenData() with its contents. It is allowed to call this function from your doOpenFile() implementation — in particular, this implementation will also correctly handle callbacks set through setFileCallback().

This function is not called when file callbacks are set through setFileCallback() and FontFeature::FileCallback is not supported — instead, file is loaded though the callback and data passed through to doOpenData().

void Magnum::Text::AbstractFont::doSetFileCallback(Containers::Optional<Containers::ArrayView<const char>>(*)(const std::string&, InputFileCallbackPolicy, void*) callback, void* userData) virtual private

Implementation for setFileCallback()

Useful when the font plugin needs to modify some internal state on callback setup. Default implementation does nothing and this function doesn't need to be implemented — the callback function and user data pointer are available through fileCallback() and fileCallbackUserData().

Properties Magnum::Text::AbstractFont::doOpenData(Containers::ArrayView<const char> data, Float size) virtual private

Implementation for openData()

If doIsOpened() returns true after calling this function, it's assumed that opening was successful and the Properties are expected to contain valid values. If doIsOpened() returns false, the returned values are ignored.

void Magnum::Text::AbstractFont::doFillGlyphCache(AbstractGlyphCache& cache, Containers::ArrayView<const char32_t> characters) virtual private

Implementation for fillGlyphCache()

The string is converted from UTF-8 to UTF-32, duplicate characters are not removed.

Containers::Pointer<AbstractShaper> Magnum::Text::AbstractFont::doCreateShaper() pure virtual private new in Git master

Implementation for createShaper()

This function is only called if the font is opened. The implementation is not allowed to return nullptr.