diff --git a/docs/en/en.json b/docs/en/en.json
index e2727229..65218732 100644
--- a/docs/en/en.json
+++ b/docs/en/en.json
@@ -1121,10 +1121,6 @@
"path": "/manuals/font",
"name": "Font"
},
- {
- "path": "/extension-fontgen",
- "name": "Runtime TTF Font"
- },
{
"path": "/manuals/resource",
"name": "Resource management"
diff --git a/docs/en/manuals/font.md b/docs/en/manuals/font.md
index d163f053..f42ff51b 100644
--- a/docs/en/manuals/font.md
+++ b/docs/en/manuals/font.md
@@ -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 footprint low.
+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.
+
## Creating a font
To create a font for use in Defold, create a new Font file by selecting File ▸ New... from the menu, then select Font. You can also right click a location in the *Assets* browser and select New... ▸ Font.
@@ -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 { | } ~
@@ -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.
@@ -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)
```