[BUG/PATCH] SkParagraph: Assertion Failure and incorrect BiDi/cluster mapping with SIL Graphite (Urdu/Nastaliq)

23 views
Skip to first unread message

David Hartman

unread,
Dec 6, 2025, 7:44:52 AM (5 days ago) Dec 6
to skia-discuss

Hello Skia Team,

I am reporting with patches to fix a severe bug in the SkParagraph module related to Urdu Nastaliq text shaping for U+0600-U+06003 combined with digits when using a custom Flutter engine with SIL Graphite enabled in HarfBuzz. The issue results in a complete app crash (due to an assertion failure) and incorrect rendering of Arabic marks combined with digits.

My team reached out to the developer of the font that we are using, and after running several tests he concluded that it appeared to be in text segmentation where characters get segmented into right-to-left and left-to-right text. These unicode characters 0600-0603 need to be in the same segment as the digits that will combine with them, but he believes they are getting put into separate segments. With this information Copilot identified the two root causes and used two small patches that resolve the issues. We are providing the analysis and patches below as we do not have permission to file an issue directly in the Modules > Paragraph component.

1. Summary of Issues
  • Cluster Gap: (ParagraphImpl.cpp) - Assertion failure/crash due to EMPTY_INDEX entries in fClustersIndexFromCodeUnit.
  • BiDi Mapping (SkUnicode_icu_bidi.cpp) - Digits do not correctly combine with Arabic marks (U+0600–U+0603) due to fragile UTF-16 to UTF-8 offset mapping.
2. Steps to Reproduce3. Root Cause and Proposed Fix (Code)

You can view and download skia-unicode-0600-0603-fix.patch. I built the Flutter engine with SIL Graphite and these patches, and I am in the process of making it available in a new bucket like I did and mentioned above for Flutter built with SIL Graphite enabled in Harfbuzz.

A. Patch 1: Defensive Cluster Fill (ParagraphImpl.cpp)

The cluster index table (fClustersIndexFromCodeUnit) is sometimes left with EMPTY_INDEX gaps after shaping, causing crashes in subsequent lookups. This patch adds a simple two-pass fill to ensure every code unit maps to a valid cluster.

Proposed Patch (Applied after line 551 in modules/skparagraph/src/ParagraphImpl.cpp)

B. Patch 2: Robust UTF-16 to UTF-8 Mapping (SkUnicode_icu_bidi.cpp)

The incremental mapping from UTF-16 indices (from ICU) to UTF-8 offsets (used by SkParagraph) is fragile for multi-byte codepoints, causing bidi regions to be mapped incorrectly. This patch replaces the incremental logic with an explicit, precomputed vector-based mapping.

Proposed Patch (Replaces bidi region extraction logic starting around line 117 in modules/skunicode/src/SkUnicode_icu_bidi.cpp)

Thank you for your time and consideration. Please let me know if you require a more information or access to our patched engine build.

Best regards,
David Hartman

David Hartman

unread,
Dec 8, 2025, 10:26:10 AM (3 days ago) Dec 8
to skia-discuss
Update: I have made this patch built in Flutter version 3.38.4 available by setting FLUTTER_STORAGE_BASE_URL="https://storage.googleapis.com/flutter-skia-patch". So you can test with and without the patch in Flutter version 3.38.4 by changing your FLUTTER_STORAGE_BASE_URL when you install Flutter, which I recommend doing with "fvm fork add <fork name: e.g. graphite, patch, etc.> https://github.com/flutter/flutter.git".
Reply all
Reply to author
Forward
0 new messages