Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Support for Custom Fonts #385

Merged
merged 3 commits into from
Jan 11, 2021
Merged

Conversation

CryZe
Copy link
Collaborator

@CryZe CryZe commented Jan 6, 2021

This implements the support for specifying custom fonts in layouts. Unlike the original LiveSplit, this acts more as an override here. One reason for this is to make it easier for platforms to just not have any font support in the first place, such as terminal applications where it's simply not possible to change fonts. If a user never specifies a custom font, it's expected that the application can then use any font that is appropriate for the situation.

Fonts are specified as a font family name and additional styling information, such as the style (italic / regular), weight and stretch of the specific font to select out of the font family. The application then chooses the most appropriate font that it can find, falling back to its default fonts if it can't find the font. It turns out that different APIs and applications have very different notions of what a family name even is. This is because a font can have lots of different, even conflicting, names in its name table. The kinds of names that you specify there have evolved over the years, so there's old entries and newer entries, where the font selector has to make very unclear decisions which to prefer. It seems like modern APIs prefer to select for the Typographic Family Name (Name ID 16), with a fallback to Font Family Name (Name ID 1) in case the former is not available. Old APIs such as GDI do their own special thing though, complicating the matter even more. I have yet to fully grasp what GDI even does. Even among browsers the font selection seems to work differently. So it all seems like a big mess where you can only try to align with as many APIs as closely as possible, and a correct solution is simply not possible. Since the original LiveSplit seems to select its fonts through GDI, we also run into the issue of importing these identifiers properly. Technically we could query GDI for the specific font, but this is only possible on Windows and is not that easy, as we'd need to parse out the typographic family name, but GDI doesn't have an API for that, nor does it give you the path of the font or access to the font's data. It gives you access to the raw data of individual tables you query for, but ttf-parser doesn't expose a way to parse individual tables in its public
API. So for all of these reason, for now we only implement a fallback converter for these identifiers where we attempt to pop individual style tokens off the end of the identifier to turn it into styling information and the family name. This is however also flawed in other ways, such as those GDI font identifiers also being limited to 32 characters, meaning that you can end up unlucky and have a style token be cut off in the middle at the end, at which point we can't recognize it as such anymore.

Another issue was vertically aligning text. Fonts have various vertical lines, such as the ascender (being the very top a glyph can reach), the baseline (the line that glyphs are written on top of), and the descender (the very bottom a glyph can reach). These three lines can be arbitrarily defined by a font. It is impossible to take two fonts and apply a linear transformation to it to align all three of these lines. Considering we usually have the text font and the times font in the same line, we however still need to make them look aligned. Also there is the possibility that more fonts get involved as certain glyphs may not be available in these fonts, such as emojis. At this point you definitely want to align their baselines. If you also keep a fixed scaling across the fonts, which is usually what you also want, you end up with ascenders and descenders that are all over the place. However this is basically a necessity, there's nothing you can do about this without harming the previously mentioned important properties. Therefore it can be concluded that any sort of attempt at vertical centering is basically impossible and you instead need to choose a baseline that looks decent across many different fonts. Coincidentally this is already what the renderer did, so nothing needed to be changed in this regard. Though we may want to take a closer look at possibly better positioning for that baseline.

The rendering unit tests aren't allowed to use any custom fonts, as that otherwise causes those tests to fail on different platforms where these fonts may not be available. Also this introduces a new font-loading feature where our renderer only loads any fonts if the feature is active. This is because the renderer could be compiled for a platform where the code isn't able to load any fonts for, such as in my experiments to compile the renderer to the GameCube and Gameboy Advance. So this is another reason why we simply can't load any custom fonts in our tests.

Resolves #104

This implements the support for specifying custom fonts in layouts.
Unlike the original LiveSplit, this acts more as an override here. One
reason for this is to make it easier for platforms to just not have any
font support in the first place, such as terminal applications where
it's simply not possible to change fonts. If a user never specifies a
custom font, it's expected that the application can then use any font
that is appropriate for the situation.

