Blame src/hb-ot-shape-complex-arabic.cc

Packit Service 5bcba8
/*
Packit Service 5bcba8
 * Copyright © 2010,2012  Google, Inc.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 *  This is part of HarfBuzz, a text shaping library.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Permission is hereby granted, without written agreement and without
Packit Service 5bcba8
 * license or royalty fees, to use, copy, modify, and distribute this
Packit Service 5bcba8
 * software and its documentation for any purpose, provided that the
Packit Service 5bcba8
 * above copyright notice and the following two paragraphs appear in
Packit Service 5bcba8
 * all copies of this software.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
Packit Service 5bcba8
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
Packit Service 5bcba8
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
Packit Service 5bcba8
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
Packit Service 5bcba8
 * DAMAGE.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
Packit Service 5bcba8
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
Packit Service 5bcba8
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
Packit Service 5bcba8
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
Packit Service 5bcba8
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Google Author(s): Behdad Esfahbod
Packit Service 5bcba8
 */
Packit Service 5bcba8
Packit Service 5bcba8
#include "hb-ot-shape-complex-arabic-private.hh"
Packit Service 5bcba8
#include "hb-ot-shape-private.hh"
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
#ifndef HB_DEBUG_ARABIC
Packit Service 5bcba8
#define HB_DEBUG_ARABIC (HB_DEBUG+0)
Packit Service 5bcba8
#endif
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* buffer var allocations */
Packit Service 5bcba8
#define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
Packit Service 5bcba8
Packit Service 5bcba8
#define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0
Packit Service 5bcba8
Packit Service 5bcba8
/* See:
Packit Service 5bcba8
 * https://github.com/behdad/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */
Packit Service 5bcba8
#define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \
Packit Service 5bcba8
	(FLAG_SAFE (gen_cat) & \
Packit Service 5bcba8
	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \
Packit Service 5bcba8
	  /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \
Packit Service 5bcba8
	  /*FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |*/ \
Packit Service 5bcba8
	  /*FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |*/ \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL) | \
Packit Service 5bcba8
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL)))
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/*
Packit Service 5bcba8
 * Joining types:
Packit Service 5bcba8
 */
Packit Service 5bcba8
Packit Service 5bcba8
/*
Packit Service 5bcba8
 * Bits used in the joining tables
Packit Service 5bcba8
 */
Packit Service 5bcba8
enum hb_arabic_joining_type_t {
Packit Service 5bcba8
  JOINING_TYPE_U		= 0,
Packit Service 5bcba8
  JOINING_TYPE_L		= 1,
Packit Service 5bcba8
  JOINING_TYPE_R		= 2,
Packit Service 5bcba8
  JOINING_TYPE_D		= 3,
Packit Service 5bcba8
  JOINING_TYPE_C		= JOINING_TYPE_D,
Packit Service 5bcba8
  JOINING_GROUP_ALAPH		= 4,
Packit Service 5bcba8
  JOINING_GROUP_DALATH_RISH	= 5,
Packit Service 5bcba8
  NUM_STATE_MACHINE_COLS	= 6,
Packit Service 5bcba8
Packit Service 5bcba8
  JOINING_TYPE_T = 7,
Packit Service 5bcba8
  JOINING_TYPE_X = 8  /* means: use general-category to choose between U or T. */
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
#include "hb-ot-shape-complex-arabic-table.hh"
Packit Service 5bcba8
Packit Service 5bcba8
static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
Packit Service 5bcba8
{
Packit Service 5bcba8
  unsigned int j_type = joining_type(u);
Packit Service 5bcba8
  if (likely (j_type != JOINING_TYPE_X))
Packit Service 5bcba8
    return j_type;
Packit Service 5bcba8
Packit Service 5bcba8
  return (FLAG_SAFE(gen_cat) &
Packit Service 5bcba8
	  (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) |
Packit Service 5bcba8
	   FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
Packit Service 5bcba8
	   FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT))
Packit Service 5bcba8
	 ) ?  JOINING_TYPE_T : JOINING_TYPE_U;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
