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 fallback font support to HarfBuzz sample #635

Merged

Conversation

LucidSigma
Copy link
Contributor

@LucidSigma LucidSigma commented Jul 17, 2024

Adds support for fallback fonts to the HarfBuzz text shaping sample. Resolves #634.

To implement this, I had to keep track of the original character codepoint alongside the glyph bitmaps (since the glyph index is always zero for unsupported glyphs, this is how they are able to be disambiguated). Each font now has a lookup table for fallback glyphs that maps each unsupported character to their respective bitmap, as well as functions to append and retrieve glyphs from fallback fonts.

For the font layers, the logic remained mostly the same. When generating a texture, they now iterate through both the regular and fallback glyphs of a font. The rectangle ID is now represented as a bit-shifted combination of the glyph index and character codepoint (using sixty-four bits). I found this to be the most elegant solution that supports mapping multiple glyph indices to character codepoints (and vice versa), and it only needs one texture layout. However, it did require me to copy all of the texture layout files to the sample just to change the type of the rectangle ID from a regular integer to a uint64_t. This won't really be an issue when integrating this into RmlUi proper, since the texture layout files can just be edited in situ.

Copy link
Owner

@mikke89 mikke89 left a comment

Choose a reason for hiding this comment

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

Very nice, thanks a lot for staying around to further improve the sample and font engine! :)

This works for me, and the code looks very reasonable. I didn't go into all the details, I hope that we at some point can de-duplicate the reused code and get the font engine into Core. At that point I will go a bit more detailed into it.

It seems like the fallback logic was mostly written from scratch. I wondered if there is something about Harfbuzz that makes it different from the one in our existing FreeType font engine in Core? Do you think we in principle could share the same fallback code?

Comment on lines +593 to +596
hb_direction_t text_direction = hb_script_get_horizontal_direction(script);
if (text_direction == HB_DIRECTION_INVALID)
// Some scripts support both horizontal directions of text flow; default to left-to-right.
text_direction = HB_DIRECTION_LTR;
Copy link
Owner

Choose a reason for hiding this comment

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

Nice to get away from hard-coded values :)

@mikke89 mikke89 merged commit 84b9cc1 into mikke89:master Jul 21, 2024
32 checks passed
@LucidSigma
Copy link
Contributor Author

LucidSigma commented Jul 22, 2024

The main issue was that the existing font engine uses Unicode character codepoints to identify glyphs, whereäs HarfBuzz uses glyph indices within the OpenType/TrueType data. This is because a single character can map to multiple glyphs (since a single character can be shaped differently depending on where it is in a word).

In the initial sample (before this PR), I made all of the function parameters and class members use glyph indices instead of character codepoints. This worked fine, but I had to change it if we wanted to support fallback glyphs.

A glyph index of zero is defined within the OpenType standard to represent a character that is unsupported by the font. This means that the character codepoint is the only way of identifying an unsupported character (since they all have the same glyph index of zero). Therefore, I needed two ways to identify a glyph: its index and its character codepoint. In the FontFaceHandle class, I decided to have two class member hash tables—one for regular glyphs (that maps indices to glyph data) and one for fallback glyphs (that maps codepoints to glyph data). This is a more readable solution, but it is where most of the duplication came from (such as having both GetGlyph and GetFallbackGlyph methods).

For the FontFaceLayer class (which generates the texture atlas), I merged them into a single hash table where the keys are represented by both the index and codepoint as a single unsigned 64-bit integer. It would be possible to also do this in the FontFaceHandle class if we wish, at the cost of code readability.

Other than that, I ensured using the preëxisting fallback code where possible (such as in FontProvider).

@LucidSigma LucidSigma deleted the add-fallback-fonts-to-harfbuzz-sample branch July 22, 2024 07:47
@mikke89
Copy link
Owner

mikke89 commented Jul 22, 2024

Thanks a lot for elaborating, that's very helpful. It makes a lot of sense to me now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Current HarfBuzz sample is not able to render emojis
2 participants