text:
/*
 * Copyright © 1998-2004  David Turner and Werner Lemberg
 * Copyright © 2004,2007,2009,2010  Red Hat, Inc.
 * Copyright © 2011,2012  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
 * Google Author(s): Behdad Esfahbod
 */

#include "hb-buffer.hh"
#include "hb-utf.hh"

/**
 * SECTION: hb-buffer
 * @title: hb-buffer
 * @short_description: Input and output buffers
 * @include: hb.h
 *
 * Buffers serve dual role in HarfBuzz; they hold the input characters that are
 * passed to hb_shape(), and after shaping they hold the output glyphs.
 **/

/**
 * hb_segment_properties_equal:
 * @a: first #hb_segment_properties_t to compare.
 * @b: second #hb_segment_properties_t to compare.
 *
 * Checks the equality of two #hb_segment_properties_t's.
 *
 * Return value:
 * %true if all properties of @a equal those of @b, false otherwise.
 *
 * Since: 0.9.7
 **/
hb_bool_t
hb_segment_properties_equal (const hb_segment_properties_t *a,
			     const hb_segment_properties_t *b)
{
  return a->direction == b->direction &&
	 a->script    == b->script    &&
	 a->language  == b->language  &&
	 a->reserved1 == b->reserved1 &&
	 a->reserved2 == b->reserved2;

}

/**
 * hb_segment_properties_hash:
 * @p: #hb_segment_properties_t to hash.
 *
 * Creates a hash representing @p.
 *
 * Return value:
 * A hash of @p.
 *
 * Since: 0.9.7
 **/
unsigned int
hb_segment_properties_hash (const hb_segment_properties_t *p)
{
  return (unsigned int) p->direction ^
	 (unsigned int) p->script ^
	 (intptr_t) (p->language);
}

/* Here is how the buffer works internally:
 *
 * There are two info pointers: info and out_info.  They always have
 * the same allocated size, but different lengths.
 *
 * As an optimization, both info and out_info may point to the
 * same piece of memory, which is owned by info.  This remains the
 * case as long as out_len doesn't exceed i at any time.
 * In that case, swap_buffers() is no-op and the glyph operations operate
 * mostly in-place.
 *
 * As soon as out_info gets longer than info, out_info is moved over
 * to an alternate buffer (which we reuse the pos buffer for!), and its
 * current contents (out_len entries) are copied to the new place.
 * This should all remain transparent to the user.  swap_buffers() then
 * switches info and out_info.
 */

/* Internal API */

bool
hb_buffer_t::enlarge (unsigned int size)
{
  if (unlikely (!successful))
    return false;
  if (unlikely (size > max_len))
  {
    successful = false;
    return false;
  }

  unsigned int new_allocated = allocated;
  hb_glyph_position_t *new_pos = nullptr;
  hb_glyph_info_t *new_info = nullptr;
  bool separate_out = out_info != info;

  if (unlikely (hb_unsigned_mul_overflows (size, sizeof (info[0]))))
    goto done;

  while (size >= new_allocated)
    new_allocated += (new_allocated >> 1) + 32;

  static_assert ((sizeof (info[0]) == sizeof (pos[0])), "");
  if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]))))
    goto done;

  new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0]));
  new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0]));

done:
  if (unlikely (!new_pos || !new_info))
    successful = false;

  if (likely (new_pos))
    pos = new_pos;

  if (likely (new_info))
    info = new_info;

  out_info = separate_out ? (hb_glyph_info_t *) pos : info;
  if (likely (successful))
    allocated = new_allocated;

  return likely (successful);
}

bool
hb_buffer_t::make_room_for (unsigned int num_in,
			    unsigned int num_out)
{
  if (unlikely (!ensure (out_len + num_out))) return false;

  if (out_info == info &&
      out_len + num_out > idx + num_in)
  {
    assert (have_output);

    out_info = (hb_glyph_info_t *) pos;
    memcpy (out_info, info, out_len * sizeof (out_info[0]));
  }

  return true;
}

bool
hb_buffer_t::shift_forward (unsigned int count)
{
  assert (have_output);
  if (unlikely (!ensure (len + count))) return false;

  memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0]));
  if (idx + count > len)
  {
    /* Under memory failure we might expose this area.  At least
     * clean it up.  Oh well...
     *
     * Ideally, we should at least set Default_Ignorable bits on
     * these, as well as consistent cluster values.  But the former
     * is layering violation... */
    memset (info + len, 0, (idx + count - len) * sizeof (info[0]));
  }
  len += count;
  idx += count;

  return true;
}

hb_buffer_t::scratch_buffer_t *
hb_buffer_t::get_scratch_buffer (unsigned int *size)
{
  have_output = false;
  have_positions = false;

  out_len = 0;
  out_info = info;

  assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0);
  *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t);
  return (scratch_buffer_t *) (void *) pos;
}

/* HarfBuzz-Internal API */

void
hb_buffer_t::reset ()
{
  if (unlikely (hb_object_is_immutable (this)))
    return;

  hb_unicode_funcs_destroy (unicode);
  unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ());
  flags = HB_BUFFER_FLAG_DEFAULT;
  replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
  invisible = 0;

  clear ();
}

void
hb_buffer_t::clear ()
{
  if (unlikely (hb_object_is_immutable (this)))
    return;

  hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
  props = default_props;
  scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;

  content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
  successful = true;
  have_output = false;
  have_positions = false;

  idx = 0;
  len = 0;
  out_len = 0;
  out_info = info;

  serial = 0;

  memset (context, 0, sizeof context);
  memset (context_len, 0, sizeof context_len);

  deallocate_var_all ();
}

void
hb_buffer_t::add (hb_codepoint_t  codepoint,
		  unsigned int    cluster)
{
  hb_glyph_info_t *glyph;

  if (unlikely (!ensure (len + 1))) return;

  glyph = &info[len];

  memset (glyph, 0, sizeof (*glyph));
  glyph->codepoint = codepoint;
  glyph->mask = 0;
  glyph->cluster = cluster;

  len++;
}

void
hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info)
{
  if (unlikely (!ensure (len + 1))) return;

  info[len] = glyph_info;

  len++;
}

void
hb_buffer_t::remove_output ()
{
  if (unlikely (hb_object_is_immutable (this)))
    return;

  have_output = false;
  have_positions = false;

  out_len = 0;
  out_info = info;
}

void
hb_buffer_t::clear_output ()
{
  if (unlikely (hb_object_is_immutable (this)))
    return;

  have_output = true;
  have_positions = false;

  out_len = 0;
  out_info = info;
}

void
hb_buffer_t::clear_positions ()
{
  if (unlikely (hb_object_is_immutable (this)))
    return;

  have_output = false;
  have_positions = true;

  out_len = 0;
  out_info = info;

  hb_memset (pos, 0, sizeof (pos[0]) * len);
}

void
hb_buffer_t::swap_buffers ()
{
  if (unlikely (!successful)) return;

  assert (have_output);
  have_output = false;

  if (out_info != info)
  {
    hb_glyph_info_t *tmp_string;
    tmp_string = info;
    info = out_info;
    out_info = tmp_string;
    pos = (hb_glyph_position_t *) out_info;
  }

  unsigned int tmp;
  tmp = len;
  len = out_len;
  out_len = tmp;

  idx = 0;
}

void
hb_buffer_t::replace_glyphs (unsigned int num_in,
			     unsigned int num_out,
			     const uint32_t *glyph_data)
{
  if (unlikely (!make_room_for (num_in, num_out))) return;

  assert (idx + num_in <= len);

  merge_clusters (idx, idx + num_in);

  hb_glyph_info_t orig_info = info[idx];
  hb_glyph_info_t *pinfo = &out_info[out_len];
  for (unsigned int i = 0; i < num_out; i++)
  {
    *pinfo = orig_info;
    pinfo->codepoint = glyph_data[i];
    pinfo++;
  }

  idx  += num_in;
  out_len += num_out;
}

bool
hb_buffer_t::move_to (unsigned int i)
{
  if (!have_output)
  {
    assert (i <= len);
    idx = i;
    return true;
  }
  if (unlikely (!successful))
    return false;

  assert (i <= out_len + (len - idx));

  if (out_len < i)
  {
    unsigned int count = i - out_len;
    if (unlikely (!make_room_for (count, count))) return false;

    memmove (out_info + out_len, info + idx, count * sizeof (out_info[0]));
    idx += count;
    out_len += count;
  }
  else if (out_len > i)
  {
    /* Tricky part: rewinding... */
    unsigned int count = out_len - i;

    /* This will blow in our face if memory allocation fails later
     * in this same lookup...
     *
     * We used to shift with extra 32 items, instead of the 0 below.
     * But that would leave empty slots in the buffer in case of allocation
     * failures.  Setting to zero for now to avoid other problems (see
     * comments in shift_forward().  This can cause O(N^2) behavior more
     * severely than adding 32 empty slots can... */
    if (unlikely (idx < count && !shift_forward (count + 0))) return false;

    assert (idx >= count);

    idx -= count;
    out_len -= count;
    memmove (info + idx, out_info + out_len, count * sizeof (out_info[0]));
  }

  return true;
}

void
hb_buffer_t::set_masks (hb_mask_t    value,
			hb_mask_t    mask,
			unsigned int cluster_start,
			unsigned int cluster_end)
{
  hb_mask_t not_mask = ~mask;
  value &= mask;

  if (!mask)
    return;

  unsigned int count = len;
  for (unsigned int i = 0; i < count; i++)
    if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end)
      info[i].mask = (info[i].mask & not_mask) | value;
}

void
hb_buffer_t::reverse_range (unsigned int start,
			    unsigned int end)
{
  if (end - start < 2)
    return;

  hb_array_t<hb_glyph_info_t> (info, len).reverse (start, end);

  if (have_positions) {
    hb_array_t<hb_glyph_position_t> (pos, len).reverse (start, end);
  }
}

void
hb_buffer_t::reverse ()
{
  if (unlikely (!len))
    return;

  reverse_range (0, len);
}

void
hb_buffer_t::reverse_clusters ()
{
  unsigned int i, start, count, last_cluster;

  if (unlikely (!len))
    return;

  reverse ();

  count = len;
  start = 0;
  last_cluster = info[0].cluster;
  for (i = 1; i < count; i++) {
    if (last_cluster != info[i].cluster) {
      reverse_range (start, i);
      start = i;
      last_cluster = info[i].cluster;
    }
  }
  reverse_range (start, i);
}

void
hb_buffer_t::merge_clusters_impl (unsigned int start,
				  unsigned int end)
{
  if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
  {
    unsafe_to_break (start, end);
    return;
  }

  unsigned int cluster = info[start].cluster;

  for (unsigned int i = start + 1; i < end; i++)
    cluster = hb_min (cluster, info[i].cluster);

  /* Extend end */
  while (end < len && info[end - 1].cluster == info[end].cluster)
    end++;

  /* Extend start */
  while (idx < start && info[start - 1].cluster == info[start].cluster)
    start--;

  /* If we hit the start of buffer, continue in out-buffer. */
  if (idx == start)
    for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
      set_cluster (out_info[i - 1], cluster);

  for (unsigned int i = start; i < end; i++)
    set_cluster (info[i], cluster);
}
void
hb_buffer_t::merge_out_clusters (unsigned int start,
				 unsigned int end)
{
  if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
    return;

  if (unlikely (end - start < 2))
    return;

  unsigned int cluster = out_info[start].cluster;

  for (unsigned int i = start + 1; i < end; i++)
    cluster = hb_min (cluster, out_info[i].cluster);

  /* Extend start */
  while (start && out_info[start - 1].cluster == out_info[start].cluster)
    start--;

  /* Extend end */
  while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster)
    end++;

  /* If we hit the end of out-buffer, continue in buffer. */
  if (end == out_len)
    for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
      set_cluster (info[i], cluster);

  for (unsigned int i = start; i < end; i++)
    set_cluster (out_info[i], cluster);
}
void
hb_buffer_t::delete_glyph ()
{
  /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */

  unsigned int cluster = info[idx].cluster;
  if (idx + 1 < len && cluster == info[idx + 1].cluster)
  {
    /* Cluster survives; do nothing. */
    goto done;
  }

  if (out_len)
  {
    /* Merge cluster backward. */
    if (cluster < out_info[out_len - 1].cluster)
    {
      unsigned int mask = info[idx].mask;
      unsigned int old_cluster = out_info[out_len - 1].cluster;
      for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--)
	set_cluster (out_info[i - 1], cluster, mask);
    }
    goto done;
  }

  if (idx + 1 < len)
  {
    /* Merge cluster forward. */
    merge_clusters (idx, idx + 2);
    goto done;
  }

