Upgrade to latest harfbuzz
[framework/uifw/harfbuzz.git] / src / hb-ot-shape-complex-arabic.cc
index 942edc7..54460f0 100644 (file)
  */
 
 #include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /* buffer var allocations */
-#define arabic_shaping_action() complex_var_temporary_u16() /* arabic shaping action */
+#define arabic_shaping_action() complex_var_temporary_u8() /* arabic shaping action */
 
 
 /*
@@ -59,23 +59,21 @@ enum {
 
 static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
 {
-  /* TODO Macroize the magic bit operations */
-
-  if (likely (hb_codepoint_in_range (u, JOINING_TABLE_FIRST, JOINING_TABLE_LAST))) {
+  if (likely (hb_in_range<hb_codepoint_t> (u, JOINING_TABLE_FIRST, JOINING_TABLE_LAST))) {
     unsigned int j_type = joining_table[u - JOINING_TABLE_FIRST];
     if (likely (j_type != JOINING_TYPE_X))
       return j_type;
   }
 
   /* Mongolian joining data is not in ArabicJoining.txt yet */
-  if (unlikely (hb_codepoint_in_range (u, 0x1800, 0x18AF)))
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1800, 0x18AF)))
   {
     /* All letters, SIBE SYLLABLE BOUNDARY MARKER, and NIRUGU are D */
     if (gen_cat == HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER || u == 0x1807 || u == 0x180A)
       return JOINING_TYPE_D;
   }
 
-  if (unlikely (hb_codepoint_in_range (u, 0x200C, 0x200D))) {
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x200C, 0x200D))) {
     return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C;
   }
 
@@ -83,7 +81,23 @@ 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_syriac_features[] =
 {
@@ -151,7 +165,8 @@ static const struct arabic_state_table_entry {
 
 
 void
-_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
+_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map,
+                                             const hb_segment_properties_t *props)
 {
   /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together,
    * then rlig and calt each in their own stage.  This makes IranNastaliq's ALLAH
@@ -164,6 +179,7 @@ _hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb
    */
 
   map->add_bool_feature (HB_TAG('c','c','m','p'));
+  map->add_bool_feature (HB_TAG('l','o','c','l'));
 
   map->add_gsub_pause (NULL, NULL);
 
@@ -183,14 +199,51 @@ _hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb
   map->add_bool_feature (HB_TAG('c','s','w','h'));
 }
 
-bool
-_hb_ot_shape_complex_prefer_decomposed_arabic (void)
+hb_ot_shape_normalization_mode_t
+_hb_ot_shape_complex_normalization_preference_arabic (void)
 {
-  return FALSE;
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
+}
+
+
+static void
+arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer)
+{
+  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 && hb_font_get_glyph (font, shaped, 0, &glyph))
+      buffer->info[i].codepoint = shaped;
+  }
+
+  /* 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) || !(hb_font_get_glyph (font, 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 ();
 }
 
 void
-_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer)
+_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map,
+                                        hb_buffer_t *buffer,
+                                        hb_font_t *font)
 {
   unsigned int count = buffer->len;
   unsigned int prev = 0, state = 0;
@@ -199,7 +252,7 @@ _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer)
 
   for (unsigned int i = 0; i < count; i++)
   {
-    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, (hb_unicode_general_category_t) buffer->info[i].general_category());
+    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[i]));
 
     if (unlikely (this_type == JOINING_TYPE_T)) {
       buffer->info[i].arabic_shaping_action() = NONE;
@@ -218,15 +271,29 @@ _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer)
   }
 
   hb_mask_t mask_array[TOTAL_NUM_FEATURES + 1] = {0};
+  hb_mask_t total_masks = 0;
   unsigned int num_masks = buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
-  for (unsigned int i = 0; i < num_masks; i++)
+  for (unsigned int i = 0; i < num_masks; i++) {
     mask_array[i] = map->get_1_mask (arabic_syriac_features[i]);
+    total_masks |= mask_array[i];
+  }
 
-  for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()];
+  if (total_masks) {
+    /* Has OpenType tables */
+    for (unsigned int i = 0; i < count; i++)
+      buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()];
+  } else if (buffer->props.script == HB_SCRIPT_ARABIC) {
+    /* Fallback Arabic shaping to Presentation Forms */
+    /* 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_fallback_shape (font, buffer);
+  }
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
 
-HB_END_DECLS