Skip to content

FontWeight without FontFamily on the hyperlink popup title copies the whole system font on font-poor devices #51

Description

@ronnycohen

RichTextBox/RichTextBox.axaml line 199, the title of the "Insert Hyperlink" popup:

<TextBlock x:Name="HyperlinkPopupTitle" Text="Insert Hyperlink" FontWeight="SemiBold" FontSize="13"/>

FontWeight is set without a FontFamily, so the request goes to the platform default family with
weight 600. On a device whose default family only ships a static regular face — Android 11 with a
static Roboto 400 is the common case — the nearest match differs from the requested key, and
Avalonia synthesises a faux-bold by copying the whole font file into native memory. The
synthetic face is then cached under the source font's real name, never under the key that was asked
for, so the next request repeats the copy.

The popup itself is never shown in our application, which is what makes this hard to spot: the
template is materialised with the control, so every RichTextBox instance pays for it. Our app
builds three per screen and rebuilds screens continuously (digital signage), which turned it into
1 834 224 bytes retained per screen rebuild — 6 × 305 608, exactly six copies of
Roboto-Regular.ttf, with frees=0.

Attribution is measured, not inferred: changing that single attribute to FontWeight="Normal",
rebuilding, and re-running the same heap profile took the allocation site from 24 allocations /
1 834 224 bytes per rebuild to zero. Devices with a variable Roboto (Android 13) or a real 600
face are unaffected, and so is Windows.

The root cause is in Avalonia, and there is a PR open for it —
AvaloniaUI/Avalonia#21993 — so this may well
fix itself for you once that lands. Until then, any FontWeight set without a FontFamily is a
trap on font-poor devices.

I did not send a PR because the fix is a design call that is yours to make: dropping the weight
changes how the popup looks. The options I see are to remove FontWeight, or to pair it with an
explicit FontFamily that actually ships the face. Happy to send whichever you prefer.

Filed for the same reason as #49: we vendor this control and would rather not carry a local
divergence.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions