Skip to content

Commit

Permalink
Add fallback font support for iOS and macOS
Browse files Browse the repository at this point in the history
This builds on top of #7661 and adds an iOS / macOS API for setting fallback fonts.

At a high-level, this adds a class property to `RiveFont` for getting / setting the fallback font(s) **based on the system** (or _optionally_, a `UIFont/NSFont`)(accessible in Objective-C and Swift via `RiveFont.fallbackFonts`). This property is an array of a objects conforming to a new protocol: `RiveFallbackFontProvider` (platform-agnostic). By default, if no fallbacks are set, or an empty array is set, the default system font of regular weight will be used.

In terms of naming, the `RiveFallbackFontDescriptorDesign` and `RiveFallbackFontDescriptorWeight` types each have cases that mirror those available in 1st party Apple APIs, so that usage and expectations are similar across our APIs, as well as those provided by UIKit / AppKit.

## Example Usage

```swift
RiveFont.fallbackFonts = [
  RiveFallbackFontDescriptor(systemDesign: .default, weight: .bold),
  RiveFallbackFontDescriptor(systemDesign: .monospaced, weight: .ultraLight),
  UIFont.systemFont(ofSize: 20, weight: .bold)
]
```

## RiveFallbackFontProvider

`RiveFallbackFontProvider` is a protocol that defines the interface for types that can be used to return system fonts, or any font that can be used as a fallback. `RiveFallbackFontDescriptor` and `UIFont/NSFont` conform to this protocol; both can be used to define fallback fonts.

## RiveFallbackFontDescriptor

`RiveFallbackFontDescriptor` is a platform-agnostic way of defining the _type_ of system font you want to request as a fallback, if necessary. It contains a couple of properties: `design` and `weight`. These are used in conjunction with each other to start with and update a system font (generated by `[UI/NS]Font.systemFont(ofSize:weight:)`, potentially matching on more than one font.

## Unit Tests

Unit tests have been written to verify that `design` and `weight` create different fonts, based on the provided values. The tests at a high-level are the same: for each case of both properties, check that there is at least one matching font. For each property, check that each font name is unique. On iOS, the font names are unique based on system design _and_ weight. I felt this was better than asserting against a specific font name, in case Apple changes that from under our feet. Additionally, what the default system fallback is set to is also tested.

## IRL Testing

This was tested by creating a riv file that contained a text run whose font was exported containing only the glyphs used, and setting the text run to some text that did not use the exported glyphs.

Tested on:
- [x] iOS (Simulator)
- [x] iPadOS
- [x] macOS

Diffs=
a4e15fb7b Add fallback font support for iOS and macOS (#7690)

Co-authored-by: David Skuza <[email protected]>
  • Loading branch information
dskuza and dskuza committed Sep 10, 2024
1 parent 4f8bc5a commit 0cf0159
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24bb958f1bcb943f51d51b55ef32e22c33f40c87
a4e15fb7b532b276e15b5d3b3a60843581641a05
2 changes: 1 addition & 1 deletion include/rive/text/font_hb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class HBFont : public rive::Font
bool hasGlyph(rive::Span<const rive::Unichar>) const override;

static rive::rcp<rive::Font> Decode(rive::Span<const uint8_t>);
static rive::rcp<rive::Font> FromSystem(void* systemFont);
static rive::rcp<rive::Font> FromSystem(void* systemFont, uint16_t weight, uint8_t width);
hb_font_t* font() const { return m_font; }

private:
Expand Down
5 changes: 4 additions & 1 deletion src/text/font_hb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ rive::rcp<rive::Font> HBFont::Decode(rive::Span<const uint8_t> span)
}

#ifndef __APPLE__
rive::rcp<rive::Font> HBFont::FromSystem(void* systemFont) { return nullptr; }
rive::rcp<rive::Font> HBFont::FromSystem(void* systemFont, uint16_t weight, uint8_t width)
{
return nullptr;
}
#endif

//////////////
Expand Down
9 changes: 8 additions & 1 deletion src/text/font_hb_apple.mm
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@
#import <CoreText/CoreText.h>

#include "hb-coretext.h"
#include "hb-ot.h"

rive::rcp<rive::Font> HBFont::FromSystem(void* systemFont)
rive::rcp<rive::Font> HBFont::FromSystem(void* systemFont, uint16_t weight, uint8_t width)
{
auto font = hb_coretext_font_create((CTFontRef)systemFont);
hb_variation_t variation_data[2];
variation_data[0].tag = HB_OT_TAG_VAR_AXIS_WEIGHT;
variation_data[0].value = weight;
variation_data[1].tag = HB_OT_TAG_VAR_AXIS_WIDTH;
variation_data[1].value = width;
hb_font_set_variations(font, variation_data, 2);
if (font)
{
return rive::rcp<rive::Font>(new HBFont(font));
Expand Down

0 comments on commit 0cf0159

Please sign in to comment.