#define FEATURE_IS_SYRIAC(tag) hb_in_range<unsigned char> ((unsigned char) (tag), '2', '3')
Packit Service 5bcba8
Packit Service 5bcba8
static const hb_tag_t arabic_features[] =
Packit Service 5bcba8
{
Packit Service 5bcba8
  HB_TAG('i','s','o','l'),
Packit Service 5bcba8
  HB_TAG('f','i','n','a'),
Packit Service 5bcba8
  HB_TAG('f','i','n','2'),
Packit Service 5bcba8
  HB_TAG('f','i','n','3'),
Packit Service 5bcba8
  HB_TAG('m','e','d','i'),
Packit Service 5bcba8
  HB_TAG('m','e','d','2'),
Packit Service 5bcba8
  HB_TAG('i','n','i','t'),
Packit Service 5bcba8
  HB_TAG_NONE
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* Same order as the feature array */
Packit Service 5bcba8
enum arabic_action_t {
Packit Service 5bcba8
  ISOL,
Packit Service 5bcba8
  FINA,
Packit Service 5bcba8
  FIN2,
Packit Service 5bcba8
  FIN3,
Packit Service 5bcba8
  MEDI,
Packit Service 5bcba8
  MED2,
Packit Service 5bcba8
  INIT,
Packit Service 5bcba8
Packit Service 5bcba8
  NONE,
Packit Service 5bcba8
Packit Service 5bcba8
  ARABIC_NUM_FEATURES = NONE,
Packit Service 5bcba8
Packit Service 5bcba8
  /* We abuse the same byte for other things... */
Packit Service 5bcba8
  STCH_FIXED,
Packit Service 5bcba8
  STCH_REPEATING,
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
static const struct arabic_state_table_entry {
Packit Service 5bcba8
	uint8_t prev_action;
Packit Service 5bcba8
	uint8_t curr_action;
Packit Service 5bcba8
	uint16_t next_state;
Packit Service 5bcba8
} arabic_state_table[][NUM_STATE_MACHINE_COLS] =
Packit Service 5bcba8
{
Packit Service 5bcba8
  /*   jt_U,          jt_L,          jt_R,          jt_D,          jg_ALAPH,      jg_DALATH_RISH */
Packit Service 5bcba8
Packit Service 5bcba8
  /* State 0: prev was U, not willing to join. */
Packit Service 5bcba8
  { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, },
Packit Service 5bcba8
Packit Service 5bcba8
  /* State 1: prev was R or ISOL/ALAPH, not willing to join. */
Packit Service 5bcba8
  { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, },
Packit Service 5bcba8
Packit Service 5bcba8
  /* State 2: prev was D/L in ISOL form, willing to join. */
Packit Service 5bcba8
  { {NONE,NONE,0}, {NONE,ISOL,2}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, },
Packit Service 5bcba8
Packit Service 5bcba8
  /* State 3: prev was D in FINA form, willing to join. */
Packit Service 5bcba8
  { {NONE,NONE,0}, {NONE,ISOL,2}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, },
Packit Service 5bcba8
Packit Service 5bcba8
  /* State 4: prev was FINA ALAPH, not willing to join. */
Packit Service 5bcba8
  { {NONE,NONE,0}, {NONE,ISOL,2}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, },
Packit Service 5bcba8
Packit Service 5bcba8
  /* State 5: prev was FIN2/FIN3 ALAPH, not willing to join. */
Packit Service 5bcba8
  { {NONE,NONE,0}, {NONE,ISOL,2}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, },
Packit Service 5bcba8
Packit Service 5bcba8
  /* State 6: prev was DALATH/RISH, not willing to join. */
Packit Service 5bcba8
  { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, }
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
nuke_joiners (const hb_ot_shape_plan_t *plan,
Packit Service 5bcba8
	      hb_font_t *font,
Packit Service 5bcba8
	      hb_buffer_t *buffer);
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
Packit Service 5bcba8
		       hb_font_t *font,
Packit Service 5bcba8
		       hb_buffer_t *buffer);
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
record_stch (const hb_ot_shape_plan_t *plan,
Packit Service 5bcba8
	     hb_font_t *font,
Packit Service 5bcba8
	     hb_buffer_t *buffer);
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
collect_features_arabic (hb_ot_shape_planner_t *plan)
Packit Service 5bcba8
{
Packit Service 5bcba8
  hb_ot_map_builder_t *map = &plan->map;
Packit Service 5bcba8
Packit Service 5bcba8
  /* We apply features according to the Arabic spec, with pauses
Packit Service 5bcba8
   * in between most.
Packit Service 5bcba8
   *
Packit Service 5bcba8
   * The pause between init/medi/... and rlig is required.  See eg:
Packit Service 5bcba8
   * https://bugzilla.mozilla.org/show_bug.cgi?id=644184
Packit Service 5bcba8
   *
Packit Service 5bcba8
   * The pauses between init/medi/... themselves are not necessarily
Packit Service 5bcba8
   * needed as only one of those features is applied to any character.
Packit Service 5bcba8
   * The only difference it makes is when fonts have contextual
Packit Service 5bcba8
   * substitutions.  We now follow the order of the spec, which makes
Packit Service 5bcba8
   * for better experience if that's what Uniscribe is doing.
Packit Service 5bcba8
   *
Packit Service 5bcba8
   * At least for Arabic, looks like Uniscribe has a pause between
Packit Service 5bcba8
   * rlig and calt.  Otherwise the IranNastaliq's ALLAH ligature won't
Packit Service 5bcba8
   * work.  However, testing shows that rlig and calt are applied
Packit Service 5bcba8
   * together for Mongolian in Uniscribe.  As such, we only add a
Packit Service 5bcba8
   * pause for Arabic, not other scripts.
Packit Service 5bcba8
   *
Packit Service 5bcba8
   * A pause after calt is required to make KFGQPC Uthmanic Script HAFS
Packit Service 5bcba8
   * work correctly.  See https://github.com/behdad/harfbuzz/issues/505
Packit Service 5bcba8
   */
Packit Service 5bcba8
Packit Service 5bcba8
  map->add_gsub_pause (nuke_joiners);
Packit Service 5bcba8
Packit Service 5bcba8
  map->add_global_bool_feature (HB_TAG('s','t','c','h'));
Packit Service 5bcba8
  map->add_gsub_pause (record_stch);
Packit Service 5bcba8
Packit Service 5bcba8
  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
Packit Service 5bcba8
  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
Packit Service 5bcba8
Packit Service 5bcba8
  map->add_gsub_pause (NULL);
Packit Service 5bcba8
Packit Service 5bcba8
  for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SYRIAC (arabic_features[i]);
Packit Service 5bcba8
    map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_NONE);
Packit Service 5bcba8
    map->add_gsub_pause (NULL);
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK);
Packit Service 5bcba8
  if (plan->props.script == HB_SCRIPT_ARABIC)
Packit Service 5bcba8
    map->add_gsub_pause (arabic_fallback_shape);
Packit Service 5bcba8
Packit Service 5bcba8
  /* No pause after rclt.  See 98460779bae19e4d64d29461ff154b3527bf8420. */
Packit Service 5bcba8
  map->add_global_bool_feature (HB_TAG('r','c','l','t'));
Packit Service 5bcba8
  map->add_global_bool_feature (HB_TAG('c','a','l','t'));
Packit Service 5bcba8
  map->add_gsub_pause (NULL);
Packit Service 5bcba8
Packit Service 5bcba8
  /* The spec includes 'cswh'.  Earlier versions of Windows
Packit Service 5bcba8
   * used to enable this by default, but testing suggests
Packit Service 5bcba8
   * that Windows 8 and later do not enable it by default,
Packit Service 5bcba8
   * and spec now says 'Off by default'.
Packit Service 5bcba8
   * We disabled this in ae23c24c32.
Packit Service 5bcba8
   * Note that IranNastaliq uses this feature extensively
Packit Service 5bcba8
   * to fixup broken glyph sequences.  Oh well...
Packit Service 5bcba8
   * Test case: U+0643,U+0640,U+0631. */
Packit Service 5bcba8
  //map->add_global_bool_feature (HB_TAG('c','s','w','h'));
Packit Service 5bcba8
  map->add_global_bool_feature (HB_TAG('m','s','e','t'));
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
#include "hb-ot-shape-complex-arabic-fallback.hh"
Packit Service 5bcba8
Packit Service 5bcba8
struct arabic_shape_plan_t
Packit Service 5bcba8
{
Packit Service 5bcba8
  ASSERT_POD ();
Packit Service 5bcba8
Packit Service 5bcba8
  /* The "+ 1" in the next array is to accommodate for the "NONE" command,
Packit Service 5bcba8
   * which is not an OpenType feature, but this simplifies the code by not
Packit Service 5bcba8
   * having to do a "if (... < NONE) ..." and just rely on the fact that
Packit Service 5bcba8
   * mask_array[NONE] == 0. */
Packit Service 5bcba8
  hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
Packit Service 5bcba8
Packit Service 5bcba8
  arabic_fallback_plan_t *fallback_plan;
Packit Service 5bcba8
Packit Service 5bcba8
  unsigned int do_fallback : 1;
Packit Service 5bcba8
  unsigned int has_stch : 1;
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
void *
Packit Service 5bcba8
data_create_arabic (const hb_ot_shape_plan_t *plan)
Packit Service 5bcba8
{
Packit Service 5bcba8
  arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (arabic_shape_plan_t));
Packit Service 5bcba8
  if (unlikely (!arabic_plan))
Packit Service 5bcba8
    return NULL;
Packit Service 5bcba8
Packit Service 5bcba8
  arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC;
Packit Service 5bcba8
  arabic_plan->has_stch = !!plan->map.get_1_mask (HB_TAG ('s','t','c','h'));
Packit Service 5bcba8
  for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) {
Packit Service 5bcba8
    arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]);
