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): Behdad Esfahbod
 */

#ifndef HB_ARRAY_HH
#define HB_ARRAY_HH

#include "hb.hh"
#include "hb-algs.hh"
#include "hb-iter.hh"
#include "hb-null.hh"

template <typename Type>
struct hb_sorted_array_t;

template <typename Type>
struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
{
  /*
   * Constructors.
   */
  hb_array_t () : arrayZ (nullptr), length (0), backwards_length (0) {}
  hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_), backwards_length (0) {}
  template <unsigned int length_>
  hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_), backwards_length (0) {}

  template <typename U,
	    hb_enable_if (hb_is_cr_convertible(U, Type))>
  hb_array_t (const hb_array_t<U> &o) :
    hb_iter_with_fallback_t<hb_array_t, Type&> (),
    arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {}
  template <typename U,
	    hb_enable_if (hb_is_cr_convertible(U, Type))>
  hb_array_t& operator = (const hb_array_t<U> &o)
  { arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; }

  /*
   * Iterator implementation.
   */
  typedef Type& __item_t__;
  static constexpr bool is_random_access_iterator = true;
  Type& __item_at__ (unsigned i) const
  {
    if (unlikely (i >= length)) return CrapOrNull (Type);
    return arrayZ[i];
  }
  void __forward__ (unsigned n)
  {
    if (unlikely (n > length))
      n = length;
    length -= n;
    backwards_length += n;
    arrayZ += n;
  }
  void __rewind__ (unsigned n)
  {
    if (unlikely (n > backwards_length))
      n = backwards_length;
    length += n;
    backwards_length -= n;
    arrayZ -= n;
  }
  unsigned __len__ () const { return length; }
  /* Ouch. The operator== compares the contents of the array.  For range-based for loops,
   * it's best if we can just compare arrayZ, though comparing contents is still fast,
   * but also would require that Type has operator==.  As such, we optimize this operator
   * for range-based for loop and just compare arrayZ.  No need to compare length, as we
   * assume we're only compared to .end(). */
  bool operator != (const hb_array_t& o) const
  { return arrayZ != o.arrayZ; }

  /* Extra operators.
   */
  Type * operator & () const { return arrayZ; }
  operator hb_array_t<const Type> () { return hb_array_t<const Type> (arrayZ, length); }
  template <typename T> operator T * () const { return arrayZ; }

  HB_INTERNAL bool operator == (const hb_array_t &o) const;

  uint32_t hash () const {
    uint32_t current = 0;
    for (unsigned int i = 0; i < this->length; i++) {
      current = current * 31 + hb_hash (this->arrayZ[i]);
    }
    return current;
  }

  /*
   * Compare, Sort, and Search.
   */

  /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */
  int cmp (const hb_array_t &a) const
  {
    if (length != a.length)
      return (int) a.length - (int) length;
    return hb_memcmp (a.arrayZ, arrayZ, get_size ());
  }
  HB_INTERNAL static int cmp (const void *pa, const void *pb)
  {
    hb_array_t *a = (hb_array_t *) pa;
    hb_array_t *b = (hb_array_t *) pb;
    return b->cmp (*a);
  }

  template <typename T>
  Type *lsearch (const T &x, Type *not_found = nullptr)
  {
    unsigned int count = length;
    for (unsigned int i = 0; i < count; i++)
      if (!this->arrayZ[i].cmp (x))
	return &this->arrayZ[i];
    return not_found;
  }
  template <typename T>
  const Type *lsearch (const T &x, const Type *not_found = nullptr) const
  {
    unsigned int count = length;
    for (unsigned int i = 0; i < count; i++)
      if (!this->arrayZ[i].cmp (x))
	return &this->arrayZ[i];
    return not_found;
  }

  hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*))
  {
    if (likely (length))
      hb_qsort (arrayZ, length, this->get_item_size (), cmp_);
    return hb_sorted_array_t<Type> (*this);
  }
  hb_sorted_array_t<Type> qsort ()
  {
    if (likely (length))
      hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp);
    return hb_sorted_array_t<Type> (*this);
  }
  void qsort (unsigned int start, unsigned int end)
  {
    end = hb_min (end, length);
    assert (start <= end);
    if (likely (start < end))
      hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp);
  }

  /*
   * Other methods.
   */

  unsigned int get_size () const { return length * this->get_item_size (); }

  /*
   * Reverse the order of items in this array in the range [start, end).
   */
  void reverse (unsigned start = 0, unsigned end = -1)
  {
    start = hb_min (start, length);
    end = hb_min (end, length);

    if (end < start + 2)
      return;

    for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) {
      Type temp = arrayZ[rhs];
      arrayZ[rhs] = arrayZ[lhs];
      arrayZ[lhs] = temp;
    }
  }

  hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
  {
    if (!start_offset && !seg_count)
      return *this;

    unsigned int count = length;
    if (unlikely (start_offset > count))
      count = 0;
    else
      count -= start_offset;
    if (seg_count)
      count = *seg_count = hb_min (count, *seg_count);
    return hb_array_t (arrayZ + start_offset, count);
  }
  hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
  { return sub_array (start_offset, &seg_count); }

  hb_array_t truncate (unsigned length) const { return sub_array (0, length); }

  template <typename T,
	    unsigned P = sizeof (Type),
	    hb_enable_if (P == 1)>
  const T *as () const
  { return length < hb_null_size (T) ? &Null (T) : reinterpret_cast<const T *> (arrayZ); }

  template <typename T,
	    unsigned P = sizeof (Type),
	    hb_enable_if (P == 1)>
  bool check_range (const T *p, unsigned int size = T::static_size) const
  {
    return arrayZ <= ((const char *) p)
	&& ((const char *) p) <= arrayZ + length
	&& (unsigned int) (arrayZ + length - (const char *) p) >= size;
  }

  /* Only call if you allocated the underlying array using malloc() or similar. */
  void free ()
  { ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; }

  template <typename hb_serialize_context_t>
  hb_array_t copy (hb_serialize_context_t *c) const
  {
    TRACE_SERIALIZE (this);
    auto* out = c->start_embed (arrayZ);
    if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ());
    for (unsigned i = 0; i < length; i++)
      out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */
    return_trace (hb_array_t (out, length));
  }

  template <typename hb_sanitize_context_t>
  bool sanitize (hb_sanitize_context_t *c) const
  { return c->check_array (arrayZ, length); }

  /*
   * Members
   */

  public:
  Type *arrayZ;
  unsigned int length;
  unsigned int backwards_length;
};
template <typename T> inline hb_array_t<T>
hb_array (T *array, unsigned int length)
{ return hb_array_t<T> (array, length); }
template <typename T, unsigned int length_> inline hb_array_t<T>
hb_array (T (&array_)[length_])
{ return hb_array_t<T> (array_); }

enum hb_bfind_not_found_t
{
  HB_BFIND_NOT_FOUND_DONT_STORE,
  HB_BFIND_NOT_FOUND_STORE,
  HB_BFIND_NOT_FOUND_STORE_CLOSEST,
};

template <typename Type>
struct hb_sorted_array_t :
	hb_iter_t<hb_sorted_array_t<Type>, Type&>,
	hb_array_t<Type>
{
  typedef hb_iter_t<hb_sorted_array_t, Type&> iter_base_t;
  HB_ITER_USING (iter_base_t);
  static constexpr bool is_random_access_iterator = true;
  static constexpr bool is_sorted_iterator = true;

  hb_sorted_array_t () : hb_array_t<Type> () {}
  hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {}
  template <unsigned int length_>
  hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}

  template <typename U,
	    hb_enable_if (hb_is_cr_convertible(U, Type))>
  hb_sorted_array_t (const hb_array_t<U> &o) :
    hb_iter_t<hb_sorted_array_t, Type&> (),
    hb_array_t<Type> (o) {}
  template <typename U,
	    hb_enable_if (hb_is_cr_convertible(U, Type))>
  hb_sorted_array_t& operator = (const hb_array_t<U> &o)
  { hb_array_t<Type> (*this) = o; return *this; }

  /* Iterator implementation. */
  bool operator != (const hb_sorted_array_t& o) const
  { return this->arrayZ != o.arrayZ || this->length != o.length; }

  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
  { return hb_sorted_array_t (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
  { return sub_array (start_offset, &seg_count); }

  hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); }

  template <typename T>
  Type *bsearch (const T &x, Type *not_found = nullptr)
  {
    unsigned int i;
    return bfind (x, &i) ? &this->arrayZ[i] : not_found;
  }
  template <typename T>
  const Type *bsearch (const T &x, const Type *not_found = nullptr) const
  {
    unsigned int i;
    return bfind (x, &i) ? &this->arrayZ[i] : not_found;
  }
  template <typename T>
  bool bfind (const T &x, unsigned int *i = nullptr,
	      hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
	      unsigned int to_store = (unsigned int) -1) const
  {
    unsigned pos;

    if (bsearch_impl (x, &pos))
    {
      if (i)
	*i = pos;
      return true;
    }

    if (i)
    {
      switch (not_found)
      {
	case HB_BFIND_NOT_FOUND_DONT_STORE:
	  break;

	case HB_BFIND_NOT_FOUND_STORE:
	  *i = to_store;
	  break;

	case HB_BFIND_NOT_FOUND_STORE_CLOSEST:
	  *i = pos;
	  break;
      }
    }
    return false;
  }
  template <typename T>
  bool bsearch_impl (const T &x, unsigned *pos) const
  {
    return hb_bsearch_impl (pos,
			    x,
			    this->arrayZ,
			    this->length,
			    sizeof (Type),
			    _hb_cmp_method<T, Type>);
  }
};
template <typename T> inline hb_sorted_array_t<T>
hb_sorted_array (T *array, unsigned int length)
{ return hb_sorted_array_t<T> (array, length); }
template <typename T, unsigned int length_> inline hb_sorted_array_t<T>
hb_sorted_array (T (&array_)[length_])
{ return hb_sorted_array_t<T> (array_); }

template <typename T>
bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
{
  if (o.length != this->length) return false;
  for (unsigned int i = 0; i < this->length; i++) {
    if (this->arrayZ[i] != o.arrayZ[i]) return false;
  }
  return true;
}

/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */

template <>
inline uint32_t hb_array_t<const char>::hash () const {
  uint32_t current = 0;
  for (unsigned int i = 0; i < this->length; i++)
    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
  return current;
}

template <>
inline uint32_t hb_array_t<const unsigned char>::hash () const {
  uint32_t current = 0;
  for (unsigned int i = 0; i < this->length; i++)
    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
  return current;
}

typedef hb_array_t<const char> hb_bytes_t;
typedef hb_array_t<const unsigned char> hb_ubytes_t;

#endif /* HB_ARRAY_HH */


text:
/*
 * Copyright © 2017  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): Behdad Esfahbod
 */

#ifndef HB_OT_H_IN
#error "Include <hb-ot.h> instead."
#endif

#ifndef HB_OT_VAR_H
#define HB_OT_VAR_H

#include "hb.h"

HB_BEGIN_DECLS

