-
Notifications
You must be signed in to change notification settings - Fork 59
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
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
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
commented
Jan 6, 2021
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
wooferzfg
reviewed
Jan 11, 2021
Co-authored-by: wooferzfg <spapushin@gmail.com>
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 toFont 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 publicAPI. 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