Packit Service 5bcba8
    arabic_plan->do_fallback = arabic_plan->do_fallback &&
Packit Service 5bcba8
			       (FEATURE_IS_SYRIAC (arabic_features[i]) ||
Packit Service 5bcba8
			        plan->map.needs_fallback (arabic_features[i]));
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  return arabic_plan;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
void
Packit Service 5bcba8
data_destroy_arabic (void *data)
Packit Service 5bcba8
{
Packit Service 5bcba8
  arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
Packit Service 5bcba8
Packit Service 5bcba8
  arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
Packit Service 5bcba8
Packit Service 5bcba8
  free (data);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
arabic_joining (hb_buffer_t *buffer)
Packit Service 5bcba8
{
Packit Service 5bcba8
  unsigned int count = buffer->len;
Packit Service 5bcba8
  hb_glyph_info_t *info = buffer->info;
Packit Service 5bcba8
  unsigned int prev = (unsigned int) -1, state = 0;
Packit Service 5bcba8
Packit Service 5bcba8
  /* Check pre-context */
Packit Service 5bcba8
  for (unsigned int i = 0; i < buffer->context_len[0]; i++)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i]));
Packit Service 5bcba8
Packit Service 5bcba8
    if (unlikely (this_type == JOINING_TYPE_T))