done:
  skip_glyph ();
}

void
hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end)
{
  unsigned int cluster = UINT_MAX;
  cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster);
  _unsafe_to_break_set_mask (info, start, end, cluster);
}
void
hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end)
{
  if (!have_output)
  {
    unsafe_to_break_impl (start, end);
    return;
  }

  assert (start <= out_len);
  assert (idx <= end);

  unsigned int cluster = UINT_MAX;
  cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster);
  cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster);
  _unsafe_to_break_set_mask (out_info, start, out_len, cluster);
  _unsafe_to_break_set_mask (info, idx, end, cluster);
}

void
hb_buffer_t::guess_segment_properties ()
{
  assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
	  (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID));

  /* If script is set to INVALID, guess from buffer contents */
  if (props.script == HB_SCRIPT_INVALID) {
    for (unsigned int i = 0; i < len; i++) {
      hb_script_t script = unicode->script (info[i].codepoint);
      if (likely (script != HB_SCRIPT_COMMON &&
		  script != HB_SCRIPT_INHERITED &&
		  script != HB_SCRIPT_UNKNOWN)) {
	props.script = script;
	break;
      }
    }
  }

  /* If direction is set to INVALID, guess from script */
  if (props.direction == HB_DIRECTION_INVALID) {
    props.direction = hb_script_get_horizontal_direction (props.script);
    if (props.direction == HB_DIRECTION_INVALID)
      props.direction = HB_DIRECTION_LTR;
  }

  /* If language is not set, use default language from locale */
  if (props.language == HB_LANGUAGE_INVALID) {
    /* TODO get_default_for_script? using $LANGUAGE */
    props.language = hb_language_get_default ();
  }
}

/* Public API */

DEFINE_NULL_INSTANCE (hb_buffer_t) =
{
  HB_OBJECT_HEADER_STATIC,

  const_cast<hb_unicode_funcs_t *> (&_hb_Null_hb_unicode_funcs_t),
  HB_BUFFER_FLAG_DEFAULT,
  HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
  HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
  0, /* invisible */
  HB_BUFFER_SCRATCH_FLAG_DEFAULT,
  HB_BUFFER_MAX_LEN_DEFAULT,
  HB_BUFFER_MAX_OPS_DEFAULT,

  HB_BUFFER_CONTENT_TYPE_INVALID,
  HB_SEGMENT_PROPERTIES_DEFAULT,
  false, /* successful */
  true, /* have_output */
  true  /* have_positions */

  /* Zero is good enough for everything else. */
};

/**
 * hb_buffer_create: (Xconstructor)
 *
 * Creates a new #hb_buffer_t with all properties to defaults.
 *
 * Return value: (transfer full):
 * A newly allocated #hb_buffer_t with a reference count of 1. The initial
 * reference count should be released with hb_buffer_destroy() when you are done
 * using the #hb_buffer_t. This function never returns %NULL. If memory cannot
 * be allocated, a special #hb_buffer_t object will be returned on which
 * hb_buffer_allocation_successful() returns %false.
 *
 * Since: 0.9.2
 **/
hb_buffer_t *
hb_buffer_create ()
{
  hb_buffer_t *buffer;

  if (!(buffer = hb_object_create<hb_buffer_t> ()))
    return hb_buffer_get_empty ();

  buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
  buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT;

  buffer->reset ();

  return buffer;
}

/**
 * hb_buffer_get_empty:
 *
 *
 *
 * Return value: (transfer full):
 *
 * Since: 0.9.2
 **/
hb_buffer_t *
hb_buffer_get_empty ()
{
  return const_cast<hb_buffer_t *> (&Null (hb_buffer_t));
}

/**
 * hb_buffer_reference: (skip)
 * @buffer: an #hb_buffer_t.
 *
 * Increases the reference count on @buffer by one. This prevents @buffer from
 * being destroyed until a matching call to hb_buffer_destroy() is made.
 *
 * Return value: (transfer full):
 * The referenced #hb_buffer_t.
 *
 * Since: 0.9.2
 **/
hb_buffer_t *
hb_buffer_reference (hb_buffer_t *buffer)
{
  return hb_object_reference (buffer);
}

/**
 * hb_buffer_destroy: (skip)
 * @buffer: an #hb_buffer_t.
 *
 * Deallocate the @buffer.
 * Decreases the reference count on @buffer by one. If the result is zero, then
 * @buffer and all associated resources are freed. See hb_buffer_reference().
 *
 * Since: 0.9.2
 **/
void
hb_buffer_destroy (hb_buffer_t *buffer)
{
  if (!hb_object_destroy (buffer)) return;

  hb_unicode_funcs_destroy (buffer->unicode);

  free (buffer->info);
  free (buffer->pos);
#ifndef HB_NO_BUFFER_MESSAGE
  if (buffer->message_destroy)
    buffer->message_destroy (buffer->message_data);
#endif

  free (buffer);
}

/**
 * hb_buffer_set_user_data: (skip)
 * @buffer: an #hb_buffer_t.
 * @key:
 * @data:
 * @destroy:
 * @replace:
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 **/
hb_bool_t
hb_buffer_set_user_data (hb_buffer_t        *buffer,
			 hb_user_data_key_t *key,
			 void *              data,
			 hb_destroy_func_t   destroy,
			 hb_bool_t           replace)
{
  return hb_object_set_user_data (buffer, key, data, destroy, replace);
}

/**
 * hb_buffer_get_user_data: (skip)
 * @buffer: an #hb_buffer_t.
 * @key:
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 **/
void *
hb_buffer_get_user_data (hb_buffer_t        *buffer,
			 hb_user_data_key_t *key)
{
  return hb_object_get_user_data (buffer, key);
}

/**
 * hb_buffer_set_content_type:
 * @buffer: an #hb_buffer_t.
 * @content_type: the type of buffer contents to set
 *
 * Sets the type of @buffer contents, buffers are either empty, contain
 * characters (before shaping) or glyphs (the result of shaping).
 *
 * Since: 0.9.5
 **/
void
hb_buffer_set_content_type (hb_buffer_t              *buffer,
			    hb_buffer_content_type_t  content_type)
{
  buffer->content_type = content_type;
}

/**
 * hb_buffer_get_content_type:
 * @buffer: an #hb_buffer_t.
 *
 * see hb_buffer_set_content_type().
 *
 * Return value:
 * The type of @buffer contents.
 *
 * Since: 0.9.5
 **/
hb_buffer_content_type_t
hb_buffer_get_content_type (hb_buffer_t *buffer)
{
  return buffer->content_type;
}

/**
 * hb_buffer_set_unicode_funcs:
 * @buffer: an #hb_buffer_t.
 * @unicode_funcs:
 *
 *
 *
 * Since: 0.9.2
 **/
void
hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
			     hb_unicode_funcs_t *unicode_funcs)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  if (!unicode_funcs)
    unicode_funcs = hb_unicode_funcs_get_default ();

  hb_unicode_funcs_reference (unicode_funcs);
  hb_unicode_funcs_destroy (buffer->unicode);
  buffer->unicode = unicode_funcs;
}

/**
 * hb_buffer_get_unicode_funcs:
 * @buffer: an #hb_buffer_t.
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 **/
hb_unicode_funcs_t *
hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
{
  return buffer->unicode;
}

/**
 * hb_buffer_set_direction:
 * @buffer: an #hb_buffer_t.
 * @direction: the #hb_direction_t of the @buffer
 *
 * Set the text flow direction of the buffer. No shaping can happen without
 * setting @buffer direction, and it controls the visual direction for the
 * output glyphs; for RTL direction the glyphs will be reversed. Many layout
 * features depend on the proper setting of the direction, for example,
 * reversing RTL text before shaping, then shaping with LTR direction is not
 * the same as keeping the text in logical order and shaping with RTL
 * direction.
 *
 * Since: 0.9.2
 **/
void
hb_buffer_set_direction (hb_buffer_t    *buffer,
			 hb_direction_t  direction)

{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  buffer->props.direction = direction;
}

/**
 * hb_buffer_get_direction:
 * @buffer: an #hb_buffer_t.
 *
 * See hb_buffer_set_direction()
 *
 * Return value:
 * The direction of the @buffer.
 *
 * Since: 0.9.2
 **/
hb_direction_t
hb_buffer_get_direction (hb_buffer_t    *buffer)
{
  return buffer->props.direction;
}

/**
 * hb_buffer_set_script:
 * @buffer: an #hb_buffer_t.
 * @script: an #hb_script_t to set.
 *
 * Sets the script of @buffer to @script.
 *
 * Script is crucial for choosing the proper shaping behaviour for scripts that
 * require it (e.g. Arabic) and the which OpenType features defined in the font
 * to be applied.
 *
 * You can pass one of the predefined #hb_script_t values, or use
 * hb_script_from_string() or hb_script_from_iso15924_tag() to get the
 * corresponding script from an ISO 15924 script tag.
 *
 * Since: 0.9.2
 **/
void
hb_buffer_set_script (hb_buffer_t *buffer,
		      hb_script_t  script)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  buffer->props.script = script;
}

/**
 * hb_buffer_get_script:
 * @buffer: an #hb_buffer_t.
 *
 * See hb_buffer_set_script().
 *
 * Return value:
 * The #hb_script_t of the @buffer.
 *
 * Since: 0.9.2
 **/
hb_script_t
hb_buffer_get_script (hb_buffer_t *buffer)
{
  return buffer->props.script;
}

/**
 * hb_buffer_set_language:
 * @buffer: an #hb_buffer_t.
 * @language: an hb_language_t to set.
 *
 * Sets the language of @buffer to @language.
 *
 * Languages are crucial for selecting which OpenType feature to apply to the
 * buffer which can result in applying language-specific behaviour. Languages
 * are orthogonal to the scripts, and though they are related, they are
 * different concepts and should not be confused with each other.
 *
 * Use hb_language_from_string() to convert from BCP 47 language tags to
 * #hb_language_t.
 *
 * Since: 0.9.2
 **/
void
hb_buffer_set_language (hb_buffer_t   *buffer,
			hb_language_t  language)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  buffer->props.language = language;
}

/**
 * hb_buffer_get_language:
 * @buffer: an #hb_buffer_t.
 *
 * See hb_buffer_set_language().
 *
 * Return value: (transfer none):
 * The #hb_language_t of the buffer. Must not be freed by the caller.
 *
 * Since: 0.9.2
 **/
hb_language_t
hb_buffer_get_language (hb_buffer_t *buffer)
{
  return buffer->props.language;
}

/**
 * hb_buffer_set_segment_properties:
 * @buffer: an #hb_buffer_t.
 * @props: an #hb_segment_properties_t to use.
 *
 * Sets the segment properties of the buffer, a shortcut for calling
 * hb_buffer_set_direction(), hb_buffer_set_script() and
 * hb_buffer_set_language() individually.
 *
 * Since: 0.9.7
 **/
