• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2018  Ebrahim Byagowi
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 
25 #ifndef HB_OT_STAT_TABLE_HH
26 #define HB_OT_STAT_TABLE_HH
27 
28 #include "hb-open-type.hh"
29 #include "hb-ot-layout-common.hh"
30 
31 /*
32  * STAT -- Style Attributes
33  * https://docs.microsoft.com/en-us/typography/opentype/spec/stat
34  */
35 #define HB_OT_TAG_STAT HB_TAG('S','T','A','T')
36 
37 
38 namespace OT {
39 
40 enum
41 {
42   OLDER_SIBLING_FONT_ATTRIBUTE = 0x0001,	/* If set, this axis value table
43 						 * provides axis value information
44 						 * that is applicable to other fonts
45 						 * within the same font family. This
46 						 * is used if the other fonts were
47 						 * released earlier and did not include
48 						 * information about values for some axis.
49 						 * If newer versions of the other
50 						 * fonts include the information
51 						 * themselves and are present,
52 						 * then this record is ignored. */
53   ELIDABLE_AXIS_VALUE_NAME = 0x0002		/* If set, it indicates that the axis
54 						 * value represents the “normal” value
55 						 * for the axis and may be omitted when
56 						 * composing name strings. */
57   // Reserved = 0xFFFC				/* Reserved for future use — set to zero. */
58 };
59 
60 struct StatAxisRecord
61 {
cmpOT::StatAxisRecord62   int cmp (hb_tag_t key) const { return tag.cmp (key); }
63 
get_name_idOT::StatAxisRecord64   hb_ot_name_id_t get_name_id () const { return nameID; }
65 
get_axis_tagOT::StatAxisRecord66   hb_tag_t get_axis_tag () const { return tag; }
67 
sanitizeOT::StatAxisRecord68   bool sanitize (hb_sanitize_context_t *c) const
69   {
70     TRACE_SANITIZE (this);
71     return_trace (likely (c->check_struct (this)));
72   }
73 
74   protected:
75   Tag		tag;		/* A tag identifying the axis of design variation. */
76   NameID	nameID;		/* The name ID for entries in the 'name' table that
77 				 * provide a display string for this axis. */
78   HBUINT16	ordering;	/* A value that applications can use to determine
79 				 * primary sorting of face names, or for ordering
80 				 * of descriptors when composing family or face names. */
81   public:
82   DEFINE_SIZE_STATIC (8);
83 };
84 
85 struct AxisValueFormat1
86 {
get_axis_indexOT::AxisValueFormat187   unsigned int get_axis_index () const { return axisIndex; }
get_valueOT::AxisValueFormat188   float get_value ()             const { return value.to_float (); }
89 
get_value_name_idOT::AxisValueFormat190   hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
91 
get_axis_tagOT::AxisValueFormat192   hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
93   {
94     unsigned axis_idx = get_axis_index ();
95     return axis_records[axis_idx].get_axis_tag ();
96   }
97 
keep_axis_valueOT::AxisValueFormat198   bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
99                         const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
100   {
101     hb_tag_t axis_tag = get_axis_tag (axis_records);
102     float axis_value = get_value ();
103 
104     if (!user_axes_location->has (axis_tag) ||
105         fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
106       return true;
107 
108     return false;
109   }
110 
subsetOT::AxisValueFormat1111   bool subset (hb_subset_context_t *c,
112                const hb_array_t<const StatAxisRecord> axis_records) const
113   {
114     TRACE_SUBSET (this);
115     const hb_hashmap_t<hb_tag_t, float>*  user_axes_location = c->plan->user_axes_location;
116 
117     if (keep_axis_value (axis_records, user_axes_location))
118       return_trace (c->serializer->embed (this));
119 
120     return_trace (false);
121   }
122 
sanitizeOT::AxisValueFormat1123   bool sanitize (hb_sanitize_context_t *c) const
124   {
125     TRACE_SANITIZE (this);
126     return_trace (c->check_struct (this));
127   }
128 
129   protected:
130   HBUINT16	format;		/* Format identifier — set to 1. */
131   HBUINT16	axisIndex;	/* Zero-base index into the axis record array
132 				 * identifying the axis of design variation
133 				 * to which the axis value record applies.
134 				 * Must be less than designAxisCount. */
135   HBUINT16	flags;		/* Flags — see below for details. */
136   NameID	valueNameID;	/* The name ID for entries in the 'name' table
137 				 * that provide a display string for this
138 				 * attribute value. */
139   F16DOT16	value;		/* A numeric value for this attribute value. */
140   public:
141   DEFINE_SIZE_STATIC (12);
142 };
143 
144 struct AxisValueFormat2
145 {
get_axis_indexOT::AxisValueFormat2146   unsigned int get_axis_index () const { return axisIndex; }
get_valueOT::AxisValueFormat2147   float get_value ()             const { return nominalValue.to_float (); }
148 
get_value_name_idOT::AxisValueFormat2149   hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
150 
get_axis_tagOT::AxisValueFormat2151   hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
152   {
153     unsigned axis_idx = get_axis_index ();
154     return axis_records[axis_idx].get_axis_tag ();
155   }
156 
keep_axis_valueOT::AxisValueFormat2157   bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
158                         const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
159   {
160     hb_tag_t axis_tag = get_axis_tag (axis_records);
161     float axis_value = get_value ();
162 
163     if (!user_axes_location->has (axis_tag) ||
164         fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
165       return true;
166 
167     return false;
168   }
169 
subsetOT::AxisValueFormat2170   bool subset (hb_subset_context_t *c,
171                const hb_array_t<const StatAxisRecord> axis_records) const
172   {
173     TRACE_SUBSET (this);
174     const hb_hashmap_t<hb_tag_t, float>*  user_axes_location = c->plan->user_axes_location;
175 
176     if (keep_axis_value (axis_records, user_axes_location))
177       return_trace (c->serializer->embed (this));
178 
179     return_trace (false);
180   }
181 
sanitizeOT::AxisValueFormat2182   bool sanitize (hb_sanitize_context_t *c) const
183   {
184     TRACE_SANITIZE (this);
185     return_trace (c->check_struct (this));
186   }
187 
188   protected:
189   HBUINT16	format;		/* Format identifier — set to 2. */
190   HBUINT16	axisIndex;	/* Zero-base index into the axis record array
191 				 * identifying the axis of design variation
192 				 * to which the axis value record applies.
193 				 * Must be less than designAxisCount. */
194   HBUINT16	flags;		/* Flags — see below for details. */
195   NameID	valueNameID;	/* The name ID for entries in the 'name' table
196 				 * that provide a display string for this
197 				 * attribute value. */
198   F16DOT16	nominalValue;	/* A numeric value for this attribute value. */
199   F16DOT16	rangeMinValue;	/* The minimum value for a range associated
200 				 * with the specified name ID. */
201   F16DOT16	rangeMaxValue;	/* The maximum value for a range associated
202 				 * with the specified name ID. */
203   public:
204   DEFINE_SIZE_STATIC (20);
205 };
206 
207 struct AxisValueFormat3
208 {
get_axis_indexOT::AxisValueFormat3209   unsigned int get_axis_index () const { return axisIndex; }
get_valueOT::AxisValueFormat3210   float get_value ()             const { return value.to_float (); }
211 
get_value_name_idOT::AxisValueFormat3212   hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
213 
get_axis_tagOT::AxisValueFormat3214   hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
215   {
216     unsigned axis_idx = get_axis_index ();
217     return axis_records[axis_idx].get_axis_tag ();
218   }
219 
keep_axis_valueOT::AxisValueFormat3220   bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
221                         const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
222   {
223     hb_tag_t axis_tag = get_axis_tag (axis_records);
224     float axis_value = get_value ();
225 
226     if (!user_axes_location->has (axis_tag) ||
227         fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
228       return true;
229 
230     return false;
231   }
232 
subsetOT::AxisValueFormat3233   bool subset (hb_subset_context_t *c,
234                const hb_array_t<const StatAxisRecord> axis_records) const
235   {
236     TRACE_SUBSET (this);
237     const hb_hashmap_t<hb_tag_t, float>* user_axes_location = c->plan->user_axes_location;
238 
239     if (keep_axis_value (axis_records, user_axes_location))
240       return_trace (c->serializer->embed (this));
241 
242     return_trace (false);
243   }
244 
sanitizeOT::AxisValueFormat3245   bool sanitize (hb_sanitize_context_t *c) const
246   {
247     TRACE_SANITIZE (this);
248     return_trace (c->check_struct (this));
249   }
250 
251   protected:
252   HBUINT16	format;		/* Format identifier — set to 3. */
253   HBUINT16	axisIndex;	/* Zero-base index into the axis record array
254 				 * identifying the axis of design variation
255 				 * to which the axis value record applies.
256 				 * Must be less than designAxisCount. */
257   HBUINT16	flags;		/* Flags — see below for details. */
258   NameID	valueNameID;	/* The name ID for entries in the 'name' table
259 				 * that provide a display string for this
260 				 * attribute value. */
261   F16DOT16	value;		/* A numeric value for this attribute value. */
262   F16DOT16	linkedValue;	/* The numeric value for a style-linked mapping
263 				 * from this value. */
264   public:
265   DEFINE_SIZE_STATIC (16);
266 };
267 
268 struct AxisValueRecord
269 {
get_axis_indexOT::AxisValueRecord270   unsigned int get_axis_index () const { return axisIndex; }
get_valueOT::AxisValueRecord271   float get_value ()             const { return value.to_float (); }
272 
sanitizeOT::AxisValueRecord273   bool sanitize (hb_sanitize_context_t *c) const
274   {
275     TRACE_SANITIZE (this);
276     return_trace (c->check_struct (this));
277   }
278 
279   protected:
280   HBUINT16	axisIndex;	/* Zero-base index into the axis record array
281 				 * identifying the axis to which this value
282 				 * applies. Must be less than designAxisCount. */
283   F16DOT16	value;		/* A numeric value for this attribute value. */
284   public:
285   DEFINE_SIZE_STATIC (6);
286 };
287 
288 struct AxisValueFormat4
289 {
get_axis_recordOT::AxisValueFormat4290   const AxisValueRecord &get_axis_record (unsigned int axis_index) const
291   { return axisValues.as_array (axisCount)[axis_index]; }
292 
keep_axis_valueOT::AxisValueFormat4293   bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
294                         const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
295   {
296     hb_array_t<const AxisValueRecord> axis_value_records = axisValues.as_array (axisCount);
297 
298     for (const auto& rec : axis_value_records)
299     {
300       unsigned axis_idx = rec.get_axis_index ();
301       float axis_value = rec.get_value ();
302       hb_tag_t axis_tag = axis_records[axis_idx].get_axis_tag ();
303 
304       if (user_axes_location->has (axis_tag) &&
305           fabsf(axis_value - user_axes_location->get (axis_tag)) > 0.001f)
306         return false;
307     }
308 
309     return true;
310   }
311 
subsetOT::AxisValueFormat4312   bool subset (hb_subset_context_t *c,
313                const hb_array_t<const StatAxisRecord> axis_records) const
314   {
315     TRACE_SUBSET (this);
316     const hb_hashmap_t<hb_tag_t, float> *user_axes_location = c->plan->user_axes_location;
317     if (!keep_axis_value (axis_records, user_axes_location))
318       return_trace (false);
319 
320     unsigned total_size = min_size + axisCount * AxisValueRecord::static_size;
321     auto *out = c->serializer->allocate_size<AxisValueFormat4> (total_size);
322     if (unlikely (!out)) return_trace (false);
323     hb_memcpy (out, this, total_size);
324     return_trace (true);
325   }
326 
get_value_name_idOT::AxisValueFormat4327   hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
328 
sanitizeOT::AxisValueFormat4329   bool sanitize (hb_sanitize_context_t *c) const
330   {
331     TRACE_SANITIZE (this);
332     return_trace (likely (c->check_struct (this) &&
333                           axisValues.sanitize (c, axisCount)));
334   }
335 
336   protected:
337   HBUINT16	format;		/* Format identifier — set to 4. */
338   HBUINT16	axisCount;	/* The total number of axes contributing to
339 				 * this axis-values combination. */
340   HBUINT16	flags;		/* Flags — see below for details. */
341   NameID	valueNameID;	/* The name ID for entries in the 'name' table
342 				 * that provide a display string for this
343 				 * attribute value. */
344   UnsizedArrayOf<AxisValueRecord>
345 		axisValues;	/* Array of AxisValue records that provide the
346 				 * combination of axis values, one for each
347 				 * contributing axis. */
348   public:
349   DEFINE_SIZE_ARRAY (8, axisValues);
350 };
351 
352 struct AxisValue
353 {
get_valueOT::AxisValue354   bool get_value (unsigned int axis_index) const
355   {
356     switch (u.format)
357     {
358     case 1: return u.format1.get_value ();
359     case 2: return u.format2.get_value ();
360     case 3: return u.format3.get_value ();
361     case 4: return u.format4.get_axis_record (axis_index).get_value ();
362     default:return 0;
363     }
364   }
365 
get_axis_indexOT::AxisValue366   unsigned int get_axis_index () const
367   {
368     switch (u.format)
369     {
370     case 1: return u.format1.get_axis_index ();
371     case 2: return u.format2.get_axis_index ();
372     case 3: return u.format3.get_axis_index ();
373     /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */
374     default:return -1;
375     }
376   }
377 
get_value_name_idOT::AxisValue378   hb_ot_name_id_t get_value_name_id () const
379   {
380     switch (u.format)
381     {
382     case 1: return u.format1.get_value_name_id ();
383     case 2: return u.format2.get_value_name_id ();
384     case 3: return u.format3.get_value_name_id ();
385     case 4: return u.format4.get_value_name_id ();
386     default:return HB_OT_NAME_ID_INVALID;
387     }
388   }
389 
390   template <typename context_t, typename ...Ts>
dispatchOT::AxisValue391   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
392   {
393     TRACE_DISPATCH (this, u.format);
394     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
395     switch (u.format) {
396     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
397     case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
398     case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
399     case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
400     default:return_trace (c->default_return_value ());
401     }
402   }
403 
keep_axis_valueOT::AxisValue404   bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
405                         hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
406   {
407     switch (u.format)
408     {
409     case 1: return u.format1.keep_axis_value (axis_records, user_axes_location);
410     case 2: return u.format2.keep_axis_value (axis_records, user_axes_location);
411     case 3: return u.format3.keep_axis_value (axis_records, user_axes_location);
412     case 4: return u.format4.keep_axis_value (axis_records, user_axes_location);
413     default:return false;
414     }
415   }
416 
sanitizeOT::AxisValue417   bool sanitize (hb_sanitize_context_t *c) const
418   {
419     TRACE_SANITIZE (this);
420     if (unlikely (!c->check_struct (this)))
421       return_trace (false);
422 
423     switch (u.format)
424     {
425     case 1: return_trace (u.format1.sanitize (c));
426     case 2: return_trace (u.format2.sanitize (c));
427     case 3: return_trace (u.format3.sanitize (c));
428     case 4: return_trace (u.format4.sanitize (c));
429     default:return_trace (true);
430     }
431   }
432 
433   protected:
434   union
435   {
436   HBUINT16		format;
437   AxisValueFormat1	format1;
438   AxisValueFormat2	format2;
439   AxisValueFormat3	format3;
440   AxisValueFormat4	format4;
441   } u;
442   public:
443   DEFINE_SIZE_UNION (2, format);
444 };
445 
446 struct AxisValueOffsetArray: UnsizedArrayOf<Offset16To<AxisValue>>
447 {
subsetOT::AxisValueOffsetArray448   bool subset (hb_subset_context_t *c,
449                unsigned axisValueCount,
450                unsigned& count,
451                const hb_array_t<const StatAxisRecord> axis_records) const
452   {
453     TRACE_SUBSET (this);
454     auto *out = c->serializer->start_embed (this);
455     if (unlikely (!out)) return_trace (false);
456 
457     auto axisValueOffsets = as_array (axisValueCount);
458     count = 0;
459     for (const auto& offset : axisValueOffsets)
460     {
461       if (!offset) continue;
462       auto o_snap = c->serializer->snapshot ();
463       auto *o = c->serializer->embed (offset);
464       if (!o) return_trace (false);
465       if (!o->serialize_subset (c, offset, this, axis_records))
466       {
467         c->serializer->revert (o_snap);
468         continue;
469       }
470       count++;
471     }
472 
473     return_trace (count);
474   }
475 };
476 
477 struct STAT
478 {
479   static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT;
480 
has_dataOT::STAT481   bool has_data () const { return version.to_int (); }
482 
get_valueOT::STAT483   bool get_value (hb_tag_t tag, float *value) const
484   {
485     unsigned int axis_index;
486     if (!get_design_axes ().lfind (tag, &axis_index)) return false;
487 
488     hb_array_t<const Offset16To<AxisValue>> axis_values = get_axis_value_offsets ();
489     for (unsigned int i = 0; i < axis_values.length; i++)
490     {
491       const AxisValue& axis_value = this+axis_values[i];
492       if (axis_value.get_axis_index () == axis_index)
493       {
494 	if (value)
495 	  *value = axis_value.get_value (axis_index);
496 	return true;
497       }
498     }
499     return false;
500   }
501 
get_design_axis_countOT::STAT502   unsigned get_design_axis_count () const { return designAxisCount; }
503 
get_axis_record_name_idOT::STAT504   hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const
505   {
506     if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID;
507     const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index];
508     return axis_record.get_name_id ();
509   }
510 
get_axis_value_countOT::STAT511   unsigned get_axis_value_count () const { return axisValueCount; }
512 
get_axis_value_name_idOT::STAT513   hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const
514   {
515     if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID;
516     const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]);
517     return axis_value.get_value_name_id ();
518   }
519 
collect_name_idsOT::STAT520   void collect_name_ids (hb_hashmap_t<hb_tag_t, float> *user_axes_location,
521                          hb_set_t *nameids_to_retain /* OUT */) const
522   {
523     if (!has_data ()) return;
524 
525     + get_design_axes ()
526     | hb_map (&StatAxisRecord::get_name_id)
527     | hb_sink (nameids_to_retain)
528     ;
529 
530     auto designAxes = get_design_axes ();
531 
532     + get_axis_value_offsets ()
533     | hb_map (hb_add (&(this + offsetToAxisValueOffsets)))
534     | hb_filter ([&] (const AxisValue& _)
535                  { return _.keep_axis_value (designAxes, user_axes_location); })
536     | hb_map (&AxisValue::get_value_name_id)
537     | hb_sink (nameids_to_retain)
538     ;
539   }
540 
subsetOT::STAT541   bool subset (hb_subset_context_t *c) const
542   {
543     TRACE_SUBSET (this);
544     STAT *out = c->serializer->embed (this);
545     if (unlikely (!out)) return_trace (false);
546 
547     auto designAxes = get_design_axes ();
548     for (unsigned i = 0; i < (unsigned)designAxisCount; i++)
549       if (unlikely (!c->serializer->embed (designAxes[i])))
550           return_trace (false);
551 
552     if (designAxisCount)
553       c->serializer->check_assign (out->designAxesOffset, this->get_size (),
554                                    HB_SERIALIZE_ERROR_INT_OVERFLOW);
555 
556     unsigned count = 0;
557     out->offsetToAxisValueOffsets.serialize_subset (c, offsetToAxisValueOffsets, this,
558                                                     axisValueCount, count, designAxes);
559     return_trace (c->serializer->check_assign (out->axisValueCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW));
560   }
561 
sanitizeOT::STAT562   bool sanitize (hb_sanitize_context_t *c) const
563   {
564     TRACE_SANITIZE (this);
565     return_trace (likely (c->check_struct (this) &&
566 			  version.major == 1 &&
567 			  version.minor > 0 &&
568 			  designAxesOffset.sanitize (c, this, designAxisCount) &&
569 			  offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets))));
570   }
571 
572   protected:
get_design_axesOT::STAT573   hb_array_t<const StatAxisRecord> const get_design_axes () const
574   { return (this+designAxesOffset).as_array (designAxisCount); }
575 
get_axis_value_offsetsOT::STAT576   hb_array_t<const Offset16To<AxisValue>> const get_axis_value_offsets () const
577   { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); }
578 
579 
580   protected:
581   FixedVersion<>version;	/* Version of the stat table
582 				 * initially set to 0x00010002u */
583   HBUINT16	designAxisSize;	/* The size in bytes of each axis record. */
584   HBUINT16	designAxisCount;/* The number of design axis records. In a
585 				 * font with an 'fvar' table, this value must be
586 				 * greater than or equal to the axisCount value
587 				 * in the 'fvar' table. In all fonts, must
588 				 * be greater than zero if axisValueCount
589 				 * is greater than zero. */
590   NNOffset32To<UnsizedArrayOf<StatAxisRecord>>
591 		designAxesOffset;
592 				/* Offset in bytes from the beginning of
593 				 * the STAT table to the start of the design
594 				 * axes array. If designAxisCount is zero,
595 				 * set to zero; if designAxisCount is greater
596 				 * than zero, must be greater than zero. */
597   HBUINT16	axisValueCount;	/* The number of axis value tables. */
598   NNOffset32To<AxisValueOffsetArray>
599 		offsetToAxisValueOffsets;
600 				/* Offset in bytes from the beginning of
601 				 * the STAT table to the start of the design
602 				 * axes value offsets array. If axisValueCount
603 				 * is zero, set to zero; if axisValueCount is
604 				 * greater than zero, must be greater than zero. */
605   NameID	elidedFallbackNameID;
606 				/* Name ID used as fallback when projection of
607 				 * names into a particular font model produces
608 				 * a subfamily name containing only elidable
609 				 * elements. */
610   public:
611   DEFINE_SIZE_STATIC (20);
612 };
613 
614 
615 } /* namespace OT */
616 
617 
618 #endif /* HB_OT_STAT_TABLE_HH */
619