#define HB_OT_TAG_VAR_AXIS_ITALIC	HB_TAG('i','t','a','l')
#define HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE	HB_TAG('o','p','s','z')
#define HB_OT_TAG_VAR_AXIS_SLANT	HB_TAG('s','l','n','t')
#define HB_OT_TAG_VAR_AXIS_WIDTH	HB_TAG('w','d','t','h')
#define HB_OT_TAG_VAR_AXIS_WEIGHT	HB_TAG('w','g','h','t')

/*
 * fvar / avar
 */

HB_EXTERN hb_bool_t
hb_ot_var_has_data (hb_face_t *face);

/*
 * Variation axes.
 */

HB_EXTERN unsigned int
hb_ot_var_get_axis_count (hb_face_t *face);

/**
 * hb_ot_var_axis_flags_t:
 * @HB_OT_VAR_AXIS_FLAG_HIDDEN: The axis should not be exposed directly in user interfaces.
 *
 * Since: 2.2.0
 */
typedef enum { /*< flags >*/
  HB_OT_VAR_AXIS_FLAG_HIDDEN	= 0x00000001u,

  _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= HB_TAG_MAX_SIGNED /*< skip >*/
} hb_ot_var_axis_flags_t;

/**
 * hb_ot_var_axis_info_t:
 *
 * Since: 2.2.0
 */
typedef struct hb_ot_var_axis_info_t
{
  unsigned int			axis_index;
  hb_tag_t			tag;
  hb_ot_name_id_t		name_id;
  hb_ot_var_axis_flags_t	flags;
  float				min_value;
  float				default_value;
  float				max_value;
  /*< private >*/
  unsigned int			reserved;
} hb_ot_var_axis_info_t;

HB_EXTERN unsigned int
hb_ot_var_get_axis_infos (hb_face_t             *face,
			  unsigned int           start_offset,
			  unsigned int          *axes_count /* IN/OUT */,
			  hb_ot_var_axis_info_t *axes_array /* OUT */);

HB_EXTERN hb_bool_t
hb_ot_var_find_axis_info (hb_face_t             *face,
			  hb_tag_t               axis_tag,
			  hb_ot_var_axis_info_t *axis_info);

/*
 * Named instances.
 */

HB_EXTERN unsigned int
hb_ot_var_get_named_instance_count (hb_face_t *face);

HB_EXTERN hb_ot_name_id_t
hb_ot_var_named_instance_get_subfamily_name_id (hb_face_t   *face,
						unsigned int instance_index);

HB_EXTERN hb_ot_name_id_t
hb_ot_var_named_instance_get_postscript_name_id (hb_face_t  *face,
						unsigned int instance_index);

HB_EXTERN unsigned int
hb_ot_var_named_instance_get_design_coords (hb_face_t    *face,
					    unsigned int  instance_index,
					    unsigned int *coords_length, /* IN/OUT */
					    float        *coords         /* OUT */);

/*
 * Conversions.
 */

HB_EXTERN void
hb_ot_var_normalize_variations (hb_face_t            *face,
				const hb_variation_t *variations, /* IN */
				unsigned int          variations_length,
				int                  *coords, /* OUT */
				unsigned int          coords_length);

HB_EXTERN void
hb_ot_var_normalize_coords (hb_face_t    *face,
			    unsigned int coords_length,
			    const float *design_coords, /* IN */
			    int *normalized_coords /* OUT */);

HB_END_DECLS

#endif /* HB_OT_VAR_H */

repo_language: C

mime_type: text/x-c


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
 */
#ifndef HB_CFF_INTERP_DICT_COMMON_HH
#define HB_CFF_INTERP_DICT_COMMON_HH

#include "hb-cff-interp-common.hh"

namespace CFF {

using namespace OT;

/* an opstr and the parsed out dict value(s) */
struct dict_val_t : op_str_t
{
  void init () { single_val.set_int (0); }
  void fini () {}

  number_t	      single_val;
};

typedef dict_val_t num_dict_val_t;

template <typename VAL> struct dict_values_t : parsed_values_t<VAL> {};

template <typename OPSTR=op_str_t>
struct top_dict_values_t : dict_values_t<OPSTR>
{
  void init ()
  {
    dict_values_t<OPSTR>::init ();
    charStringsOffset = 0;
    FDArrayOffset = 0;
  }
  void fini () { dict_values_t<OPSTR>::fini (); }

  unsigned int  charStringsOffset;
  unsigned int  FDArrayOffset;
};

struct dict_opset_t : opset_t<number_t>
{
  static void process_op (op_code_t op, interp_env_t<number_t>& env)
  {
    switch (op) {
      case OpCode_longintdict:  /* 5-byte integer */
	env.argStack.push_longint_from_substr (env.str_ref);
	break;

      case OpCode_BCD:  /* real number */
	env.argStack.push_real (parse_bcd (env.str_ref));
	break;

      default:
	opset_t<number_t>::process_op (op, env);
	break;
    }
  }

  /* Turns CFF's BCD format into strtod understandable string */
  static double parse_bcd (byte_str_ref_t& str_ref)
  {
    if (unlikely (str_ref.in_error ())) return .0;

    enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };

    char buf[32];
    unsigned char byte = 0;
    for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count)
    {
      unsigned nibble;
      if (!(i & 1))
      {
	if (unlikely (!str_ref.avail ())) break;

	byte = str_ref[0];
	str_ref.inc ();
	nibble = byte >> 4;
      }
      else
	nibble = byte & 0x0F;

      if (unlikely (nibble == RESERVED)) break;
      else if (nibble == END)
      {
	const char *p = buf;
	double pv;
	if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */)))
	  break;
	return pv;
      }
      else
      {
	buf[count] = "0123456789.EE?-?"[nibble];
	if (nibble == EXP_NEG)
	{
	  ++count;
	  if (unlikely (count == ARRAY_LENGTH (buf))) break;
	  buf[count] = '-';
	}
      }
    }

    str_ref.set_error ();
    return .0;
  }

  static bool is_hint_op (op_code_t op)
  {
    switch (op)
    {
      case OpCode_BlueValues:
      case OpCode_OtherBlues:
      case OpCode_FamilyBlues:
      case OpCode_FamilyOtherBlues:
      case OpCode_StemSnapH:
      case OpCode_StemSnapV:
      case OpCode_StdHW:
      case OpCode_StdVW:
      case OpCode_BlueScale:
      case OpCode_BlueShift:
      case OpCode_BlueFuzz:
      case OpCode_ForceBold:
      case OpCode_LanguageGroup:
      case OpCode_ExpansionFactor:
	return true;
      default:
	return false;
    }
  }
};

template <typename VAL=op_str_t>
struct top_dict_opset_t : dict_opset_t
{
  static void process_op (op_code_t op, interp_env_t<number_t>& env, top_dict_values_t<VAL> & dictval)
  {
    switch (op) {
      case OpCode_CharStrings:
	dictval.charStringsOffset = env.argStack.pop_uint ();
	env.clear_args ();
	break;
      case OpCode_FDArray:
	dictval.FDArrayOffset = env.argStack.pop_uint ();
	env.clear_args ();
	break;
      case OpCode_FontMatrix:
	env.clear_args ();
	break;
      default:
	dict_opset_t::process_op (op, env);
	break;
    }
  }
};

template <typename OPSET, typename PARAM, typename ENV=num_interp_env_t>
struct dict_interpreter_t : interpreter_t<ENV>
{
  bool interpret (PARAM& param)
  {
    param.init ();
    while (SUPER::env.str_ref.avail ())
    {
      OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
      if (unlikely (SUPER::env.in_error ()))
	return false;
    }

    return true;
  }

  private:
  typedef interpreter_t<ENV> SUPER;
};

} /* namespace CFF */

#endif /* HB_CFF_INTERP_DICT_COMMON_HH */


text:
/*** BEGIN file-header ***/
/*
 * Copyright (C) 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
 */

#include "hb.hh"

#ifdef HAVE_GOBJECT

/* g++ didn't like older gtype.h gcc-only code path. */
#include <glib.h>
#if !GLIB_CHECK_VERSION(2,29,16)
#undef __GNUC__
#undef __GNUC_MINOR__
#define __GNUC__ 2
#define __GNUC_MINOR__ 6
#endif

#include "hb-gobject.h"

/*** END file-header ***/

/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
/*** END file-production ***/

/*** BEGIN file-tail ***/

#endif
/*** END file-tail ***/

/*** BEGIN value-header ***/
GType
@enum_name@_get_type ()
{
  static gsize type_id = 0;

  if (g_once_init_enter (&type_id))
    {
      static const G@Type@Value values[] = {
/*** END value-header ***/

/*** BEGIN value-production ***/
        { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/

/*** BEGIN value-tail ***/
        { 0, NULL, NULL }
      };
      GType id =
        g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
      g_once_init_leave (&type_id, id);
    }

  return type_id;
}

/*** END value-tail ***/


text:
/*
 * Copyright © 2007  Chris Wilson
 * Copyright © 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.
 *
 * Contributor(s):
 *	Chris Wilson <chris@chris-wilson.co.uk>
 * Red Hat Author(s): Behdad Esfahbod
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_ATOMIC_HH
#define HB_ATOMIC_HH

#include "hb.hh"
#include "hb-meta.hh"

/*
 * Atomic integers and pointers.
 */

/* We need external help for these */

#if defined(hb_atomic_int_impl_add) \
 && defined(hb_atomic_ptr_impl_get) \
 && defined(hb_atomic_ptr_impl_cmpexch)

/* Defined externally, i.e. in config.h. */

#elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE)

/* C++11-style GCC primitives. */

#define _hb_memory_barrier()			__sync_synchronize ()

#define hb_atomic_int_impl_add(AI, V)		__atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL)
#define hb_atomic_int_impl_set_relaxed(AI, V)	__atomic_store_n ((AI), (V), __ATOMIC_RELAXED)
#define hb_atomic_int_impl_set(AI, V)		__atomic_store_n ((AI), (V), __ATOMIC_RELEASE)
#define hb_atomic_int_impl_get_relaxed(AI)	__atomic_load_n ((AI), __ATOMIC_RELAXED)
#define hb_atomic_int_impl_get(AI)		__atomic_load_n ((AI), __ATOMIC_ACQUIRE)

#define hb_atomic_ptr_impl_set_relaxed(P, V)	__atomic_store_n ((P), (V), __ATOMIC_RELAXED)
#define hb_atomic_ptr_impl_get_relaxed(P)	__atomic_load_n ((P), __ATOMIC_RELAXED)
#define hb_atomic_ptr_impl_get(P)		__atomic_load_n ((P), __ATOMIC_ACQUIRE)
static inline bool
_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
{
  const void *O = O_; // Need lvalue
  return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
}
#define hb_atomic_ptr_impl_cmpexch(P,O,N)	_hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))

#elif !defined(HB_NO_MT) && __cplusplus >= 201103L

/* C++11 atomics. */

#include <atomic>

#define _hb_memory_barrier()			std::atomic_thread_fence(std::memory_order_ack_rel)
#define _hb_memory_r_barrier()			std::atomic_thread_fence(std::memory_order_acquire)
#define _hb_memory_w_barrier()			std::atomic_thread_fence(std::memory_order_release)

#define hb_atomic_int_impl_add(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
#define hb_atomic_int_impl_set_relaxed(AI, V)	(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_relaxed))
#define hb_atomic_int_impl_set(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_release))
#define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<int> const *> (AI)->load (std::memory_order_relaxed))
#define hb_atomic_int_impl_get(AI)		(reinterpret_cast<std::atomic<int> const *> (AI)->load (std::memory_order_acquire))

#define hb_atomic_ptr_impl_set_relaxed(P, V)	(reinterpret_cast<std::atomic<void*> *> (P)->store ((V), std::memory_order_relaxed))
#define hb_atomic_ptr_impl_get_relaxed(P)	(reinterpret_cast<std::atomic<void*> const *> (P)->load (std::memory_order_relaxed))
#define hb_atomic_ptr_impl_get(P)		(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_acquire))
static inline bool
_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
{
  const void *O = O_; // Need lvalue
  return reinterpret_cast<std::atomic<const void*> *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed);
}
#define hb_atomic_ptr_impl_cmpexch(P,O,N)	_hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))

