text:
/*
 * Copyright © 2014  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_CMAP_TABLE_HH
#define HB_OT_CMAP_TABLE_HH

#include "hb-open-type.hh"
#include "hb-set.hh"

/*
 * cmap -- Character to Glyph Index Mapping
 * https://docs.microsoft.com/en-us/typography/opentype/spec/cmap
 */
#define HB_OT_TAG_cmap HB_TAG('c','m','a','p')

namespace OT {

struct CmapSubtableFormat0
{
  bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0;
    if (!gid)
      return false;
    *glyph = gid;
    return true;
  }
  void collect_unicodes (hb_set_t *out) const
  {
    for (unsigned int i = 0; i < 256; i++)
      if (glyphIdArray[i])
	out->add (i);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  protected:
  HBUINT16	format;		/* Format number is set to 0. */
  HBUINT16	length;		/* Byte length of this subtable. */
  HBUINT16	language;	/* Ignore. */
  HBUINT8	glyphIdArray[256];/* An array that maps character
				 * code to glyph index values. */
  public:
  DEFINE_SIZE_STATIC (6 + 256);
};

struct CmapSubtableFormat4
{

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  HBUINT16* serialize_endcode_array (hb_serialize_context_t *c,
				     Iterator it)
  {
    HBUINT16 *endCode = c->start_embed<HBUINT16> ();
    hb_codepoint_t prev_endcp = 0xFFFF;

    for (const hb_item_type<Iterator> _ : +it)
    {
      if (prev_endcp != 0xFFFF && prev_endcp + 1u != _.first)
      {
	HBUINT16 end_code;
	end_code = prev_endcp;
	c->copy<HBUINT16> (end_code);
      }
      prev_endcp = _.first;
    }

    {
      // last endCode
      HBUINT16 endcode;
      endcode = prev_endcp;
      if (unlikely (!c->copy<HBUINT16> (endcode))) return nullptr;
      // There must be a final entry with end_code == 0xFFFF.
      if (prev_endcp != 0xFFFF)
      {
	HBUINT16 finalcode;
	finalcode = 0xFFFF;
	if (unlikely (!c->copy<HBUINT16> (finalcode))) return nullptr;
      }
    }

    return endCode;
  }

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  HBUINT16* serialize_startcode_array (hb_serialize_context_t *c,
				       Iterator it)
  {
    HBUINT16 *startCode = c->start_embed<HBUINT16> ();
    hb_codepoint_t prev_cp = 0xFFFF;

    for (const hb_item_type<Iterator> _ : +it)
    {
      if (prev_cp == 0xFFFF || prev_cp + 1u != _.first)
      {
	HBUINT16 start_code;
	start_code = _.first;
	c->copy<HBUINT16> (start_code);
      }

      prev_cp = _.first;
    }

    // There must be a final entry with end_code == 0xFFFF.
    if (it.len () == 0 || prev_cp != 0xFFFF)
    {
      HBUINT16 finalcode;
      finalcode = 0xFFFF;
      if (unlikely (!c->copy<HBUINT16> (finalcode))) return nullptr;
    }

    return startCode;
  }

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  HBINT16* serialize_idDelta_array (hb_serialize_context_t *c,
				     Iterator it,
				     HBUINT16 *endCode,
				     HBUINT16 *startCode,
				     unsigned segcount)
  {
    unsigned i = 0;
    hb_codepoint_t last_gid = 0, start_gid = 0, last_cp = 0xFFFF;
    bool use_delta = true;

    HBINT16 *idDelta = c->start_embed<HBINT16> ();
    if ((char *)idDelta - (char *)startCode != (int) segcount * (int) HBINT16::static_size)
      return nullptr;

    for (const hb_item_type<Iterator> _ : +it)
    {
      if (_.first == startCode[i])
      {
	use_delta = true;
	start_gid = _.second;
      }
      else if (_.second != last_gid + 1) use_delta = false;

      if (_.first == endCode[i])
      {
	HBINT16 delta;
	if (use_delta) delta = (int)start_gid - (int)startCode[i];
	else delta = 0;
	c->copy<HBINT16> (delta);

	i++;
      }

      last_gid = _.second;
      last_cp = _.first;
    }

    if (it.len () == 0 || last_cp != 0xFFFF)
    {
      HBINT16 delta;
      delta = 1;
      if (unlikely (!c->copy<HBINT16> (delta))) return nullptr;
    }

    return idDelta;
  }

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c,
					 Iterator it,
					 HBUINT16 *endCode,
					 HBUINT16 *startCode,
					 HBINT16 *idDelta,
					 unsigned segcount)
  {
    HBUINT16 *idRangeOffset = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount);
    if (unlikely (!c->check_success (idRangeOffset))) return nullptr;
    if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr;

    + hb_range (segcount)
    | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; })
    | hb_apply ([&] (const unsigned i)
		{
		  idRangeOffset[i] = 2 * (c->start_embed<HBUINT16> () - idRangeOffset - i);

		  + it
		  | hb_filter ([&] (const hb_item_type<Iterator> _) { return _.first >= startCode[i] && _.first <= endCode[i]; })
		  | hb_apply ([&] (const hb_item_type<Iterator> _)
			      {
				HBUINT16 glyID;
				glyID = _.second;
				c->copy<HBUINT16> (glyID);
			      })
		  ;

		})
    ;

    return idRangeOffset;
  }

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  void serialize (hb_serialize_context_t *c,
		  Iterator it)
  {
    auto format4_iter =
    + it
    | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
		 { return _.first <= 0xFFFF; })
    ;

    //comment off temporarily while we're working on supporting --gids option
    //if (format4_iter.len () == 0) return;

    unsigned table_initpos = c->length ();
    if (unlikely (!c->extend_min (*this))) return;
    this->format = 4;

    //serialize endCode[]
    HBUINT16 *endCode = serialize_endcode_array (c, format4_iter);
    if (unlikely (!endCode)) return;

    unsigned segcount = (c->length () - min_size) / HBUINT16::static_size;

    // 2 bytes of padding.
    if (unlikely (!c->allocate_size<HBUINT16> (HBUINT16::static_size))) return; // 2 bytes of padding.

   // serialize startCode[]
    HBUINT16 *startCode = serialize_startcode_array (c, format4_iter);
    if (unlikely (!startCode)) return;

    //serialize idDelta[]
    HBINT16 *idDelta = serialize_idDelta_array (c, format4_iter, endCode, startCode, segcount);
    if (unlikely (!idDelta)) return;

    HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, format4_iter, endCode, startCode, idDelta, segcount);
    if (unlikely (!c->check_success (idRangeOffset))) return;

    if (unlikely (!c->check_assign(this->length, c->length () - table_initpos))) return;
    this->segCountX2 = segcount * 2;
    this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1;
    this->searchRange = 2 * (1u << this->entrySelector);
    this->rangeShift = segcount * 2 > this->searchRange
		       ? 2 * segcount - this->searchRange
		       : 0;
  }

  struct accelerator_t
  {
    accelerator_t () {}
    accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); }
    ~accelerator_t () { fini (); }

    void init (const CmapSubtableFormat4 *subtable)
    {
      segCount = subtable->segCountX2 / 2;
      endCount = subtable->values.arrayZ;
      startCount = endCount + segCount + 1;
      idDelta = startCount + segCount;
      idRangeOffset = idDelta + segCount;
      glyphIdArray = idRangeOffset + segCount;
      glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
    }
    void fini () {}

    bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
    {
      struct CustomRange
      {
	int cmp (hb_codepoint_t k,
		 unsigned distance) const
	{
	  if (k > last) return +1;
	  if (k < (&last)[distance]) return -1;
	  return 0;
	}
	HBUINT16 last;
      };

      const HBUINT16 *found =hb_bsearch (codepoint,
					 this->endCount,
					 this->segCount,
					 2,
					 _hb_cmp_method<hb_codepoint_t, CustomRange, unsigned>,
					 this->segCount + 1);
      if (!found)
	return false;
      unsigned int i = found - endCount;

      hb_codepoint_t gid;
      unsigned int rangeOffset = this->idRangeOffset[i];
      if (rangeOffset == 0)
	gid = codepoint + this->idDelta[i];
      else
      {
	/* Somebody has been smoking... */
	unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
	if (unlikely (index >= this->glyphIdArrayLength))
	  return false;
	gid = this->glyphIdArray[index];
	if (unlikely (!gid))
	  return false;
	gid += this->idDelta[i];
      }
      gid &= 0xFFFFu;
      if (!gid)
	return false;
      *glyph = gid;
      return true;
    }

    HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
    { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); }

    void collect_unicodes (hb_set_t *out) const
    {
      unsigned int count = this->segCount;
      if (count && this->startCount[count - 1] == 0xFFFFu)
	count--; /* Skip sentinel segment. */
      for (unsigned int i = 0; i < count; i++)
      {
	hb_codepoint_t start = this->startCount[i];
	hb_codepoint_t end = this->endCount[i];
	unsigned int rangeOffset = this->idRangeOffset[i];
	if (rangeOffset == 0)
	{
	  for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
	  {
	    hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu;
	    if (unlikely (!gid))
	      continue;
	    out->add (codepoint);
	  }
	}
	else
	{
	  for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
	  {
	    unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
	    if (unlikely (index >= this->glyphIdArrayLength))
	      break;
	    hb_codepoint_t gid = this->glyphIdArray[index];
	    if (unlikely (!gid))
	      continue;
	    out->add (codepoint);
	  }
	}
      }
    }

    const HBUINT16 *endCount;
    const HBUINT16 *startCount;
    const HBUINT16 *idDelta;
    const HBUINT16 *idRangeOffset;
    const HBUINT16 *glyphIdArray;
    unsigned int segCount;
    unsigned int glyphIdArrayLength;
  };

  bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    accelerator_t accel (this);
    return accel.get_glyph_func (&accel, codepoint, glyph);
  }
  void collect_unicodes (hb_set_t *out) const
  {
    accelerator_t accel (this);
    accel.collect_unicodes (out);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (unlikely (!c->check_struct (this)))
      return_trace (false);

    if (unlikely (!c->check_range (this, length)))
    {
      /* Some broken fonts have too long of a "length" value.
       * If that is the case, just change the value to truncate
       * the subtable at the end of the blob. */
      uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535,
					    (uintptr_t) (c->end -
							 (char *) this));
      if (!c->try_set (&length, new_length))
	return_trace (false);
    }

    return_trace (16 + 4 * (unsigned int) segCountX2 <= length);
  }

  protected:
  HBUINT16	format;		/* Format number is set to 4. */
  HBUINT16	length;		/* This is the length in bytes of the
				 * subtable. */
  HBUINT16	language;	/* Ignore. */
  HBUINT16	segCountX2;	/* 2 x segCount. */
  HBUINT16	searchRange;	/* 2 * (2**floor(log2(segCount))) */
  HBUINT16	entrySelector;	/* log2(searchRange/2) */
  HBUINT16	rangeShift;	/* 2 x segCount - searchRange */

  UnsizedArrayOf<HBUINT16>
		values;
#if 0
  HBUINT16	endCount[segCount];	/* End characterCode for each segment,
					 * last=0xFFFFu. */
  HBUINT16	reservedPad;		/* Set to 0. */
  HBUINT16	startCount[segCount];	/* Start character code for each segment. */
  HBINT16		idDelta[segCount];	/* Delta for all character codes in segment. */
  HBUINT16	idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */
  UnsizedArrayOf<HBUINT16>
		glyphIdArray;	/* Glyph index array (arbitrary length) */
#endif

  public:
  DEFINE_SIZE_ARRAY (14, values);
};

struct CmapSubtableLongGroup
{
  friend struct CmapSubtableFormat12;
  friend struct CmapSubtableFormat13;
  template<typename U>
  friend struct CmapSubtableLongSegmented;
  friend struct cmap;