void
hb_buffer_set_segment_properties (hb_buffer_t *buffer,
				  const hb_segment_properties_t *props)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  buffer->props = *props;
}

/**
 * hb_buffer_get_segment_properties:
 * @buffer: an #hb_buffer_t.
 * @props: (out): the output #hb_segment_properties_t.
 *
 * Sets @props to the #hb_segment_properties_t of @buffer.
 *
 * Since: 0.9.7
 **/
void
hb_buffer_get_segment_properties (hb_buffer_t *buffer,
				  hb_segment_properties_t *props)
{
  *props = buffer->props;
}

/**
 * hb_buffer_set_flags:
 * @buffer: an #hb_buffer_t.
 * @flags: the buffer flags to set.
 *
 * Sets @buffer flags to @flags. See #hb_buffer_flags_t.
 *
 * Since: 0.9.7
 **/
void
hb_buffer_set_flags (hb_buffer_t       *buffer,
		     hb_buffer_flags_t  flags)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  buffer->flags = flags;
}

/**
 * hb_buffer_get_flags:
 * @buffer: an #hb_buffer_t.
 *
 * See hb_buffer_set_flags().
 *
 * Return value:
 * The @buffer flags.
 *
 * Since: 0.9.7
 **/
hb_buffer_flags_t
hb_buffer_get_flags (hb_buffer_t *buffer)
{
  return buffer->flags;
}

/**
 * hb_buffer_set_cluster_level:
 * @buffer: an #hb_buffer_t.
 * @cluster_level:
 *
 *
 *
 * Since: 0.9.42
 **/
void
hb_buffer_set_cluster_level (hb_buffer_t       *buffer,
		     hb_buffer_cluster_level_t  cluster_level)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  buffer->cluster_level = cluster_level;
}

/**
 * hb_buffer_get_cluster_level:
 * @buffer: an #hb_buffer_t.
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.42
 **/
hb_buffer_cluster_level_t
hb_buffer_get_cluster_level (hb_buffer_t *buffer)
{
  return buffer->cluster_level;
}

/**
 * hb_buffer_set_replacement_codepoint:
 * @buffer: an #hb_buffer_t.
 * @replacement: the replacement #hb_codepoint_t
 *
 * Sets the #hb_codepoint_t that replaces invalid entries for a given encoding
 * when adding text to @buffer.
 *
 * Default is %HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT.
 *
 * Since: 0.9.31
 **/
void
hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
				     hb_codepoint_t  replacement)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  buffer->replacement = replacement;
}

/**
 * hb_buffer_get_replacement_codepoint:
 * @buffer: an #hb_buffer_t.
 *
 * See hb_buffer_set_replacement_codepoint().
 *
 * Return value:
 * The @buffer replacement #hb_codepoint_t.
 *
 * Since: 0.9.31
 **/
hb_codepoint_t
hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer)
{
  return buffer->replacement;
}

/**
 * hb_buffer_set_invisible_glyph:
 * @buffer: an #hb_buffer_t.
 * @invisible: the invisible #hb_codepoint_t
 *
 * Sets the #hb_codepoint_t that replaces invisible characters in
 * the shaping result.  If set to zero (default), the glyph for the
 * U+0020 SPACE character is used.  Otherwise, this value is used
 * verbatim.
 *
 * Since: 2.0.0
 **/
void
hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
			       hb_codepoint_t  invisible)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  buffer->invisible = invisible;
}

/**
 * hb_buffer_get_invisible_glyph:
 * @buffer: an #hb_buffer_t.
 *
 * See hb_buffer_set_invisible_glyph().
 *
 * Return value:
 * The @buffer invisible #hb_codepoint_t.
 *
 * Since: 2.0.0
 **/
hb_codepoint_t
hb_buffer_get_invisible_glyph (hb_buffer_t    *buffer)
{
  return buffer->invisible;
}

/**
 * hb_buffer_reset:
 * @buffer: an #hb_buffer_t.
 *
 * Resets the buffer to its initial status, as if it was just newly created
 * with hb_buffer_create().
 *
 * Since: 0.9.2
 **/
void
hb_buffer_reset (hb_buffer_t *buffer)
{
  buffer->reset ();
}

/**
 * hb_buffer_clear_contents:
 * @buffer: an #hb_buffer_t.
 *
 * Similar to hb_buffer_reset(), but does not clear the Unicode functions and
 * the replacement code point.
 *
 * Since: 0.9.11
 **/
void
hb_buffer_clear_contents (hb_buffer_t *buffer)
{
  buffer->clear ();
}

/**
 * hb_buffer_pre_allocate:
 * @buffer: an #hb_buffer_t.
 * @size: number of items to pre allocate.
 *
 * Pre allocates memory for @buffer to fit at least @size number of items.
 *
 * Return value:
 * %true if @buffer memory allocation succeeded, %false otherwise.
 *
 * Since: 0.9.2
 **/
hb_bool_t
hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size)
{
  return buffer->ensure (size);
}

/**
 * hb_buffer_allocation_successful:
 * @buffer: an #hb_buffer_t.
 *
 * Check if allocating memory for the buffer succeeded.
 *
 * Return value:
 * %true if @buffer memory allocation succeeded, %false otherwise.
 *
 * Since: 0.9.2
 **/
hb_bool_t
hb_buffer_allocation_successful (hb_buffer_t  *buffer)
{
  return buffer->successful;
}

/**
 * hb_buffer_add:
 * @buffer: an #hb_buffer_t.
 * @codepoint: a Unicode code point.
 * @cluster: the cluster value of @codepoint.
 *
 * Appends a character with the Unicode value of @codepoint to @buffer, and
 * gives it the initial cluster value of @cluster. Clusters can be any thing
 * the client wants, they are usually used to refer to the index of the
 * character in the input text stream and are output in
 * #hb_glyph_info_t.cluster field.
 *
 * This function does not check the validity of @codepoint, it is up to the
 * caller to ensure it is a valid Unicode code point.
 *
 * Since: 0.9.7
 **/
void
hb_buffer_add (hb_buffer_t    *buffer,
	       hb_codepoint_t  codepoint,
	       unsigned int    cluster)
{
  buffer->add (codepoint, cluster);
  buffer->clear_context (1);
}

/**
 * hb_buffer_set_length:
 * @buffer: an #hb_buffer_t.
 * @length: the new length of @buffer.
 *
 * Similar to hb_buffer_pre_allocate(), but clears any new items added at the
 * end.
 *
 * Return value:
 * %true if @buffer memory allocation succeeded, %false otherwise.
 *
 * Since: 0.9.2
 **/
hb_bool_t
hb_buffer_set_length (hb_buffer_t  *buffer,
		      unsigned int  length)
{
  if (unlikely (hb_object_is_immutable (buffer)))
    return length == 0;

  if (!buffer->ensure (length))
    return false;

  /* Wipe the new space */
  if (length > buffer->len) {
    memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
    if (buffer->have_positions)
      memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len));
  }

  buffer->len = length;

  if (!length)
  {
    buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
    buffer->clear_context (0);
  }
  buffer->clear_context (1);

  return true;
}

/**
 * hb_buffer_get_length:
 * @buffer: an #hb_buffer_t.
 *
 * Returns the number of items in the buffer.
 *
 * Return value:
 * The @buffer length.
 * The value valid as long as buffer has not been modified.
 *
 * Since: 0.9.2
 **/
unsigned int
hb_buffer_get_length (hb_buffer_t *buffer)
{
  return buffer->len;
}

/**
 * hb_buffer_get_glyph_infos:
 * @buffer: an #hb_buffer_t.
 * @length: (out): output array length.
 *
 * Returns @buffer glyph information array.  Returned pointer
 * is valid as long as @buffer contents are not modified.
 *
 * Return value: (transfer none) (array length=length):
 * The @buffer glyph information array.
 * The value valid as long as buffer has not been modified.
 *
 * Since: 0.9.2
 **/
hb_glyph_info_t *
hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
			   unsigned int *length)
{
  if (length)
    *length = buffer->len;

  return (hb_glyph_info_t *) buffer->info;
}

/**
 * hb_buffer_get_glyph_positions:
 * @buffer: an #hb_buffer_t.
 * @length: (out): output length.
 *
 * Returns @buffer glyph position array.  Returned pointer
 * is valid as long as @buffer contents are not modified.
 *
 * Return value: (transfer none) (array length=length):
 * The @buffer glyph position array.
 * The value valid as long as buffer has not been modified.
 *
 * Since: 0.9.2
 **/
hb_glyph_position_t *
hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
			       unsigned int *length)
{
  if (!buffer->have_positions)
    buffer->clear_positions ();

  if (length)
    *length = buffer->len;

  return (hb_glyph_position_t *) buffer->pos;
}

/**
 * hb_glyph_info_get_glyph_flags:
 * @info: a #hb_glyph_info_t.
 *
 * Returns glyph flags encoded within a #hb_glyph_info_t.
 *
 * Return value:
 * The #hb_glyph_flags_t encoded within @info.
 *
 * Since: 1.5.0
 **/
hb_glyph_flags_t
(hb_glyph_info_get_glyph_flags) (const hb_glyph_info_t *info)
{
  return hb_glyph_info_get_glyph_flags (info);
}

/**
 * hb_buffer_reverse:
 * @buffer: an #hb_buffer_t.
 *
 * Reverses buffer contents.
 *
 * Since: 0.9.2
 **/
void
hb_buffer_reverse (hb_buffer_t *buffer)
{
  buffer->reverse ();
}

/**
 * hb_buffer_reverse_range:
 * @buffer: an #hb_buffer_t.
 * @start: start index.
 * @end: end index.
 *
 * Reverses buffer contents between start to end.
 *
 * Since: 0.9.41
 **/
void
hb_buffer_reverse_range (hb_buffer_t *buffer,
			 unsigned int start, unsigned int end)
{
  buffer->reverse_range (start, end);
}

/**
 * hb_buffer_reverse_clusters:
 * @buffer: an #hb_buffer_t.
 *
 * Reverses buffer clusters.  That is, the buffer contents are
 * reversed, then each cluster (consecutive items having the
 * same cluster number) are reversed again.
 *
 * Since: 0.9.2
 **/
void
hb_buffer_reverse_clusters (hb_buffer_t *buffer)
{
  buffer->reverse_clusters ();
}

/**
 * hb_buffer_guess_segment_properties:
 * @buffer: an #hb_buffer_t.
 *
 * Sets unset buffer segment properties based on buffer Unicode
 * contents.  If buffer is not empty, it must have content type
 * %HB_BUFFER_CONTENT_TYPE_UNICODE.
 *
 * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it
 * will be set to the Unicode script of the first character in
 * the buffer that has a script other than %HB_SCRIPT_COMMON,
 * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN.
 *
 * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID),
 * it will be set to the natural horizontal direction of the
 * buffer script as returned by hb_script_get_horizontal_direction().
 * If hb_script_get_horizontal_direction() returns %HB_DIRECTION_INVALID,
 * then %HB_DIRECTION_LTR is used.
 *
 * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID),
 * it will be set to the process's default language as returned by
 * hb_language_get_default().  This may change in the future by
 * taking buffer script into consideration when choosing a language.
 * Note that hb_language_get_default() is NOT threadsafe the first time
 * it is called.  See documentation for that function for details.
 *
 * Since: 0.9.7
 **/
void
hb_buffer_guess_segment_properties (hb_buffer_t *buffer)
{
  buffer->guess_segment_properties ();
}

