question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Discussion: HarfBuzz shaping extension design

See original GitHub issue

In #5244 I have mentioned wanting to create HarfBuzz-based font shaping extension for libGDX. Since then I’ve been working on it, in my limited spare time. I have already created a JNI-Gen binding for HarfBuzz, and now I’m designing the shaper itself. It is not yet in a PR form, but I want to discuss some things I have encountered so far and how to tackle them.

First off, current BitmapFont is not well suited for being part of this, so I am creating a new “Font stack” around FreeType and HarfBuzz, which may eventually replace current one, if successful. BitmapFont does a lot of things which are not really needed in the new system, and more importantly assumes some things which are no longer true in complex layout, for example, that glyph ID maps 1:1 to char.

Separation of glyph generation and shaping

Even if the new font stack would be better than old one in every way, it could never replace it, unless it supports all platforms. And in this respect, the only one that is problematic, is GWT. It could be possible to emscripten all native libraries for JS, but it is not something I would really want to do. And since we’re probably not dropping support for GWT either, I am planning to split the new stack around these three responsibilities:

  • Shaping (with implementations: )
    • HarfBuzz
    • current approach, enough for Latin scripts, possibly extended to support widely used scripts like Arabic
  • Supplying glyphs to shaper
    • FreeType creation on demand (“incremental”)
    • Pre-rendered, cached from incremental, or even hybrid
  • Rendering shaped runs to (Sprite)Batch
    • BitmapFontCache reimplementation
    • maybe DistanceFieldFontCache also?

This division feels more appropriate than BitmapFont/BitmapFontData, which now do “too little and too much at the same time” respectively, as BitmapFont is basically just a wrapper for BitmapFontCache + BitmapFontData, and BitmapFontData is too tightly coupled to shaping and to the idea of pre-rendered glyph pages.

While on this topic: I could be wrong in my judgement, but I’d like to simplify the new equivalent of BitmapFontCache. Does it really need the ability to add multiple unrelated text layouts and to render only a part of the text? Latter will become almost impossible to implement fast & correct with the complex layout which can omit, add or even reorder characters.

Scaling and flipping

I don’t think that font should care about its scaling (BitmapFontData.setScale()) nor direction of Y coordinate (BitmapFont.isFlipped()). These things should be handled in layer one above (possibly in aforementioned BitmapFontCache) - currently it only complicates already pretty complicated code. IMO font should have only 2 size-related settings:

  • size in pixels - this is what shaping works in (HarfBuzz works only on integers anyway) and this is also the size of glyphs on backing texture. Both things should be pixel perfect for pretty results.
  • size in points - this is what (for example) UI system that uses the font works in. Ideally, these sizes would match on standard screen and be multiples of each other on high DPI screens (e.g. 100 points = 200px on 2x retina).

Maybe it would be best to specify only (float) size in points and (float) scaling factor to get size in pixels (int) from it. But I’m not sure on this yet.

Unicode

HarfBuzz does shaping, but does not help with BiDirectional text layout, nor deciding where line should be wrapped, nor between which chars can editing caret go (which seems to be a surprisingly tricky problem in HarfBuzz-ed unicode). Thankfully, all this can be solved with ICU library. However, it weighs around 12MB, which is quite a lot, but it should be possible to trim it as it contains a lot of unnecessary data (for this purpose).

Input text

While the current markup [BLUE]language[] is easy to use, it does not play well with most parts of the text stack. So for this font stack, I am playing with the other approach (used at least in iOS and possibly in Android): annotated text, i.e. object that associates text characters with their attributes.

This should make it easier to generate colors programatically, as text does not have to be modified anymore, and it will allow for colored text in input text fields, as markup tags previously got in the way and messed up mapping between GlyphRun indices and source text indices. Converter from current markup strings to annotated strings is, of course, planned.

Above, I mentioned attributes and not just color - I am playing with mixing different fonts in as well. This not only means multiple different typefaces in single string, but also italics, bolds, underlines (courtesy of FreeType) and also different sizes.

I am also considering to add a special support for tabulators (\t). Initially they will probably work like in terminal console (“move the run beginning with ‘\t’ to the right up to the nearest multiple of 4 spaces worth of space”) but it would be pretty easy to add support for more complex behavior, customizable when invoking the text layout.


What are your thoughts? Are there any other things that should be changed about the current system? Some things that I want to omit which are actually very useful? Or some other features which were overlooked, but should be factored in from the start?

I’ll keep this issue open until I have a PR ready.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:21 (21 by maintainers)

github_iconTop GitHub Comments

2reactions
NathanSweetcommented, Jun 27, 2018

Sounds cool! Our text area and even text field support has always been a bit janky. I wish you the best of luck, I know it’s a huge project.

1reaction
Darkyenuscommented, Sep 9, 2020

My repo is here and while it works as a proof of concept, it is not yet finished and ready for general use. The main problems from the usability point of view right now are:

  • No integration into existing font infrastructure (no scene2d, etc. integration)
  • No pre-built artifacts of natives (HarfBuzz)

And the code is not 100% finished either, although most of the “hard problems” are already solved. So it is not something any random user can just start using. But it can serve as a robust base for any further development, if you are prepared to put some effort in.

I am currently not planning on working on it further, but I will be able to help and answer questions about it, should somebody want to continue working on it.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Shaping and shape plans: HarfBuzz Manual
Shaping and buffer output. The hb_shape() function call takes four arguments: the font object to use, the buffer of characters to shape, an ......
Read more >
Making JSTF better - TypeDrawers
I've been trying to implement OpenType justification (JSTF table) in SILE, and, well, I can see why nobody else has done much with...
Read more >
OpenType MATH in HarfBuzz - Frédéric Wang
The choice for the shaping API is a bit more complex and discussions is still in progress. For example because we want to...
Read more >
State of Text Rendering - Behdad Esfahbod
OpenOffice.org currently uses ICU and AbiWord uses Pango. Both will have a better time using HarfBuzz shaping directly. Designer tools demand ...
Read more >
HarfBuzz حرف‌باز - Twitter
HarfBuzz حرف‌باز. @harfbuzz. The free and open text shaping library. harfbuzz.github.io Born September 21 Joined October 2015.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found