  int cmp (hb_codepoint_t codepoint) const
  {
    if (codepoint < startCharCode) return -1;
    if (codepoint > endCharCode)   return +1;
    return 0;
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  private:
  HBUINT32		startCharCode;	/* First character code in this group. */
  HBUINT32		endCharCode;	/* Last character code in this group. */
  HBUINT32		glyphID;	/* Glyph index; interpretation depends on
					 * subtable format. */
  public:
  DEFINE_SIZE_STATIC (12);
};
DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup);

template <typename UINT>
struct CmapSubtableTrimmed
{
  bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    /* Rely on our implicit array bound-checking. */
    hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode];
    if (!gid)
      return false;
    *glyph = gid;
    return true;
  }
  void collect_unicodes (hb_set_t *out) const
  {
    hb_codepoint_t start = startCharCode;
    unsigned int count = glyphIdArray.len;
    for (unsigned int i = 0; i < count; i++)
      if (glyphIdArray[i])
	out->add (start + i);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && glyphIdArray.sanitize (c));
  }

  protected:
  UINT		formatReserved;	/* Subtable format and (maybe) padding. */
  UINT		length;		/* Byte length of this subtable. */
  UINT		language;	/* Ignore. */
  UINT		startCharCode;	/* First character code covered. */
  ArrayOf<HBGlyphID, UINT>
		glyphIdArray;	/* Array of glyph index values for character
				 * codes in the range. */
  public:
  DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray);
};

struct CmapSubtableFormat6  : CmapSubtableTrimmed<HBUINT16> {};
struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {};

template <typename T>
struct CmapSubtableLongSegmented
{
  friend struct cmap;

  bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint);
    if (!gid)
      return false;
    *glyph = gid;
    return true;
  }

  void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const
  {
    for (unsigned int i = 0; i < this->groups.len; i++)
    {
      hb_codepoint_t start = this->groups[i].startCharCode;
      hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode,
				   (hb_codepoint_t) HB_UNICODE_MAX);
      hb_codepoint_t gid = this->groups[i].glyphID;
      if (!gid)
      {
	/* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */
	if (! T::group_get_glyph (this->groups[i], end)) continue;
	start++;
	gid++;
      }
      if (unlikely ((unsigned int) gid >= num_glyphs)) continue;
      if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs))
	end = start + (hb_codepoint_t) num_glyphs - gid;

      out->add_range (start, end);
    }
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && groups.sanitize (c));
  }

  protected:
  HBUINT16	format;		/* Subtable format; set to 12. */
  HBUINT16	reserved;	/* Reserved; set to 0. */
  HBUINT32	length;		/* Byte length of this subtable. */
  HBUINT32	language;	/* Ignore. */
  SortedArrayOf<CmapSubtableLongGroup, HBUINT32>
		groups;		/* Groupings. */
  public:
  DEFINE_SIZE_ARRAY (16, groups);
};

struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
{
  static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
					 hb_codepoint_t u)
  { return likely (group.startCharCode <= group.endCharCode) ?
	   group.glyphID + (u - group.startCharCode) : 0; }

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  void serialize (hb_serialize_context_t *c,
		  Iterator it)
  {
    if (it.len () == 0) return;
    unsigned table_initpos = c->length ();
    if (unlikely (!c->extend_min (*this))) return;

    hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF;
    hb_codepoint_t glyphID = 0;

    for (const hb_item_type<Iterator> _ : +it)
    {
      if (startCharCode == 0xFFFF)
      {
	startCharCode = _.first;
	endCharCode = _.first;
	glyphID = _.second;
      }
      else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second))
      {
	CmapSubtableLongGroup  grouprecord;
	grouprecord.startCharCode = startCharCode;
	grouprecord.endCharCode = endCharCode;
	grouprecord.glyphID = glyphID;
	c->copy<CmapSubtableLongGroup> (grouprecord);

	startCharCode = _.first;
	endCharCode = _.first;
	glyphID = _.second;
      }
      else
	endCharCode = _.first;
    }

    CmapSubtableLongGroup record;
    record.startCharCode = startCharCode;
    record.endCharCode = endCharCode;
    record.glyphID = glyphID;
    c->copy<CmapSubtableLongGroup> (record);

    this->format = 12;
    this->reserved = 0;
    this->length = c->length () - table_initpos;
    this->groups.len = (this->length - min_size)/CmapSubtableLongGroup::static_size;
  }

  static size_t get_sub_table_size (const hb_sorted_vector_t<CmapSubtableLongGroup> &groups_data)
  { return 16 + 12 * groups_data.length; }

  private:
  static bool _is_gid_consecutive (hb_codepoint_t endCharCode,
				   hb_codepoint_t startCharCode,
				   hb_codepoint_t glyphID,
				   hb_codepoint_t cp,
				   hb_codepoint_t new_gid)
  {
    return (cp - 1 == endCharCode) &&
	new_gid == glyphID + (cp - startCharCode);
  }

};

struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13>
{
  static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
					 hb_codepoint_t u HB_UNUSED)
  { return group.glyphID; }
};

typedef enum
{
  GLYPH_VARIANT_NOT_FOUND = 0,
  GLYPH_VARIANT_FOUND = 1,
  GLYPH_VARIANT_USE_DEFAULT = 2
} glyph_variant_t;

struct UnicodeValueRange
{
  int cmp (const hb_codepoint_t &codepoint) const
  {
    if (codepoint < startUnicodeValue) return -1;
    if (codepoint > startUnicodeValue + additionalCount) return +1;
    return 0;
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  HBUINT24	startUnicodeValue;	/* First value in this range. */
  HBUINT8	additionalCount;	/* Number of additional values in this
					 * range. */
  public:
  DEFINE_SIZE_STATIC (4);
};

struct DefaultUVS : SortedArrayOf<UnicodeValueRange, HBUINT32>
{
  void collect_unicodes (hb_set_t *out) const
  {
    unsigned int count = len;
    for (unsigned int i = 0; i < count; i++)
    {
      hb_codepoint_t first = arrayZ[i].startUnicodeValue;
      hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount),
				 (hb_codepoint_t) HB_UNICODE_MAX);
      out->add_range (first, last);
    }
  }

  DefaultUVS* copy (hb_serialize_context_t *c,
		    const hb_set_t *unicodes) const
  {
    DefaultUVS *out = c->start_embed<DefaultUVS> ();
    if (unlikely (!out)) return nullptr;
    auto snap = c->snapshot ();

    HBUINT32 len;
    len = 0;
    if (unlikely (!c->copy<HBUINT32> (len))) return nullptr;
    unsigned init_len = c->length ();

    hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID;
    int count = -1;

    for (const UnicodeValueRange& _ : as_array ())
    {
      for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1))
      {
	unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt;
	if (!unicodes->has (curEntry)) continue;
	count += 1;
	if (lastCode == HB_MAP_VALUE_INVALID)
	  lastCode = curEntry;
	else if (lastCode + count != curEntry)
	{
	  UnicodeValueRange rec;
	  rec.startUnicodeValue = lastCode;
	  rec.additionalCount = count - 1;
	  c->copy<UnicodeValueRange> (rec);

	  lastCode = curEntry;
	  count = 0;
	}
      }
    }

    if (lastCode != HB_MAP_VALUE_INVALID)
    {
      UnicodeValueRange rec;
      rec.startUnicodeValue = lastCode;
      rec.additionalCount = count;
      c->copy<UnicodeValueRange> (rec);
    }

    if (c->length () - init_len == 0)
    {
      c->revert (snap);
      return nullptr;
    }
    else
    {
      if (unlikely (!c->check_assign (out->len, (c->length () - init_len) / UnicodeValueRange::static_size))) return nullptr;
      return out;
    }
  }

  public:
  DEFINE_SIZE_ARRAY (4, *this);
};

struct UVSMapping
{
  int cmp (const hb_codepoint_t &codepoint) const
  { return unicodeValue.cmp (codepoint); }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  HBUINT24	unicodeValue;	/* Base Unicode value of the UVS */
  HBGlyphID	glyphID;	/* Glyph ID of the UVS */
  public:
  DEFINE_SIZE_STATIC (5);
};

struct NonDefaultUVS : SortedArrayOf<UVSMapping, HBUINT32>
{
  void collect_unicodes (hb_set_t *out) const
  {
    unsigned int count = len;
    for (unsigned int i = 0; i < count; i++)
      out->add (arrayZ[i].unicodeValue);
  }

  void closure_glyphs (const hb_set_t      *unicodes,
		       hb_set_t            *glyphset) const
  {
    + as_array ()
    | hb_filter (unicodes, &UVSMapping::unicodeValue)
    | hb_map (&UVSMapping::glyphID)
    | hb_sink (glyphset)
    ;
  }

  NonDefaultUVS* copy (hb_serialize_context_t *c,
		       const hb_set_t *unicodes,
		       const hb_set_t *glyphs,
		       const hb_map_t *glyph_map) const
  {
    NonDefaultUVS *out = c->start_embed<NonDefaultUVS> ();
    if (unlikely (!out)) return nullptr;

    auto it =
    + as_array ()
    | hb_filter ([&] (const UVSMapping& _)
		 {
		   return unicodes->has (_.unicodeValue) || glyphs->has (_.glyphID);
		 })
    ;

    if (!it) return nullptr;

    HBUINT32 len;
    len = it.len ();
    if (unlikely (!c->copy<HBUINT32> (len))) return nullptr;

    for (const UVSMapping& _ : it)
    {
      UVSMapping mapping;
      mapping.unicodeValue = _.unicodeValue;
      mapping.glyphID = glyph_map->get (_.glyphID);
      c->copy<UVSMapping> (mapping);
    }

    return out;
  }

  public:
  DEFINE_SIZE_ARRAY (4, *this);
};

struct VariationSelectorRecord
{
  glyph_variant_t get_glyph (hb_codepoint_t codepoint,
			     hb_codepoint_t *glyph,
			     const void *base) const
  {
    if ((base+defaultUVS).bfind (codepoint))
      return GLYPH_VARIANT_USE_DEFAULT;
    const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint);
    if (nonDefault.glyphID)
    {
      *glyph = nonDefault.glyphID;
       return GLYPH_VARIANT_FOUND;
    }
    return GLYPH_VARIANT_NOT_FOUND;
  }

  VariationSelectorRecord(const VariationSelectorRecord& other)
  {
    *this = other;
  }

  void operator= (const VariationSelectorRecord& other)
  {
    varSelector = other.varSelector;
    HBUINT32 offset = other.defaultUVS;
    defaultUVS = offset;
    offset = other.nonDefaultUVS;
    nonDefaultUVS = offset;
  }

  void collect_unicodes (hb_set_t *out, const void *base) const
  {
    (base+defaultUVS).collect_unicodes (out);
    (base+nonDefaultUVS).collect_unicodes (out);
  }

  int cmp (const hb_codepoint_t &variation_selector) const
  { return varSelector.cmp (variation_selector); }

  bool sanitize (hb_sanitize_context_t *c, const void *base) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
		  defaultUVS.sanitize (c, base) &&
		  nonDefaultUVS.sanitize (c, base));
  }

  hb_pair_t<unsigned, unsigned>
  copy (hb_serialize_context_t *c,
	const hb_set_t *unicodes,
	const hb_set_t *glyphs,
	const hb_map_t *glyph_map,
	const void *base) const
  {
    auto snap = c->snapshot ();
    auto *out = c->embed<VariationSelectorRecord> (*this);
    if (unlikely (!out)) return hb_pair (0, 0);

    out->defaultUVS = 0;
    out->nonDefaultUVS = 0;

    unsigned non_default_uvs_objidx = 0;
    if (nonDefaultUVS != 0)
    {
      c->push ();
      if (c->copy (base+nonDefaultUVS, unicodes, glyphs, glyph_map))
	non_default_uvs_objidx = c->pop_pack ();
      else c->pop_discard ();
    }

    unsigned default_uvs_objidx = 0;
    if (defaultUVS != 0)
    {
      c->push ();
      if (c->copy (base+defaultUVS, unicodes))
	default_uvs_objidx = c->pop_pack ();
      else c->pop_discard ();
    }

    if (!default_uvs_objidx && !non_default_uvs_objidx)
      c->revert (snap);

    return hb_pair (default_uvs_objidx, non_default_uvs_objidx);
  }

  HBUINT24	varSelector;	/* Variation selector. */
  LOffsetTo<DefaultUVS>
		defaultUVS;	/* Offset to Default UVS Table.  May be 0. */
  LOffsetTo<NonDefaultUVS>
		nonDefaultUVS;	/* Offset to Non-Default UVS Table.  May be 0. */
  public:
  DEFINE_SIZE_STATIC (11);
};

