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
38namespace OT {
39
40enum
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
60struct AxisValueFormat1
61{
62 unsigned int get_axis_index () const { return axisIndex; }
63 float get_value () const { return value.to_float (); }
64
65 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
66
67 bool sanitize (hb_sanitize_context_t *c) const
68 {
69 TRACE_SANITIZE (this);
70 return_trace (likely (c->check_struct (this)));
71 }
72
73 protected:
74 HBUINT16 format; /* Format identifier — set to 1. */
75 HBUINT16 axisIndex; /* Zero-base index into the axis record array
76 * identifying the axis of design variation
77 * to which the axis value record applies.
78 * Must be less than designAxisCount. */
79 HBUINT16 flags; /* Flags — see below for details. */
80 NameID valueNameID; /* The name ID for entries in the 'name' table
81 * that provide a display string for this
82 * attribute value. */
83 HBFixed value; /* A numeric value for this attribute value. */
84 public:
85 DEFINE_SIZE_STATIC (12);
86};
87
88struct AxisValueFormat2
89{
90 unsigned int get_axis_index () const { return axisIndex; }
91 float get_value () const { return nominalValue.to_float (); }
92
93 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
94
95 bool sanitize (hb_sanitize_context_t *c) const
96 {
97 TRACE_SANITIZE (this);
98 return_trace (likely (c->check_struct (this)));
99 }
100
101 protected:
102 HBUINT16 format; /* Format identifier — set to 2. */
103 HBUINT16 axisIndex; /* Zero-base index into the axis record array
104 * identifying the axis of design variation
105 * to which the axis value record applies.
106 * Must be less than designAxisCount. */
107 HBUINT16 flags; /* Flags — see below for details. */
108 NameID valueNameID; /* The name ID for entries in the 'name' table
109 * that provide a display string for this
110 * attribute value. */
111 HBFixed nominalValue; /* A numeric value for this attribute value. */
112 HBFixed rangeMinValue; /* The minimum value for a range associated
113 * with the specified name ID. */
114 HBFixed rangeMaxValue; /* The maximum value for a range associated
115 * with the specified name ID. */
116 public:
117 DEFINE_SIZE_STATIC (20);
118};
119
120struct AxisValueFormat3
121{
122 unsigned int get_axis_index () const { return axisIndex; }
123 float get_value () const { return value.to_float (); }
124
125 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
126
127 bool sanitize (hb_sanitize_context_t *c) const
128 {
129 TRACE_SANITIZE (this);
130 return_trace (likely (c->check_struct (this)));
131 }
132
133 protected:
134 HBUINT16 format; /* Format identifier — set to 3. */
135 HBUINT16 axisIndex; /* Zero-base index into the axis record array
136 * identifying the axis of design variation
137 * to which the axis value record applies.
138 * Must be less than designAxisCount. */
139 HBUINT16 flags; /* Flags — see below for details. */
140 NameID valueNameID; /* The name ID for entries in the 'name' table
141 * that provide a display string for this
142 * attribute value. */
143 HBFixed value; /* A numeric value for this attribute value. */
144 HBFixed linkedValue; /* The numeric value for a style-linked mapping
145 * from this value. */
146 public:
147 DEFINE_SIZE_STATIC (16);
148};
149
150struct AxisValueRecord
151{
152 unsigned int get_axis_index () const { return axisIndex; }
153 float get_value () const { return value.to_float (); }
154
155 bool sanitize (hb_sanitize_context_t *c) const
156 {
157 TRACE_SANITIZE (this);
158 return_trace (likely (c->check_struct (this)));
159 }
160
161 protected:
162 HBUINT16 axisIndex; /* Zero-base index into the axis record array
163 * identifying the axis to which this value
164 * applies. Must be less than designAxisCount. */
165 HBFixed value; /* A numeric value for this attribute value. */
166 public:
167 DEFINE_SIZE_STATIC (6);
168};
169
170struct AxisValueFormat4
171{
172 const AxisValueRecord &get_axis_record (unsigned int axis_index) const
173 { return axisValues.as_array (axisCount)[axis_index]; }
174
175 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
176
177 bool sanitize (hb_sanitize_context_t *c) const
178 {
179 TRACE_SANITIZE (this);
180 return_trace (likely (c->check_struct (this)));
181 }
182
183 protected:
184 HBUINT16 format; /* Format identifier — set to 4. */
185 HBUINT16 axisCount; /* The total number of axes contributing to
186 * this axis-values combination. */
187 HBUINT16 flags; /* Flags — see below for details. */
188 NameID valueNameID; /* The name ID for entries in the 'name' table
189 * that provide a display string for this
190 * attribute value. */
191 UnsizedArrayOf<AxisValueRecord>
192 axisValues; /* Array of AxisValue records that provide the
193 * combination of axis values, one for each
194 * contributing axis. */
195 public:
196 DEFINE_SIZE_ARRAY (8, axisValues);
197};
198
199struct AxisValue
200{
201 bool get_value (unsigned int axis_index) const
202 {
203 switch (u.format)
204 {
205 case 1: return u.format1.get_value ();
206 case 2: return u.format2.get_value ();
207 case 3: return u.format3.get_value ();
208 case 4: return u.format4.get_axis_record (axis_index).get_value ();
209 default:return 0;
210 }
211 }
212
213 unsigned int get_axis_index () const
214 {
215 switch (u.format)
216 {
217 case 1: return u.format1.get_axis_index ();
218 case 2: return u.format2.get_axis_index ();
219 case 3: return u.format3.get_axis_index ();
220 /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */
221 default:return -1;
222 }
223 }
224
225 hb_ot_name_id_t get_value_name_id () const
226 {
227 switch (u.format)
228 {
229 case 1: return u.format1.get_value_name_id ();
230 case 2: return u.format2.get_value_name_id ();
231 case 3: return u.format3.get_value_name_id ();
232 case 4: return u.format4.get_value_name_id ();
233 default:return HB_OT_NAME_ID_INVALID;
234 }
235 }
236
237 bool sanitize (hb_sanitize_context_t *c) const
238 {
239 TRACE_SANITIZE (this);
240 if (unlikely (!c->check_struct (this)))
241 return_trace (false);
242
243 switch (u.format)
244 {
245 case 1: return_trace (u.format1.sanitize (c));
246 case 2: return_trace (u.format2.sanitize (c));
247 case 3: return_trace (u.format3.sanitize (c));
248 case 4: return_trace (u.format4.sanitize (c));
249 default:return_trace (true);
250 }
251 }
252
253 protected:
254 union
255 {
256 HBUINT16 format;
257 AxisValueFormat1 format1;
258 AxisValueFormat2 format2;
259 AxisValueFormat3 format3;
260 AxisValueFormat4 format4;
261 } u;
262 public:
263 DEFINE_SIZE_UNION (2, format);
264};
265
266struct StatAxisRecord
267{
268 int cmp (hb_tag_t key) const { return tag.cmp (key); }
269
270 hb_ot_name_id_t get_name_id () const { return nameID; }
271
272 bool sanitize (hb_sanitize_context_t *c) const
273 {
274 TRACE_SANITIZE (this);
275 return_trace (likely (c->check_struct (this)));
276 }
277
278 protected:
279 Tag tag; /* A tag identifying the axis of design variation. */
280 NameID nameID; /* The name ID for entries in the 'name' table that
281 * provide a display string for this axis. */
282 HBUINT16 ordering; /* A value that applications can use to determine
283 * primary sorting of face names, or for ordering
284 * of descriptors when composing family or face names. */
285 public:
286 DEFINE_SIZE_STATIC (8);
287};
288
289struct STAT
290{
291 static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT;
292
293 bool has_data () const { return version.to_int (); }
294
295 bool get_value (hb_tag_t tag, float *value) const
296 {
297 unsigned int axis_index;
298 if (!get_design_axes ().lfind (tag, &axis_index)) return false;
299
300 hb_array_t<const OffsetTo<AxisValue>> axis_values = get_axis_value_offsets ();
301 for (unsigned int i = 0; i < axis_values.length; i++)
302 {
303 const AxisValue& axis_value = this+axis_values[i];
304 if (axis_value.get_axis_index () == axis_index)
305 {
306 if (value)
307 *value = axis_value.get_value (axis_index);
308 return true;
309 }
310 }
311 return false;
312 }
313
314 unsigned get_design_axis_count () const { return designAxisCount; }
315
316 hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const
317 {
318 if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID;
319 const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index];
320 return axis_record.get_name_id ();
321 }
322
323 unsigned get_axis_value_count () const { return axisValueCount; }
324
325 hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const
326 {
327 if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID;
328 const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]);
329 return axis_value.get_value_name_id ();
330 }
331
332 void collect_name_ids (hb_set_t *nameids_to_retain) const
333 {
334 if (!has_data ()) return;
335
336 + get_design_axes ()
337 | hb_map (&StatAxisRecord::get_name_id)
338 | hb_sink (nameids_to_retain)
339 ;
340
341 + get_axis_value_offsets ()
342 | hb_map (hb_add (&(this + offsetToAxisValueOffsets)))
343 | hb_map (&AxisValue::get_value_name_id)
344 | hb_sink (nameids_to_retain)
345 ;
346 }
347
348 bool sanitize (hb_sanitize_context_t *c) const
349 {
350 TRACE_SANITIZE (this);
351 return_trace (likely (c->check_struct (this) &&
352 version.major == 1 &&
353 version.minor > 0 &&
354 designAxesOffset.sanitize (c, this, designAxisCount) &&
355 offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets))));
356 }
357
358 protected:
359 hb_array_t<const StatAxisRecord> const get_design_axes () const
360 { return (this+designAxesOffset).as_array (designAxisCount); }
361
362 hb_array_t<const OffsetTo<AxisValue>> const get_axis_value_offsets () const
363 { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); }
364
365
366 protected:
367 FixedVersion<>version; /* Version of the stat table
368 * initially set to 0x00010002u */
369 HBUINT16 designAxisSize; /* The size in bytes of each axis record. */
370 HBUINT16 designAxisCount;/* The number of design axis records. In a
371 * font with an 'fvar' table, this value must be
372 * greater than or equal to the axisCount value
373 * in the 'fvar' table. In all fonts, must
374 * be greater than zero if axisValueCount
375 * is greater than zero. */
376 LNNOffsetTo<UnsizedArrayOf<StatAxisRecord>>
377 designAxesOffset;
378 /* Offset in bytes from the beginning of
379 * the STAT table to the start of the design
380 * axes array. If designAxisCount is zero,
381 * set to zero; if designAxisCount is greater
382 * than zero, must be greater than zero. */
383 HBUINT16 axisValueCount; /* The number of axis value tables. */
384 LNNOffsetTo<UnsizedArrayOf<OffsetTo<AxisValue>>>
385 offsetToAxisValueOffsets;
386 /* Offset in bytes from the beginning of
387 * the STAT table to the start of the design
388 * axes value offsets array. If axisValueCount
389 * is zero, set to zero; if axisValueCount is
390 * greater than zero, must be greater than zero. */
391 NameID elidedFallbackNameID;
392 /* Name ID used as fallback when projection of
393 * names into a particular font model produces
394 * a subfamily name containing only elidable
395 * elements. */
396 public:
397 DEFINE_SIZE_STATIC (20);
398};
399
400
401} /* namespace OT */
402
403
404#endif /* HB_OT_STAT_TABLE_HH */
405