#elif !defined(HB_NO_MT) && defined(_WIN32)

#include <windows.h>

static inline void _hb_memory_barrier ()
{
#if !defined(MemoryBarrier) && !defined(__MINGW32_VERSION)
  /* MinGW has a convoluted history of supporting MemoryBarrier. */
  LONG dummy = 0;
  InterlockedExchange (&dummy, 1);
#else
  MemoryBarrier ();
#endif
}
#define _hb_memory_barrier()			_hb_memory_barrier ()

#define hb_atomic_int_impl_add(AI, V)		InterlockedExchangeAdd ((LONG *) (AI), (V))
static_assert ((sizeof (LONG) == sizeof (int)), "");

#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((P), (N), (O)) == (O))

#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)

#define _hb_memory_barrier()			__sync_synchronize ()

#define hb_atomic_int_impl_add(AI, V)		__sync_fetch_and_add ((AI), (V))

#define hb_atomic_ptr_impl_cmpexch(P,O,N)	__sync_bool_compare_and_swap ((P), (O), (N))

#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS)

#include <atomic.h>
#include <mbarrier.h>

#define _hb_memory_r_barrier()			__machine_r_barrier ()
#define _hb_memory_w_barrier()			__machine_w_barrier ()
#define _hb_memory_barrier()			__machine_rw_barrier ()

static inline int _hb_fetch_and_add (int *AI, int V)
{
  _hb_memory_w_barrier ();
  int result = atomic_add_int_nv ((uint_t *) AI, V) - V;
  _hb_memory_r_barrier ();
  return result;
}
static inline bool _hb_compare_and_swap_ptr (void **P, void *O, void *N)
{
  _hb_memory_w_barrier ();
  bool result = atomic_cas_ptr (P, O, N) == O;
  _hb_memory_r_barrier ();
  return result;
}

#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))

#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swap_ptr ((P), (O), (N))

#elif !defined(HB_NO_MT) && defined(__APPLE__)

#include <libkern/OSAtomic.h>
#ifdef __MAC_OS_X_MIN_REQUIRED
#include <AvailabilityMacros.h>
#elif defined(__IPHONE_OS_MIN_REQUIRED)
#include <Availability.h>
#endif

#define _hb_memory_barrier()			OSMemoryBarrier ()

#define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), (AI)) - (V))

#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100)
#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((O), (N), (P))
#else
#if __ppc64__ || __x86_64__ || __aarch64__
#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P))
#else
#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P))
#endif
#endif

#elif !defined(HB_NO_MT) && defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__))

#include <builtins.h>

#define _hb_memory_barrier()			__lwsync ()

static inline int _hb_fetch_and_add (int *AI, int V)
{
  _hb_memory_barrier ();
  int result = __fetch_and_add (AI, V);
  _hb_memory_barrier ();
  return result;
}
static inline bool _hb_compare_and_swaplp (long *P, long O, long N)
{
  _hb_memory_barrier ();
  bool result = __compare_and_swaplp (P, &O, N);
  _hb_memory_barrier ();
  return result;
}

#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))

#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swaplp ((long *) (P), (long) (O), (long) (N))
static_assert ((sizeof (long) == sizeof (void *)), "");

#elif defined(HB_NO_MT)

#define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))

#define _hb_memory_barrier()			do {} while (0)

#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)

#else

#error "Could not find any system to define atomic_int macros."
#error "Check hb-atomic.hh for possible resolutions."

#endif

#ifndef _hb_memory_r_barrier
#define _hb_memory_r_barrier()			_hb_memory_barrier ()
#endif
#ifndef _hb_memory_w_barrier
#define _hb_memory_w_barrier()			_hb_memory_barrier ()
#endif
#ifndef hb_atomic_int_impl_set_relaxed
#define hb_atomic_int_impl_set_relaxed(AI, V)	(*(AI) = (V))
#endif
#ifndef hb_atomic_int_impl_get_relaxed
#define hb_atomic_int_impl_get_relaxed(AI)	(*(AI))
#endif

#ifndef hb_atomic_ptr_impl_set_relaxed
#define hb_atomic_ptr_impl_set_relaxed(P, V)	(*(P) = (V))
#endif
#ifndef hb_atomic_ptr_impl_get_relaxed
#define hb_atomic_ptr_impl_get_relaxed(P)	(*(P))
#endif
#ifndef hb_atomic_int_impl_set
inline void hb_atomic_int_impl_set (int *AI, int v)	{ _hb_memory_w_barrier (); *AI = v; }
#endif
#ifndef hb_atomic_int_impl_get
inline int hb_atomic_int_impl_get (const int *AI)	{ int v = *AI; _hb_memory_r_barrier (); return v; }
#endif
#ifndef hb_atomic_ptr_impl_get
inline void *hb_atomic_ptr_impl_get (void ** const P)	{ void *v = *P; _hb_memory_r_barrier (); return v; }
#endif

#define HB_ATOMIC_INT_INIT(V)          {V}
struct hb_atomic_int_t
{
  void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
  void set (int v_) { hb_atomic_int_impl_set (&v, v_); }
  int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
  int get () const { return hb_atomic_int_impl_get (&v); }
  int inc () { return hb_atomic_int_impl_add (&v,  1); }
  int dec () { return hb_atomic_int_impl_add (&v, -1); }

  int v;
};

#define HB_ATOMIC_PTR_INIT(V)          {V}
template <typename P>
struct hb_atomic_ptr_t
{
  typedef hb_remove_pointer<P> T;

  void init (T* v_ = nullptr) { set_relaxed (v_); }
  void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
  T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); }
  T *get () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
  bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }

  T * operator -> () const                    { return get (); }
  template <typename C> operator C * () const { return get (); }

  T *v;
};

#endif /* HB_ATOMIC_HH */


text:
/*
 * Copyright © 2009  Red Hat, Inc.
 * Copyright © 2009  Keith Stribley
 * 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.
 *
 * Red Hat Author(s): Behdad Esfahbod
 * Google Author(s): Behdad Esfahbod
 */

#include "hb.hh"

#ifdef HAVE_ICU

#include "hb-icu.h"

#include "hb-machinery.hh"

#include <unicode/uchar.h>
#include <unicode/unorm2.h>
#include <unicode/ustring.h>
#include <unicode/utf16.h>
#include <unicode/uversion.h>

/* ICU extra semicolon, fixed since 65, https://github.com/unicode-org/icu/commit/480bec3 */
#if U_ICU_VERSION_MAJOR_NUM < 65 && (defined(__GNUC__) || defined(__clang__))
#define HB_ICU_EXTRA_SEMI_IGNORED
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wextra-semi-stmt"
#endif

/**
 * SECTION:hb-icu
 * @title: hb-icu
 * @short_description: ICU integration
 * @include: hb-icu.h
 *
 * Functions for using HarfBuzz with the International Components for Unicode
 * (ICU) library. HarfBuzz supports using ICU to provide Unicode data, by attaching
 * ICU functions to the virtual methods in a #hb_unicode_funcs_t function
 * structure.
 **/

/**
 * hb_icu_script_to_script:
 * @script: The UScriptCode identifier to query
 *
 * Fetches the #hb_script_t script that corresponds to the
 * specified UScriptCode identifier.
 *
 * Return value: the #hb_script_t script found
 *
 **/

hb_script_t
hb_icu_script_to_script (UScriptCode script)
{
  if (unlikely (script == USCRIPT_INVALID_CODE))
    return HB_SCRIPT_INVALID;

  return hb_script_from_string (uscript_getShortName (script), -1);
}

/**
 * hb_icu_script_from_script:
 * @script: The #hb_script_t script to query
 *
 * Fetches the UScriptCode identifier that corresponds to the
 * specified #hb_script_t script.
 *
 * Return value: the UScriptCode identifier found
 *
 **/
UScriptCode
hb_icu_script_from_script (hb_script_t script)
{
  if (unlikely (script == HB_SCRIPT_INVALID))
    return USCRIPT_INVALID_CODE;

  unsigned int numScriptCode = 1 + u_getIntPropertyMaxValue (UCHAR_SCRIPT);
  for (unsigned int i = 0; i < numScriptCode; i++)
    if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script))
      return (UScriptCode) i;

  return USCRIPT_UNKNOWN;
}

static hb_unicode_combining_class_t
hb_icu_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED,
				hb_codepoint_t      unicode,
				void               *user_data HB_UNUSED)

{
  return (hb_unicode_combining_class_t) u_getCombiningClass (unicode);
}

static hb_unicode_general_category_t
hb_icu_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED,
				 hb_codepoint_t      unicode,
				 void               *user_data HB_UNUSED)
{
  switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY))
  {
  case U_UNASSIGNED:			return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;

  case U_UPPERCASE_LETTER:		return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER;
  case U_LOWERCASE_LETTER:		return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER;
  case U_TITLECASE_LETTER:		return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER;
  case U_MODIFIER_LETTER:		return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER;
  case U_OTHER_LETTER:			return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER;

  case U_NON_SPACING_MARK:		return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK;
  case U_ENCLOSING_MARK:		return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK;
  case U_COMBINING_SPACING_MARK:	return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK;

  case U_DECIMAL_DIGIT_NUMBER:		return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER;
  case U_LETTER_NUMBER:			return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER;
  case U_OTHER_NUMBER:			return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER;

  case U_SPACE_SEPARATOR:		return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR;
  case U_LINE_SEPARATOR:		return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR;
  case U_PARAGRAPH_SEPARATOR:		return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR;

  case U_CONTROL_CHAR:			return HB_UNICODE_GENERAL_CATEGORY_CONTROL;
  case U_FORMAT_CHAR:			return HB_UNICODE_GENERAL_CATEGORY_FORMAT;
  case U_PRIVATE_USE_CHAR:		return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE;
  case U_SURROGATE:			return HB_UNICODE_GENERAL_CATEGORY_SURROGATE;

  case U_DASH_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION;
  case U_START_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION;
  case U_END_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION;
  case U_CONNECTOR_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION;
  case U_OTHER_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION;

  case U_MATH_SYMBOL:			return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL;
  case U_CURRENCY_SYMBOL:		return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL;
  case U_MODIFIER_SYMBOL:		return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL;
  case U_OTHER_SYMBOL:			return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL;

  case U_INITIAL_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION;
  case U_FINAL_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION;
  }

  return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
}