struct CmapSubtableFormat14
{
  glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
				     hb_codepoint_t variation_selector,
				     hb_codepoint_t *glyph) const
  { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); }

  void collect_variation_selectors (hb_set_t *out) const
  {
    unsigned int count = record.len;
    for (unsigned int i = 0; i < count; i++)
      out->add (record.arrayZ[i].varSelector);
  }
  void collect_variation_unicodes (hb_codepoint_t variation_selector,
				   hb_set_t *out) const
  { record.bsearch (variation_selector).collect_unicodes (out, this); }

  void serialize (hb_serialize_context_t *c,
		  const hb_set_t *unicodes,
		  const hb_set_t *glyphs,
		  const hb_map_t *glyph_map,
		  const void *base)
  {
    auto snap = c->snapshot ();
    unsigned table_initpos = c->length ();
    const char* init_tail = c->tail;

    if (unlikely (!c->extend_min (*this))) return;
    this->format = 14;

    auto src_tbl = reinterpret_cast<const CmapSubtableFormat14*> (base);

    /*
     * Some versions of OTS require that offsets are in order. Due to the use
     * of push()/pop_pack() serializing the variation records in order results
     * in the offsets being in reverse order (first record has the largest
     * offset). While this is perfectly valid, it will cause some versions of
     * OTS to consider this table bad.
     *
     * So to prevent this issue we serialize the variation records in reverse
     * order, so that the offsets are ordered from small to large. Since
     * variation records are supposed to be in increasing order of varSelector
     * we then have to reverse the order of the written variation selector
     * records after everything is finalized.
     */
    hb_vector_t<hb_pair_t<unsigned, unsigned>> obj_indices;
    for (int i = src_tbl->record.len - 1; i >= 0; i--)
    {
      hb_pair_t<unsigned, unsigned> result = src_tbl->record[i].copy (c, unicodes, glyphs, glyph_map, base);
      if (result.first || result.second)
	obj_indices.push (result);
    }

    if (c->length () - table_initpos == CmapSubtableFormat14::min_size)
    {
      c->revert (snap);
      return;
    }

    int tail_len = init_tail - c->tail;
    c->check_assign (this->length, c->length () - table_initpos + tail_len);
    c->check_assign (this->record.len,
		     (c->length () - table_initpos - CmapSubtableFormat14::min_size) /
		     VariationSelectorRecord::static_size);

    /* Correct the incorrect write order by reversing the order of the variation
       records array. */
    _reverse_variation_records ();

    /* Now that records are in the right order, we can set up the offsets. */
    _add_links_to_variation_records (c, obj_indices);
  }

  void _reverse_variation_records ()
  {
    record.as_array ().reverse ();
  }

  void _add_links_to_variation_records (hb_serialize_context_t *c,
					const hb_vector_t<hb_pair_t<unsigned, unsigned>>& obj_indices)
  {
    for (unsigned i = 0; i < obj_indices.length; i++)
    {
      /*
       * Since the record array has been reversed (see comments in copy())
       * but obj_indices has not been, the indices at obj_indices[i]
       * are for the variation record at record[j].
       */
      int j = obj_indices.length - 1 - i;
      c->add_link (record[j].defaultUVS, obj_indices[i].first);
      c->add_link (record[j].nonDefaultUVS, obj_indices[i].second);
    }
  }

  void closure_glyphs (const hb_set_t      *unicodes,
		       hb_set_t            *glyphset) const
  {
    + hb_iter (record)
    | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS)
    | hb_map (&VariationSelectorRecord::nonDefaultUVS)
    | hb_map (hb_add (this))
    | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); })
    ;
  }

  void collect_unicodes (hb_set_t *out) const
  {
    for (const VariationSelectorRecord& _ : record)
      _.collect_unicodes (out, this);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
		  record.sanitize (c, this));
  }

  protected:
  HBUINT16	format;		/* Format number is set to 14. */
  HBUINT32	length;		/* Byte length of this subtable. */
  SortedArrayOf<VariationSelectorRecord, HBUINT32>
		record;		/* Variation selector records; sorted
				 * in increasing order of `varSelector'. */
  public:
  DEFINE_SIZE_ARRAY (10, record);
};

struct CmapSubtable
{
  /* Note: We intentionally do NOT implement subtable formats 2 and 8. */

  bool get_glyph (hb_codepoint_t codepoint,
		  hb_codepoint_t *glyph) const
  {
    switch (u.format) {
    case  0: return u.format0 .get_glyph (codepoint, glyph);
    case  4: return u.format4 .get_glyph (codepoint, glyph);
    case  6: return u.format6 .get_glyph (codepoint, glyph);
    case 10: return u.format10.get_glyph (codepoint, glyph);
    case 12: return u.format12.get_glyph (codepoint, glyph);
    case 13: return u.format13.get_glyph (codepoint, glyph);
    case 14:
    default: return false;
    }
  }
  void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const
  {
    switch (u.format) {
    case  0: u.format0 .collect_unicodes (out); return;
    case  4: u.format4 .collect_unicodes (out); return;
    case  6: u.format6 .collect_unicodes (out); return;
    case 10: u.format10.collect_unicodes (out); return;
    case 12: u.format12.collect_unicodes (out, num_glyphs); return;
    case 13: u.format13.collect_unicodes (out, num_glyphs); return;
    case 14:
    default: return;
    }
  }

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  void serialize (hb_serialize_context_t *c,
		  Iterator it,
		  unsigned format,
		  const hb_subset_plan_t *plan,
		  const void *base)
  {
    switch (format) {
    case  4: return u.format4.serialize (c, it);
    case 12: return u.format12.serialize (c, it);
    case 14: return u.format14.serialize (c, plan->unicodes, plan->_glyphset, plan->glyph_map, base);
    default: return;
    }
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (!u.format.sanitize (c)) return_trace (false);
    switch (u.format) {
    case  0: return_trace (u.format0 .sanitize (c));
    case  4: return_trace (u.format4 .sanitize (c));
    case  6: return_trace (u.format6 .sanitize (c));
    case 10: return_trace (u.format10.sanitize (c));
    case 12: return_trace (u.format12.sanitize (c));
    case 13: return_trace (u.format13.sanitize (c));
    case 14: return_trace (u.format14.sanitize (c));
    default:return_trace (true);
    }
  }

  public:
  union {
  HBUINT16		format;		/* Format identifier */
  CmapSubtableFormat0	format0;
  CmapSubtableFormat4	format4;
  CmapSubtableFormat6	format6;
  CmapSubtableFormat10	format10;
  CmapSubtableFormat12	format12;
  CmapSubtableFormat13	format13;
  CmapSubtableFormat14	format14;
  } u;
  public:
  DEFINE_SIZE_UNION (2, format);
};

struct EncodingRecord
{
  int cmp (const EncodingRecord &other) const
  {
    int ret;
    ret = platformID.cmp (other.platformID);
    if (ret) return ret;
    ret = encodingID.cmp (other.encodingID);
    if (ret) return ret;
    return 0;
  }

  bool sanitize (hb_sanitize_context_t *c, const void *base) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
		  subtable.sanitize (c, base));
  }

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  EncodingRecord* copy (hb_serialize_context_t *c,
			Iterator it,
			unsigned format,
			const void *base,
			const hb_subset_plan_t *plan,
			/* INOUT */ unsigned *objidx) const
  {
    TRACE_SERIALIZE (this);
    auto snap = c->snapshot ();
    auto *out = c->embed (this);
    if (unlikely (!out)) return_trace (nullptr);
    out->subtable = 0;

    if (*objidx == 0)
    {
      CmapSubtable *cmapsubtable = c->push<CmapSubtable> ();
      unsigned origin_length = c->length ();
      cmapsubtable->serialize (c, it, format, plan, &(base+subtable));
      if (c->length () - origin_length > 0) *objidx = c->pop_pack ();
      else c->pop_discard ();
    }

    if (*objidx == 0)
    {
      c->revert (snap);
      return_trace (nullptr);
    }

    c->add_link (out->subtable, *objidx);
    return_trace (out);
  }

  HBUINT16	platformID;	/* Platform ID. */
  HBUINT16	encodingID;	/* Platform-specific encoding ID. */
  LOffsetTo<CmapSubtable>
		subtable;	/* Byte offset from beginning of table to the subtable for this encoding. */
  public:
  DEFINE_SIZE_STATIC (8);
};

struct cmap
{
  static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap;