template <typename utf_t>
static inline void
hb_buffer_add_utf (hb_buffer_t  *buffer,
		   const typename utf_t::codepoint_t *text,
		   int           text_length,
		   unsigned int  item_offset,
		   int           item_length)
{
  typedef typename utf_t::codepoint_t T;
  const hb_codepoint_t replacement = buffer->replacement;

  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));

  if (unlikely (hb_object_is_immutable (buffer)))
    return;

  if (text_length == -1)
    text_length = utf_t::strlen (text);

  if (item_length == -1)
    item_length = text_length - item_offset;

  buffer->ensure (buffer->len + item_length * sizeof (T) / 4);

  /* If buffer is empty and pre-context provided, install it.
   * This check is written this way, to make sure people can
   * provide pre-context in one add_utf() call, then provide
   * text in a follow-up call.  See:
   *
   * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13
   */
  if (!buffer->len && item_offset > 0)
  {
    /* Add pre-context */
    buffer->clear_context (0);
    const T *prev = text + item_offset;
    const T *start = text;
    while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH)
    {
      hb_codepoint_t u;
      prev = utf_t::prev (prev, start, &u, replacement);
      buffer->context[0][buffer->context_len[0]++] = u;
    }
  }

  const T *next = text + item_offset;
  const T *end = next + item_length;
  while (next < end)
  {
    hb_codepoint_t u;
    const T *old_next = next;
    next = utf_t::next (next, end, &u, replacement);
    buffer->add (u, old_next - (const T *) text);
  }

  /* Add post-context */
  buffer->clear_context (1);
  end = text + text_length;
  while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH)
  {
    hb_codepoint_t u;
    next = utf_t::next (next, end, &u, replacement);
    buffer->context[1][buffer->context_len[1]++] = u;
  }

  buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
}

/**
 * hb_buffer_add_utf8:
 * @buffer: an #hb_buffer_t.
 * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8
 *               characters to append.
 * @text_length: the length of the @text, or -1 if it is %NULL terminated.
 * @item_offset: the offset of the first character to add to the @buffer.
 * @item_length: the number of characters to add to the @buffer, or -1 for the
 *               end of @text (assuming it is %NULL terminated).
 *
 * See hb_buffer_add_codepoints().
 *
 * Replaces invalid UTF-8 characters with the @buffer replacement code point,
 * see hb_buffer_set_replacement_codepoint().
 *
 * Since: 0.9.2
 **/
void
hb_buffer_add_utf8 (hb_buffer_t  *buffer,
		    const char   *text,
		    int           text_length,
		    unsigned int  item_offset,
		    int           item_length)
{
  hb_buffer_add_utf<hb_utf8_t> (buffer, (const uint8_t *) text, text_length, item_offset, item_length);
}

/**
 * hb_buffer_add_utf16:
 * @buffer: an #hb_buffer_t.
 * @text: (array length=text_length): an array of UTF-16 characters to append.
 * @text_length: the length of the @text, or -1 if it is %NULL terminated.
 * @item_offset: the offset of the first character to add to the @buffer.
 * @item_length: the number of characters to add to the @buffer, or -1 for the
 *               end of @text (assuming it is %NULL terminated).
 *
 * See hb_buffer_add_codepoints().
 *
 * Replaces invalid UTF-16 characters with the @buffer replacement code point,
 * see hb_buffer_set_replacement_codepoint().
 *
 * Since: 0.9.2
 **/
void
hb_buffer_add_utf16 (hb_buffer_t    *buffer,
		     const uint16_t *text,
		     int             text_length,
		     unsigned int    item_offset,
		     int             item_length)
{
  hb_buffer_add_utf<hb_utf16_t> (buffer, text, text_length, item_offset, item_length);
}

/**
 * hb_buffer_add_utf32:
 * @buffer: an #hb_buffer_t.
 * @text: (array length=text_length): an array of UTF-32 characters to append.
 * @text_length: the length of the @text, or -1 if it is %NULL terminated.
 * @item_offset: the offset of the first character to add to the @buffer.
 * @item_length: the number of characters to add to the @buffer, or -1 for the
 *               end of @text (assuming it is %NULL terminated).
 *
 * See hb_buffer_add_codepoints().
 *
 * Replaces invalid UTF-32 characters with the @buffer replacement code point,
 * see hb_buffer_set_replacement_codepoint().
 *
 * Since: 0.9.2
 **/
void
hb_buffer_add_utf32 (hb_buffer_t    *buffer,
		     const uint32_t *text,
		     int             text_length,
		     unsigned int    item_offset,
		     int             item_length)
{
  hb_buffer_add_utf<hb_utf32_t> (buffer, text, text_length, item_offset, item_length);
}

/**
 * hb_buffer_add_latin1:
 * @buffer: an #hb_buffer_t.
 * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8
 *               characters to append.
 * @text_length: the length of the @text, or -1 if it is %NULL terminated.
 * @item_offset: the offset of the first character to add to the @buffer.
 * @item_length: the number of characters to add to the @buffer, or -1 for the
 *               end of @text (assuming it is %NULL terminated).
 *
 * Similar to hb_buffer_add_codepoints(), but allows only access to first 256
 * Unicode code points that can fit in 8-bit strings.
 *
 * <note>Has nothing to do with non-Unicode Latin-1 encoding.</note>
 *
 * Since: 0.9.39
 **/
void
hb_buffer_add_latin1 (hb_buffer_t   *buffer,
		      const uint8_t *text,
		      int            text_length,
		      unsigned int   item_offset,
		      int            item_length)
{
  hb_buffer_add_utf<hb_latin1_t> (buffer, text, text_length, item_offset, item_length);
}

/**
 * hb_buffer_add_codepoints:
 * @buffer: a #hb_buffer_t to append characters to.
 * @text: (array length=text_length): an array of Unicode code points to append.
 * @text_length: the length of the @text, or -1 if it is %NULL terminated.
 * @item_offset: the offset of the first code point to add to the @buffer.
 * @item_length: the number of code points to add to the @buffer, or -1 for the
 *               end of @text (assuming it is %NULL terminated).
 *
 * Appends characters from @text array to @buffer. The @item_offset is the
 * position of the first character from @text that will be appended, and
 * @item_length is the number of character. When shaping part of a larger text
 * (e.g. a run of text from a paragraph), instead of passing just the substring
 * corresponding to the run, it is preferable to pass the whole
 * paragraph and specify the run start and length as @item_offset and
 * @item_length, respectively, to give HarfBuzz the full context to be able,
 * for example, to do cross-run Arabic shaping or properly handle combining
 * marks at stat of run.
 *
 * This function does not check the validity of @text, it is up to the caller
 * to ensure it contains a valid Unicode code points.
 *
 * Since: 0.9.31
 **/
void
hb_buffer_add_codepoints (hb_buffer_t          *buffer,
			  const hb_codepoint_t *text,
			  int                   text_length,
			  unsigned int          item_offset,
			  int                   item_length)
{
  hb_buffer_add_utf<hb_utf32_novalidate_t> (buffer, text, text_length, item_offset, item_length);
}

/**
 * hb_buffer_append:
 * @buffer: an #hb_buffer_t.
 * @source: source #hb_buffer_t.
 * @start: start index into source buffer to copy.  Use 0 to copy from start of buffer.
 * @end: end index into source buffer to copy.  Use @HB_FEATURE_GLOBAL_END to copy to end of buffer.
 *
 * Append (part of) contents of another buffer to this buffer.
 *
 * Since: 1.5.0
 **/
HB_EXTERN void
hb_buffer_append (hb_buffer_t *buffer,
		  hb_buffer_t *source,
		  unsigned int start,
		  unsigned int end)
{
  assert (!buffer->have_output && !source->have_output);
  assert (buffer->have_positions == source->have_positions ||
	  !buffer->len || !source->len);
  assert (buffer->content_type == source->content_type ||
	  !buffer->len || !source->len);

  if (end > source->len)
    end = source->len;
  if (start > end)
    start = end;
  if (start == end)
    return;

  if (!buffer->len)
    buffer->content_type = source->content_type;
  if (!buffer->have_positions && source->have_positions)
    buffer->clear_positions ();

  if (buffer->len + (end - start) < buffer->len) /* Overflows. */
  {
    buffer->successful = false;
    return;
  }

  unsigned int orig_len = buffer->len;
  hb_buffer_set_length (buffer, buffer->len + (end - start));
  if (unlikely (!buffer->successful))
    return;

  memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0]));
  if (buffer->have_positions)
    memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0]));
}

static int
compare_info_codepoint (const hb_glyph_info_t *pa,
			const hb_glyph_info_t *pb)
{
  return (int) pb->codepoint - (int) pa->codepoint;
}

static inline void
normalize_glyphs_cluster (hb_buffer_t *buffer,
			  unsigned int start,
			  unsigned int end,
			  bool backward)
{
  hb_glyph_position_t *pos = buffer->pos;

  /* Total cluster advance */
  hb_position_t total_x_advance = 0, total_y_advance = 0;
  for (unsigned int i = start; i < end; i++)
  {
    total_x_advance += pos[i].x_advance;
    total_y_advance += pos[i].y_advance;
  }

  hb_position_t x_advance = 0, y_advance = 0;
  for (unsigned int i = start; i < end; i++)
  {
    pos[i].x_offset += x_advance;
    pos[i].y_offset += y_advance;

    x_advance += pos[i].x_advance;
    y_advance += pos[i].y_advance;

    pos[i].x_advance = 0;
    pos[i].y_advance = 0;
  }

  if (backward)
  {
    /* Transfer all cluster advance to the last glyph. */
    pos[end - 1].x_advance = total_x_advance;
    pos[end - 1].y_advance = total_y_advance;

    hb_stable_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start);
  } else {
    /* Transfer all cluster advance to the first glyph. */
    pos[start].x_advance += total_x_advance;
    pos[start].y_advance += total_y_advance;
    for (unsigned int i = start + 1; i < end; i++) {
      pos[i].x_offset -= total_x_advance;
      pos[i].y_offset -= total_y_advance;
    }
    hb_stable_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1);
  }
}

/**
 * hb_buffer_normalize_glyphs:
 * @buffer: an #hb_buffer_t.
 *
 * Reorders a glyph buffer to have canonical in-cluster glyph order / position.
 * The resulting clusters should behave identical to pre-reordering clusters.
 *
 * <note>This has nothing to do with Unicode normalization.</note>
 *
 * Since: 0.9.2
 **/
void
hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
{
  assert (buffer->have_positions);
  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS ||
	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));

  bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);

  foreach_cluster (buffer, start, end)
    normalize_glyphs_cluster (buffer, start, end, backward);
}

void
hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *))
{
  assert (!have_positions);
  for (unsigned int i = start + 1; i < end; i++)
  {
    unsigned int j = i;
    while (j > start && compar (&info[j - 1], &info[i]) > 0)
      j--;
    if (i == j)
      continue;
    /* Move item i to occupy place for item j, shift what's in between. */
    merge_clusters (j, i + 1);
    {
      hb_glyph_info_t t = info[i];
      memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t));
      info[j] = t;
    }
  }
}

/*
 * Comparing buffers.
 */

/**
 * hb_buffer_diff:
 * @buffer: a buffer.
 * @reference: other buffer to compare to.
 * @dottedcircle_glyph: glyph id of U+25CC DOTTED CIRCLE, or (hb_codepont_t) -1.
 * @position_fuzz: allowed absolute difference in position values.
 *
 * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
 * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned.  This should be used by most
 * callers if just comparing two buffers is needed.
 *
 * Since: 1.5.0
 **/