Packit Service 5bcba8
      continue;
Packit Service 5bcba8
Packit Service 5bcba8
    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
Packit Service 5bcba8
    state = entry->next_state;
Packit Service 5bcba8
    break;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  for (unsigned int i = 0; i < count; i++)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    unsigned int this_type = get_joining_type (info[i].codepoint, _hb_glyph_info_get_general_category (&info[i]));
Packit Service 5bcba8
Packit Service 5bcba8
    if (unlikely (this_type == JOINING_TYPE_T)) {
Packit Service 5bcba8
      info[i].arabic_shaping_action() = NONE;
Packit Service 5bcba8
      continue;
Packit Service 5bcba8
    }
Packit Service 5bcba8
Packit Service 5bcba8
    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
Packit Service 5bcba8
Packit Service 5bcba8
    if (entry->prev_action != NONE && prev != (unsigned int) -1)
Packit Service 5bcba8
      info[prev].arabic_shaping_action() = entry->prev_action;
Packit Service 5bcba8
Packit Service 5bcba8
    info[i].arabic_shaping_action() = entry->curr_action;
Packit Service 5bcba8
Packit Service 5bcba8
    prev = i;
Packit Service 5bcba8
    state = entry->next_state;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  for (unsigned int i = 0; i < buffer->context_len[1]; i++)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[1][i]));