  template<typename Iterator, typename EncodingRecIter,
	   hb_requires (hb_is_iterator (Iterator))>
  void serialize (hb_serialize_context_t *c,
		  Iterator it,
		  EncodingRecIter encodingrec_iter,
		  const void *base,
		  const hb_subset_plan_t *plan)
  {
    if (unlikely (!c->extend_min ((*this))))  return;
    this->version = 0;

    unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;

    for (const EncodingRecord& _ : encodingrec_iter)
    {
      hb_set_t unicodes_set;
      (base+_.subtable).collect_unicodes (&unicodes_set);

      unsigned format = (base+_.subtable).u.format;

      if (format == 4) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx);
      else if (format == 12) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 12u, base, plan, &format12objidx);
      else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx);
    }

    c->check_assign(this->encodingRecord.len, (c->length () - cmap::min_size)/EncodingRecord::static_size);
  }

  void closure_glyphs (const hb_set_t      *unicodes,
		       hb_set_t            *glyphset) const
  {
    + hb_iter (encodingRecord)
    | hb_map (&EncodingRecord::subtable)
    | hb_map (hb_add (this))
    | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; })
    | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); })
    ;
  }

  bool subset (hb_subset_context_t *c) const
  {
    TRACE_SUBSET (this);

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

    auto encodingrec_iter =
    + hb_iter (encodingRecord)
    | hb_filter ([&] (const EncodingRecord& _)
		{
		  if ((_.platformID == 0 && _.encodingID == 3) ||
		      (_.platformID == 0 && _.encodingID == 4) ||
		      (_.platformID == 3 && _.encodingID == 1) ||
		      (_.platformID == 3 && _.encodingID == 10) ||
		      (this + _.subtable).u.format == 14)
		    return true;

		  return false;
		})
    ;

    if (unlikely (!encodingrec_iter.len ())) return_trace (false);

    const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr;
    bool has_format12 = false;

    for (const EncodingRecord& _ : encodingrec_iter)
    {
      unsigned format = (this + _.subtable).u.format;
      if (format == 12) has_format12 = true;

      const EncodingRecord *table = hb_addressof (_);
      if      (_.platformID == 0 && _.encodingID ==  3) unicode_bmp = table;
      else if (_.platformID == 0 && _.encodingID ==  4) unicode_ucs4 = table;
      else if (_.platformID == 3 && _.encodingID ==  1) ms_bmp = table;
      else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table;
    }

    if (unlikely (!has_format12 && !unicode_bmp && !ms_bmp)) return_trace (false);
    if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false);

    auto it =
    + hb_iter (c->plan->unicodes)
    | hb_map ([&] (hb_codepoint_t _)
	      {
		hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID;
		c->plan->new_gid_for_codepoint (_, &new_gid);
		return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, new_gid);
	      })
    | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
		 { return (_.second != HB_MAP_VALUE_INVALID); })
    ;

    cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan);
    return_trace (true);
  }

  const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const
  {
    if (symbol) *symbol = false;

    const CmapSubtable *subtable;

    /* Symbol subtable.
     * Prefer symbol if available.
     * https://github.com/harfbuzz/harfbuzz/issues/1918 */
    if ((subtable = this->find_subtable (3, 0)))
    {
      if (symbol) *symbol = true;
      return subtable;
    }

    /* 32-bit subtables. */
    if ((subtable = this->find_subtable (3, 10))) return subtable;
    if ((subtable = this->find_subtable (0, 6))) return subtable;
    if ((subtable = this->find_subtable (0, 4))) return subtable;

    /* 16-bit subtables. */
    if ((subtable = this->find_subtable (3, 1))) return subtable;
    if ((subtable = this->find_subtable (0, 3))) return subtable;
    if ((subtable = this->find_subtable (0, 2))) return subtable;
    if ((subtable = this->find_subtable (0, 1))) return subtable;
    if ((subtable = this->find_subtable (0, 0))) return subtable;

    /* Meh. */
    return &Null (CmapSubtable);
  }

  struct accelerator_t
  {
    void init (hb_face_t *face)
    {
      this->table = hb_sanitize_context_t ().reference_table<cmap> (face);
      bool symbol;
      this->subtable = table->find_best_subtable (&symbol);
      this->subtable_uvs = &Null (CmapSubtableFormat14);
      {
	const CmapSubtable *st = table->find_subtable (0, 5);
	if (st && st->u.format == 14)
	  subtable_uvs = &st->u.format14;
      }

      this->get_glyph_data = subtable;
      if (unlikely (symbol))
	this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable>;
      else
      {
	switch (subtable->u.format) {
	/* Accelerate format 4 and format 12. */
	default:
	  this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
	  break;
	case 12:
	  this->get_glyph_funcZ = get_glyph_from<CmapSubtableFormat12>;
	  break;
	case  4:
	{
	  this->format4_accel.init (&subtable->u.format4);
	  this->get_glyph_data = &this->format4_accel;
	  this->get_glyph_funcZ = this->format4_accel.get_glyph_func;
	  break;
	}
	}
      }
    }

    void fini () { this->table.destroy (); }

    bool get_nominal_glyph (hb_codepoint_t  unicode,
			    hb_codepoint_t *glyph) const
    {
      if (unlikely (!this->get_glyph_funcZ)) return false;
      return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph);
    }
    unsigned int get_nominal_glyphs (unsigned int count,
				     const hb_codepoint_t *first_unicode,
				     unsigned int unicode_stride,
				     hb_codepoint_t *first_glyph,
				     unsigned int glyph_stride) const
    {
      if (unlikely (!this->get_glyph_funcZ)) return 0;

      hb_cmap_get_glyph_func_t get_glyph_funcZ = this->get_glyph_funcZ;
      const void *get_glyph_data = this->get_glyph_data;

      unsigned int done;
      for (done = 0;
	   done < count && get_glyph_funcZ (get_glyph_data, *first_unicode, first_glyph);
	   done++)
      {
	first_unicode = &StructAtOffsetUnaligned<hb_codepoint_t> (first_unicode, unicode_stride);
	first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
      }
      return done;
    }

    bool get_variation_glyph (hb_codepoint_t  unicode,
			      hb_codepoint_t  variation_selector,
			      hb_codepoint_t *glyph) const
    {
      switch (this->subtable_uvs->get_glyph_variant (unicode,
						     variation_selector,
						     glyph))
      {
	case GLYPH_VARIANT_NOT_FOUND:	return false;
	case GLYPH_VARIANT_FOUND:	return true;
	case GLYPH_VARIANT_USE_DEFAULT:	break;
      }

      return get_nominal_glyph (unicode, glyph);
    }

    void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const
    { subtable->collect_unicodes (out, num_glyphs); }
    void collect_variation_selectors (hb_set_t *out) const
    { subtable_uvs->collect_variation_selectors (out); }
    void collect_variation_unicodes (hb_codepoint_t variation_selector,
				     hb_set_t *out) const
    { subtable_uvs->collect_variation_unicodes (variation_selector, out); }

    protected:
    typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
					      hb_codepoint_t codepoint,
					      hb_codepoint_t *glyph);

    template <typename Type>
    HB_INTERNAL static bool get_glyph_from (const void *obj,
					    hb_codepoint_t codepoint,
					    hb_codepoint_t *glyph)
    {
      const Type *typed_obj = (const Type *) obj;
      return typed_obj->get_glyph (codepoint, glyph);
    }

    template <typename Type>
    HB_INTERNAL static bool get_glyph_from_symbol (const void *obj,
						   hb_codepoint_t codepoint,
						   hb_codepoint_t *glyph)
    {
      const Type *typed_obj = (const Type *) obj;
      if (likely (typed_obj->get_glyph (codepoint, glyph)))
	return true;

      if (codepoint <= 0x00FFu)
      {
	/* For symbol-encoded OpenType fonts, we duplicate the
	 * U+F000..F0FF range at U+0000..U+00FF.  That's what
	 * Windows seems to do, and that's hinted about at:
	 * https://docs.microsoft.com/en-us/typography/opentype/spec/recom
	 * under "Non-Standard (Symbol) Fonts". */
	return typed_obj->get_glyph (0xF000u + codepoint, glyph);
      }

      return false;
    }

    private:
    hb_nonnull_ptr_t<const CmapSubtable> subtable;
    hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs;

    hb_cmap_get_glyph_func_t get_glyph_funcZ;
    const void *get_glyph_data;

    CmapSubtableFormat4::accelerator_t format4_accel;

    public:
    hb_blob_ptr_t<cmap> table;
  };

  protected:

  const CmapSubtable *find_subtable (unsigned int platform_id,
				     unsigned int encoding_id) const
  {
    EncodingRecord key;
    key.platformID = platform_id;
    key.encodingID = encoding_id;

    const EncodingRecord &result = encodingRecord.bsearch (key);
    if (!result.subtable)
      return nullptr;

    return &(this+result.subtable);
  }

  const EncodingRecord *find_encodingrec (unsigned int platform_id,
					  unsigned int encoding_id) const
  {
    EncodingRecord key;
    key.platformID = platform_id;
    key.encodingID = encoding_id;

    return encodingRecord.as_array ().bsearch (key);
  }

  bool find_subtable (unsigned format) const
  {
    auto it =
    + hb_iter (encodingRecord)
    | hb_map (&EncodingRecord::subtable)
    | hb_map (hb_add (this))
    | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; })
    ;

    return it.len ();
  }

  public:

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
		  likely (version == 0) &&
		  encodingRecord.sanitize (c, this));
  }

  protected:
  HBUINT16	version;	/* Table version number (0). */
  SortedArrayOf<EncodingRecord>
		encodingRecord;	/* Encoding tables. */
  public:
  DEFINE_SIZE_ARRAY (4, encodingRecord);
};

struct cmap_accelerator_t : cmap::accelerator_t {};

} /* namespace OT */

#endif /* HB_OT_CMAP_TABLE_HH */


text:
/*
 * 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.
 */

#include "hb.hh"

#ifndef HB_NO_META

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

/**
 * SECTION:hb-ot-meta
 * @title: hb-ot-meta
 * @short_description: OpenType Metadata
 * @include: hb-ot.h
 *
 * Functions for fetching metadata from fonts.
 **/

/**
 * hb_ot_meta_reference_entry:
 * @face: a face object
 * @start_offset: iteration's start offset
 * @entries_count:(inout) (allow-none): buffer size as input, filled size as output
 * @entries: (out caller-allocates) (array length=entries_count): entries tags buffer
 *
 * Return value: Number of all available feature types.
 *
 * Since: 2.6.0
 **/
unsigned int
hb_ot_meta_get_entry_tags (hb_face_t        *face,
			   unsigned int      start_offset,
			   unsigned int     *entries_count, /* IN/OUT.  May be NULL. */
			   hb_ot_meta_tag_t *entries        /* OUT.     May be NULL. */)
{
  return face->table.meta->get_entries (start_offset, entries_count, entries);
}

/**
 * hb_ot_meta_reference_entry:
 * @face: a #hb_face_t object.
 * @meta_tag: tag of metadata you like to have.
 *
 * It fetches metadata entry of a given tag from a font.
 *
 * Returns: (transfer full): A blob containing the blob.
 *
 * Since: 2.6.0
 **/
hb_blob_t *
hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag)
{
  return face->table.meta->reference_entry (meta_tag);
}

#endif

repo_language: C

mime_type: text/x-c


text:
/*
 * Copyright © 2013  Red Hat, 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_SHAPE_H
#define HB_OT_SHAPE_H

#include "hb.h"

HB_BEGIN_DECLS

/* TODO port to shape-plan / set. */
HB_EXTERN void
hb_ot_shape_glyphs_closure (hb_font_t          *font,
			    hb_buffer_t        *buffer,
			    const hb_feature_t *features,
			    unsigned int        num_features,
			    hb_set_t           *glyphs);

HB_EXTERN void
hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
				  hb_tag_t         table_tag,
				  hb_set_t        *lookup_indexes /* OUT */);

HB_END_DECLS

#endif /* HB_OT_SHAPE_H */


text:
#include "hb-aat-layout.cc"
#include "hb-aat-map.cc"
#include "hb-blob.cc"
#include "hb-buffer-serialize.cc"
#include "hb-buffer.cc"
#include "hb-common.cc"
#include "hb-draw.cc"
#include "hb-face.cc"
#include "hb-fallback-shape.cc"
#include "hb-font.cc"
#include "hb-map.cc"
#include "hb-number.cc"
#include "hb-ot-cff1-table.cc"
#include "hb-ot-cff2-table.cc"
#include "hb-ot-color.cc"
#include "hb-ot-face.cc"
#include "hb-ot-font.cc"
#include "hb-ot-layout.cc"
#include "hb-ot-map.cc"
#include "hb-ot-math.cc"
#include "hb-ot-meta.cc"
#include "hb-ot-metrics.cc"
#include "hb-ot-name.cc"
#include "hb-ot-shape-complex-arabic.cc"
#include "hb-ot-shape-complex-default.cc"
#include "hb-ot-shape-complex-hangul.cc"
#include "hb-ot-shape-complex-hebrew.cc"
#include "hb-ot-shape-complex-indic-table.cc"
#include "hb-ot-shape-complex-indic.cc"
#include "hb-ot-shape-complex-khmer.cc"
#include "hb-ot-shape-complex-myanmar.cc"
#include "hb-ot-shape-complex-thai.cc"
#include "hb-ot-shape-complex-use-table.cc"
#include "hb-ot-shape-complex-use.cc"
#include "hb-ot-shape-complex-vowel-constraints.cc"
#include "hb-ot-shape-fallback.cc"
#include "hb-ot-shape-normalize.cc"
#include "hb-ot-shape.cc"
#include "hb-ot-tag.cc"
#include "hb-ot-var.cc"
#include "hb-set.cc"
#include "hb-shape-plan.cc"
#include "hb-shape.cc"
#include "hb-shaper.cc"
#include "hb-static.cc"
#include "hb-ucd.cc"
#include "hb-unicode.cc"
#include "hb-glib.cc"
#include "hb-ft.cc"
#include "hb-graphite2.cc"
#include "hb-uniscribe.cc"
#include "hb-gdi.cc"
#include "hb-directwrite.cc"
#include "hb-coretext.cc"