static hb_codepoint_t
hb_icu_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED,
			  hb_codepoint_t      unicode,
			  void               *user_data HB_UNUSED)
{
  return u_charMirror(unicode);
}

static hb_script_t
hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED,
		       hb_codepoint_t      unicode,
		       void               *user_data HB_UNUSED)
{
  UErrorCode status = U_ZERO_ERROR;
  UScriptCode scriptCode = uscript_getScript(unicode, &status);

  if (unlikely (U_FAILURE (status)))
    return HB_SCRIPT_UNKNOWN;

  return hb_icu_script_to_script (scriptCode);
}

static hb_bool_t
hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
			hb_codepoint_t      a,
			hb_codepoint_t      b,
			hb_codepoint_t     *ab,
			void               *user_data HB_UNUSED)
{
#if U_ICU_VERSION_MAJOR_NUM >= 49
  {
    const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data;
    UChar32 ret = unorm2_composePair (normalizer, a, b);
    if (ret < 0) return false;
    *ab = ret;
    return true;
  }
#endif

  /* We don't ifdef-out the fallback code such that compiler always
   * sees it and makes sure it's compilable. */

  UChar utf16[4], normalized[5];
  unsigned int len;
  hb_bool_t ret, err;
  UErrorCode icu_err;

  len = 0;
  err = false;
  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err);
  if (err) return false;
  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err);
  if (err) return false;

  icu_err = U_ZERO_ERROR;
  len = unorm2_normalize (unorm2_getNFCInstance (&icu_err), utf16, len, normalized, ARRAY_LENGTH (normalized), &icu_err);
  if (U_FAILURE (icu_err))
    return false;
  if (u_countChar32 (normalized, len) == 1) {
    U16_GET_UNSAFE (normalized, 0, *ab);
    ret = true;
  } else {
    ret = false;
  }

  return ret;
}

static hb_bool_t
hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
			  hb_codepoint_t      ab,
			  hb_codepoint_t     *a,
			  hb_codepoint_t     *b,
			  void               *user_data HB_UNUSED)
{
#if U_ICU_VERSION_MAJOR_NUM >= 49
  {
    const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data;
    UChar decomposed[4];
    int len;
    UErrorCode icu_err = U_ZERO_ERROR;
    len = unorm2_getRawDecomposition (normalizer, ab, decomposed,
				      ARRAY_LENGTH (decomposed), &icu_err);
    if (U_FAILURE (icu_err) || len < 0) return false;

    len = u_countChar32 (decomposed, len);
    if (len == 1) {
      U16_GET_UNSAFE (decomposed, 0, *a);
      *b = 0;
      return *a != ab;
    } else if (len == 2) {
      len = 0;
      U16_NEXT_UNSAFE (decomposed, len, *a);
      U16_NEXT_UNSAFE (decomposed, len, *b);
    }
    return true;
  }
#endif

  /* We don't ifdef-out the fallback code such that compiler always
   * sees it and makes sure it's compilable. */

  UChar utf16[2], normalized[2 * 19/*HB_UNICODE_MAX_DECOMPOSITION_LEN*/ + 1];
  unsigned int len;
  hb_bool_t ret, err;
  UErrorCode icu_err;

  /* This function is a monster! Maybe it wasn't a good idea adding a
   * pairwise decompose API... */
  /* Watchout for the dragons.  Err, watchout for macros changing len. */

  len = 0;
  err = false;
  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), ab, err);
  if (err) return false;

  icu_err = U_ZERO_ERROR;
  len = unorm2_normalize (unorm2_getNFDInstance (&icu_err), utf16, len, normalized, ARRAY_LENGTH (normalized), &icu_err);
  if (U_FAILURE (icu_err))
    return false;

  len = u_countChar32 (normalized, len);

  if (len == 1) {
    U16_GET_UNSAFE (normalized, 0, *a);
    *b = 0;
    ret = *a != ab;
  } else if (len == 2) {
    len = 0;
    U16_NEXT_UNSAFE (normalized, len, *a);
    U16_NEXT_UNSAFE (normalized, len, *b);

    /* Here's the ugly part: if ab decomposes to a single character and
     * that character decomposes again, we have to detect that and undo
     * the second part :-(. */
    UChar recomposed[20];
    icu_err = U_ZERO_ERROR;
    unorm2_normalize (unorm2_getNFCInstance (&icu_err), normalized, len, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
    if (U_FAILURE (icu_err))
      return false;
    hb_codepoint_t c;
    U16_GET_UNSAFE (recomposed, 0, c);
    if (c != *a && c != ab) {
      *a = c;
      *b = 0;
    }
    ret = true;
  } else {
    /* If decomposed to more than two characters, take the last one,
     * and recompose the rest to get the first component. */
    U16_PREV_UNSAFE (normalized, len, *b); /* Changes len in-place. */
    UChar recomposed[18 * 2];
    icu_err = U_ZERO_ERROR;
    len = unorm2_normalize (unorm2_getNFCInstance (&icu_err), normalized, len, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
    if (U_FAILURE (icu_err))
      return false;
    /* We expect that recomposed has exactly one character now. */
    if (unlikely (u_countChar32 (recomposed, len) != 1))
      return false;
    U16_GET_UNSAFE (recomposed, 0, *a);
    ret = true;
  }

  return ret;
}

#if HB_USE_ATEXIT
static void free_static_icu_funcs ();
#endif

static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_icu_unicode_funcs_lazy_loader_t>
{
  static hb_unicode_funcs_t *create ()
  {
    void *user_data = nullptr;
#if U_ICU_VERSION_MAJOR_NUM >= 49
    UErrorCode icu_err = U_ZERO_ERROR;
    user_data = (void *) unorm2_getNFCInstance (&icu_err);
    assert (user_data);
#endif

    hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);

    hb_unicode_funcs_set_combining_class_func (funcs, hb_icu_unicode_combining_class, nullptr, nullptr);
    hb_unicode_funcs_set_general_category_func (funcs, hb_icu_unicode_general_category, nullptr, nullptr);
    hb_unicode_funcs_set_mirroring_func (funcs, hb_icu_unicode_mirroring, nullptr, nullptr);
    hb_unicode_funcs_set_script_func (funcs, hb_icu_unicode_script, nullptr, nullptr);
    hb_unicode_funcs_set_compose_func (funcs, hb_icu_unicode_compose, user_data, nullptr);
    hb_unicode_funcs_set_decompose_func (funcs, hb_icu_unicode_decompose, user_data, nullptr);

    hb_unicode_funcs_make_immutable (funcs);

#if HB_USE_ATEXIT
    atexit (free_static_icu_funcs);
#endif

    return funcs;
  }
} static_icu_funcs;

#if HB_USE_ATEXIT
static
void free_static_icu_funcs ()
{
  static_icu_funcs.free_instance ();
}
#endif

/**
 * hb_icu_get_unicode_funcs:
 *
 * Fetches a Unicode-functions structure that is populated
 * with the appropriate ICU function for each method.
 *
 * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure
 *
 * Since: 0.9.38
 **/
hb_unicode_funcs_t *
hb_icu_get_unicode_funcs ()
{
  return static_icu_funcs.get_unconst ();
}

#ifdef HB_ICU_EXTRA_SEMI_IGNORED
#pragma GCC diagnostic pop
#endif

#endif


text:
/*
 * Copyright © 2010,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.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_OT_SHAPE_COMPLEX_HH
#define HB_OT_SHAPE_COMPLEX_HH

#include "hb.hh"

#include "hb-ot-layout.hh"
#include "hb-ot-shape.hh"
#include "hb-ot-shape-normalize.hh"

/* buffer var allocations, used by complex shapers */
#define complex_var_u8_0()	var2.u8[2]
#define complex_var_u8_1()	var2.u8[3]

#define HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS 32

enum hb_ot_shape_zero_width_marks_type_t {
  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE
};

/* Master OT shaper list */
#define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
  HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
  HB_COMPLEX_SHAPER_IMPLEMENT (default) \
  HB_COMPLEX_SHAPER_IMPLEMENT (dumber) \
  HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
  HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \
  HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
  HB_COMPLEX_SHAPER_IMPLEMENT (khmer) \
  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_zawgyi) \
  HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
  HB_COMPLEX_SHAPER_IMPLEMENT (use) \
  /* ^--- Add new shapers here; keep sorted. */

struct hb_ot_complex_shaper_t
{
  /* collect_features()
   * Called during shape_plan().
   * Shapers should use plan->map to add their features and callbacks.
   * May be NULL.
   */
  void (*collect_features) (hb_ot_shape_planner_t *plan);

  /* override_features()
   * Called during shape_plan().
   * Shapers should use plan->map to override features and add callbacks after
   * common features are added.
   * May be NULL.
   */
  void (*override_features) (hb_ot_shape_planner_t *plan);

  /* data_create()
   * Called at the end of shape_plan().
   * Whatever shapers return will be accessible through plan->data later.
   * If nullptr is returned, means a plan failure.
   */
  void *(*data_create) (const hb_ot_shape_plan_t *plan);

  /* data_destroy()
   * Called when the shape_plan is being destroyed.
   * plan->data is passed here for destruction.
   * If nullptr is returned, means a plan failure.
   * May be NULL.
   */
  void (*data_destroy) (void *data);

  /* preprocess_text()
   * Called during shape().
   * Shapers can use to modify text before shaping starts.
   * May be NULL.
   */
  void (*preprocess_text) (const hb_ot_shape_plan_t *plan,
			   hb_buffer_t              *buffer,
			   hb_font_t                *font);

  /* postprocess_glyphs()
   * Called during shape().
   * Shapers can use to modify glyphs after shaping ends.
   * May be NULL.
   */
  void (*postprocess_glyphs) (const hb_ot_shape_plan_t *plan,
			      hb_buffer_t              *buffer,
			      hb_font_t                *font);

  hb_ot_shape_normalization_mode_t normalization_preference;

  /* decompose()
   * Called during shape()'s normalization.
   * May be NULL.
   */
  bool (*decompose) (const hb_ot_shape_normalize_context_t *c,
		     hb_codepoint_t  ab,
		     hb_codepoint_t *a,
		     hb_codepoint_t *b);

  /* compose()
   * Called during shape()'s normalization.
   * May be NULL.
   */
  bool (*compose) (const hb_ot_shape_normalize_context_t *c,
		   hb_codepoint_t  a,
		   hb_codepoint_t  b,
		   hb_codepoint_t *ab);

  /* setup_masks()
   * Called during shape().
   * Shapers should use map to get feature masks and set on buffer.
   * Shapers may NOT modify characters.
   * May be NULL.
   */
  void (*setup_masks) (const hb_ot_shape_plan_t *plan,
		       hb_buffer_t              *buffer,
		       hb_font_t                *font);