Packit Service 5bcba8
Packit Service 5bcba8
    if (unlikely (this_type == JOINING_TYPE_T))
Packit Service 5bcba8
      continue;
Packit Service 5bcba8
Packit Service 5bcba8
    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
Packit Service 5bcba8
    if (entry->prev_action != NONE && prev != (unsigned int) -1)
Packit Service 5bcba8
      info[prev].arabic_shaping_action() = entry->prev_action;
Packit Service 5bcba8
    break;
Packit Service 5bcba8
  }
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
mongolian_variation_selectors (hb_buffer_t *buffer)
Packit Service 5bcba8
{
Packit Service 5bcba8
  /* Copy arabic_shaping_action() from base to Mongolian variation selectors. */
Packit Service 5bcba8
  unsigned int count = buffer->len;
Packit Service 5bcba8
  hb_glyph_info_t *info = buffer->info;
Packit Service 5bcba8
  for (unsigned int i = 1; i < count; i++)
Packit Service 5bcba8
    if (unlikely (hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x180Bu, 0x180Du)))
Packit Service 5bcba8
      info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action();
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
void
Packit Service 5bcba8
setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan,
Packit Service 5bcba8
			 hb_buffer_t               *buffer,
Packit Service 5bcba8
			 hb_script_t                script)
Packit Service 5bcba8
{
Packit Service 5bcba8
  HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action);
Packit Service 5bcba8
Packit Service 5bcba8
  arabic_joining (buffer);
Packit Service 5bcba8
  if (script == HB_SCRIPT_MONGOLIAN)
Packit Service 5bcba8
    mongolian_variation_selectors (buffer);
Packit Service 5bcba8
Packit Service 5bcba8
  unsigned int count = buffer->len;
Packit Service 5bcba8
  hb_glyph_info_t *info = buffer->info;
Packit Service 5bcba8
  for (unsigned int i = 0; i < count; i++)
Packit Service 5bcba8
    info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()];
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
setup_masks_arabic (const hb_ot_shape_plan_t *plan,
Packit Service 5bcba8
		    hb_buffer_t              *buffer,
Packit Service 5bcba8
		    hb_font_t                *font HB_UNUSED)
Packit Service 5bcba8
{
Packit Service 5bcba8
  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
Packit Service 5bcba8
  setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
nuke_joiners (const hb_ot_shape_plan_t *plan HB_UNUSED,
Packit Service 5bcba8
	      hb_font_t *font HB_UNUSED,
Packit Service 5bcba8
	      hb_buffer_t *buffer)
Packit Service 5bcba8
{
Packit Service 5bcba8
  unsigned int count = buffer->len;
Packit Service 5bcba8
  hb_glyph_info_t *info = buffer->info;
Packit Service 5bcba8
  for (unsigned int i = 0; i < count; i++)
Packit Service 5bcba8
    if (_hb_glyph_info_is_zwj (&info[i]))
Packit Service 5bcba8
      _hb_glyph_info_flip_joiners (&info[i]);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
Packit Service 5bcba8
		       hb_font_t *font,
Packit Service 5bcba8
		       hb_buffer_t *buffer)
Packit Service 5bcba8
{
Packit Service 5bcba8
  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
Packit Service 5bcba8
Packit Service 5bcba8
  if (!arabic_plan->do_fallback)
Packit Service 5bcba8
    return;
Packit Service 5bcba8
Packit Service 5bcba8
retry:
Packit Service 5bcba8
  arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_atomic_ptr_get (&arabic_plan->fallback_plan);
Packit Service 5bcba8
  if (unlikely (!fallback_plan))
Packit Service 5bcba8
  {
Packit Service 5bcba8
    /* This sucks.  We need a font to build the fallback plan... */
Packit Service 5bcba8
    fallback_plan = arabic_fallback_plan_create (plan, font);
Packit Service 5bcba8
    if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (arabic_plan))->fallback_plan, NULL, fallback_plan))) {
Packit Service 5bcba8
      arabic_fallback_plan_destroy (fallback_plan);
Packit Service 5bcba8
      goto retry;
Packit Service 5bcba8
    }
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  arabic_fallback_plan_shape (fallback_plan, font, buffer);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/*
Packit Service 5bcba8
 * Stretch feature: "stch".
Packit Service 5bcba8
 * See example here:
Packit Service 5bcba8
 * https://www.microsoft.com/typography/OpenTypeDev/syriac/intro.htm
Packit Service 5bcba8
 * We implement this in a generic way, such that the Arabic subtending
Packit Service 5bcba8
 * marks can use it as well.
Packit Service 5bcba8
 */
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
record_stch (const hb_ot_shape_plan_t *plan,
Packit Service 5bcba8
	     hb_font_t *font,
Packit Service 5bcba8
	     hb_buffer_t *buffer)
