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

#ifndef HB_OT_VAR_HVAR_TABLE_HH
#define HB_OT_VAR_HVAR_TABLE_HH

#include "hb-ot-layout-common.hh"

namespace OT {

struct DeltaSetIndexMap
{
  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
		  c->check_range (mapDataZ.arrayZ,
				  mapCount,
				  get_width ()));
  }

  template <typename T>
  bool serialize (hb_serialize_context_t *c, const T &plan)
  {
    unsigned int width = plan.get_width ();
    unsigned int inner_bit_count = plan.get_inner_bit_count ();
    const hb_array_t<const unsigned int> output_map = plan.get_output_map ();

    TRACE_SERIALIZE (this);
    if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0))))
      return_trace (false);
    if (unlikely (!c->extend_min (*this))) return_trace (false);

    format = ((width-1)<<4)|(inner_bit_count-1);
    mapCount = output_map.length;
    HBUINT8 *p = c->allocate_size<HBUINT8> (width * output_map.length);
    if (unlikely (!p)) return_trace (false);
    for (unsigned int i = 0; i < output_map.length; i++)
    {
      unsigned int v = output_map[i];
      unsigned int outer = v >> 16;
      unsigned int inner = v & 0xFFFF;
      unsigned int u = (outer << inner_bit_count) | inner;
      for (unsigned int w = width; w > 0;)
      {
	p[--w] = u;
	u >>= 8;
      }
      p += width;
    }
    return_trace (true);
  }

  unsigned int map (unsigned int v) const /* Returns 16.16 outer.inner. */
  {
    /* If count is zero, pass value unchanged.  This takes
     * care of direct mapping for advance map. */
    if (!mapCount)
      return v;

    if (v >= mapCount)
      v = mapCount - 1;

    unsigned int u = 0;
    { /* Fetch it. */
      unsigned int w = get_width ();
      const HBUINT8 *p = mapDataZ.arrayZ + w * v;
      for (; w; w--)
	u = (u << 8) + *p++;
    }

    { /* Repack it. */
      unsigned int n = get_inner_bit_count ();
      unsigned int outer = u >> n;
      unsigned int inner = u & ((1 << n) - 1);
      u = (outer<<16) | inner;
    }

    return u;
  }

  unsigned int get_map_count () const	    { return mapCount; }
  unsigned int get_width () const           { return ((format >> 4) & 3) + 1; }
  unsigned int get_inner_bit_count () const { return (format & 0xF) + 1; }

  protected:
  HBUINT16	format;		/* A packed field that describes the compressed
				 * representation of delta-set indices. */
  HBUINT16	mapCount;	/* The number of mapping entries. */
  UnsizedArrayOf<HBUINT8>
		mapDataZ;	/* The delta-set index mapping data. */

  public:
  DEFINE_SIZE_ARRAY (4, mapDataZ);
};

struct index_map_subset_plan_t
{
  enum index_map_index_t {
    ADV_INDEX,
    LSB_INDEX,	/* dual as TSB */
    RSB_INDEX,	/* dual as BSB */
    VORG_INDEX
  };

  void init (const DeltaSetIndexMap  &index_map,
	     hb_inc_bimap_t	     &outer_map,
	     hb_vector_t<hb_set_t *> &inner_sets,
	     const hb_subset_plan_t  *plan)
  {
    map_count = 0;
    outer_bit_count = 0;
    inner_bit_count = 1;
    max_inners.init ();
    output_map.init ();

    if (&index_map == &Null (DeltaSetIndexMap)) return;

    unsigned int	last_val = (unsigned int)-1;
    hb_codepoint_t	last_gid = (hb_codepoint_t)-1;
    hb_codepoint_t	gid = (hb_codepoint_t) hb_min (index_map.get_map_count (), plan->num_output_glyphs ());

    outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count ();
    max_inners.resize (inner_sets.length);
    for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0;

    /* Search backwards for a map value different from the last map value */
    for (; gid > 0; gid--)
    {
      hb_codepoint_t	old_gid;
      if (!plan->old_gid_for_new_gid (gid - 1, &old_gid))
      {
	if (last_gid == (hb_codepoint_t) -1)
	  continue;
	else
	  break;
      }

      unsigned int v = index_map.map (old_gid);
      if (last_gid == (hb_codepoint_t) -1)
      {
	last_val = v;
	last_gid = gid;
	continue;
      }
      if (v != last_val) break;

      last_gid = gid;
    }

    if (unlikely (last_gid == (hb_codepoint_t)-1)) return;
    map_count = last_gid;
    for (gid = 0; gid < map_count; gid++)
    {
      hb_codepoint_t	old_gid;
      if (plan->old_gid_for_new_gid (gid, &old_gid))
      {
	unsigned int v = index_map.map (old_gid);
	unsigned int outer = v >> 16;
	unsigned int inner = v & 0xFFFF;
	outer_map.add (outer);
	if (inner > max_inners[outer]) max_inners[outer] = inner;
	if (outer >= inner_sets.length) return;
	inner_sets[outer]->add (inner);
      }
    }
  }

  void fini ()
  {
    max_inners.fini ();
    output_map.fini ();
  }

  void remap (const DeltaSetIndexMap *input_map,
	      const hb_inc_bimap_t &outer_map,
	      const hb_vector_t<hb_inc_bimap_t> &inner_maps,
	      const hb_subset_plan_t *plan)
  {
    if (input_map == &Null (DeltaSetIndexMap)) return;

    for (unsigned int i = 0; i < max_inners.length; i++)
    {
      if (inner_maps[i].get_population () == 0) continue;
      unsigned int bit_count = (max_inners[i]==0)? 1: hb_bit_storage (inner_maps[i][max_inners[i]]);
      if (bit_count > inner_bit_count) inner_bit_count = bit_count;
    }

    output_map.resize (map_count);
    for (hb_codepoint_t gid = 0; gid < output_map.length; gid++)
    {
      hb_codepoint_t	old_gid;
      if (plan->old_gid_for_new_gid (gid, &old_gid))
      {
	unsigned int v = input_map->map (old_gid);
	unsigned int outer = v >> 16;
	output_map[gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]);
      }
      else
	output_map[gid] = 0;	/* Map unused glyph to outer/inner=0/0 */
    }
  }

  unsigned int get_inner_bit_count () const { return inner_bit_count; }
  unsigned int get_width ()           const { return ((outer_bit_count + inner_bit_count + 7) / 8); }
  unsigned int get_map_count ()       const { return map_count; }

  unsigned int get_size () const
  { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); }

  bool is_identity () const { return get_output_map ().length == 0; }
  hb_array_t<const unsigned int> get_output_map () const { return output_map.as_array (); }

  protected:
  unsigned int map_count;
  hb_vector_t<unsigned int> max_inners;
  unsigned int outer_bit_count;
  unsigned int inner_bit_count;
  hb_vector_t<unsigned int> output_map;
};

struct hvarvvar_subset_plan_t
{
  hvarvvar_subset_plan_t() : inner_maps (), index_map_plans () {}
  ~hvarvvar_subset_plan_t() { fini (); }

  void init (const hb_array_t<const DeltaSetIndexMap *> &index_maps,
	     const VariationStore &_var_store,
	     const hb_subset_plan_t *plan)
  {
    index_map_plans.resize (index_maps.length);

    var_store = &_var_store;
    inner_sets.resize (var_store->get_sub_table_count ());
    for (unsigned int i = 0; i < inner_sets.length; i++)
      inner_sets[i] = hb_set_create ();
    adv_set = hb_set_create ();

    inner_maps.resize (var_store->get_sub_table_count ());

    for (unsigned int i = 0; i < inner_maps.length; i++)
      inner_maps[i].init ();

    if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return;

    bool retain_adv_map = false;
    index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan);
    if (index_maps[0] == &Null (DeltaSetIndexMap))
    {
      retain_adv_map = plan->retain_gids;
      outer_map.add (0);
      for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++)
      {
	hb_codepoint_t old_gid;
	if (plan->old_gid_for_new_gid (gid, &old_gid))
	  inner_sets[0]->add (old_gid);
      }
      hb_set_union (adv_set, inner_sets[0]);
    }

    for (unsigned int i = 1; i < index_maps.length; i++)
      index_map_plans[i].init (*index_maps[i], outer_map, inner_sets, plan);

    outer_map.sort ();

    if (retain_adv_map)
    {
      for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++)
	if (inner_sets[0]->has (gid))
	  inner_maps[0].add (gid);
	else
	  inner_maps[0].skip ();
    }
    else
    {
      inner_maps[0].add_set (adv_set);
      hb_set_subtract (inner_sets[0], adv_set);
      inner_maps[0].add_set (inner_sets[0]);
    }

    for (unsigned int i = 1; i < inner_maps.length; i++)
      inner_maps[i].add_set (inner_sets[i]);

    for (unsigned int i = 0; i < index_maps.length; i++)
      index_map_plans[i].remap (index_maps[i], outer_map, inner_maps, plan);
  }

  void fini ()
  {
    for (unsigned int i = 0; i < inner_sets.length; i++)
      hb_set_destroy (inner_sets[i]);
    hb_set_destroy (adv_set);
    inner_maps.fini_deep ();
    index_map_plans.fini_deep ();
  }

  hb_inc_bimap_t outer_map;
  hb_vector_t<hb_inc_bimap_t> inner_maps;
  hb_vector_t<index_map_subset_plan_t> index_map_plans;
  const VariationStore *var_store;

  protected:
  hb_vector_t<hb_set_t *> inner_sets;
  hb_set_t *adv_set;
};

/*
 * HVAR -- Horizontal Metrics Variations
 * https://docs.microsoft.com/en-us/typography/opentype/spec/hvar
 * VVAR -- Vertical Metrics Variations
 * https://docs.microsoft.com/en-us/typography/opentype/spec/vvar
 */
#define HB_OT_TAG_HVAR HB_TAG('H','V','A','R')
#define HB_OT_TAG_VVAR HB_TAG('V','V','A','R')

