Imported Upstream version 0.9.12
[platform/upstream/harfbuzz.git] / src / hb-ot-shape-complex-arabic.cc
index 857bf55..35356fe 100644 (file)
@@ -28,7 +28,6 @@
 #include "hb-ot-shape-private.hh"
 
 
-
 /* buffer var allocations */
 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
 
@@ -65,15 +64,31 @@ static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_categ
       return j_type;
   }
 
-  /* Mongolian joining data is not in ArabicJoining.txt yet */
+  /* Mongolian joining data is not in ArabicJoining.txt yet. */
   if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1800, 0x18AF)))
   {
+    if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1880, 0x1886)))
+      return JOINING_TYPE_U;
+
     /* All letters, SIBE SYLLABLE BOUNDARY MARKER, and NIRUGU are D */
-    if (gen_cat == HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER || u == 0x1807 || u == 0x180A)
+    if ((FLAG(gen_cat) & (FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) |
+                         FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER)))
+       || u == 0x1807 || u == 0x180A)
+      return JOINING_TYPE_D;
+  }
+
+  /* 'Phags-pa joining data is not in ArabicJoining.txt yet. */
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xA840, 0xA872)))
+  {
+      if (unlikely (u == 0xA872))
+       /* XXX Looks like this should be TYPE_L, but we don't support that yet! */
+       return JOINING_TYPE_R;
+
       return JOINING_TYPE_D;
   }
 
-  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x200C, 0x200D))) {
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x200C, 0x200D)))
+  {
     return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C;
   }
 
@@ -81,24 +96,6 @@ static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_categ
         JOINING_TYPE_T : JOINING_TYPE_U;
 }
 
-static hb_codepoint_t get_arabic_shape (hb_codepoint_t u, unsigned int shape)
-{
-  if (likely (hb_in_range<hb_codepoint_t> (u, SHAPING_TABLE_FIRST, SHAPING_TABLE_LAST)) && shape < 4)
-    return shaping_table[u - SHAPING_TABLE_FIRST][shape];
-  return u;
-}
-
-static uint16_t get_ligature (hb_codepoint_t first, hb_codepoint_t second)
-{
-  if (unlikely (!second)) return 0;
-  for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++)
-    if (ligature_table[i].first == first)
-      for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++)
-       if (ligature_table[i].ligatures[j].second == second)
-         return ligature_table[i].ligatures[j].ligature;
-  return 0;
-}
-
 static const hb_tag_t arabic_features[] =
 {
   HB_TAG('i','n','i','t'),
@@ -161,6 +158,10 @@ static const struct arabic_state_table_entry {
 };
 
 
+static void
+arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
+                      hb_font_t *font,
+                      hb_buffer_t *buffer);
 
 static void
 collect_features_arabic (hb_ot_shape_planner_t *plan)
@@ -183,30 +184,35 @@ collect_features_arabic (hb_ot_shape_planner_t *plan)
   map->add_gsub_pause (NULL);
 
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++)
-    map->add_bool_feature (arabic_features[i], false);
+    map->add_bool_feature (arabic_features[i], false, i < 4); /* The first four features have fallback. */
 
   map->add_gsub_pause (NULL);
 
-  map->add_bool_feature (HB_TAG('r','l','i','g'));
-  map->add_gsub_pause (NULL);
+  map->add_bool_feature (HB_TAG('r','l','i','g'), true, true);
+  map->add_gsub_pause (arabic_fallback_shape);
 
   map->add_bool_feature (HB_TAG('c','a','l','t'));
   map->add_gsub_pause (NULL);
 
-  /* ArabicOT spec enables 'cswh' for Arabic where as for basic shaper it's disabled by default. */
   map->add_bool_feature (HB_TAG('c','s','w','h'));
+  map->add_bool_feature (HB_TAG('d','l','i','g'));
+  map->add_bool_feature (HB_TAG('m','s','e','t'));
 }
 
+#include "hb-ot-shape-complex-arabic-fallback.hh"
+
 struct arabic_shape_plan_t
 {
   ASSERT_POD ();
 
-  bool do_fallback;
   /* The "+ 1" in the next array is to accommodate for the "NONE" command,
    * which is not an OpenType feature, but this simplifies the code by not
    * having to do a "if (... < NONE) ..." and just rely on the fact that
    * mask_array[NONE] == 0. */
   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
+
+  bool do_fallback;
+  arabic_fallback_plan_t *fallback_plan;
 };
 
 static void *
@@ -216,75 +222,48 @@ data_create_arabic (const hb_ot_shape_plan_t *plan)
   if (unlikely (!arabic_plan))
     return NULL;
 
-  hb_mask_t total_masks = 0;
+  arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC;
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) {
     arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]);
-    total_masks |= arabic_plan->mask_array[i];
+    if (i < 4)
+      arabic_plan->do_fallback = arabic_plan->do_fallback && plan->map.needs_fallback (arabic_features[i]);
   }
 
-  /* Pitfalls:
-   * - This path fires if user force-set init/medi/fina/isol off,
-   * - If font does not declare script 'arab', well, what to do?
-   *   Most probably it's safe to assume that init/medi/fina/isol
-   *   still mean Arabic shaping, although they do not have to.
-   */
-  arabic_plan->do_fallback = 0 == total_masks;
-
   return arabic_plan;
 }
 
 static void
 data_destroy_arabic (void *data)
 {
-  free (data);
-}
+  arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
 
