[ot-shape] Numeric runs native direction is LTR

See inline comments. Slightly modified version of the code from Jonathan
Kew on the linked issue.

Fixes https://github.com/harfbuzz/harfbuzz/issues/501
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 4fe148d..97958af 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -574,6 +574,35 @@
   hb_direction_t direction = buffer->props.direction;
   hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script);
 
+  /* Numeric runs in natively-RTL scripts are actually native-LTR, so we reset
+   * the horiz_dir if the run contains at least one decimal-number char, and no
+   * letter chars (ideally we should be checking for chars with strong
+   * directionality but hb-unicode currently lacks bidi categories).
+   *
+   * This allows digit sequences in Arabic etc to be shaped in "native"
+   * direction, so that features like ligatures will work as intended.
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/501
+   */
+  if (unlikely (horiz_dir == HB_DIRECTION_RTL && direction == HB_DIRECTION_LTR))
+  {
+    bool found_number = false, found_letter = false;
+    const auto* info = buffer->info;
+    foreach_grapheme (buffer, start, end)
+    {
+      auto gc = _hb_glyph_info_get_general_category (&info[start]);
+      if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
+        found_number = true;
+      else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc))
+      {
+        found_letter = true;
+        break;
+      }
+    }
+    if (found_number && !found_letter)
+      horiz_dir = HB_DIRECTION_LTR;
+  }
+
   /* TODO vertical:
    * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
    * Ogham fonts are supposed to be implemented BTT or not.  Need to research that
diff --git a/src/hb-unicode.hh b/src/hb-unicode.hh
index 5c0cb85..0a79944 100644
--- a/src/hb-unicode.hh
+++ b/src/hb-unicode.hh
@@ -359,6 +359,13 @@
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
 
+#define HB_UNICODE_GENERAL_CATEGORY_IS_LETTER(gen_cat) \
+	(FLAG_UNSAFE (gen_cat) & \
+	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | \
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) | \
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER)))
 
 /*
  * Ranges, used for bsearch tables.
diff --git a/test/shaping/data/in-house/Makefile.sources b/test/shaping/data/in-house/Makefile.sources
index b3fdca2..39cc8d6 100644
--- a/test/shaping/data/in-house/Makefile.sources
+++ b/test/shaping/data/in-house/Makefile.sources
@@ -14,6 +14,7 @@
 	tests/context-matching.tests \
 	tests/cursive-positioning.tests \
 	tests/default-ignorables.tests \
+	tests/digits.tests \
 	tests/emoji.tests \
 	tests/fallback-positioning.tests \
 	tests/hangul-jamo.tests \
diff --git a/test/shaping/data/in-house/fonts/3b791518a9ba89675df02f1eefbc9026a50648a6.ttf b/test/shaping/data/in-house/fonts/3b791518a9ba89675df02f1eefbc9026a50648a6.ttf
new file mode 100644
index 0000000..76855be
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/3b791518a9ba89675df02f1eefbc9026a50648a6.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/a6b17da98b9f1565ba428719777bbf94a66403c1.ttf b/test/shaping/data/in-house/fonts/a6b17da98b9f1565ba428719777bbf94a66403c1.ttf
new file mode 100644
index 0000000..84e8a59
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/a6b17da98b9f1565ba428719777bbf94a66403c1.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/b082211be29a3e2cf91f0fd43497e40b2a27b344.ttf b/test/shaping/data/in-house/fonts/b082211be29a3e2cf91f0fd43497e40b2a27b344.ttf
new file mode 100644
index 0000000..6e59fd4
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/b082211be29a3e2cf91f0fd43497e40b2a27b344.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/e5ff44940364c2247abed50bdda30d2ef5aedfe4.ttf b/test/shaping/data/in-house/fonts/e5ff44940364c2247abed50bdda30d2ef5aedfe4.ttf
new file mode 100644
index 0000000..a7c569f
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/e5ff44940364c2247abed50bdda30d2ef5aedfe4.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/meson.build b/test/shaping/data/in-house/meson.build
index 0bd1a9d..474a789 100644
--- a/test/shaping/data/in-house/meson.build
+++ b/test/shaping/data/in-house/meson.build
@@ -14,6 +14,7 @@
   'context-matching.tests',
   'cursive-positioning.tests',
   'default-ignorables.tests',
+  'digits.tests',
   'emoji.tests',
   'fallback-positioning.tests',
   'hangul-jamo.tests',
diff --git a/test/shaping/data/in-house/tests/digits.tests b/test/shaping/data/in-house/tests/digits.tests
new file mode 100644
index 0000000..75f873b
--- /dev/null
+++ b/test/shaping/data/in-house/tests/digits.tests
@@ -0,0 +1,5 @@
+../fonts/a6b17da98b9f1565ba428719777bbf94a66403c1.ttf:--direction=ltr --script=arab:U+06DD,U+0661,U+0662,U+0663:[uni06DD=0+1279|uni0661.small=1@-1079,0+0|uni0662.small=2@-786,0+0|uni0663.small=3@-493,0+0]
+../fonts/e5ff44940364c2247abed50bdda30d2ef5aedfe4.ttf:--direction=ltr --script=arab --features=pnum:U+0661,U+0662,U+0668,U+0663,U+0667:[uni0661.prop=0+361|uni0662.prop=1+436|uni0668.prop=2+478|uni0663.prop=3+597|uni0667.prop=4+527]
+../fonts/b082211be29a3e2cf91f0fd43497e40b2a27b344.ttf:--direction=ltr --script=arab:U+06DD,U+0661,U+0662,U+0628:[uni06DD=0+1279|uni0661=1+585|uni0662=2+585|uni0628=3+926]
+../fonts/3b791518a9ba89675df02f1eefbc9026a50648a6.ttf:--direction=ltr --script=arab:U+06DD,U+0661,U+0662,U+0663:[AyahEnd.alt3=0+1724|OneArabic.encl=0@-1143,444+0|TwoArabic.encl=0@-864,444+0|ThreeArabic.encl=0@-584,444+0]
+../fonts/3b791518a9ba89675df02f1eefbc9026a50648a6.ttf:--direction=rtl --script=arab:U+06DD,U+0661,U+0662,U+0663:[ThreeArabic.encl=0@1140,444+0|TwoArabic.encl=0@860,444+0|OneArabic.encl=0@581,444+0|AyahEnd.alt3=0+1724]