Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions docs/en/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1121,10 +1121,6 @@
"path": "/manuals/font",
"name": "Font"
},
{
"path": "/extension-fontgen",
"name": "Runtime TTF Font"
},
{
"path": "/manuals/resource",
"name": "Resource management"
Expand Down
105 changes: 67 additions & 38 deletions docs/en/manuals/font.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ Fonts added to your project are automatically converted into a texture format th
- Bitmap
- Distance field

## Offline or Runtime fonts

By default, the conversion to rasterized glyph images happens at build time (offline). This has the drawback that each font needs to rasterize all possible glyphs in the build stage, producing potentially very large textures that consume memory and also increase the bundle size.

By using "runtime fonts", the .ttf fonts will be bundled as-is, and the rasterization will happen on-demand at runtime. This minimizes both runtime memory usage and the bundle size.

## Text layout support (e.g. Right-to-left)

The runtime fonts also have the benefit of supporting full text layout, e.g. right-to-left.
We currently use the libraries [HarfBuzz](https://github.com/harfbuzz/harfbuzz), [SheenBidi](https://github.com/Tehreer/SheenBidi), [libunibreak](https://github.com/adah1972/libunibreak) and [SkriBidi](https://github.com/memononen/Skribidi).

See [Enabling Runtime Fonts](/manuals/font#enabling-runtime-fonts)

## Font collection

The `.fontc` file format is also known as a font collection. In offline mode, only one font is associated with it.
When using runtime fonts, you can associate more than one font file (.ttf) with the font collection.

This allows for using the a font collection when rendering multiple texts in different languages, while also keeping the memory foot print low.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foot print -> footprint

E.g. loading a collection with the japanese font, then associate that font with the current main font, followed by unloading the japanese font collection.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Japanese with an uppercase 'j'


## Creating a font

To create a font for use in Defold, create a new Font file by selecting <kbd>File ▸ New...</kbd> from the menu, then select <kbd>Font</kbd>. You can also <kbd>right click</kbd> a location in the *Assets* browser and select <kbd>New... ▸ Font</kbd>.
Expand Down Expand Up @@ -81,6 +102,8 @@ Shadow support is enabled by the built-in font material shaders and handles both
*Characters*
: Which characters to include in the font. By default this field include the ASCII printable characters (character codes 32-126). You can add or remove characters from this field to include more or less characters in the font..

For runtime fonts, this text acts as a cache prewarming with the correct glyphs. This happens during load time. See `font.prewarm_text()`.

::: sidenote
The ASCII printable characters are:
space ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ \` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
Expand Down Expand Up @@ -163,23 +186,29 @@ For example - to generate a gradient in a shader fragment, simply write:

For more information about shader uniforms, see the [Shader manual](/manuals/shader).

## Runtime generation
## Enabling Runtime Fonts

It is possible to use runtime generation for SDF type fonts, when using TrueType (.ttf) fonts.
This approach can greatly reduce the download size and runtime memory consumption of a Defold game.
The small downside is a very small delay for each glyph generated at runtime.
The small downside is the asynchronous nature of generating each glyph.

* Enable the feature by setting `font.runtime_generation` in game.project.

Enable the feature by setting `font.runtime_generation` in game.project.
* Add an [App Manifest](/manuals/app-manifest) and enable the `Use full text layout system` option.
This builds a custom engine that has this feature enabled.

::: sidenote
This feature is currently experimental, but with the intention to be used as the default workflow in the future.
:::

::: important
This setting affects all .ttf fonts in the project.
The `font.runtime_generation` setting affects all .ttf fonts in the project.
:::

### Prewarming glyph cache

### Font Scripting

#### Prewarming glyph cache

In order to make the runtime fonts easier to use, they support prewarming of the glyph cache.
This means the font will generate the glyphs listed in *Characters* in the font.
Expand All @@ -188,53 +217,53 @@ This means the font will generate the glyphs listed in *Characters* in the font.
If `All Chars` is selected, there will be no prewarming as it defeats the purpose of not having to generate all glyphs at the same time.
:::

### Font Scripting
If the `Characters` field of the `.fontc` file is set, this is used as a text, to figure out which glyphs needs to be updated in the glyph cache.

It is also possible to manually update the glyph cache by calling `font.prewarm_text(font_collection, text, callback)`. It provides a callback to let you know when all the missing glyphs have been added to the glyph cache, and it's safe to present the text on screen.

### Adding/removing fonts to a font collection

For runtime fonts, it's possible to add or removed sub fonts.
For runtime fonts, it's possible to add or remove fonts (.ttf) to a font collection.
This is useful when a large font has been split up into multiple files for different character sets (e.g. CJK)

::: important
Adding a subfont doesn't automatically load or render all the glyphs.
Adding a font to a font collection doesn't automatically load or render all the glyphs.
:::

```lua
-- Add the range A-Z to the .fontc
local font_hash = hash("/assets/fonts/roboto.fontc")
local ttf_hash = hash("/assets/fonts/Roboto/Roboto-Bold.ttf")
local codepoint_min = 0x00000041 -- A
local codepoint_max = 0x0000005A -- Z
font.add_source(font_hash, ttf_hash, codepoint_min, codepoint_max)
-- get the main font
local font_collection = go.get("#label", "font")
font.add_font(font_collection, self.language_ttf_hash)

-- get the selected language font
local font_collection_language = go.get("localization_japanese#label", "font")
local font_info = font.get_info(font_collection_language)
self.language_ttf_hash = font_info.fonts[1].path_hash -- get the first font (the one specified in the editor)
font.add_font(self.font_collection, self.language_ttf_hash) -- increases the reference count to the font
```

```lua
-- Remove the associated ttf resource
local font_hash = hash("/assets/fonts/roboto.fontc")
local ttf_hash = hash("/assets/fonts/Roboto/Roboto-Bold.ttf")
font.remove_source(font_hash, ttf_hash)
-- remove the font reference
font.add_font(self.font_collection, self.language_ttf_hash)
```

To load the glyphs to the font, you will need to call the `font.add_glyphs()`.
It is an asynchronous operation, and once it's done, it's safe to progress to show any message containing the glyphs.
### Prewarming glyphs

```lua
local function add_glyph_callback(self, id, result, errmsg)
if not result then
print("Request " .. id .." finished with error:", errmsg)
else
msg.post(some_url, "show_dialog")
end
end

-- Load glyphs into the font
local font_hash = hash("/assets/fonts/roboto.fontc")
local glyphs = "Some text to be shown!" -- for optimal performance, make this a list of unique glyphs
local request_id = font.add_glyphs(font_hash, ttf_hash, add_glyph_callback)
```
To properly show a text with a runtime font, the glyphs need to be resolved. The `font.prewarm_text()` does this for you.
It is an asynchronous operation, and once it's done and you get the callback, it's safe to progress to show any message containing the glyphs.

::: important
If the glyph cache gets full, the oldest glyph in the cache will be evicted.
:::

And, once the characters aren't needed anymore, you can discard that memory:
```lua
-- Remove the associated ttf resource
local font_hash = hash("/assets/fonts/roboto.fontc")
font.remove_glyphs(font_hash, "All the characters in the set")
font.prewarm_text(self.font_collection, info.text, function (self, request_id, result, err)
if result then
print("PREWARMING OK!")
label.set_text(self.label, info.text)
else
print("Error prewarming text:", err)
end
end)
```

Loading