text:
/*
 * Copyright © 2018-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.
 */

#include "hb.hh"

#include "hb-ot-var-mvar-table.hh"
#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise.
#include "hb-ot-os2-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-hhea-table.hh"
#include "hb-ot-metrics.hh"
#include "hb-ot-face.hh"

static float
_fix_ascender_descender (float value, hb_ot_metrics_tag_t metrics_tag)
{
  if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER ||
      metrics_tag == HB_OT_METRICS_TAG_VERTICAL_ASCENDER)
    return fabs ((double) value);
  if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER ||
      metrics_tag == HB_OT_METRICS_TAG_VERTICAL_DESCENDER)
    return -fabs ((double) value);
  return value;
}

/* The common part of _get_position logic needed on hb-ot-font and here
   to be able to have slim builds without the not always needed parts */
bool
_hb_ot_metrics_get_position_common (hb_font_t           *font,
				    hb_ot_metrics_tag_t  metrics_tag,
				    hb_position_t       *position     /* OUT.  May be NULL. */)
{
  hb_face_t *face = font->face;
  switch ((unsigned) metrics_tag)
  {
#ifndef HB_NO_VAR
#define GET_VAR face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords)
#else
#define GET_VAR .0f
#endif
#define GET_METRIC_X(TABLE, ATTR) \
  (face->table.TABLE->has_data () && \
    (position && (*position = font->em_scalef_x (_fix_ascender_descender ( \
      face->table.TABLE->ATTR + GET_VAR, metrics_tag))), true))
#define GET_METRIC_Y(TABLE, ATTR) \
  (face->table.TABLE->has_data () && \
    (position && (*position = font->em_scalef_y (_fix_ascender_descender ( \
      face->table.TABLE->ATTR + GET_VAR, metrics_tag))), true))
  case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER:
    return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoAscender)) ||
	   GET_METRIC_Y (hhea, ascender);
  case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER:
    return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoDescender)) ||
	   GET_METRIC_Y (hhea, descender);
  case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP:
    return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoLineGap)) ||
	   GET_METRIC_Y (hhea, lineGap);
  case HB_OT_METRICS_TAG_VERTICAL_ASCENDER:  return GET_METRIC_X (vhea, ascender);
  case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: return GET_METRIC_X (vhea, descender);
  case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP:  return GET_METRIC_X (vhea, lineGap);
#undef GET_METRIC_Y
#undef GET_METRIC_X
#undef GET_VAR
  default:                               assert (0); return false;
  }
}

#ifndef HB_NO_METRICS

#if 0
static bool
_get_gasp (hb_face_t *face, float *result, hb_ot_metrics_tag_t metrics_tag)
{
  const OT::GaspRange& range = face->table.gasp->get_gasp_range (metrics_tag - HB_TAG ('g','s','p','0'));
  if (&range == &Null (OT::GaspRange)) return false;
  if (result) *result = range.rangeMaxPPEM + font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords);
  return true;
}
#endif

/* Private tags for https://github.com/harfbuzz/harfbuzz/issues/1866 */
#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2   HB_TAG ('O','a','s','c')
#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA  HB_TAG ('H','a','s','c')
#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2  HB_TAG ('O','d','s','c')
#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA HB_TAG ('H','d','s','c')
#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2   HB_TAG ('O','l','g','p')
#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA  HB_TAG ('H','l','g','p')

/**
 * hb_ot_metrics_get_position:
 * @font: a #hb_font_t object.
 * @metrics_tag: tag of metrics value you like to fetch.
 * @position: (out) (optional): result of metrics value from the font.
 *
 * It fetches metrics value corresponding to a given tag from a font.
 *
 * Returns: Whether found the requested metrics in the font.
 * Since: 2.6.0
 **/
hb_bool_t
hb_ot_metrics_get_position (hb_font_t           *font,
			    hb_ot_metrics_tag_t  metrics_tag,
			    hb_position_t       *position     /* OUT.  May be NULL. */)
{
  hb_face_t *face = font->face;
  switch ((unsigned) metrics_tag)
  {
  case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER:
  case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER:
  case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP:
  case HB_OT_METRICS_TAG_VERTICAL_ASCENDER:
  case HB_OT_METRICS_TAG_VERTICAL_DESCENDER:
  case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP:           return _hb_ot_metrics_get_position_common (font, metrics_tag, position);
#ifndef HB_NO_VAR
#define GET_VAR hb_ot_metrics_get_variation (font, metrics_tag)
#else
#define GET_VAR 0
#endif
#define GET_METRIC_X(TABLE, ATTR) \
  (face->table.TABLE->has_data () && \
    (position && (*position = font->em_scalef_x (face->table.TABLE->ATTR + GET_VAR)), true))
#define GET_METRIC_Y(TABLE, ATTR) \
  (face->table.TABLE->has_data () && \
    (position && (*position = font->em_scalef_y (face->table.TABLE->ATTR + GET_VAR)), true))
  case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT:  return GET_METRIC_Y (OS2, usWinAscent);
  case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: return GET_METRIC_Y (OS2, usWinDescent);
  case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE:       return GET_METRIC_Y (hhea, caretSlopeRise);
  case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN:        return GET_METRIC_X (hhea, caretSlopeRun);
  case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET:     return GET_METRIC_X (hhea, caretOffset);
  case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE:         return GET_METRIC_X (vhea, caretSlopeRise);
  case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN:          return GET_METRIC_Y (vhea, caretSlopeRun);
  case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET:       return GET_METRIC_Y (vhea, caretOffset);
  case HB_OT_METRICS_TAG_X_HEIGHT:                    return GET_METRIC_Y (OS2->v2 (), sxHeight);
  case HB_OT_METRICS_TAG_CAP_HEIGHT:                  return GET_METRIC_Y (OS2->v2 (), sCapHeight);
  case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE:         return GET_METRIC_X (OS2, ySubscriptXSize);
  case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE:         return GET_METRIC_Y (OS2, ySubscriptYSize);
  case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET:       return GET_METRIC_X (OS2, ySubscriptXOffset);
  case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET:       return GET_METRIC_Y (OS2, ySubscriptYOffset);
  case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE:       return GET_METRIC_X (OS2, ySuperscriptXSize);
  case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE:       return GET_METRIC_Y (OS2, ySuperscriptYSize);
  case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET:     return GET_METRIC_X (OS2, ySuperscriptXOffset);
  case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET:     return GET_METRIC_Y (OS2, ySuperscriptYOffset);
  case HB_OT_METRICS_TAG_STRIKEOUT_SIZE:              return GET_METRIC_Y (OS2, yStrikeoutSize);
  case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET:            return GET_METRIC_Y (OS2, yStrikeoutPosition);
  case HB_OT_METRICS_TAG_UNDERLINE_SIZE:              return GET_METRIC_Y (post->table, underlineThickness);
  case HB_OT_METRICS_TAG_UNDERLINE_OFFSET:            return GET_METRIC_Y (post->table, underlinePosition);

  /* Private tags */
  case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2:    return GET_METRIC_Y (OS2, sTypoAscender);
  case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA:   return GET_METRIC_Y (hhea, ascender);
  case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2:   return GET_METRIC_Y (OS2, sTypoDescender);
  case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA:  return GET_METRIC_Y (hhea, descender);
  case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2:    return GET_METRIC_Y (OS2, sTypoLineGap);
  case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA:   return GET_METRIC_Y (hhea, lineGap);
#undef GET_METRIC_Y
#undef GET_METRIC_X
#undef GET_VAR
  default:                                        return false;
  }
}

#ifndef HB_NO_VAR
/**
 * hb_ot_metrics_get_variation:
 * @font:
 * @metrics_tag:
 *
 * Returns:
 *
 * Since: 2.6.0
 **/
float
hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag)
{
  return font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords);
}

/**
 * hb_ot_metrics_get_x_variation:
 * @font:
 * @metrics_tag:
 *
 * Returns:
 *
 * Since: 2.6.0
 **/
hb_position_t
hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag)
{
  return font->em_scalef_x (hb_ot_metrics_get_variation (font, metrics_tag));
}

/**
 * hb_ot_metrics_get_y_variation:
 * @font:
 * @metrics_tag:
 *
 * Returns:
 *
 * Since: 2.6.0
 **/
hb_position_t
hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag)
{
  return font->em_scalef_y (hb_ot_metrics_get_variation (font, metrics_tag));
}
#endif

#endif


text:
#!/bin/sh

LC_ALL=C
export LC_ALL

test -z "$srcdir" && srcdir=.
stat=0

test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
test "x$EGREP" = x && EGREP='grep -E'

echo 'Checking that all public symbols are exported with HB_EXTERN'

for x in $HBHEADERS; do
	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
	$EGREP -B1 -n '^hb_' /dev/null "$x" |
	$EGREP -v '(^--|:hb_|-HB_EXTERN )' -A1
done |
grep . >&2 && stat=1

exit $stat


text:
#!/bin/sh

LC_ALL=C
export LC_ALL

test -z "$srcdir" && srcdir=.
test -z "$builddir" && builddir=.
stat=0

if which objdump 2>/dev/null >/dev/null; then
	:
else
	echo "check-static-inits.sh: 'objdump' not found; skipping test"
	exit 77
fi

OBJS=$(find $builddir/ -name '*.o')
if test "x`echo $OBJS`" = "x$OBJS" 2>/dev/null >/dev/null; then
	echo "check-static-inits.sh: object files not found; skipping test"
	exit 77
fi

tested=false

echo "Checking that no object file has static initializers"
for obj in $OBJS; do
	if objdump -t "$obj" | grep '[.][cd]tors' | grep -v '\<00*\>'; then
		echo "Ouch, $obj has static initializers/finalizers"
		stat=1
	fi
	tested=true
done

echo "Checking that no object file has lazy static C++ constructors/destructors or other such stuff"
for obj in $OBJS; do
	if objdump -t "$obj" | grep -q '__cxa_' && ! objdump -t "$obj" | grep -q __ubsan_handle; then
		objdump -t "$obj" | grep '__cxa_'
		echo "Ouch, $obj has lazy static C++ constructors/destructors or other such stuff"
		stat=1
	fi
	tested=true
done

if ! $tested; then
	echo "check-static-inits.sh: no objects found; skipping test"
	exit 77
fi

exit $stat


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_OT_NAME_LANGUAGE_STATIC_HH
#define HB_OT_NAME_LANGUAGE_STATIC_HH

#include "hb-ot-name-language.hh"

/* Following two tables were generated by joining FreeType, FontConfig,
 * and OpenType specification language lists, then filled in missing
 * entries using:
 * https://docs.microsoft.com/en-us/windows/desktop/intl/language-identifier-constants-and-strings
 */

struct hb_ot_language_map_t
{
  int cmp (unsigned int key) const
  { return key < code ? -1 : key > code ? +1 : 0; }

  uint16_t	code;
  char		lang[6];
};

