comparison mupdf-source/thirdparty/harfbuzz/src/hb-ot-stat-table.hh @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 {
62 int cmp (hb_tag_t key) const { return tag.cmp (key); }
63
64 hb_ot_name_id_t get_name_id () const { return nameID; }
65
66 hb_tag_t get_axis_tag () const { return tag; }
67
68 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 {
87 unsigned int get_axis_index () const { return axisIndex; }
88 float get_value () const { return value.to_float (); }
89
90 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
91
92 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
98 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
111 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
123 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 {
146 unsigned int get_axis_index () const { return axisIndex; }
147 float get_value () const { return nominalValue.to_float (); }
148
149 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
150
151 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
157 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
170 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
182 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 {
209 unsigned int get_axis_index () const { return axisIndex; }
210 float get_value () const { return value.to_float (); }
211
212 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
213
214 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
220 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
233 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
245 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 {
270 unsigned int get_axis_index () const { return axisIndex; }
271 float get_value () const { return value.to_float (); }
272
273 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 {
290 const AxisValueRecord &get_axis_record (unsigned int axis_index) const
291 { return axisValues.as_array (axisCount)[axis_index]; }
292
293 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
312 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
327 hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
328
329 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 {
354 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
366 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
378 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>
391 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
404 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
417 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 {
448 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
481 bool has_data () const { return version.to_int (); }
482
483 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
502 unsigned get_design_axis_count () const { return designAxisCount; }
503
504 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
511 unsigned get_axis_value_count () const { return axisValueCount; }
512
513 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
520 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
541 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
562 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:
573 hb_array_t<const StatAxisRecord> const get_design_axes () const
574 { return (this+designAxesOffset).as_array (designAxisCount); }
575
576 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 */