1 /* 2 * Copyright © 2017 Google, Inc. 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 * Google Author(s): Behdad Esfahbod 25 */ 26 27 #ifndef HB_OT_VAR_FVAR_TABLE_HH 28 #define HB_OT_VAR_FVAR_TABLE_HH 29 30 #include "hb-open-type.hh" 31 32 /* 33 * fvar -- Font Variations 34 * https://docs.microsoft.com/en-us/typography/opentype/spec/fvar 35 */ 36 37 #define HB_OT_TAG_fvar HB_TAG('f','v','a','r') 38 39 40 namespace OT { 41 42 43 struct InstanceRecord 44 { 45 friend struct fvar; 46 get_coordinatesOT::InstanceRecord47 hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const 48 { return coordinatesZ.as_array (axis_count); } 49 subsetOT::InstanceRecord50 bool subset (hb_subset_context_t *c, 51 unsigned axis_count, 52 bool has_postscript_nameid) const 53 { 54 TRACE_SUBSET (this); 55 if (unlikely (!c->serializer->embed (subfamilyNameID))) return_trace (false); 56 if (unlikely (!c->serializer->embed (flags))) return_trace (false); 57 58 const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count); 59 const hb_hashmap_t<hb_tag_t, float> *axes_location = c->plan->user_axes_location; 60 for (unsigned i = 0 ; i < axis_count; i++) 61 { 62 uint32_t *axis_tag; 63 // only keep instances whose coordinates == pinned axis location 64 if (!c->plan->axes_old_index_tag_map->has (i, &axis_tag)) continue; 65 66 if (axes_location->has (*axis_tag) && 67 fabsf (axes_location->get (*axis_tag) - coords[i].to_float ()) > 0.001f) 68 return_trace (false); 69 70 if (!c->plan->axes_index_map->has (i)) 71 continue; 72 73 if (!c->serializer->embed (coords[i])) 74 return_trace (false); 75 } 76 77 if (has_postscript_nameid) 78 { 79 NameID name_id; 80 name_id = StructAfter<NameID> (coords); 81 if (!c->serializer->embed (name_id)) 82 return_trace (false); 83 } 84 85 return_trace (true); 86 } 87 sanitizeOT::InstanceRecord88 bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const 89 { 90 TRACE_SANITIZE (this); 91 return_trace (c->check_struct (this) && 92 c->check_array (coordinatesZ.arrayZ, axis_count)); 93 } 94 95 protected: 96 NameID subfamilyNameID;/* The name ID for entries in the 'name' table 97 * that provide subfamily names for this instance. */ 98 HBUINT16 flags; /* Reserved for future use — set to 0. */ 99 UnsizedArrayOf<F16DOT16> 100 coordinatesZ; /* The coordinates array for this instance. */ 101 //NameID postScriptNameIDX;/*Optional. The name ID for entries in the 'name' 102 // * table that provide PostScript names for this 103 // * instance. */ 104 105 public: 106 DEFINE_SIZE_UNBOUNDED (4); 107 }; 108 109 struct AxisRecord 110 { cmpOT::AxisRecord111 int cmp (hb_tag_t key) const { return axisTag.cmp (key); } 112 113 enum 114 { 115 AXIS_FLAG_HIDDEN = 0x0001, 116 }; 117 118 #ifndef HB_DISABLE_DEPRECATED get_axis_deprecatedOT::AxisRecord119 void get_axis_deprecated (hb_ot_var_axis_t *info) const 120 { 121 info->tag = axisTag; 122 info->name_id = axisNameID; 123 get_coordinates (info->min_value, info->default_value, info->max_value); 124 } 125 #endif 126 get_axis_infoOT::AxisRecord127 void get_axis_info (unsigned axis_index, hb_ot_var_axis_info_t *info) const 128 { 129 info->axis_index = axis_index; 130 info->tag = axisTag; 131 info->name_id = axisNameID; 132 info->flags = (hb_ot_var_axis_flags_t) (unsigned int) flags; 133 get_coordinates (info->min_value, info->default_value, info->max_value); 134 info->reserved = 0; 135 } 136 get_axis_tagOT::AxisRecord137 hb_tag_t get_axis_tag () const { return axisTag; } 138 normalize_axis_valueOT::AxisRecord139 int normalize_axis_value (float v) const 140 { 141 float min_value, default_value, max_value; 142 get_coordinates (min_value, default_value, max_value); 143 144 v = hb_clamp (v, min_value, max_value); 145 146 if (v == default_value) 147 return 0; 148 else if (v < default_value) 149 v = (v - default_value) / (default_value - min_value); 150 else 151 v = (v - default_value) / (max_value - default_value); 152 return roundf (v * 16384.f); 153 } 154 unnormalize_axis_valueOT::AxisRecord155 float unnormalize_axis_value (int v) const 156 { 157 float min_value, default_value, max_value; 158 get_coordinates (min_value, default_value, max_value); 159 160 if (v == 0) 161 return default_value; 162 else if (v < 0) 163 return v * (default_value - min_value) / 16384.f + default_value; 164 else 165 return v * (max_value - default_value) / 16384.f + default_value; 166 } 167 get_name_idOT::AxisRecord168 hb_ot_name_id_t get_name_id () const { return axisNameID; } 169 sanitizeOT::AxisRecord170 bool sanitize (hb_sanitize_context_t *c) const 171 { 172 TRACE_SANITIZE (this); 173 return_trace (c->check_struct (this)); 174 } 175 get_coordinatesOT::AxisRecord176 void get_coordinates (float &min, float &default_, float &max) const 177 { 178 default_ = defaultValue / 65536.f; 179 /* Ensure order, to simplify client math. */ 180 min = hb_min (default_, minValue / 65536.f); 181 max = hb_max (default_, maxValue / 65536.f); 182 } 183 get_defaultOT::AxisRecord184 float get_default () const 185 { 186 return defaultValue / 65536.f; 187 } 188 189 public: 190 Tag axisTag; /* Tag identifying the design variation for the axis. */ 191 protected: 192 F16DOT16 minValue; /* The minimum coordinate value for the axis. */ 193 F16DOT16 defaultValue; /* The default coordinate value for the axis. */ 194 F16DOT16 maxValue; /* The maximum coordinate value for the axis. */ 195 public: 196 HBUINT16 flags; /* Axis flags. */ 197 NameID axisNameID; /* The name ID for entries in the 'name' table that 198 * provide a display name for this axis. */ 199 200 public: 201 DEFINE_SIZE_STATIC (20); 202 }; 203 204 struct fvar 205 { 206 static constexpr hb_tag_t tableTag = HB_OT_TAG_fvar; 207 has_dataOT::fvar208 bool has_data () const { return version.to_int (); } 209 sanitizeOT::fvar210 bool sanitize (hb_sanitize_context_t *c) const 211 { 212 TRACE_SANITIZE (this); 213 return_trace (version.sanitize (c) && 214 likely (version.major == 1) && 215 c->check_struct (this) && 216 axisSize == 20 && /* Assumed in our code. */ 217 instanceSize >= axisCount * 4 + 4 && 218 get_axes ().sanitize (c) && 219 c->check_range (get_instance (0), instanceCount, instanceSize)); 220 } 221 get_axis_countOT::fvar222 unsigned int get_axis_count () const { return axisCount; } 223 224 #ifndef HB_DISABLE_DEPRECATED get_axes_deprecatedOT::fvar225 unsigned int get_axes_deprecated (unsigned int start_offset, 226 unsigned int *axes_count /* IN/OUT */, 227 hb_ot_var_axis_t *axes_array /* OUT */) const 228 { 229 if (axes_count) 230 { 231 hb_array_t<const AxisRecord> arr = get_axes ().sub_array (start_offset, axes_count); 232 for (unsigned i = 0; i < arr.length; ++i) 233 arr[i].get_axis_deprecated (&axes_array[i]); 234 } 235 return axisCount; 236 } 237 #endif 238 get_axis_infosOT::fvar239 unsigned int get_axis_infos (unsigned int start_offset, 240 unsigned int *axes_count /* IN/OUT */, 241 hb_ot_var_axis_info_t *axes_array /* OUT */) const 242 { 243 if (axes_count) 244 { 245 hb_array_t<const AxisRecord> arr = get_axes ().sub_array (start_offset, axes_count); 246 for (unsigned i = 0; i < arr.length; ++i) 247 arr[i].get_axis_info (start_offset + i, &axes_array[i]); 248 } 249 return axisCount; 250 } 251 252 #ifndef HB_DISABLE_DEPRECATED 253 bool find_axis_deprecatedOT::fvar254 find_axis_deprecated (hb_tag_t tag, unsigned *axis_index, hb_ot_var_axis_t *info) const 255 { 256 unsigned i; 257 if (!axis_index) axis_index = &i; 258 *axis_index = HB_OT_VAR_NO_AXIS_INDEX; 259 auto axes = get_axes (); 260 return axes.lfind (tag, axis_index) && ((void) axes[*axis_index].get_axis_deprecated (info), true); 261 } 262 #endif 263 bool find_axis_infoOT::fvar264 find_axis_info (hb_tag_t tag, hb_ot_var_axis_info_t *info) const 265 { 266 unsigned i; 267 auto axes = get_axes (); 268 return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true); 269 } 270 normalize_axis_valueOT::fvar271 int normalize_axis_value (unsigned int axis_index, float v) const 272 { return get_axes ()[axis_index].normalize_axis_value (v); } 273 unnormalize_axis_valueOT::fvar274 float unnormalize_axis_value (unsigned int axis_index, int v) const 275 { return get_axes ()[axis_index].unnormalize_axis_value (v); } 276 get_instance_countOT::fvar277 unsigned int get_instance_count () const { return instanceCount; } 278 get_instance_subfamily_name_idOT::fvar279 hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const 280 { 281 const InstanceRecord *instance = get_instance (instance_index); 282 if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID; 283 return instance->subfamilyNameID; 284 } 285 get_instance_postscript_name_idOT::fvar286 hb_ot_name_id_t get_instance_postscript_name_id (unsigned int instance_index) const 287 { 288 const InstanceRecord *instance = get_instance (instance_index); 289 if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID; 290 if (instanceSize >= axisCount * 4 + 6) 291 return StructAfter<NameID> (instance->get_coordinates (axisCount)); 292 return HB_OT_NAME_ID_INVALID; 293 } 294 get_instance_coordsOT::fvar295 unsigned int get_instance_coords (unsigned int instance_index, 296 unsigned int *coords_length, /* IN/OUT */ 297 float *coords /* OUT */) const 298 { 299 const InstanceRecord *instance = get_instance (instance_index); 300 if (unlikely (!instance)) 301 { 302 if (coords_length) 303 *coords_length = 0; 304 return 0; 305 } 306 307 if (coords_length && *coords_length) 308 { 309 hb_array_t<const F16DOT16> instanceCoords = instance->get_coordinates (axisCount) 310 .sub_array (0, coords_length); 311 for (unsigned int i = 0; i < instanceCoords.length; i++) 312 coords[i] = instanceCoords.arrayZ[i].to_float (); 313 } 314 return axisCount; 315 } 316 collect_name_idsOT::fvar317 void collect_name_ids (hb_hashmap_t<hb_tag_t, float> *user_axes_location, 318 hb_set_t *nameids /* IN/OUT */) const 319 { 320 if (!has_data ()) return; 321 hb_map_t pinned_axes; 322 323 auto axis_records = get_axes (); 324 for (unsigned i = 0 ; i < (unsigned)axisCount; i++) 325 { 326 hb_tag_t axis_tag = axis_records[i].get_axis_tag (); 327 if (user_axes_location->has (axis_tag)) 328 { 329 pinned_axes.set (i, axis_tag); 330 continue; 331 } 332 333 nameids->add (axis_records[i].get_name_id ()); 334 } 335 336 for (unsigned i = 0 ; i < (unsigned)instanceCount; i++) 337 { 338 const InstanceRecord *instance = get_instance (i); 339 340 if (hb_any (+ hb_enumerate (instance->get_coordinates (axisCount)) 341 | hb_filter (pinned_axes, hb_first) 342 | hb_map ([&] (const hb_pair_t<unsigned, const F16DOT16&>& _) 343 { 344 hb_tag_t axis_tag = pinned_axes.get (_.first); 345 float location = user_axes_location->get (axis_tag); 346 if (fabs ((double)location - (double)_.second.to_float ()) > 0.001) return true; 347 return false; 348 }) 349 )) 350 continue; 351 352 nameids->add (instance->subfamilyNameID); 353 354 if (instanceSize >= axisCount * 4 + 6) 355 { 356 unsigned post_script_name_id = StructAfter<NameID> (instance->get_coordinates (axisCount)); 357 if (post_script_name_id != HB_OT_NAME_ID_INVALID) nameids->add (post_script_name_id); 358 } 359 } 360 } 361 subsetOT::fvar362 bool subset (hb_subset_context_t *c) const 363 { 364 TRACE_SUBSET (this); 365 unsigned retained_axis_count = c->plan->axes_index_map->get_population (); 366 if (!retained_axis_count) //all axes are pinned 367 return_trace (false); 368 369 fvar *out = c->serializer->embed (this); 370 if (unlikely (!out)) return_trace (false); 371 372 if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) 373 return_trace (false); 374 375 bool has_postscript_nameid = false; 376 if (instanceSize >= axisCount * 4 + 6) 377 has_postscript_nameid = true; 378 379 if (!c->serializer->check_assign (out->instanceSize, retained_axis_count * 4 + (has_postscript_nameid ? 6 : 4), 380 HB_SERIALIZE_ERROR_INT_OVERFLOW)) 381 return_trace (false); 382 383 auto axes_records = get_axes (); 384 for (unsigned i = 0 ; i < (unsigned)axisCount; i++) 385 { 386 if (!c->plan->axes_index_map->has (i)) continue; 387 if (unlikely (!c->serializer->embed (axes_records[i]))) 388 return_trace (false); 389 } 390 391 if (!c->serializer->check_assign (out->firstAxis, get_size (), HB_SERIALIZE_ERROR_INT_OVERFLOW)) 392 return_trace (false); 393 394 for (unsigned i = 0 ; i < (unsigned)instanceCount; i++) 395 { 396 const InstanceRecord *instance = get_instance (i); 397 auto snap = c->serializer->snapshot (); 398 if (!instance->subset (c, axisCount, has_postscript_nameid)) 399 c->serializer->revert (snap); 400 } 401 return_trace (true); 402 } 403 404 public: get_axesOT::fvar405 hb_array_t<const AxisRecord> get_axes () const 406 { return hb_array (&(this+firstAxis), axisCount); } 407 get_instanceOT::fvar408 const InstanceRecord *get_instance (unsigned int i) const 409 { 410 if (unlikely (i >= instanceCount)) return nullptr; 411 return &StructAtOffset<InstanceRecord> (&StructAfter<InstanceRecord> (get_axes ()), 412 i * instanceSize); 413 } 414 415 protected: 416 FixedVersion<>version; /* Version of the fvar table 417 * initially set to 0x00010000u */ 418 Offset16To<AxisRecord> 419 firstAxis; /* Offset in bytes from the beginning of the table 420 * to the start of the AxisRecord array. */ 421 HBUINT16 reserved; /* This field is permanently reserved. Set to 2. */ 422 HBUINT16 axisCount; /* The number of variation axes in the font (the 423 * number of records in the axes array). */ 424 HBUINT16 axisSize; /* The size in bytes of each VariationAxisRecord — 425 * set to 20 (0x0014) for this version. */ 426 HBUINT16 instanceCount; /* The number of named instances defined in the font 427 * (the number of records in the instances array). */ 428 HBUINT16 instanceSize; /* The size in bytes of each InstanceRecord — set 429 * to either axisCount * sizeof(F16DOT16) + 4, or to 430 * axisCount * sizeof(F16DOT16) + 6. */ 431 432 public: 433 DEFINE_SIZE_STATIC (16); 434 }; 435 436 } /* namespace OT */ 437 438 439 #endif /* HB_OT_VAR_FVAR_TABLE_HH */ 440