Packit Service 5bcba8
{
Packit Service 5bcba8
  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
Packit Service 5bcba8
  if (!arabic_plan->has_stch)
Packit Service 5bcba8
    return;
Packit Service 5bcba8
Packit Service 5bcba8
  /* 'stch' feature was just applied.  Look for anything that multiplied,
Packit Service 5bcba8
   * and record it for stch treatment later.  Note that rtlm, frac, etc
Packit Service 5bcba8
   * are applied before stch, but we assume that they didn't result in
Packit Service 5bcba8
   * anything multiplying into 5 pieces, so it's safe-ish... */
Packit Service 5bcba8
Packit Service 5bcba8
  unsigned int count = buffer->len;
Packit Service 5bcba8
  hb_glyph_info_t *info = buffer->info;
Packit Service 5bcba8
  for (unsigned int i = 0; i < count; i++)
Packit Service 5bcba8
    if (unlikely (_hb_glyph_info_multiplied (&info[i])))
Packit Service 5bcba8
    {
Packit Service 5bcba8
      unsigned int comp = _hb_glyph_info_get_lig_comp (&info[i]);
Packit Service 5bcba8
      info[i].arabic_shaping_action() = comp % 2 ? STCH_REPEATING : STCH_FIXED;
Packit Service 5bcba8
      buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH;
Packit Service 5bcba8
    }
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
apply_stch (const hb_ot_shape_plan_t *plan,
Packit Service 5bcba8
	    hb_buffer_t              *buffer,
Packit Service 5bcba8
	    hb_font_t                *font)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH)))
Packit Service 5bcba8
    return;
Packit Service 5bcba8
Packit Service 5bcba8
  /* The Arabic shaper currently always processes in RTL mode, so we should
Packit Service 5bcba8
   * stretch / position the stretched pieces to the left / preceding glyphs. */
Packit Service 5bcba8
Packit Service 5bcba8
  /* We do a two pass implementation:
Packit Service 5bcba8
   * First pass calculates the exact number of extra glyphs we need,
Packit Service 5bcba8
   * We then enlarge buffer to have that much room,
Packit Service 5bcba8
   * Second pass applies the stretch, copying things to the end of buffer.
Packit Service 5bcba8
   */
Packit Service 5bcba8
Packit Service 5bcba8
  int sign = font->x_scale < 0 ? -1 : +1;
Packit Service 5bcba8
  unsigned int extra_glyphs_needed = 0; // Set during MEASURE, used during CUT
Packit Service 5bcba8
  typedef enum { MEASURE, CUT } step_t;
Packit Service 5bcba8
Packit Service 5bcba8
  for (step_t step = MEASURE; step <= CUT; step = (step_t) (step + 1))