struct HVARVVAR
{
  static constexpr hb_tag_t HVARTag = HB_OT_TAG_HVAR;
  static constexpr hb_tag_t VVARTag = HB_OT_TAG_VVAR;

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (version.sanitize (c) &&
		  likely (version.major == 1) &&
		  varStore.sanitize (c, this) &&
		  advMap.sanitize (c, this) &&
		  lsbMap.sanitize (c, this) &&
		  rsbMap.sanitize (c, this));
  }

  void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const
  {
    index_maps.push (&(this+advMap));
    index_maps.push (&(this+lsbMap));
    index_maps.push (&(this+rsbMap));
  }

  bool serialize_index_maps (hb_serialize_context_t *c,
			     const hb_array_t<index_map_subset_plan_t> &im_plans)
  {
    TRACE_SERIALIZE (this);
    if (im_plans[index_map_subset_plan_t::ADV_INDEX].is_identity ())
      advMap = 0;
    else if (unlikely (!advMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::ADV_INDEX])))
      return_trace (false);
    if (im_plans[index_map_subset_plan_t::LSB_INDEX].is_identity ())
      lsbMap = 0;
    else if (unlikely (!lsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::LSB_INDEX])))
      return_trace (false);
    if (im_plans[index_map_subset_plan_t::RSB_INDEX].is_identity ())
      rsbMap = 0;
    else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::RSB_INDEX])))
      return_trace (false);

    return_trace (true);
  }

  template <typename T>
  bool _subset (hb_subset_context_t *c) const
  {
    TRACE_SUBSET (this);
    hvarvvar_subset_plan_t	hvar_plan;
    hb_vector_t<const DeltaSetIndexMap *>
				index_maps;

    ((T*)this)->listup_index_maps (index_maps);
    hvar_plan.init (index_maps.as_array (), this+varStore, c->plan);

    T *out = c->serializer->allocate_min<T> ();
    if (unlikely (!out)) return_trace (false);

    out->version.major = 1;
    out->version.minor = 0;

    if (unlikely (!out->varStore.serialize (c->serializer, out)
		     .serialize (c->serializer, hvar_plan.var_store, hvar_plan.inner_maps.as_array ())))
      return_trace (false);

    return_trace (out->T::serialize_index_maps (c->serializer,
						hvar_plan.index_map_plans.as_array ()));
  }

  float get_advance_var (hb_codepoint_t glyph, hb_font_t *font) const
  {
    unsigned int varidx = (this+advMap).map (glyph);
    return (this+varStore).get_delta (varidx, font->coords, font->num_coords);
  }

  float get_side_bearing_var (hb_codepoint_t glyph,
			      const int *coords, unsigned int coord_count) const
  {
    if (!has_side_bearing_deltas ()) return 0.f;
    unsigned int varidx = (this+lsbMap).map (glyph);
    return (this+varStore).get_delta (varidx, coords, coord_count);
  }

  bool has_side_bearing_deltas () const { return lsbMap && rsbMap; }

  protected:
  FixedVersion<>version;	/* Version of the metrics variation table
				 * initially set to 0x00010000u */
  LOffsetTo<VariationStore>
		varStore;	/* Offset to item variation store table. */
  LOffsetTo<DeltaSetIndexMap>
		advMap;		/* Offset to advance var-idx mapping. */
  LOffsetTo<DeltaSetIndexMap>
		lsbMap;		/* Offset to lsb/tsb var-idx mapping. */
  LOffsetTo<DeltaSetIndexMap>
		rsbMap;		/* Offset to rsb/bsb var-idx mapping. */

  public:
  DEFINE_SIZE_STATIC (20);
};

struct HVAR : HVARVVAR {
  static constexpr hb_tag_t tableTag = HB_OT_TAG_HVAR;
  bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset<HVAR> (c); }
};
struct VVAR : HVARVVAR {
  static constexpr hb_tag_t tableTag = HB_OT_TAG_VVAR;

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (static_cast<const HVARVVAR *> (this)->sanitize (c) &&
		  vorgMap.sanitize (c, this));
  }

  void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const
  {
    HVARVVAR::listup_index_maps (index_maps);
    index_maps.push (&(this+vorgMap));
  }

  bool serialize_index_maps (hb_serialize_context_t *c,
			     const hb_array_t<index_map_subset_plan_t> &im_plans)
  {
    TRACE_SERIALIZE (this);
    if (unlikely (!HVARVVAR::serialize_index_maps (c, im_plans)))
      return_trace (false);
    if (!im_plans[index_map_subset_plan_t::VORG_INDEX].get_map_count ())
      vorgMap = 0;
    else if (unlikely (!vorgMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::VORG_INDEX])))
      return_trace (false);

    return_trace (true);
  }

  bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset<VVAR> (c); }

  protected:
  LOffsetTo<DeltaSetIndexMap>
		vorgMap;	/* Offset to vertical-origin var-idx mapping. */

  public:
  DEFINE_SIZE_STATIC (24);
};

} /* namespace OT */

#endif /* HB_OT_VAR_HVAR_TABLE_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
 */

#ifndef HB_BIMAP_HH
#define HB_BIMAP_HH

#include "hb.hh"
#include "hb-map.hh"

/* Bi-directional map */
struct hb_bimap_t
{
  hb_bimap_t () { init (); }
  ~hb_bimap_t () { fini (); }

  void init ()
  {
    forw_map.init ();
    back_map.init ();
  }

  void fini ()
  {
    forw_map.fini ();
    back_map.fini ();
  }

  void reset ()
  {
    forw_map.reset ();
    back_map.reset ();
  }

  bool in_error () const { return forw_map.in_error () || back_map.in_error (); }

  void set (hb_codepoint_t lhs, hb_codepoint_t rhs)
  {
    if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return;
    if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; }
    forw_map.set (lhs, rhs);
    back_map.set (rhs, lhs);
  }

  hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); }
  hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); }

  hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); }
  bool has (hb_codepoint_t lhs, hb_codepoint_t *vp = nullptr) const { return forw_map.has (lhs, vp); }

  void del (hb_codepoint_t lhs)
  {
    back_map.del (get (lhs));
    forw_map.del (lhs);
  }

  void clear ()
  {
    forw_map.clear ();
    back_map.clear ();
  }

  bool is_empty () const { return get_population () == 0; }

  unsigned int get_population () const { return forw_map.get_population (); }

  protected:
  hb_map_t  forw_map;
  hb_map_t  back_map;
};

/* Inremental bimap: only lhs is given, rhs is incrementally assigned */
struct hb_inc_bimap_t : hb_bimap_t
{
  hb_inc_bimap_t () { init (); }

  void init ()
  {
    hb_bimap_t::init ();
    next_value = 0;
  }

  /* Add a mapping from lhs to rhs with a unique value if lhs is unknown.
   * Return the rhs value as the result.
   */
  hb_codepoint_t add (hb_codepoint_t lhs)
  {
    hb_codepoint_t  rhs = forw_map[lhs];
    if (rhs == HB_MAP_VALUE_INVALID)
    {
      rhs = next_value++;
      set (lhs, rhs);
    }
    return rhs;
  }

  hb_codepoint_t skip ()
  { return next_value++; }

  hb_codepoint_t get_next_value () const
  { return next_value; }

  void add_set (const hb_set_t *set)
  {
    hb_codepoint_t i = HB_SET_VALUE_INVALID;
    while (hb_set_next (set, &i)) add (i);
  }

  /* Create an identity map. */
  bool identity (unsigned int size)
  {
    clear ();
    for (hb_codepoint_t i = 0; i < size; i++) set (i, i);
    return !in_error ();
  }

  protected:
  static int cmp_id (const void* a, const void* b)
  { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; }

  public:
  /* Optional: after finished adding all mappings in a random order,
   * reassign rhs to lhs so that they are in the same order. */
  void sort ()
  {
    hb_codepoint_t  count = get_population ();
    hb_vector_t <hb_codepoint_t> work;
    work.resize (count);

    for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
      work[rhs] = back_map[rhs];

    work.qsort (cmp_id);

    clear ();
    for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
      set (work[rhs], rhs);
  }

  protected:
  unsigned int	next_value;
};

#endif /* HB_BIMAP_HH */

repo_language: C

mime_type: text/x-c


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

#include "hb.hh"

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

#include <stdio.h>

#ifdef HAVE_FREETYPE
#include "hb-ft.h"
#endif

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

int
main (int argc, char **argv)
{
  if (argc != 4 && argc != 5) {
    fprintf (stderr, "usage: %s font-file lookup-index first-glyph [second-glyph]\n", argv[0]);
    exit (1);
  }

  /* Create the face */
  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
  hb_face_t *face = hb_face_create (blob, 0 /* first face */);
  hb_blob_destroy (blob);
  blob = nullptr;

  hb_font_t *font = hb_font_create (face);
#ifdef HAVE_FREETYPE
  hb_ft_font_set_funcs (font);
#endif

  unsigned int len = argc - 3;
  hb_codepoint_t glyphs[2];
  if (!hb_font_glyph_from_string (font, argv[3], -1, &glyphs[0]) ||
      (argc > 4 &&
       !hb_font_glyph_from_string (font, argv[4], -1, &glyphs[1])))
    return 2;
  return !hb_ot_layout_lookup_would_substitute (face, strtol (argv[2], nullptr, 0), glyphs, len, false);
}


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

#ifndef HB_UNICODE_HH
#define HB_UNICODE_HH

#include "hb.hh"

extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256];

/*
 * hb_unicode_funcs_t
 */

#define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS \
  HB_UNICODE_FUNC_IMPLEMENT (combining_class) \
  HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (eastasian_width)) \
  HB_UNICODE_FUNC_IMPLEMENT (general_category) \
  HB_UNICODE_FUNC_IMPLEMENT (mirroring) \
  HB_UNICODE_FUNC_IMPLEMENT (script) \
  HB_UNICODE_FUNC_IMPLEMENT (compose) \
  HB_UNICODE_FUNC_IMPLEMENT (decompose) \
  HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility)) \
  /* ^--- Add new callbacks here */

/* Simple callbacks are those taking a hb_codepoint_t and returning a hb_codepoint_t */
#define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE \
  HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_combining_class_t, combining_class) \
  HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (unsigned int, eastasian_width)) \
  HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_general_category_t, general_category) \
  HB_UNICODE_FUNC_IMPLEMENT (hb_codepoint_t, mirroring) \
  HB_UNICODE_FUNC_IMPLEMENT (hb_script_t, script) \
  /* ^--- Add new simple callbacks here */

struct hb_unicode_funcs_t
{
  hb_object_header_t header;

  hb_unicode_funcs_t *parent;

#define HB_UNICODE_FUNC_IMPLEMENT(return_type, name) \
  return_type name (hb_codepoint_t unicode) { return func.name (this, unicode, user_data.name); }
HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
#undef HB_UNICODE_FUNC_IMPLEMENT

  hb_bool_t compose (hb_codepoint_t a, hb_codepoint_t b,
		     hb_codepoint_t *ab)
  {
    *ab = 0;
    if (unlikely (!a || !b)) return false;
    return func.compose (this, a, b, ab, user_data.compose);
  }

  hb_bool_t decompose (hb_codepoint_t ab,
		       hb_codepoint_t *a, hb_codepoint_t *b)
  {
    *a = ab; *b = 0;
    return func.decompose (this, ab, a, b, user_data.decompose);
  }

  unsigned int decompose_compatibility (hb_codepoint_t  u,
					hb_codepoint_t *decomposed)
  {
#ifdef HB_DISABLE_DEPRECATED
    unsigned int ret  = 0;
#else
    unsigned int ret = func.decompose_compatibility (this, u, decomposed, user_data.decompose_compatibility);
#endif
    if (ret == 1 && u == decomposed[0]) {
      decomposed[0] = 0;
      return 0;
    }
    decomposed[ret] = 0;
    return ret;
  }