  /* gpos_tag()
   * If not HB_TAG_NONE, then must match found GPOS script tag for
   * GPOS to be applied.  Otherwise, fallback positioning will be used.
   */
  hb_tag_t gpos_tag;

  /* reorder_marks()
   * Called during shape().
   * Shapers can use to modify ordering of combining marks.
   * May be NULL.
   */
  void (*reorder_marks) (const hb_ot_shape_plan_t *plan,
			 hb_buffer_t              *buffer,
			 unsigned int              start,
			 unsigned int              end);

  hb_ot_shape_zero_width_marks_type_t zero_width_marks;

  bool fallback_position;
};

#define HB_COMPLEX_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_complex_shaper_t _hb_ot_complex_shaper_##name;
HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
#undef HB_COMPLEX_SHAPER_IMPLEMENT

static inline const hb_ot_complex_shaper_t *
hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
{
  switch ((hb_tag_t) planner->props.script)
  {
    default:
      return &_hb_ot_complex_shaper_default;

    /* Unicode-1.1 additions */
    case HB_SCRIPT_ARABIC:

    /* Unicode-3.0 additions */
    case HB_SCRIPT_MONGOLIAN:
    case HB_SCRIPT_SYRIAC:

    /* Unicode-5.0 additions */
    case HB_SCRIPT_NKO:
    case HB_SCRIPT_PHAGS_PA:

    /* Unicode-6.0 additions */
    case HB_SCRIPT_MANDAIC:

    /* Unicode-7.0 additions */
    case HB_SCRIPT_MANICHAEAN:
    case HB_SCRIPT_PSALTER_PAHLAVI:

    /* Unicode-9.0 additions */
    case HB_SCRIPT_ADLAM:

    /* Unicode-11.0 additions */
    case HB_SCRIPT_HANIFI_ROHINGYA:
    case HB_SCRIPT_SOGDIAN:

      /* For Arabic script, use the Arabic shaper even if no OT script tag was found.
       * This is because we do fallback shaping for Arabic script (and not others).
       * But note that Arabic shaping is applicable only to horizontal layout; for
       * vertical text, just use the generic shaper instead. */
      if ((planner->map.chosen_script[0] != HB_OT_TAG_DEFAULT_SCRIPT ||
	   planner->props.script == HB_SCRIPT_ARABIC) &&
	  HB_DIRECTION_IS_HORIZONTAL(planner->props.direction))
	return &_hb_ot_complex_shaper_arabic;
      else
	return &_hb_ot_complex_shaper_default;

    /* Unicode-1.1 additions */
    case HB_SCRIPT_THAI:
    case HB_SCRIPT_LAO:

      return &_hb_ot_complex_shaper_thai;

    /* Unicode-1.1 additions */
    case HB_SCRIPT_HANGUL:

      return &_hb_ot_complex_shaper_hangul;

    /* Unicode-1.1 additions */
    case HB_SCRIPT_HEBREW:

      return &_hb_ot_complex_shaper_hebrew;

    /* Unicode-1.1 additions */
    case HB_SCRIPT_BENGALI:
    case HB_SCRIPT_DEVANAGARI:
    case HB_SCRIPT_GUJARATI:
    case HB_SCRIPT_GURMUKHI:
    case HB_SCRIPT_KANNADA:
    case HB_SCRIPT_MALAYALAM:
    case HB_SCRIPT_ORIYA:
    case HB_SCRIPT_TAMIL:
    case HB_SCRIPT_TELUGU:

    /* Unicode-3.0 additions */
    case HB_SCRIPT_SINHALA:

      /* If the designer designed the font for the 'DFLT' script,
       * (or we ended up arbitrarily pick 'latn'), use the default shaper.
       * Otherwise, use the specific shaper.
       *
       * If it's indy3 tag, send to USE. */
      if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n'))
	return &_hb_ot_complex_shaper_default;
      else if ((planner->map.chosen_script[0] & 0x000000FF) == '3')
	return &_hb_ot_complex_shaper_use;
      else
	return &_hb_ot_complex_shaper_indic;

    case HB_SCRIPT_KHMER:
	return &_hb_ot_complex_shaper_khmer;

    case HB_SCRIPT_MYANMAR:
      /* If the designer designed the font for the 'DFLT' script,
       * (or we ended up arbitrarily pick 'latn'), use the default shaper.
       * Otherwise, use the specific shaper.
       *
       * If designer designed for 'mymr' tag, also send to default
       * shaper.  That's tag used from before Myanmar shaping spec
       * was developed.  The shaping spec uses 'mym2' tag. */
      if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n') ||
	  planner->map.chosen_script[0] == HB_TAG ('m','y','m','r'))
	return &_hb_ot_complex_shaper_default;
      else
	return &_hb_ot_complex_shaper_myanmar;

    /* https://github.com/harfbuzz/harfbuzz/issues/1162 */
    case HB_SCRIPT_MYANMAR_ZAWGYI:

      return &_hb_ot_complex_shaper_myanmar_zawgyi;

    /* Unicode-2.0 additions */
    case HB_SCRIPT_TIBETAN:

    /* Unicode-3.0 additions */
    //case HB_SCRIPT_MONGOLIAN:
    //case HB_SCRIPT_SINHALA:

    /* Unicode-3.2 additions */
    case HB_SCRIPT_BUHID:
    case HB_SCRIPT_HANUNOO:
    case HB_SCRIPT_TAGALOG:
    case HB_SCRIPT_TAGBANWA:

    /* Unicode-4.0 additions */
    case HB_SCRIPT_LIMBU:
    case HB_SCRIPT_TAI_LE:

    /* Unicode-4.1 additions */
    case HB_SCRIPT_BUGINESE:
    case HB_SCRIPT_KHAROSHTHI:
    case HB_SCRIPT_SYLOTI_NAGRI:
    case HB_SCRIPT_TIFINAGH:

    /* Unicode-5.0 additions */
    case HB_SCRIPT_BALINESE:
    //case HB_SCRIPT_NKO:
    //case HB_SCRIPT_PHAGS_PA:

    /* Unicode-5.1 additions */
    case HB_SCRIPT_CHAM:
    case HB_SCRIPT_KAYAH_LI:
    case HB_SCRIPT_LEPCHA:
    case HB_SCRIPT_REJANG:
    case HB_SCRIPT_SAURASHTRA:
    case HB_SCRIPT_SUNDANESE:

    /* Unicode-5.2 additions */
    case HB_SCRIPT_EGYPTIAN_HIEROGLYPHS:
    case HB_SCRIPT_JAVANESE:
    case HB_SCRIPT_KAITHI:
    case HB_SCRIPT_MEETEI_MAYEK:
    case HB_SCRIPT_TAI_THAM:
    case HB_SCRIPT_TAI_VIET:

    /* Unicode-6.0 additions */
    case HB_SCRIPT_BATAK:
    case HB_SCRIPT_BRAHMI:
    //case HB_SCRIPT_MANDAIC:

    /* Unicode-6.1 additions */
    case HB_SCRIPT_CHAKMA:
    case HB_SCRIPT_SHARADA:
    case HB_SCRIPT_TAKRI:

    /* Unicode-7.0 additions */
    case HB_SCRIPT_DUPLOYAN:
    case HB_SCRIPT_GRANTHA:
    case HB_SCRIPT_KHOJKI:
    case HB_SCRIPT_KHUDAWADI:
    case HB_SCRIPT_MAHAJANI:
    //case HB_SCRIPT_MANICHAEAN:
    case HB_SCRIPT_MODI:
    case HB_SCRIPT_PAHAWH_HMONG:
    //case HB_SCRIPT_PSALTER_PAHLAVI:
    case HB_SCRIPT_SIDDHAM:
    case HB_SCRIPT_TIRHUTA:

    /* Unicode-8.0 additions */
    case HB_SCRIPT_AHOM:

    /* Unicode-9.0 additions */
    //case HB_SCRIPT_ADLAM:
    case HB_SCRIPT_BHAIKSUKI:
    case HB_SCRIPT_MARCHEN:
    case HB_SCRIPT_NEWA:

    /* Unicode-10.0 additions */
    case HB_SCRIPT_MASARAM_GONDI:
    case HB_SCRIPT_SOYOMBO:
    case HB_SCRIPT_ZANABAZAR_SQUARE:

    /* Unicode-11.0 additions */
    case HB_SCRIPT_DOGRA:
    case HB_SCRIPT_GUNJALA_GONDI:
    //case HB_SCRIPT_HANIFI_ROHINGYA:
    case HB_SCRIPT_MAKASAR:
    //case HB_SCRIPT_SOGDIAN:

    /* Unicode-12.0 additions */
    case HB_SCRIPT_NANDINAGARI:

      /* If the designer designed the font for the 'DFLT' script,
       * (or we ended up arbitrarily pick 'latn'), use the default shaper.
       * Otherwise, use the specific shaper.
       * Note that for some simple scripts, there may not be *any*
       * GSUB/GPOS needed, so there may be no scripts found! */
      if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n'))
	return &_hb_ot_complex_shaper_default;
      else
	return &_hb_ot_complex_shaper_use;
  }
}

#endif /* HB_OT_SHAPE_COMPLEX_HH */


text:
/*
 * Copyright © 2015  Google, Inc.
 * Copyright © 2019  Adobe Inc.
 * Copyright © 2019  Ebrahim Byagowi
 *
 *  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, Garret Rieger, Roderick Sheeter
 * Adobe Author(s): Michiharu Ariza
 */

#ifndef HB_OT_GLYF_TABLE_HH
#define HB_OT_GLYF_TABLE_HH

#include "hb-open-type.hh"
#include "hb-ot-head-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-var-gvar-table.hh"
#include "hb-draw.hh"

namespace OT {

/*
 * loca -- Index to Location
 * https://docs.microsoft.com/en-us/typography/opentype/spec/loca
 */
#define HB_OT_TAG_loca HB_TAG('l','o','c','a')

struct loca
{
  friend struct glyf;

  static constexpr hb_tag_t tableTag = HB_OT_TAG_loca;

  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
  {
    TRACE_SANITIZE (this);
    return_trace (true);
  }

  protected:
  UnsizedArrayOf<HBUINT8>
		dataZ;	/* Location data. */
  public:
  DEFINE_SIZE_MIN (0);	/* In reality, this is UNBOUNDED() type; but since we always
			 * check the size externally, allow Null() object of it by
			 * defining it _MIN instead. */
};

/*
 * glyf -- TrueType Glyph Data
 * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
 */
#define HB_OT_TAG_glyf HB_TAG('g','l','y','f')

struct glyf
{
  static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;

  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
  {
    TRACE_SANITIZE (this);
    /* Runtime checks as eager sanitizing each glyph is costy */
    return_trace (true);
  }

  template<typename Iterator,
	   hb_requires (hb_is_source_of (Iterator, unsigned int))>
  static bool
  _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets)
  {
    unsigned max_offset = + padded_offsets | hb_reduce(hb_add, 0);
    unsigned num_offsets = padded_offsets.len () + 1;
    bool use_short_loca = max_offset < 0x1FFFF;
    unsigned entry_size = use_short_loca ? 2 : 4;
    char *loca_prime_data = (char *) calloc (entry_size, num_offsets);

    if (unlikely (!loca_prime_data)) return false;

    DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d "
				"max_offset %d size %d",
	       entry_size, num_offsets, max_offset, entry_size * num_offsets);