Packit Service 5bcba8
  {
Packit Service 5bcba8
    unsigned int count = buffer->len;
Packit Service 5bcba8
    hb_glyph_info_t *info = buffer->info;
Packit Service 5bcba8
    hb_glyph_position_t *pos = buffer->pos;
Packit Service 5bcba8
    unsigned int new_len = count + extra_glyphs_needed; // write head during CUT
Packit Service 5bcba8
    unsigned int j = new_len;
Packit Service 5bcba8
    for (unsigned int i = count; i; i--)
Packit Service 5bcba8
    {
Packit Service 5bcba8
      if (!hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING))
Packit Service 5bcba8
      {
Packit Service 5bcba8
        if (step == CUT)
Packit Service 5bcba8
	{
Packit Service 5bcba8
	  --j;
Packit Service 5bcba8
	  info[j] = info[i - 1];
Packit Service 5bcba8
	  pos[j] = pos[i - 1];
Packit Service 5bcba8
	}
Packit Service 5bcba8
        continue;
Packit Service 5bcba8
      }
Packit Service 5bcba8
Packit Service 5bcba8
      /* Yay, justification! */
Packit Service 5bcba8
Packit Service 5bcba8
      hb_position_t w_total = 0; // Total to be filled
Packit Service 5bcba8
      hb_position_t w_fixed = 0; // Sum of fixed tiles
Packit Service 5bcba8
      hb_position_t w_repeating = 0; // Sum of repeating tiles
Packit Service 5bcba8
      int n_fixed = 0;
Packit Service 5bcba8
      int n_repeating = 0;
Packit Service 5bcba8
Packit Service 5bcba8
      unsigned int end = i;
Packit Service 5bcba8
      while (i &&
Packit Service 5bcba8
	     hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING))
Packit Service 5bcba8
      {
Packit Service 5bcba8
	i--;
Packit Service 5bcba8
	hb_position_t width = font->get_glyph_h_advance (info[i].codepoint);
Packit Service 5bcba8
	if (info[i].arabic_shaping_action() == STCH_FIXED)
Packit Service 5bcba8
	{
Packit Service 5bcba8
	  w_fixed += width;
Packit Service 5bcba8
	  n_fixed++;
Packit Service 5bcba8
	}
Packit Service 5bcba8
	else
Packit Service 5bcba8
	{
Packit Service 5bcba8
	  w_repeating += width;
Packit Service 5bcba8
	  n_repeating++;
Packit Service 5bcba8
	}
Packit Service 5bcba8
      }
Packit Service 5bcba8
      unsigned int start = i;
Packit Service 5bcba8
      unsigned int context = i;
Packit Service 5bcba8
      while (context &&
Packit Service 5bcba8
	     !hb_in_range<unsigned> (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) &&
Packit Service 5bcba8
	     (_hb_glyph_info_is_default_ignorable (&info[context - 1]) ||
Packit Service 5bcba8
	      HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_category (&info[context - 1]))))
Packit Service 5bcba8
      {
Packit Service 5bcba8
	context--;
Packit Service 5bcba8
	w_total += pos[context].x_advance;
Packit Service 5bcba8
      }
Packit Service 5bcba8
      i++; // Don't touch i again.
Packit Service 5bcba8
Packit Service 5bcba8
      DEBUG_MSG (ARABIC, NULL, "%s stretch at (%d,%d,%d)",
Packit Service 5bcba8
		 step == MEASURE ? "measuring" : "cutting", context, start, end);
Packit Service 5bcba8
      DEBUG_MSG (ARABIC, NULL, "rest of word:    count=%d width %d", start - context, w_total);
Packit Service 5bcba8
      DEBUG_MSG (ARABIC, NULL, "fixed tiles:     count=%d width=%d", n_fixed, w_fixed);
Packit Service 5bcba8
      DEBUG_MSG (ARABIC, NULL, "repeating tiles: count=%d width=%d", n_repeating, w_repeating);
Packit Service 5bcba8
Packit Service 5bcba8
      /* Number of additional times to repeat each repeating tile. */
Packit Service 5bcba8
      int n_copies = 0;
Packit Service 5bcba8
Packit Service 5bcba8
      hb_position_t w_remaining = w_total - w_fixed;
Packit Service 5bcba8
      if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0)
Packit Service 5bcba8
	n_copies = (sign * w_remaining) / (sign * w_repeating) - 1;
Packit Service 5bcba8
Packit Service 5bcba8
      /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */
Packit Service 5bcba8
      hb_position_t extra_repeat_overlap = 0;
Packit Service 5bcba8
      hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1);
Packit Service 5bcba8
      if (shortfall > 0 && n_repeating > 0)