  unsigned int
  modified_combining_class (hb_codepoint_t u)
  {
    /* XXX This hack belongs to the USE shaper (for Tai Tham):
     * Reorder SAKOT to ensure it comes after any tone marks. */
    if (unlikely (u == 0x1A60u)) return 254;

    /* XXX This hack belongs to the Tibetan shaper:
     * Reorder PADMA to ensure it comes after any vowel marks. */
    if (unlikely (u == 0x0FC6u)) return 254;
    /* Reorder TSA -PHRU to reorder before U+0F74 */
    if (unlikely (u == 0x0F39u)) return 127;

    return _hb_modified_combining_class[combining_class (u)];
  }

  static hb_bool_t
  is_variation_selector (hb_codepoint_t unicode)
  {
    /* U+180B..180D MONGOLIAN FREE VARIATION SELECTORs are handled in the
     * Arabic shaper.  No need to match them here. */
    return unlikely (hb_in_ranges<hb_codepoint_t> (unicode,
						   0xFE00u, 0xFE0Fu, /* VARIATION SELECTOR-1..16 */
						   0xE0100u, 0xE01EFu));  /* VARIATION SELECTOR-17..256 */
  }

  /* Default_Ignorable codepoints:
   *
   * Note: While U+115F, U+1160, U+3164 and U+FFA0 are Default_Ignorable,
   * we do NOT want to hide them, as the way Uniscribe has implemented them
   * is with regular spacing glyphs, and that's the way fonts are made to work.
   * As such, we make exceptions for those four.
   * Also ignoring U+1BCA0..1BCA3. https://github.com/harfbuzz/harfbuzz/issues/503
   *
   * Unicode 7.0:
   * $ grep '; Default_Ignorable_Code_Point ' DerivedCoreProperties.txt | sed 's/;.*#/#/'
   * 00AD          # Cf       SOFT HYPHEN
   * 034F          # Mn       COMBINING GRAPHEME JOINER
   * 061C          # Cf       ARABIC LETTER MARK
   * 115F..1160    # Lo   [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER
   * 17B4..17B5    # Mn   [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
   * 180B..180D    # Mn   [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
   * 180E          # Cf       MONGOLIAN VOWEL SEPARATOR
   * 200B..200F    # Cf   [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
   * 202A..202E    # Cf   [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
   * 2060..2064    # Cf   [5] WORD JOINER..INVISIBLE PLUS
   * 2065          # Cn       <reserved-2065>
   * 2066..206F    # Cf  [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
   * 3164          # Lo       HANGUL FILLER
   * FE00..FE0F    # Mn  [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
   * FEFF          # Cf       ZERO WIDTH NO-BREAK SPACE
   * FFA0          # Lo       HALFWIDTH HANGUL FILLER
   * FFF0..FFF8    # Cn   [9] <reserved-FFF0>..<reserved-FFF8>
   * 1BCA0..1BCA3  # Cf   [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
   * 1D173..1D17A  # Cf   [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
   * E0000         # Cn       <reserved-E0000>
   * E0001         # Cf       LANGUAGE TAG
   * E0002..E001F  # Cn  [30] <reserved-E0002>..<reserved-E001F>
   * E0020..E007F  # Cf  [96] TAG SPACE..CANCEL TAG
   * E0080..E00FF  # Cn [128] <reserved-E0080>..<reserved-E00FF>
   * E0100..E01EF  # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
   * E01F0..E0FFF  # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
   */
  static hb_bool_t
  is_default_ignorable (hb_codepoint_t ch)
  {
    hb_codepoint_t plane = ch >> 16;
    if (likely (plane == 0))
    {
      /* BMP */
      hb_codepoint_t page = ch >> 8;
      switch (page) {
	case 0x00: return unlikely (ch == 0x00ADu);
	case 0x03: return unlikely (ch == 0x034Fu);
	case 0x06: return unlikely (ch == 0x061Cu);
	case 0x17: return hb_in_range<hb_codepoint_t> (ch, 0x17B4u, 0x17B5u);
	case 0x18: return hb_in_range<hb_codepoint_t> (ch, 0x180Bu, 0x180Eu);
	case 0x20: return hb_in_ranges<hb_codepoint_t> (ch, 0x200Bu, 0x200Fu,
					    0x202Au, 0x202Eu,
					    0x2060u, 0x206Fu);
	case 0xFE: return hb_in_range<hb_codepoint_t> (ch, 0xFE00u, 0xFE0Fu) || ch == 0xFEFFu;
	case 0xFF: return hb_in_range<hb_codepoint_t> (ch, 0xFFF0u, 0xFFF8u);
	default: return false;
      }
    }
    else
    {
      /* Other planes */
      switch (plane) {
	case 0x01: return hb_in_range<hb_codepoint_t> (ch, 0x1D173u, 0x1D17Au);
	case 0x0E: return hb_in_range<hb_codepoint_t> (ch, 0xE0000u, 0xE0FFFu);
	default: return false;
      }
    }
  }

  /* Space estimates based on:
   * https://unicode.org/charts/PDF/U2000.pdf
   * https://docs.microsoft.com/en-us/typography/develop/character-design-standards/whitespace
   */
  enum space_t {
    NOT_SPACE = 0,
    SPACE_EM   = 1,
    SPACE_EM_2 = 2,
    SPACE_EM_3 = 3,
    SPACE_EM_4 = 4,
    SPACE_EM_5 = 5,
    SPACE_EM_6 = 6,
    SPACE_EM_16 = 16,
    SPACE_4_EM_18,	/* 4/18th of an EM! */
    SPACE,
    SPACE_FIGURE,
    SPACE_PUNCTUATION,
    SPACE_NARROW,
  };
  static space_t
  space_fallback_type (hb_codepoint_t u)
  {
    switch (u)
    {
      /* All GC=Zs chars that can use a fallback. */
      default:	    return NOT_SPACE;	/* U+1680 OGHAM SPACE MARK */
      case 0x0020u: return SPACE;	/* U+0020 SPACE */
      case 0x00A0u: return SPACE;	/* U+00A0 NO-BREAK SPACE */
      case 0x2000u: return SPACE_EM_2;	/* U+2000 EN QUAD */
      case 0x2001u: return SPACE_EM;	/* U+2001 EM QUAD */
      case 0x2002u: return SPACE_EM_2;	/* U+2002 EN SPACE */
      case 0x2003u: return SPACE_EM;	/* U+2003 EM SPACE */
      case 0x2004u: return SPACE_EM_3;	/* U+2004 THREE-PER-EM SPACE */
      case 0x2005u: return SPACE_EM_4;	/* U+2005 FOUR-PER-EM SPACE */
      case 0x2006u: return SPACE_EM_6;	/* U+2006 SIX-PER-EM SPACE */
      case 0x2007u: return SPACE_FIGURE;	/* U+2007 FIGURE SPACE */
      case 0x2008u: return SPACE_PUNCTUATION;	/* U+2008 PUNCTUATION SPACE */
      case 0x2009u: return SPACE_EM_5;		/* U+2009 THIN SPACE */
      case 0x200Au: return SPACE_EM_16;		/* U+200A HAIR SPACE */
      case 0x202Fu: return SPACE_NARROW;	/* U+202F NARROW NO-BREAK SPACE */
      case 0x205Fu: return SPACE_4_EM_18;	/* U+205F MEDIUM MATHEMATICAL SPACE */
      case 0x3000u: return SPACE_EM;		/* U+3000 IDEOGRAPHIC SPACE */
    }
  }

  struct {
#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name;
    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_UNICODE_FUNC_IMPLEMENT
  } func;

  struct {
#define HB_UNICODE_FUNC_IMPLEMENT(name) void *name;
    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_UNICODE_FUNC_IMPLEMENT
  } user_data;

  struct {
#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_UNICODE_FUNC_IMPLEMENT
  } destroy;
};
DECLARE_NULL_INSTANCE (hb_unicode_funcs_t);

/*
 * Modified combining marks
 */

/* Hebrew
 *
 * We permute the "fixed-position" classes 10-26 into the order
 * described in the SBL Hebrew manual:
 *
 * https://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf
 *
 * (as recommended by:
 *  https://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering/msg22823/)
 *
 * More details here:
 * https://bugzilla.mozilla.org/show_bug.cgi?id=662055
 */
#define HB_MODIFIED_COMBINING_CLASS_CCC10 22 /* sheva */
#define HB_MODIFIED_COMBINING_CLASS_CCC11 15 /* hataf segol */
#define HB_MODIFIED_COMBINING_CLASS_CCC12 16 /* hataf patah */
#define HB_MODIFIED_COMBINING_CLASS_CCC13 17 /* hataf qamats */
#define HB_MODIFIED_COMBINING_CLASS_CCC14 23 /* hiriq */
#define HB_MODIFIED_COMBINING_CLASS_CCC15 18 /* tsere */
#define HB_MODIFIED_COMBINING_CLASS_CCC16 19 /* segol */
#define HB_MODIFIED_COMBINING_CLASS_CCC17 20 /* patah */
#define HB_MODIFIED_COMBINING_CLASS_CCC18 21 /* qamats */
#define HB_MODIFIED_COMBINING_CLASS_CCC19 14 /* holam */
#define HB_MODIFIED_COMBINING_CLASS_CCC20 24 /* qubuts */
#define HB_MODIFIED_COMBINING_CLASS_CCC21 12 /* dagesh */
#define HB_MODIFIED_COMBINING_CLASS_CCC22 25 /* meteg */
#define HB_MODIFIED_COMBINING_CLASS_CCC23 13 /* rafe */
#define HB_MODIFIED_COMBINING_CLASS_CCC24 10 /* shin dot */
#define HB_MODIFIED_COMBINING_CLASS_CCC25 11 /* sin dot */
#define HB_MODIFIED_COMBINING_CLASS_CCC26 26 /* point varika */

/*
 * Arabic
 *
 * Modify to move Shadda (ccc=33) before other marks.  See:
 * https://unicode.org/faq/normalization.html#8
 * https://unicode.org/faq/normalization.html#9
 */
#define HB_MODIFIED_COMBINING_CLASS_CCC27 28 /* fathatan */
#define HB_MODIFIED_COMBINING_CLASS_CCC28 29 /* dammatan */
#define HB_MODIFIED_COMBINING_CLASS_CCC29 30 /* kasratan */
#define HB_MODIFIED_COMBINING_CLASS_CCC30 31 /* fatha */
#define HB_MODIFIED_COMBINING_CLASS_CCC31 32 /* damma */
#define HB_MODIFIED_COMBINING_CLASS_CCC32 33 /* kasra */
#define HB_MODIFIED_COMBINING_CLASS_CCC33 27 /* shadda */
#define HB_MODIFIED_COMBINING_CLASS_CCC34 34 /* sukun */
#define HB_MODIFIED_COMBINING_CLASS_CCC35 35 /* superscript alef */

/* Syriac */
#define HB_MODIFIED_COMBINING_CLASS_CCC36 36 /* superscript alaph */

/* Telugu
 *
 * Modify Telugu length marks (ccc=84, ccc=91).
 * These are the only matras in the main Indic scripts range that have
 * a non-zero ccc.  That makes them reorder with the Halant (ccc=9).
 * Assign 4 and 5, which are otherwise unassigned.
 */