hb_buffer_diff_flags_t
hb_buffer_diff (hb_buffer_t *buffer,
		hb_buffer_t *reference,
		hb_codepoint_t dottedcircle_glyph,
		unsigned int position_fuzz)
{
  if (buffer->content_type != reference->content_type && buffer->len && reference->len)
    return HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH;

  hb_buffer_diff_flags_t result = HB_BUFFER_DIFF_FLAG_EQUAL;
  bool contains = dottedcircle_glyph != (hb_codepoint_t) -1;

  unsigned int count = reference->len;

  if (buffer->len != count)
  {
    /*
     * we can't compare glyph-by-glyph, but we do want to know if there
     * are .notdef or dottedcircle glyphs present in the reference buffer
     */
    const hb_glyph_info_t *info = reference->info;
    unsigned int i;
    for (i = 0; i < count; i++)
    {
      if (contains && info[i].codepoint == dottedcircle_glyph)
	result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
      if (contains && info[i].codepoint == 0)
	result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
    }
    result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH;
    return hb_buffer_diff_flags_t (result);
  }

  if (!count)
    return hb_buffer_diff_flags_t (result);

  const hb_glyph_info_t *buf_info = buffer->info;
  const hb_glyph_info_t *ref_info = reference->info;
  for (unsigned int i = 0; i < count; i++)
  {
    if (buf_info->codepoint != ref_info->codepoint)
      result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH;
    if (buf_info->cluster != ref_info->cluster)
      result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH;
    if ((buf_info->mask & ~ref_info->mask & HB_GLYPH_FLAG_DEFINED))
      result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH;
    if (contains && ref_info->codepoint == dottedcircle_glyph)
      result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
    if (contains && ref_info->codepoint == 0)
      result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
    buf_info++;
    ref_info++;
  }

  if (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)
  {
    assert (buffer->have_positions);
    const hb_glyph_position_t *buf_pos = buffer->pos;
    const hb_glyph_position_t *ref_pos = reference->pos;
    for (unsigned int i = 0; i < count; i++)
    {
      if ((unsigned int) abs (buf_pos->x_advance - ref_pos->x_advance) > position_fuzz ||
	  (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz ||
	  (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz ||
	  (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz)
      {
	result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH;
	break;
      }
      buf_pos++;
      ref_pos++;
    }
  }

  return result;
}

/*
 * Debugging.
 */

#ifndef HB_NO_BUFFER_MESSAGE
/**
 * hb_buffer_set_message_func:
 * @buffer: an #hb_buffer_t.
 * @func: (closure user_data) (destroy destroy) (scope notified):
 * @user_data:
 * @destroy:
 *
 *
 *
 * Since: 1.1.3
 **/
void
hb_buffer_set_message_func (hb_buffer_t *buffer,
			    hb_buffer_message_func_t func,
			    void *user_data, hb_destroy_func_t destroy)
{
  if (buffer->message_destroy)
    buffer->message_destroy (buffer->message_data);

  if (func) {
    buffer->message_func = func;
    buffer->message_data = user_data;
    buffer->message_destroy = destroy;
  } else {
    buffer->message_func = nullptr;
    buffer->message_data = nullptr;
    buffer->message_destroy = nullptr;
  }
}
bool
hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap)
{
  char buf[100];
  vsnprintf (buf, sizeof (buf), fmt, ap);
  return (bool) this->message_func (this, font, buf, this->message_data);
}
#endif


text:
/*
 * Copyright © 2007,2008,2009  Red Hat, Inc.
 * Copyright © 2018,2019,2020  Ebrahim Byagowi
 * Copyright © 2018  Khaled Hosny
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Red Hat Author(s): Behdad Esfahbod
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "hb.h"
#include "hb-ot.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef HB_NO_OPEN
#define hb_blob_create_from_file(x)  hb_blob_get_empty ()
#endif

#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) && defined(HB_EXPERIMENTAL_API)
static void
svg_dump (hb_face_t *face, unsigned face_index)
{
  unsigned glyph_count = hb_face_get_glyph_count (face);

  for (unsigned glyph_id = 0; glyph_id < glyph_count; ++glyph_id)
  {
    hb_blob_t *blob = hb_ot_color_glyph_reference_svg (face, glyph_id);

    if (hb_blob_get_length (blob) == 0) continue;

    unsigned length;
    const char *data = hb_blob_get_data (blob, &length);

    char output_path[255];
    sprintf (output_path, "out/svg-%u-%u.svg%s",
	     glyph_id,
	     face_index,
	     // append "z" if the content is gzipped, https://stackoverflow.com/a/6059405
	     (length > 2 && (data[0] == '\x1F') && (data[1] == '\x8B')) ? "z" : "");

    FILE *f = fopen (output_path, "wb");
    fwrite (data, 1, length, f);
    fclose (f);

    hb_blob_destroy (blob);
  }
}

/* _png API is so easy to use unlike the below code, don't get confused */
static void
png_dump (hb_face_t *face, unsigned face_index)
{
  unsigned glyph_count = hb_face_get_glyph_count (face);
  hb_font_t *font = hb_font_create (face);

  /* scans the font for strikes */
  unsigned sample_glyph_id;
  /* we don't care about different strikes for different glyphs at this point */
  for (sample_glyph_id = 0; sample_glyph_id < glyph_count; ++sample_glyph_id)
  {
    hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, sample_glyph_id);
    unsigned blob_length = hb_blob_get_length (blob);
    hb_blob_destroy (blob);
    if (blob_length != 0)
      break;
  }

  unsigned upem = hb_face_get_upem (face);
  unsigned blob_length = 0;
  unsigned strike = 0;
  for (unsigned ppem = 1; ppem < upem; ++ppem)
  {
    hb_font_set_ppem (font, ppem, ppem);
    hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, sample_glyph_id);
    unsigned new_blob_length = hb_blob_get_length (blob);
    hb_blob_destroy (blob);
    if (new_blob_length != blob_length)
    {
      for (unsigned glyph_id = 0; glyph_id < glyph_count; ++glyph_id)
      {
	hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, glyph_id);

	if (hb_blob_get_length (blob) == 0) continue;

	unsigned length;
	const char *data = hb_blob_get_data (blob, &length);

	char output_path[255];
	sprintf (output_path, "out/png-%u-%u-%u.png", glyph_id, strike, face_index);

	FILE *f = fopen (output_path, "wb");
	fwrite (data, 1, length, f);
	fclose (f);

	hb_blob_destroy (blob);
      }

      strike++;
      blob_length = new_blob_length;
    }
  }

  hb_font_destroy (font);
}

struct user_data_t
{
  FILE *f;
  hb_position_t ascender;
};

static void
move_to (hb_position_t to_x, hb_position_t to_y, user_data_t &user_data)
{
  fprintf (user_data.f, "M%d,%d", to_x, user_data.ascender - to_y);
}

static void
line_to (hb_position_t to_x, hb_position_t to_y, user_data_t &user_data)
{
  fprintf (user_data.f, "L%d,%d", to_x, user_data.ascender - to_y);
}

static void
quadratic_to (hb_position_t control_x, hb_position_t control_y,
	      hb_position_t to_x, hb_position_t to_y,
	      user_data_t &user_data)
{
  fprintf (user_data.f, "Q%d,%d %d,%d", control_x, user_data.ascender - control_y,
					to_x, user_data.ascender - to_y);
}

static void
cubic_to (hb_position_t control1_x, hb_position_t control1_y,
	  hb_position_t control2_x, hb_position_t control2_y,
	  hb_position_t to_x, hb_position_t to_y,
	  user_data_t &user_data)
{
  fprintf (user_data.f, "C%d,%d %d,%d %d,%d", control1_x, user_data.ascender - control1_y,
					       control2_x, user_data.ascender - control2_y,
					       to_x, user_data.ascender - to_y);
}

static void
close_path (user_data_t &user_data)
{
  fprintf (user_data.f, "Z");
}

static void
layered_glyph_dump (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index)
{
  hb_face_t *face = hb_font_get_face (font);
  unsigned palette_count = hb_ot_color_palette_get_count (face);
  for (unsigned palette = 0; palette < palette_count; ++palette)
  {
    unsigned num_colors = hb_ot_color_palette_get_colors (face, palette, 0, nullptr, nullptr);
    if (!num_colors) continue;

    hb_color_t *colors = (hb_color_t*) calloc (num_colors, sizeof (hb_color_t));
    hb_ot_color_palette_get_colors (face, palette, 0, &num_colors, colors);
    if (!num_colors)
    {
      free (colors);
      continue;
    }

    unsigned num_glyphs = hb_face_get_glyph_count (face);
    for (hb_codepoint_t gid = 0; gid < num_glyphs; ++gid)
    {
      unsigned num_layers = hb_ot_color_glyph_get_layers (face, gid, 0, nullptr, nullptr);
      if (!num_layers) continue;

      hb_ot_color_layer_t *layers = (hb_ot_color_layer_t*) malloc (num_layers * sizeof (hb_ot_color_layer_t));

      hb_ot_color_glyph_get_layers (face, gid, 0, &num_layers, layers);
      if (num_layers)
      {
	hb_font_extents_t font_extents;
	hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents);
	hb_glyph_extents_t extents = {0};
	if (!hb_font_get_glyph_extents (font, gid, &extents))
	{
	  printf ("Skip gid: %d\n", gid);
	  continue;
	}

	char output_path[255];
	sprintf (output_path, "out/colr-%u-%u-%u.svg", gid, palette, face_index);
	FILE *f = fopen (output_path, "wb");
	fprintf (f, "<svg xmlns=\"http://www.w3.org/2000/svg\""
		    " viewBox=\"%d %d %d %d\">\n",
		    extents.x_bearing, 0,
		    extents.x_bearing + extents.width, -extents.height);
	user_data_t user_data;
	user_data.ascender = extents.y_bearing;
	user_data.f = f;

	for (unsigned layer = 0; layer < num_layers; ++layer)
	{
	  hb_color_t color = 0x000000FF;
	  if (layers[layer].color_index != 0xFFFF)
	    color = colors[layers[layer].color_index];
	  fprintf (f, "<path fill=\"#%02X%02X%02X\" ",
		   hb_color_get_red (color), hb_color_get_green (color), hb_color_get_green (color));
	  if (hb_color_get_alpha (color) != 255)
	    fprintf (f, "fill-opacity=\"%.3f\"", (double) hb_color_get_alpha (color) / 255.);
	  fprintf (f, "d=\"");
	  if (!hb_font_draw_glyph (font, layers[layer].glyph, funcs, &user_data))
	    printf ("Failed to decompose layer %d while %d\n", layers[layer].glyph, gid);
	  fprintf (f, "\"/>\n");
	}

	fprintf (f, "</svg>");
	fclose (f);
      }
      free (layers);
    }

    free (colors);
  }
}

static void
dump_glyphs (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index)
{
  unsigned num_glyphs = hb_face_get_glyph_count (hb_font_get_face (font));
  for (unsigned gid = 0; gid < num_glyphs; ++gid)
  {
    hb_font_extents_t font_extents;
    hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents);
    hb_glyph_extents_t extents = {0};
    if (!hb_font_get_glyph_extents (font, gid, &extents))
    {
      printf ("Skip gid: %d\n", gid);
      continue;
    }

    char output_path[255];
    sprintf (output_path, "out/%u-%u.svg", face_index, gid);
    FILE *f = fopen (output_path, "wb");
    fprintf (f, "<svg xmlns=\"http://www.w3.org/2000/svg\""
		" viewBox=\"%d %d %d %d\"><path d=\"",
		extents.x_bearing, 0,
		extents.x_bearing + extents.width, font_extents.ascender - font_extents.descender);
    user_data_t user_data;
    user_data.ascender = font_extents.ascender;
    user_data.f = f;
    if (!hb_font_draw_glyph (font, gid, funcs, &user_data))
      printf ("Failed to decompose gid: %d\n", gid);
    fprintf (f, "\"/></svg>");
    fclose (f);
  }
}