static const hb_ot_language_map_t
hb_ms_language_map[] =
{
  {0x0001,	"ar"},	/* ??? */
  {0x0004,	"zh"},	/* ??? */
  {0x0009,	"en"},	/* ??? */
  {0x0401,	"ar"},	/* Arabic (Saudi Arabia) */
  {0x0402,	"bg"},	/* Bulgarian (Bulgaria) */
  {0x0403,	"ca"},	/* Catalan (Catalan) */
  {0x0404,	"zh-tw"},	/* Chinese (Taiwan) */
  {0x0405,	"cs"},	/* Czech (Czech Republic) */
  {0x0406,	"da"},	/* Danish (Denmark) */
  {0x0407,	"de"},	/* German (Germany) */
  {0x0408,	"el"},	/* Greek (Greece) */
  {0x0409,	"en"},	/* English (United States) */
  {0x040A,	"es"},	/* Spanish (Traditional Sort) (Spain) */
  {0x040B,	"fi"},	/* Finnish (Finland) */
  {0x040C,	"fr"},	/* French (France) */
  {0x040D,	"he"},	/* Hebrew (Israel) */
  {0x040E,	"hu"},	/* Hungarian (Hungary) */
  {0x040F,	"is"},	/* Icelandic (Iceland) */
  {0x0410,	"it"},	/* Italian (Italy) */
  {0x0411,	"ja"},	/* Japanese (Japan) */
  {0x0412,	"ko"},	/* Korean (Korea) */
  {0x0413,	"nl"},	/* Dutch (Netherlands) */
  {0x0414,	"no"},	/* Norwegian (Bokmal) (Norway) */
  {0x0415,	"pl"},	/* Polish (Poland) */
  {0x0416,	"pt"},	/* Portuguese (Brazil) */
  {0x0417,	"rm"},	/* Romansh (Switzerland) */
  {0x0418,	"ro"},	/* Romanian (Romania) */
  {0x0419,	"ru"},	/* Russian (Russia) */
  {0x041A,	"hr"},	/* Croatian (Croatia) */
  {0x041B,	"sk"},	/* Slovak (Slovakia) */
  {0x041C,	"sq"},	/* Albanian (Albania) */
  {0x041D,	"sv"},	/* Swedish (Sweden) */
  {0x041E,	"th"},	/* Thai (Thailand) */
  {0x041F,	"tr"},	/* Turkish (Turkey) */
  {0x0420,	"ur"},	/* Urdu (Islamic Republic of Pakistan) */
  {0x0421,	"id"},	/* Indonesian (Indonesia) */
  {0x0422,	"uk"},	/* Ukrainian (Ukraine) */
  {0x0423,	"be"},	/* Belarusian (Belarus) */
  {0x0424,	"sl"},	/* Slovenian (Slovenia) */
  {0x0425,	"et"},	/* Estonian (Estonia) */
  {0x0426,	"lv"},	/* Latvian (Latvia) */
  {0x0427,	"lt"},	/* Lithuanian (Lithuania) */
  {0x0428,	"tg"},	/* Tajik (Cyrillic) (Tajikistan) */
  {0x0429,	"fa"},	/* Persian (Iran) */
  {0x042A,	"vi"},	/* Vietnamese (Vietnam) */
  {0x042B,	"hy"},	/* Armenian (Armenia) */
  {0x042C,	"az"},	/* Azeri (Latin) (Azerbaijan) */
  {0x042D,	"eu"},	/* Basque (Basque) */
  {0x042E,	"hsb"},	/* Upper Sorbian (Germany) */
  {0x042F,	"mk"},	/* Macedonian (FYROM) (Former Yugoslav Republic of Macedonia) */
  {0x0430,	"st"},	/* ??? */
  {0x0431,	"ts"},	/* ??? */
  {0x0432,	"tn"},	/* Setswana (South Africa) */
  {0x0433,	"ven"},	/* ??? */
  {0x0434,	"xh"},	/* isiXhosa (South Africa) */
  {0x0435,	"zu"},	/* isiZulu (South Africa) */
  {0x0436,	"af"},	/* Afrikaans (South Africa) */
  {0x0437,	"ka"},	/* Georgian (Georgia) */
  {0x0438,	"fo"},	/* Faroese (Faroe Islands) */
  {0x0439,	"hi"},	/* Hindi (India) */
  {0x043A,	"mt"},	/* Maltese (Malta) */
  {0x043B,	"se"},	/* Sami (Northern) (Norway) */
  {0x043C,	"ga"},	/* ??? */
  {0x043D,	"yi"},	/* ??? */
  {0x043E,	"ms"},	/* Malay (Malaysia) */
  {0x043F,	"kk"},	/* Kazakh (Kazakhstan) */
  {0x0440,	"ky"},	/* Kyrgyz (Kyrgyzstan) */
  {0x0441,	"sw"},	/* Kiswahili (Kenya) */
  {0x0442,	"tk"},	/* Turkmen (Turkmenistan) */
  {0x0443,	"uz"},	/* Uzbek (Latin) (Uzbekistan) */
  {0x0444,	"tt"},	/* Tatar (Russia) */
  {0x0445,	"bn"},	/* Bengali (India) */
  {0x0446,	"pa"},	/* Punjabi (India) */
  {0x0447,	"gu"},	/* Gujarati (India) */
  {0x0448,	"or"},	/* Odia (formerly Oriya) (India) */
  {0x0449,	"ta"},	/* Tamil (India) */
  {0x044A,	"te"},	/* Telugu (India) */
  {0x044B,	"kn"},	/* Kannada (India) */
  {0x044C,	"ml"},	/* Malayalam (India) */
  {0x044D,	"as"},	/* Assamese (India) */
  {0x044E,	"mr"},	/* Marathi (India) */
  {0x044F,	"sa"},	/* Sanskrit (India) */
  {0x0450,	"mn"},	/* Mongolian (Cyrillic) (Mongolia) */
  {0x0451,	"bo"},	/* Tibetan (PRC) */
  {0x0452,	"cy"},	/* Welsh (United Kingdom) */
  {0x0453,	"km"},	/* Khmer (Cambodia) */
  {0x0454,	"lo"},	/* Lao (Lao P.D.R.) */
  {0x0455,	"my"},	/* ??? */
  {0x0456,	"gl"},	/* Galician (Galician) */
  {0x0457,	"kok"},	/* Konkani (India) */
  {0x0458,	"mni"},	/* ??? */
  {0x0459,	"sd"},	/* ??? */
  {0x045A,	"syr"},	/* Syriac (Syria) */
  {0x045B,	"si"},	/* Sinhala (Sri Lanka) */
  {0x045C,	"chr"},	/* ??? */
  {0x045D,	"iu"},	/* Inuktitut (Canada) */
  {0x045E,	"am"},	/* Amharic (Ethiopia) */
  {0x0460,	"ks"},	/* ??? */
  {0x0461,	"ne"},	/* Nepali (Nepal) */
  {0x0462,	"fy"},	/* Frisian (Netherlands) */
  {0x0463,	"ps"},	/* Pashto (Afghanistan) */
  {0x0464,	"phi"},	/* Filipino (Philippines) */
  {0x0465,	"div"},	/* Divehi (Maldives) */
  {0x0468,	"ha"},	/* Hausa (Latin) (Nigeria) */
  {0x046A,	"yo"},	/* Yoruba (Nigeria) */
  {0x046B,	"quz"},	/* Quechua (Bolivia) */
  {0x046C,	"nso"},	/* Sesotho sa Leboa (South Africa) */
  {0x046D,	"ba"},	/* Bashkir (Russia) */
  {0x046E,	"lb"},	/* Luxembourgish (Luxembourg) */
  {0x046F,	"kl"},	/* Greenlandic (Greenland) */
  {0x0470,	"ibo"},	/* Igbo (Nigeria) */
  {0x0471,	"kau"},	/* ??? */
  {0x0472,	"om"},	/* ??? */
  {0x0473,	"ti"},	/* ??? */
  {0x0474,	"gn"},	/* ??? */
  {0x0475,	"haw"},	/* ??? */
  {0x0476,	"la"},	/* ??? */
  {0x0477,	"so"},	/* ??? */
  {0x0478,	"ii"},	/* Yi (PRC) */
  {0x0479,	"pap"},	/* ??? */
  {0x047A,	"arn"},	/* Mapudungun (Chile) */
  {0x047C,	"moh"},	/* Mohawk (Mohawk) */
  {0x047E,	"br"},	/* Breton (France) */
  {0x0480,	"ug"},	/* Uighur (PRC) */
  {0x0481,	"mi"},	/* Maori (New Zealand) */
  {0x0482,	"oc"},	/* Occitan (France) */
  {0x0483,	"co"},	/* Corsican (France) */
  {0x0484,	"gsw"},	/* Alsatian (France) */
  {0x0485,	"sah"},	/* Yakut (Russia) */
  {0x0486,	"qut"},	/* K'iche (Guatemala) */
  {0x0487,	"rw"},	/* Kinyarwanda (Rwanda) */
  {0x0488,	"wo"},	/* Wolof (Senegal) */
  {0x048C,	"fa"},	/* Dari (Afghanistan) */
  {0x0801,	"ar"},	/* Arabic (Iraq) */
  {0x0804,	"zh-cn"},	/* Chinese (People’s Republic of China) */
  {0x0807,	"de"},	/* German (Switzerland) */
  {0x0809,	"en"},	/* English (United Kingdom) */
  {0x080A,	"es"},	/* Spanish (Mexico) */
  {0x080C,	"fr"},	/* French (Belgium) */
  {0x0810,	"it"},	/* Italian (Switzerland) */
  {0x0812,	"ko"},	/* ??? */
  {0x0813,	"nl"},	/* Dutch (Belgium) */
  {0x0814,	"nn"},	/* Norwegian (Nynorsk) (Norway) */
  {0x0816,	"pt"},	/* Portuguese (Portugal) */
  {0x0818,	"mo"},	/* ??? */
  {0x0819,	"ru"},	/* ??? */
  {0x081A,	"sr"},	/* Serbian (Latin) (Serbia) */
  {0x081D,	"sv"},	/* Sweden (Finland) */
  {0x0820,	"ur"},	/* ??? */
  {0x0827,	"lt"},	/* ??? */
  {0x082C,	"az"},	/* Azeri (Cyrillic) (Azerbaijan) */
  {0x082E,	"dsb"},	/* Lower Sorbian (Germany) */
//{0x083B,	""},	/* Sami (Northern) (Sweden) */
  {0x083C,	"gd"},	/* Irish (Ireland) */
  {0x083E,	"ms"},	/* Malay (Brunei Darussalam) */
  {0x0843,	"uz"},	/* Uzbek (Cyrillic) (Uzbekistan) */
  {0x0845,	"bn"},	/* Bengali (Bangladesh) */
  {0x0846,	"ar"},	/* ??? */
  {0x0850,	"mn"},	/* Mongolian (Traditional) (People’s Republic of China) */
  {0x0851,	"dz"},	/* ??? */
  {0x085D,	"iu"},	/* Inuktitut (Latin) (Canada) */
  {0x085F,	"tzm"},	/* Tamazight (Latin) (Algeria) */
  {0x0861,	"ne"},	/* ??? */
//{0x086B,	""},	/* Quechua (Ecuador) */
  {0x0873,	"ti"},	/* ??? */
  {0x0C01,	"ar"},	/* Arabic (Egypt) */
  {0x0C04,	"zh-hk"},	/* Chinese (Hong Kong S.A.R.) */
  {0x0C07,	"de"},	/* German (Austria) */
  {0x0C09,	"en"},	/* English (Australia) */
  {0x0C0A,	"es"},	/* Spanish (Modern Sort) (Spain) */
  {0x0C0C,	"fr"},	/* French (Canada) */
  {0x0C1A,	"sr"},	/* Serbian (Cyrillic) (Serbia) */
  {0x0C3B,	"se"},	/* Sami (Northern) (Finland) */
//{0x0C6B,	""},	/* Quechua (Peru) */
  {0x1001,	"ar"},	/* Arabic (Libya) */
  {0x1004,	"zh-sg"},	/* Chinese (Singapore) */
  {0x1007,	"de"},	/* German (Luxembourg) */
  {0x1009,	"en"},	/* English (Canada) */
  {0x100A,	"es"},	/* Spanish (Guatemala) */
  {0x100C,	"fr"},	/* French (Switzerland) */
  {0x101A,	"hr"},	/* Croatian (Latin) (Bosnia and Herzegovina) */
  {0x103B,	"smj"},	/* Sami (Lule) (Norway) */
  {0x1401,	"ar"},	/* Arabic (Algeria) */
//{0x1404,	""},	/* Chinese (Macao S.A.R.) */
  {0x1407,	"de"},	/* German (Liechtenstein) */
  {0x1409,	"en"},	/* English (New Zealand) */
  {0x140A,	"es"},	/* Spanish (Costa Rica) */
  {0x140C,	"fr"},	/* French (Luxembourg) */
  {0x141A,	"bs"},	/* Bosnian (Latin) (Bosnia and Herzegovina) */
//{0x143B,	""},	/* Sami (Lule) (Sweden) */
  {0x1801,	"ar"},	/* Arabic (Morocco) */
  {0x1809,	"en"},	/* English (Ireland) */
  {0x180A,	"es"},	/* Spanish (Panama) */
  {0x180C,	"fr"},	/* French (Principality of Monaco) */
//{0x181A,	""},	/* Serbian (Latin) (Bosnia and Herzegovina) */
  {0x183B,	"sma"},	/* Sami (Southern) (Norway) */
  {0x1C01,	"ar"},	/* Arabic (Tunisia) */
  {0x1C09,	"en"},	/* English (South Africa) */
  {0x1C0A,	"es"},	/* Spanish (Dominican Republic) */
  {0x1C0C,	"fr"},	/* ??? */
//{0x1C1A,	""},	/* Serbian (Cyrillic) (Bosnia and Herzegovina) */
//{0x1C3B,	""},	/* Sami (Southern) (Sweden) */
  {0x2001,	"ar"},	/* Arabic (Oman) */
  {0x2009,	"en"},	/* English (Jamaica) */
  {0x200A,	"es"},	/* Spanish (Venezuela) */
  {0x200C,	"fr"},	/* ??? */
  {0x201A,	"bs"},	/* Bosnian (Cyrillic) (Bosnia and Herzegovina) */
  {0x203B,	"sms"},	/* Sami (Skolt) (Finland) */
  {0x2401,	"ar"},	/* Arabic (Yemen) */
  {0x2409,	"en"},	/* English (Caribbean) */
  {0x240A,	"es"},	/* Spanish (Colombia) */
  {0x240C,	"fr"},	/* ??? */
  {0x243B,	"smn"},	/* Sami (Inari) (Finland) */
  {0x2801,	"ar"},	/* Arabic (Syria) */
  {0x2809,	"en"},	/* English (Belize) */
  {0x280A,	"es"},	/* Spanish (Peru) */
  {0x280C,	"fr"},	/* ??? */
  {0x2C01,	"ar"},	/* Arabic (Jordan) */
  {0x2C09,	"en"},	/* English (Trinidad and Tobago) */
  {0x2C0A,	"es"},	/* Spanish (Argentina) */
  {0x2C0C,	"fr"},	/* ??? */
  {0x3001,	"ar"},	/* Arabic (Lebanon) */
  {0x3009,	"en"},	/* English (Zimbabwe) */
  {0x300A,	"es"},	/* Spanish (Ecuador) */
  {0x300C,	"fr"},	/* ??? */
  {0x3401,	"ar"},	/* Arabic (Kuwait) */
  {0x3409,	"en"},	/* English (Republic of the Philippines) */
  {0x340A,	"es"},	/* Spanish (Chile) */
  {0x340C,	"fr"},	/* ??? */
  {0x3801,	"ar"},	/* Arabic (U.A.E.) */
  {0x380A,	"es"},	/* Spanish (Uruguay) */
  {0x380C,	"fr"},	/* ??? */
  {0x3C01,	"ar"},	/* Arabic (Bahrain) */
  {0x3C09,	"en"},	/* ??? */
  {0x3C0A,	"es"},	/* Spanish (Paraguay) */
  {0x3C0C,	"fr"},	/* ??? */
  {0x4001,	"ar"},	/* Arabic (Qatar) */
  {0x4009,	"en"},	/* English (India) */
  {0x400A,	"es"},	/* Spanish (Bolivia) */
  {0x4409,	"en"},	/* English (Malaysia) */
  {0x440A,	"es"},	/* Spanish (El Salvador) */
  {0x4809,	"en"},	/* English (Singapore) */
  {0x480A,	"es"},	/* Spanish (Honduras) */
  {0x4C0A,	"es"},	/* Spanish (Nicaragua) */
  {0x500A,	"es"},	/* Spanish (Puerto Rico) */
  {0x540A,	"es"},	/* Spanish (United States) */
  {0xE40A,	"es"},	/* ??? */
  {0xE40C,	"fr"},	/* ??? */
};

