1 /*
   2  * Copyright © 2018 Adobe Inc.
   3  *
   4  *  This is part of HarfBuzz, a text shaping library.
   5  *
   6  * Permission is hereby granted, without written agreement and without
   7  * license or royalty fees, to use, copy, modify, and distribute this
   8  * software and its documentation for any purpose, provided that the
   9  * above copyright notice and the following two paragraphs appear in
  10  * all copies of this software.
  11  *
  12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
  13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
  14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
  15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
  16  * DAMAGE.
  17  *
  18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  23  *
  24  * Adobe Author(s): Michiharu Ariza
  25  */
  26 #ifndef HB_OT_CFF_COMMON_HH
  27 #define HB_OT_CFF_COMMON_HH
  28 
  29 #include "hb-open-type.hh"
  30 #include "hb-ot-layout-common.hh"
  31 #include "hb-cff-interp-dict-common.hh"
  32 #include "hb-subset-plan.hh"
  33 
  34 namespace CFF {
  35 
  36 using namespace OT;
  37 
  38 #define CFF_UNDEF_CODE  0xFFFFFFFF
  39 
  40 /* utility macro */
  41 template<typename Type>
  42 static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset)
  43 { return offset? (* reinterpret_cast<const Type*> ((const char *) P + offset)): Null(Type); }
  44 
  45 inline unsigned int calcOffSize(unsigned int dataSize)
  46 {
  47   unsigned int size = 1;
  48   unsigned int offset = dataSize + 1;
  49   while ((offset & ~0xFF) != 0)
  50   {
  51     size++;
  52     offset >>= 8;
  53   }
  54   /* format does not support size > 4; caller should handle it as an error */
  55   return size;
  56 }
  57 
  58 struct code_pair_t
  59 {
  60   hb_codepoint_t  code;
  61   hb_codepoint_t  glyph;
  62 };
  63 
  64 typedef hb_vector_t<unsigned char> str_buff_t;
  65 struct str_buff_vec_t : hb_vector_t<str_buff_t>
  66 {
  67   void fini () { SUPER::fini_deep (); }
  68 
  69   unsigned int total_size () const
  70   {
  71     unsigned int size = 0;
  72     for (unsigned int i = 0; i < length; i++)
  73       size += (*this)[i].length;
  74     return size;
  75   }
  76 
  77   private:
  78   typedef hb_vector_t<str_buff_t> SUPER;
  79 };
  80 
  81 /* CFF INDEX */
  82 template <typename COUNT>
  83 struct CFFIndex
  84 {
  85   bool sanitize (hb_sanitize_context_t *c) const
  86   {
  87     TRACE_SANITIZE (this);
  88     return_trace (likely ((count.sanitize (c) && count == 0) || /* empty INDEX */
  89                           (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
  90                            c->check_array (offsets, offSize, count + 1) &&
  91                            c->check_array ((const HBUINT8*)data_base (), 1, max_offset () - 1))));
  92   }
  93 
  94   static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
  95   { return offSize * (count + 1); }
  96 
  97   unsigned int offset_array_size () const
  98   { return calculate_offset_array_size (offSize, count); }
  99 
 100   static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize)
 101   {
 102     if (count == 0)
 103       return COUNT::static_size;
 104     else
 105       return min_size + calculate_offset_array_size (offSize, count) + dataSize;
 106   }
 107 
 108   bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
 109   {
 110     TRACE_SERIALIZE (this);
 111     unsigned int size = src.get_size ();
 112     CFFIndex *dest = c->allocate_size<CFFIndex> (size);
 113     if (unlikely (dest == nullptr)) return_trace (false);
 114     memcpy (dest, &src, size);
 115     return_trace (true);
 116   }
 117 
 118   bool serialize (hb_serialize_context_t *c,
 119                   unsigned int offSize_,
 120                   const byte_str_array_t &byteArray)
 121   {
 122     TRACE_SERIALIZE (this);
 123     if (byteArray.length == 0)
 124     {
 125       COUNT *dest = c->allocate_min<COUNT> ();
 126       if (unlikely (dest == nullptr)) return_trace (false);
 127       dest->set (0);
 128     }
 129     else
 130     {
 131       /* serialize CFFIndex header */
 132       if (unlikely (!c->extend_min (*this))) return_trace (false);
 133       this->count.set (byteArray.length);
 134       this->offSize.set (offSize_);
 135       if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1))))
 136         return_trace (false);
 137 
 138       /* serialize indices */
 139       unsigned int  offset = 1;
 140       unsigned int  i = 0;
 141       for (; i < byteArray.length; i++)
 142       {
 143         set_offset_at (i, offset);
 144         offset += byteArray[i].get_size ();
 145       }
 146       set_offset_at (i, offset);
 147 
 148       /* serialize data */
 149       for (unsigned int i = 0; i < byteArray.length; i++)
 150       {
 151         const byte_str_t &bs = byteArray[i];
 152         unsigned char  *dest = c->allocate_size<unsigned char> (bs.length);
 153         if (unlikely (dest == nullptr))
 154           return_trace (false);
 155         memcpy (dest, &bs[0], bs.length);
 156       }
 157     }
 158     return_trace (true);
 159   }
 160 
 161   bool serialize (hb_serialize_context_t *c,
 162                   unsigned int offSize_,
 163                   const str_buff_vec_t &buffArray)
 164   {
 165     byte_str_array_t  byteArray;
 166     byteArray.init ();
 167     byteArray.resize (buffArray.length);
 168     for (unsigned int i = 0; i < byteArray.length; i++)
 169     {
 170       byteArray[i] = byte_str_t (buffArray[i].arrayZ (), buffArray[i].length);
 171     }
 172     bool result = this->serialize (c, offSize_, byteArray);
 173     byteArray.fini ();
 174     return result;
 175   }
 176 
 177   void set_offset_at (unsigned int index, unsigned int offset)
 178   {
 179     HBUINT8 *p = offsets + offSize * index + offSize;
 180     unsigned int size = offSize;
 181     for (; size; size--)
 182     {
 183       --p;
 184       p->set (offset & 0xFF);
 185       offset >>= 8;
 186     }
 187   }
 188 
 189   unsigned int offset_at (unsigned int index) const
 190   {
 191     assert (index <= count);
 192     const HBUINT8 *p = offsets + offSize * index;
 193     unsigned int size = offSize;
 194     unsigned int offset = 0;
 195     for (; size; size--)
 196       offset = (offset << 8) + *p++;
 197     return offset;
 198   }
 199 
 200   unsigned int length_at (unsigned int index) const
 201   {
 202         if (likely ((offset_at (index + 1) >= offset_at (index)) &&
 203                     (offset_at (index + 1) <= offset_at (count))))
 204           return offset_at (index + 1) - offset_at (index);
 205         else
 206           return 0;
 207   }
 208 
 209   const unsigned char *data_base () const
 210   { return (const unsigned char *)this + min_size + offset_array_size (); }
 211 
 212   unsigned int data_size () const { return HBINT8::static_size; }
 213 
 214   byte_str_t operator [] (unsigned int index) const
 215   {
 216     if (likely (index < count))
 217       return byte_str_t (data_base () + offset_at (index) - 1, length_at (index));
 218     else
 219       return Null(byte_str_t);
 220   }
 221 
 222   unsigned int get_size () const
 223   {
 224     if (this != &Null(CFFIndex))
 225     {
 226       if (count > 0)
 227         return min_size + offset_array_size () + (offset_at (count) - 1);
 228       else
 229         return count.static_size;  /* empty CFFIndex contains count only */
 230     }
 231     else
 232       return 0;
 233   }
 234 
 235   protected:
 236   unsigned int max_offset () const
 237   {
 238     unsigned int max = 0;
 239     for (unsigned int i = 0; i < count + 1u; i++)
 240     {
 241       unsigned int off = offset_at (i);
 242       if (off > max) max = off;
 243     }
 244     return max;
 245   }
 246 
 247   public:
 248   COUNT     count;      /* Number of object data. Note there are (count+1) offsets */
 249   HBUINT8   offSize;      /* The byte size of each offset in the offsets array. */
 250   HBUINT8   offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */
 251   /* HBUINT8 data[VAR];      Object data */
 252   public:
 253   DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
 254 };
 255 
 256 template <typename COUNT, typename TYPE>
 257 struct CFFIndexOf : CFFIndex<COUNT>
 258 {
 259   const byte_str_t operator [] (unsigned int index) const
 260   {
 261     if (likely (index < CFFIndex<COUNT>::count))
 262       return byte_str_t (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
 263     return Null(byte_str_t);
 264   }
 265 
 266   template <typename DATA, typename PARAM1, typename PARAM2>
 267   bool serialize (hb_serialize_context_t *c,
 268                   unsigned int offSize_,
 269                   const DATA *dataArray,
 270                   unsigned int dataArrayLen,
 271                   const hb_vector_t<unsigned int> &dataSizeArray,
 272                   const PARAM1 &param1,
 273                   const PARAM2 &param2)
 274   {
 275     TRACE_SERIALIZE (this);
 276     /* serialize CFFIndex header */
 277     if (unlikely (!c->extend_min (*this))) return_trace (false);
 278     this->count.set (dataArrayLen);
 279     this->offSize.set (offSize_);
 280     if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1))))
 281       return_trace (false);
 282 
 283     /* serialize indices */
 284     unsigned int  offset = 1;
 285     unsigned int  i = 0;
 286     for (; i < dataArrayLen; i++)
 287     {
 288       CFFIndex<COUNT>::set_offset_at (i, offset);
 289       offset += dataSizeArray[i];
 290     }
 291     CFFIndex<COUNT>::set_offset_at (i, offset);
 292 
 293     /* serialize data */
 294     for (unsigned int i = 0; i < dataArrayLen; i++)
 295     {
 296       TYPE  *dest = c->start_embed<TYPE> ();
 297       if (unlikely (dest == nullptr ||
 298                     !dest->serialize (c, dataArray[i], param1, param2)))
 299         return_trace (false);
 300     }
 301     return_trace (true);
 302   }
 303 
 304   /* in parallel to above */
 305   template <typename DATA, typename PARAM>
 306   static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
 307                                                  const DATA *dataArray,
 308                                                  unsigned int dataArrayLen,
 309                                                  hb_vector_t<unsigned int> &dataSizeArray, /* OUT */
 310                                                  const PARAM &param)
 311   {
 312     /* determine offset size */
 313     unsigned int  totalDataSize = 0;
 314     for (unsigned int i = 0; i < dataArrayLen; i++)
 315     {
 316       unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param);
 317       dataSizeArray[i] = dataSize;
 318       totalDataSize += dataSize;
 319     }
 320     offSize_ = calcOffSize (totalDataSize);
 321 
 322     return CFFIndex<COUNT>::calculate_serialized_size (offSize_, dataArrayLen, totalDataSize);
 323   }
 324 };
 325 
 326 /* Top Dict, Font Dict, Private Dict */
 327 struct Dict : UnsizedByteStr
 328 {
 329   template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
 330   bool serialize (hb_serialize_context_t *c,
 331                   const DICTVAL &dictval,
 332                   OP_SERIALIZER& opszr,
 333                   PARAM& param)
 334   {
 335     TRACE_SERIALIZE (this);
 336     for (unsigned int i = 0; i < dictval.get_count (); i++)
 337     {
 338       if (unlikely (!opszr.serialize (c, dictval[i], param)))
 339         return_trace (false);
 340     }
 341     return_trace (true);
 342   }
 343 
 344   /* in parallel to above */
 345   template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
 346   static unsigned int calculate_serialized_size (const DICTVAL &dictval,
 347                                                  OP_SERIALIZER& opszr,
 348                                                  PARAM& param)
 349   {
 350     unsigned int size = 0;
 351     for (unsigned int i = 0; i < dictval.get_count (); i++)
 352       size += opszr.calculate_serialized_size (dictval[i], param);
 353     return size;
 354   }
 355 
 356   template <typename DICTVAL, typename OP_SERIALIZER>
 357   static unsigned int calculate_serialized_size (const DICTVAL &dictval,
 358                                                  OP_SERIALIZER& opszr)
 359   {
 360     unsigned int size = 0;
 361     for (unsigned int i = 0; i < dictval.get_count (); i++)
 362       size += opszr.calculate_serialized_size (dictval[i]);
 363     return size;
 364   }
 365 
 366   template <typename INTTYPE, int minVal, int maxVal>
 367   static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, int value, op_code_t intOp)
 368   {
 369     // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
 370     if (/*unlikely*/ (!serialize_int<INTTYPE, minVal, maxVal> (c, intOp, value)))
 371       return false;
 372 
 373     TRACE_SERIALIZE (this);
 374     /* serialize the opcode */
 375     HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op));
 376     if (unlikely (p == nullptr)) return_trace (false);
 377     if (Is_OpCode_ESC (op))
 378     {
 379       p->set (OpCode_escape);
 380       op = Unmake_OpCode_ESC (op);
 381       p++;
 382     }
 383     p->set (op);
 384     return_trace (true);
 385   }
 386 
 387   static bool serialize_uint4_op (hb_serialize_context_t *c, op_code_t op, int value)
 388   { return serialize_int_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longintdict); }
 389 
 390   static bool serialize_uint2_op (hb_serialize_context_t *c, op_code_t op, int value)
 391   { return serialize_int_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
 392 
 393   static bool serialize_offset4_op (hb_serialize_context_t *c, op_code_t op, int value)
 394   {
 395     return serialize_uint4_op (c, op, value);
 396   }
 397 
 398   static bool serialize_offset2_op (hb_serialize_context_t *c, op_code_t op, int value)
 399   {
 400     return serialize_uint2_op (c, op, value);
 401   }
 402 };
 403 
 404 struct TopDict : Dict {};
 405 struct FontDict : Dict {};
 406 struct PrivateDict : Dict {};
 407 
 408 struct table_info_t
 409 {
 410   void init () { offSize = offset = size = 0; }
 411 
 412   unsigned int    offset;
 413   unsigned int    size;
 414   unsigned int    offSize;
 415 };
 416 
 417 /* used to remap font index or SID from fullset to subset.
 418  * set to CFF_UNDEF_CODE if excluded from subset */
 419 struct remap_t : hb_vector_t<hb_codepoint_t>
 420 {
 421   void init () { SUPER::init (); }
 422 
 423   void fini () { SUPER::fini (); }
 424 
 425   bool reset (unsigned int size)
 426   {
 427     if (unlikely (!SUPER::resize (size)))
 428       return false;
 429     for (unsigned int i = 0; i < length; i++)
 430       (*this)[i] = CFF_UNDEF_CODE;
 431     count = 0;
 432     return true;
 433   }
 434 
 435   bool identity (unsigned int size)
 436   {
 437     if (unlikely (!SUPER::resize (size)))
 438       return false;
 439     unsigned int i;
 440     for (i = 0; i < length; i++)
 441       (*this)[i] = i;
 442     count = i;
 443     return true;
 444   }
 445 
 446   bool excludes (hb_codepoint_t id) const
 447   { return (id < length) && ((*this)[id] == CFF_UNDEF_CODE); }
 448 
 449   bool includes (hb_codepoint_t id) const
 450   { return !excludes (id); }
 451 
 452   unsigned int add (unsigned int i)
 453   {
 454     if ((*this)[i] == CFF_UNDEF_CODE)
 455       (*this)[i] = count++;
 456     return (*this)[i];
 457   }
 458 
 459   hb_codepoint_t get_count () const { return count; }
 460 
 461   protected:
 462   hb_codepoint_t  count;
 463 
 464   private:
 465   typedef hb_vector_t<hb_codepoint_t> SUPER;
 466 };
 467 
 468 template <typename COUNT>
 469 struct FDArray : CFFIndexOf<COUNT, FontDict>
 470 {
 471   /* used by CFF1 */
 472   template <typename DICTVAL, typename OP_SERIALIZER>
 473   bool serialize (hb_serialize_context_t *c,
 474                   unsigned int offSize_,
 475                   const hb_vector_t<DICTVAL> &fontDicts,
 476                   OP_SERIALIZER& opszr)
 477   {
 478     TRACE_SERIALIZE (this);
 479     if (unlikely (!c->extend_min (*this))) return_trace (false);
 480     this->count.set (fontDicts.length);
 481     this->offSize.set (offSize_);
 482     if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fontDicts.length + 1))))
 483       return_trace (false);
 484 
 485     /* serialize font dict offsets */
 486     unsigned int  offset = 1;
 487     unsigned int fid = 0;
 488     for (; fid < fontDicts.length; fid++)
 489     {
 490       CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
 491       offset += FontDict::calculate_serialized_size (fontDicts[fid], opszr);
 492     }
 493     CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
 494 
 495     /* serialize font dicts */
 496     for (unsigned int i = 0; i < fontDicts.length; i++)
 497     {
 498       FontDict *dict = c->start_embed<FontDict> ();
 499       if (unlikely (!dict->serialize (c, fontDicts[i], opszr, fontDicts[i])))
 500         return_trace (false);
 501     }
 502     return_trace (true);
 503   }
 504 
 505   /* used by CFF2 */
 506   template <typename DICTVAL, typename OP_SERIALIZER>
 507   bool serialize (hb_serialize_context_t *c,
 508                   unsigned int offSize_,
 509                   const hb_vector_t<DICTVAL> &fontDicts,
 510                   unsigned int fdCount,
 511                   const remap_t &fdmap,
 512                   OP_SERIALIZER& opszr,
 513                   const hb_vector_t<table_info_t> &privateInfos)
 514   {
 515     TRACE_SERIALIZE (this);
 516     if (unlikely (!c->extend_min (*this))) return_trace (false);
 517     this->count.set (fdCount);
 518     this->offSize.set (offSize_);
 519     if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fdCount + 1))))
 520       return_trace (false);
 521 
 522     /* serialize font dict offsets */
 523     unsigned int  offset = 1;
 524     unsigned int  fid = 0;
 525     for (unsigned i = 0; i < fontDicts.length; i++)
 526       if (fdmap.includes (i))
 527       {
 528         CFFIndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
 529         offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
 530       }
 531     CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
 532 
 533     /* serialize font dicts */
 534     for (unsigned int i = 0; i < fontDicts.length; i++)
 535       if (fdmap.includes (i))
 536       {
 537         FontDict *dict = c->start_embed<FontDict> ();
 538         if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]])))
 539           return_trace (false);
 540       }
 541     return_trace (true);
 542   }
 543 
 544   /* in parallel to above */
 545   template <typename OP_SERIALIZER, typename DICTVAL>
 546   static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
 547                                                  const hb_vector_t<DICTVAL> &fontDicts,
 548                                                  unsigned int fdCount,
 549                                                  const remap_t &fdmap,
 550                                                  OP_SERIALIZER& opszr)
 551   {
 552     unsigned int dictsSize = 0;
 553     for (unsigned int i = 0; i < fontDicts.len; i++)
 554       if (fdmap.includes (i))
 555         dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
 556 
 557     offSize_ = calcOffSize (dictsSize);
 558     return CFFIndex<COUNT>::calculate_serialized_size (offSize_, fdCount, dictsSize);
 559   }
 560 };
 561 
 562 /* FDSelect */
 563 struct FDSelect0 {
 564   bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
 565   {
 566     TRACE_SANITIZE (this);
 567     if (unlikely (!(c->check_struct (this))))
 568       return_trace (false);
 569     for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
 570       if (unlikely (!fds[i].sanitize (c)))
 571         return_trace (false);
 572 
 573     return_trace (true);
 574   }
 575 
 576   hb_codepoint_t get_fd (hb_codepoint_t glyph) const
 577   {
 578     return (hb_codepoint_t)fds[glyph];
 579   }
 580 
 581   unsigned int get_size (unsigned int num_glyphs) const
 582   { return HBUINT8::static_size * num_glyphs; }
 583 
 584   HBUINT8     fds[VAR];
 585 
 586   DEFINE_SIZE_MIN (1);
 587 };
 588 
 589 template <typename GID_TYPE, typename FD_TYPE>
 590 struct FDSelect3_4_Range {
 591   bool sanitize (hb_sanitize_context_t *c, const void */*nullptr*/, unsigned int fdcount) const
 592   {
 593     TRACE_SANITIZE (this);
 594     return_trace (first < c->get_num_glyphs () && (fd < fdcount));
 595   }
 596 
 597   GID_TYPE    first;
 598   FD_TYPE     fd;
 599 
 600   DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
 601 };
 602 
 603 template <typename GID_TYPE, typename FD_TYPE>
 604 struct FDSelect3_4 {
 605   unsigned int get_size () const
 606   { return GID_TYPE::static_size * 2 + ranges.get_size (); }
 607 
 608   bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
 609   {
 610     TRACE_SANITIZE (this);
 611     if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) ||
 612                   (nRanges () == 0) || ranges[0].first != 0))
 613       return_trace (false);
 614 
 615     for (unsigned int i = 1; i < nRanges (); i++)
 616     {
 617       if (unlikely (ranges[i - 1].first >= ranges[i].first))
 618           return_trace (false);
 619     }
 620 
 621     if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
 622       return_trace (false);
 623 
 624     return_trace (true);
 625   }
 626 
 627   hb_codepoint_t get_fd (hb_codepoint_t glyph) const
 628   {
 629     unsigned int i;
 630     for (i = 1; i < nRanges (); i++)
 631       if (glyph < ranges[i].first)
 632         break;
 633 
 634     return (hb_codepoint_t)ranges[i - 1].fd;
 635   }
 636 
 637   GID_TYPE &nRanges () { return ranges.len; }
 638   GID_TYPE nRanges () const { return ranges.len; }
 639   GID_TYPE &sentinel ()  { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
 640   const GID_TYPE &sentinel () const  { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
 641 
 642   ArrayOf<FDSelect3_4_Range<GID_TYPE, FD_TYPE>, GID_TYPE> ranges;
 643   /* GID_TYPE sentinel */
 644 
 645   DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges);
 646 };
 647 
 648 typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
 649 typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;
 650 
 651 struct FDSelect {
 652   bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
 653   {
 654     TRACE_SANITIZE (this);
 655 
 656     return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) &&
 657                           (format == 0)?
 658                           u.format0.sanitize (c, fdcount):
 659                           u.format3.sanitize (c, fdcount)));
 660   }
 661 
 662   bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
 663   {
 664     TRACE_SERIALIZE (this);
 665     unsigned int size = src.get_size (num_glyphs);
 666     FDSelect *dest = c->allocate_size<FDSelect> (size);
 667     if (unlikely (dest == nullptr)) return_trace (false);
 668     memcpy (dest, &src, size);
 669     return_trace (true);
 670   }
 671 
 672   unsigned int calculate_serialized_size (unsigned int num_glyphs) const
 673   { return get_size (num_glyphs); }
 674 
 675   unsigned int get_size (unsigned int num_glyphs) const
 676   {
 677     unsigned int size = format.static_size;
 678     if (format == 0)
 679       size += u.format0.get_size (num_glyphs);
 680     else
 681       size += u.format3.get_size ();
 682     return size;
 683   }
 684 
 685   hb_codepoint_t get_fd (hb_codepoint_t glyph) const
 686   {
 687     if (this == &Null(FDSelect))
 688       return 0;
 689     if (format == 0)
 690       return u.format0.get_fd (glyph);
 691     else
 692       return u.format3.get_fd (glyph);
 693   }
 694 
 695   HBUINT8       format;
 696   union {
 697     FDSelect0   format0;
 698     FDSelect3   format3;
 699   } u;
 700 
 701   DEFINE_SIZE_MIN (1);
 702 };
 703 
 704 template <typename COUNT>
 705 struct Subrs : CFFIndex<COUNT>
 706 {
 707   typedef COUNT count_type;
 708   typedef CFFIndex<COUNT> SUPER;
 709 };
 710 
 711 } /* namespace CFF */
 712 
 713 #endif /* HB_OT_CFF_COMMON_HH */