hb-ot-stat-table.hh (21222B)
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 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 { 73 int cmp (hb_tag_t key) const { return tag.cmp (key); } 74 75 hb_ot_name_id_t get_name_id () const { return nameID; } 76 77 hb_tag_t get_axis_tag () const { return tag; } 78 79 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 { 98 unsigned int get_axis_index () const { return axisIndex; } 99 float get_value () const { return value.to_float (); } 100 101 hb_ot_name_id_t get_value_name_id () const { return valueNameID; } 102 103 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 109 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 118 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 130 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 { 153 unsigned int get_axis_index () const { return axisIndex; } 154 float get_value () const { return nominalValue.to_float (); } 155 156 hb_ot_name_id_t get_value_name_id () const { return valueNameID; } 157 158 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 164 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 173 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 185 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 { 212 unsigned int get_axis_index () const { return axisIndex; } 213 float get_value () const { return value.to_float (); } 214 215 hb_ot_name_id_t get_value_name_id () const { return valueNameID; } 216 217 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 223 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 232 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 244 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 { 269 unsigned int get_axis_index () const { return axisIndex; } 270 float get_value () const { return value.to_float (); } 271 272 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 { 289 const AxisValueRecord &get_axis_record (unsigned int axis_index) const 290 { return axisValues.as_array (axisCount)[axis_index]; } 291 292 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 310 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 325 hb_ot_name_id_t get_value_name_id () const { return valueNameID; } 326 327 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 { 353 float get_value (unsigned int axis_index) const 354 { 355 switch (u.format.v) 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 365 unsigned int get_axis_index () const 366 { 367 switch (u.format.v) 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 377 hb_ot_name_id_t get_value_name_id () const 378 { 379 switch (u.format.v) 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> 390 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const 391 { 392 if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); 393 TRACE_DISPATCH (this, u.format.v); 394 switch (u.format.v) { 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 403 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.v) 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 416 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.v) 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 struct { HBUINT16 v; } format; 437 AxisValueFormat1 format1; 438 AxisValueFormat2 format2; 439 AxisValueFormat3 format3; 440 AxisValueFormat4 format4; 441 } u; 442 public: 443 DEFINE_SIZE_UNION (2, format.v); 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 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 479 bool has_data () const { return version.to_int (); } 480 481 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 500 unsigned get_design_axis_count () const { return designAxisCount; } 501 502 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 509 unsigned get_axis_value_count () const { return axisValueCount; } 510 511 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 518 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 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 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: 574 hb_array_t<const StatAxisRecord> const get_design_axes () const 575 { return (this+designAxesOffset).as_array (designAxisCount); } 576 577 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 */