#define HB_MODIFIED_COMBINING_CLASS_CCC84 4 /* length mark */
#define HB_MODIFIED_COMBINING_CLASS_CCC91 5 /* ai length mark */

/* Thai
 *
 * Modify U+0E38 and U+0E39 (ccc=103) to be reordered before U+0E3A (ccc=9).
 * Assign 3, which is unassigned otherwise.
 * Uniscribe does this reordering too.
 */
#define HB_MODIFIED_COMBINING_CLASS_CCC103 3 /* sara u / sara uu */
#define HB_MODIFIED_COMBINING_CLASS_CCC107 107 /* mai * */

/* Lao */
#define HB_MODIFIED_COMBINING_CLASS_CCC118 118 /* sign u / sign uu */
#define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */

/* Tibetan
 *
 * In case of multiple vowel-signs, use u first (but after achung)
 * this allows Dzongkha multi-vowel shortcuts to render correctly
 */
#define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */
#define HB_MODIFIED_COMBINING_CLASS_CCC130 132 /* sign i */
#define HB_MODIFIED_COMBINING_CLASS_CCC132 131 /* sign u */

/* Misc */

#define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \
	(FLAG_UNSAFE (gen_cat) & \
	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \
	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))

/*
 * Ranges, used for bsearch tables.
 */

struct hb_unicode_range_t
{
  static int
  cmp (const void *_key, const void *_item)
  {
    hb_codepoint_t cp = *((hb_codepoint_t *) _key);
    const hb_unicode_range_t *range = (hb_unicode_range_t *) _item;

    if (cp < range->start)
      return -1;
    else if (cp <= range->end)
      return 0;
    else
      return +1;
  }

  hb_codepoint_t start;
  hb_codepoint_t end;
};

/*
 * Emoji.
 */

HB_INTERNAL bool
_hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp);

extern "C" HB_INTERNAL hb_unicode_funcs_t *hb_ucd_get_unicode_funcs ();

#endif /* HB_UNICODE_HH */


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' ! -name 'hb-gobject-structs.h'`
test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`

for x in $HBHEADERS $HBSOURCES; do
	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
	echo "$x" | grep -q '[^h]$' && continue;
	xx=`echo "$x" | sed 's@.*/@@'`
	tag=`echo "$xx" | tr 'a-z.-' 'A-Z_'`
	lines=`grep -w "$tag" "$x" | wc -l | sed 's/[ 	]*//g'`
	if test "x$lines" != x3; then
		echo "Ouch, header file $x does not have correct preprocessor guards"
		stat=1
	fi
done

exit $stat


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

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

Libs: -L${libdir} -lharfbuzz
Libs.private: -lm %libs_private%
Requires.private: %requires_private%
Cflags: -I${includedir}/harfbuzz


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

#ifndef HB_SUBSET_HH
#define HB_SUBSET_HH

#include "hb.hh"

#include "hb-subset.h"

#include "hb-machinery.hh"
#include "hb-subset-input.hh"
#include "hb-subset-plan.hh"

struct hb_subset_context_t :
       hb_dispatch_context_t<hb_subset_context_t, bool, HB_DEBUG_SUBSET>
{
  const char *get_name () { return "SUBSET"; }
  static return_t default_return_value () { return true; }

  private:
  template <typename T, typename ...Ts> auto
  _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
  ( obj.subset (this, hb_forward<Ts> (ds)...) )
  template <typename T, typename ...Ts> auto
  _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
  ( obj.dispatch (this, hb_forward<Ts> (ds)...) )
  public:
  template <typename T, typename ...Ts> auto
  dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
  ( _dispatch (obj, hb_prioritize, hb_forward<Ts> (ds)...) )

  hb_blob_t *source_blob;
  hb_subset_plan_t *plan;
  hb_serialize_context_t *serializer;
  unsigned int debug_depth;

  hb_subset_context_t (hb_blob_t *source_blob_,
		       hb_subset_plan_t *plan_,
		       hb_serialize_context_t *serializer_) :
			source_blob (source_blob_),
			plan (plan_),
			serializer (serializer_),
			debug_depth (0) {}
};

#endif /* HB_SUBSET_HH */


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

#include "hb.hh"

#include "hb-unicode.hh"

/**
 * SECTION: hb-unicode
 * @title: hb-unicode
 * @short_description: Unicode character property access
 * @include: hb.h
 *
 * Unicode functions are used to access Unicode character properties.
 * Client can pass its own Unicode functions to HarfBuzz, or access
 * the built-in Unicode functions that come with HarfBuzz.
 *
 * With the Unicode functions, one can query variour Unicode character
 * properties, such as General Category, Script, Combining Class, etc.
 **/

/*
 * hb_unicode_funcs_t
 */

static hb_unicode_combining_class_t
hb_unicode_combining_class_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
				hb_codepoint_t      unicode   HB_UNUSED,
				void               *user_data HB_UNUSED)
{
  return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED;
}

#ifndef HB_DISABLE_DEPRECATED
static unsigned int
hb_unicode_eastasian_width_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
				hb_codepoint_t      unicode   HB_UNUSED,
				void               *user_data HB_UNUSED)
{
  return 1;
}
#endif

static hb_unicode_general_category_t
hb_unicode_general_category_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
				 hb_codepoint_t      unicode   HB_UNUSED,
				 void               *user_data HB_UNUSED)
{
  return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER;
}

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

static hb_script_t
hb_unicode_script_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
		       hb_codepoint_t      unicode   HB_UNUSED,
		       void               *user_data HB_UNUSED)
{
  return HB_SCRIPT_UNKNOWN;
}

static hb_bool_t
hb_unicode_compose_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
			hb_codepoint_t      a         HB_UNUSED,
			hb_codepoint_t      b         HB_UNUSED,
			hb_codepoint_t     *ab        HB_UNUSED,
			void               *user_data HB_UNUSED)
{
  return false;
}

static hb_bool_t
hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
			  hb_codepoint_t      ab        HB_UNUSED,
			  hb_codepoint_t     *a         HB_UNUSED,
			  hb_codepoint_t     *b         HB_UNUSED,
			  void               *user_data HB_UNUSED)
{
  return false;
}

#ifndef HB_DISABLE_DEPRECATED
static unsigned int
hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs     HB_UNUSED,
					hb_codepoint_t      u          HB_UNUSED,
					hb_codepoint_t     *decomposed HB_UNUSED,
					void               *user_data  HB_UNUSED)
{
  return 0;
}
#endif

#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB)
#include "hb-glib.h"
#endif
#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN)
#include "hb-icu.h"
#endif

hb_unicode_funcs_t *
hb_unicode_funcs_get_default ()
{
#if !defined(HB_NO_UNICODE_FUNCS) && !defined(HB_NO_UCD)
  return hb_ucd_get_unicode_funcs ();
#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB)
  return hb_glib_get_unicode_funcs ();
#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN)
  return hb_icu_get_unicode_funcs ();
#else
#define HB_UNICODE_FUNCS_NIL 1
  return hb_unicode_funcs_get_empty ();
#endif
}

#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL)
#error "Could not find any Unicode functions implementation, you have to provide your own"
#error "Consider building hb-ucd.cc.  If you absolutely want to build without any, check the code."
#endif

/**
 * hb_unicode_funcs_create: (Xconstructor)
 * @parent: (nullable):
 *
 *
 *
 * Return value: (transfer full):
 *
 * Since: 0.9.2
 **/
hb_unicode_funcs_t *
hb_unicode_funcs_create (hb_unicode_funcs_t *parent)
{
  hb_unicode_funcs_t *ufuncs;

  if (!(ufuncs = hb_object_create<hb_unicode_funcs_t> ()))
    return hb_unicode_funcs_get_empty ();

  if (!parent)
    parent = hb_unicode_funcs_get_empty ();

  hb_unicode_funcs_make_immutable (parent);
  ufuncs->parent = hb_unicode_funcs_reference (parent);

  ufuncs->func = parent->func;

  /* We can safely copy user_data from parent since we hold a reference
   * onto it and it's immutable.  We should not copy the destroy notifiers
   * though. */
  ufuncs->user_data = parent->user_data;

  return ufuncs;
}

DEFINE_NULL_INSTANCE (hb_unicode_funcs_t) =
{
  HB_OBJECT_HEADER_STATIC,

  nullptr, /* parent */
  {
#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil,
    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_UNICODE_FUNC_IMPLEMENT
  }
};

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

/**
 * hb_unicode_funcs_reference: (skip)
 * @ufuncs: Unicode functions.
 *
 *
 *
 * Return value: (transfer full):
 *
 * Since: 0.9.2
 **/
hb_unicode_funcs_t *
hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs)
{
  return hb_object_reference (ufuncs);
}

/**
 * hb_unicode_funcs_destroy: (skip)
 * @ufuncs: Unicode functions.
 *
 *
 *
 * Since: 0.9.2
 **/
void
hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs)
{
  if (!hb_object_destroy (ufuncs)) return;

#define HB_UNICODE_FUNC_IMPLEMENT(name) \
  if (ufuncs->destroy.name) ufuncs->destroy.name (ufuncs->user_data.name);
    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_UNICODE_FUNC_IMPLEMENT

  hb_unicode_funcs_destroy (ufuncs->parent);

  free (ufuncs);
}

/**
 * hb_unicode_funcs_set_user_data: (skip)
 * @ufuncs: Unicode functions.
 * @key:
 * @data:
 * @destroy:
 * @replace:
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 **/
hb_bool_t
hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs,
				hb_user_data_key_t *key,
				void *              data,
				hb_destroy_func_t   destroy,
				hb_bool_t           replace)
{
  return hb_object_set_user_data (ufuncs, key, data, destroy, replace);
}

/**
 * hb_unicode_funcs_get_user_data: (skip)
 * @ufuncs: Unicode functions.
 * @key:
 *
 *
 *
 * Return value: (transfer none):
 *
 * Since: 0.9.2
 **/
void *
hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs,
				hb_user_data_key_t *key)
{
  return hb_object_get_user_data (ufuncs, key);
}

/**
 * hb_unicode_funcs_make_immutable:
 * @ufuncs: Unicode functions.
 *
 *
 *
 * Since: 0.9.2
 **/
void
hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs)
{
  if (hb_object_is_immutable (ufuncs))
    return;

  hb_object_make_immutable (ufuncs);
}

/**
 * hb_unicode_funcs_is_immutable:
 * @ufuncs: Unicode functions.
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 **/
hb_bool_t
hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs)
{
  return hb_object_is_immutable (ufuncs);
}

/**
 * hb_unicode_funcs_get_parent:
 * @ufuncs: Unicode functions.
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 **/
hb_unicode_funcs_t *
hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs)
{
  return ufuncs->parent ? ufuncs->parent : hb_unicode_funcs_get_empty ();
}

#define HB_UNICODE_FUNC_IMPLEMENT(name)						\
										\
void										\
hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t		   *ufuncs,	\
				    hb_unicode_##name##_func_t	    func,	\
				    void			   *user_data,	\
				    hb_destroy_func_t		    destroy)	\
{										\
  if (hb_object_is_immutable (ufuncs))						\
    return;									\
										\
  if (ufuncs->destroy.name)							\
    ufuncs->destroy.name (ufuncs->user_data.name);				\
										\
  if (func) {									\
    ufuncs->func.name = func;							\
    ufuncs->user_data.name = user_data;						\
    ufuncs->destroy.name = destroy;						\
  } else {									\
    ufuncs->func.name = ufuncs->parent->func.name;				\
    ufuncs->user_data.name = ufuncs->parent->user_data.name;			\
    ufuncs->destroy.name = nullptr;						\
  }										\
}

HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_UNICODE_FUNC_IMPLEMENT

#define HB_UNICODE_FUNC_IMPLEMENT(return_type, name)				\
										\
return_type									\
hb_unicode_##name (hb_unicode_funcs_t *ufuncs,					\
		   hb_codepoint_t      unicode)					\
{										\
  return ufuncs->name (unicode);						\
}
HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
#undef HB_UNICODE_FUNC_IMPLEMENT

/**
 * hb_unicode_compose:
 * @ufuncs: Unicode functions.
 * @a:
 * @b:
 * @ab: (out):
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 **/
hb_bool_t
hb_unicode_compose (hb_unicode_funcs_t *ufuncs,
		    hb_codepoint_t      a,
		    hb_codepoint_t      b,
		    hb_codepoint_t     *ab)
{
  return ufuncs->compose (a, b, ab);
}

/**
 * hb_unicode_decompose:
 * @ufuncs: Unicode functions.
 * @ab:
 * @a: (out):
 * @b: (out):
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 **/
hb_bool_t
hb_unicode_decompose (hb_unicode_funcs_t *ufuncs,
		      hb_codepoint_t      ab,
		      hb_codepoint_t     *a,
		      hb_codepoint_t     *b)
{
  return ufuncs->decompose (ab, a, b);
}

#ifndef HB_DISABLE_DEPRECATED
/**
 * hb_unicode_decompose_compatibility:
 * @ufuncs: Unicode functions.
 * @u:
 * @decomposed: (out):
 *
 *
 *
 * Return value:
 *
 * Since: 0.9.2
 * Deprecated: 2.0.0
 **/
unsigned int
hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
				    hb_codepoint_t      u,
				    hb_codepoint_t     *decomposed)
{
  return ufuncs->decompose_compatibility (u, decomposed);
}
#endif

#ifndef HB_NO_OT_SHAPE
/* See hb-unicode.hh for details. */
const uint8_t
_hb_modified_combining_class[256] =
{
  0, /* HB_UNICODE_COMBINING_CLASS_NOT_REORDERED */
  1, /* HB_UNICODE_COMBINING_CLASS_OVERLAY */
  2, 3, 4, 5, 6,
  7, /* HB_UNICODE_COMBINING_CLASS_NUKTA */
  8, /* HB_UNICODE_COMBINING_CLASS_KANA_VOICING */
  9, /* HB_UNICODE_COMBINING_CLASS_VIRAMA */

  /* Hebrew */
  HB_MODIFIED_COMBINING_CLASS_CCC10,
  HB_MODIFIED_COMBINING_CLASS_CCC11,
  HB_MODIFIED_COMBINING_CLASS_CCC12,
  HB_MODIFIED_COMBINING_CLASS_CCC13,
  HB_MODIFIED_COMBINING_CLASS_CCC14,
  HB_MODIFIED_COMBINING_CLASS_CCC15,
  HB_MODIFIED_COMBINING_CLASS_CCC16,
  HB_MODIFIED_COMBINING_CLASS_CCC17,
  HB_MODIFIED_COMBINING_CLASS_CCC18,
  HB_MODIFIED_COMBINING_CLASS_CCC19,
  HB_MODIFIED_COMBINING_CLASS_CCC20,
  HB_MODIFIED_COMBINING_CLASS_CCC21,
  HB_MODIFIED_COMBINING_CLASS_CCC22,
  HB_MODIFIED_COMBINING_CLASS_CCC23,
  HB_MODIFIED_COMBINING_CLASS_CCC24,
  HB_MODIFIED_COMBINING_CLASS_CCC25,
  HB_MODIFIED_COMBINING_CLASS_CCC26,

  /* Arabic */
  HB_MODIFIED_COMBINING_CLASS_CCC27,
  HB_MODIFIED_COMBINING_CLASS_CCC28,
  HB_MODIFIED_COMBINING_CLASS_CCC29,
  HB_MODIFIED_COMBINING_CLASS_CCC30,
  HB_MODIFIED_COMBINING_CLASS_CCC31,
  HB_MODIFIED_COMBINING_CLASS_CCC32,
  HB_MODIFIED_COMBINING_CLASS_CCC33,
  HB_MODIFIED_COMBINING_CLASS_CCC34,
  HB_MODIFIED_COMBINING_CLASS_CCC35,

  /* Syriac */
  HB_MODIFIED_COMBINING_CLASS_CCC36,

  37, 38, 39,
  40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
  60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
  80, 81, 82, 83,

  /* Telugu */
  HB_MODIFIED_COMBINING_CLASS_CCC84,
  85, 86, 87, 88, 89, 90,
  HB_MODIFIED_COMBINING_CLASS_CCC91,
  92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,

  /* Thai */
  HB_MODIFIED_COMBINING_CLASS_CCC103,
  104, 105, 106,
  HB_MODIFIED_COMBINING_CLASS_CCC107,
  108, 109, 110, 111, 112, 113, 114, 115, 116, 117,

  /* Lao */
  HB_MODIFIED_COMBINING_CLASS_CCC118,
  119, 120, 121,
  HB_MODIFIED_COMBINING_CLASS_CCC122,
  123, 124, 125, 126, 127, 128,

  /* Tibetan */
  HB_MODIFIED_COMBINING_CLASS_CCC129,
  HB_MODIFIED_COMBINING_CLASS_CCC130,
  131,
  HB_MODIFIED_COMBINING_CLASS_CCC132,
  133, 134, 135, 136, 137, 138, 139,

  140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
  150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
  160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
  170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
  180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
  190, 191, 192, 193, 194, 195, 196, 197, 198, 199,

  200, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT */
  201,
  202, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW */
  203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213,
  214, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE */
  215,
  216, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT */
  217,
  218, /* HB_UNICODE_COMBINING_CLASS_BELOW_LEFT */
  219,
  220, /* HB_UNICODE_COMBINING_CLASS_BELOW */
  221,
  222, /* HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT */
  223,
  224, /* HB_UNICODE_COMBINING_CLASS_LEFT */
  225,
  226, /* HB_UNICODE_COMBINING_CLASS_RIGHT */
  227,
  228, /* HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT */
  229,
  230, /* HB_UNICODE_COMBINING_CLASS_ABOVE */
  231,
  232, /* HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT */
  233, /* HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW */
  234, /* HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE */
  235, 236, 237, 238, 239,
  240, /* HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT */
  241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
  255, /* HB_UNICODE_COMBINING_CLASS_INVALID */
};
#endif

/*
 * Emoji
 */
#ifndef HB_NO_EMOJI_SEQUENCES

#include "hb-unicode-emoji-table.hh"

bool
_hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp)
{
  return _hb_emoji_is_Extended_Pictographic (cp);
}
#endif


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

#include "hb.hh"

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

#include <stdio.h>

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

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

  /* Create the face */
  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
  hb_face_t *face = hb_face_create (blob, 0 /* first face */);
  hb_blob_destroy (blob);
  blob = nullptr;

  bool ret = true;

#ifndef HB_NO_LAYOUT_FEATURE_PARAMS
  unsigned int p[5];
  ret = hb_ot_layout_get_size_params (face, p, p+1, (p+2), p+3, p+4);
  printf ("%g %u %u %g %g\n", p[0]/10., p[1], p[2], p[3]/10., p[4]/10.);
#endif

  hb_face_destroy (face);

  return !ret;
}


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, Roderick Sheeter
 */

#ifndef HB_OT_HMTX_TABLE_HH
#define HB_OT_HMTX_TABLE_HH

#include "hb-open-type.hh"
#include "hb-ot-hhea-table.hh"
#include "hb-ot-var-hvar-table.hh"
#include "hb-ot-metrics.hh"

/*
 * hmtx -- Horizontal Metrics
 * https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx
 * vmtx -- Vertical Metrics
 * https://docs.microsoft.com/en-us/typography/opentype/spec/vmtx
 */
#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x')
#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x')

HB_INTERNAL int
_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);

HB_INTERNAL unsigned
_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);

namespace OT {

struct LongMetric
{
  UFWORD	advance; /* Advance width/height. */
  FWORD		sb; /* Leading (left/top) side bearing. */
  public:
  DEFINE_SIZE_STATIC (4);
};

template <typename T, typename H>
struct hmtxvmtx
{
  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
  {
    TRACE_SANITIZE (this);
    /* We don't check for anything specific here.  The users of the
     * struct do all the hard work... */
    return_trace (true);
  }

  bool subset_update_header (hb_subset_plan_t *plan,
			     unsigned int num_hmetrics) const
  {
    hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (plan->source, H::tableTag);
    hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob);
    hb_blob_destroy (src_blob);

    if (unlikely (!dest_blob)) {
      return false;
    }

    unsigned int length;
    H *table = (H *) hb_blob_get_data (dest_blob, &length);
    table->numberOfLongMetrics = num_hmetrics;

    bool result = plan->add_table (H::tableTag, dest_blob);
    hb_blob_destroy (dest_blob);

    return result;
  }

  template<typename Iterator,
	   hb_requires (hb_is_iterator (Iterator))>
  void serialize (hb_serialize_context_t *c,
		  Iterator it,
		  unsigned num_advances)
  {
    unsigned idx = 0;
    + it
    | hb_apply ([c, &idx, num_advances] (const hb_item_type<Iterator>& _)
		{
		  if (idx < num_advances)
		  {
		    LongMetric lm;
		    lm.advance = _.first;
		    lm.sb = _.second;
		    if (unlikely (!c->embed<LongMetric> (&lm))) return;
		  }
		  else
		  {
		    FWORD *sb = c->allocate_size<FWORD> (FWORD::static_size);
		    if (unlikely (!sb)) return;
		    *sb = _.second;
		  }
		  idx++;
		})
    ;
  }

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

    T *table_prime = c->serializer->start_embed <T> ();
    if (unlikely (!table_prime)) return_trace (false);

    accelerator_t _mtx;
    _mtx.init (c->plan->source);
    unsigned num_advances = _mtx.num_advances_for_subset (c->plan);

    auto it =
    + hb_range (c->plan->num_output_glyphs ())
    | hb_map ([c, &_mtx] (unsigned _)
	      {
		hb_codepoint_t old_gid;
		if (!c->plan->old_gid_for_new_gid (_, &old_gid))
		  return hb_pair (0u, 0);
		return hb_pair (_mtx.get_advance (old_gid), _mtx.get_side_bearing (old_gid));
	      })
    ;

    table_prime->serialize (c->serializer, it, num_advances);

    _mtx.fini ();

    if (unlikely (c->serializer->ran_out_of_room || c->serializer->in_error ()))
      return_trace (false);

    // Amend header num hmetrics
    if (unlikely (!subset_update_header (c->plan, num_advances)))
      return_trace (false);

    return_trace (true);
  }

  struct accelerator_t
  {
    friend struct hmtxvmtx;

    void init (hb_face_t *face,
	       unsigned int default_advance_ = 0)
    {
      default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);

      num_advances = T::is_horizontal ? face->table.hhea->numberOfLongMetrics : face->table.vhea->numberOfLongMetrics;

      table = hb_sanitize_context_t ().reference_table<hmtxvmtx> (face, T::tableTag);

      /* Cap num_metrics() and num_advances() based on table length. */
      unsigned int len = table.get_length ();
      if (unlikely (num_advances * 4 > len))
	num_advances = len / 4;
      num_metrics = num_advances + (len - 4 * num_advances) / 2;

      /* We MUST set num_metrics to zero if num_advances is zero.
       * Our get_advance() depends on that. */
      if (unlikely (!num_advances))
      {
	num_metrics = num_advances = 0;
	table.destroy ();
	table = hb_blob_get_empty ();
      }

      var_table = hb_sanitize_context_t ().reference_table<HVARVVAR> (face, T::variationsTag);
    }

    void fini ()
    {
      table.destroy ();
      var_table.destroy ();
    }

    int get_side_bearing (hb_codepoint_t glyph) const
    {
      if (glyph < num_advances)
	return table->longMetricZ[glyph].sb;

      if (unlikely (glyph >= num_metrics))
	return 0;

      const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_advances];
      return bearings[glyph - num_advances];
    }

    int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const
    {
      int side_bearing = get_side_bearing (glyph);

#ifndef HB_NO_VAR
      if (unlikely (glyph >= num_metrics) || !font->num_coords)
	return side_bearing;

      if (var_table.get_length ())
	return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?!

      return _glyf_get_side_bearing_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
#else
      return side_bearing;
#endif
    }

    unsigned int get_advance (hb_codepoint_t glyph) const
    {
      if (unlikely (glyph >= num_metrics))
      {
	/* If num_metrics is zero, it means we don't have the metrics table
	 * for this direction: return default advance.  Otherwise, it means that the
	 * glyph index is out of bound: return zero. */
	if (num_metrics)
	  return 0;
	else
	  return default_advance;
      }

      return table->longMetricZ[hb_min (glyph, (uint32_t) num_advances - 1)].advance;
    }

    unsigned int get_advance (hb_codepoint_t  glyph,
			      hb_font_t      *font) const
    {
      unsigned int advance = get_advance (glyph);

#ifndef HB_NO_VAR
      if (unlikely (glyph >= num_metrics) || !font->num_coords)
	return advance;

      if (var_table.get_length ())
	return advance + roundf (var_table->get_advance_var (glyph, font)); // TODO Optimize?!

      return _glyf_get_advance_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
#else
      return advance;
#endif
    }

    unsigned int num_advances_for_subset (const hb_subset_plan_t *plan) const
    {
      unsigned int num_advances = plan->num_output_glyphs ();
      unsigned int last_advance = _advance_for_new_gid (plan,
							num_advances - 1);
      while (num_advances > 1 &&
	     last_advance == _advance_for_new_gid (plan,
						   num_advances - 2))
      {
	num_advances--;
      }

      return num_advances;
    }

    private:
    unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan,
				       hb_codepoint_t new_gid) const
    {
      hb_codepoint_t old_gid;
      if (!plan->old_gid_for_new_gid (new_gid, &old_gid))
	return 0;

      return get_advance (old_gid);
    }

    protected:
    unsigned int num_metrics;
    unsigned int num_advances;
    unsigned int default_advance;

    private:
    hb_blob_ptr_t<hmtxvmtx> table;
    hb_blob_ptr_t<HVARVVAR> var_table;
  };

  protected:
  UnsizedArrayOf<LongMetric>
		longMetricZ;	/* Paired advance width and leading
				 * bearing values for each glyph. The
				 * value numOfHMetrics comes from
				 * the 'hhea' table. If the font is
				 * monospaced, only one entry need
				 * be in the array, but that entry is
				 * required. The last entry applies to
				 * all subsequent glyphs. */
/*UnsizedArrayOf<FWORD>	leadingBearingX;*/
				/* Here the advance is assumed
				 * to be the same as the advance
				 * for the last entry above. The
				 * number of entries in this array is
				 * derived from numGlyphs (from 'maxp'
				 * table) minus numberOfLongMetrics.
				 * This generally is used with a run
				 * of monospaced glyphs (e.g., Kanji
				 * fonts or Courier fonts). Only one
				 * run is allowed and it must be at
				 * the end. This allows a monospaced
				 * font to vary the side bearing
				 * values for each glyph. */
  public:
  DEFINE_SIZE_ARRAY (0, longMetricZ);
};

struct hmtx : hmtxvmtx<hmtx, hhea> {
  static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx;
  static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR;
  static constexpr bool is_horizontal = true;
};
struct vmtx : hmtxvmtx<vmtx, vhea> {
  static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx;
  static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR;
  static constexpr bool is_horizontal = false;
};

struct hmtx_accelerator_t : hmtx::accelerator_t {};
struct vmtx_accelerator_t : vmtx::accelerator_t {};

} /* namespace OT */

#endif /* HB_OT_HMTX_TABLE_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_OT_CFF2_TABLE_HH
#define HB_OT_CFF2_TABLE_HH

#include "hb-ot-cff-common.hh"
#include "hb-subset-cff2.hh"
#include "hb-draw.hh"

namespace CFF {

/*
 * CFF2 -- Compact Font Format (CFF) Version 2
 * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2
 */
#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')

typedef CFFIndex<HBUINT32>  CFF2Index;
template <typename Type> struct CFF2IndexOf : CFFIndexOf<HBUINT32, Type> {};

typedef CFF2Index         CFF2CharStrings;
typedef Subrs<HBUINT32>   CFF2Subrs;

typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;

struct CFF2FDSelect
{
  bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs)
  {
    TRACE_SERIALIZE (this);
    unsigned int size = src.get_size (num_glyphs);
    CFF2FDSelect *dest = c->allocate_size<CFF2FDSelect> (size);
    if (unlikely (!dest)) return_trace (false);
    memcpy (dest, &src, size);
    return_trace (true);
  }

  unsigned int get_size (unsigned int num_glyphs) const
  {
    switch (format)
    {
    case 0: return format.static_size + u.format0.get_size (num_glyphs);
    case 3: return format.static_size + u.format3.get_size ();
    case 4: return format.static_size + u.format4.get_size ();
    default:return 0;
    }
  }

  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
  {
    if (this == &Null (CFF2FDSelect))
      return 0;

    switch (format)
    {
    case 0: return u.format0.get_fd (glyph);
    case 3: return u.format3.get_fd (glyph);
    case 4: return u.format4.get_fd (glyph);
    default:return 0;
    }
  }

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

    switch (format)
    {
    case 0: return_trace (u.format0.sanitize (c, fdcount));
    case 3: return_trace (u.format3.sanitize (c, fdcount));
    case 4: return_trace (u.format4.sanitize (c, fdcount));
    default:return_trace (false);
    }
  }

  HBUINT8	format;
  union {
  FDSelect0	format0;
  FDSelect3	format3;
  FDSelect4	format4;
  } u;
  public:
  DEFINE_SIZE_MIN (2);
};

struct CFF2VariationStore
{
  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c));
  }

  bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore)
  {
    TRACE_SERIALIZE (this);
    unsigned int size_ = varStore->get_size ();
    CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_);
    if (unlikely (!dest)) return_trace (false);
    memcpy (dest, varStore, size_);
    return_trace (true);
  }

  unsigned int get_size () const { return HBUINT16::static_size + size; }

  HBUINT16	size;
  VariationStore  varStore;

  DEFINE_SIZE_MIN (2 + VariationStore::min_size);
};

struct cff2_top_dict_values_t : top_dict_values_t<>
{
  void init ()
  {
    top_dict_values_t<>::init ();
    vstoreOffset = 0;
    FDSelectOffset = 0;
  }
  void fini () { top_dict_values_t<>::fini (); }

  unsigned int  vstoreOffset;
  unsigned int  FDSelectOffset;
};

struct cff2_top_dict_opset_t : top_dict_opset_t<>
{
  static void process_op (op_code_t op, num_interp_env_t& env, cff2_top_dict_values_t& dictval)
  {
    switch (op) {
      case OpCode_FontMatrix:
	{
	  dict_val_t val;
	  val.init ();
	  dictval.add_op (op, env.str_ref);
	  env.clear_args ();
	}
	break;

      case OpCode_vstore:
	dictval.vstoreOffset = env.argStack.pop_uint ();
	env.clear_args ();
	break;
      case OpCode_FDSelect:
	dictval.FDSelectOffset = env.argStack.pop_uint ();
	env.clear_args ();
	break;

      default:
	SUPER::process_op (op, env, dictval);
	/* Record this operand below if stack is empty, otherwise done */
	if (!env.argStack.is_empty ()) return;
    }

    if (unlikely (env.in_error ())) return;

    dictval.add_op (op, env.str_ref);
  }

  typedef top_dict_opset_t<> SUPER;
};

struct cff2_font_dict_values_t : dict_values_t<op_str_t>
{
  void init ()
  {
    dict_values_t<op_str_t>::init ();
    privateDictInfo.init ();
  }
  void fini () { dict_values_t<op_str_t>::fini (); }

  table_info_t    privateDictInfo;
};

struct cff2_font_dict_opset_t : dict_opset_t
{
  static void process_op (op_code_t op, num_interp_env_t& env, cff2_font_dict_values_t& dictval)
  {
    switch (op) {
      case OpCode_Private:
	dictval.privateDictInfo.offset = env.argStack.pop_uint ();
	dictval.privateDictInfo.size = env.argStack.pop_uint ();
	env.clear_args ();
	break;

      default:
	SUPER::process_op (op, env);
	if (!env.argStack.is_empty ())
	  return;
    }

    if (unlikely (env.in_error ())) return;

    dictval.add_op (op, env.str_ref);
  }

  private:
  typedef dict_opset_t SUPER;
};

template <typename VAL>
struct cff2_private_dict_values_base_t : dict_values_t<VAL>
{
  void init ()
  {
    dict_values_t<VAL>::init ();
    subrsOffset = 0;
    localSubrs = &Null (CFF2Subrs);
    ivs = 0;
  }
  void fini () { dict_values_t<VAL>::fini (); }

  unsigned int      subrsOffset;
  const CFF2Subrs   *localSubrs;
  unsigned int      ivs;
};

typedef cff2_private_dict_values_base_t<op_str_t> cff2_private_dict_values_subset_t;
typedef cff2_private_dict_values_base_t<num_dict_val_t> cff2_private_dict_values_t;

struct cff2_priv_dict_interp_env_t : num_interp_env_t
{
  void init (const byte_str_t &str)
  {
    num_interp_env_t::init (str);
    ivs = 0;
    seen_vsindex = false;
  }

  void process_vsindex ()
  {
    if (likely (!seen_vsindex))
    {
      set_ivs (argStack.pop_uint ());
    }
    seen_vsindex = true;
  }

  unsigned int get_ivs () const { return ivs; }
  void	 set_ivs (unsigned int ivs_) { ivs = ivs_; }

  protected:
  unsigned int  ivs;
  bool	  seen_vsindex;
};

struct cff2_private_dict_opset_t : dict_opset_t
{
  static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_t& dictval)
  {
    num_dict_val_t val;
    val.init ();

    switch (op) {
      case OpCode_StdHW:
      case OpCode_StdVW:
      case OpCode_BlueScale:
      case OpCode_BlueShift:
      case OpCode_BlueFuzz:
      case OpCode_ExpansionFactor:
      case OpCode_LanguageGroup:
	val.single_val = env.argStack.pop_num ();
	env.clear_args ();
	break;
      case OpCode_BlueValues:
      case OpCode_OtherBlues:
      case OpCode_FamilyBlues:
      case OpCode_FamilyOtherBlues:
      case OpCode_StemSnapH:
      case OpCode_StemSnapV:
	env.clear_args ();
	break;
      case OpCode_Subrs:
	dictval.subrsOffset = env.argStack.pop_uint ();
	env.clear_args ();
	break;
      case OpCode_vsindexdict:
	env.process_vsindex ();
	dictval.ivs = env.get_ivs ();
	env.clear_args ();
	break;
      case OpCode_blenddict:
	break;

      default:
	dict_opset_t::process_op (op, env);
	if (!env.argStack.is_empty ()) return;
	break;
    }

    if (unlikely (env.in_error ())) return;

    dictval.add_op (op, env.str_ref, val);
  }
};

struct cff2_private_dict_opset_subset_t : dict_opset_t
{
  static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_subset_t& dictval)
  {
    switch (op) {
      case OpCode_BlueValues:
      case OpCode_OtherBlues:
      case OpCode_FamilyBlues:
      case OpCode_FamilyOtherBlues:
      case OpCode_StdHW:
      case OpCode_StdVW:
      case OpCode_BlueScale:
      case OpCode_BlueShift:
      case OpCode_BlueFuzz:
      case OpCode_StemSnapH:
      case OpCode_StemSnapV:
      case OpCode_LanguageGroup:
      case OpCode_ExpansionFactor:
	env.clear_args ();
	break;

      case OpCode_blenddict:
	env.clear_args ();
	return;

      case OpCode_Subrs:
	dictval.subrsOffset = env.argStack.pop_uint ();
	env.clear_args ();
	break;

      default:
	SUPER::process_op (op, env);
	if (!env.argStack.is_empty ()) return;
	break;
    }

    if (unlikely (env.in_error ())) return;

    dictval.add_op (op, env.str_ref);
  }

  private:
  typedef dict_opset_t SUPER;
};

typedef dict_interpreter_t<cff2_top_dict_opset_t, cff2_top_dict_values_t> cff2_top_dict_interpreter_t;
typedef dict_interpreter_t<cff2_font_dict_opset_t, cff2_font_dict_values_t> cff2_font_dict_interpreter_t;

struct CFF2FDArray : FDArray<HBUINT32>
{
  /* FDArray::serialize does not compile without this partial specialization */
  template <typename ITER, typename OP_SERIALIZER>
  bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr)
  { return FDArray<HBUINT32>::serialize<cff2_font_dict_values_t, table_info_t> (c, it, opszr); }
};

} /* namespace CFF */

namespace OT {

using namespace CFF;

struct cff2
{
  static constexpr hb_tag_t tableTag = HB_OT_TAG_cff2;

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

  template <typename PRIVOPSET, typename PRIVDICTVAL>
  struct accelerator_templ_t
  {
    void init (hb_face_t *face)
    {
      topDict.init ();
      fontDicts.init ();
      privateDicts.init ();

      this->blob = sc.reference_table<cff2> (face);

      /* setup for run-time santization */
      sc.init (this->blob);
      sc.start_processing ();

      const OT::cff2 *cff2 = this->blob->template as<OT::cff2> ();

      if (cff2 == &Null (OT::cff2))
      { fini (); return; }

      { /* parse top dict */
	byte_str_t topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
	if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
	cff2_top_dict_interpreter_t top_interp;
	top_interp.env.init (topDictStr);
	topDict.init ();
	if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
      }

      globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize);
      varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset);
      charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset);
      fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset);
      fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset);

      if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) ||
	  (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) ||
	  (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) ||
	  (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) ||
	  (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
      { fini (); return; }

      num_glyphs = charStrings->count;
      if (num_glyphs != sc.get_num_glyphs ())
      { fini (); return; }

      fdCount = fdArray->count;
      privateDicts.resize (fdCount);

      /* parse font dicts and gather private dicts */
      for (unsigned int i = 0; i < fdCount; i++)
      {
	const byte_str_t fontDictStr = (*fdArray)[i];
	if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
	cff2_font_dict_values_t  *font;
	cff2_font_dict_interpreter_t font_interp;
	font_interp.env.init (fontDictStr);
	font = fontDicts.push ();
	if (unlikely (font == &Crap (cff2_font_dict_values_t))) { fini (); return; }
	font->init ();
	if (unlikely (!font_interp.interpret (*font))) { fini (); return; }

	const byte_str_t privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
	if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t>  priv_interp;
	priv_interp.env.init(privDictStr);
	privateDicts[i].init ();
	if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }

	privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset);
	if (privateDicts[i].localSubrs != &Null (CFF2Subrs) &&
	  unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
	{ fini (); return; }
      }
    }

    void fini ()
    {
      sc.end_processing ();
      topDict.fini ();
      fontDicts.fini_deep ();
      privateDicts.fini_deep ();
      hb_blob_destroy (blob);
      blob = nullptr;
    }

    bool is_valid () const { return blob; }

    protected:
    hb_blob_t			*blob;
    hb_sanitize_context_t	sc;

    public:
    cff2_top_dict_values_t	topDict;
    const CFF2Subrs		*globalSubrs;
    const CFF2VariationStore	*varStore;
    const CFF2CharStrings	*charStrings;
    const CFF2FDArray		*fdArray;
    const CFF2FDSelect		*fdSelect;
    unsigned int		fdCount;

    hb_vector_t<cff2_font_dict_values_t>     fontDicts;
    hb_vector_t<PRIVDICTVAL>  privateDicts;

    unsigned int	      num_glyphs;
  };

  struct accelerator_t : accelerator_templ_t<cff2_private_dict_opset_t, cff2_private_dict_values_t>
  {
    HB_INTERNAL bool get_extents (hb_font_t *font,
				  hb_codepoint_t glyph,
				  hb_glyph_extents_t *extents) const;
#ifdef HB_EXPERIMENTAL_API
    HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const;
#endif
  };

  typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> accelerator_subset_t;

  bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); }

  public:
  FixedVersion<HBUINT8>		version;	/* Version of CFF2 table. set to 0x0200u */
  NNOffsetTo<TopDict, HBUINT8>	topDict;	/* headerSize = Offset to Top DICT. */
  HBUINT16			topDictSize;	/* Top DICT size */

  public:
  DEFINE_SIZE_STATIC (5);
};

struct cff2_accelerator_t : cff2::accelerator_t {};
} /* namespace OT */

#endif /* HB_OT_CFF2_TABLE_HH */


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

#include "hb-subset.hh"
#include "hb-set.hh"

/**
 * hb_subset_input_create_or_fail:
 *
 * Return value: New subset input.
 *
 * Since: 1.8.0
 **/
hb_subset_input_t *
hb_subset_input_create_or_fail ()
{
  hb_subset_input_t *input = hb_object_create<hb_subset_input_t>();

  if (unlikely (!input))
    return nullptr;

  input->unicodes = hb_set_create ();
  input->glyphs = hb_set_create ();
  input->name_ids = hb_set_create ();
  hb_set_add_range (input->name_ids, 0, 6);
  input->name_languages = hb_set_create ();
  hb_set_add (input->name_languages, 0x0409);
  input->drop_tables = hb_set_create ();
  input->drop_hints = false;
  input->desubroutinize = false;
  input->retain_gids = false;
  input->name_legacy = false;

  hb_tag_t default_drop_tables[] = {
    // Layout disabled by default
    HB_TAG ('G', 'S', 'U', 'B'),
    HB_TAG ('G', 'P', 'O', 'S'),
    HB_TAG ('G', 'D', 'E', 'F'),
    HB_TAG ('m', 'o', 'r', 'x'),
    HB_TAG ('m', 'o', 'r', 't'),
    HB_TAG ('k', 'e', 'r', 'x'),
    HB_TAG ('k', 'e', 'r', 'n'),

    // Copied from fontTools:
    HB_TAG ('B', 'A', 'S', 'E'),
    HB_TAG ('J', 'S', 'T', 'F'),
    HB_TAG ('D', 'S', 'I', 'G'),
    HB_TAG ('E', 'B', 'D', 'T'),
    HB_TAG ('E', 'B', 'L', 'C'),
    HB_TAG ('E', 'B', 'S', 'C'),
    HB_TAG ('S', 'V', 'G', ' '),
    HB_TAG ('P', 'C', 'L', 'T'),
    HB_TAG ('L', 'T', 'S', 'H'),
    // Graphite tables
    HB_TAG ('F', 'e', 'a', 't'),
    HB_TAG ('G', 'l', 'a', 't'),
    HB_TAG ('G', 'l', 'o', 'c'),
    HB_TAG ('S', 'i', 'l', 'f'),
    HB_TAG ('S', 'i', 'l', 'l'),
  };

  input->drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables));

  return input;
}

/**
 * hb_subset_input_reference: (skip)
 * @subset_input: a subset_input.
 *
 *
 *
 * Return value:
 *
 * Since: 1.8.0
 **/
hb_subset_input_t *
hb_subset_input_reference (hb_subset_input_t *subset_input)
{
  return hb_object_reference (subset_input);
}

/**
 * hb_subset_input_destroy:
 * @subset_input: a subset_input.
 *
 * Since: 1.8.0
 **/
void
hb_subset_input_destroy (hb_subset_input_t *subset_input)
{
  if (!hb_object_destroy (subset_input)) return;

  hb_set_destroy (subset_input->unicodes);
  hb_set_destroy (subset_input->glyphs);
  hb_set_destroy (subset_input->name_ids);
  hb_set_destroy (subset_input->name_languages);
  hb_set_destroy (subset_input->drop_tables);

  free (subset_input);
}

/**
 * hb_subset_input_unicode_set:
 * @subset_input: a subset_input.
 *
 * Since: 1.8.0
 **/
HB_EXTERN hb_set_t *
hb_subset_input_unicode_set (hb_subset_input_t *subset_input)
{
  return subset_input->unicodes;
}

/**
 * hb_subset_input_glyph_set:
 * @subset_input: a subset_input.
 *
 * Since: 1.8.0
 **/
HB_EXTERN hb_set_t *
hb_subset_input_glyph_set (hb_subset_input_t *subset_input)
{
  return subset_input->glyphs;
}

HB_EXTERN hb_set_t *
hb_subset_input_nameid_set (hb_subset_input_t *subset_input)
{
  return subset_input->name_ids;
}

HB_EXTERN hb_set_t *
hb_subset_input_namelangid_set (hb_subset_input_t *subset_input)
{
  return subset_input->name_languages;
}

HB_EXTERN hb_set_t *
hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input)
{
  return subset_input->drop_tables;
}

HB_EXTERN void
hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
				hb_bool_t drop_hints)
{
  subset_input->drop_hints = drop_hints;
}

HB_EXTERN hb_bool_t
hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input)
{
  return subset_input->drop_hints;
}

HB_EXTERN void
hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
				    hb_bool_t desubroutinize)
{
  subset_input->desubroutinize = desubroutinize;
}

HB_EXTERN hb_bool_t
hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input)
{
  return subset_input->desubroutinize;
}

/**
 * hb_subset_input_set_retain_gids:
 * @subset_input: a subset_input.
 * @retain_gids: If true the subsetter will not renumber glyph ids.
 * Since: 2.4.0
 **/
HB_EXTERN void
hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input,
				 hb_bool_t retain_gids)
{
  subset_input->retain_gids = retain_gids;
}

/**
 * hb_subset_input_get_retain_gids:
 * Returns: value of retain_gids.
 * Since: 2.4.0
 **/
HB_EXTERN hb_bool_t
hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input)
{
  return subset_input->retain_gids;
}

HB_EXTERN void
hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input,
				 hb_bool_t name_legacy)
{
  subset_input->name_legacy = name_legacy;
}

HB_EXTERN hb_bool_t
hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input)
{
  return subset_input->name_legacy;
}


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
 */

#include "hb-ot-face.hh"

#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-kern-table.hh"
#include "hb-ot-meta-table.hh"
#include "hb-ot-name-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-color-cbdt-table.hh"
#include "hb-ot-color-sbix-table.hh"
#include "hb-ot-color-svg-table.hh"
#include "hb-ot-layout-gdef-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"

void hb_ot_face_t::init0 (hb_face_t *face)
{
  this->face = face;
#define HB_OT_TABLE(Namespace, Type) Type.init0 ();
#include "hb-ot-face-table-list.hh"
#undef HB_OT_TABLE
}
void hb_ot_face_t::fini ()
{
#define HB_OT_TABLE(Namespace, Type) Type.fini ();
#include "hb-ot-face-table-list.hh"
#undef HB_OT_TABLE
}


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

#include "hb.hh"

#ifndef HB_NO_COLOR

#include "hb-ot.h"

#include "hb-ot-color-cbdt-table.hh"
#include "hb-ot-color-colr-table.hh"
#include "hb-ot-color-cpal-table.hh"
#include "hb-ot-color-sbix-table.hh"
#include "hb-ot-color-svg-table.hh"

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

/**
 * SECTION:hb-ot-color
 * @title: hb-ot-color
 * @short_description: OpenType Color Fonts
 * @include: hb-ot.h
 *
 * Functions for fetching color-font information from OpenType font faces.
 *
 * HarfBuzz supports `COLR`/`CPAL`, `sbix`, `CBDT`, and `SVG` color fonts.
 **/

/*
 * CPAL
 */

/**
 * hb_ot_color_has_palettes:
 * @face: #hb_face_t to work upon
 *
 * Tests whether a face includes a `CPAL` color-palette table.
 *
 * Return value: true if data found, false otherwise
 *
 * Since: 2.1.0
 */
hb_bool_t
hb_ot_color_has_palettes (hb_face_t *face)
{
  return face->table.CPAL->has_data ();
}

/**
 * hb_ot_color_palette_get_count:
 * @face: #hb_face_t to work upon
 *
 * Fetches the number of color palettes in a face.
 *
 * Return value: the number of palettes found
 *
 * Since: 2.1.0
 */
unsigned int
hb_ot_color_palette_get_count (hb_face_t *face)
{
  return face->table.CPAL->get_palette_count ();
}

/**
 * hb_ot_color_palette_get_name_id:
 * @face: #hb_face_t to work upon
 * @palette_index: The index of the color palette
 *
 * Fetches the `name` table Name ID that provides display names for
 * a `CPAL` color palette.
 *
 * Palette display names can be generic (e.g., "Default") or provide
 * specific, themed names (e.g., "Spring", "Summer", "Fall", and "Winter").
 *
 * Return value: the Named ID found for the palette.
 * If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID.
 *
 * Since: 2.1.0
 */
hb_ot_name_id_t
hb_ot_color_palette_get_name_id (hb_face_t *face,
				 unsigned int palette_index)
{
  return face->table.CPAL->get_palette_name_id (palette_index);
}

/**
 * hb_ot_color_palette_color_get_name_id:
 * @face: #hb_face_t to work upon
 * @color_index: The index of the color
 *
 * Fetches the `name` table Name ID that provides display names for
 * the specificed color in a face's `CPAL` color palette.
 *
 * Display names can be generic (e.g., "Background") or specific
 * (e.g., "Eye color").
 *
 * Return value: the Name ID found for the color.
 *
 * Since: 2.1.0
 */
hb_ot_name_id_t
hb_ot_color_palette_color_get_name_id (hb_face_t *face,
				       unsigned int color_index)
{
  return face->table.CPAL->get_color_name_id (color_index);
}

/**
 * hb_ot_color_palette_get_flags:
 * @face: #hb_face_t to work upon
 * @palette_index: The index of the color palette
 *
 * Fetches the flags defined for a color palette.
 *
 * Return value: the #hb_ot_color_palette_flags_t of the requested color palette
 *
 * Since: 2.1.0
 */
hb_ot_color_palette_flags_t
hb_ot_color_palette_get_flags (hb_face_t *face,
			       unsigned int palette_index)
{
  return face->table.CPAL->get_palette_flags (palette_index);
}

/**
 * hb_ot_color_palette_get_colors:
 * @face: #hb_face_t to work upon
 * @palette_index: the index of the color palette to query
 * @start_offset: offset of the first color to retrieve
 * @color_count: (inout) (optional): Input = the maximum number of colors to return;
 *               Output = the actual number of colors returned (may be zero)
 * @colors: (out) (array length=color_count) (nullable): The array of #hb_color_t records found
 *
 * Fetches a list of the colors in a color palette.
 *
 * After calling this function, @colors will be filled with the palette
 * colors. If @colors is NULL, the function will just return the number
 * of total colors without storing any actual colors; this can be used
 * for allocating a buffer of suitable size before calling
 * hb_ot_color_palette_get_colors() a second time.
 *
 * Return value: the total number of colors in the palette
 *
 * Since: 2.1.0
 */
unsigned int
hb_ot_color_palette_get_colors (hb_face_t     *face,
				unsigned int   palette_index,
				unsigned int   start_offset,
				unsigned int  *colors_count  /* IN/OUT.  May be NULL. */,
				hb_color_t    *colors        /* OUT.     May be NULL. */)
{
  return face->table.CPAL->get_palette_colors (palette_index, start_offset, colors_count, colors);
}

/*
 * COLR
 */

/**
 * hb_ot_color_has_layers:
 * @face: #hb_face_t to work upon
 *
 * Tests whether a face includes any `COLR` color layers.
 *
 * Return value: true if data found, false otherwise
 *
 * Since: 2.1.0
 */
hb_bool_t
hb_ot_color_has_layers (hb_face_t *face)
{
  return face->table.COLR->has_data ();
}

/**
 * hb_ot_color_glyph_get_layers:
 * @face: #hb_face_t to work upon
 * @glyph: The glyph index to query
 * @start_offset: offset of the first layer to retrieve
 * @layer_count: (inout) (optional): Input = the maximum number of layers to return;
 *         Output = the actual number of layers returned (may be zero)
 * @layers: (out) (array length=layer_count) (nullable): The array of layers found
 *
 * Fetches a list of all color layers for the specified glyph index in the specified
 * face. The list returned will begin at the offset provided.
 *
 * Return value: Total number of layers available for the glyph index queried
 *
 * Since: 2.1.0
 */
unsigned int
hb_ot_color_glyph_get_layers (hb_face_t           *face,
			      hb_codepoint_t       glyph,
			      unsigned int         start_offset,
			      unsigned int        *layer_count, /* IN/OUT.  May be NULL. */
			      hb_ot_color_layer_t *layers /* OUT.     May be NULL. */)
{
  return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers);
}

/*
 * SVG
 */

/**
 * hb_ot_color_has_svg:
 * @face: #hb_face_t to work upon.
 *
 * Tests whether a face includes any `SVG` glyph images.
 *
 * Return value: true if data found, false otherwise.
 *
 * Since: 2.1.0
 */
hb_bool_t
hb_ot_color_has_svg (hb_face_t *face)
{
  return face->table.SVG->has_data ();
}

/**
 * hb_ot_color_glyph_reference_svg:
 * @face: #hb_face_t to work upon
 * @glyph: a svg glyph index
 *
 * Fetches the SVG document for a glyph. The blob may be either plain text or gzip-encoded.
 *
 * Return value: (transfer full): An #hb_blob_t containing the SVG document of the glyph, if available
 *
 * Since: 2.1.0
 */
hb_blob_t *
hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph)
{
  return face->table.SVG->reference_blob_for_glyph (glyph);
}

/*
 * PNG: CBDT or sbix
 */

/**
 * hb_ot_color_has_png:
 * @face: #hb_face_t to work upon
 *
 * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables).
 *
 * Return value: true if data found, false otherwise
 *
 * Since: 2.1.0
 */
hb_bool_t
hb_ot_color_has_png (hb_face_t *face)
{
  return face->table.CBDT->has_data () || face->table.sbix->has_data ();
}

/**
 * hb_ot_color_glyph_reference_png:
 * @font: #hb_font_t to work upon
 * @glyph: a glyph index
 *
 * Fetches the PNG image for a glyph. This function takes a font object, not a face object,
 * as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font
 * object. If UPEM is unset, the blob returned will be the largest PNG available.
 *
 * Return value: (transfer full): An #hb_blob_t containing the PNG image for the glyph, if available
 *
 * Since: 2.1.0
 */
hb_blob_t *
hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t  glyph)
{
  hb_blob_t *blob = hb_blob_get_empty ();

  if (font->face->table.sbix->has_data ())
    blob = font->face->table.sbix->reference_png (font, glyph, nullptr, nullptr, nullptr);

  if (!blob->length && font->face->table.CBDT->has_data ())
    blob = font->face->table.CBDT->reference_png (font, glyph);

  return blob;
}

#endif