Fonts are specified as a font family name and additional styling
information, such as the style (italic / regular), weight and stretch of
the specific font to select out of the font family. The application then
chooses the most appropriate font that it can find, falling back to its
default fonts if it can't find the font. It turns out that different
APIs and applications have very different notions of what a family name
even is. This is because a font can have lots of different, even
conflicting, names in its name table. The kinds of names that you
specify there have evolved over the years, so there's old entries and
newer entries, where the font selector has to make very unclear
decisions which to prefer. It seems like modern APIs prefer to select
for the `Typographic Family Name` (Name ID 16), with a fallback to `Font
Family Name` (Name ID 1) in case the former is not available. Old APIs
such as GDI do their own special thing though, complicating the matter
even more. I have yet to fully grasp what GDI even does. Even among
browsers the font selection seems to work differently. So it all seems
like a big mess where you can only try to align with as many APIs as
closely as possible, and a correct solution is simply not possible.
Since the original LiveSplit seems to select its fonts through GDI, we
also run into the issue of importing these identifiers properly.
Technically we could query GDI for the specific font, but this is only
possible on Windows and is not that easy, as we'd need to parse out the
typographic family name, but GDI doesn't have an API for that, nor does
it give you the path of the font or access to the font's data. It gives
you access to the raw data of individual tables you query for, but
ttf-parser doesn't expose a way to parse individual tables in its public
API. So for all of these reason, for now we only implement a fallback
converter for these identifiers where we attempt to pop individual style
tokens off the end of the identifier to turn it into styling information
and the family name. This is however also flawed in other ways, such as
those GDI font identifiers also being limited to 32 characters, meaning
that you can end up unlucky and have a style token be cut off in the
middle at the end, at which point we can't recognize it as such anymore.

Another issue was vertically aligning text. Fonts have various vertical
lines, such as the ascender (being the very top a glyph can reach), the
baseline (the line that glyphs are written on top of), and the descender
(the very bottom a glyph can reach). These three lines can be
arbitrarily defined by a font. It is impossible to take two fonts and
apply a linear transformation to it to align all three of these lines.
Considering we usually have the text font and the times font in the same
line, we however still need to make them look aligned. Also there is the
possibility that more fonts get involved as certain glyphs may not be
available in these fonts, such as emojis. At this point you definitely
want to align their baselines. If you also keep a fixed scaling across
the fonts, which is usually what you also want, you end up with
ascenders and descenders that are all over the place. However this is
basically a necessity, there's nothing you can do about this without
harming the previously mentioned important properties. Therefore it can
be concluded that any sort of attempt at vertical centering is basically
impossible and you instead need to choose a baseline that looks decent
across many different fonts. Coincidentally this is already what the
renderer did, so nothing needed to be changed in this regard. Though we
may want to take a closer look at possibly better positioning for that
baseline.

The rendering unit tests aren't allowed to use any custom fonts, as that
otherwise causes those tests to fail on different platforms where these
fonts may not be available. Also this introduces a new `font-loading`
feature where our renderer only loads any fonts if the feature is
active. This is because the renderer could be compiled for a platform
where the code isn't able to load any fonts for, such as in my
experiments to compile the renderer to the GameCube and Gameboy Advance.
So this is another reason why we simply can't load any custom fonts in
our tests.
@CryZe CryZe added enhancement An improvement for livesplit-core. c api The issue or pull request is about the C API. rendering The issue or pull request is affecting the rendering. feature A new user visible feature for livesplit-core. priority: high This is a high priority issue. labels Jan 6, 2021
@CryZe CryZe added this to the v0.12 milestone Jan 6, 2021
@CryZe CryZe requested a review from wooferzfg January 6, 2021 23:23
@CryZe
Copy link
Collaborator Author

CryZe commented Jan 6, 2021

Oh god, font-kit really doesn't like to be cross compiled... ooof

1. no_std support was broken because of a lacking import of String.

2. If we cross compile with cross, it'll look for freetype on Linux,
   which isn't installed in these docker images
