linebender / linebender/parley

Fontique fails to find font for ❯ char (U+276F) on macOS

Open
#678 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug C-fontique
Dominant language
Rust
Stars
736
Forks
120
Avg merge
1d 3h
Merged PRs (30d)
53

Description

This character not rendering has been a long-standing issue in Blitz's TodoMVC example, and font-fallback failure in Fontique was determined to be the root cause by Fable which had this to say:

The ❯ (U+276F, Dingbats block, script Common) isn't in the Helvetica Neue/Helvetica/Arial stack, and fontique's fallback is script-based (one CoreText-picked family per script sample) with no per-character last-resort search, so no font covering U+276Fis ever tried → .notdef. This is an upstream parley/fontique limitation.

I had GLM-5.2 write a test for it, and it had this to add:

  1. The character has Common Unicode script, so parley's itemizer assigns it the Latin script when surrounded by Latin text (Common/Inherited scripts inherit from surrounding context per UAX #24).
  2. The default system fallback for Latin (e.g. Helvetica on macOS) doesn't cover U+276F
  3. One font present on my system (macOS) that does cover this glyph is "Zapf Dingbats".

The following test reproduces the issue on macOS (not tested on other OSs). It must be run with the system feature enabled:

#[cfg(all(target_os = "macos", feature = "system"))]
#[test]
fn font_fallback_heavy_angle_quote() {
    let mut env = TestEnv::new(test_name!(), None);

    // Load system fonts so that fontique can find a fallback for U+276F.
    env.font_context().collection.load_system_fonts();

    let text = "\u{276F}";
    let builder = env.ranged_builder(text);
    let mut layout = builder.build(text);
    layout.break_all_lines(None);
    layout.align(Alignment::Start, AlignmentOptions::default());

    let glyph_id = layout
        .lines()
        .flat_map(|line| line.items())
        .find_map(|item| match item {
            PositionedLayoutItem::GlyphRun(glyph_run) => glyph_run.glyphs().next(),
            PositionedLayoutItem::InlineBox(_) => None,
        })
        .map(|glyph| glyph.id)
        .expect("expected a glyph for the ❯ character");

    // The fallback font should have a real glyph for ❯, not a `.notdef`.
    assert_ne!(
        glyph_id, 0,
        "the ❯ character (U+276F) should not resolve to a .notdef glyph when a fallback font is available"
    );

}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Use the supplied macOS regression test, gated by the system feature, as the starting point. Inspect Fontique's script-based fallback and the font_context().collection.load_system_fonts() path; done means U+276F resolves to a real glyph instead of .notdef when a fallback font is available.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, rust
Domain
desktop, testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.