static const hb_ot_language_map_t
hb_mac_language_map[] =
{
  {  0,	"en"},	/* English */
  {  1,	"fr"},	/* French */
  {  2,	"de"},	/* German */
  {  3,	"it"},	/* Italian */
  {  4,	"nl"},	/* Dutch */
  {  5,	"sv"},	/* Swedish */
  {  6,	"es"},	/* Spanish */
  {  7,	"da"},	/* Danish */
  {  8,	"pt"},	/* Portuguese */
  {  9,	"no"},	/* Norwegian */
  { 10,	"he"},	/* Hebrew */
  { 11,	"ja"},	/* Japanese */
  { 12,	"ar"},	/* Arabic */
  { 13,	"fi"},	/* Finnish */
  { 14,	"el"},	/* Greek */
  { 15,	"is"},	/* Icelandic */
  { 16,	"mt"},	/* Maltese */
  { 17,	"tr"},	/* Turkish */
  { 18,	"hr"},	/* Croatian */
  { 19,	"zh-tw"},	/* Chinese (Traditional) */
  { 20,	"ur"},	/* Urdu */
  { 21,	"hi"},	/* Hindi */
  { 22,	"th"},	/* Thai */
  { 23,	"ko"},	/* Korean */
  { 24,	"lt"},	/* Lithuanian */
  { 25,	"pl"},	/* Polish */
  { 26,	"hu"},	/* Hungarian */
  { 27,	"et"},	/* Estonian */
  { 28,	"lv"},	/* Latvian */
//{ 29,	""},	/* Sami */
  { 30,	"fo"},	/* Faroese */
  { 31,	"fa"},	/* Farsi/Persian */
  { 32,	"ru"},	/* Russian */
  { 33,	"zh-cn"},	/* Chinese (Simplified) */
  { 34,	"nl"},	/* Flemish */
  { 35,	"ga"},	/* Irish Gaelic */
  { 36,	"sq"},	/* Albanian */
  { 37,	"ro"},	/* Romanian */
  { 38,	"cs"},	/* Czech */
  { 39,	"sk"},	/* Slovak */
  { 40,	"sl"},	/* Slovenian */
  { 41,	"yi"},	/* Yiddish */
  { 42,	"sr"},	/* Serbian */
  { 43,	"mk"},	/* Macedonian */
  { 44,	"bg"},	/* Bulgarian */
  { 45,	"uk"},	/* Ukrainian */
  { 46,	"be"},	/* Byelorussian */
  { 47,	"uz"},	/* Uzbek */
  { 48,	"kk"},	/* Kazakh */
  { 49,	"az"},	/* Azerbaijani (Cyrillic script) */
  { 50,	"az"},	/* Azerbaijani (Arabic script) */
  { 51,	"hy"},	/* Armenian */
  { 52,	"ka"},	/* Georgian */
  { 53,	"mo"},	/* Moldavian */
  { 54,	"ky"},	/* Kirghiz */
  { 55,	"tg"},	/* Tajiki */
  { 56,	"tk"},	/* Turkmen */
  { 57,	"mn"},	/* Mongolian (Mongolian script) */
  { 58,	"mn"},	/* Mongolian (Cyrillic script) */
  { 59,	"ps"},	/* Pashto */
  { 60,	"ku"},	/* Kurdish */
  { 61,	"ks"},	/* Kashmiri */
  { 62,	"sd"},	/* Sindhi */
  { 63,	"bo"},	/* Tibetan */
  { 64,	"ne"},	/* Nepali */
  { 65,	"sa"},	/* Sanskrit */
  { 66,	"mr"},	/* Marathi */
  { 67,	"bn"},	/* Bengali */
  { 68,	"as"},	/* Assamese */
  { 69,	"gu"},	/* Gujarati */
  { 70,	"pa"},	/* Punjabi */
  { 71,	"or"},	/* Oriya */
  { 72,	"ml"},	/* Malayalam */
  { 73,	"kn"},	/* Kannada */
  { 74,	"ta"},	/* Tamil */
  { 75,	"te"},	/* Telugu */
  { 76,	"si"},	/* Sinhalese */
  { 77,	"my"},	/* Burmese */
  { 78,	"km"},	/* Khmer */
  { 79,	"lo"},	/* Lao */
  { 80,	"vi"},	/* Vietnamese */
  { 81,	"id"},	/* Indonesian */
  { 82,	"tl"},	/* Tagalog */
  { 83,	"ms"},	/* Malay (Roman script) */
  { 84,	"ms"},	/* Malay (Arabic script) */
  { 85,	"am"},	/* Amharic */
  { 86,	"ti"},	/* Tigrinya */
  { 87,	"om"},	/* Galla */
  { 88,	"so"},	/* Somali */
  { 89,	"sw"},	/* Swahili */
  { 90,	"rw"},	/* Kinyarwanda/Ruanda */
  { 91,	"rn"},	/* Rundi */
  { 92,	"ny"},	/* Nyanja/Chewa */
  { 93,	"mg"},	/* Malagasy */
  { 94,	"eo"},	/* Esperanto */
  {128,	"cy"},	/* Welsh */
  {129,	"eu"},	/* Basque */
  {130,	"ca"},	/* Catalan */
  {131,	"la"},	/* Latin */
  {132,	"qu"},	/* Quechua */
  {133,	"gn"},	/* Guarani */
  {134,	"ay"},	/* Aymara */
  {135,	"tt"},	/* Tatar */
  {136,	"ug"},	/* Uighur */
  {137,	"dz"},	/* Dzongkha */
  {138,	"jw"},	/* Javanese (Roman script) */
  {139,	"su"},	/* Sundanese (Roman script) */
  {140,	"gl"},	/* Galician */
  {141,	"af"},	/* Afrikaans */
  {142,	"br"},	/* Breton */
  {143,	"iu"},	/* Inuktitut */
  {144,	"gd"},	/* Scottish Gaelic */
  {145,	"gv"},	/* Manx Gaelic */
  {146,	"ga"},	/* Irish Gaelic (with dot above) */
  {147,	"to"},	/* Tongan */
  {148,	"el"},	/* Greek (polytonic) */
  {149,	"ik"},	/* Greenlandic */
  {150,	"az"},	/* Azerbaijani (Roman script) */
};

static hb_language_t
_hb_ot_name_language_for (unsigned int code,
			  const hb_ot_language_map_t *array,
			  unsigned int len)
{
#ifdef HB_NO_OT_NAME_LANGUAGE
  return HB_LANGUAGE_INVALID;
#endif
  auto *entry = hb_bsearch (code, array, len);

  if (entry)
    return hb_language_from_string (entry->lang, -1);

  return HB_LANGUAGE_INVALID;
}

hb_language_t
_hb_ot_name_language_for_ms_code (unsigned int code)
{
  return _hb_ot_name_language_for (code,
				   hb_ms_language_map,
				   ARRAY_LENGTH (hb_ms_language_map));
}

hb_language_t
_hb_ot_name_language_for_mac_code (unsigned int code)
{
  return _hb_ot_name_language_for (code,
				   hb_mac_language_map,
				   ARRAY_LENGTH (hb_mac_language_map));
}

#endif /* HB_OT_NAME_LANGUAGE_STATIC_HH */


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

#include "hb.hh"
#include "hb-bimap.hh"