    if (use_short_loca)
      _write_loca (padded_offsets, 1, hb_array ((HBUINT16*) loca_prime_data, num_offsets));
    else
      _write_loca (padded_offsets, 0, hb_array ((HBUINT32*) loca_prime_data, num_offsets));

    hb_blob_t * loca_blob = hb_blob_create (loca_prime_data,
					    entry_size * num_offsets,
					    HB_MEMORY_MODE_WRITABLE,
					    loca_prime_data,
					    free);

    bool result = plan->add_table (HB_OT_TAG_loca, loca_blob)
		  && _add_head_and_set_loca_version (plan, use_short_loca);

    hb_blob_destroy (loca_blob);
    return result;
  }

  template<typename IteratorIn, typename IteratorOut,
	   hb_requires (hb_is_source_of (IteratorIn, unsigned int)),
	   hb_requires (hb_is_sink_of (IteratorOut, unsigned))>
  static void
  _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest)
  {
    unsigned int offset = 0;
    dest << 0;
    + it
    | hb_map ([=, &offset] (unsigned int padded_size)
	      {
		offset += padded_size;
		DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset);
		return offset >> right_shift;
	      })
    | hb_sink (dest)
    ;
  }

  /* requires source of SubsetGlyph complains the identifier isn't declared */
  template <typename Iterator>
  bool serialize (hb_serialize_context_t *c,
		  Iterator it,
		  const hb_subset_plan_t *plan)
  {
    TRACE_SERIALIZE (this);
    unsigned init_len = c->length ();
    for (const auto &_ : it) _.serialize (c, plan);

    /* As a special case when all glyph in the font are empty, add a zero byte
     * to the table, so that OTS doesn’t reject it, and to make the table work
     * on Windows as well.
     * See https://github.com/khaledhosny/ots/issues/52 */
    if (init_len == c->length ())
    {
      HBUINT8 empty_byte;
      empty_byte = 0;
      c->copy (empty_byte);
    }
    return_trace (true);
  }

  /* Byte region(s) per glyph to output
     unpadded, hints removed if so requested
     If we fail to process a glyph we produce an empty (0-length) glyph */
  bool subset (hb_subset_context_t *c) const
  {
    TRACE_SUBSET (this);

    glyf *glyf_prime = c->serializer->start_embed <glyf> ();
    if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);

    hb_vector_t<SubsetGlyph> glyphs;
    _populate_subset_glyphs (c->plan, &glyphs);

    glyf_prime->serialize (c->serializer, hb_iter (glyphs), c->plan);

    auto padded_offsets =
    + hb_iter (glyphs)
    | hb_map (&SubsetGlyph::padded_size)
    ;

    if (c->serializer->in_error ()) return_trace (false);
    return_trace (c->serializer->check_success (_add_loca_and_head (c->plan,
								    padded_offsets)));
  }

  template <typename SubsetGlyph>
  void
  _populate_subset_glyphs (const hb_subset_plan_t   *plan,
			   hb_vector_t<SubsetGlyph> *glyphs /* OUT */) const
  {
    OT::glyf::accelerator_t glyf;
    glyf.init (plan->source);

    + hb_range (plan->num_output_glyphs ())
    | hb_map ([&] (hb_codepoint_t new_gid)
	      {
		SubsetGlyph subset_glyph = {0};
		subset_glyph.new_gid = new_gid;

		/* should never fail: all old gids should be mapped */
		if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid))
		  return subset_glyph;

		subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true);
		if (plan->drop_hints) subset_glyph.drop_hints_bytes ();
		else subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();

		return subset_glyph;
	      })
    | hb_sink (glyphs)
    ;

    glyf.fini ();
  }

  static bool
  _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
  {
    hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<head> (plan->source);
    hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob);
    hb_blob_destroy (head_blob);

    if (unlikely (!head_prime_blob))
      return false;

    head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
    head_prime->indexToLocFormat = use_short_loca ? 0 : 1;
    bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);

    hb_blob_destroy (head_prime_blob);
    return success;
  }

  struct CompositeGlyphChain
  {
    enum composite_glyph_flag_t
    {
      ARG_1_AND_2_ARE_WORDS	= 0x0001,
      ARGS_ARE_XY_VALUES	= 0x0002,
      ROUND_XY_TO_GRID		= 0x0004,
      WE_HAVE_A_SCALE		= 0x0008,
      MORE_COMPONENTS		= 0x0020,
      WE_HAVE_AN_X_AND_Y_SCALE	= 0x0040,
      WE_HAVE_A_TWO_BY_TWO	= 0x0080,
      WE_HAVE_INSTRUCTIONS	= 0x0100,
      USE_MY_METRICS		= 0x0200,
      OVERLAP_COMPOUND		= 0x0400,
      SCALED_COMPONENT_OFFSET	= 0x0800,
      UNSCALED_COMPONENT_OFFSET = 0x1000
    };

    unsigned int get_size () const
    {
      unsigned int size = min_size;
      /* arg1 and 2 are int16 */
      if (flags & ARG_1_AND_2_ARE_WORDS) size += 4;
      /* arg1 and 2 are int8 */
      else size += 2;

      /* One x 16 bit (scale) */
      if (flags & WE_HAVE_A_SCALE) size += 2;
      /* Two x 16 bit (xscale, yscale) */
      else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4;
      /* Four x 16 bit (xscale, scale01, scale10, yscale) */
      else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8;

      return size;
    }

    bool is_use_my_metrics () const { return   flags & USE_MY_METRICS; }
    bool is_anchored ()       const { return !(flags & ARGS_ARE_XY_VALUES); }
    void get_anchor_points (unsigned int &point1, unsigned int &point2) const
    {
      const HBUINT8 *p = &StructAfter<const HBUINT8> (glyphIndex);
      if (flags & ARG_1_AND_2_ARE_WORDS)
      {
	point1 = ((const HBUINT16 *) p)[0];
	point2 = ((const HBUINT16 *) p)[1];
      }
      else
      {
	point1 = p[0];
	point2 = p[1];
      }
    }

    void transform_points (contour_point_vector_t &points) const
    {
      float matrix[4];
      contour_point_t trans;
      if (get_transformation (matrix, trans))
      {
	if (scaled_offsets ())
	{
	  points.translate (trans);
	  points.transform (matrix);
	}
	else
	{
	  points.transform (matrix);
	  points.translate (trans);
	}
      }
    }

    protected:
    bool scaled_offsets () const
    { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; }

    bool get_transformation (float (&matrix)[4], contour_point_t &trans) const
    {
      matrix[0] = matrix[3] = 1.f;
      matrix[1] = matrix[2] = 0.f;

      int tx, ty;
      const HBINT8 *p = &StructAfter<const HBINT8> (glyphIndex);
      if (flags & ARG_1_AND_2_ARE_WORDS)
      {
	tx = *(const HBINT16 *) p;
	p += HBINT16::static_size;
	ty = *(const HBINT16 *) p;
	p += HBINT16::static_size;
      }
      else
      {
	tx = *p++;
	ty = *p++;
      }
      if (is_anchored ()) tx = ty = 0;

      trans.init ((float) tx, (float) ty);

      {
	const F2DOT14 *points = (const F2DOT14 *) p;
	if (flags & WE_HAVE_A_SCALE)
	{
	  matrix[0] = matrix[3] = points[0].to_float ();
	  return true;
	}
	else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
	{
	  matrix[0] = points[0].to_float ();
	  matrix[3] = points[1].to_float ();
	  return true;
	}
	else if (flags & WE_HAVE_A_TWO_BY_TWO)
	{
	  matrix[0] = points[0].to_float ();
	  matrix[1] = points[1].to_float ();
	  matrix[2] = points[2].to_float ();
	  matrix[3] = points[3].to_float ();
	  return true;
	}
      }
      return tx || ty;
    }

    public:
    HBUINT16	flags;
    HBGlyphID	glyphIndex;
    public:
    DEFINE_SIZE_MIN (4);
  };

  struct composite_iter_t : hb_iter_with_fallback_t<composite_iter_t, const CompositeGlyphChain &>
  {
    typedef const CompositeGlyphChain *__item_t__;
    composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) :
      glyph (glyph_), current (current_)
    { if (!check_range (current)) current = nullptr; }
    composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr) {}

    const CompositeGlyphChain &__item__ () const { return *current; }
    bool __more__ () const { return current; }
    void __next__ ()
    {
      if (!(current->flags & CompositeGlyphChain::MORE_COMPONENTS)) { current = nullptr; return; }

      const CompositeGlyphChain *possible = &StructAfter<CompositeGlyphChain,
							 CompositeGlyphChain> (*current);
      if (!check_range (possible)) { current = nullptr; return; }
      current = possible;
    }
    bool operator != (const composite_iter_t& o) const
    { return glyph != o.glyph || current != o.current; }

    bool check_range (const CompositeGlyphChain *composite) const
    {
      return glyph.check_range (composite, CompositeGlyphChain::min_size)
	  && glyph.check_range (composite, composite->get_size ());
    }

    private:
    hb_bytes_t glyph;
    __item_t__ current;
  };

  enum phantom_point_index_t
  {
    PHANTOM_LEFT   = 0,
    PHANTOM_RIGHT  = 1,
    PHANTOM_TOP    = 2,
    PHANTOM_BOTTOM = 3,
    PHANTOM_COUNT  = 4
  };

  struct Glyph
  {
    enum simple_glyph_flag_t
    {
      FLAG_ON_CURVE  = 0x01,
      FLAG_X_SHORT   = 0x02,
      FLAG_Y_SHORT   = 0x04,
      FLAG_REPEAT    = 0x08,
      FLAG_X_SAME    = 0x10,
      FLAG_Y_SAME    = 0x20,
      FLAG_RESERVED1 = 0x40,
      FLAG_RESERVED2 = 0x80
    };

    private:
    struct GlyphHeader
    {
      bool has_data () const { return numberOfContours; }

      bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
      {
	/* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */
	/* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */
	extents->x_bearing = font->em_scale_x (font->face->table.hmtx->get_side_bearing (gid));
	extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax));
	extents->width     = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax));
	extents->height    = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax));

	return true;
      }

      HBINT16	numberOfContours;
			/* If the number of contours is
			 * greater than or equal to zero,
			 * this is a simple glyph; if negative,
			 * this is a composite glyph. */
      FWORD	xMin;	/* Minimum x for coordinate data. */
      FWORD	yMin;	/* Minimum y for coordinate data. */
      FWORD	xMax;	/* Maximum x for coordinate data. */
      FWORD	yMax;	/* Maximum y for coordinate data. */
      public:
      DEFINE_SIZE_STATIC (10);
    };

    struct SimpleGlyph
    {
      const GlyphHeader &header;
      hb_bytes_t bytes;
      SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
	header (header_), bytes (bytes_) {}

      unsigned int instruction_len_offset () const
      { return GlyphHeader::static_size + 2 * header.numberOfContours; }

      unsigned int length (unsigned int instruction_len) const
      { return instruction_len_offset () + 2 + instruction_len; }

      unsigned int instructions_length () const
      {
	unsigned int instruction_length_offset = instruction_len_offset ();
	if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0;

	const HBUINT16 &instructionLength = StructAtOffset<HBUINT16> (&bytes, instruction_length_offset);
	/* Out of bounds of the current glyph */
	if (unlikely (length (instructionLength) > bytes.length)) return 0;
	return instructionLength;
      }

      const Glyph trim_padding () const
      {
	/* based on FontTools _g_l_y_f.py::trim */
	const char *glyph = bytes.arrayZ;
	const char *glyph_end = glyph + bytes.length;
	/* simple glyph w/contours, possibly trimmable */
	glyph += instruction_len_offset ();

	if (unlikely (glyph + 2 >= glyph_end)) return Glyph ();
	unsigned int num_coordinates = StructAtOffset<HBUINT16> (glyph - 2, 0) + 1;
	unsigned int num_instructions = StructAtOffset<HBUINT16> (glyph, 0);

	glyph += 2 + num_instructions;

	unsigned int coord_bytes = 0;
	unsigned int coords_with_flags = 0;
	while (glyph < glyph_end)
	{
	  uint8_t flag = *glyph;
	  glyph++;

	  unsigned int repeat = 1;
	  if (flag & FLAG_REPEAT)
	  {
	    if (unlikely (glyph >= glyph_end)) return Glyph ();
	    repeat = *glyph + 1;
	    glyph++;
	  }

	  unsigned int xBytes, yBytes;
	  xBytes = yBytes = 0;
	  if (flag & FLAG_X_SHORT) xBytes = 1;
	  else if ((flag & FLAG_X_SAME) == 0) xBytes = 2;

	  if (flag & FLAG_Y_SHORT) yBytes = 1;
	  else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2;

	  coord_bytes += (xBytes + yBytes) * repeat;
	  coords_with_flags += repeat;
	  if (coords_with_flags >= num_coordinates) break;
	}

	if (unlikely (coords_with_flags != num_coordinates)) return Glyph ();
	return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph)));
      }

      /* zero instruction length */
      void drop_hints ()
      {
	GlyphHeader &glyph_header = const_cast<GlyphHeader &> (header);
	(HBUINT16 &) StructAtOffset<HBUINT16> (&glyph_header, instruction_len_offset ()) = 0;
      }

      void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
      {
	unsigned int instructions_len = instructions_length ();
	unsigned int glyph_length = length (instructions_len);
	dest_start = bytes.sub_array (0, glyph_length - instructions_len);
	dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length);
      }

      static bool read_points (const HBUINT8 *&p /* IN/OUT */,
			       contour_point_vector_t &points_ /* IN/OUT */,
			       const hb_bytes_t &bytes,
			       void (* setter) (contour_point_t &_, float v),
			       const simple_glyph_flag_t short_flag,
			       const simple_glyph_flag_t same_flag)
      {
	float v = 0;
	for (unsigned i = 0; i < points_.length; i++)
	{
	  uint8_t flag = points_[i].flag;
	  if (flag & short_flag)
	  {
	    if (unlikely (!bytes.check_range (p))) return false;
	    if (flag & same_flag)
	      v += *p++;
	    else
	      v -= *p++;
	  }
	  else
	  {
	    if (!(flag & same_flag))
	    {
	      if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false;
	      v += *(const HBINT16 *) p;
	      p += HBINT16::static_size;
	    }
	  }
	  setter (points_[i], v);
	}
	return true;
      }

      bool get_contour_points (contour_point_vector_t &points_ /* OUT */,
			       bool phantom_only = false) const
      {
	const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header);
	int num_contours = header.numberOfContours;
	if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false;
	unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;

	points_.resize (num_points);
	for (unsigned int i = 0; i < points_.length; i++) points_[i].init ();
	if (phantom_only) return true;

	for (int i = 0; i < num_contours; i++)
	  points_[endPtsOfContours[i]].is_end_point = true;

	/* Skip instructions */
	const HBUINT8 *p = &StructAtOffset<HBUINT8> (&endPtsOfContours[num_contours + 1],
						     endPtsOfContours[num_contours]);

	/* Read flags */
	for (unsigned int i = 0; i < num_points; i++)
	{
	  if (unlikely (!bytes.check_range (p))) return false;
	  uint8_t flag = *p++;
	  points_[i].flag = flag;
	  if (flag & FLAG_REPEAT)
	  {
	    if (unlikely (!bytes.check_range (p))) return false;
	    unsigned int repeat_count = *p++;
	    while ((repeat_count-- > 0) && (++i < num_points))
	      points_[i].flag = flag;
	  }
	}

	/* Read x & y coordinates */
	return (read_points (p, points_, bytes,
			     [] (contour_point_t &p, float v) { p.x = v; }, FLAG_X_SHORT, FLAG_X_SAME) &&
		read_points (p, points_, bytes,
			     [] (contour_point_t &p, float v) { p.y = v; }, FLAG_Y_SHORT, FLAG_Y_SAME));
      }
    };

    struct CompositeGlyph
    {
      const GlyphHeader &header;
      hb_bytes_t bytes;
      CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
	header (header_), bytes (bytes_) {}

      composite_iter_t get_iterator () const
      { return composite_iter_t (bytes, &StructAfter<CompositeGlyphChain, GlyphHeader> (header)); }

      unsigned int instructions_length (hb_bytes_t bytes) const
      {
	unsigned int start = bytes.length;
	unsigned int end = bytes.length;
	const CompositeGlyphChain *last = nullptr;
	for (auto &item : get_iterator ())
	  last = &item;
	if (unlikely (!last)) return 0;

	if ((uint16_t) last->flags & CompositeGlyphChain::WE_HAVE_INSTRUCTIONS)
	  start = (char *) last - &bytes + last->get_size ();
	if (unlikely (start > end)) return 0;
	return end - start;
      }

      /* Trimming for composites not implemented.
       * If removing hints it falls out of that. */
      const Glyph trim_padding () const { return Glyph (bytes); }

      /* remove WE_HAVE_INSTRUCTIONS flag from composite glyph */
      void drop_hints ()
      {
	for (const auto &_ : get_iterator ())
	  *const_cast<OT::HBUINT16 *> (&_.flags) = (uint16_t) _.flags & ~OT::glyf::CompositeGlyphChain::WE_HAVE_INSTRUCTIONS;
      }

      /* Chop instructions off the end */
      void drop_hints_bytes (hb_bytes_t &dest_start) const
      { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); }
    };

    enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE };

    public:
    composite_iter_t get_composite_iterator () const
    {
      if (type != COMPOSITE) return composite_iter_t ();
      return CompositeGlyph (*header, bytes).get_iterator ();
    }

    const Glyph trim_padding () const
    {
      switch (type) {
      case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
      case SIMPLE:    return SimpleGlyph (*header, bytes).trim_padding ();
      default:        return bytes;
      }
    }

    void drop_hints ()
    {
      switch (type) {
      case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
      case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints (); return;
      default:        return;
      }
    }

    void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
    {
      switch (type) {
      case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
      case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
      default:        return;
      }
    }

    /* Note: Recursively calls itself.
     * all_points includes phantom points
     */
    template<typename T>
    bool get_points (T glyph_for_gid, hb_font_t *font,
		     contour_point_vector_t &all_points /* OUT */,
		     bool phantom_only = false,
		     unsigned int depth = 0) const
    {
      if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
      contour_point_vector_t points;

      switch (type) {
      case COMPOSITE:
      {
	/* pseudo component points for each component in composite glyph */
	unsigned num_points = hb_len (CompositeGlyph (*header, bytes).get_iterator ());
	if (unlikely (!points.resize (num_points))) return false;
	for (unsigned i = 0; i < points.length; i++)
	  points[i].init ();
	break;
      }
      case SIMPLE:
	if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
	  return false;
	break;
      default: return false; /* empty glyph */
      }

      hb_face_t *face = font->face;

      /* Init phantom points */
      if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
      hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
      {
	for (unsigned i = 0; i < PHANTOM_COUNT; ++i) phantoms[i].init ();
	int h_delta = (int) header->xMin - face->table.hmtx->get_side_bearing (gid);
	int v_orig  = (int) header->yMax + face->table.vmtx->get_side_bearing (gid);
	unsigned h_adv = face->table.hmtx->get_advance (gid);
	unsigned v_adv = face->table.vmtx->get_advance (gid);
	phantoms[PHANTOM_LEFT].x = h_delta;
	phantoms[PHANTOM_RIGHT].x = h_adv + h_delta;
	phantoms[PHANTOM_TOP].y = v_orig;
	phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
      }

#ifndef HB_NO_VAR
      if (unlikely (!face->table.gvar->apply_deltas_to_points (gid, font, points.as_array ())))
	return false;
#endif

      switch (type) {
      case SIMPLE:
	all_points.extend (points.as_array ());
	break;
      case COMPOSITE:
      {
	unsigned int comp_index = 0;
	for (auto &item : get_composite_iterator ())
	{
	  contour_point_vector_t comp_points;
	  if (unlikely (!glyph_for_gid (item.glyphIndex).get_points (glyph_for_gid, font, comp_points, phantom_only, depth + 1))
			|| comp_points.length < PHANTOM_COUNT)
	    return false;

	  /* Copy phantom points from component if USE_MY_METRICS flag set */
	  if (item.is_use_my_metrics ())
	    for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
	      phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];

	  /* Apply component transformation & translation */
	  item.transform_points (comp_points);

	  /* Apply translation from gvar */
	  comp_points.translate (points[comp_index]);

	  if (item.is_anchored ())
	  {
	    unsigned int p1, p2;
	    item.get_anchor_points (p1, p2);
	    if (likely (p1 < all_points.length && p2 < comp_points.length))
	    {
	      contour_point_t delta;
	      delta.init (all_points[p1].x - comp_points[p2].x,
			  all_points[p1].y - comp_points[p2].y);

	      comp_points.translate (delta);
	    }
	  }

	  all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT));

	  comp_index++;
	}

	all_points.extend (phantoms);
      } break;
      default: return false;
      }

      if (depth == 0) /* Apply at top level */
      {
	/* Undocumented rasterizer behavior:
	 * Shift points horizontally by the updated left side bearing
	 */
	contour_point_t delta;
	delta.init (-phantoms[PHANTOM_LEFT].x, 0.f);
	if (delta.x) all_points.translate (delta);
      }

      return true;
    }

    bool get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const
    {
      if (type == EMPTY) return true; /* Empty glyph; zero extents. */
      return header->get_extents (font, gid, extents);
    }

    hb_bytes_t get_bytes () const { return bytes; }

    Glyph (hb_bytes_t bytes_ = hb_bytes_t (),
	   hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_),
							header (bytes.as<GlyphHeader> ())
    {
      int num_contours = header->numberOfContours;
      if (unlikely (num_contours == 0)) type = EMPTY;
      else if (num_contours > 0) type = SIMPLE;
      else type = COMPOSITE; /* negative numbers */
    }

    protected:
    hb_bytes_t bytes;
    hb_codepoint_t gid;
    const GlyphHeader *header;
    unsigned type;
  };

  struct accelerator_t
  {
    void init (hb_face_t *face_)
    {
      short_offset = false;
      num_glyphs = 0;
      loca_table = nullptr;
      glyf_table = nullptr;
      face = face_;
      const OT::head &head = *face->table.head;
      if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0)
	/* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
	return;
      short_offset = 0 == head.indexToLocFormat;

      loca_table = hb_sanitize_context_t ().reference_table<loca> (face);
      glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);

      num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
      num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
    }

    void fini ()
    {
      loca_table.destroy ();
      glyf_table.destroy ();
    }

    protected:
    template<typename T>
    bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const
    {
      /* Making this alloc free is not that easy
	 https://github.com/harfbuzz/harfbuzz/issues/2095
	 mostly because of gvar handling in VF fonts,
	 perhaps a separate path for non-VF fonts can be considered */
      contour_point_vector_t all_points;

      bool phantom_only = !consumer.is_consuming_contour_points ();
      if (unlikely (!glyph_for_gid (gid).get_points ([this] (hb_codepoint_t gid) -> const Glyph { return this->glyph_for_gid (gid); },
						     font, all_points, phantom_only)))
	return false;

      if (consumer.is_consuming_contour_points ())
      {
	for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index)
	  consumer.consume_point (all_points[point_index]);
	consumer.points_end ();
      }

      /* Where to write phantoms, nullptr if not requested */
      contour_point_t *phantoms = consumer.get_phantoms_sink ();
      if (phantoms)
	for (unsigned i = 0; i < PHANTOM_COUNT; ++i)
	  phantoms[i] = all_points[all_points.length - PHANTOM_COUNT + i];

      return true;
    }

    public:
#ifndef HB_NO_VAR
    struct points_aggregator_t
    {
      hb_font_t *font;
      hb_glyph_extents_t *extents;
      contour_point_t *phantoms;

      struct contour_bounds_t
      {
	contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }

	void add (const contour_point_t &p)
	{
	  min_x = hb_min (min_x, p.x);
	  min_y = hb_min (min_y, p.y);
	  max_x = hb_max (max_x, p.x);
	  max_y = hb_max (max_y, p.y);
	}

	bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }

	void get_extents (hb_font_t *font, hb_glyph_extents_t *extents)
	{
	  if (unlikely (empty ()))
	  {
	    extents->width = 0;
	    extents->x_bearing = 0;
	    extents->height = 0;
	    extents->y_bearing = 0;
	    return;
	  }
	  extents->x_bearing = font->em_scalef_x (min_x);
	  extents->width = font->em_scalef_x (max_x - min_x);
	  extents->y_bearing = font->em_scalef_y (max_y);
	  extents->height = font->em_scalef_y (min_y - max_y);
	}

	protected:
	float min_x, min_y, max_x, max_y;
      } bounds;

      points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_)
      {
	font = font_;
	extents = extents_;
	phantoms = phantoms_;
	if (extents) bounds = contour_bounds_t ();
      }

      void consume_point (const contour_point_t &point) { bounds.add (point); }
      void points_end () { bounds.get_extents (font, extents); }

      bool is_consuming_contour_points () { return extents; }
      contour_point_t *get_phantoms_sink () { return phantoms; }
    };

    unsigned int
    get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
    {
      bool success = false;

      contour_point_t phantoms[PHANTOM_COUNT];
      if (likely (font->num_coords == face->table.gvar->get_axis_count ()))
	success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms));

      if (unlikely (!success))
	return is_vertical
	     ? face->table.vmtx->get_advance (gid)
	     : face->table.hmtx->get_advance (gid);

      return is_vertical
	   ? roundf (phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y)
	   : roundf (phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x);
    }

    int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
    {
      hb_glyph_extents_t extents;

      contour_point_t phantoms[PHANTOM_COUNT];
      if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms))))
	return is_vertical
	     ? face->table.vmtx->get_side_bearing (gid)
	     : face->table.hmtx->get_side_bearing (gid);

      return is_vertical
	   ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing
	   : floorf (phantoms[PHANTOM_LEFT].x);
    }
#endif

    bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
    {
      if (unlikely (gid >= num_glyphs)) return false;
#ifndef HB_NO_VAR
      if (font->num_coords && font->num_coords == face->table.gvar->get_axis_count ())
	return get_points (font, gid, points_aggregator_t (font, extents, nullptr));
#endif
      return glyph_for_gid (gid).get_extents (font, extents);
    }

    const Glyph
    glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
    {
      unsigned int start_offset, end_offset;
      if (unlikely (gid >= num_glyphs)) return Glyph ();

      if (short_offset)
      {
	const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
	start_offset = 2 * offsets[gid];
	end_offset   = 2 * offsets[gid + 1];
      }
      else
      {
	const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
	start_offset = offsets[gid];
	end_offset   = offsets[gid + 1];
      }

      if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ()))
	return Glyph ();

      Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
			       end_offset - start_offset), gid);
      return needs_padding_removal ? glyph.trim_padding () : glyph;
    }

    void
    add_gid_and_children (hb_codepoint_t gid, hb_set_t *gids_to_retain,
			  unsigned int depth = 0) const
    {
      if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return;
      /* Check if is already visited */
      if (gids_to_retain->has (gid)) return;

      gids_to_retain->add (gid);

      for (auto &item : glyph_for_gid (gid).get_composite_iterator ())
	add_gid_and_children (item.glyphIndex, gids_to_retain, depth);
    }

#ifdef HB_EXPERIMENTAL_API
    struct path_builder_t
    {
      hb_font_t *font;
      draw_helper_t *draw_helper;

      struct optional_point_t
      {
	optional_point_t () { has_data = false; }
	optional_point_t (float x_, float y_) { x = x_; y = y_; has_data = true; }

	bool has_data;
	float x;
	float y;

	optional_point_t lerp (optional_point_t p, float t)
	{ return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
      } first_oncurve, first_offcurve, last_offcurve;

      path_builder_t (hb_font_t *font_, draw_helper_t &draw_helper_)
      {
	font = font_;
	draw_helper = &draw_helper_;
	first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
      }

      /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287
	 See also:
	 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
	 * https://stackoverflow.com/a/20772557 */
      void consume_point (const contour_point_t &point)
      {
	/* Skip empty contours */
	if (unlikely (point.is_end_point && !first_oncurve.has_data && !first_offcurve.has_data))
	  return;

	bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE;
	optional_point_t p (point.x, point.y);
	if (!first_oncurve.has_data)
	{
	  if (is_on_curve)
	  {
	    first_oncurve = p;
	    draw_helper->move_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y));
	  }
	  else
	  {
	    if (first_offcurve.has_data)
	    {
	      optional_point_t mid = first_offcurve.lerp (p, .5f);
	      first_oncurve = mid;
	      last_offcurve = p;
	      draw_helper->move_to (font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
	    }
	    else
	      first_offcurve = p;
	  }
	}
	else
	{
	  if (last_offcurve.has_data)
	  {
	    if (is_on_curve)
	    {
	      draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
					 font->em_scalef_x (p.x), font->em_scalef_y (p.y));
	      last_offcurve = optional_point_t ();
	    }
	    else
	    {
	      optional_point_t mid = last_offcurve.lerp (p, .5f);
	      draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
					 font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
	      last_offcurve = p;
	    }
	  }
	  else
	  {
	    if (is_on_curve)
	      draw_helper->line_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y));
	    else
	      last_offcurve = p;
	  }
	}

	if (point.is_end_point)
	{
	  if (first_offcurve.has_data && last_offcurve.has_data)
	  {
	    optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f);
	    draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
				       font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
	    last_offcurve = optional_point_t ();
	    /* now check the rest */
	  }

	  if (first_offcurve.has_data && first_oncurve.has_data)
	    draw_helper->quadratic_to (font->em_scalef_x (first_offcurve.x), font->em_scalef_y (first_offcurve.y),
				       font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));
	  else if (last_offcurve.has_data && first_oncurve.has_data)
	    draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
				       font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));
	  else if (first_oncurve.has_data)
	    draw_helper->line_to (font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));

	  /* Getting ready for the next contour */
	  first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
	  draw_helper->end_path ();
	}
      }
      void points_end () {}

      bool is_consuming_contour_points () { return true; }
      contour_point_t *get_phantoms_sink () { return nullptr; }
    };

    bool
    get_path (hb_font_t *font, hb_codepoint_t gid, draw_helper_t &draw_helper) const
    { return get_points (font, gid, path_builder_t (font, draw_helper)); }
#endif

    private:
    bool short_offset;
    unsigned int num_glyphs;
    hb_blob_ptr_t<loca> loca_table;
    hb_blob_ptr_t<glyf> glyf_table;
    hb_face_t *face;
  };

  struct SubsetGlyph
  {
    hb_codepoint_t new_gid;
    hb_codepoint_t old_gid;
    Glyph source_glyph;
    hb_bytes_t dest_start;  /* region of source_glyph to copy first */
    hb_bytes_t dest_end;    /* region of source_glyph to copy second */

    bool serialize (hb_serialize_context_t *c,
		    const hb_subset_plan_t *plan) const
    {
      TRACE_SERIALIZE (this);

      hb_bytes_t dest_glyph = dest_start.copy (c);
      dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length);
      unsigned int pad_length = padding ();
      DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length  + pad_length, pad_length);

      HBUINT8 pad;
      pad = 0;
      while (pad_length > 0)
      {
	c->embed (pad);
	pad_length--;
      }

      if (unlikely (!dest_glyph.length)) return_trace (true);

      /* update components gids */
      for (auto &_ : Glyph (dest_glyph).get_composite_iterator ())
      {
	hb_codepoint_t new_gid;
	if (plan->new_gid_for_old_gid (_.glyphIndex, &new_gid))
	  ((OT::glyf::CompositeGlyphChain *) &_)->glyphIndex = new_gid;
      }

      if (plan->drop_hints) Glyph (dest_glyph).drop_hints ();

      return_trace (true);
    }

    void drop_hints_bytes ()
    { source_glyph.drop_hints_bytes (dest_start, dest_end); }

    unsigned int      length () const { return dest_start.length + dest_end.length; }
    /* pad to 2 to ensure 2-byte loca will be ok */
    unsigned int     padding () const { return length () % 2; }
    unsigned int padded_size () const { return length () + padding (); }
  };

  protected:
  UnsizedArrayOf<HBUINT8>
		dataZ;	/* Glyphs data. */
  public:
  DEFINE_SIZE_MIN (0);	/* In reality, this is UNBOUNDED() type; but since we always
			 * check the size externally, allow Null() object of it by
			 * defining it _MIN instead. */
};

struct glyf_accelerator_t : glyf::accelerator_t {};

} /* namespace OT */

#endif /* HB_OT_GLYF_TABLE_HH */