feat: Arabic/Farsi/Urdu bidi reordering and contextual shaping — PR 1/3 (#2541)

Co-authored-by: Uri Tauber <uritaube@gmail.com>
This commit is contained in:
Husam Younis
2026-07-13 16:02:21 +03:00
committed by GitHub
co-authored by Uri Tauber
parent 552b2683e6
commit 932a472835
18 changed files with 1087 additions and 62 deletions
+48 -6
View File
@@ -1,15 +1,19 @@
/* bidiclasses.t — bidi class table for CrossPoint Hebrew/English epub.
/* bidiclasses.t — bidi class table for CrossPoint RTL (Hebrew/Arabic) epub.
*
* Coverage rationale:
* Hebrew + English is the primary target. However, CrossPoint renders
* Latin and Cyrillic scripts for many other languages, so these MUST be
* classified as L (not fall through to ON) to avoid regression when they
* appear adjacent to Hebrew runs.
* Hebrew and Arabic-script languages (Arabic, Farsi, Urdu, Sindhi, Pashto,
* Kurdish) are the RTL targets. CrossPoint also renders Latin and Cyrillic
* scripts for many other languages, so these MUST be classified as L (not
* fall through to ON) to avoid regression when they appear adjacent to
* RTL runs.
*
* Scripts NOT in this table fall through to ON correct per UAX#9 for
* scripts CrossPoint's fonts don't support (CJK, Arabic, Devanagari, etc.)
* scripts CrossPoint's fonts don't support (CJK, Devanagari, etc.)
* ON is the right class for "unknown" it behaves neutrally.
*
* Arabic ranges are sourced from Unicode UCD extracted/DerivedBidiClass.txt
* (values verified against Unicode 17.0.0).
*
* Entries sorted ascending by first (binary search requirement).
*/
@@ -84,6 +88,36 @@
{0x05D0, 0x05EA, R}, /* alef … tav */
{0x05F0, 0x05F4, R}, /* alternative forms + geresh/gershayim */
/* ── Arabic / Perso-Arabic (DerivedBidiClass.txt) */
/* Letters are AL (Arabic Letter), harakat/marks are NSM, Arabic-Indic
digits are AN, extended (Farsi/Urdu) digits are EN.
0x0606-0x0607, 0x060E-0x060F, 0x06DE, 0x06E9 are ON fall through. */
{0x0600, 0x0605, AN}, /* Arabic number signs (Cf) */
{0x0608, 0x0608, AL}, /* Arabic ray */
{0x0609, 0x060A, ET}, /* per mille / per ten thousand */
{0x060B, 0x060B, AL}, /* afghani sign */
{0x060C, 0x060C, CS}, /* Arabic comma */
{0x060D, 0x060D, AL}, /* Arabic date separator */
{0x0610, 0x061A, NSM}, /* honorifics + small marks */
{0x061B, 0x061F, AL}, /* semicolon, ALM, end-of-text, hamza mark, question mark */
{0x0620, 0x064A, AL}, /* core Arabic letters + tatweel */
{0x064B, 0x065F, NSM}, /* harakat (fathatan … wavy hamza below) */
{0x0660, 0x0669, AN}, /* Arabic-Indic digits ٠-٩ */
{0x066A, 0x066A, ET}, /* Arabic percent sign */
{0x066B, 0x066C, AN}, /* decimal / thousands separators */
{0x066D, 0x066F, AL}, /* five-pointed star, dotless beh/qaf */
{0x0670, 0x0670, NSM}, /* superscript alef */
{0x0671, 0x06D5, AL}, /* extended letters: Farsi, Urdu, Sindhi, Pashto, Kurdish */
{0x06D6, 0x06DC, NSM}, /* Quranic annotation marks */
{0x06DD, 0x06DD, AN}, /* end of ayah */
{0x06DF, 0x06E4, NSM},
{0x06E5, 0x06E6, AL}, /* small waw / small yeh */
{0x06E7, 0x06E8, NSM},
{0x06EA, 0x06ED, NSM},
{0x06EE, 0x06EF, AL}, /* dal/reh with inverted V */
{0x06F0, 0x06F9, EN}, /* extended Arabic-Indic digits ۰-۹ (Farsi/Urdu) EN per UCD */
{0x06FA, 0x06FF, AL},
/* ── Latin Extended Additional (L) ─────────────────────────────────── */
/* Covers accented chars for Vietnamese, Welsh, Romanian, etc.
Not currently rendered by CrossPoint fonts, but costs only 2 table rows. */
@@ -110,6 +144,14 @@
{0x2069, 0x2069, PDI},
{0x206A, 0x206F, BN},
/* ── Arabic presentation forms (output of do_shape()) ───────────────── */
/* Contextual/ligature forms emitted by the shaper must classify as AL so
a reshaped line still resolves RTL. Ranges per DerivedBidiClass.txt;
0xFBC3-0xFBD2 are ON fall through. */
{0xFB50, 0xFBC2, AL}, /* Presentation Forms-A: Perso-Arabic contextual forms */
{0xFBD3, 0xFBFF, AL}, /* Presentation Forms-A: NG … Farsi Yeh forms */
{0xFE70, 0xFE74, AL}, /* Presentation Forms-B: harakat isolated forms */
{0xFE76, 0xFEFC, AL}, /* Presentation Forms-B: contextual forms + Lam-Alef ligatures */
/* ── Byte Order Mark ────────────────────────────────────────────────── */
{0xFEFF, 0xFEFF, BN},