macOS font fallback implementation discussion

Dmitry Batrak dmitry.batrak at jetbrains.com
Thu Dec 16 14:13:30 UTC 2021


Hello,

I'm working on modifications to JDK's font fallback implementation on macOS
- first to be tested in JetBrains Runtime,
and then planned for submission into OpenJDK - and I'd like to discuss the
overall approach now, way before the
pull-request stage.

Currently, OpenJDK uses two macOS system functions to implement font
fallback: `JRSFontCreateFallbackFontForCharacters`
and `CTFontCopyDefaultCascadeListForLanguages`. Using two different
functions in different code paths is a source of
inconsistencies, and, I believe, it's generally agreed that some unified
approach should be used instead.

For the past 5 years, JetBrains Runtime solved this problem by using
`JRSFontCreateFallbackFontForCharacters`
exclusively for all code paths. But, as discussed previously [1], it's not
a preferred solution
(e.g. due to JDK-8024281).

The obvious remaining alternative is to use
`CTFontCopyDefaultCascadeListForLanguages` exclusively. It seems to work
quite fine (see e.g. [2] for implementation details), a runtime build using
such an approach is already used
internally in the company. The only shortcoming I see is the smaller
Unicode repertoire coverage.

Taking, for example, 'Menlo' as a base font, with
`JRSFontCreateFallbackFontForCharacters` used for fallback, we get
about 72k Unicode code points covered, while with
`CTFontCopyDefaultCascadeListForLanguages` - only about 60k. This is
apparently due to a larger set of fonts used by
`JRSFontCreateFallbackFontForCharacters` - investigation shows it can
even use fonts installed by the user.

Code points 'omitted' by the new approach belong to quite exotic scripts,
e.g. the largest missing 'blocks' are for
cuneiform and Egyptian hieroglyphs (each of them accounting for about 1k
code points). So, I believe, it shouldn't be a
significant problem, at least for our use cases, but this waits to be
confirmed or denied in a public testing.

What are your thoughts on this? Is such a regression in behaviour
acceptable, or is it a no-go for OpenJDK, and the
approach should be improved? To be more precise, switching to
`CTFontCopyDefaultCascadeListForLanguages` for font
fallback in a straightforward manner will make certain characters not
renderable with plain `Graphics.drawString`
call, while currently they are not renderable only in cases font has layout
attributes (ligatures or kerning), or
text layout is requested explicitly (via `Font.layoutGlyphVector` or using
`TextLayout` class). Of course, we are
talking about font fallback situation only - by selecting a font supporting
those characters specifically, it is always
possible to render the characters.

Speaking of options to improve the coverage, the simplest one is, of
course, extending the cascade list with
additional fonts. Either some hardcoded list of them can be used, or a
subset of the fonts available on the system
selected by some criteria. E.g. using all 'Noto Sans *' fonts (there are
about 100 of them on my macOS system) reduces
the coverage gap to about 1k code points. This option is quite easy to
implement, but it will require revisiting if
future macOS versions will switch to some other fonts for fallback.

Another possible option is to use `CTFontCreateForString` function, which
is basically the same as
`JRSFontCreateFallbackFontForCharacters`, but is a part of the public Core
Text API. It can be used in addition to cascade
list or as a sole source of font fallback information (as, I believe, it
does use the same cascade list internally).
This will require using a 'dynamic' variant of `CompositeFont`, which is
populated during its usage, but that worked OK
in JetBrains Runtime with  `JRSFontCreateFallbackFontForCharacters`.

A more extreme option would be to scan all available fonts, if the cascade
list doesn't contain a suitable one, but
I guess it might cause problems with performance.

What do you think about it? Did I miss some viable options? Does it make
sense to go beyond the default cascade list
at all, at least in the initial version?

[1] https://mail.openjdk.java.net/pipermail/2d-dev/2017-February/008205.html
[2] https://github.com/JB-Dmitry/jdk/commits/macFontFallback


Best regards,
Dmitry Batrak



More information about the client-libs-dev mailing list