Packit Service 5bcba8
      {
Packit Service 5bcba8
        ++n_copies;
Packit Service 5bcba8
        hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining;
Packit Service 5bcba8
        if (excess > 0)
Packit Service 5bcba8
          extra_repeat_overlap = excess / (n_copies * n_repeating);
Packit Service 5bcba8
      }
Packit Service 5bcba8
Packit Service 5bcba8
      if (step == MEASURE)
Packit Service 5bcba8
      {
Packit Service 5bcba8
	extra_glyphs_needed += n_copies * n_repeating;
Packit Service 5bcba8
	DEBUG_MSG (ARABIC, NULL, "will add extra %d copies of repeating tiles", n_copies);
Packit Service 5bcba8
      }
Packit Service 5bcba8
      else
Packit Service 5bcba8
      {
Packit Service 5bcba8
	hb_position_t x_offset = 0;
Packit Service 5bcba8
	for (unsigned int k = end; k > start; k--)
Packit Service 5bcba8
	{
Packit Service 5bcba8
	  hb_position_t width = font->get_glyph_h_advance (info[k - 1].codepoint);
Packit Service 5bcba8
Packit Service 5bcba8
	  unsigned int repeat = 1;
Packit Service 5bcba8
	  if (info[k - 1].arabic_shaping_action() == STCH_REPEATING)
Packit Service 5bcba8
	    repeat += n_copies;
Packit Service 5bcba8
Packit Service 5bcba8
	  DEBUG_MSG (ARABIC, NULL, "appending %d copies of glyph %d; j=%d",
Packit Service 5bcba8
		     repeat, info[k - 1].codepoint, j);
Packit Service 5bcba8
	  for (unsigned int n = 0; n < repeat; n++)
Packit Service 5bcba8
	  {
Packit Service 5bcba8
	    x_offset -= width;
Packit Service 5bcba8
	    if (n > 0)
Packit Service 5bcba8
	      x_offset += extra_repeat_overlap;
Packit Service 5bcba8
	    pos[k - 1].x_offset = x_offset;
Packit Service 5bcba8
	    /* Append copy. */
Packit Service 5bcba8
	    --j;
Packit Service 5bcba8
	    info[j] = info[k - 1];
Packit Service 5bcba8
	    pos[j] = pos[k - 1];
Packit Service 5bcba8
	  }
Packit Service 5bcba8
	}
Packit Service 5bcba8
      }
Packit Service 5bcba8
    }
Packit Service 5bcba8
Packit Service 5bcba8
    if (step == MEASURE)
Packit Service 5bcba8
    {
Packit Service 5bcba8
      if (unlikely (!buffer->ensure (count + extra_glyphs_needed)))
Packit Service 5bcba8
        break;
Packit Service 5bcba8
    }
Packit Service 5bcba8
    else
Packit Service 5bcba8
    {
Packit Service 5bcba8
      assert (j == 0);
Packit Service 5bcba8
      buffer->len = new_len;
Packit Service 5bcba8
    }
Packit Service 5bcba8
  }
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
static void
Packit Service 5bcba8
postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan,
Packit Service 5bcba8
			   hb_buffer_t              *buffer,
Packit Service 5bcba8
			   hb_font_t                *font)
Packit Service 5bcba8
{
Packit Service 5bcba8
  apply_stch (plan, buffer, font);
Packit Service 5bcba8
Packit Service 5bcba8
  HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
Packit Service 5bcba8
{
Packit Service 5bcba8
  "arabic",
Packit Service 5bcba8
  collect_features_arabic,
Packit Service 5bcba8
  NULL, /* override_features */
Packit Service 5bcba8
  data_create_arabic,
Packit Service 5bcba8
  data_destroy_arabic,
Packit Service 5bcba8
  NULL, /* preprocess_text */
Packit Service 5bcba8
  postprocess_glyphs_arabic,
Packit Service 5bcba8
  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
Packit Service 5bcba8
  NULL, /* decompose */
Packit Service 5bcba8
  NULL, /* compose */
Packit Service 5bcba8
  setup_masks_arabic,
Packit Service 5bcba8
  NULL, /* disable_otl */
Packit Service 5bcba8
  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
Packit Service 5bcba8
  true, /* fallback_position */
Packit Service 5bcba8
};