Merge branch 'master' into cff-subset
authorMichiharu Ariza <ariza@adobe.com>
Wed, 14 Nov 2018 21:54:07 +0000 (13:54 -0800)
committerMichiharu Ariza <ariza@adobe.com>
Wed, 14 Nov 2018 21:54:07 +0000 (13:54 -0800)
1  2 
src/Makefile.sources
src/hb-ot-cff-common.hh
src/hb-ot-face.hh
src/hb-ot-font.cc
src/hb-ot-layout-common.hh

Simple merge
index f9f7fde,0000000..db4b46d
mode 100644,000000..100644
--- /dev/null
@@@ -1,703 -1,0 +1,703 @@@
-                            c->check_array (data_base (), 1, max_offset () - 1))));
 +/*
 + * 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_CFF_COMMON_HH
 +#define HB_OT_CFF_COMMON_HH
 +
 +#include "hb-open-type.hh"
 +#include "hb-ot-layout-common.hh"
 +#include "hb-cff-interp-dict-common.hh"
 +#include "hb-subset-plan.hh"
 +
 +namespace CFF {
 +
 +using namespace OT;
 +
 +#define CFF_UNDEF_CODE  0xFFFFFFFF
 +
 +/* utility macro */
 +template<typename Type>
 +static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset)
 +{ return offset? (* reinterpret_cast<const Type*> ((const char *) P + offset)): Null(Type); }
 +
 +inline unsigned int calcOffSize(unsigned int dataSize)
 +{
 +  unsigned int size = 1;
 +  unsigned int offset = dataSize + 1;
 +  while ((offset & ~0xFF) != 0)
 +  {
 +    size++;
 +    offset >>= 8;
 +  }
 +  assert (size <= 4);
 +  return size;
 +}
 +
 +struct code_pair
 +{
 +  hb_codepoint_t  code;
 +  hb_codepoint_t  glyph;
 +};
 +
 +typedef hb_vector_t<char, 1> StrBuff;
 +struct StrBuffArray : hb_vector_t<StrBuff>
 +{
 +  inline void fini (void)
 +  {
 +    SUPER::fini_deep ();
 +  }
 +
 +  inline unsigned int total_size (void) const
 +  {
 +    unsigned int size = 0;
 +    for (unsigned int i = 0; i < len; i++)
 +      size += (*this)[i].len;
 +    return size;
 +  }
 +
 +  private:
 +  typedef hb_vector_t<StrBuff> SUPER;
 +};
 +
 +/* CFF INDEX */
 +template <typename COUNT>
 +struct CFFIndex
 +{
 +  inline bool sanitize (hb_sanitize_context_t *c) const
 +  {
 +    TRACE_SANITIZE (this);
 +    return_trace (likely ((count.sanitize (c) && count == 0) || /* empty INDEX */
 +                          (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
 +                           c->check_array (offsets, offSize, count + 1) &&
++                           c->check_array ((const HBUINT8*)data_base (), 1, max_offset () - 1))));
 +  }
 +
 +  inline static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
 +  { return offSize * (count + 1); }
 +
 +  inline unsigned int offset_array_size (void) const
 +  { return calculate_offset_array_size (offSize, count); }
 +
 +  inline static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize)
 +  {
 +    if (count == 0)
 +      return COUNT::static_size;
 +    else
 +      return min_size + calculate_offset_array_size (offSize, count) + dataSize;
 +  }
 +
 +  inline bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
 +  {
 +    TRACE_SERIALIZE (this);
 +    unsigned int size = src.get_size ();
 +    CFFIndex *dest = c->allocate_size<CFFIndex> (size);
 +    if (unlikely (dest == nullptr)) return_trace (false);
 +    memcpy (dest, &src, size);
 +    return_trace (true);
 +  }
 +
 +  inline bool serialize (hb_serialize_context_t *c,
 +                         unsigned int offSize_,
 +                         const ByteStrArray &byteArray)
 +  {
 +    TRACE_SERIALIZE (this);
 +    if (byteArray.len == 0)
 +    {
 +      COUNT *dest = c->allocate_min<COUNT> ();
 +      if (unlikely (dest == nullptr)) return_trace (false);
 +      dest->set (0);
 +    }
 +    else
 +    {
 +      /* serialize CFFIndex header */
 +      if (unlikely (!c->extend_min (*this))) return_trace (false);
 +      this->count.set (byteArray.len);
 +      this->offSize.set (offSize_);
 +      if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.len + 1))))
 +        return_trace (false);
 +    
 +      /* serialize indices */
 +      unsigned int  offset = 1;
 +      unsigned int  i = 0;
 +      for (; i < byteArray.len; i++)
 +      {
 +        set_offset_at (i, offset);
 +        offset += byteArray[i].get_size ();
 +      }
 +      set_offset_at (i, offset);
 +
 +      /* serialize data */
 +      for (unsigned int i = 0; i < byteArray.len; i++)
 +      {
 +        ByteStr  *dest = c->start_embed<ByteStr> ();
 +        if (unlikely (dest == nullptr ||
 +                      !dest->serialize (c, byteArray[i])))
 +          return_trace (false);
 +      }
 +    }
 +    return_trace (true);
 +  }
 +
 +  inline bool serialize (hb_serialize_context_t *c,
 +                         unsigned int offSize_,
 +                         const StrBuffArray &buffArray)
 +  {
 +    ByteStrArray  byteArray;
 +    byteArray.init ();
 +    byteArray.resize (buffArray.len);
 +    for (unsigned int i = 0; i < byteArray.len; i++)
 +    {
 +      byteArray[i] = ByteStr (buffArray[i].arrayZ (), buffArray[i].len);
 +    }
 +    bool result = this->serialize (c, offSize_, byteArray);
 +    byteArray.fini ();
 +    return result;
 +  }
 +
 +  inline void set_offset_at (unsigned int index, unsigned int offset)
 +  {
 +    HBUINT8 *p = offsets + offSize * index + offSize;
 +    unsigned int size = offSize;
 +    for (; size; size--)
 +    {
 +      --p;
 +      p->set (offset & 0xFF);
 +      offset >>= 8;
 +    }
 +  }
 +
 +  inline unsigned int offset_at (unsigned int index) const
 +  {
 +    assert (index <= count);
 +    const HBUINT8 *p = offsets + offSize * index;
 +    unsigned int size = offSize;
 +    unsigned int offset = 0;
 +    for (; size; size--)
 +      offset = (offset << 8) + *p++;
 +    return offset;
 +  }
 +
 +  inline unsigned int length_at (unsigned int index) const
 +  { return offset_at (index + 1) - offset_at (index); }
 +
 +  inline const char *data_base (void) const
 +  { return (const char *)this + min_size + offset_array_size (); }
 +
 +  inline unsigned int data_size (void) const
 +  { return HBINT8::static_size; }
 +
 +  ByteStr operator [] (unsigned int index) const
 +  {
 +    if (likely (index < count))
 +      return ByteStr (data_base () + offset_at (index) - 1, offset_at (index + 1) - offset_at (index));
 +    else
 +      return Null(ByteStr);
 +  }
 +
 +  inline unsigned int get_size (void) const
 +  {
 +    if (this != &Null(CFFIndex))
 +    {
 +      if (count > 0)
 +        return min_size + offset_array_size () + (offset_at (count) - 1);
 +      else
 +        return count.static_size;  /* empty CFFIndex contains count only */
 +    }
 +    else
 +      return 0;
 +  }
 +
 +  protected:
 +  inline unsigned int max_offset (void) const
 +  {
 +    unsigned int max = 0;
 +    for (unsigned int i = 0; i <= count; i++)
 +    {
 +      unsigned int off = offset_at (i);
 +      if (off > max) max = off;
 +    }
 +    return max;
 +  }
 +
 +  public:
 +  COUNT     count;        /* Number of object data. Note there are (count+1) offsets */
 +  HBUINT8   offSize;      /* The byte size of each offset in the offsets array. */
 +  HBUINT8   offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */
 +  /* HBUINT8 data[VAR];      Object data */
 +  public:
 +  DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
 +};
 +
 +template <typename COUNT, typename TYPE>
 +struct CFFIndexOf : CFFIndex<COUNT>
 +{
 +  inline const ByteStr operator [] (unsigned int index) const
 +  {
 +    if (likely (index < CFFIndex<COUNT>::count))
 +      return ByteStr (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
 +    return Null(ByteStr);
 +  }
 +
 +  template <typename DATA, typename PARAM1, typename PARAM2>
 +  inline bool serialize (hb_serialize_context_t *c,
 +                         unsigned int offSize_,
 +                         const DATA *dataArray,
 +                         unsigned int dataArrayLen,
 +                         const hb_vector_t<unsigned int> &dataSizeArray,
 +                         const PARAM1 &param1,
 +                         const PARAM2 &param2)
 +  {
 +    TRACE_SERIALIZE (this);
 +    /* serialize CFFIndex header */
 +    if (unlikely (!c->extend_min (*this))) return_trace (false);
 +    this->count.set (dataArrayLen);
 +    this->offSize.set (offSize_);
 +    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1))))
 +      return_trace (false);
 +  
 +    /* serialize indices */
 +    unsigned int  offset = 1;
 +    unsigned int  i = 0;
 +    for (; i < dataArrayLen; i++)
 +    {
 +      CFFIndex<COUNT>::set_offset_at (i, offset);
 +      offset += dataSizeArray[i];
 +    }
 +    CFFIndex<COUNT>::set_offset_at (i, offset);
 +
 +    /* serialize data */
 +    for (unsigned int i = 0; i < dataArrayLen; i++)
 +    {
 +      TYPE  *dest = c->start_embed<TYPE> ();
 +      if (unlikely (dest == nullptr ||
 +                    !dest->serialize (c, dataArray[i], param1, param2)))
 +        return_trace (false);
 +    }
 +    return_trace (true);
 +  }
 +
 +  /* in parallel to above */
 +  template <typename DATA, typename PARAM>
 +  inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
 +                                                        const DATA *dataArray,
 +                                                        unsigned int dataArrayLen,
 +                                                        hb_vector_t<unsigned int> &dataSizeArray, /* OUT */
 +                                                        const PARAM &param)
 +  {
 +    /* determine offset size */
 +    unsigned int  totalDataSize = 0;
 +    for (unsigned int i = 0; i < dataArrayLen; i++)
 +    {
 +      unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param);
 +      dataSizeArray[i] = dataSize;
 +      totalDataSize += dataSize;
 +    }
 +    offSize_ = calcOffSize (totalDataSize);
 +
 +    return CFFIndex<COUNT>::calculate_serialized_size (offSize_, dataArrayLen, totalDataSize);
 +  }
 +};
 +
 +/* Top Dict, Font Dict, Private Dict */
 +struct Dict : UnsizedByteStr
 +{
 +  template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
 +  inline bool serialize (hb_serialize_context_t *c,
 +                        const DICTVAL &dictval,
 +                        OP_SERIALIZER& opszr,
 +                        PARAM& param)
 +  {
 +    TRACE_SERIALIZE (this);
 +    for (unsigned int i = 0; i < dictval.get_count (); i++)
 +    {
 +      if (unlikely (!opszr.serialize (c, dictval[i], param)))
 +        return_trace (false);
 +    }
 +    return_trace (true);
 +  }
 +
 +  /* in parallel to above */
 +  template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
 +  inline static unsigned int calculate_serialized_size (const DICTVAL &dictval,
 +                                                        OP_SERIALIZER& opszr,
 +                                                        PARAM& param)
 +  {
 +    unsigned int size = 0;
 +    for (unsigned int i = 0; i < dictval.get_count (); i++)
 +      size += opszr.calculate_serialized_size (dictval[i], param);
 +    return size;
 +  }
 +
 +  template <typename DICTVAL, typename OP_SERIALIZER>
 +  inline static unsigned int calculate_serialized_size (const DICTVAL &dictval,
 +                                                        OP_SERIALIZER& opszr)
 +  {
 +    unsigned int size = 0;
 +    for (unsigned int i = 0; i < dictval.get_count (); i++)
 +      size += opszr.calculate_serialized_size (dictval[i]);
 +    return size;
 +  }
 +
 +  template <typename INTTYPE, int minVal, int maxVal>
 +  inline static bool serialize_int_op (hb_serialize_context_t *c, OpCode op, int value, OpCode intOp)
 +  {
 +    // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
 +    if (/*unlikely*/ (!serialize_int<INTTYPE, minVal, maxVal> (c, intOp, value)))
 +      return false;
 +
 +    TRACE_SERIALIZE (this);
 +    /* serialize the opcode */
 +    HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op));
 +    if (unlikely (p == nullptr)) return_trace (false);
 +    if (Is_OpCode_ESC (op))
 +    {
 +      p->set (OpCode_escape);
 +      op = Unmake_OpCode_ESC (op);
 +      p++;
 +    }
 +    p->set (op);
 +    return_trace (true);
 +  }
 +
 +  inline static bool serialize_uint4_op (hb_serialize_context_t *c, OpCode op, int value)
 +  { return serialize_int_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longintdict); }
 +
 +  inline static bool serialize_uint2_op (hb_serialize_context_t *c, OpCode op, int value)
 +  { return serialize_int_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
 +
 +  inline static bool serialize_offset4_op (hb_serialize_context_t *c, OpCode op, int value)
 +  {
 +    if (value == 0) return true;
 +    return serialize_uint4_op (c, op, value);
 +  }
 +
 +  inline static bool serialize_offset2_op (hb_serialize_context_t *c, OpCode op, int value)
 +  {
 +    if (value == 0) return true;
 +    return serialize_uint2_op (c, op, value);
 +  }
 +};
 +
 +struct TopDict : Dict {};
 +struct FontDict : Dict {};
 +struct PrivateDict : Dict {};
 +
 +struct TableInfo
 +{
 +  void init (void) { offSize = offset = size = 0; }
 +
 +  unsigned int    offset;
 +  unsigned int    size;
 +  unsigned int    offSize;
 +};
 +
 +/* used to remap font index or SID from fullset to subset.
 + * set to CFF_UNDEF_CODE if excluded from subset */
 +struct Remap : hb_vector_t<hb_codepoint_t>
 +{
 +  inline void init (void)
 +  { SUPER::init (); }
 +
 +  inline void fini (void)
 +  { SUPER::fini (); }
 +
 +  inline bool reset (unsigned int size)
 +  {
 +    if (unlikely (!SUPER::resize (size)))
 +      return false;
 +    for (unsigned int i = 0; i < len; i++)
 +      (*this)[i] = CFF_UNDEF_CODE;
 +    count = 0;
 +    return true;
 +  }
 +
 +  inline bool excludes (hb_codepoint_t id) const
 +  { return (id < len) && ((*this)[id] == CFF_UNDEF_CODE); }
 +
 +  inline bool includes (hb_codepoint_t id) const
 +  { return !excludes (id); }
 +
 +  inline unsigned int add (unsigned int i)
 +  {
 +    if ((*this)[i] == CFF_UNDEF_CODE)
 +      (*this)[i] = count++;
 +    return (*this)[i];
 +  }
 +
 +  inline hb_codepoint_t get_count (void) const
 +  { return count; }
 +
 +  protected:
 +  hb_codepoint_t  count;
 +
 +  private:
 +  typedef hb_vector_t<hb_codepoint_t> SUPER;
 +};
 +
 +template <typename COUNT>
 +struct FDArray : CFFIndexOf<COUNT, FontDict>
 +{
 +  /* used by CFF1 */
 +  template <typename DICTVAL, typename OP_SERIALIZER>
 +  inline bool serialize (hb_serialize_context_t *c,
 +                        unsigned int offSize_,
 +                        const hb_vector_t<DICTVAL> &fontDicts,
 +                        OP_SERIALIZER& opszr)
 +  {
 +    TRACE_SERIALIZE (this);
 +    if (unlikely (!c->extend_min (*this))) return_trace (false);
 +    this->count.set (fontDicts.len);
 +    this->offSize.set (offSize_);
 +    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fontDicts.len + 1))))
 +      return_trace (false);
 +    
 +    /* serialize font dict offsets */
 +    unsigned int  offset = 1;
 +    unsigned int fid = 0;
 +    for (; fid < fontDicts.len; fid++)
 +    {
 +      CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
 +      offset += FontDict::calculate_serialized_size (fontDicts[fid], opszr);
 +    }
 +    CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
 +
 +    /* serialize font dicts */
 +    for (unsigned int i = 0; i < fontDicts.len; i++)
 +    {
 +      FontDict *dict = c->start_embed<FontDict> ();
 +      if (unlikely (!dict->serialize (c, fontDicts[i], opszr, fontDicts[i])))
 +        return_trace (false);
 +    }
 +    return_trace (true);
 +  }
 +  
 +  /* used by CFF2 */
 +  template <typename DICTVAL, typename OP_SERIALIZER>
 +  inline bool serialize (hb_serialize_context_t *c,
 +                        unsigned int offSize_,
 +                        const hb_vector_t<DICTVAL> &fontDicts,
 +                        unsigned int fdCount,
 +                        const Remap &fdmap,
 +                        OP_SERIALIZER& opszr,
 +                        const hb_vector_t<TableInfo> &privateInfos)
 +  {
 +    TRACE_SERIALIZE (this);
 +    if (unlikely (!c->extend_min (*this))) return_trace (false);
 +    this->count.set (fdCount);
 +    this->offSize.set (offSize_);
 +    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fdCount + 1))))
 +      return_trace (false);
 +    
 +    /* serialize font dict offsets */
 +    unsigned int  offset = 1;
 +    unsigned int  fid = 0;
 +    for (unsigned i = 0; i < fontDicts.len; i++)
 +      if (fdmap.includes (i))
 +      {
 +        CFFIndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
 +        offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
 +      }
 +    CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
 +
 +    /* serialize font dicts */
 +    for (unsigned int i = 0; i < fontDicts.len; i++)
 +      if (fdmap.includes (i))
 +      {
 +        FontDict *dict = c->start_embed<FontDict> ();
 +        if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]])))
 +          return_trace (false);
 +      }
 +    return_trace (true);
 +  }
 +  
 +  /* in parallel to above */
 +  template <typename OP_SERIALIZER, typename DICTVAL>
 +  inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
 +                                                        const hb_vector_t<DICTVAL> &fontDicts,
 +                                                        unsigned int fdCount,
 +                                                        const Remap &fdmap,
 +                                                        OP_SERIALIZER& opszr)
 +  {
 +    unsigned int dictsSize = 0;
 +    for (unsigned int i = 0; i < fontDicts.len; i++)
 +      if (fdmap.includes (i))
 +        dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
 +
 +    offSize_ = calcOffSize (dictsSize);
 +    return CFFIndex<COUNT>::calculate_serialized_size (offSize_, fdCount, dictsSize);
 +  }
 +};
 +
 +/* FDSelect */
 +struct FDSelect0 {
 +  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
 +  {
 +    TRACE_SANITIZE (this);
 +    if (unlikely (!(c->check_struct (this))))
 +      return_trace (false);
 +    for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
 +      if (unlikely (!fds[i].sanitize (c)))
 +        return_trace (false);
 +
 +    return_trace (true);
 +  }
 +
 +  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
 +  {
 +    return (hb_codepoint_t)fds[glyph];
 +  }
 +
 +  inline unsigned int get_size (unsigned int num_glyphs) const
 +  { return HBUINT8::static_size * num_glyphs; }
 +
 +  HBUINT8     fds[VAR];
 +
 +  DEFINE_SIZE_MIN (1);
 +};
 +
 +template <typename GID_TYPE, typename FD_TYPE>
 +struct FDSelect3_4_Range {
 +  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
 +  {
 +    TRACE_SANITIZE (this);
 +    return_trace (likely (c->check_struct (this) && (first < c->get_num_glyphs ()) && (fd < fdcount)));
 +  }
 +
 +  GID_TYPE    first;
 +  FD_TYPE     fd;
 +
 +  DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
 +};
 +
 +template <typename GID_TYPE, typename FD_TYPE>
 +struct FDSelect3_4 {
 +  inline unsigned int get_size (void) const
 +  { return GID_TYPE::static_size * 2 + FDSelect3_4_Range<GID_TYPE, FD_TYPE>::static_size * nRanges; }
 +
 +  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
 +  {
 +    TRACE_SANITIZE (this);
 +    if (unlikely (!(c->check_struct (this) && (nRanges > 0) && (ranges[0].first == 0))))
 +      return_trace (false);
 +
 +    for (unsigned int i = 0; i < nRanges; i++)
 +    {
 +      if (unlikely (!ranges[i].sanitize (c, fdcount)))
 +        return_trace (false);
 +      if ((0 < i) && unlikely (ranges[i - 1].first >= ranges[i].first))
 +        return_trace (false);
 +    }
 +    if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
 +      return_trace (false);
 +
 +    return_trace (true);
 +  }
 +
 +  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
 +  {
 +    unsigned int i;
 +    for (i = 1; i < nRanges; i++)
 +      if (glyph < ranges[i].first)
 +        break;
 +
 +    return (hb_codepoint_t)ranges[i - 1].fd;
 +  }
 +
 +  inline GID_TYPE &sentinel (void)  { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
 +  inline const GID_TYPE &sentinel (void) const  { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
 +
 +  GID_TYPE         nRanges;
 +  FDSelect3_4_Range<GID_TYPE, FD_TYPE>  ranges[VAR];
 +  /* GID_TYPE sentinel */
 +
 +  DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges);
 +};
 +
 +typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
 +typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;
 +
 +struct FDSelect {
 +  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
 +  {
 +    TRACE_SANITIZE (this);
 +
 +    return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) &&
 +                          (format == 0)?
 +                          u.format0.sanitize (c, fdcount):
 +                          u.format3.sanitize (c, fdcount)));
 +  }
 +
 +  inline bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
 +  {
 +    TRACE_SERIALIZE (this);
 +    unsigned int size = src.get_size (num_glyphs);
 +    FDSelect *dest = c->allocate_size<FDSelect> (size);
 +    if (unlikely (dest == nullptr)) return_trace (false);
 +    memcpy (dest, &src, size);
 +    return_trace (true);
 +  }
 +
 +  inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
 +  { return get_size (num_glyphs); }
 +
 +  inline unsigned int get_size (unsigned int num_glyphs) const
 +  {
 +    unsigned int size = format.static_size;
 +    if (format == 0)
 +      size += u.format0.get_size (num_glyphs);
 +    else
 +      size += u.format3.get_size ();
 +    return size;
 +  }
 +
 +  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
 +  {
 +    if (this == &Null(FDSelect))
 +      return 0;
 +    if (format == 0)
 +      return u.format0.get_fd (glyph);
 +    else
 +      return u.format3.get_fd (glyph);
 +  }
 +
 +  HBUINT8       format;
 +  union {
 +    FDSelect0   format0;
 +    FDSelect3   format3;
 +  } u;
 +
 +  DEFINE_SIZE_MIN (1);
 +};
 +
 +template <typename COUNT>
 +struct Subrs : CFFIndex<COUNT>
 +{
 +  typedef COUNT count_type;
 +  typedef CFFIndex<COUNT> SUPER;
 +};
 +
 +} /* namespace CFF */
 +
 +#endif /* HB_OT_CFF_COMMON_HH */
      HB_OT_ACCELERATOR(OT, post) \
      HB_OT_TABLE(OT, kern) \
      HB_OT_ACCELERATOR(OT, glyf) \
 +    HB_OT_ACCELERATOR(OT, cff1) \
 +    HB_OT_ACCELERATOR(OT, cff2) \
      HB_OT_TABLE(OT, VORG) \
      HB_OT_ACCELERATOR(OT, name) \
+     HB_OT_TABLE(OT, OS2) \
+     HB_OT_TABLE(OT, STAT) \
      /* OpenType shaping. */ \
      HB_OT_ACCELERATOR(OT, GDEF) \
      HB_OT_ACCELERATOR(OT, GSUB) \
  #include "hb-ot-face.hh"
  
  #include "hb-ot-cmap-table.hh"
- #include "hb-ot-hmtx-table.hh"
- #include "hb-ot-kern-table.hh"
- #include "hb-ot-post-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-os2-table.hh"
+ #include "hb-ot-post-table.hh"
+ #include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
  #include "hb-ot-vorg-table.hh"
  #include "hb-ot-color-cbdt-table.hh"
  #include "hb-ot-color-sbix-table.hh"
Simple merge