Previously, font selection ignored the Unicode emoji presentation of a
code point. Emoji-capable code points were always resolved through
pre-baked color emoji and symbol fonts. Text-default code points which
lacked the `Emoji_Presentation` property, were therefore rendered as
color emoji rather than text. We now classify each code point's default
presentation from its `Emoji_Presentation` property and any trailing
variation selector.
The shaping cache previously stored HarfBuzz buffers keyed by string,
and `shape_text()` rebuilt a fresh `GlyphRun` on every call. Cache the
font-independent shape data instead, so repeated shaping of the same
input skips both the HarfBuzz call and the glyph-vector build.
Clamp the CSS pixel size used for HarfBuzz font scales before passing
it to the int-sized HarfBuzz API. Also accumulate shaped advances
in double precision while measuring text. Very large web-provided font
sizes can otherwise overflow before layout clamps dimensions.
Add a reduced crash test for laying out text with a huge font size.
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.
Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
Keep CoreText system UI fonts backed by their CoreGraphics font instead
of opening the Skia typeface stream during font matching. Opening that
stream copies the full macOS system font data into SkData, which is very
expensive when CSS font computation asks for several system UI faces.
Let Typeface subclasses provide the HarfBuzz face creation path, and use
HarfBuzz's CoreText integration for these macOS system fonts. Clone Skia
font variations through SkTypeface::makeClone() so variation handling no
longer depends on rebuilding a typeface from the raw font stream.
Display-list resource transactions need to carry fonts across an IPC
boundary without making LibWeb know how each typeface stores its bytes.
Add a LibGfx-owned Typeface IPC representation so callers can encode
and decode typefaces directly.
Anonymous-buffer and Core::Resource-backed typefaces serialize through
their retained backing. System typefaces serialize as family and style
data, with macOS system UI typefaces carrying their SystemUIFontKind
from creation so the receiver can rematch them through CoreText.
This is preparatory work required to add IPC between the main and
compositor threads.
The compositor IPC path needs variable-font axes to survive transport
with the font data that owns them. Add LibGfx-local specializations for
FontVariationSettings, compile the new implementation into LibGfx, and
expose the stored settings from Font so later font serializers can read
them without reaching into the class internals.
This is preparatory work required to add IPC between the main and
compositor threads.
Teach font-family parsing about system-ui, then route macOS UI generic
families through CoreText. This makes -apple-system, system-ui,
ui-serif, ui-sans-serif, ui-monospace, and ui-rounded resolve to the
same system designs as Safari.
The CoreText result is converted back into a Skia typeface and still
uses the existing HarfBuzz shaping path. CSS weight and width continue
through the normal font variation settings.
HarfBuzz uses ptem for AAT trak table lookup. Match other browsers
for macOS system fonts by passing the CSS pixel size instead of the
physical point size.
Future compositor-side rendering will need WebContent to share typefaces
with the compositor process over IPC. If a typeface already keeps its
backing bytes in anonymous shared memory, IPC encoding can clone that
buffer handle instead of allocating a new shared buffer and copying the
same font data during upload.
FontData was added when the WOFF and WOFF2 typeface wrappers went
away, so decoded TTF buffers and resource-backed font data could be
kept alive after Typeface loaded from non-owned ReadonlyBytes.
The ownership cases are now small enough to keep directly on the
typeface objects. Store resource-backed fonts in Typeface via a
Core::Resource RefPtr, keep copied or decoded bytes in ByteBuffer
members, and have the WOFF loaders pass decoded buffers directly to
Typeface.
fontconfig's font directory list may contain overlapping entries
(e.g. both /usr/share/fonts and /usr/share/fonts/truetype/msttcorefonts)
and our recursive walk was visiting each .ttf/.otf/.ttc several times
and mmap'ing it once per visit. On a typical Linux system this resulted
in 2-3 mappings per file before any actual rendering.
Track each font's filesystem path and skip files we've already loaded.
Drops WebContent's font-file VMA count from ~7400 to ~2500 on my box.
Commands used to keep rendering resources directly in the variant:
image frames, external content and video sources, filters, SVG paint
styles, and nested display lists. That makes the command stream own its
dependencies and prevents it from becoming a POD-like byte buffer.
Add DisplayListResourceStorage and replace those command fields with
stable resource IDs. The storage deduplicates resources by their
existing IDs and can copy only the resources referenced by a captured
command sequence, giving the future IPC boundary a clear list of
resources that must be communicated to the rasterization process.
When Skia's openStream() returns a memory-backed stream, retain it
on TypefaceSkia::Impl so the underlying bytes stay alive, and
reference them directly instead of copying into a ByteBuffer.
This pattern is used regularly inside Skia, so it seems fair game.
Add an off-thread preparation step for downloaded vector fonts so
WOFF2 resources can be decompressed before LibWeb tries to create a
typeface from them. This avoids doing the conversion work on the main
thread during @font-face loading.
Expose raw WOFF2-to-TTF conversion from LibGfx's WOFF2 loader and use
that from the new preparation path. Keeping the libwoff2 integration
in LibGfx preserves the layering between LibWeb and the third-party
decoder while still letting LibWeb schedule the work off-thread.
Pass the requested font weight and slope to generic_font_name() and use
them to select the most suitable font family. Families that have the
exact requested weight are strongly preferred, followed by families with
more style variety.
This ensures that when CSS requests bold text with a generic font family
like `serif`, we select a family that actually supports bold weights
rather than one that only has regular weight.
Co-authored-by: Jelle Raaijmakers <jelle@ladybird.org>
Parse the TTC header to determine the number of fonts in the collection
and load each one individually. This ensures that all font weights and
styles bundled in a TTC file are available for font matching.
The #pragma once was placed after the #include directives instead of
immediately after the copyright comment, inconsistent with every other
header file
Font objects are cached per-Typeface with non-atomic RefCounted, but
GlyphRun (which is AtomicRefCounted) holds a NonnullRefPtr<Font const>
and can be destroyed on the rendering thread when tearing down display
lists. This created a data race on the Font refcount, leading to
use-after-free crashes in Typeface::font() where the cache's
NonnullRefPtr would point to memory already freed by a concurrent
unref.
We had three instances of `pair_int_hash()` being called with a value
that was pulled through `u32_hash()`, which is not necessary - both
arguments to `pair_int_hash()` will be properly hashed.
Rework our hash functions a bit for significant better performance:
* Rename int_hash to u32_hash to mirror u64_hash.
* Make pair_int_hash call u64_hash instead of multiple u32_hash()es.
* Implement MurmurHash3's fmix32 and fmix64 for u32_hash and u64_hash.
On my machine, this speeds up u32_hash by 20%, u64_hash by ~290%, and
pair_int_hash by ~260%.
We lose the property that an input of 0 results in something that is not
0. I've experimented with an offset to both hash functions, but it
resulted in a measurable performance degradation for u64_hash. If
there's a good use case for 0 not to result in 0, we can always add in
that offset as a countermeasure in the future.
Previously we would resolve font features
(https://drafts.csswg.org/css-fonts-4/#feature-variation-precedence)
per element, while this works for the current subset of the font feature
resolution algorithm that we support, some as yet unimplemented parts
require us to know whether we are resolving against a CSS @font-face
rule, and if so which one (e.g. applying descriptors from the @font-face
rule, deciding which @font-feature-values rules to apply, etc).
To achieve this we store the data required to resolve font features in a
struct and pass that to `FontComputer` which resolves the font features
and stores them with the computed `Font`.
We no longer need to invalidate the font shaping cache when features
change since the features are defined per font (and therefore won't ever
change).
While our default font supporting variations is unlikely, this is
nevertheless required for our fallback font to be considered equal to
it's non-default/fallback equivalent (i.e. `font-family: serif`) which
in turn is required for LineBuilder to merge chunks into a single
fragment.
When rendering text, if none of the fonts in the cascade list contain a
glyph for a given code point, we now query Skia's font manager to find
a system font that can render it.
Skia deprecated some non-span versions of their API, but they provided
SK_SUPPORT_UNSPANNED_APIS to expose the legacy versions.
SkFontMgr_New_FontConfig now requires a font scanner to be passed in.
There were a few screenshot tests that visibily looked the same but skia
must've changed some rendering infrastructure as the PNGs were not
matching anymore so I rebaselined those and adjusted the fuzzy matching
config to allow them to pass on both macOS and Linux.
The empty-radial-gradient-crash Ref test started to fail as we were
setting the horizontal scale factor to inf in when the height = 0. It
looks like something changed to make doing that not valid anymore.
The overlay port is removed as the issues, mainly skcms symbol import
and export were resolved upstream in skia and utilized in the new port
version.
Skia deprecated some non-span versions of their API, but they provided
SK_SUPPORT_UNSPANNED_APIS to expose the legacy versions.
SkFontMgr_New_FontConfig now requires a font scanner to be passed in.
There were a few screenshot tests that visibily looked the same but skia
must've changed some rendering infrastructure as the PNGs were not
matching anymore so I rebaselined those and adjusted the fuzzy matching
config to allow them to pass on both macOS and Linux.
The empty-radial-gradient-crash Ref test started to fail as we were
setting the horizontal scale factor to inf in when the height = 0. It
looks like something changed to make doing that not valid anymore.
The overlay port is removed as the issues, mainly skcms symbol import
and export were resolved upstream in skia and utilized in the new port
version.
Previously when launching a UI process and/or a WebContent process on
Windows, the following message would be output to the console:
Fontconfig error: Cannot load default config file: No such file: (null)
Apparently on Windows, you have to provide a font .conf file to
fontconfig explicitly. Since we are not doing that, the default fonts
that are backed off to are not ideal.
Similar to how we aren't using fontconfig on Android, Windows also has
native font infrastructure in terms of both a collection of System
installed fonts as well as skia support for font managers that use
native Windows APIs. With that, we can remove the usage of fontconfig
entirely and improve the fonts used on websites like ladybird.org or
google.com.