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
axis_value_is_outside_axis_range(hb_tag_t axis_tag,float axis_value,const hb_hashmap_t<hb_tag_t,Triple> * user_axes_location)60 static bool axis_value_is_outside_axis_range (hb_tag_t axis_tag, float axis_value,
61 const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location)
62 {
63 if (!user_axes_location->has (axis_tag))
64 return false;
65
66 double axis_value_double = static_cast<double>(axis_value);
67 Triple axis_range = user_axes_location->get (axis_tag);
68 return (axis_value_double < axis_range.minimum || axis_value_double > axis_range.maximum);
69 }
70
71 struct StatAxisRecord
72 {
cmpOT::StatAxisRecord73 int cmp (hb_tag_t key) const { return tag.cmp (key); }
74
get_name_idOT::StatAxisRecord75 hb_ot_name_id_t get_name_id () const { return nameID; }
76
get_axis_tagOT::StatAxisRecord77 hb_tag_t get_axis_tag () const { return tag; }
78
sanitizeOT::StatAxisRecord79 bool sanitize (hb_sanitize_context_t *c) const
80 {
81 TRACE_SANITIZE (this);
82 return_trace (likely (c->check_struct (this)));
83 }
84
85 protected:
86 Tag tag; /* A tag identifying the axis of design variation. */
87 NameID nameID; /* The name ID for entries in the 'name' table that
88 * provide a display string for this axis. */
89 HBUINT16 ordering; /* A value that applications can use to determine
90 * primary sorting of face names, or for ordering
91 * of descriptors when composing family or face names. */
92 public:
93 DEFINE_SIZE_STATIC (8);
94 };
95
96 struct AxisValueFormat1
97 {
get_axis_indexOT::AxisValueFormat198 unsigned int get_axis_index () const { return axisIndex; }
get_valueOT::AxisValueFormat199 float get_value () const { return value.to_float (); }
100
get_value_name_idOT::AxisValueFormat1101 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
102
get_axis_tagOT::AxisValueFormat1103 hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
104 {
105 unsigned axis_idx = get_axis_index ();
106 return axis_records[axis_idx].get_axis_tag ();
107 }
108
keep_axis_valueOT::AxisValueFormat1109 bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
110 const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
111 {
112 hb_tag_t axis_tag = get_axis_tag (axis_records);
113 float axis_value = get_value ();
114
115 return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
116 }
117
subsetOT::AxisValueFormat1118 bool subset (hb_subset_context_t *c,
119 const hb_array_t<const StatAxisRecord> axis_records) const
120 {
121 TRACE_SUBSET (this);
122 const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
123
124 if (keep_axis_value (axis_records, user_axes_location))
125 return_trace (c->serializer->embed (this));
126
127 return_trace (false);
128 }
129
sanitizeOT::AxisValueFormat1130 bool sanitize (hb_sanitize_context_t *c) const
131 {
132 TRACE_SANITIZE (this);
133 return_trace (c->check_struct (this));
134 }
135
136 protected:
137 HBUINT16 format; /* Format identifier — set to 1. */
138 HBUINT16 axisIndex; /* Zero-base index into the axis record array
139 * identifying the axis of design variation
140 * to which the axis value record applies.
141 * Must be less than designAxisCount. */
142 HBUINT16 flags; /* Flags — see below for details. */
143 NameID valueNameID; /* The name ID for entries in the 'name' table
144 * that provide a display string for this
145 * attribute value. */
146 F16DOT16 value; /* A numeric value for this attribute value. */
147 public:
148 DEFINE_SIZE_STATIC (12);
149 };
150
151 struct AxisValueFormat2
152 {
get_axis_indexOT::AxisValueFormat2153 unsigned int get_axis_index () const { return axisIndex; }
get_valueOT::AxisValueFormat2154 float get_value () const { return nominalValue.to_float (); }
155
get_value_name_idOT::AxisValueFormat2156 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
157
get_axis_tagOT::AxisValueFormat2158 hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
159 {
160 unsigned axis_idx = get_axis_index ();
161 return axis_records[axis_idx].get_axis_tag ();
162 }
163
keep_axis_valueOT::AxisValueFormat2164 bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
165 const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
166 {
167 hb_tag_t axis_tag = get_axis_tag (axis_records);
168 float axis_value = get_value ();
169
170 return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
171 }
172
subsetOT::AxisValueFormat2173 bool subset (hb_subset_context_t *c,
174 const hb_array_t<const StatAxisRecord> axis_records) const
175 {
176 TRACE_SUBSET (this);
177 const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
178
179 if (keep_axis_value (axis_records, user_axes_location))
180 return_trace (c->serializer->embed (this));
181
182 return_trace (false);
183 }
184
sanitizeOT::AxisValueFormat2185 bool sanitize (hb_sanitize_context_t *c) const
186 {
187 TRACE_SANITIZE (this);
188 return_trace (c->check_struct (this));
189 }
190
191 protected:
192 HBUINT16 format; /* Format identifier — set to 2. */
193 HBUINT16 axisIndex; /* Zero-base index into the axis record array
194 * identifying the axis of design variation
195 * to which the axis value record applies.
196 * Must be less than designAxisCount. */
197 HBUINT16 flags; /* Flags — see below for details. */
198 NameID valueNameID; /* The name ID for entries in the 'name' table
199 * that provide a display string for this
200 * attribute value. */
201 F16DOT16 nominalValue; /* A numeric value for this attribute value. */
202 F16DOT16 rangeMinValue; /* The minimum value for a range associated
203 * with the specified name ID. */
204 F16DOT16 rangeMaxValue; /* The maximum value for a range associated
205 * with the specified name ID. */
206 public:
207 DEFINE_SIZE_STATIC (20);
208 };
209
210 struct AxisValueFormat3
211 {
get_axis_indexOT::AxisValueFormat3212 unsigned int get_axis_index () const { return axisIndex; }
get_valueOT::AxisValueFormat3213 float get_value () const { return value.to_float (); }
214
get_value_name_idOT::AxisValueFormat3215 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
216
get_axis_tagOT::AxisValueFormat3217 hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
218 {
219 unsigned axis_idx = get_axis_index ();
220 return axis_records[axis_idx].get_axis_tag ();
221 }
222
keep_axis_valueOT::AxisValueFormat3223 bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
224 const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
225 {
226 hb_tag_t axis_tag = get_axis_tag (axis_records);
227 float axis_value = get_value ();
228
229 return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
230 }
231
subsetOT::AxisValueFormat3232 bool subset (hb_subset_context_t *c,
233 const hb_array_t<const StatAxisRecord> axis_records) const
234 {
235 TRACE_SUBSET (this);
236 const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
237
238 if (keep_axis_value (axis_records, user_axes_location))
239 return_trace (c->serializer->embed (this));
240
241 return_trace (false);
242 }
243
sanitizeOT::AxisValueFormat3244 bool sanitize (hb_sanitize_context_t *c) const
245 {
246 TRACE_SANITIZE (this);
247 return_trace (c->check_struct (this));
248 }
249
250 protected:
251 HBUINT16 format; /* Format identifier — set to 3. */
252 HBUINT16 axisIndex; /* Zero-base index into the axis record array
253 * identifying the axis of design variation
254 * to which the axis value record applies.
255 * Must be less than designAxisCount. */
256 HBUINT16 flags; /* Flags — see below for details. */
257 NameID valueNameID; /* The name ID for entries in the 'name' table
258 * that provide a display string for this
259 * attribute value. */
260 F16DOT16 value; /* A numeric value for this attribute value. */
261 F16DOT16 linkedValue; /* The numeric value for a style-linked mapping
262 * from this value. */
263 public:
264 DEFINE_SIZE_STATIC (16);
265 };
266
267 struct AxisValueRecord
268 {
get_axis_indexOT::AxisValueRecord269 unsigned int get_axis_index () const { return axisIndex; }
get_valueOT::AxisValueRecord270 float get_value () const { return value.to_float (); }
271
sanitizeOT::AxisValueRecord272 bool sanitize (hb_sanitize_context_t *c) const
273 {
274 TRACE_SANITIZE (this);
275 return_trace (c->check_struct (this));
276 }
277
278 protected:
279 HBUINT16 axisIndex; /* Zero-base index into the axis record array
280 * identifying the axis to which this value
281 * applies. Must be less than designAxisCount. */
282 F16DOT16 value; /* A numeric value for this attribute value. */
283 public:
284 DEFINE_SIZE_STATIC (6);
285 };
286
287 struct AxisValueFormat4
288 {
get_axis_recordOT::AxisValueFormat4289 const AxisValueRecord &get_axis_record (unsigned int axis_index) const
290 { return axisValues.as_array (axisCount)[axis_index]; }
291
keep_axis_valueOT::AxisValueFormat4292 bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
293 const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
294 {
295 hb_array_t<const AxisValueRecord> axis_value_records = axisValues.as_array (axisCount);
296
297 for (const auto& rec : axis_value_records)
298 {
299 unsigned axis_idx = rec.get_axis_index ();
300 float axis_value = rec.get_value ();
301 hb_tag_t axis_tag = axis_records[axis_idx].get_axis_tag ();
302
303 if (axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location))
304 return false;
305 }
306
307 return true;
308 }
309
subsetOT::AxisValueFormat4310 bool subset (hb_subset_context_t *c,
311 const hb_array_t<const StatAxisRecord> axis_records) const
312 {
313 TRACE_SUBSET (this);
314 const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location = &c->plan->user_axes_location;
315 if (!keep_axis_value (axis_records, user_axes_location))
316 return_trace (false);
317
318 unsigned total_size = min_size + axisCount * AxisValueRecord::static_size;
319 auto *out = c->serializer->allocate_size<AxisValueFormat4> (total_size);
320 if (unlikely (!out)) return_trace (false);
321 hb_memcpy (out, this, total_size);
322 return_trace (true);
323 }
324
get_value_name_idOT::AxisValueFormat4325 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
326
sanitizeOT::AxisValueFormat4327 bool sanitize (hb_sanitize_context_t *c) const
328 {
329 TRACE_SANITIZE (this);
330 return_trace (likely (c->check_struct (this) &&
331 hb_barrier () &&
332 axisValues.sanitize (c, axisCount)));
333 }
334
335 protected:
336 HBUINT16 format; /* Format identifier — set to 4. */
337 HBUINT16 axisCount; /* The total number of axes contributing to
338 * this axis-values combination. */
339 HBUINT16 flags; /* Flags — see below for details. */
340 NameID valueNameID; /* The name ID for entries in the 'name' table
341 * that provide a display string for this
342 * attribute value. */
343 UnsizedArrayOf<AxisValueRecord>
344 axisValues; /* Array of AxisValue records that provide the
345 * combination of axis values, one for each
346 * contributing axis. */
347 public:
348 DEFINE_SIZE_ARRAY (8, axisValues);
349 };
350
351 struct AxisValue
352 {
get_valueOT::AxisValue353 float get_value (unsigned int axis_index) const
354 {
355 switch (u.format)
356 {
357 case 1: hb_barrier (); return u.format1.get_value ();
358 case 2: hb_barrier (); return u.format2.get_value ();
359 case 3: hb_barrier (); return u.format3.get_value ();
360 case 4: hb_barrier (); return u.format4.get_axis_record (axis_index).get_value ();
361 default:return 0.f;
362 }
363 }
364
get_axis_indexOT::AxisValue365 unsigned int get_axis_index () const
366 {
367 switch (u.format)
368 {
369 case 1: hb_barrier (); return u.format1.get_axis_index ();
370 case 2: hb_barrier (); return u.format2.get_axis_index ();
371 case 3: hb_barrier (); return u.format3.get_axis_index ();
372 /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */
373 default:return -1;
374 }
375 }
376
get_value_name_idOT::AxisValue377 hb_ot_name_id_t get_value_name_id () const
378 {
379 switch (u.format)
380 {
381 case 1: hb_barrier (); return u.format1.get_value_name_id ();
382 case 2: hb_barrier (); return u.format2.get_value_name_id ();
383 case 3: hb_barrier (); return u.format3.get_value_name_id ();
384 case 4: hb_barrier (); return u.format4.get_value_name_id ();
385 default:return HB_OT_NAME_ID_INVALID;
386 }
387 }
388
389 template <typename context_t, typename ...Ts>
dispatchOT::AxisValue390 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
391 {
392 if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
393 TRACE_DISPATCH (this, u.format);
394 switch (u.format) {
395 case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
396 case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
397 case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
398 case 4: hb_barrier (); return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
399 default:return_trace (c->default_return_value ());
400 }
401 }
402
keep_axis_valueOT::AxisValue403 bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
404 hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
405 {
406 switch (u.format)
407 {
408 case 1: hb_barrier (); return u.format1.keep_axis_value (axis_records, user_axes_location);
409 case 2: hb_barrier (); return u.format2.keep_axis_value (axis_records, user_axes_location);
410 case 3: hb_barrier (); return u.format3.keep_axis_value (axis_records, user_axes_location);
411 case 4: hb_barrier (); return u.format4.keep_axis_value (axis_records, user_axes_location);
412 default:return false;
413 }
414 }
415
sanitizeOT::AxisValue416 bool sanitize (hb_sanitize_context_t *c) const
417 {
418 TRACE_SANITIZE (this);
419 if (unlikely (!c->check_struct (this)))
420 return_trace (false);
421 hb_barrier ();
422
423 switch (u.format)
424 {
425 case 1: hb_barrier (); return_trace (u.format1.sanitize (c));
426 case 2: hb_barrier (); return_trace (u.format2.sanitize (c));
427 case 3: hb_barrier (); return_trace (u.format3.sanitize (c));
428 case 4: hb_barrier (); 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
455 auto axisValueOffsets = as_array (axisValueCount);
456 count = 0;
457 for (const auto& offset : axisValueOffsets)
458 {
459 if (!offset) continue;
460 auto o_snap = c->serializer->snapshot ();
461 auto *o = c->serializer->embed (offset);
462 if (!o) return_trace (false);
463 if (!o->serialize_subset (c, offset, this, axis_records))
464 {
465 c->serializer->revert (o_snap);
466 continue;
467 }
468 count++;
469 }
470
471 return_trace (count);
472 }
473 };
474
475 struct STAT
476 {
477 static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT;
478
has_dataOT::STAT479 bool has_data () const { return version.to_int (); }
480
get_valueOT::STAT481 bool get_value (hb_tag_t tag, float *value) const
482 {
483 unsigned int axis_index;
484 if (!get_design_axes ().lfind (tag, &axis_index)) return false;
485
486 hb_array_t<const Offset16To<AxisValue>> axis_values = get_axis_value_offsets ();
487 for (unsigned int i = 0; i < axis_values.length; i++)
488 {
489 const AxisValue& axis_value = this+offsetToAxisValueOffsets+axis_values[i];
490 if (axis_value.get_axis_index () == axis_index)
491 {
492 if (value)
493 *value = axis_value.get_value (axis_index);
494 return true;
495 }
496 }
497 return false;
498 }
499
get_design_axis_countOT::STAT500 unsigned get_design_axis_count () const { return designAxisCount; }
501
get_axis_record_name_idOT::STAT502 hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const
503 {
504 if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID;
505 const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index];
506 return axis_record.get_name_id ();
507 }
508
get_axis_value_countOT::STAT509 unsigned get_axis_value_count () const { return axisValueCount; }
510
get_axis_value_name_idOT::STAT511 hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const
512 {
513 if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID;
514 const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]);
515 return axis_value.get_value_name_id ();
516 }
517
collect_name_idsOT::STAT518 void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location,
519 hb_set_t *nameids_to_retain /* OUT */) const
520 {
521 if (!has_data ()) return;
522
523 + get_design_axes ()
524 | hb_map (&StatAxisRecord::get_name_id)
525 | hb_sink (nameids_to_retain)
526 ;
527
528 auto designAxes = get_design_axes ();
529
530 + get_axis_value_offsets ()
531 | hb_map (hb_add (&(this + offsetToAxisValueOffsets)))
532 | hb_filter ([&] (const AxisValue& _)
533 { return _.keep_axis_value (designAxes, user_axes_location); })
534 | hb_map (&AxisValue::get_value_name_id)
535 | hb_sink (nameids_to_retain)
536 ;
537
538 nameids_to_retain->add (elidedFallbackNameID);
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 hb_barrier () &&
567 version.major == 1 &&
568 version.minor > 0 &&
569 designAxesOffset.sanitize (c, this, designAxisCount) &&
570 offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets))));
571 }
572
573 protected:
get_design_axesOT::STAT574 hb_array_t<const StatAxisRecord> const get_design_axes () const
575 { return (this+designAxesOffset).as_array (designAxisCount); }
576
get_axis_value_offsetsOT::STAT577 hb_array_t<const Offset16To<AxisValue>> const get_axis_value_offsets () const
578 { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); }
579
580
581 protected:
582 FixedVersion<>version; /* Version of the stat table
583 * initially set to 0x00010002u */
584 HBUINT16 designAxisSize; /* The size in bytes of each axis record. */
585 HBUINT16 designAxisCount;/* The number of design axis records. In a
586 * font with an 'fvar' table, this value must be
587 * greater than or equal to the axisCount value
588 * in the 'fvar' table. In all fonts, must
589 * be greater than zero if axisValueCount
590 * is greater than zero. */
591 NNOffset32To<UnsizedArrayOf<StatAxisRecord>>
592 designAxesOffset;
593 /* Offset in bytes from the beginning of
594 * the STAT table to the start of the design
595 * axes array. If designAxisCount is zero,
596 * set to zero; if designAxisCount is greater
597 * than zero, must be greater than zero. */
598 HBUINT16 axisValueCount; /* The number of axis value tables. */
599 NNOffset32To<AxisValueOffsetArray>
600 offsetToAxisValueOffsets;
601 /* Offset in bytes from the beginning of
602 * the STAT table to the start of the design
603 * axes value offsets array. If axisValueCount
604 * is zero, set to zero; if axisValueCount is
605 * greater than zero, must be greater than zero. */
606 NameID elidedFallbackNameID;
607 /* Name ID used as fallback when projection of
608 * names into a particular font model produces
609 * a subfamily name containing only elidable
610 * elements. */
611 public:
612 DEFINE_SIZE_STATIC (20);
613 };
614
615
616 } /* namespace OT */
617
618
619 #endif /* HB_OT_STAT_TABLE_HH */
620