-static void
-arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer)
-{
-  /* Only Arabic has presentation forms encoded in Unicode. */
-  if (buffer->props.script != HB_SCRIPT_ARABIC)
-    return;
-
-  unsigned int count = buffer->len;
-  hb_codepoint_t glyph;
-
-  /* Shape to presentation forms */
-  for (unsigned int i = 0; i < count; i++) {
-    hb_codepoint_t u = buffer->info[i].codepoint;
-    hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action());
-    if (shaped != u && font->get_glyph (shaped, 0, &glyph))
-      buffer->info[i].codepoint = shaped;
-  }
+  arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
 
-  /* Mandatory ligatures */
-  buffer->clear_output ();
-  for (buffer->idx = 0; buffer->idx + 1 < count;) {
-    hb_codepoint_t ligature = get_ligature (buffer->cur().codepoint,
-                                           buffer->cur(+1).codepoint);
-    if (likely (!ligature) || !(font->get_glyph (ligature, 0, &glyph))) {
-      buffer->next_glyph ();
-      continue;
-    }
-
-    buffer->replace_glyphs (2, 1, &ligature);
-
-    /* Technically speaking we can skip marks and stuff, like the GSUB path does.
-     * But who cares, we're in fallback! */
-  }
-  for (; buffer->idx < count;)
-      buffer->next_glyph ();
-  buffer->swap_buffers ();
+  free (data);
 }
 
 static void
 arabic_joining (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
-  unsigned int prev = 0, state = 0;
+  unsigned int prev = (unsigned int) -1, state = 0;
 
   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action);
 
+  /* Check pre-context */
+  if (!(buffer->flags & HB_BUFFER_FLAG_BOT))
+    for (unsigned int i = 0; i < buffer->context_len[0]; i++)
+    {
+      unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i]));
+
+      if (unlikely (this_type == JOINING_TYPE_T))
+       continue;
+
+      const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+      state = entry->next_state;
+      break;
+    }
+
   for (unsigned int i = 0; i < count; i++)
   {
     unsigned int this_type = get_joining_type (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[i]));
@@ -296,7 +275,7 @@ arabic_joining (hb_buffer_t *buffer)
 
     const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
 
-    if (entry->prev_action != NONE)
+    if (entry->prev_action != NONE && prev != (unsigned int) -1)
       buffer->info[prev].arabic_shaping_action() = entry->prev_action;
 
     buffer->info[i].arabic_shaping_action() = entry->curr_action;
@@ -305,39 +284,64 @@ arabic_joining (hb_buffer_t *buffer)
     state = entry->next_state;
   }
 
+  if (!(buffer->flags & HB_BUFFER_FLAG_EOT))
+    for (unsigned int i = 0; i < buffer->context_len[1]; i++)
+    {
+      unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[1][i]));
+
+      if (unlikely (this_type == JOINING_TYPE_T))
+       continue;
+
+      const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+      if (entry->prev_action != NONE && prev != (unsigned int) -1)
+       buffer->info[prev].arabic_shaping_action() = entry->prev_action;
+      break;
+    }
+
+
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
 static void
-preprocess_text_arabic (const hb_ot_shape_plan_t *plan,
-                       hb_buffer_t              *buffer,
-                       hb_font_t                *font)
+setup_masks_arabic (const hb_ot_shape_plan_t *plan,
+                   hb_buffer_t              *buffer,
+                   hb_font_t                *font HB_UNUSED)
 {
   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
 
-  if (unlikely (arabic_plan->do_fallback))
-  {
-    arabic_joining (buffer);
-    arabic_fallback_shape (font, buffer);
-  }
+  arabic_joining (buffer);
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()];
 }
 
+
 static void
-setup_masks_arabic (const hb_ot_shape_plan_t *plan,
-                   hb_buffer_t              *buffer,
-                   hb_font_t                *font)
+arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
+                      hb_font_t *font,
+                      hb_buffer_t *buffer)
 {
   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
 
-  if (likely (!arabic_plan->do_fallback))
+  if (!arabic_plan->do_fallback)
+    return;
+
+retry:
+  arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_atomic_ptr_get (&arabic_plan->fallback_plan);
+  if (unlikely (!fallback_plan))
   {
-    arabic_joining (buffer);
-    unsigned int count = buffer->len;
-    for (unsigned int i = 0; i < count; i++)
-      buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()];
+    /* This sucks.  We need a font to build the fallback plan... */
+    fallback_plan = arabic_fallback_plan_create (plan, font);
+    if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (arabic_plan))->fallback_plan, NULL, fallback_plan))) {
+      arabic_fallback_plan_destroy (fallback_plan);
+      goto retry;
+    }
   }
+
+  arabic_fallback_plan_shape (fallback_plan, font, buffer);
 }
 
+
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
 {
   "arabic",
@@ -345,8 +349,11 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
   NULL, /* override_features */
   data_create_arabic,
   data_destroy_arabic,
-  preprocess_text_arabic,
+  NULL, /* preprocess_text_arabic */
   NULL, /* normalization_preference */
+  NULL, /* decompose */
+  NULL, /* compose */
   setup_masks_arabic,
   true, /* zero_width_attached_marks */
+  true, /* fallback_position */
 };