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-bimap.hh"
31 #include "hb-ot-layout-common.hh"
32 #include "hb-cff-interp-dict-common.hh"
33 #include "hb-subset-plan.hh"
34 
35 namespace CFF {
36 
37 using namespace OT;
38 
39 #define CFF_UNDEF_CODE  0xFFFFFFFF
40 
41 /* utility macro */
42 template<typename Type>
StructAtOffsetOrNull(const void * P,unsigned int offset)43 static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset)
44 { return offset ? StructAtOffset<Type> (P, offset) : Null (Type); }
45 
calcOffSize(unsigned int dataSize)46 inline unsigned int calcOffSize (unsigned int dataSize)
47 {
48   unsigned int size = 1;
49   unsigned int offset = dataSize + 1;
50   while (offset & ~0xFF)
51   {
52     size++;
53     offset >>= 8;
54   }
55   /* format does not support size > 4; caller should handle it as an error */
56   return size;
57 }
58 
59 struct code_pair_t
60 {
61   hb_codepoint_t code;
62   hb_codepoint_t glyph;
63 };
64 
65 typedef hb_vector_t<unsigned char> str_buff_t;
66 struct str_buff_vec_t : hb_vector_t<str_buff_t>
67 {
finiCFF::str_buff_vec_t68   void fini () { SUPER::fini_deep (); }
69 
total_sizeCFF::str_buff_vec_t70   unsigned int total_size () const
71   {
72     unsigned int size = 0;
73     for (unsigned int i = 0; i < length; i++)
74       size += (*this)[i].length;
75     return size;
76   }
77 
78   private:
79   typedef hb_vector_t<str_buff_t> SUPER;
80 };
81 
82 /* CFF INDEX */
83 template <typename COUNT>
84 struct CFFIndex
85 {
calculate_offset_array_sizeCFF::CFFIndex86   static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
87   { return offSize * (count + 1); }
88 
offset_array_sizeCFF::CFFIndex89   unsigned int offset_array_size () const
90   { return calculate_offset_array_size (offSize, count); }
91 
calculate_serialized_sizeCFF::CFFIndex92   static unsigned int calculate_serialized_size (unsigned int offSize_, unsigned int count,
93 						 unsigned int dataSize)
94   {
95     if (count == 0) return COUNT::static_size;
96     return min_size + calculate_offset_array_size (offSize_, count) + dataSize;
97   }
98 
serializeCFF::CFFIndex99   bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
100   {
101     TRACE_SERIALIZE (this);
102     unsigned int size = src.get_size ();
103     CFFIndex *dest = c->allocate_size<CFFIndex> (size);
104     if (unlikely (dest == nullptr)) return_trace (false);
105     memcpy (dest, &src, size);
106     return_trace (true);
107   }
108 
serializeCFF::CFFIndex109   bool serialize (hb_serialize_context_t *c,
110 		  unsigned int offSize_,
111 		  const byte_str_array_t &byteArray)
112   {
113     TRACE_SERIALIZE (this);
114     if (byteArray.length == 0)
115     {
116       COUNT *dest = c->allocate_min<COUNT> ();
117       if (unlikely (dest == nullptr)) return_trace (false);
118       *dest = 0;
119     }
120     else
121     {
122       /* serialize CFFIndex header */
123       if (unlikely (!c->extend_min (*this))) return_trace (false);
124       this->count = byteArray.length;
125       this->offSize = offSize_;
126       if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1))))
127 	return_trace (false);
128 
129       /* serialize indices */
130       unsigned int  offset = 1;
131       unsigned int  i = 0;
132       for (; i < byteArray.length; i++)
133       {
134 	set_offset_at (i, offset);
135 	offset += byteArray[i].get_size ();
136       }
137       set_offset_at (i, offset);
138 
139       /* serialize data */
140       for (unsigned int i = 0; i < byteArray.length; i++)
141       {
142       	const byte_str_t &bs = byteArray[i];
143 	unsigned char  *dest = c->allocate_size<unsigned char> (bs.length);
144 	if (unlikely (dest == nullptr))
145 	  return_trace (false);
146 	memcpy (dest, &bs[0], bs.length);
147       }
148     }
149     return_trace (true);
150   }
151 
serializeCFF::CFFIndex152   bool serialize (hb_serialize_context_t *c,
153 		  unsigned int offSize_,
154 		  const str_buff_vec_t &buffArray)
155   {
156     byte_str_array_t  byteArray;
157     byteArray.init ();
158     byteArray.resize (buffArray.length);
159     for (unsigned int i = 0; i < byteArray.length; i++)
160       byteArray[i] = byte_str_t (buffArray[i].arrayZ, buffArray[i].length);
161     bool result = this->serialize (c, offSize_, byteArray);
162     byteArray.fini ();
163     return result;
164   }
165 
set_offset_atCFF::CFFIndex166   void set_offset_at (unsigned int index, unsigned int offset)
167   {
168     HBUINT8 *p = offsets + offSize * index + offSize;
169     unsigned int size = offSize;
170     for (; size; size--)
171     {
172       --p;
173       *p = offset & 0xFF;
174       offset >>= 8;
175     }
176   }
177 
offset_atCFF::CFFIndex178   unsigned int offset_at (unsigned int index) const
179   {
180     assert (index <= count);
181     const HBUINT8 *p = offsets + offSize * index;
182     unsigned int size = offSize;
183     unsigned int offset = 0;
184     for (; size; size--)
185       offset = (offset << 8) + *p++;
186     return offset;
187   }
188 
length_atCFF::CFFIndex189   unsigned int length_at (unsigned int index) const
190   {
191     if (unlikely ((offset_at (index + 1) < offset_at (index)) ||
192 	          (offset_at (index + 1) > offset_at (count))))
193       return 0;
194     return offset_at (index + 1) - offset_at (index);
195   }
196 
data_baseCFF::CFFIndex197   const unsigned char *data_base () const
198   { return (const unsigned char *) this + min_size + offset_array_size (); }
199 
data_sizeCFF::CFFIndex200   unsigned int data_size () const { return HBINT8::static_size; }
201 
operator []CFF::CFFIndex202   byte_str_t operator [] (unsigned int index) const
203   {
204     if (unlikely (index >= count)) return Null (byte_str_t);
205     return byte_str_t (data_base () + offset_at (index) - 1, length_at (index));
206   }
207 
get_sizeCFF::CFFIndex208   unsigned int get_size () const
209   {
210     if (this == &Null (CFFIndex)) return 0;
211     if (count > 0)
212       return min_size + offset_array_size () + (offset_at (count) - 1);
213     return count.static_size;  /* empty CFFIndex contains count only */
214   }
215 
sanitizeCFF::CFFIndex216   bool sanitize (hb_sanitize_context_t *c) const
217   {
218     TRACE_SANITIZE (this);
219     return_trace (likely ((c->check_struct (this) && count == 0) || /* empty INDEX */
220 			  (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
221 			   c->check_array (offsets, offSize, count + 1) &&
222 			   c->check_array ((const HBUINT8*) data_base (), 1, max_offset () - 1))));
223   }
224 
225   protected:
max_offsetCFF::CFFIndex226   unsigned int max_offset () const
227   {
228     unsigned int max = 0;
229     for (unsigned int i = 0; i < count + 1u; i++)
230     {
231       unsigned int off = offset_at (i);
232       if (off > max) max = off;
233     }
234     return max;
235   }
236 
237   public:
238   COUNT		count;		/* Number of object data. Note there are (count+1) offsets */
239   HBUINT8	offSize;	/* The byte size of each offset in the offsets array. */
240   HBUINT8	offsets[HB_VAR_ARRAY];	/* The array of (count + 1) offsets into objects array (1-base). */
241   /* HBUINT8 data[HB_VAR_ARRAY];	Object data */
242   public:
243   DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
244 };
245 
246 template <typename COUNT, typename TYPE>
247 struct CFFIndexOf : CFFIndex<COUNT>
248 {
operator []CFF::CFFIndexOf249   const byte_str_t operator [] (unsigned int index) const
250   {
251     if (likely (index < CFFIndex<COUNT>::count))
252       return byte_str_t (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
253     return Null(byte_str_t);
254   }
255 
256   template <typename DATA, typename PARAM1, typename PARAM2>
serializeCFF::CFFIndexOf257   bool serialize (hb_serialize_context_t *c,
258 		  unsigned int offSize_,
259 		  const DATA *dataArray,
260 		  unsigned int dataArrayLen,
261 		  const hb_vector_t<unsigned int> &dataSizeArray,
262 		  const PARAM1 &param1,
263 		  const PARAM2 &param2)
264   {
265     TRACE_SERIALIZE (this);
266     /* serialize CFFIndex header */
267     if (unlikely (!c->extend_min (*this))) return_trace (false);
268     this->count = dataArrayLen;
269     this->offSize = offSize_;
270     if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1))))
271       return_trace (false);
272 
273     /* serialize indices */
274     unsigned int  offset = 1;
275     unsigned int  i = 0;
276     for (; i < dataArrayLen; i++)
277     {
278       CFFIndex<COUNT>::set_offset_at (i, offset);
279       offset += dataSizeArray[i];
280     }
281     CFFIndex<COUNT>::set_offset_at (i, offset);
282 
283     /* serialize data */
284     for (unsigned int i = 0; i < dataArrayLen; i++)
285     {
286       TYPE *dest = c->start_embed<TYPE> ();
287       if (unlikely (dest == nullptr ||
288 		    !dest->serialize (c, dataArray[i], param1, param2)))
289 	return_trace (false);
290     }
291     return_trace (true);
292   }
293 
294   /* in parallel to above */
295   template <typename DATA, typename PARAM>
calculate_serialized_sizeCFF::CFFIndexOf296   static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
297 						 const DATA *dataArray,
298 						 unsigned int dataArrayLen,
299 						 hb_vector_t<unsigned int> &dataSizeArray, /* OUT */
300 						 const PARAM &param)
301   {
302     /* determine offset size */
303     unsigned int totalDataSize = 0;
304     for (unsigned int i = 0; i < dataArrayLen; i++)
305     {
306       unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param);
307       dataSizeArray[i] = dataSize;
308       totalDataSize += dataSize;
309     }
310     offSize_ = calcOffSize (totalDataSize);
311 
312     return CFFIndex<COUNT>::calculate_serialized_size (offSize_, dataArrayLen, totalDataSize);
313   }
314 };
315 
316 /* Top Dict, Font Dict, Private Dict */
317 struct Dict : UnsizedByteStr
318 {
319   template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
serializeCFF::Dict320   bool serialize (hb_serialize_context_t *c,
321 		  const DICTVAL &dictval,
322 		  OP_SERIALIZER& opszr,
323 		  PARAM& param)
324   {
325     TRACE_SERIALIZE (this);
326     for (unsigned int i = 0; i < dictval.get_count (); i++)
327       if (unlikely (!opszr.serialize (c, dictval[i], param)))
328 	return_trace (false);
329 
330     return_trace (true);
331   }
332 
333   /* in parallel to above */
334   template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
calculate_serialized_sizeCFF::Dict335   static unsigned int calculate_serialized_size (const DICTVAL &dictval,
336 						 OP_SERIALIZER& opszr,
337 						 PARAM& param)
338   {
339     unsigned int size = 0;
340     for (unsigned int i = 0; i < dictval.get_count (); i++)
341       size += opszr.calculate_serialized_size (dictval[i], param);
342     return size;
343   }
344 
345   template <typename DICTVAL, typename OP_SERIALIZER>
calculate_serialized_sizeCFF::Dict346   static unsigned int calculate_serialized_size (const DICTVAL &dictval,
347 						 OP_SERIALIZER& opszr)
348   {
349     unsigned int size = 0;
350     for (unsigned int i = 0; i < dictval.get_count (); i++)
351       size += opszr.calculate_serialized_size (dictval[i]);
352     return size;
353   }
354 
355   template <typename INTTYPE, int minVal, int maxVal>
serialize_int_opCFF::Dict356   static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, int value, op_code_t intOp)
357   {
358     // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
359     if (/*unlikely*/ (!serialize_int<INTTYPE, minVal, maxVal> (c, intOp, value)))
360       return false;
361 
362     TRACE_SERIALIZE (this);
363     /* serialize the opcode */
364     HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op));
365     if (unlikely (p == nullptr)) return_trace (false);
366     if (Is_OpCode_ESC (op))
367     {
368       *p = OpCode_escape;
369       op = Unmake_OpCode_ESC (op);
370       p++;
371     }
372     *p = op;
373     return_trace (true);
374   }
375 
serialize_uint4_opCFF::Dict376   static bool serialize_uint4_op (hb_serialize_context_t *c, op_code_t op, int value)
377   { return serialize_int_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longintdict); }
378 
serialize_uint2_opCFF::Dict379   static bool serialize_uint2_op (hb_serialize_context_t *c, op_code_t op, int value)
380   { return serialize_int_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
381 
serialize_offset4_opCFF::Dict382   static bool serialize_offset4_op (hb_serialize_context_t *c, op_code_t op, int value)
383   { return serialize_uint4_op (c, op, value); }
384 
serialize_offset2_opCFF::Dict385   static bool serialize_offset2_op (hb_serialize_context_t *c, op_code_t op, int value)
386   { return serialize_uint2_op (c, op, value); }
387 };
388 
389 struct TopDict : Dict {};
390 struct FontDict : Dict {};
391 struct PrivateDict : Dict {};
392 
393 struct table_info_t
394 {
initCFF::table_info_t395   void init () { offSize = offset = size = 0; }
396 
397   unsigned int    offset;
398   unsigned int    size;
399   unsigned int    offSize;
400 };
401 
402 template <typename COUNT>
403 struct FDArray : CFFIndexOf<COUNT, FontDict>
404 {
405   /* used by CFF1 */
406   template <typename DICTVAL, typename OP_SERIALIZER>
serializeCFF::FDArray407   bool serialize (hb_serialize_context_t *c,
408 		  unsigned int offSize_,
409 		  const hb_vector_t<DICTVAL> &fontDicts,
410 		  OP_SERIALIZER& opszr)
411   {
412     TRACE_SERIALIZE (this);
413     if (unlikely (!c->extend_min (*this))) return_trace (false);
414     this->count = fontDicts.length;
415     this->offSize = offSize_;
416     if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (fontDicts.length + 1))))
417       return_trace (false);
418 
419     /* serialize font dict offsets */
420     unsigned int offset = 1;
421     unsigned int fid = 0;
422     for (; fid < fontDicts.length; fid++)
423     {
424       CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
425       offset += FontDict::calculate_serialized_size (fontDicts[fid], opszr);
426     }
427     CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
428 
429     /* serialize font dicts */
430     for (unsigned int i = 0; i < fontDicts.length; i++)
431     {
432       FontDict *dict = c->start_embed<FontDict> ();
433       if (unlikely (!dict->serialize (c, fontDicts[i], opszr, fontDicts[i])))
434 	return_trace (false);
435     }
436     return_trace (true);
437   }
438 
439   /* used by CFF2 */
440   template <typename DICTVAL, typename OP_SERIALIZER>
serializeCFF::FDArray441   bool serialize (hb_serialize_context_t *c,
442 		  unsigned int offSize_,
443 		  const hb_vector_t<DICTVAL> &fontDicts,
444 		  unsigned int fdCount,
445 		  const hb_inc_bimap_t &fdmap,
446 		  OP_SERIALIZER& opszr,
447 		  const hb_vector_t<table_info_t> &privateInfos)
448   {
449     TRACE_SERIALIZE (this);
450     if (unlikely (!c->extend_min (*this))) return_trace (false);
451     this->count = fdCount;
452     this->offSize = offSize_;
453     if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (fdCount + 1))))
454       return_trace (false);
455 
456     /* serialize font dict offsets */
457     unsigned int  offset = 1;
458     unsigned int  fid = 0;
459     for (unsigned i = 0; i < fontDicts.length; i++)
460       if (fdmap.has (i))
461       {
462       	if (unlikely (fid >= fdCount)) return_trace (false);
463 	CFFIndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
464 	offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
465       }
466     CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
467 
468     /* serialize font dicts */
469     for (unsigned int i = 0; i < fontDicts.length; i++)
470       if (fdmap.has (i))
471       {
472 	FontDict *dict = c->start_embed<FontDict> ();
473 	if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]])))
474 	  return_trace (false);
475       }
476     return_trace (true);
477   }
478 
479   /* in parallel to above */
480   template <typename OP_SERIALIZER, typename DICTVAL>
calculate_serialized_sizeCFF::FDArray481   static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
482 						 const hb_vector_t<DICTVAL> &fontDicts,
483 						 unsigned int fdCount,
484 						 const hb_inc_bimap_t &fdmap,
485 						 OP_SERIALIZER& opszr)
486   {
487     unsigned int dictsSize = 0;
488     for (unsigned int i = 0; i < fontDicts.len; i++)
489       if (fdmap.has (i))
490 	dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
491 
492     offSize_ = calcOffSize (dictsSize);
493     return CFFIndex<COUNT>::calculate_serialized_size (offSize_, fdCount, dictsSize);
494   }
495 };
496 
497 /* FDSelect */
498 struct FDSelect0 {
sanitizeCFF::FDSelect0499   bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
500   {
501     TRACE_SANITIZE (this);
502     if (unlikely (!(c->check_struct (this))))
503       return_trace (false);
504     for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
505       if (unlikely (!fds[i].sanitize (c)))
506 	return_trace (false);
507 
508     return_trace (true);
509   }
510 
get_fdCFF::FDSelect0511   hb_codepoint_t get_fd (hb_codepoint_t glyph) const
512   { return (hb_codepoint_t) fds[glyph]; }
513 
get_sizeCFF::FDSelect0514   unsigned int get_size (unsigned int num_glyphs) const
515   { return HBUINT8::static_size * num_glyphs; }
516 
517   HBUINT8     fds[HB_VAR_ARRAY];
518 
519   DEFINE_SIZE_MIN (0);
520 };
521 
522 template <typename GID_TYPE, typename FD_TYPE>
523 struct FDSelect3_4_Range
524 {
sanitizeCFF::FDSelect3_4_Range525   bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const
526   {
527     TRACE_SANITIZE (this);
528     return_trace (first < c->get_num_glyphs () && (fd < fdcount));
529   }
530 
531   GID_TYPE    first;
532   FD_TYPE     fd;
533   public:
534   DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
535 };
536 
537 template <typename GID_TYPE, typename FD_TYPE>
538 struct FDSelect3_4
539 {
get_sizeCFF::FDSelect3_4540   unsigned int get_size () const
541   { return GID_TYPE::static_size * 2 + ranges.get_size (); }
542 
sanitizeCFF::FDSelect3_4543   bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
544   {
545     TRACE_SANITIZE (this);
546     if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) ||
547     		  (nRanges () == 0) || ranges[0].first != 0))
548       return_trace (false);
549 
550     for (unsigned int i = 1; i < nRanges (); i++)
551       if (unlikely (ranges[i - 1].first >= ranges[i].first))
552 	return_trace (false);
553 
554     if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
555       return_trace (false);
556 
557     return_trace (true);
558   }
559 
get_fdCFF::FDSelect3_4560   hb_codepoint_t get_fd (hb_codepoint_t glyph) const
561   {
562     unsigned int i;
563     for (i = 1; i < nRanges (); i++)
564       if (glyph < ranges[i].first)
565 	break;
566 
567     return (hb_codepoint_t) ranges[i - 1].fd;
568   }
569 
nRangesCFF::FDSelect3_4570   GID_TYPE        &nRanges ()       { return ranges.len; }
nRangesCFF::FDSelect3_4571   GID_TYPE         nRanges () const { return ranges.len; }
sentinelCFF::FDSelect3_4572   GID_TYPE       &sentinel ()       { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
sentinelCFF::FDSelect3_4573   const GID_TYPE &sentinel () const { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
574 
575   ArrayOf<FDSelect3_4_Range<GID_TYPE, FD_TYPE>, GID_TYPE> ranges;
576   /* GID_TYPE sentinel */
577 
578   DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges);
579 };
580 
581 typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
582 typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;
583 
584 struct FDSelect
585 {
serializeCFF::FDSelect586   bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
587   {
588     TRACE_SERIALIZE (this);
589     unsigned int size = src.get_size (num_glyphs);
590     FDSelect *dest = c->allocate_size<FDSelect> (size);
591     if (unlikely (dest == nullptr)) return_trace (false);
592     memcpy (dest, &src, size);
593     return_trace (true);
594   }
595 
calculate_serialized_sizeCFF::FDSelect596   unsigned int calculate_serialized_size (unsigned int num_glyphs) const
597   { return get_size (num_glyphs); }
598 
get_sizeCFF::FDSelect599   unsigned int get_size (unsigned int num_glyphs) const
600   {
601     switch (format)
602     {
603     case 0: return format.static_size + u.format0.get_size (num_glyphs);
604     case 3: return format.static_size + u.format3.get_size ();
605     default:return 0;
606     }
607   }
608 
get_fdCFF::FDSelect609   hb_codepoint_t get_fd (hb_codepoint_t glyph) const
610   {
611     if (this == &Null (FDSelect)) return 0;
612 
613     switch (format)
614     {
615     case 0: return u.format0.get_fd (glyph);
616     case 3: return u.format3.get_fd (glyph);
617     default:return 0;
618     }
619   }
620 
sanitizeCFF::FDSelect621   bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
622   {
623     TRACE_SANITIZE (this);
624     if (unlikely (!c->check_struct (this)))
625       return_trace (false);
626 
627     switch (format)
628     {
629     case 0: return_trace (u.format0.sanitize (c, fdcount));
630     case 3: return_trace (u.format3.sanitize (c, fdcount));
631     default:return_trace (false);
632     }
633   }
634 
635   HBUINT8	format;
636   union {
637   FDSelect0	format0;
638   FDSelect3	format3;
639   } u;
640   public:
641   DEFINE_SIZE_MIN (1);
642 };
643 
644 template <typename COUNT>
645 struct Subrs : CFFIndex<COUNT>
646 {
647   typedef COUNT count_type;
648   typedef CFFIndex<COUNT> SUPER;
649 };
650 
651 } /* namespace CFF */
652 
653 #endif /* HB_OT_CFF_COMMON_HH */
654