static void
dump_glyphs (hb_blob_t *blob, const char *font_name)
{
  FILE *font_name_file = fopen ("out/.dumped_font_name", "r");
  if (font_name_file)
  {
    fprintf (stderr, "Purge or rename ./out folder if you like to run a glyph dump,\n"
		     "run it like `rm -rf out && mkdir out && src/main font-file.ttf`\n");
    return;
  }

  font_name_file = fopen ("out/.dumped_font_name", "w");
  if (!font_name_file)
  {
    fprintf (stderr, "./out is not accessible as a folder, create it please\n");
    return;
  }
  fwrite (font_name, 1, strlen (font_name), font_name_file);
  fclose (font_name_file);

  hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
  hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to);
  hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to);
  hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) quadratic_to);
  hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to);
  hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path);

  unsigned num_faces = hb_face_count (blob);
  for (unsigned face_index = 0; face_index < num_faces; ++face_index)
  {
    hb_face_t *face = hb_face_create (blob, face_index);
    hb_font_t *font = hb_font_create (face);

    if (hb_ot_color_has_png (face))
      printf ("Dumping png (CBDT/sbix)...\n");
    png_dump (face, face_index);

    if (hb_ot_color_has_svg (face))
      printf ("Dumping svg (SVG )...\n");
    svg_dump (face, face_index);

    if (hb_ot_color_has_layers (face) && hb_ot_color_has_palettes (face))
      printf ("Dumping layered color glyphs (COLR/CPAL)...\n");
    layered_glyph_dump (font, funcs, face_index);

    dump_glyphs (font, funcs, face_index);

    hb_font_destroy (font);
    hb_face_destroy (face);
  }

  hb_draw_funcs_destroy (funcs);
}
#endif

#ifndef MAIN_CC_NO_PRIVATE_API
/* Only this part of this mini app uses private API */
#include "hb-static.cc"
#include "hb-open-file.hh"
#include "hb-ot-layout-gdef-table.hh"
#include "hb-ot-layout-gsubgpos.hh"

using namespace OT;

static void
print_layout_info_using_private_api (hb_blob_t *blob)
{
  const char *font_data = hb_blob_get_data (blob, nullptr);
  hb_blob_t *font_blob = hb_sanitize_context_t ().sanitize_blob<OpenTypeFontFile> (blob);
  const OpenTypeFontFile* sanitized = font_blob->as<OpenTypeFontFile> ();
  if (!font_blob->data)
  {
    printf ("Sanitization of the file wasn't successful. Exit");
    exit (1);
  }
  const OpenTypeFontFile& ot = *sanitized;

  switch (ot.get_tag ())
  {
  case OpenTypeFontFile::TrueTypeTag:
    printf ("OpenType font with TrueType outlines\n");
    break;
  case OpenTypeFontFile::CFFTag:
    printf ("OpenType font with CFF (Type1) outlines\n");
    break;
  case OpenTypeFontFile::TTCTag:
    printf ("TrueType Collection of OpenType fonts\n");
    break;
  case OpenTypeFontFile::TrueTag:
    printf ("Obsolete Apple TrueType font\n");
    break;
  case OpenTypeFontFile::Typ1Tag:
    printf ("Obsolete Apple Type1 font in SFNT container\n");
    break;
  case OpenTypeFontFile::DFontTag:
    printf ("DFont Mac Resource Fork\n");
    break;
  default:
    printf ("Unknown font format\n");
    break;
  }

  unsigned num_faces = hb_face_count (blob);
  printf ("%d font(s) found in file\n", num_faces);
  for (unsigned n_font = 0; n_font < num_faces; ++n_font)
  {
    const OpenTypeFontFace &font = ot.get_face (n_font);
    printf ("Font %d of %d:\n", n_font, num_faces);

    unsigned num_tables = font.get_table_count ();
    printf ("  %d table(s) found in font\n", num_tables);
    for (unsigned n_table = 0; n_table < num_tables; ++n_table)
    {
      const OpenTypeTable &table = font.get_table (n_table);
      printf ("  Table %2d of %2d: %.4s (0x%08x+0x%08x)\n", n_table, num_tables,
	      (const char *) table.tag,
	      (unsigned) table.offset,
	      (unsigned) table.length);

      switch (table.tag)
      {

      case HB_OT_TAG_GSUB:
      case HB_OT_TAG_GPOS:
	{

	const GSUBGPOS &g = *reinterpret_cast<const GSUBGPOS *> (font_data + table.offset);

	unsigned num_scripts = g.get_script_count ();
	printf ("    %d script(s) found in table\n", num_scripts);
	for (unsigned n_script = 0; n_script < num_scripts; ++n_script)
	{
	  const Script &script = g.get_script (n_script);
	  printf ("    Script %2d of %2d: %.4s\n", n_script, num_scripts,
		  (const char *) g.get_script_tag (n_script));

	  if (!script.has_default_lang_sys ())
	    printf ("      No default language system\n");
	  int num_langsys = script.get_lang_sys_count ();
	  printf ("      %d language system(s) found in script\n", num_langsys);
	  for (int n_langsys = script.has_default_lang_sys () ? -1 : 0; n_langsys < num_langsys; ++n_langsys)
	  {
	    const LangSys &langsys = n_langsys == -1
				   ? script.get_default_lang_sys ()
				   : script.get_lang_sys (n_langsys);
	    if (n_langsys == -1)
	      printf ("      Default Language System\n");
	    else
	      printf ("      Language System %2d of %2d: %.4s\n", n_langsys, num_langsys,
		      (const char *) script.get_lang_sys_tag (n_langsys));
	    if (!langsys.has_required_feature ())
	      printf ("        No required feature\n");
	    else
	      printf ("        Required feature index: %d\n",
		      langsys.get_required_feature_index ());

	    unsigned num_features = langsys.get_feature_count ();
	    printf ("        %d feature(s) found in language system\n", num_features);
	    for (unsigned n_feature = 0; n_feature < num_features; ++n_feature)
	    {
	      printf ("        Feature index %2d of %2d: %d\n", n_feature, num_features,
		      langsys.get_feature_index (n_feature));
	    }
	  }
	}

	unsigned num_features = g.get_feature_count ();
	printf ("    %d feature(s) found in table\n", num_features);
	for (unsigned n_feature = 0; n_feature < num_features; ++n_feature)
	{
	  const Feature &feature = g.get_feature (n_feature);
	  unsigned num_lookups = feature.get_lookup_count ();
	  printf ("    Feature %2d of %2d: %c%c%c%c\n", n_feature, num_features,
		  HB_UNTAG (g.get_feature_tag (n_feature)));

	  printf ("        %d lookup(s) found in feature\n", num_lookups);
	  for (unsigned n_lookup = 0; n_lookup < num_lookups; ++n_lookup) {
	    printf ("        Lookup index %2d of %2d: %d\n", n_lookup, num_lookups,
		    feature.get_lookup_index (n_lookup));
	  }
	}

	unsigned num_lookups = g.get_lookup_count ();
	printf ("    %d lookup(s) found in table\n", num_lookups);
	for (unsigned n_lookup = 0; n_lookup < num_lookups; ++n_lookup)
	{
	  const Lookup &lookup = g.get_lookup (n_lookup);
	  printf ("    Lookup %2d of %2d: type %d, props 0x%04X\n", n_lookup, num_lookups,
		  lookup.get_type (), lookup.get_props ());
	}

	}
	break;

      case GDEF::tableTag:
	{

	const GDEF &gdef = *reinterpret_cast<const GDEF *> (font_data + table.offset);

	printf ("    Has %sglyph classes\n",
		  gdef.has_glyph_classes () ? "" : "no ");
	printf ("    Has %smark attachment types\n",
		  gdef.has_mark_attachment_types () ? "" : "no ");
	printf ("    Has %sattach points\n",
		  gdef.has_attach_points () ? "" : "no ");
	printf ("    Has %slig carets\n",
		  gdef.has_lig_carets () ? "" : "no ");
	printf ("    Has %smark sets\n",
		  gdef.has_mark_sets () ? "" : "no ");
	break;
	}
      }
    }
  }
}
/* end of private API use */
#endif

int
main (int argc, char **argv)
{
  if (argc != 2)
  {
    fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
    exit (1);
  }

  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
  printf ("Opened font file %s: %d bytes long\n", argv[1], hb_blob_get_length (blob));
#ifndef MAIN_CC_NO_PRIVATE_API
  print_layout_info_using_private_api (blob);
#endif
#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) && defined(HB_EXPERIMENTAL_API)
  dump_glyphs (blob, argv[1]);
#endif
  hb_blob_destroy (blob);

  return 0;
}

repo_language: C

mime_type: text/x-c


text:
prefix=%prefix%
exec_prefix=%exec_prefix%
libdir=%libdir%
includedir=%includedir%

Name: harfbuzz
Description: HarfBuzz text shaping library ICU integration
Version: %VERSION%

Requires: harfbuzz
Requires.private: icu-uc
Libs: -L${libdir} -lharfbuzz-icu
Cflags: -I${includedir}/harfbuzz


text:
/*
 * Copyright © 2011  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_H_IN
#error "Include <hb.h> instead."
#endif

#ifndef HB_VERSION_H
#define HB_VERSION_H

#include "hb-common.h"

HB_BEGIN_DECLS

#define HB_VERSION_MAJOR 2
#define HB_VERSION_MINOR 6
#define HB_VERSION_MICRO 6

#define HB_VERSION_STRING "2.6.6"

#define HB_VERSION_ATLEAST(major,minor,micro) \
	((major)*10000+(minor)*100+(micro) <= \
	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)

HB_EXTERN void
hb_version (unsigned int *major,
	    unsigned int *minor,
	    unsigned int *micro);

HB_EXTERN const char *
hb_version_string (void);

HB_EXTERN hb_bool_t
hb_version_atleast (unsigned int major,
		    unsigned int minor,
		    unsigned int micro);

HB_END_DECLS

#endif /* HB_VERSION_H */


text:
/*
 * Copyright © 2018 Adobe Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Adobe Author(s): Michiharu Ariza
 */

#include "hb.hh"

#ifndef HB_NO_CFF

#include "hb-draw.hh"
#include "hb-algs.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-cff1-interp-cs.hh"

using namespace CFF;

struct sid_to_gid_t
{
  uint16_t  sid;
  uint8_t   gid;

  int cmp (uint16_t a) const
  {
    if (a == sid) return 0;
    return (a < sid) ? -1 : 1;
  }
};

/* SID to code */
static const uint8_t standard_encoding_to_code [] =
{
    0,   32,   33,   34,   35,   36,   37,   38,  39,   40,   41,   42,   43,   44,   45,   46,
   47,   48,   49,   50,   51,   52,   53,   54,  55,   56,   57,   58,   59,   60,   61,   62,
   63,   64,   65,   66,   67,   68,   69,   70,  71,   72,   73,   74,   75,   76,   77,   78,
   79,   80,   81,   82,   83,   84,   85,   86,  87,   88,   89,   90,   91,   92,   93,   94,
   95,   96,   97,   98,   99,  100,  101,  102, 103,  104,  105,  106,  107,  108,  109,  110,
  111,  112,  113,  114,  115,  116,  117,  118, 119,  120,  121,  122,  123,  124,  125,  126,
  161,  162,  163,  164,  165,  166,  167,  168, 169,  170,  171,  172,  173,  174,  175,  177,
  178,  179,  180,  182,  183,  184,  185,  186, 187,  188,  189,  191,  193,  194,  195,  196,
  197,  198,  199,  200,  202,  203,  205,  206, 207,  208,  225,  227,  232,  233,  234,  235,
  241,  245,  248,  249,  250,  251
};

/* SID to code */
static const uint8_t expert_encoding_to_code [] =
{
    0,   32,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,   44,   45,   46,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,   58,   59,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,   47,    0,    0,    0,    0,    0,    0,    0,    0,    0,   87,   88,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,  201,    0,    0,    0,    0,  189,    0,    0,  188,    0,
    0,    0,    0,  190,  202,    0,    0,    0,    0,  203,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,   33,   34,   36,   37,   38,   39,   40,   41,   42,   43,   48,
   49,   50,   51,   52,   53,   54,   55,   56,   57,   60,   61,   62,   63,   65,   66,   67,
   68,   69,   73,   76,   77,   78,   79,   82,   83,   84,   86,   89,   90,   91,   93,   94,
   95,   96,   97,   98,   99,  100,  101,  102,  103,  104,  105,  106,  107,  108,  109,  110,
  111,  112,  113,  114,  115,  116,  117,  118,  119,  120,  121,  122,  123,  124,  125,  126,
  161,  162,  163,  166,  167,  168,  169,  170,  172,  175,  178,  179,  182,  183,  184,  191,
  192,  193,  194,  195,  196,  197,  200,  204,  205,  206,  207,  208,  209,  210,  211,  212,
  213,  214,  215,  216,  217,  218,  219,  220,  221,  222,  223,  224,  225,  226,  227,  228,
  229,  230,  231,  232,  233,  234,  235,  236,  237,  238,  239,  240,  241,  242,  243,  244,
  245,  246,  247,  248,  249,  250,  251,  252,  253,  254,  255
};

/* glyph ID to SID */
static const uint16_t expert_charset_to_sid [] =
{
    0,    1,  229,  230,  231,  232,  233,  234,  235,  236,  237,  238,   13,   14,   15,   99,
  239,  240,  241,  242,  243,  244,  245,  246,  247,  248,   27,   28,  249,  250,  251,  252,
  253,  254,  255,  256,  257,  258,  259,  260,  261,  262,  263,  264,  265,  266,  109,  110,
  267,  268,  269,  270,  271,  272,  273,  274,  275,  276,  277,  278,  279,  280,  281,  282,
  283,  284,  285,  286,  287,  288,  289,  290,  291,  292,  293,  294,  295,  296,  297,  298,
  299,  300,  301,  302,  303,  304,  305,  306,  307,  308,  309,  310,  311,  312,  313,  314,
  315,  316,  317,  318,  158,  155,  163,  319,  320,  321,  322,  323,  324,  325,  326,  150,
  164,  169,  327,  328,  329,  330,  331,  332,  333,  334,  335,  336,  337,  338,  339,  340,
  341,  342,  343,  344,  345,  346,  347,  348,  349,  350,  351,  352,  353,  354,  355,  356,
  357,  358,  359,  360,  361,  362,  363,  364,  365,  366,  367,  368,  369,  370,  371,  372,
  373,  374,  375,  376,  377,  378
};

/* glyph ID to SID */
static const uint16_t expert_subset_charset_to_sid [] =
{
    0,    1,  231,  232,  235,  236,  237,  238,   13,   14,   15,   99,  239,  240,  241,  242,
  243,  244,  245,  246,  247,  248,   27,   28,  249,  250,  251,  253,  254,  255,  256,  257,
  258,  259,  260,  261,  262,  263,  264,  265,  266,  109,  110,  267,  268,  269,  270,  272,
  300,  301,  302,  305,  314,  315,  158,  155,  163,  320,  321,  322,  323,  324,  325,  326,
  150,  164,  169,  327,  328,  329,  330,  331,  332,  333,  334,  335,  336,  337,  338,  339,
  340,  341,  342,  343,  344,  345,  346
};

/* SID to glyph ID */
static const sid_to_gid_t expert_charset_sid_to_gid [] =
{
    { 1, 1 },     { 13, 12 },   { 14, 13 },   { 15, 14 },
    { 27, 26 },   { 28, 27 },   { 99, 15 },   { 109, 46 },
    { 110, 47 },  { 150, 111 }, { 155, 101 }, { 158, 100 },
    { 163, 102 }, { 164, 112 }, { 169, 113 }, { 229, 2 },
    { 230, 3 },   { 231, 4 },   { 232, 5 },   { 233, 6 },
    { 234, 7 },   { 235, 8 },   { 236, 9 },   { 237, 10 },
    { 238, 11 },  { 239, 16 },  { 240, 17 },  { 241, 18 },
    { 242, 19 },  { 243, 20 },  { 244, 21 },  { 245, 22 },
    { 246, 23 },  { 247, 24 },  { 248, 25 },  { 249, 28 },
    { 250, 29 },  { 251, 30 },  { 252, 31 },  { 253, 32 },
    { 254, 33 },  { 255, 34 },  { 256, 35 },  { 257, 36 },
    { 258, 37 },  { 259, 38 },  { 260, 39 },  { 261, 40 },
    { 262, 41 },  { 263, 42 },  { 264, 43 },  { 265, 44 },
    { 266, 45 },  { 267, 48 },  { 268, 49 },  { 269, 50 },
    { 270, 51 },  { 271, 52 },  { 272, 53 },  { 273, 54 },
    { 274, 55 },  { 275, 56 },  { 276, 57 },  { 277, 58 },
    { 278, 59 },  { 279, 60 },  { 280, 61 },  { 281, 62 },
    { 282, 63 },  { 283, 64 },  { 284, 65 },  { 285, 66 },
    { 286, 67 },  { 287, 68 },  { 288, 69 },  { 289, 70 },
    { 290, 71 },  { 291, 72 },  { 292, 73 },  { 293, 74 },
    { 294, 75 },  { 295, 76 },  { 296, 77 },  { 297, 78 },
    { 298, 79 },  { 299, 80 },  { 300, 81 },  { 301, 82 },
    { 302, 83 },  { 303, 84 },  { 304, 85 },  { 305, 86 },
    { 306, 87 },  { 307, 88 },  { 308, 89 },  { 309, 90 },
    { 310, 91 },  { 311, 92 },  { 312, 93 },  { 313, 94 },
    { 314, 95 },  { 315, 96 },  { 316, 97 },  { 317, 98 },
    { 318, 99 },  { 319, 103 }, { 320, 104 }, { 321, 105 },
    { 322, 106 }, { 323, 107 }, { 324, 108 }, { 325, 109 },
    { 326, 110 }, { 327, 114 }, { 328, 115 }, { 329, 116 },
    { 330, 117 }, { 331, 118 }, { 332, 119 }, { 333, 120 },
    { 334, 121 }, { 335, 122 }, { 336, 123 }, { 337, 124 },
    { 338, 125 }, { 339, 126 }, { 340, 127 }, { 341, 128 },
    { 342, 129 }, { 343, 130 }, { 344, 131 }, { 345, 132 },
    { 346, 133 }, { 347, 134 }, { 348, 135 }, { 349, 136 },
    { 350, 137 }, { 351, 138 }, { 352, 139 }, { 353, 140 },
    { 354, 141 }, { 355, 142 }, { 356, 143 }, { 357, 144 },
    { 358, 145 }, { 359, 146 }, { 360, 147 }, { 361, 148 },
    { 362, 149 }, { 363, 150 }, { 364, 151 }, { 365, 152 },
    { 366, 153 }, { 367, 154 }, { 368, 155 }, { 369, 156 },
    { 370, 157 }, { 371, 158 }, { 372, 159 }, { 373, 160 },
    { 374, 161 }, { 375, 162 }, { 376, 163 }, { 377, 164 },
    { 378, 165 }
};

/* SID to glyph ID */
static const sid_to_gid_t expert_subset_charset_sid_to_gid [] =
{
  { 1, 1 },       { 13, 8 },      { 14, 9 },      { 15, 10 },
  { 27, 22 },     { 28, 23 },     { 99, 11 },     { 109, 41 },
  { 110, 42 },    { 150, 64 },    { 155, 55 },    { 158, 54 },
  { 163, 56 },    { 164, 65 },    { 169, 66 },    { 231, 2 },
  { 232, 3 },     { 235, 4 },     { 236, 5 },     { 237, 6 },
  { 238, 7 },     { 239, 12 },    { 240, 13 },    { 241, 14 },
  { 242, 15 },    { 243, 16 },    { 244, 17 },    { 245, 18 },
  { 246, 19 },    { 247, 20 },    { 248, 21 },    { 249, 24 },
  { 250, 25 },    { 251, 26 },    { 253, 27 },    { 254, 28 },
  { 255, 29 },    { 256, 30 },    { 257, 31 },    { 258, 32 },
  { 259, 33 },    { 260, 34 },    { 261, 35 },    { 262, 36 },
  { 263, 37 },    { 264, 38 },    { 265, 39 },    { 266, 40 },
  { 267, 43 },    { 268, 44 },    { 269, 45 },    { 270, 46 },
  { 272, 47 },    { 300, 48 },    { 301, 49 },    { 302, 50 },
  { 305, 51 },    { 314, 52 },    { 315, 53 },    { 320, 57 },
  { 321, 58 },    { 322, 59 },    { 323, 60 },    { 324, 61 },
  { 325, 62 },    { 326, 63 },    { 327, 67 },    { 328, 68 },
  { 329, 69 },    { 330, 70 },    { 331, 71 },    { 332, 72 },
  { 333, 73 },    { 334, 74 },    { 335, 75 },    { 336, 76 },
  { 337, 77 },    { 338, 78 },    { 339, 79 },    { 340, 80 },
  { 341, 81 },    { 342, 82 },    { 343, 83 },    { 344, 84 },
  { 345, 85 },    { 346, 86 }
};

/* code to SID */
static const uint8_t standard_encoding_to_sid [] =
{
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,   15,   16,
    17,  18,   19,   20,   21,   22,   23,   24,   25,   26,   27,   28,   29,   30,   31,   32,
    33,  34,   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,   45,   46,   47,   48,
    49,  50,   51,   52,   53,   54,   55,   56,   57,   58,   59,   60,   61,   62,   63,   64,
    65,  66,   67,   68,   69,   70,   71,   72,   73,   74,   75,   76,   77,   78,   79,   80,
    81,  82,   83,   84,   85,   86,   87,   88,   89,   90,   91,   92,   93,   94,   95,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,   96,   97,   98,   99,  100,  101,  102,  103,  104,  105,  106,  107,  108,  109,  110,
    0,  111,  112,  113,  114,    0,  115,  116,  117,  118,  119,  120,  121,  122,    0,  123,
    0,  124,  125,  126,  127,  128,  129,  130,  131,    0,  132,  133,    0,  134,  135,  136,
  137,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,   138,   0,  139,    0,    0,    0,    0,  140,  141,  142,  143,    0,    0,    0,    0,
    0,   144,   0,    0,    0,  145,    0,    0,  146,  147,  148,  149,    0,    0,    0,    0
};

hb_codepoint_t OT::cff1::lookup_standard_encoding_for_code (hb_codepoint_t sid)
{
  if (sid < ARRAY_LENGTH (standard_encoding_to_code))
    return (hb_codepoint_t)standard_encoding_to_code[sid];
  else
    return 0;
}

hb_codepoint_t OT::cff1::lookup_expert_encoding_for_code (hb_codepoint_t sid)
{
  if (sid < ARRAY_LENGTH (expert_encoding_to_code))
    return (hb_codepoint_t)expert_encoding_to_code[sid];
  else
    return 0;
}

hb_codepoint_t OT::cff1::lookup_expert_charset_for_sid (hb_codepoint_t glyph)
{
  if (glyph < ARRAY_LENGTH (expert_charset_to_sid))
    return (hb_codepoint_t)expert_charset_to_sid[glyph];
  else
    return 0;
}

hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph)
{
  if (glyph < ARRAY_LENGTH (expert_subset_charset_to_sid))
    return (hb_codepoint_t)expert_subset_charset_to_sid[glyph];
  else
    return 0;
}

hb_codepoint_t OT::cff1::lookup_expert_charset_for_glyph (hb_codepoint_t sid)
{
  const auto *pair = hb_sorted_array (expert_charset_sid_to_gid).bsearch (sid);
  return pair ? pair->gid : 0;
}

hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid)
{
  const auto *pair = hb_sorted_array (expert_subset_charset_sid_to_gid).bsearch (sid);
  return pair ? pair->gid : 0;
}

hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code)
{
  if (code < ARRAY_LENGTH (standard_encoding_to_sid))
    return (hb_codepoint_t)standard_encoding_to_sid[code];
  else
    return CFF_UNDEF_SID;
}

struct bounds_t
{
  void init ()
  {
    min.set_int (INT_MAX, INT_MAX);
    max.set_int (INT_MIN, INT_MIN);
  }

  void update (const point_t &pt)
  {
    if (pt.x < min.x) min.x = pt.x;
    if (pt.x > max.x) max.x = pt.x;
    if (pt.y < min.y) min.y = pt.y;
    if (pt.y > max.y) max.y = pt.y;
  }

  void merge (const bounds_t &b)
  {
    if (empty ())
      *this = b;
    else if (!b.empty ())
    {
      if (b.min.x < min.x) min.x = b.min.x;
      if (b.max.x > max.x) max.x = b.max.x;
      if (b.min.y < min.y) min.y = b.min.y;
      if (b.max.y > max.y) max.y = b.max.y;
    }
  }

  void offset (const point_t &delta)
  {
    if (!empty ())
    {
      min.move (delta);
      max.move (delta);
    }
  }

  bool empty () const { return (min.x >= max.x) || (min.y >= max.y); }

  point_t min;
  point_t max;
};

struct cff1_extents_param_t
{
  void init (const OT::cff1::accelerator_t *_cff)
  {
    path_open = false;
    cff = _cff;
    bounds.init ();
  }

  void start_path   ()       { path_open = true; }
  void end_path     ()       { path_open = false; }
  bool is_path_open () const { return path_open; }

  bool path_open;
  bounds_t bounds;

  const OT::cff1::accelerator_t *cff;
};

struct cff1_path_procs_extents_t : path_procs_t<cff1_path_procs_extents_t, cff1_cs_interp_env_t, cff1_extents_param_t>
{
  static void moveto (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt)
  {
    param.end_path ();
    env.moveto (pt);
  }

  static void line (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1)
  {
    if (!param.is_path_open ())
    {
      param.start_path ();
      param.bounds.update (env.get_pt ());
    }
    env.moveto (pt1);
    param.bounds.update (env.get_pt ());
  }

  static void curve (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
  {
    if (!param.is_path_open ())
    {
      param.start_path ();
      param.bounds.update (env.get_pt ());
    }
    /* include control points */
    param.bounds.update (pt1);
    param.bounds.update (pt2);
    env.moveto (pt3);
    param.bounds.update (env.get_pt ());
  }
};

static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac=false);

struct cff1_cs_opset_extents_t : cff1_cs_opset_t<cff1_cs_opset_extents_t, cff1_extents_param_t, cff1_path_procs_extents_t>
{
  static void process_seac (cff1_cs_interp_env_t &env, cff1_extents_param_t& param)
  {
    unsigned int  n = env.argStack.get_count ();
    point_t delta;
    delta.x = env.argStack[n-4];
    delta.y = env.argStack[n-3];
    hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ());
    hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ());

    bounds_t  base_bounds, accent_bounds;
    if (likely (!env.in_seac && base && accent
	       && _get_bounds (param.cff, base, base_bounds, true)
	       && _get_bounds (param.cff, accent, accent_bounds, true)))
    {
      param.bounds.merge (base_bounds);
      accent_bounds.offset (delta);
      param.bounds.merge (accent_bounds);
    }
    else
      env.set_error ();
  }
};

bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac)
{
  bounds.init ();
  if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;

  unsigned int fd = cff->fdSelect->get_fd (glyph);
  cff1_cs_interpreter_t<cff1_cs_opset_extents_t, cff1_extents_param_t> interp;
  const byte_str_t str = (*cff->charStrings)[glyph];
  interp.env.init (str, *cff, fd);
  interp.env.set_in_seac (in_seac);
  cff1_extents_param_t  param;
  param.init (cff);
  if (unlikely (!interp.interpret (param))) return false;
  bounds = param.bounds;
  return true;
}

bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
{
#ifdef HB_NO_OT_FONT_CFF
  /* XXX Remove check when this code moves to .hh file. */
  return true;
#endif

  bounds_t bounds;

  if (!_get_bounds (this, glyph, bounds))
    return false;

  if (bounds.min.x >= bounds.max.x)
  {
    extents->width = 0;
    extents->x_bearing = 0;
  }
  else
  {
    extents->x_bearing = font->em_scalef_x (bounds.min.x.to_real ());
    extents->width = font->em_scalef_x (bounds.max.x.to_real () - bounds.min.x.to_real ());
  }
  if (bounds.min.y >= bounds.max.y)
  {
    extents->height = 0;
    extents->y_bearing = 0;
  }
  else
  {
    extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ());
    extents->height = font->em_scalef_y (bounds.min.y.to_real () - bounds.max.y.to_real ());
  }

  return true;
}

#ifdef HB_EXPERIMENTAL_API
struct cff1_path_param_t
{
  cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_,
		     draw_helper_t &draw_helper_, point_t *delta_)
  {
    draw_helper = &draw_helper_;
    cff = cff_;
    font = font_;
    delta = delta_;
  }

  void move_to (const point_t &p)
  {
    point_t point = p;
    if (delta) point.move (*delta);
    draw_helper->move_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ()));
  }

  void line_to (const point_t &p)
  {
    point_t point = p;
    if (delta) point.move (*delta);
    draw_helper->line_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ()));
  }

  void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3)
  {
    point_t point1 = p1, point2 = p2, point3 = p3;
    if (delta)
    {
      point1.move (*delta);
      point2.move (*delta);
      point3.move (*delta);
    }
    draw_helper->cubic_to (font->em_scalef_x (point1.x.to_real ()), font->em_scalef_y (point1.y.to_real ()),
			   font->em_scalef_x (point2.x.to_real ()), font->em_scalef_y (point2.y.to_real ()),
			   font->em_scalef_x (point3.x.to_real ()), font->em_scalef_y (point3.y.to_real ()));
  }

  void end_path () { draw_helper->end_path (); }

  hb_font_t *font;
  draw_helper_t *draw_helper;
  point_t *delta;

  const OT::cff1::accelerator_t *cff;
};

struct cff1_path_procs_path_t : path_procs_t<cff1_path_procs_path_t, cff1_cs_interp_env_t, cff1_path_param_t>
{
  static void moveto (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt)
  {
    param.move_to (pt);
    env.moveto (pt);
  }

  static void line (cff1_cs_interp_env_t &env, cff1_path_param_t &param, const point_t &pt1)
  {
    param.line_to (pt1);
    env.moveto (pt1);
  }

  static void curve (cff1_cs_interp_env_t &env, cff1_path_param_t &param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
  {
    param.cubic_to (pt1, pt2, pt3);
    env.moveto (pt3);
  }
};

static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph,
		       draw_helper_t &draw_helper, bool in_seac = false, point_t *delta = nullptr);

struct cff1_cs_opset_path_t : cff1_cs_opset_t<cff1_cs_opset_path_t, cff1_path_param_t, cff1_path_procs_path_t>
{
  static void process_seac (cff1_cs_interp_env_t &env, cff1_path_param_t& param)
  {
    /* End previous path */
    param.end_path ();

    unsigned int n = env.argStack.get_count ();
    point_t delta;
    delta.x = env.argStack[n-4];
    delta.y = env.argStack[n-3];
    hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ());
    hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ());

    if (unlikely (!(!env.in_seac && base && accent
		    && _get_path (param.cff, param.font, base, *param.draw_helper, true)
		    && _get_path (param.cff, param.font, accent, *param.draw_helper, true, &delta))))
      env.set_error ();
  }
};

bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph,
		draw_helper_t &draw_helper, bool in_seac, point_t *delta)
{
  if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;

  unsigned int fd = cff->fdSelect->get_fd (glyph);
  cff1_cs_interpreter_t<cff1_cs_opset_path_t, cff1_path_param_t> interp;
  const byte_str_t str = (*cff->charStrings)[glyph];
  interp.env.init (str, *cff, fd);
  interp.env.set_in_seac (in_seac);
  cff1_path_param_t param (cff, font, draw_helper, delta);
  if (unlikely (!interp.interpret (param))) return false;

  /* Let's end the path specially since it is called inside seac also */
  param.end_path ();

  return true;
}

bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const
{
#ifdef HB_NO_OT_FONT_CFF
  /* XXX Remove check when this code moves to .hh file. */
  return true;
#endif

  return _get_path (this, font, glyph, draw_helper);
}
#endif

struct get_seac_param_t
{
  void init (const OT::cff1::accelerator_t *_cff)
  {
    cff = _cff;
    base = 0;
    accent = 0;
  }

  bool has_seac () const { return base && accent; }

  const OT::cff1::accelerator_t *cff;
  hb_codepoint_t  base;
  hb_codepoint_t  accent;
};

struct cff1_cs_opset_seac_t : cff1_cs_opset_t<cff1_cs_opset_seac_t, get_seac_param_t>
{
  static void process_seac (cff1_cs_interp_env_t &env, get_seac_param_t& param)
  {
    unsigned int  n = env.argStack.get_count ();
    hb_codepoint_t  base_char = (hb_codepoint_t)env.argStack[n-2].to_int ();
    hb_codepoint_t  accent_char = (hb_codepoint_t)env.argStack[n-1].to_int ();

    param.base = param.cff->std_code_to_glyph (base_char);
    param.accent = param.cff->std_code_to_glyph (accent_char);
  }
};

bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const
{
  if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;

  unsigned int fd = fdSelect->get_fd (glyph);
  cff1_cs_interpreter_t<cff1_cs_opset_seac_t, get_seac_param_t> interp;
  const byte_str_t str = (*charStrings)[glyph];
  interp.env.init (str, *this, fd);
  get_seac_param_t  param;
  param.init (this);
  if (unlikely (!interp.interpret (param))) return false;

  if (param.has_seac ())
  {
    *base = param.base;
    *accent = param.accent;
    return true;
  }
  return false;
}

#endif


text:
/*
 * Copyright © 2018  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Garret Rieger
 */

#include "hb.hh"
#include "hb-ot-os2-unicode-ranges.hh"

static void
test (hb_codepoint_t cp, unsigned int bit)
{
  if (OT::_hb_ot_os2_get_unicode_range_bit (cp) != bit)
  {
    fprintf (stderr, "got incorrect bit (%d) for cp 0x%X. Should have been %d.",
	     OT::_hb_ot_os2_get_unicode_range_bit (cp),
	     cp,
	     bit);
    abort();
  }
}

static void
test_get_unicode_range_bit ()
{
  test (0x0000, 0);
  test (0x0042, 0);
  test (0x007F, 0);
  test (0x0080, 1);

  test (0x30A0, 50);
  test (0x30B1, 50);
  test (0x30FF, 50);

  test (0x10FFFD, 90);

  test (0x30000, -1);
  test (0x110000, -1);
}

int
main ()
{
  test_get_unicode_range_bit ();
  return 0;
}