@CryZe CryZe added the ci Affects the Continuous Integration. label Jan 6, 2021
src/layout/general_settings.rs Show resolved Hide resolved
src/settings/font.rs Outdated Show resolved Hide resolved
src/settings/font.rs Outdated Show resolved Hide resolved
src/settings/font.rs Outdated Show resolved Hide resolved
src/rendering/font.rs Outdated Show resolved Hide resolved
src/layout/parser.rs Outdated Show resolved Hide resolved
src/layout/parser.rs Outdated Show resolved Hide resolved
src/layout/layout_state.rs Show resolved Hide resolved
src/layout/parser.rs Show resolved Hide resolved
Co-authored-by: wooferzfg <spapushin@gmail.com>
@CryZe CryZe merged commit 0ea1b4a into LiveSplit:master Jan 11, 2021
@CryZe CryZe deleted the custom-fonts branch January 11, 2021 17:49
@CryZe CryZe added the parsing This is about one of the parsers. label Mar 26, 2021
CryZe added a commit that referenced this pull request Nov 14, 2021
- Runs now support custom variables that are key value pairs that either the
  user can specify in the run editor or are provided by a script like an auto
  splitter. [#201](#201)
- There is now an option in the run editor to generate a comparison based on a
  user specified goal time. This uses the same algorithm as the `Balanced PB`
  comparison but with the time specified instead of the personal best.
  [#209](#209)
- Images internally are now stored as is without being reencoded as Base64 which
  was done before in order to make it easier for the web LiveSplit One to
  display them. [#227](#227)
- The Splits.io API is now available under the optional `networking` feature.
  [#236](#236)
- All key value based components share the same component state type now.
  [#257](#257)
- The crate now properly supports `wasm-bindgen` and `WASI`.
  [#263](#263)
- There is now a dedicated component for displaying the comparison's segment
  time. [#264](#264)
- Compiling the crate without `std` is now supported. Most features are not
  supported at this time though.
  [#270](#270)
- [`Splitterino`](https://github.com/prefixaut/splitterino) splits can now be
  parsed. [#276](#276)
- The `Timer` component can now show a segment timer instead.
  [#288](#288)
- Gamepads are now supported on the web.
  [#310](#310)
- The underlying "skill curve" that the `Balanced PB` samples is now exposed in
  the API. [#330](#330)
- The layout states can now be updated, which means almost all of the
  allocations can be reused from the previous frame. This is a lot faster.
  [#334](#334)
- In order to calculate a layout state, the timer now provides a snapshot
  mechanism that ensures that the layout state gets calculated at a fixed point
  in time. [#339](#339)
- Text shaping is now done via `rustybuzz` which is a port of `harfbuzz`.
  [#378](#378)
- Custom fonts are now supported.
  [#385](#385)
- The renderer is not based on meshes anymore that are suitable for rendering
  with a 3D graphics API. Instead the renderer is now based on paths, which are
  suitable for rendering with a 2D graphics API such as Direct2D, Skia, HTML
  Canvas, and many more. The software renderer is now based on `tiny-skia` which
  is so fast that it actually outperforms any other rendering and is the
  recommended way to render.
  [#408](#408)
- Remove support for parsing `worstrun` splits. `worstrun` doesn't support
  splits anymore, so `livesplit-core` doesn't need to keep its parsing support.
  [#411](#411)
- Remove support for parsing `Llanfair 2` splits. `Llanfair 2` was never
  publicly available and is now deleted entirely.
  [#420](#420)
- Hotkeys are now supported on macOS.
  [#422](#422)
- The renderer is now based on two layers. A bottom layer that rarely needs to
  be rerendered and the top layer that needs to be rerendered on every frame.
  Additionally the renderer is now a scene manager which manages a scene that an
  actual rendering backend can then render out.
  [#430](#430)
- The hotkeys are now based on the [UI Events KeyboardEvent code
  Values](https://www.w3.org/TR/uievents-code/) web standard.
  [#440](#440)
- Timing is now based on `CLOCK_BOOTTIME` on Linux and `CLOCK_MONOTONIC` on
  macOS and iOS. This ensures that all platforms keep tracking time while the
  operating system is in a suspended state.
  [#445](#445)
- Segment time columns are now formatted as segment times.
  [#448](#448)
- Hotkeys can now be resolved to the US keyboard layout.
  [#452](#452)
- They hotkeys are now based on `keydown` instead of `keypress` in the web.
  `keydown` handles all keys whereas `keypress` only handles visual keys and is
  also deprecated. [#455](#455)
- Hotkeys can now be resolved to the user's keyboard layout on both Windows and
  macOS. [#459](#459) and
  [#460](#460)
- The `time` crate is now used instead of `chrono` for keeping track of time.
  [#462](#462)
- The scene manager now caches a lot more information. This improves the
  performance a lot as it does not need to reshape the text on every frame
  anymore, which is a very expensive operation.
  [#466](#466) and
  [#467](#467)
- The hotkeys on Linux are now based on `evdev`, which means Wayland is now
  supported. Additionally the hotkeys are not consuming the key press anymore.
  [#474](#474)
- When holding down a key, the hotkey doesn't repeat anymore on Linux, macOS and
  WebAssembly. The problem still occurs on Windows at this time.
  [#475](#475) and
  [#476](#476)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c api The issue or pull request is about the C API. ci Affects the Continuous Integration. enhancement An improvement for livesplit-core. feature A new user visible feature for livesplit-core. parsing This is about one of the parsers. priority: high This is a high priority issue. rendering The issue or pull request is affecting the rendering.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Customizable Fonts
2 participants