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

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