int
main (int argc, char **argv)
{
  hb_bimap_t	bm;

  assert (bm.is_empty () == true);
  bm.set (1, 4);
  bm.set (2, 5);
  bm.set (3, 6);
  assert (bm.get_population () == 3);
  assert (bm.has (1) == true);
  assert (bm.has (4) == false);
  assert (bm[2] == 5);
  assert (bm.backward (6) == 3);
  bm.del (1);
  assert (bm.has (1) == false);
  assert (bm.has (3) == true);
  bm.clear ();
  assert (bm.get_population () == 0);

  hb_inc_bimap_t  ibm;

  assert (ibm.add (13) == 0);
  assert (ibm.add (8) == 1);
  assert (ibm.add (10) == 2);
  assert (ibm.add (8) == 1);
  assert (ibm.add (7) == 3);
  assert (ibm.get_population () == 4);
  assert (ibm[7] == 3);

  ibm.sort ();
  assert (ibm.get_population () == 4);
  assert (ibm[7] == 0);
  assert (ibm[13] == 3);

  ibm.identity (3);
  assert (ibm.get_population () == 3);
  assert (ibm[0] == 0);
  assert (ibm[1] == 1);
  assert (ibm[2] == 2);
  assert (ibm.backward (0) == 0);
  assert (ibm.backward (1) == 1);
  assert (ibm.backward (2) == 2);
  assert (ibm.has (4) == false);

  return 0;
}


text:
/*
 * Copyright © 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_FALLBACK_HH
#define HB_OT_SHAPE_FALLBACK_HH

#include "hb.hh"

#include "hb-ot-shape.hh"

HB_INTERNAL void _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan,
						      hb_font_t *font,
						      hb_buffer_t  *buffer,
						      bool adjust_offsets_when_zeroing);

HB_INTERNAL void _hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t *plan,
									 hb_font_t *font,
									 hb_buffer_t  *buffer);

HB_INTERNAL void _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
					     hb_font_t *font,
					     hb_buffer_t  *buffer);

HB_INTERNAL void _hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan,
					       hb_font_t *font,
					       hb_buffer_t  *buffer);

#endif /* HB_OT_SHAPE_FALLBACK_HH */


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_CFF1_INTERP_CS_HH
#define HB_CFF1_INTERP_CS_HH

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

namespace CFF {

using namespace OT;

typedef biased_subrs_t<CFF1Subrs>   cff1_biased_subrs_t;

struct cff1_cs_interp_env_t : cs_interp_env_t<number_t, CFF1Subrs>
{
  template <typename ACC>
  void init (const byte_str_t &str, ACC &acc, unsigned int fd)
  {
    SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
    processed_width = false;
    has_width = false;
    arg_start = 0;
    in_seac = false;
  }

  void fini () { SUPER::fini (); }

  void set_width (bool has_width_)
  {
    if (likely (!processed_width && (SUPER::argStack.get_count () > 0)))
    {
      if (has_width_)
      {
	width = SUPER::argStack[0];
	has_width = true;
	arg_start = 1;
      }
    }
    processed_width = true;
  }

  void clear_args ()
  {
    arg_start = 0;
    SUPER::clear_args ();
  }

  void set_in_seac (bool _in_seac) { in_seac = _in_seac; }

  bool	  processed_width;
  bool	  has_width;
  unsigned int  arg_start;
  number_t	width;
  bool	  in_seac;

  private:
  typedef cs_interp_env_t<number_t, CFF1Subrs> SUPER;
};

template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff1_cs_interp_env_t, PARAM>>
struct cff1_cs_opset_t : cs_opset_t<number_t, OPSET, cff1_cs_interp_env_t, PARAM, PATH>
{
  /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */
  /* Type 1-originated deprecated opcodes, seac behavior of endchar and dotsection are supported */

  static void process_op (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param)
  {
    switch (op) {
      case OpCode_dotsection:
	SUPER::flush_args_and_op (op, env, param);
	break;

      case OpCode_endchar:
	OPSET::check_width (op, env, param);
	if (env.argStack.get_count () >= 4)
	{
	  OPSET::process_seac (env, param);
	}
	OPSET::flush_args_and_op (op, env, param);
	env.set_endchar (true);
	break;

      default:
	SUPER::process_op (op, env, param);
    }
  }

  static void check_width (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param)
  {
    if (!env.processed_width)
    {
      bool  has_width = false;
      switch (op)
      {
	case OpCode_endchar:
	case OpCode_hstem:
	case OpCode_hstemhm:
	case OpCode_vstem:
	case OpCode_vstemhm:
	case OpCode_hintmask:
	case OpCode_cntrmask:
	  has_width = ((env.argStack.get_count () & 1) != 0);
	  break;
	case OpCode_hmoveto:
	case OpCode_vmoveto:
	  has_width = (env.argStack.get_count () > 1);
	  break;
	case OpCode_rmoveto:
	  has_width = (env.argStack.get_count () > 2);
	  break;
	default:
	  return;
      }
      env.set_width (has_width);
    }
  }

  static void process_seac (cff1_cs_interp_env_t &env, PARAM& param)
  {
  }

  static void flush_args (cff1_cs_interp_env_t &env, PARAM& param)
  {
    SUPER::flush_args (env, param);
    env.clear_args ();  /* pop off width */
  }

  private:
  typedef cs_opset_t<number_t, OPSET, cff1_cs_interp_env_t, PARAM, PATH>  SUPER;
};

template <typename OPSET, typename PARAM>
struct cff1_cs_interpreter_t : cs_interpreter_t<cff1_cs_interp_env_t, OPSET, PARAM> {};

} /* namespace CFF */

#endif /* HB_CFF1_INTERP_CS_HH */


text:
/*
 * Copyright © 2009  Red Hat, 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_H_IN
#error "Include <hb.h> instead."
#endif

#ifndef HB_BLOB_H
#define HB_BLOB_H

#include "hb-common.h"

HB_BEGIN_DECLS

/*
 * Note re various memory-modes:
 *
 * - In no case shall the HarfBuzz client modify memory
 *   that is passed to HarfBuzz in a blob.  If there is
 *   any such possibility, MODE_DUPLICATE should be used
 *   such that HarfBuzz makes a copy immediately,
 *
 * - Use MODE_READONLY otherwise, unless you really really
 *   really know what you are doing,
 *
 * - MODE_WRITABLE is appropriate if you really made a
 *   copy of data solely for the purpose of passing to
 *   HarfBuzz and doing that just once (no reuse!),
 *
 * - If the font is mmap()ed, it's ok to use
 *   READONLY_MAY_MAKE_WRITABLE, however, using that mode
 *   correctly is very tricky.  Use MODE_READONLY instead.
 */
typedef enum {
  HB_MEMORY_MODE_DUPLICATE,
  HB_MEMORY_MODE_READONLY,
  HB_MEMORY_MODE_WRITABLE,
  HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE
} hb_memory_mode_t;

typedef struct hb_blob_t hb_blob_t;

HB_EXTERN hb_blob_t *
hb_blob_create (const char        *data,
		unsigned int       length,
		hb_memory_mode_t   mode,
		void              *user_data,
		hb_destroy_func_t  destroy);

HB_EXTERN hb_blob_t *
hb_blob_create_from_file (const char *file_name);

/* Always creates with MEMORY_MODE_READONLY.
 * Even if the parent blob is writable, we don't
 * want the user of the sub-blob to be able to
 * modify the parent data as that data may be
 * shared among multiple sub-blobs.
 */
HB_EXTERN hb_blob_t *
hb_blob_create_sub_blob (hb_blob_t    *parent,
			 unsigned int  offset,
			 unsigned int  length);

HB_EXTERN hb_blob_t *
hb_blob_copy_writable_or_fail (hb_blob_t *blob);

HB_EXTERN hb_blob_t *
hb_blob_get_empty (void);

HB_EXTERN hb_blob_t *
hb_blob_reference (hb_blob_t *blob);

HB_EXTERN void
hb_blob_destroy (hb_blob_t *blob);

HB_EXTERN hb_bool_t
hb_blob_set_user_data (hb_blob_t          *blob,
		       hb_user_data_key_t *key,
		       void *              data,
		       hb_destroy_func_t   destroy,
		       hb_bool_t           replace);

HB_EXTERN void *
hb_blob_get_user_data (hb_blob_t          *blob,
		       hb_user_data_key_t *key);

HB_EXTERN void
hb_blob_make_immutable (hb_blob_t *blob);

HB_EXTERN hb_bool_t
hb_blob_is_immutable (hb_blob_t *blob);

HB_EXTERN unsigned int
hb_blob_get_length (hb_blob_t *blob);

HB_EXTERN const char *
hb_blob_get_data (hb_blob_t *blob, unsigned int *length);

HB_EXTERN char *
hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length);

HB_END_DECLS

#endif /* HB_BLOB_H */


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

#ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
#define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH

#include "hb.hh"

%%{
  machine indic_syllable_machine;
  alphtype unsigned char;
  write data;
}%%

%%{

# Same order as enum indic_category_t.  Not sure how to avoid duplication.
C    = 1;
V    = 2;
N    = 3;
H    = 4;
ZWNJ = 5;
ZWJ  = 6;
M    = 7;
SM   = 8;
A    = 10;
PLACEHOLDER = 11;
DOTTEDCIRCLE = 12;
RS    = 13;
Repha = 15;
Ra    = 16;
CM    = 17;
Symbol= 18;
CS    = 19;

c = (C | Ra);			# is_consonant
n = ((ZWNJ?.RS)? (N.N?)?);	# is_consonant_modifier
z = ZWJ|ZWNJ;			# is_joiner
reph = (Ra H | Repha);		# possible reph

cn = c.ZWJ?.n?;
forced_rakar = ZWJ H ZWJ Ra;
symbol = Symbol.N?;
matra_group = z*.M.N?.(H | forced_rakar)?;
syllable_tail = (z?.SM.SM?.ZWNJ?)? A*;
halant_group = (z?.H.(ZWJ.N?)?);
final_halant_group = halant_group | H.ZWNJ;
medial_group = CM?;
halant_or_matra_group = (final_halant_group | matra_group*);

complex_syllable_tail = (halant_group.cn)* medial_group halant_or_matra_group syllable_tail;

consonant_syllable =	(Repha|CS)? cn complex_syllable_tail;
vowel_syllable =	reph? V.n? (ZWJ | complex_syllable_tail);
standalone_cluster =	((Repha|CS)? PLACEHOLDER | reph? DOTTEDCIRCLE).n? complex_syllable_tail;
symbol_cluster =	symbol syllable_tail;
broken_cluster =	reph? n? complex_syllable_tail;
other =			any;

main := |*
	consonant_syllable	=> { found_syllable (consonant_syllable); };
	vowel_syllable		=> { found_syllable (vowel_syllable); };
	standalone_cluster	=> { found_syllable (standalone_cluster); };
	symbol_cluster		=> { found_syllable (symbol_cluster); };
	broken_cluster		=> { found_syllable (broken_cluster); };
	other			=> { found_syllable (non_indic_cluster); };
*|;

}%%

#define found_syllable(syllable_type) \
  HB_STMT_START { \
    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
    for (unsigned int i = ts; i < te; i++) \
      info[i].syllable() = (syllable_serial << 4) | indic_##syllable_type; \
    syllable_serial++; \
    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
  } HB_STMT_END

static void
find_syllables_indic (hb_buffer_t *buffer)
{
  unsigned int p, pe, eof, ts, te, act;
  int cs;
  hb_glyph_info_t *info = buffer->info;
  %%{
    write init;
    getkey info[p].indic_category();
  }%%

  p = 0;
  pe = eof = buffer->len;

  unsigned int syllable_serial = 1;
  %%{
    write exec;
  }%%
}

#undef found_syllable

#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */