| /* | 
 |  * 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-head-table.hh" | 
 | #include "hb-ot-cff-common.hh" | 
 | #include "hb-subset-cff2.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 FDArray<HBUINT32> CFF2FDArray; | 
 | typedef Subrs<HBUINT32>   CFF2Subrs; | 
 |  | 
 | typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4; | 
 | typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range; | 
 |  | 
 | struct CFF2FDSelect | 
 | { | 
 |   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 == 4) && | 
 |                           (format == 0)? | 
 |                           u.format0.sanitize (c, fdcount): | 
 |                             ((format == 3)? | 
 |                             u.format3.sanitize (c, fdcount): | 
 |                             u.format4.sanitize (c, fdcount)))); | 
 |   } | 
 |  | 
 |   inline 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 == 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 if (format == 3) | 
 |       size += u.format3.get_size (); | 
 |     else | 
 |       size += u.format4.get_size (); | 
 |     return size; | 
 |   } | 
 |  | 
 |   inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const | 
 |   { | 
 |     if (this == &Null(CFF2FDSelect)) | 
 |       return 0; | 
 |     if (format == 0) | 
 |       return u.format0.get_fd (glyph); | 
 |     else if (format == 3) | 
 |       return u.format3.get_fd (glyph); | 
 |     else | 
 |       return u.format4.get_fd (glyph); | 
 |   } | 
 |  | 
 |   HBUINT8       format; | 
 |   union { | 
 |     FDSelect0   format0; | 
 |     FDSelect3   format3; | 
 |     FDSelect4   format4; | 
 |   } u; | 
 |  | 
 |   DEFINE_SIZE_MIN (2); | 
 | }; | 
 |  | 
 | struct CFF2VariationStore | 
 | { | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (likely (c->check_struct (this)) && varStore.sanitize (c)); | 
 |   } | 
 |  | 
 |   inline 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 == nullptr)) return_trace (false); | 
 |     memcpy (dest, varStore, size_); | 
 |     return_trace (true); | 
 |   } | 
 |  | 
 |   inline unsigned int get_size (void) const { return HBUINT16::static_size + size; } | 
 |  | 
 |   HBUINT16        size; | 
 |   VariationStore  varStore; | 
 |  | 
 |   DEFINE_SIZE_MIN (2 + VariationStore::min_size); | 
 | }; | 
 |  | 
 | struct CFF2TopDictValues : TopDictValues<> | 
 | { | 
 |   inline void init (void) | 
 |   { | 
 |     TopDictValues<>::init (); | 
 |     vstoreOffset = 0; | 
 |     FDSelectOffset = 0; | 
 |   } | 
 |  | 
 |   inline void fini (void) | 
 |   { | 
 |     TopDictValues<>::fini (); | 
 |   } | 
 |  | 
 |   inline unsigned int calculate_serialized_size (void) const | 
 |   { | 
 |     unsigned int size = 0; | 
 |     for (unsigned int i = 0; i < get_count (); i++) | 
 |     { | 
 |       OpCode op = get_value (i).op; | 
 |       switch (op) | 
 |       { | 
 |         case OpCode_vstore: | 
 |         case OpCode_FDSelect: | 
 |           size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op); | 
 |           break; | 
 |         default: | 
 |           size += TopDictValues<>::calculate_serialized_op_size (get_value (i)); | 
 |           break; | 
 |       } | 
 |     } | 
 |     return size; | 
 |   } | 
 |  | 
 |   unsigned int  vstoreOffset; | 
 |   unsigned int  FDSelectOffset; | 
 | }; | 
 |  | 
 | struct CFF2TopDictOpSet : TopDictOpSet<> | 
 | { | 
 |   static inline void process_op (OpCode op, NumInterpEnv& env, CFF2TopDictValues& dictval) | 
 |   { | 
 |     switch (op) { | 
 |       case OpCode_FontMatrix: | 
 |         { | 
 |           DictVal val; | 
 |           val.init (); | 
 |           dictval.add_op (op, env.substr); | 
 |           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.substr); | 
 |   } | 
 |  | 
 |   typedef TopDictOpSet<> SUPER; | 
 | }; | 
 |  | 
 | struct CFF2FontDictValues : DictValues<OpStr> | 
 | { | 
 |   inline void init (void) | 
 |   { | 
 |     DictValues<OpStr>::init (); | 
 |     privateDictInfo.init (); | 
 |   } | 
 |  | 
 |   inline void fini (void) | 
 |   { | 
 |     DictValues<OpStr>::fini (); | 
 |   } | 
 |  | 
 |   TableInfo    privateDictInfo; | 
 | }; | 
 |  | 
 | struct CFF2FontDictOpSet : DictOpSet | 
 | { | 
 |   static inline void process_op (OpCode op, NumInterpEnv& env, CFF2FontDictValues& 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.substr); | 
 |   } | 
 |  | 
 |   private: | 
 |   typedef DictOpSet SUPER; | 
 | }; | 
 |  | 
 | template <typename VAL> | 
 | struct CFF2PrivateDictValues_Base : DictValues<VAL> | 
 | { | 
 |   inline void init (void) | 
 |   { | 
 |     DictValues<VAL>::init (); | 
 |     subrsOffset = 0; | 
 |     localSubrs = &Null(CFF2Subrs); | 
 |     ivs = 0; | 
 |   } | 
 |  | 
 |   inline void fini (void) | 
 |   { | 
 |     DictValues<VAL>::fini (); | 
 |   } | 
 |  | 
 |   inline unsigned int calculate_serialized_size (void) const | 
 |   { | 
 |     unsigned int size = 0; | 
 |     for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++) | 
 |       if (DictValues<VAL>::get_value (i).op == OpCode_Subrs) | 
 |         size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); | 
 |       else | 
 |         size += DictValues<VAL>::get_value (i).str.len; | 
 |     return size; | 
 |   } | 
 |  | 
 |   unsigned int      subrsOffset; | 
 |   const CFF2Subrs   *localSubrs; | 
 |   unsigned int      ivs; | 
 | }; | 
 |  | 
 | typedef CFF2PrivateDictValues_Base<OpStr> CFF2PrivateDictValues_Subset; | 
 | typedef CFF2PrivateDictValues_Base<NumDictVal> CFF2PrivateDictValues; | 
 |  | 
 | struct CFF2PrivDictInterpEnv : NumInterpEnv | 
 | { | 
 |   inline void init (const ByteStr &str) | 
 |   { | 
 |     NumInterpEnv::init (str); | 
 |     ivs = 0; | 
 |     seen_vsindex = false; | 
 |   } | 
 |  | 
 |   inline void process_vsindex (void) | 
 |   { | 
 |     if (likely (!seen_vsindex)) | 
 |     { | 
 |       set_ivs (argStack.pop_uint ()); | 
 |     } | 
 |     seen_vsindex = true; | 
 |   } | 
 |  | 
 |   inline unsigned int get_ivs (void) const { return ivs; } | 
 |   inline void         set_ivs (unsigned int ivs_) { ivs = ivs_; } | 
 |  | 
 |   protected: | 
 |   unsigned int  ivs; | 
 |   bool          seen_vsindex; | 
 | }; | 
 |  | 
 | struct CFF2PrivateDictOpSet : DictOpSet | 
 | { | 
 |   static inline void process_op (OpCode op, CFF2PrivDictInterpEnv& env, CFF2PrivateDictValues& dictval) | 
 |   { | 
 |     NumDictVal 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: | 
 |         DictOpSet::process_op (op, env); | 
 |         if (!env.argStack.is_empty ()) return; | 
 |         break; | 
 |     } | 
 |  | 
 |     if (unlikely (env.in_error ())) return; | 
 |  | 
 |     dictval.add_op (op, env.substr, val); | 
 |   } | 
 | }; | 
 |  | 
 | struct CFF2PrivateDictOpSet_Subset : DictOpSet | 
 | { | 
 |   static inline void process_op (OpCode op, CFF2PrivDictInterpEnv& env, CFF2PrivateDictValues_Subset& 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.substr); | 
 |   } | 
 |  | 
 |   private: | 
 |   typedef DictOpSet SUPER; | 
 | }; | 
 |  | 
 | typedef DictInterpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter; | 
 | typedef DictInterpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter; | 
 |  | 
 | }; /* namespace CFF */ | 
 |  | 
 | namespace OT { | 
 |  | 
 | using namespace CFF; | 
 |  | 
 | struct cff2 | 
 | { | 
 |   static const hb_tag_t tableTag        = HB_OT_TAG_cff2; | 
 |  | 
 |   inline 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 | 
 |   { | 
 |     inline 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 */ | 
 |         ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize); | 
 |         if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; } | 
 |         CFF2TopDict_Interpreter 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)) || | 
 |           (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 ByteStr fontDictStr = (*fdArray)[i]; | 
 |         if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; } | 
 |         CFF2FontDictValues  *font; | 
 |         CFF2FontDict_Interpreter font_interp; | 
 |         font_interp.env.init (fontDictStr); | 
 |         font = fontDicts.push (); | 
 |         font->init (); | 
 |         if (unlikely (!font_interp.interpret (*font))) { fini (); return; } | 
 |  | 
 |         const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size); | 
 |         if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } | 
 |         DictInterpreter<PRIVOPSET, PRIVDICTVAL, CFF2PrivDictInterpEnv>  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.str, privateDicts[i].subrsOffset); | 
 |         if (privateDicts[i].localSubrs != &Null(CFF2Subrs) && | 
 |           unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) | 
 |         { fini (); return; } | 
 |       } | 
 |     } | 
 |  | 
 |     inline void fini (void) | 
 |     { | 
 |       sc.end_processing (); | 
 |       fontDicts.fini (); | 
 |       privateDicts.fini_deep (); | 
 |       hb_blob_destroy (blob); | 
 |       blob = nullptr; | 
 |     } | 
 |  | 
 |     inline bool is_valid (void) const { return blob != nullptr; } | 
 |  | 
 |     protected: | 
 |     hb_blob_t               *blob; | 
 |     hb_sanitize_context_t   sc; | 
 |  | 
 |     public: | 
 |     CFF2TopDictValues         topDict; | 
 |     const CFF2Subrs           *globalSubrs; | 
 |     const CFF2VariationStore  *varStore; | 
 |     const CFF2CharStrings     *charStrings; | 
 |     const CFF2FDArray         *fdArray; | 
 |     const CFF2FDSelect        *fdSelect; | 
 |     unsigned int              fdCount; | 
 |  | 
 |     hb_vector_t<CFF2FontDictValues>     fontDicts; | 
 |     hb_vector_t<PRIVDICTVAL>  privateDicts; | 
 |  | 
 |     unsigned int            num_glyphs; | 
 |   }; | 
 |  | 
 |   struct accelerator_t : accelerator_templ_t<CFF2PrivateDictOpSet, CFF2PrivateDictValues> | 
 |   { | 
 |     HB_INTERNAL bool get_extents (hb_font_t *font, | 
 |                                   hb_codepoint_t glyph, | 
 |                                   hb_glyph_extents_t *extents) const; | 
 |   }; | 
 |  | 
 |   typedef accelerator_templ_t<CFF2PrivateDictOpSet_Subset, CFF2PrivateDictValues_Subset> accelerator_subset_t; | 
 |  | 
 |   inline bool subset (hb_subset_plan_t *plan) const | 
 |   { | 
 |     hb_blob_t *cff2_prime = nullptr; | 
 |  | 
 |     bool success = true; | 
 |     if (hb_subset_cff2 (plan, &cff2_prime)) { | 
 |       success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime); | 
 |       hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (plan->source); | 
 |       success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob); | 
 |       hb_blob_destroy (head_blob); | 
 |     } else { | 
 |       success = false; | 
 |     } | 
 |     hb_blob_destroy (cff2_prime); | 
 |  | 
 |     return success; | 
 |   } | 
 |  | 
 |   public: | 
 |   FixedVersion<HBUINT8> version;        /* Version of CFF2 table. set to 0x0200u */ | 
 |   OffsetTo<TopDict, HBUINT8, false> 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 */ |