| /* | |
| * Copyright © 2018 Ebrahim Byagowi | |
| * | |
| * 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. | |
| */ | |
| #ifndef HB_OT_STAT_TABLE_HH | |
| #define HB_OT_STAT_TABLE_HH | |
| #include "hb-open-type.hh" | |
| #include "hb-ot-layout-common.hh" | |
| /* | |
| * STAT -- Style Attributes | |
| * https://docs.microsoft.com/en-us/typography/opentype/spec/stat | |
| */ | |
| #define HB_OT_TAG_STAT HB_TAG('S','T','A','T') | |
| namespace OT { | |
| enum | |
| { | |
| OLDER_SIBLING_FONT_ATTRIBUTE = 0x0001, /* If set, this axis value table | |
| * provides axis value information | |
| * that is applicable to other fonts | |
| * within the same font family. This | |
| * is used if the other fonts were | |
| * released earlier and did not include | |
| * information about values for some axis. | |
| * If newer versions of the other | |
| * fonts include the information | |
| * themselves and are present, | |
| * then this record is ignored. */ | |
| ELIDABLE_AXIS_VALUE_NAME = 0x0002 /* If set, it indicates that the axis | |
| * value represents the “normal” value | |
| * for the axis and may be omitted when | |
| * composing name strings. */ | |
| // Reserved = 0xFFFC /* Reserved for future use — set to zero. */ | |
| }; | |
| static bool 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) | |
| { | |
| if (!user_axes_location->has (axis_tag)) | |
| return false; | |
| double axis_value_double = static_cast<double>(axis_value); | |
| Triple axis_range = user_axes_location->get (axis_tag); | |
| return (axis_value_double < axis_range.minimum || axis_value_double > axis_range.maximum); | |
| } | |
| struct StatAxisRecord | |
| { | |
| int cmp (hb_tag_t key) const { return tag.cmp (key); } | |
| hb_ot_name_id_t get_name_id () const { return nameID; } | |
| hb_tag_t get_axis_tag () const { return tag; } | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| return_trace (likely (c->check_struct (this))); | |
| } | |
| protected: | |
| Tag tag; /* A tag identifying the axis of design variation. */ | |
| NameID nameID; /* The name ID for entries in the 'name' table that | |
| * provide a display string for this axis. */ | |
| HBUINT16 ordering; /* A value that applications can use to determine | |
| * primary sorting of face names, or for ordering | |
| * of descriptors when composing family or face names. */ | |
| public: | |
| DEFINE_SIZE_STATIC (8); | |
| }; | |
| struct AxisValueFormat1 | |
| { | |
| unsigned int get_axis_index () const { return axisIndex; } | |
| float get_value () const { return value.to_float (); } | |
| hb_ot_name_id_t get_value_name_id () const { return valueNameID; } | |
| hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const | |
| { | |
| unsigned axis_idx = get_axis_index (); | |
| return axis_records[axis_idx].get_axis_tag (); | |
| } | |
| bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, | |
| const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const | |
| { | |
| hb_tag_t axis_tag = get_axis_tag (axis_records); | |
| float axis_value = get_value (); | |
| return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location); | |
| } | |
| bool subset (hb_subset_context_t *c, | |
| const hb_array_t<const StatAxisRecord> axis_records) const | |
| { | |
| TRACE_SUBSET (this); | |
| const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location; | |
| if (keep_axis_value (axis_records, user_axes_location)) | |
| return_trace (c->serializer->embed (this)); | |
| return_trace (false); | |
| } | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| return_trace (c->check_struct (this)); | |
| } | |
| protected: | |
| HBUINT16 format; /* Format identifier — set to 1. */ | |
| HBUINT16 axisIndex; /* Zero-base index into the axis record array | |
| * identifying the axis of design variation | |
| * to which the axis value record applies. | |
| * Must be less than designAxisCount. */ | |
| HBUINT16 flags; /* Flags — see below for details. */ | |
| NameID valueNameID; /* The name ID for entries in the 'name' table | |
| * that provide a display string for this | |
| * attribute value. */ | |
| F16DOT16 value; /* A numeric value for this attribute value. */ | |
| public: | |
| DEFINE_SIZE_STATIC (12); | |
| }; | |
| struct AxisValueFormat2 | |
| { | |
| unsigned int get_axis_index () const { return axisIndex; } | |
| float get_value () const { return nominalValue.to_float (); } | |
| hb_ot_name_id_t get_value_name_id () const { return valueNameID; } | |
| hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const | |
| { | |
| unsigned axis_idx = get_axis_index (); | |
| return axis_records[axis_idx].get_axis_tag (); | |
| } | |
| bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, | |
| const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const | |
| { | |
| hb_tag_t axis_tag = get_axis_tag (axis_records); | |
| float axis_value = get_value (); | |
| return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location); | |
| } | |
| bool subset (hb_subset_context_t *c, | |
| const hb_array_t<const StatAxisRecord> axis_records) const | |
| { | |
| TRACE_SUBSET (this); | |
| const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location; | |
| if (keep_axis_value (axis_records, user_axes_location)) | |
| return_trace (c->serializer->embed (this)); | |
| return_trace (false); | |
| } | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| return_trace (c->check_struct (this)); | |
| } | |
| protected: | |
| HBUINT16 format; /* Format identifier — set to 2. */ | |
| HBUINT16 axisIndex; /* Zero-base index into the axis record array | |
| * identifying the axis of design variation | |
| * to which the axis value record applies. | |
| * Must be less than designAxisCount. */ | |
| HBUINT16 flags; /* Flags — see below for details. */ | |
| NameID valueNameID; /* The name ID for entries in the 'name' table | |
| * that provide a display string for this | |
| * attribute value. */ | |
| F16DOT16 nominalValue; /* A numeric value for this attribute value. */ | |
| F16DOT16 rangeMinValue; /* The minimum value for a range associated | |
| * with the specified name ID. */ | |
| F16DOT16 rangeMaxValue; /* The maximum value for a range associated | |
| * with the specified name ID. */ | |
| public: | |
| DEFINE_SIZE_STATIC (20); | |
| }; | |
| struct AxisValueFormat3 | |
| { | |
| unsigned int get_axis_index () const { return axisIndex; } | |
| float get_value () const { return value.to_float (); } | |
| hb_ot_name_id_t get_value_name_id () const { return valueNameID; } | |
| hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const | |
| { | |
| unsigned axis_idx = get_axis_index (); | |
| return axis_records[axis_idx].get_axis_tag (); | |
| } | |
| bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, | |
| const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const | |
| { | |
| hb_tag_t axis_tag = get_axis_tag (axis_records); | |
| float axis_value = get_value (); | |
| return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location); | |
| } | |
| bool subset (hb_subset_context_t *c, | |
| const hb_array_t<const StatAxisRecord> axis_records) const | |
| { | |
| TRACE_SUBSET (this); | |
| const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location; | |
| if (keep_axis_value (axis_records, user_axes_location)) | |
| return_trace (c->serializer->embed (this)); | |
| return_trace (false); | |
| } | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| return_trace (c->check_struct (this)); | |
| } | |
| protected: | |
| HBUINT16 format; /* Format identifier — set to 3. */ | |
| HBUINT16 axisIndex; /* Zero-base index into the axis record array | |
| * identifying the axis of design variation | |
| * to which the axis value record applies. | |
| * Must be less than designAxisCount. */ | |
| HBUINT16 flags; /* Flags — see below for details. */ | |
| NameID valueNameID; /* The name ID for entries in the 'name' table | |
| * that provide a display string for this | |
| * attribute value. */ | |
| F16DOT16 value; /* A numeric value for this attribute value. */ | |
| F16DOT16 linkedValue; /* The numeric value for a style-linked mapping | |
| * from this value. */ | |
| public: | |
| DEFINE_SIZE_STATIC (16); | |
| }; | |
| struct AxisValueRecord | |
| { | |
| unsigned int get_axis_index () const { return axisIndex; } | |
| float get_value () const { return value.to_float (); } | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| return_trace (c->check_struct (this)); | |
| } | |
| protected: | |
| HBUINT16 axisIndex; /* Zero-base index into the axis record array | |
| * identifying the axis to which this value | |
| * applies. Must be less than designAxisCount. */ | |
| F16DOT16 value; /* A numeric value for this attribute value. */ | |
| public: | |
| DEFINE_SIZE_STATIC (6); | |
| }; | |
| struct AxisValueFormat4 | |
| { | |
| const AxisValueRecord &get_axis_record (unsigned int axis_index) const | |
| { return axisValues.as_array (axisCount)[axis_index]; } | |
| bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, | |
| const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const | |
| { | |
| hb_array_t<const AxisValueRecord> axis_value_records = axisValues.as_array (axisCount); | |
| for (const auto& rec : axis_value_records) | |
| { | |
| unsigned axis_idx = rec.get_axis_index (); | |
| float axis_value = rec.get_value (); | |
| hb_tag_t axis_tag = axis_records[axis_idx].get_axis_tag (); | |
| if (axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool subset (hb_subset_context_t *c, | |
| const hb_array_t<const StatAxisRecord> axis_records) const | |
| { | |
| TRACE_SUBSET (this); | |
| const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location = &c->plan->user_axes_location; | |
| if (!keep_axis_value (axis_records, user_axes_location)) | |
| return_trace (false); | |
| unsigned total_size = min_size + axisCount * AxisValueRecord::static_size; | |
| auto *out = c->serializer->allocate_size<AxisValueFormat4> (total_size); | |
| if (unlikely (!out)) return_trace (false); | |
| hb_memcpy (out, this, total_size); | |
| return_trace (true); | |
| } | |
| hb_ot_name_id_t get_value_name_id () const { return valueNameID; } | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| return_trace (likely (c->check_struct (this) && | |
| hb_barrier () && | |
| axisValues.sanitize (c, axisCount))); | |
| } | |
| protected: | |
| HBUINT16 format; /* Format identifier — set to 4. */ | |
| HBUINT16 axisCount; /* The total number of axes contributing to | |
| * this axis-values combination. */ | |
| HBUINT16 flags; /* Flags — see below for details. */ | |
| NameID valueNameID; /* The name ID for entries in the 'name' table | |
| * that provide a display string for this | |
| * attribute value. */ | |
| UnsizedArrayOf<AxisValueRecord> | |
| axisValues; /* Array of AxisValue records that provide the | |
| * combination of axis values, one for each | |
| * contributing axis. */ | |
| public: | |
| DEFINE_SIZE_ARRAY (8, axisValues); | |
| }; | |
| struct AxisValue | |
| { | |
| float get_value (unsigned int axis_index) const | |
| { | |
| switch (u.format.v) | |
| { | |
| case 1: hb_barrier (); return u.format1.get_value (); | |
| case 2: hb_barrier (); return u.format2.get_value (); | |
| case 3: hb_barrier (); return u.format3.get_value (); | |
| case 4: hb_barrier (); return u.format4.get_axis_record (axis_index).get_value (); | |
| default:return 0.f; | |
| } | |
| } | |
| unsigned int get_axis_index () const | |
| { | |
| switch (u.format.v) | |
| { | |
| case 1: hb_barrier (); return u.format1.get_axis_index (); | |
| case 2: hb_barrier (); return u.format2.get_axis_index (); | |
| case 3: hb_barrier (); return u.format3.get_axis_index (); | |
| /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */ | |
| default:return -1; | |
| } | |
| } | |
| hb_ot_name_id_t get_value_name_id () const | |
| { | |
| switch (u.format.v) | |
| { | |
| case 1: hb_barrier (); return u.format1.get_value_name_id (); | |
| case 2: hb_barrier (); return u.format2.get_value_name_id (); | |
| case 3: hb_barrier (); return u.format3.get_value_name_id (); | |
| case 4: hb_barrier (); return u.format4.get_value_name_id (); | |
| default:return HB_OT_NAME_ID_INVALID; | |
| } | |
| } | |
| template <typename context_t, typename ...Ts> | |
| typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const | |
| { | |
| if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); | |
| TRACE_DISPATCH (this, u.format.v); | |
| switch (u.format.v) { | |
| case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); | |
| case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); | |
| case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); | |
| case 4: hb_barrier (); return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); | |
| default:return_trace (c->default_return_value ()); | |
| } | |
| } | |
| bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, | |
| hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const | |
| { | |
| switch (u.format.v) | |
| { | |
| case 1: hb_barrier (); return u.format1.keep_axis_value (axis_records, user_axes_location); | |
| case 2: hb_barrier (); return u.format2.keep_axis_value (axis_records, user_axes_location); | |
| case 3: hb_barrier (); return u.format3.keep_axis_value (axis_records, user_axes_location); | |
| case 4: hb_barrier (); return u.format4.keep_axis_value (axis_records, user_axes_location); | |
| default:return false; | |
| } | |
| } | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| if (unlikely (!c->check_struct (this))) | |
| return_trace (false); | |
| hb_barrier (); | |
| switch (u.format.v) | |
| { | |
| case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); | |
| case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); | |
| case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); | |
| case 4: hb_barrier (); return_trace (u.format4.sanitize (c)); | |
| default:return_trace (true); | |
| } | |
| } | |
| protected: | |
| union | |
| { | |
| struct { HBUINT16 v; } format; | |
| AxisValueFormat1 format1; | |
| AxisValueFormat2 format2; | |
| AxisValueFormat3 format3; | |
| AxisValueFormat4 format4; | |
| } u; | |
| public: | |
| DEFINE_SIZE_UNION (2, format.v); | |
| }; | |
| struct AxisValueOffsetArray: UnsizedArrayOf<Offset16To<AxisValue>> | |
| { | |
| bool subset (hb_subset_context_t *c, | |
| unsigned axisValueCount, | |
| unsigned& count, | |
| const hb_array_t<const StatAxisRecord> axis_records) const | |
| { | |
| TRACE_SUBSET (this); | |
| auto axisValueOffsets = as_array (axisValueCount); | |
| count = 0; | |
| for (const auto& offset : axisValueOffsets) | |
| { | |
| if (!offset) continue; | |
| auto o_snap = c->serializer->snapshot (); | |
| auto *o = c->serializer->embed (offset); | |
| if (!o) return_trace (false); | |
| if (!o->serialize_subset (c, offset, this, axis_records)) | |
| { | |
| c->serializer->revert (o_snap); | |
| continue; | |
| } | |
| count++; | |
| } | |
| return_trace (count); | |
| } | |
| }; | |
| struct STAT | |
| { | |
| static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT; | |
| bool has_data () const { return version.to_int (); } | |
| bool get_value (hb_tag_t tag, float *value) const | |
| { | |
| unsigned int axis_index; | |
| if (!get_design_axes ().lfind (tag, &axis_index)) return false; | |
| hb_array_t<const Offset16To<AxisValue>> axis_values = get_axis_value_offsets (); | |
| for (unsigned int i = 0; i < axis_values.length; i++) | |
| { | |
| const AxisValue& axis_value = this+offsetToAxisValueOffsets+axis_values[i]; | |
| if (axis_value.get_axis_index () == axis_index) | |
| { | |
| if (value) | |
| *value = axis_value.get_value (axis_index); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| unsigned get_design_axis_count () const { return designAxisCount; } | |
| hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const | |
| { | |
| if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID; | |
| const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index]; | |
| return axis_record.get_name_id (); | |
| } | |
| unsigned get_axis_value_count () const { return axisValueCount; } | |
| hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const | |
| { | |
| if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID; | |
| const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]); | |
| return axis_value.get_value_name_id (); | |
| } | |
| void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location, | |
| hb_set_t *nameids_to_retain /* OUT */) const | |
| { | |
| if (!has_data ()) return; | |
| + get_design_axes () | |
| | hb_map (&StatAxisRecord::get_name_id) | |
| | hb_sink (nameids_to_retain) | |
| ; | |
| auto designAxes = get_design_axes (); | |
| + get_axis_value_offsets () | |
| | hb_map (hb_add (&(this + offsetToAxisValueOffsets))) | |
| | hb_filter ([&] (const AxisValue& _) | |
| { return _.keep_axis_value (designAxes, user_axes_location); }) | |
| | hb_map (&AxisValue::get_value_name_id) | |
| | hb_sink (nameids_to_retain) | |
| ; | |
| nameids_to_retain->add (elidedFallbackNameID); | |
| } | |
| bool subset (hb_subset_context_t *c) const | |
| { | |
| TRACE_SUBSET (this); | |
| STAT *out = c->serializer->embed (this); | |
| if (unlikely (!out)) return_trace (false); | |
| auto designAxes = get_design_axes (); | |
| for (unsigned i = 0; i < (unsigned)designAxisCount; i++) | |
| if (unlikely (!c->serializer->embed (designAxes[i]))) | |
| return_trace (false); | |
| if (designAxisCount) | |
| c->serializer->check_assign (out->designAxesOffset, this->get_size (), | |
| HB_SERIALIZE_ERROR_INT_OVERFLOW); | |
| unsigned count = 0; | |
| out->offsetToAxisValueOffsets.serialize_subset (c, offsetToAxisValueOffsets, this, | |
| axisValueCount, count, designAxes); | |
| return_trace (c->serializer->check_assign (out->axisValueCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); | |
| } | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| return_trace (likely (c->check_struct (this) && | |
| hb_barrier () && | |
| version.major == 1 && | |
| version.minor > 0 && | |
| designAxesOffset.sanitize (c, this, designAxisCount) && | |
| offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets)))); | |
| } | |
| protected: | |
| hb_array_t<const StatAxisRecord> const get_design_axes () const | |
| { return (this+designAxesOffset).as_array (designAxisCount); } | |
| hb_array_t<const Offset16To<AxisValue>> const get_axis_value_offsets () const | |
| { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); } | |
| protected: | |
| FixedVersion<>version; /* Version of the stat table | |
| * initially set to 0x00010002u */ | |
| HBUINT16 designAxisSize; /* The size in bytes of each axis record. */ | |
| HBUINT16 designAxisCount;/* The number of design axis records. In a | |
| * font with an 'fvar' table, this value must be | |
| * greater than or equal to the axisCount value | |
| * in the 'fvar' table. In all fonts, must | |
| * be greater than zero if axisValueCount | |
| * is greater than zero. */ | |
| NNOffset32To<UnsizedArrayOf<StatAxisRecord>> | |
| designAxesOffset; | |
| /* Offset in bytes from the beginning of | |
| * the STAT table to the start of the design | |
| * axes array. If designAxisCount is zero, | |
| * set to zero; if designAxisCount is greater | |
| * than zero, must be greater than zero. */ | |
| HBUINT16 axisValueCount; /* The number of axis value tables. */ | |
| NNOffset32To<AxisValueOffsetArray> | |
| offsetToAxisValueOffsets; | |
| /* Offset in bytes from the beginning of | |
| * the STAT table to the start of the design | |
| * axes value offsets array. If axisValueCount | |
| * is zero, set to zero; if axisValueCount is | |
| * greater than zero, must be greater than zero. */ | |
| NameID elidedFallbackNameID; | |
| /* Name ID used as fallback when projection of | |
| * names into a particular font model produces | |
| * a subfamily name containing only elidable | |
| * elements. */ | |
| public: | |
| DEFINE_SIZE_STATIC (20); | |
| }; | |
| } /* namespace OT */ | |
| #endif /* HB_OT_STAT_TABLE_HH */ |