Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/harfbuzz/src/hb-ot-layout-common.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 © 2007,2008,2009 Red Hat, Inc. | |
| 3 * Copyright © 2010,2012 Google, Inc. | |
| 4 * | |
| 5 * This is part of HarfBuzz, a text shaping library. | |
| 6 * | |
| 7 * Permission is hereby granted, without written agreement and without | |
| 8 * license or royalty fees, to use, copy, modify, and distribute this | |
| 9 * software and its documentation for any purpose, provided that the | |
| 10 * above copyright notice and the following two paragraphs appear in | |
| 11 * all copies of this software. | |
| 12 * | |
| 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | |
| 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | |
| 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | |
| 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |
| 17 * DAMAGE. | |
| 18 * | |
| 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
| 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
| 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | |
| 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | |
| 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
| 24 * | |
| 25 * Red Hat Author(s): Behdad Esfahbod | |
| 26 * Google Author(s): Behdad Esfahbod | |
| 27 */ | |
| 28 | |
| 29 #ifndef HB_OT_LAYOUT_COMMON_HH | |
| 30 #define HB_OT_LAYOUT_COMMON_HH | |
| 31 | |
| 32 #include "hb.hh" | |
| 33 #include "hb-ot-layout.hh" | |
| 34 #include "hb-open-type.hh" | |
| 35 #include "hb-set.hh" | |
| 36 #include "hb-bimap.hh" | |
| 37 | |
| 38 #include "OT/Layout/Common/Coverage.hh" | |
| 39 #include "OT/Layout/types.hh" | |
| 40 | |
| 41 // TODO(garretrieger): cleanup these after migration. | |
| 42 using OT::Layout::Common::Coverage; | |
| 43 using OT::Layout::Common::RangeRecord; | |
| 44 using OT::Layout::SmallTypes; | |
| 45 using OT::Layout::MediumTypes; | |
| 46 | |
| 47 #ifndef HB_MAX_NESTING_LEVEL | |
| 48 #define HB_MAX_NESTING_LEVEL 64 | |
| 49 #endif | |
| 50 #ifndef HB_MAX_CONTEXT_LENGTH | |
| 51 #define HB_MAX_CONTEXT_LENGTH 64 | |
| 52 #endif | |
| 53 #ifndef HB_CLOSURE_MAX_STAGES | |
| 54 /* | |
| 55 * The maximum number of times a lookup can be applied during shaping. | |
| 56 * Used to limit the number of iterations of the closure algorithm. | |
| 57 * This must be larger than the number of times add_gsub_pause() is | |
| 58 * called in a collect_features call of any shaper. | |
| 59 */ | |
| 60 #define HB_CLOSURE_MAX_STAGES 12 | |
| 61 #endif | |
| 62 | |
| 63 #ifndef HB_MAX_SCRIPTS | |
| 64 #define HB_MAX_SCRIPTS 500 | |
| 65 #endif | |
| 66 | |
| 67 #ifndef HB_MAX_LANGSYS | |
| 68 #define HB_MAX_LANGSYS 2000 | |
| 69 #endif | |
| 70 | |
| 71 #ifndef HB_MAX_LANGSYS_FEATURE_COUNT | |
| 72 #define HB_MAX_LANGSYS_FEATURE_COUNT 50000 | |
| 73 #endif | |
| 74 | |
| 75 #ifndef HB_MAX_FEATURE_INDICES | |
| 76 #define HB_MAX_FEATURE_INDICES 1500 | |
| 77 #endif | |
| 78 | |
| 79 #ifndef HB_MAX_LOOKUP_VISIT_COUNT | |
| 80 #define HB_MAX_LOOKUP_VISIT_COUNT 35000 | |
| 81 #endif | |
| 82 | |
| 83 | |
| 84 namespace OT { | |
| 85 | |
| 86 template<typename Iterator> | |
| 87 static inline bool ClassDef_serialize (hb_serialize_context_t *c, | |
| 88 Iterator it); | |
| 89 | |
| 90 static bool ClassDef_remap_and_serialize ( | |
| 91 hb_serialize_context_t *c, | |
| 92 const hb_set_t &klasses, | |
| 93 bool use_class_zero, | |
| 94 hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */ | |
| 95 hb_map_t *klass_map /*IN/OUT*/); | |
| 96 | |
| 97 struct hb_collect_feature_substitutes_with_var_context_t | |
| 98 { | |
| 99 const hb_map_t *axes_index_tag_map; | |
| 100 const hb_hashmap_t<hb_tag_t, int> *axes_location; | |
| 101 hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *record_cond_idx_map; | |
| 102 hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map; | |
| 103 | |
| 104 // not stored in subset_plan | |
| 105 hb_set_t *feature_indices; | |
| 106 bool apply; | |
| 107 unsigned cur_record_idx; | |
| 108 hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> *conditionset_map; | |
| 109 }; | |
| 110 | |
| 111 struct hb_prune_langsys_context_t | |
| 112 { | |
| 113 hb_prune_langsys_context_t (const void *table_, | |
| 114 hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map_, | |
| 115 const hb_map_t *duplicate_feature_map_, | |
| 116 hb_set_t *new_collected_feature_indexes_) | |
| 117 :table (table_), | |
| 118 script_langsys_map (script_langsys_map_), | |
| 119 duplicate_feature_map (duplicate_feature_map_), | |
| 120 new_feature_indexes (new_collected_feature_indexes_), | |
| 121 script_count (0),langsys_feature_count (0) {} | |
| 122 | |
| 123 bool visitScript () | |
| 124 { return script_count++ < HB_MAX_SCRIPTS; } | |
| 125 | |
| 126 bool visitLangsys (unsigned feature_count) | |
| 127 { | |
| 128 langsys_feature_count += feature_count; | |
| 129 return langsys_feature_count < HB_MAX_LANGSYS_FEATURE_COUNT; | |
| 130 } | |
| 131 | |
| 132 public: | |
| 133 const void *table; | |
| 134 hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map; | |
| 135 const hb_map_t *duplicate_feature_map; | |
| 136 hb_set_t *new_feature_indexes; | |
| 137 | |
| 138 private: | |
| 139 unsigned script_count; | |
| 140 unsigned langsys_feature_count; | |
| 141 }; | |
| 142 | |
| 143 struct hb_subset_layout_context_t : | |
| 144 hb_dispatch_context_t<hb_subset_layout_context_t, hb_empty_t, HB_DEBUG_SUBSET> | |
| 145 { | |
| 146 const char *get_name () { return "SUBSET_LAYOUT"; } | |
| 147 static return_t default_return_value () { return hb_empty_t (); } | |
| 148 | |
| 149 bool visitScript () | |
| 150 { | |
| 151 return script_count++ < HB_MAX_SCRIPTS; | |
| 152 } | |
| 153 | |
| 154 bool visitLangSys () | |
| 155 { | |
| 156 return langsys_count++ < HB_MAX_LANGSYS; | |
| 157 } | |
| 158 | |
| 159 bool visitFeatureIndex (int count) | |
| 160 { | |
| 161 feature_index_count += count; | |
| 162 return feature_index_count < HB_MAX_FEATURE_INDICES; | |
| 163 } | |
| 164 | |
| 165 bool visitLookupIndex() | |
| 166 { | |
| 167 lookup_index_count++; | |
| 168 return lookup_index_count < HB_MAX_LOOKUP_VISIT_COUNT; | |
| 169 } | |
| 170 | |
| 171 hb_subset_context_t *subset_context; | |
| 172 const hb_tag_t table_tag; | |
| 173 const hb_map_t *lookup_index_map; | |
| 174 const hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map; | |
| 175 const hb_map_t *feature_index_map; | |
| 176 const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map; | |
| 177 hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map; | |
| 178 | |
| 179 unsigned cur_script_index; | |
| 180 unsigned cur_feature_var_record_idx; | |
| 181 | |
| 182 hb_subset_layout_context_t (hb_subset_context_t *c_, | |
| 183 hb_tag_t tag_) : | |
| 184 subset_context (c_), | |
| 185 table_tag (tag_), | |
| 186 cur_script_index (0xFFFFu), | |
| 187 cur_feature_var_record_idx (0u), | |
| 188 script_count (0), | |
| 189 langsys_count (0), | |
| 190 feature_index_count (0), | |
| 191 lookup_index_count (0) | |
| 192 { | |
| 193 if (tag_ == HB_OT_TAG_GSUB) | |
| 194 { | |
| 195 lookup_index_map = c_->plan->gsub_lookups; | |
| 196 script_langsys_map = c_->plan->gsub_langsys; | |
| 197 feature_index_map = c_->plan->gsub_features; | |
| 198 feature_substitutes_map = c_->plan->gsub_feature_substitutes_map; | |
| 199 feature_record_cond_idx_map = c_->plan->user_axes_location->is_empty () ? nullptr : c_->plan->gsub_feature_record_cond_idx_map; | |
| 200 } | |
| 201 else | |
| 202 { | |
| 203 lookup_index_map = c_->plan->gpos_lookups; | |
| 204 script_langsys_map = c_->plan->gpos_langsys; | |
| 205 feature_index_map = c_->plan->gpos_features; | |
| 206 feature_substitutes_map = c_->plan->gpos_feature_substitutes_map; | |
| 207 feature_record_cond_idx_map = c_->plan->user_axes_location->is_empty () ? nullptr : c_->plan->gpos_feature_record_cond_idx_map; | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 private: | |
| 212 unsigned script_count; | |
| 213 unsigned langsys_count; | |
| 214 unsigned feature_index_count; | |
| 215 unsigned lookup_index_count; | |
| 216 }; | |
| 217 | |
| 218 struct VariationStore; | |
| 219 struct hb_collect_variation_indices_context_t : | |
| 220 hb_dispatch_context_t<hb_collect_variation_indices_context_t> | |
| 221 { | |
| 222 template <typename T> | |
| 223 return_t dispatch (const T &obj) { obj.collect_variation_indices (this); return hb_empty_t (); } | |
| 224 static return_t default_return_value () { return hb_empty_t (); } | |
| 225 | |
| 226 hb_set_t *layout_variation_indices; | |
| 227 hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map; | |
| 228 hb_font_t *font; | |
| 229 const VariationStore *var_store; | |
| 230 const hb_set_t *glyph_set; | |
| 231 const hb_map_t *gpos_lookups; | |
| 232 float *store_cache; | |
| 233 | |
| 234 hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_, | |
| 235 hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map_, | |
| 236 hb_font_t *font_, | |
| 237 const VariationStore *var_store_, | |
| 238 const hb_set_t *glyph_set_, | |
| 239 const hb_map_t *gpos_lookups_, | |
| 240 float *store_cache_) : | |
| 241 layout_variation_indices (layout_variation_indices_), | |
| 242 varidx_delta_map (varidx_delta_map_), | |
| 243 font (font_), | |
| 244 var_store (var_store_), | |
| 245 glyph_set (glyph_set_), | |
| 246 gpos_lookups (gpos_lookups_), | |
| 247 store_cache (store_cache_) {} | |
| 248 }; | |
| 249 | |
| 250 template<typename OutputArray> | |
| 251 struct subset_offset_array_t | |
| 252 { | |
| 253 subset_offset_array_t (hb_subset_context_t *subset_context_, | |
| 254 OutputArray& out_, | |
| 255 const void *base_) : subset_context (subset_context_), | |
| 256 out (out_), base (base_) {} | |
| 257 | |
| 258 template <typename T> | |
| 259 bool operator () (T&& offset) | |
| 260 { | |
| 261 auto snap = subset_context->serializer->snapshot (); | |
| 262 auto *o = out.serialize_append (subset_context->serializer); | |
| 263 if (unlikely (!o)) return false; | |
| 264 bool ret = o->serialize_subset (subset_context, offset, base); | |
| 265 if (!ret) | |
| 266 { | |
| 267 out.pop (); | |
| 268 subset_context->serializer->revert (snap); | |
| 269 } | |
| 270 return ret; | |
| 271 } | |
| 272 | |
| 273 private: | |
| 274 hb_subset_context_t *subset_context; | |
| 275 OutputArray &out; | |
| 276 const void *base; | |
| 277 }; | |
| 278 | |
| 279 | |
| 280 template<typename OutputArray, typename Arg> | |
| 281 struct subset_offset_array_arg_t | |
| 282 { | |
| 283 subset_offset_array_arg_t (hb_subset_context_t *subset_context_, | |
| 284 OutputArray& out_, | |
| 285 const void *base_, | |
| 286 Arg &&arg_) : subset_context (subset_context_), out (out_), | |
| 287 base (base_), arg (arg_) {} | |
| 288 | |
| 289 template <typename T> | |
| 290 bool operator () (T&& offset) | |
| 291 { | |
| 292 auto snap = subset_context->serializer->snapshot (); | |
| 293 auto *o = out.serialize_append (subset_context->serializer); | |
| 294 if (unlikely (!o)) return false; | |
| 295 bool ret = o->serialize_subset (subset_context, offset, base, arg); | |
| 296 if (!ret) | |
| 297 { | |
| 298 out.pop (); | |
| 299 subset_context->serializer->revert (snap); | |
| 300 } | |
| 301 return ret; | |
| 302 } | |
| 303 | |
| 304 private: | |
| 305 hb_subset_context_t *subset_context; | |
| 306 OutputArray &out; | |
| 307 const void *base; | |
| 308 Arg &&arg; | |
| 309 }; | |
| 310 | |
| 311 /* | |
| 312 * Helper to subset an array of offsets. Subsets the thing pointed to by each offset | |
| 313 * and discards the offset in the array if the subset operation results in an empty | |
| 314 * thing. | |
| 315 */ | |
| 316 struct | |
| 317 { | |
| 318 template<typename OutputArray> | |
| 319 subset_offset_array_t<OutputArray> | |
| 320 operator () (hb_subset_context_t *subset_context, OutputArray& out, | |
| 321 const void *base) const | |
| 322 { return subset_offset_array_t<OutputArray> (subset_context, out, base); } | |
| 323 | |
| 324 /* Variant with one extra argument passed to serialize_subset */ | |
| 325 template<typename OutputArray, typename Arg> | |
| 326 subset_offset_array_arg_t<OutputArray, Arg> | |
| 327 operator () (hb_subset_context_t *subset_context, OutputArray& out, | |
| 328 const void *base, Arg &&arg) const | |
| 329 { return subset_offset_array_arg_t<OutputArray, Arg> (subset_context, out, base, arg); } | |
| 330 } | |
| 331 HB_FUNCOBJ (subset_offset_array); | |
| 332 | |
| 333 template<typename OutputArray> | |
| 334 struct subset_record_array_t | |
| 335 { | |
| 336 subset_record_array_t (hb_subset_layout_context_t *c_, OutputArray* out_, | |
| 337 const void *base_) : subset_layout_context (c_), | |
| 338 out (out_), base (base_) {} | |
| 339 | |
| 340 template <typename T> | |
| 341 void | |
| 342 operator () (T&& record) | |
| 343 { | |
| 344 auto snap = subset_layout_context->subset_context->serializer->snapshot (); | |
| 345 bool ret = record.subset (subset_layout_context, base); | |
| 346 if (!ret) subset_layout_context->subset_context->serializer->revert (snap); | |
| 347 else out->len++; | |
| 348 } | |
| 349 | |
| 350 private: | |
| 351 hb_subset_layout_context_t *subset_layout_context; | |
| 352 OutputArray *out; | |
| 353 const void *base; | |
| 354 }; | |
| 355 | |
| 356 template<typename OutputArray, typename Arg> | |
| 357 struct subset_record_array_arg_t | |
| 358 { | |
| 359 subset_record_array_arg_t (hb_subset_layout_context_t *c_, OutputArray* out_, | |
| 360 const void *base_, | |
| 361 Arg &&arg_) : subset_layout_context (c_), | |
| 362 out (out_), base (base_), arg (arg_) {} | |
| 363 | |
| 364 template <typename T> | |
| 365 void | |
| 366 operator () (T&& record) | |
| 367 { | |
| 368 auto snap = subset_layout_context->subset_context->serializer->snapshot (); | |
| 369 bool ret = record.subset (subset_layout_context, base, arg); | |
| 370 if (!ret) subset_layout_context->subset_context->serializer->revert (snap); | |
| 371 else out->len++; | |
| 372 } | |
| 373 | |
| 374 private: | |
| 375 hb_subset_layout_context_t *subset_layout_context; | |
| 376 OutputArray *out; | |
| 377 const void *base; | |
| 378 Arg &&arg; | |
| 379 }; | |
| 380 | |
| 381 /* | |
| 382 * Helper to subset a RecordList/record array. Subsets each Record in the array and | |
| 383 * discards the record if the subset operation returns false. | |
| 384 */ | |
| 385 struct | |
| 386 { | |
| 387 template<typename OutputArray> | |
| 388 subset_record_array_t<OutputArray> | |
| 389 operator () (hb_subset_layout_context_t *c, OutputArray* out, | |
| 390 const void *base) const | |
| 391 { return subset_record_array_t<OutputArray> (c, out, base); } | |
| 392 | |
| 393 /* Variant with one extra argument passed to subset */ | |
| 394 template<typename OutputArray, typename Arg> | |
| 395 subset_record_array_arg_t<OutputArray, Arg> | |
| 396 operator () (hb_subset_layout_context_t *c, OutputArray* out, | |
| 397 const void *base, Arg &&arg) const | |
| 398 { return subset_record_array_arg_t<OutputArray, Arg> (c, out, base, arg); } | |
| 399 } | |
| 400 HB_FUNCOBJ (subset_record_array); | |
| 401 | |
| 402 | |
| 403 template<typename OutputArray> | |
| 404 struct serialize_math_record_array_t | |
| 405 { | |
| 406 serialize_math_record_array_t (hb_serialize_context_t *serialize_context_, | |
| 407 OutputArray& out_, | |
| 408 const void *base_) : serialize_context (serialize_context_), | |
| 409 out (out_), base (base_) {} | |
| 410 | |
| 411 template <typename T> | |
| 412 bool operator () (T&& record) | |
| 413 { | |
| 414 if (!serialize_context->copy (record, base)) return false; | |
| 415 out.len++; | |
| 416 return true; | |
| 417 } | |
| 418 | |
| 419 private: | |
| 420 hb_serialize_context_t *serialize_context; | |
| 421 OutputArray &out; | |
| 422 const void *base; | |
| 423 }; | |
| 424 | |
| 425 /* | |
| 426 * Helper to serialize an array of MATH records. | |
| 427 */ | |
| 428 struct | |
| 429 { | |
| 430 template<typename OutputArray> | |
| 431 serialize_math_record_array_t<OutputArray> | |
| 432 operator () (hb_serialize_context_t *serialize_context, OutputArray& out, | |
| 433 const void *base) const | |
| 434 { return serialize_math_record_array_t<OutputArray> (serialize_context, out, base); } | |
| 435 | |
| 436 } | |
| 437 HB_FUNCOBJ (serialize_math_record_array); | |
| 438 | |
| 439 /* | |
| 440 * | |
| 441 * OpenType Layout Common Table Formats | |
| 442 * | |
| 443 */ | |
| 444 | |
| 445 | |
| 446 /* | |
| 447 * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList | |
| 448 */ | |
| 449 | |
| 450 struct IndexArray : Array16Of<Index> | |
| 451 { | |
| 452 bool intersects (const hb_map_t *indexes) const | |
| 453 { return hb_any (*this, indexes); } | |
| 454 | |
| 455 template <typename Iterator, | |
| 456 hb_requires (hb_is_iterator (Iterator))> | |
| 457 void serialize (hb_serialize_context_t *c, | |
| 458 hb_subset_layout_context_t *l, | |
| 459 Iterator it) | |
| 460 { | |
| 461 if (!it) return; | |
| 462 if (unlikely (!c->extend_min ((*this)))) return; | |
| 463 | |
| 464 for (const auto _ : it) | |
| 465 { | |
| 466 if (!l->visitLookupIndex()) break; | |
| 467 | |
| 468 Index i; | |
| 469 i = _; | |
| 470 c->copy (i); | |
| 471 this->len++; | |
| 472 } | |
| 473 } | |
| 474 | |
| 475 unsigned int get_indexes (unsigned int start_offset, | |
| 476 unsigned int *_count /* IN/OUT */, | |
| 477 unsigned int *_indexes /* OUT */) const | |
| 478 { | |
| 479 if (_count) | |
| 480 { | |
| 481 + this->as_array ().sub_array (start_offset, _count) | |
| 482 | hb_sink (hb_array (_indexes, *_count)) | |
| 483 ; | |
| 484 } | |
| 485 return this->len; | |
| 486 } | |
| 487 | |
| 488 void add_indexes_to (hb_set_t* output /* OUT */) const | |
| 489 { | |
| 490 output->add_array (as_array ()); | |
| 491 } | |
| 492 }; | |
| 493 | |
| 494 | |
| 495 /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */ | |
| 496 struct FeatureParamsSize | |
| 497 { | |
| 498 bool sanitize (hb_sanitize_context_t *c) const | |
| 499 { | |
| 500 TRACE_SANITIZE (this); | |
| 501 if (unlikely (!c->check_struct (this))) return_trace (false); | |
| 502 | |
| 503 /* This subtable has some "history", if you will. Some earlier versions of | |
| 504 * Adobe tools calculated the offset of the FeatureParams subtable from the | |
| 505 * beginning of the FeatureList table! Now, that is dealt with in the | |
| 506 * Feature implementation. But we still need to be able to tell junk from | |
| 507 * real data. Note: We don't check that the nameID actually exists. | |
| 508 * | |
| 509 * Read Roberts wrote on 9/15/06 on opentype-list@indx.co.uk : | |
| 510 * | |
| 511 * Yes, it is correct that a new version of the AFDKO (version 2.0) will be | |
| 512 * coming out soon, and that the makeotf program will build a font with a | |
| 513 * 'size' feature that is correct by the specification. | |
| 514 * | |
| 515 * The specification for this feature tag is in the "OpenType Layout Tag | |
| 516 * Registry". You can see a copy of this at: | |
| 517 * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size | |
| 518 * | |
| 519 * Here is one set of rules to determine if the 'size' feature is built | |
| 520 * correctly, or as by the older versions of MakeOTF. You may be able to do | |
| 521 * better. | |
| 522 * | |
| 523 * Assume that the offset to the size feature is according to specification, | |
| 524 * and make the following value checks. If it fails, assume the size | |
| 525 * feature is calculated as versions of MakeOTF before the AFDKO 2.0 built it. | |
| 526 * If this fails, reject the 'size' feature. The older makeOTF's calculated the | |
| 527 * offset from the beginning of the FeatureList table, rather than from the | |
| 528 * beginning of the 'size' Feature table. | |
| 529 * | |
| 530 * If "design size" == 0: | |
| 531 * fails check | |
| 532 * | |
| 533 * Else if ("subfamily identifier" == 0 and | |
| 534 * "range start" == 0 and | |
| 535 * "range end" == 0 and | |
| 536 * "range start" == 0 and | |
| 537 * "menu name ID" == 0) | |
| 538 * passes check: this is the format used when there is a design size | |
| 539 * specified, but there is no recommended size range. | |
| 540 * | |
| 541 * Else if ("design size" < "range start" or | |
| 542 * "design size" > "range end" or | |
| 543 * "range end" <= "range start" or | |
| 544 * "menu name ID" < 256 or | |
| 545 * "menu name ID" > 32767 or | |
| 546 * menu name ID is not a name ID which is actually in the name table) | |
| 547 * fails test | |
| 548 * Else | |
| 549 * passes test. | |
| 550 */ | |
| 551 | |
| 552 if (!designSize) | |
| 553 return_trace (false); | |
| 554 else if (subfamilyID == 0 && | |
| 555 subfamilyNameID == 0 && | |
| 556 rangeStart == 0 && | |
| 557 rangeEnd == 0) | |
| 558 return_trace (true); | |
| 559 else if (designSize < rangeStart || | |
| 560 designSize > rangeEnd || | |
| 561 subfamilyNameID < 256 || | |
| 562 subfamilyNameID > 32767) | |
| 563 return_trace (false); | |
| 564 else | |
| 565 return_trace (true); | |
| 566 } | |
| 567 | |
| 568 bool subset (hb_subset_context_t *c) const | |
| 569 { | |
| 570 TRACE_SUBSET (this); | |
| 571 return_trace ((bool) c->serializer->embed (*this)); | |
| 572 } | |
| 573 | |
| 574 HBUINT16 designSize; /* Represents the design size in 720/inch | |
| 575 * units (decipoints). The design size entry | |
| 576 * must be non-zero. When there is a design | |
| 577 * size but no recommended size range, the | |
| 578 * rest of the array will consist of zeros. */ | |
| 579 HBUINT16 subfamilyID; /* Has no independent meaning, but serves | |
| 580 * as an identifier that associates fonts | |
| 581 * in a subfamily. All fonts which share a | |
| 582 * Preferred or Font Family name and which | |
| 583 * differ only by size range shall have the | |
| 584 * same subfamily value, and no fonts which | |
| 585 * differ in weight or style shall have the | |
| 586 * same subfamily value. If this value is | |
| 587 * zero, the remaining fields in the array | |
| 588 * will be ignored. */ | |
| 589 NameID subfamilyNameID;/* If the preceding value is non-zero, this | |
| 590 * value must be set in the range 256 - 32767 | |
| 591 * (inclusive). It records the value of a | |
| 592 * field in the name table, which must | |
| 593 * contain English-language strings encoded | |
| 594 * in Windows Unicode and Macintosh Roman, | |
| 595 * and may contain additional strings | |
| 596 * localized to other scripts and languages. | |
| 597 * Each of these strings is the name an | |
| 598 * application should use, in combination | |
| 599 * with the family name, to represent the | |
| 600 * subfamily in a menu. Applications will | |
| 601 * choose the appropriate version based on | |
| 602 * their selection criteria. */ | |
| 603 HBUINT16 rangeStart; /* Large end of the recommended usage range | |
| 604 * (inclusive), stored in 720/inch units | |
| 605 * (decipoints). */ | |
| 606 HBUINT16 rangeEnd; /* Small end of the recommended usage range | |
| 607 (exclusive), stored in 720/inch units | |
| 608 * (decipoints). */ | |
| 609 public: | |
| 610 DEFINE_SIZE_STATIC (10); | |
| 611 }; | |
| 612 | |
| 613 /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#ssxx */ | |
| 614 struct FeatureParamsStylisticSet | |
| 615 { | |
| 616 bool sanitize (hb_sanitize_context_t *c) const | |
| 617 { | |
| 618 TRACE_SANITIZE (this); | |
| 619 /* Right now minorVersion is at zero. Which means, any table supports | |
| 620 * the uiNameID field. */ | |
| 621 return_trace (c->check_struct (this)); | |
| 622 } | |
| 623 | |
| 624 bool subset (hb_subset_context_t *c) const | |
| 625 { | |
| 626 TRACE_SUBSET (this); | |
| 627 return_trace ((bool) c->serializer->embed (*this)); | |
| 628 } | |
| 629 | |
| 630 HBUINT16 version; /* (set to 0): This corresponds to a “minor” | |
| 631 * version number. Additional data may be | |
| 632 * added to the end of this Feature Parameters | |
| 633 * table in the future. */ | |
| 634 | |
| 635 NameID uiNameID; /* The 'name' table name ID that specifies a | |
| 636 * string (or strings, for multiple languages) | |
| 637 * for a user-interface label for this | |
| 638 * feature. The values of uiLabelNameId and | |
| 639 * sampleTextNameId are expected to be in the | |
| 640 * font-specific name ID range (256-32767), | |
| 641 * though that is not a requirement in this | |
| 642 * Feature Parameters specification. The | |
| 643 * user-interface label for the feature can | |
| 644 * be provided in multiple languages. An | |
| 645 * English string should be included as a | |
| 646 * fallback. The string should be kept to a | |
| 647 * minimal length to fit comfortably with | |
| 648 * different application interfaces. */ | |
| 649 public: | |
| 650 DEFINE_SIZE_STATIC (4); | |
| 651 }; | |
| 652 | |
| 653 /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99 */ | |
| 654 struct FeatureParamsCharacterVariants | |
| 655 { | |
| 656 unsigned | |
| 657 get_characters (unsigned start_offset, unsigned *char_count, hb_codepoint_t *chars) const | |
| 658 { | |
| 659 if (char_count) | |
| 660 { | |
| 661 + characters.as_array ().sub_array (start_offset, char_count) | |
| 662 | hb_sink (hb_array (chars, *char_count)) | |
| 663 ; | |
| 664 } | |
| 665 return characters.len; | |
| 666 } | |
| 667 | |
| 668 unsigned get_size () const | |
| 669 { return min_size + characters.len * HBUINT24::static_size; } | |
| 670 | |
| 671 bool subset (hb_subset_context_t *c) const | |
| 672 { | |
| 673 TRACE_SUBSET (this); | |
| 674 return_trace ((bool) c->serializer->embed (*this)); | |
| 675 } | |
| 676 | |
| 677 bool sanitize (hb_sanitize_context_t *c) const | |
| 678 { | |
| 679 TRACE_SANITIZE (this); | |
| 680 return_trace (c->check_struct (this) && | |
| 681 characters.sanitize (c)); | |
| 682 } | |
| 683 | |
| 684 HBUINT16 format; /* Format number is set to 0. */ | |
| 685 NameID featUILableNameID; /* The ‘name’ table name ID that | |
| 686 * specifies a string (or strings, | |
| 687 * for multiple languages) for a | |
| 688 * user-interface label for this | |
| 689 * feature. (May be NULL.) */ | |
| 690 NameID featUITooltipTextNameID;/* The ‘name’ table name ID that | |
| 691 * specifies a string (or strings, | |
| 692 * for multiple languages) that an | |
| 693 * application can use for tooltip | |
| 694 * text for this feature. (May be | |
| 695 * nullptr.) */ | |
| 696 NameID sampleTextNameID; /* The ‘name’ table name ID that | |
| 697 * specifies sample text that | |
| 698 * illustrates the effect of this | |
| 699 * feature. (May be NULL.) */ | |
| 700 HBUINT16 numNamedParameters; /* Number of named parameters. (May | |
| 701 * be zero.) */ | |
| 702 NameID firstParamUILabelNameID;/* The first ‘name’ table name ID | |
| 703 * used to specify strings for | |
| 704 * user-interface labels for the | |
| 705 * feature parameters. (Must be zero | |
| 706 * if numParameters is zero.) */ | |
| 707 Array16Of<HBUINT24> | |
| 708 characters; /* Array of the Unicode Scalar Value | |
| 709 * of the characters for which this | |
| 710 * feature provides glyph variants. | |
| 711 * (May be zero.) */ | |
| 712 public: | |
| 713 DEFINE_SIZE_ARRAY (14, characters); | |
| 714 }; | |
| 715 | |
| 716 struct FeatureParams | |
| 717 { | |
| 718 bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const | |
| 719 { | |
| 720 #ifdef HB_NO_LAYOUT_FEATURE_PARAMS | |
| 721 return true; | |
| 722 #endif | |
| 723 TRACE_SANITIZE (this); | |
| 724 if (tag == HB_TAG ('s','i','z','e')) | |
| 725 return_trace (u.size.sanitize (c)); | |
| 726 if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ | |
| 727 return_trace (u.stylisticSet.sanitize (c)); | |
| 728 if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ | |
| 729 return_trace (u.characterVariants.sanitize (c)); | |
| 730 return_trace (true); | |
| 731 } | |
| 732 | |
| 733 bool subset (hb_subset_context_t *c, const Tag* tag) const | |
| 734 { | |
| 735 TRACE_SUBSET (this); | |
| 736 if (!tag) return_trace (false); | |
| 737 if (*tag == HB_TAG ('s','i','z','e')) | |
| 738 return_trace (u.size.subset (c)); | |
| 739 if ((*tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ | |
| 740 return_trace (u.stylisticSet.subset (c)); | |
| 741 if ((*tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ | |
| 742 return_trace (u.characterVariants.subset (c)); | |
| 743 return_trace (false); | |
| 744 } | |
| 745 | |
| 746 #ifndef HB_NO_LAYOUT_FEATURE_PARAMS | |
| 747 const FeatureParamsSize& get_size_params (hb_tag_t tag) const | |
| 748 { | |
| 749 if (tag == HB_TAG ('s','i','z','e')) | |
| 750 return u.size; | |
| 751 return Null (FeatureParamsSize); | |
| 752 } | |
| 753 const FeatureParamsStylisticSet& get_stylistic_set_params (hb_tag_t tag) const | |
| 754 { | |
| 755 if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ | |
| 756 return u.stylisticSet; | |
| 757 return Null (FeatureParamsStylisticSet); | |
| 758 } | |
| 759 const FeatureParamsCharacterVariants& get_character_variants_params (hb_tag_t tag) const | |
| 760 { | |
| 761 if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ | |
| 762 return u.characterVariants; | |
| 763 return Null (FeatureParamsCharacterVariants); | |
| 764 } | |
| 765 #endif | |
| 766 | |
| 767 private: | |
| 768 union { | |
| 769 FeatureParamsSize size; | |
| 770 FeatureParamsStylisticSet stylisticSet; | |
| 771 FeatureParamsCharacterVariants characterVariants; | |
| 772 } u; | |
| 773 public: | |
| 774 DEFINE_SIZE_MIN (0); | |
| 775 }; | |
| 776 | |
| 777 struct Record_sanitize_closure_t { | |
| 778 hb_tag_t tag; | |
| 779 const void *list_base; | |
| 780 }; | |
| 781 | |
| 782 struct Feature | |
| 783 { | |
| 784 unsigned int get_lookup_count () const | |
| 785 { return lookupIndex.len; } | |
| 786 hb_tag_t get_lookup_index (unsigned int i) const | |
| 787 { return lookupIndex[i]; } | |
| 788 unsigned int get_lookup_indexes (unsigned int start_index, | |
| 789 unsigned int *lookup_count /* IN/OUT */, | |
| 790 unsigned int *lookup_tags /* OUT */) const | |
| 791 { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); } | |
| 792 void add_lookup_indexes_to (hb_set_t *lookup_indexes) const | |
| 793 { lookupIndex.add_indexes_to (lookup_indexes); } | |
| 794 | |
| 795 const FeatureParams &get_feature_params () const | |
| 796 { return this+featureParams; } | |
| 797 | |
| 798 bool intersects_lookup_indexes (const hb_map_t *lookup_indexes) const | |
| 799 { return lookupIndex.intersects (lookup_indexes); } | |
| 800 | |
| 801 bool subset (hb_subset_context_t *c, | |
| 802 hb_subset_layout_context_t *l, | |
| 803 const Tag *tag = nullptr) const | |
| 804 { | |
| 805 TRACE_SUBSET (this); | |
| 806 auto *out = c->serializer->start_embed (*this); | |
| 807 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 808 | |
| 809 out->featureParams.serialize_subset (c, featureParams, this, tag); | |
| 810 | |
| 811 auto it = | |
| 812 + hb_iter (lookupIndex) | |
| 813 | hb_filter (l->lookup_index_map) | |
| 814 | hb_map (l->lookup_index_map) | |
| 815 ; | |
| 816 | |
| 817 out->lookupIndex.serialize (c->serializer, l, it); | |
| 818 // The decision to keep or drop this feature is already made before we get here | |
| 819 // so always retain it. | |
| 820 return_trace (true); | |
| 821 } | |
| 822 | |
| 823 bool sanitize (hb_sanitize_context_t *c, | |
| 824 const Record_sanitize_closure_t *closure = nullptr) const | |
| 825 { | |
| 826 TRACE_SANITIZE (this); | |
| 827 if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) | |
| 828 return_trace (false); | |
| 829 | |
| 830 /* Some earlier versions of Adobe tools calculated the offset of the | |
| 831 * FeatureParams subtable from the beginning of the FeatureList table! | |
| 832 * | |
| 833 * If sanitizing "failed" for the FeatureParams subtable, try it with the | |
| 834 * alternative location. We would know sanitize "failed" if old value | |
| 835 * of the offset was non-zero, but it's zeroed now. | |
| 836 * | |
| 837 * Only do this for the 'size' feature, since at the time of the faulty | |
| 838 * Adobe tools, only the 'size' feature had FeatureParams defined. | |
| 839 */ | |
| 840 | |
| 841 if (likely (featureParams.is_null ())) | |
| 842 return_trace (true); | |
| 843 | |
| 844 unsigned int orig_offset = featureParams; | |
| 845 if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) | |
| 846 return_trace (false); | |
| 847 | |
| 848 if (featureParams == 0 && closure && | |
| 849 closure->tag == HB_TAG ('s','i','z','e') && | |
| 850 closure->list_base && closure->list_base < this) | |
| 851 { | |
| 852 unsigned int new_offset_int = orig_offset - | |
| 853 (((char *) this) - ((char *) closure->list_base)); | |
| 854 | |
| 855 Offset16To<FeatureParams> new_offset; | |
| 856 /* Check that it would not overflow. */ | |
| 857 new_offset = new_offset_int; | |
| 858 if (new_offset == new_offset_int && | |
| 859 c->try_set (&featureParams, new_offset_int) && | |
| 860 !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) | |
| 861 return_trace (false); | |
| 862 } | |
| 863 | |
| 864 return_trace (true); | |
| 865 } | |
| 866 | |
| 867 Offset16To<FeatureParams> | |
| 868 featureParams; /* Offset to Feature Parameters table (if one | |
| 869 * has been defined for the feature), relative | |
| 870 * to the beginning of the Feature Table; = Null | |
| 871 * if not required */ | |
| 872 IndexArray lookupIndex; /* Array of LookupList indices */ | |
| 873 public: | |
| 874 DEFINE_SIZE_ARRAY_SIZED (4, lookupIndex); | |
| 875 }; | |
| 876 | |
| 877 template <typename Type> | |
| 878 struct Record | |
| 879 { | |
| 880 int cmp (hb_tag_t a) const { return tag.cmp (a); } | |
| 881 | |
| 882 bool subset (hb_subset_layout_context_t *c, const void *base, const void *f_sub = nullptr) const | |
| 883 { | |
| 884 TRACE_SUBSET (this); | |
| 885 auto *out = c->subset_context->serializer->embed (this); | |
| 886 if (unlikely (!out)) return_trace (false); | |
| 887 | |
| 888 if (!f_sub) | |
| 889 return_trace (out->offset.serialize_subset (c->subset_context, offset, base, c, &tag)); | |
| 890 | |
| 891 const Feature& f = *reinterpret_cast<const Feature *> (f_sub); | |
| 892 auto *s = c->subset_context->serializer; | |
| 893 s->push (); | |
| 894 | |
| 895 out->offset = 0; | |
| 896 bool ret = f.subset (c->subset_context, c, &tag); | |
| 897 if (ret) | |
| 898 s->add_link (out->offset, s->pop_pack ()); | |
| 899 else | |
| 900 s->pop_discard (); | |
| 901 | |
| 902 return_trace (ret); | |
| 903 } | |
| 904 | |
| 905 bool sanitize (hb_sanitize_context_t *c, const void *base) const | |
| 906 { | |
| 907 TRACE_SANITIZE (this); | |
| 908 const Record_sanitize_closure_t closure = {tag, base}; | |
| 909 return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); | |
| 910 } | |
| 911 | |
| 912 Tag tag; /* 4-byte Tag identifier */ | |
| 913 Offset16To<Type> | |
| 914 offset; /* Offset from beginning of object holding | |
| 915 * the Record */ | |
| 916 public: | |
| 917 DEFINE_SIZE_STATIC (6); | |
| 918 }; | |
| 919 | |
| 920 template <typename Type> | |
| 921 struct RecordArrayOf : SortedArray16Of<Record<Type>> | |
| 922 { | |
| 923 const Offset16To<Type>& get_offset (unsigned int i) const | |
| 924 { return (*this)[i].offset; } | |
| 925 Offset16To<Type>& get_offset (unsigned int i) | |
| 926 { return (*this)[i].offset; } | |
| 927 const Tag& get_tag (unsigned int i) const | |
| 928 { return (*this)[i].tag; } | |
| 929 unsigned int get_tags (unsigned int start_offset, | |
| 930 unsigned int *record_count /* IN/OUT */, | |
| 931 hb_tag_t *record_tags /* OUT */) const | |
| 932 { | |
| 933 if (record_count) | |
| 934 { | |
| 935 + this->as_array ().sub_array (start_offset, record_count) | |
| 936 | hb_map (&Record<Type>::tag) | |
| 937 | hb_sink (hb_array (record_tags, *record_count)) | |
| 938 ; | |
| 939 } | |
| 940 return this->len; | |
| 941 } | |
| 942 bool find_index (hb_tag_t tag, unsigned int *index) const | |
| 943 { | |
| 944 return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); | |
| 945 } | |
| 946 }; | |
| 947 | |
| 948 template <typename Type> | |
| 949 struct RecordListOf : RecordArrayOf<Type> | |
| 950 { | |
| 951 const Type& operator [] (unsigned int i) const | |
| 952 { return this+this->get_offset (i); } | |
| 953 | |
| 954 bool subset (hb_subset_context_t *c, | |
| 955 hb_subset_layout_context_t *l) const | |
| 956 { | |
| 957 TRACE_SUBSET (this); | |
| 958 auto *out = c->serializer->start_embed (*this); | |
| 959 if (unlikely (!c->serializer->extend_min (out))) return_trace (false); | |
| 960 | |
| 961 + this->iter () | |
| 962 | hb_apply (subset_record_array (l, out, this)) | |
| 963 ; | |
| 964 return_trace (true); | |
| 965 } | |
| 966 | |
| 967 bool sanitize (hb_sanitize_context_t *c) const | |
| 968 { | |
| 969 TRACE_SANITIZE (this); | |
| 970 return_trace (RecordArrayOf<Type>::sanitize (c, this)); | |
| 971 } | |
| 972 }; | |
| 973 | |
| 974 struct RecordListOfFeature : RecordListOf<Feature> | |
| 975 { | |
| 976 bool subset (hb_subset_context_t *c, | |
| 977 hb_subset_layout_context_t *l) const | |
| 978 { | |
| 979 TRACE_SUBSET (this); | |
| 980 auto *out = c->serializer->start_embed (*this); | |
| 981 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 982 | |
| 983 + hb_enumerate (*this) | |
| 984 | hb_filter (l->feature_index_map, hb_first) | |
| 985 | hb_apply ([l, out, this] (const hb_pair_t<unsigned, const Record<Feature>&>& _) | |
| 986 { | |
| 987 const Feature *f_sub = nullptr; | |
| 988 const Feature **f = nullptr; | |
| 989 if (l->feature_substitutes_map->has (_.first, &f)) | |
| 990 f_sub = *f; | |
| 991 | |
| 992 subset_record_array (l, out, this, f_sub) (_.second); | |
| 993 }) | |
| 994 ; | |
| 995 | |
| 996 return_trace (true); | |
| 997 } | |
| 998 }; | |
| 999 | |
| 1000 typedef RecordListOf<Feature> FeatureList; | |
| 1001 | |
| 1002 | |
| 1003 struct LangSys | |
| 1004 { | |
| 1005 unsigned int get_feature_count () const | |
| 1006 { return featureIndex.len; } | |
| 1007 hb_tag_t get_feature_index (unsigned int i) const | |
| 1008 { return featureIndex[i]; } | |
| 1009 unsigned int get_feature_indexes (unsigned int start_offset, | |
| 1010 unsigned int *feature_count /* IN/OUT */, | |
| 1011 unsigned int *feature_indexes /* OUT */) const | |
| 1012 { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } | |
| 1013 void add_feature_indexes_to (hb_set_t *feature_indexes) const | |
| 1014 { featureIndex.add_indexes_to (feature_indexes); } | |
| 1015 | |
| 1016 bool has_required_feature () const { return reqFeatureIndex != 0xFFFFu; } | |
| 1017 unsigned int get_required_feature_index () const | |
| 1018 { | |
| 1019 if (reqFeatureIndex == 0xFFFFu) | |
| 1020 return Index::NOT_FOUND_INDEX; | |
| 1021 return reqFeatureIndex; | |
| 1022 } | |
| 1023 | |
| 1024 LangSys* copy (hb_serialize_context_t *c) const | |
| 1025 { | |
| 1026 TRACE_SERIALIZE (this); | |
| 1027 return_trace (c->embed (*this)); | |
| 1028 } | |
| 1029 | |
| 1030 bool compare (const LangSys& o, const hb_map_t *feature_index_map) const | |
| 1031 { | |
| 1032 if (reqFeatureIndex != o.reqFeatureIndex) | |
| 1033 return false; | |
| 1034 | |
| 1035 auto iter = | |
| 1036 + hb_iter (featureIndex) | |
| 1037 | hb_filter (feature_index_map) | |
| 1038 | hb_map (feature_index_map) | |
| 1039 ; | |
| 1040 | |
| 1041 auto o_iter = | |
| 1042 + hb_iter (o.featureIndex) | |
| 1043 | hb_filter (feature_index_map) | |
| 1044 | hb_map (feature_index_map) | |
| 1045 ; | |
| 1046 | |
| 1047 for (; iter && o_iter; iter++, o_iter++) | |
| 1048 { | |
| 1049 unsigned a = *iter; | |
| 1050 unsigned b = *o_iter; | |
| 1051 if (a != b) return false; | |
| 1052 } | |
| 1053 | |
| 1054 if (iter || o_iter) return false; | |
| 1055 | |
| 1056 return true; | |
| 1057 } | |
| 1058 | |
| 1059 void collect_features (hb_prune_langsys_context_t *c) const | |
| 1060 { | |
| 1061 if (!has_required_feature () && !get_feature_count ()) return; | |
| 1062 if (has_required_feature () && | |
| 1063 c->duplicate_feature_map->has (reqFeatureIndex)) | |
| 1064 c->new_feature_indexes->add (get_required_feature_index ()); | |
| 1065 | |
| 1066 + hb_iter (featureIndex) | |
| 1067 | hb_filter (c->duplicate_feature_map) | |
| 1068 | hb_sink (c->new_feature_indexes) | |
| 1069 ; | |
| 1070 } | |
| 1071 | |
| 1072 bool subset (hb_subset_context_t *c, | |
| 1073 hb_subset_layout_context_t *l, | |
| 1074 const Tag *tag = nullptr) const | |
| 1075 { | |
| 1076 TRACE_SUBSET (this); | |
| 1077 auto *out = c->serializer->start_embed (*this); | |
| 1078 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 1079 | |
| 1080 const uint32_t *v; | |
| 1081 out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu; | |
| 1082 | |
| 1083 if (!l->visitFeatureIndex (featureIndex.len)) | |
| 1084 return_trace (false); | |
| 1085 | |
| 1086 auto it = | |
| 1087 + hb_iter (featureIndex) | |
| 1088 | hb_filter (l->feature_index_map) | |
| 1089 | hb_map (l->feature_index_map) | |
| 1090 ; | |
| 1091 | |
| 1092 bool ret = bool (it); | |
| 1093 out->featureIndex.serialize (c->serializer, l, it); | |
| 1094 return_trace (ret); | |
| 1095 } | |
| 1096 | |
| 1097 bool sanitize (hb_sanitize_context_t *c, | |
| 1098 const Record_sanitize_closure_t * = nullptr) const | |
| 1099 { | |
| 1100 TRACE_SANITIZE (this); | |
| 1101 return_trace (c->check_struct (this) && featureIndex.sanitize (c)); | |
| 1102 } | |
| 1103 | |
| 1104 Offset16 lookupOrderZ; /* = Null (reserved for an offset to a | |
| 1105 * reordering table) */ | |
| 1106 HBUINT16 reqFeatureIndex;/* Index of a feature required for this | |
| 1107 * language system--if no required features | |
| 1108 * = 0xFFFFu */ | |
| 1109 IndexArray featureIndex; /* Array of indices into the FeatureList */ | |
| 1110 public: | |
| 1111 DEFINE_SIZE_ARRAY_SIZED (6, featureIndex); | |
| 1112 }; | |
| 1113 DECLARE_NULL_NAMESPACE_BYTES (OT, LangSys); | |
| 1114 | |
| 1115 struct Script | |
| 1116 { | |
| 1117 unsigned int get_lang_sys_count () const | |
| 1118 { return langSys.len; } | |
| 1119 const Tag& get_lang_sys_tag (unsigned int i) const | |
| 1120 { return langSys.get_tag (i); } | |
| 1121 unsigned int get_lang_sys_tags (unsigned int start_offset, | |
| 1122 unsigned int *lang_sys_count /* IN/OUT */, | |
| 1123 hb_tag_t *lang_sys_tags /* OUT */) const | |
| 1124 { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } | |
| 1125 const LangSys& get_lang_sys (unsigned int i) const | |
| 1126 { | |
| 1127 if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); | |
| 1128 return this+langSys[i].offset; | |
| 1129 } | |
| 1130 bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const | |
| 1131 { return langSys.find_index (tag, index); } | |
| 1132 | |
| 1133 bool has_default_lang_sys () const { return defaultLangSys != 0; } | |
| 1134 const LangSys& get_default_lang_sys () const { return this+defaultLangSys; } | |
| 1135 | |
| 1136 void prune_langsys (hb_prune_langsys_context_t *c, | |
| 1137 unsigned script_index) const | |
| 1138 { | |
| 1139 if (!has_default_lang_sys () && !get_lang_sys_count ()) return; | |
| 1140 if (!c->visitScript ()) return; | |
| 1141 | |
| 1142 if (!c->script_langsys_map->has (script_index)) | |
| 1143 { | |
| 1144 if (unlikely (!c->script_langsys_map->set (script_index, hb::unique_ptr<hb_set_t> {hb_set_create ()}))) | |
| 1145 return; | |
| 1146 } | |
| 1147 | |
| 1148 if (has_default_lang_sys ()) | |
| 1149 { | |
| 1150 //only collect features from non-redundant langsys | |
| 1151 const LangSys& d = get_default_lang_sys (); | |
| 1152 if (c->visitLangsys (d.get_feature_count ())) { | |
| 1153 d.collect_features (c); | |
| 1154 } | |
| 1155 | |
| 1156 for (auto _ : + hb_enumerate (langSys)) | |
| 1157 { | |
| 1158 const LangSys& l = this+_.second.offset; | |
| 1159 if (!c->visitLangsys (l.get_feature_count ())) continue; | |
| 1160 if (l.compare (d, c->duplicate_feature_map)) continue; | |
| 1161 | |
| 1162 l.collect_features (c); | |
| 1163 c->script_langsys_map->get (script_index)->add (_.first); | |
| 1164 } | |
| 1165 } | |
| 1166 else | |
| 1167 { | |
| 1168 for (auto _ : + hb_enumerate (langSys)) | |
| 1169 { | |
| 1170 const LangSys& l = this+_.second.offset; | |
| 1171 if (!c->visitLangsys (l.get_feature_count ())) continue; | |
| 1172 l.collect_features (c); | |
| 1173 c->script_langsys_map->get (script_index)->add (_.first); | |
| 1174 } | |
| 1175 } | |
| 1176 } | |
| 1177 | |
| 1178 bool subset (hb_subset_context_t *c, | |
| 1179 hb_subset_layout_context_t *l, | |
| 1180 const Tag *tag) const | |
| 1181 { | |
| 1182 TRACE_SUBSET (this); | |
| 1183 if (!l->visitScript ()) return_trace (false); | |
| 1184 if (tag && !c->plan->layout_scripts->has (*tag)) | |
| 1185 return false; | |
| 1186 | |
| 1187 auto *out = c->serializer->start_embed (*this); | |
| 1188 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 1189 | |
| 1190 bool defaultLang = false; | |
| 1191 if (has_default_lang_sys ()) | |
| 1192 { | |
| 1193 c->serializer->push (); | |
| 1194 const LangSys& ls = this+defaultLangSys; | |
| 1195 bool ret = ls.subset (c, l); | |
| 1196 if (!ret && tag && *tag != HB_TAG ('D', 'F', 'L', 'T')) | |
| 1197 { | |
| 1198 c->serializer->pop_discard (); | |
| 1199 out->defaultLangSys = 0; | |
| 1200 } | |
| 1201 else | |
| 1202 { | |
| 1203 c->serializer->add_link (out->defaultLangSys, c->serializer->pop_pack ()); | |
| 1204 defaultLang = true; | |
| 1205 } | |
| 1206 } | |
| 1207 | |
| 1208 const hb_set_t *active_langsys = l->script_langsys_map->get (l->cur_script_index); | |
| 1209 if (active_langsys) | |
| 1210 { | |
| 1211 + hb_enumerate (langSys) | |
| 1212 | hb_filter (active_langsys, hb_first) | |
| 1213 | hb_map (hb_second) | |
| 1214 | hb_filter ([=] (const Record<LangSys>& record) {return l->visitLangSys (); }) | |
| 1215 | hb_apply (subset_record_array (l, &(out->langSys), this)) | |
| 1216 ; | |
| 1217 } | |
| 1218 | |
| 1219 return_trace (bool (out->langSys.len) || defaultLang || l->table_tag == HB_OT_TAG_GSUB); | |
| 1220 } | |
| 1221 | |
| 1222 bool sanitize (hb_sanitize_context_t *c, | |
| 1223 const Record_sanitize_closure_t * = nullptr) const | |
| 1224 { | |
| 1225 TRACE_SANITIZE (this); | |
| 1226 return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this)); | |
| 1227 } | |
| 1228 | |
| 1229 protected: | |
| 1230 Offset16To<LangSys> | |
| 1231 defaultLangSys; /* Offset to DefaultLangSys table--from | |
| 1232 * beginning of Script table--may be Null */ | |
| 1233 RecordArrayOf<LangSys> | |
| 1234 langSys; /* Array of LangSysRecords--listed | |
| 1235 * alphabetically by LangSysTag */ | |
| 1236 public: | |
| 1237 DEFINE_SIZE_ARRAY_SIZED (4, langSys); | |
| 1238 }; | |
| 1239 | |
| 1240 struct RecordListOfScript : RecordListOf<Script> | |
| 1241 { | |
| 1242 bool subset (hb_subset_context_t *c, | |
| 1243 hb_subset_layout_context_t *l) const | |
| 1244 { | |
| 1245 TRACE_SUBSET (this); | |
| 1246 auto *out = c->serializer->start_embed (*this); | |
| 1247 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 1248 | |
| 1249 for (auto _ : + hb_enumerate (*this)) | |
| 1250 { | |
| 1251 auto snap = c->serializer->snapshot (); | |
| 1252 l->cur_script_index = _.first; | |
| 1253 bool ret = _.second.subset (l, this); | |
| 1254 if (!ret) c->serializer->revert (snap); | |
| 1255 else out->len++; | |
| 1256 } | |
| 1257 | |
| 1258 return_trace (true); | |
| 1259 } | |
| 1260 }; | |
| 1261 | |
| 1262 typedef RecordListOfScript ScriptList; | |
| 1263 | |
| 1264 | |
| 1265 | |
| 1266 struct LookupFlag : HBUINT16 | |
| 1267 { | |
| 1268 enum Flags { | |
| 1269 RightToLeft = 0x0001u, | |
| 1270 IgnoreBaseGlyphs = 0x0002u, | |
| 1271 IgnoreLigatures = 0x0004u, | |
| 1272 IgnoreMarks = 0x0008u, | |
| 1273 IgnoreFlags = 0x000Eu, | |
| 1274 UseMarkFilteringSet = 0x0010u, | |
| 1275 Reserved = 0x00E0u, | |
| 1276 MarkAttachmentType = 0xFF00u | |
| 1277 }; | |
| 1278 public: | |
| 1279 DEFINE_SIZE_STATIC (2); | |
| 1280 }; | |
| 1281 | |
| 1282 } /* namespace OT */ | |
| 1283 /* This has to be outside the namespace. */ | |
| 1284 HB_MARK_AS_FLAG_T (OT::LookupFlag::Flags); | |
| 1285 namespace OT { | |
| 1286 | |
| 1287 struct Lookup | |
| 1288 { | |
| 1289 unsigned int get_subtable_count () const { return subTable.len; } | |
| 1290 | |
| 1291 template <typename TSubTable> | |
| 1292 const Array16OfOffset16To<TSubTable>& get_subtables () const | |
| 1293 { return reinterpret_cast<const Array16OfOffset16To<TSubTable> &> (subTable); } | |
| 1294 template <typename TSubTable> | |
| 1295 Array16OfOffset16To<TSubTable>& get_subtables () | |
| 1296 { return reinterpret_cast<Array16OfOffset16To<TSubTable> &> (subTable); } | |
| 1297 | |
| 1298 template <typename TSubTable> | |
| 1299 const TSubTable& get_subtable (unsigned int i) const | |
| 1300 { return this+get_subtables<TSubTable> ()[i]; } | |
| 1301 template <typename TSubTable> | |
| 1302 TSubTable& get_subtable (unsigned int i) | |
| 1303 { return this+get_subtables<TSubTable> ()[i]; } | |
| 1304 | |
| 1305 unsigned int get_size () const | |
| 1306 { | |
| 1307 const HBUINT16 &markFilteringSet = StructAfter<const HBUINT16> (subTable); | |
| 1308 if (lookupFlag & LookupFlag::UseMarkFilteringSet) | |
| 1309 return (const char *) &StructAfter<const char> (markFilteringSet) - (const char *) this; | |
| 1310 return (const char *) &markFilteringSet - (const char *) this; | |
| 1311 } | |
| 1312 | |
| 1313 unsigned int get_type () const { return lookupType; } | |
| 1314 | |
| 1315 /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and | |
| 1316 * higher 16-bit is mark-filtering-set if the lookup uses one. | |
| 1317 * Not to be confused with glyph_props which is very similar. */ | |
| 1318 uint32_t get_props () const | |
| 1319 { | |
| 1320 unsigned int flag = lookupFlag; | |
| 1321 if (unlikely (flag & LookupFlag::UseMarkFilteringSet)) | |
| 1322 { | |
| 1323 const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable); | |
| 1324 flag += (markFilteringSet << 16); | |
| 1325 } | |
| 1326 return flag; | |
| 1327 } | |
| 1328 | |
| 1329 template <typename TSubTable, typename context_t, typename ...Ts> | |
| 1330 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const | |
| 1331 { | |
| 1332 unsigned int lookup_type = get_type (); | |
| 1333 TRACE_DISPATCH (this, lookup_type); | |
| 1334 unsigned int count = get_subtable_count (); | |
| 1335 for (unsigned int i = 0; i < count; i++) { | |
| 1336 typename context_t::return_t r = get_subtable<TSubTable> (i).dispatch (c, lookup_type, std::forward<Ts> (ds)...); | |
| 1337 if (c->stop_sublookup_iteration (r)) | |
| 1338 return_trace (r); | |
| 1339 } | |
| 1340 return_trace (c->default_return_value ()); | |
| 1341 } | |
| 1342 | |
| 1343 bool serialize (hb_serialize_context_t *c, | |
| 1344 unsigned int lookup_type, | |
| 1345 uint32_t lookup_props, | |
| 1346 unsigned int num_subtables) | |
| 1347 { | |
| 1348 TRACE_SERIALIZE (this); | |
| 1349 if (unlikely (!c->extend_min (this))) return_trace (false); | |
| 1350 lookupType = lookup_type; | |
| 1351 lookupFlag = lookup_props & 0xFFFFu; | |
| 1352 if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false); | |
| 1353 if (lookupFlag & LookupFlag::UseMarkFilteringSet) | |
| 1354 { | |
| 1355 if (unlikely (!c->extend (this))) return_trace (false); | |
| 1356 HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable); | |
| 1357 markFilteringSet = lookup_props >> 16; | |
| 1358 } | |
| 1359 return_trace (true); | |
| 1360 } | |
| 1361 | |
| 1362 template <typename TSubTable> | |
| 1363 bool subset (hb_subset_context_t *c) const | |
| 1364 { | |
| 1365 TRACE_SUBSET (this); | |
| 1366 auto *out = c->serializer->start_embed (*this); | |
| 1367 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 1368 out->lookupType = lookupType; | |
| 1369 out->lookupFlag = lookupFlag; | |
| 1370 | |
| 1371 const hb_set_t *glyphset = c->plan->glyphset_gsub (); | |
| 1372 unsigned int lookup_type = get_type (); | |
| 1373 + hb_iter (get_subtables <TSubTable> ()) | |
| 1374 | hb_filter ([this, glyphset, lookup_type] (const Offset16To<TSubTable> &_) { return (this+_).intersects (glyphset, lookup_type); }) | |
| 1375 | hb_apply (subset_offset_array (c, out->get_subtables<TSubTable> (), this, lookup_type)) | |
| 1376 ; | |
| 1377 | |
| 1378 if (lookupFlag & LookupFlag::UseMarkFilteringSet) | |
| 1379 { | |
| 1380 if (unlikely (!c->serializer->extend (out))) return_trace (false); | |
| 1381 const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable); | |
| 1382 HBUINT16 &outMarkFilteringSet = StructAfter<HBUINT16> (out->subTable); | |
| 1383 outMarkFilteringSet = markFilteringSet; | |
| 1384 } | |
| 1385 | |
| 1386 // Always keep the lookup even if it's empty. The rest of layout subsetting depends on lookup | |
| 1387 // indices being consistent with those computed during planning. So if an empty lookup is | |
| 1388 // discarded during the subset phase it will invalidate all subsequent lookup indices. | |
| 1389 // Generally we shouldn't end up with an empty lookup as we pre-prune them during the planning | |
| 1390 // phase, but it can happen in rare cases such as when during closure subtable is considered | |
| 1391 // degenerate (see: https://github.com/harfbuzz/harfbuzz/issues/3853) | |
| 1392 return true; | |
| 1393 } | |
| 1394 | |
| 1395 template <typename TSubTable> | |
| 1396 bool sanitize (hb_sanitize_context_t *c) const | |
| 1397 { | |
| 1398 TRACE_SANITIZE (this); | |
| 1399 if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false); | |
| 1400 | |
| 1401 unsigned subtables = get_subtable_count (); | |
| 1402 if (unlikely (!c->visit_subtables (subtables))) return_trace (false); | |
| 1403 | |
| 1404 if (lookupFlag & LookupFlag::UseMarkFilteringSet) | |
| 1405 { | |
| 1406 const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable); | |
| 1407 if (!markFilteringSet.sanitize (c)) return_trace (false); | |
| 1408 } | |
| 1409 | |
| 1410 if (unlikely (!get_subtables<TSubTable> ().sanitize (c, this, get_type ()))) | |
| 1411 return_trace (false); | |
| 1412 | |
| 1413 if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) | |
| 1414 { | |
| 1415 /* The spec says all subtables of an Extension lookup should | |
| 1416 * have the same type, which shall not be the Extension type | |
| 1417 * itself (but we already checked for that). | |
| 1418 * This is specially important if one has a reverse type! | |
| 1419 * | |
| 1420 * We only do this if sanitizer edit_count is zero. Otherwise, | |
| 1421 * some of the subtables might have become insane after they | |
| 1422 * were sanity-checked by the edits of subsequent subtables. | |
| 1423 * https://bugs.chromium.org/p/chromium/issues/detail?id=960331 | |
| 1424 */ | |
| 1425 unsigned int type = get_subtable<TSubTable> (0).u.extension.get_type (); | |
| 1426 for (unsigned int i = 1; i < subtables; i++) | |
| 1427 if (get_subtable<TSubTable> (i).u.extension.get_type () != type) | |
| 1428 return_trace (false); | |
| 1429 } | |
| 1430 return_trace (true); | |
| 1431 } | |
| 1432 | |
| 1433 protected: | |
| 1434 HBUINT16 lookupType; /* Different enumerations for GSUB and GPOS */ | |
| 1435 HBUINT16 lookupFlag; /* Lookup qualifiers */ | |
| 1436 Array16Of<Offset16> | |
| 1437 subTable; /* Array of SubTables */ | |
| 1438 /*HBUINT16 markFilteringSetX[HB_VAR_ARRAY];*//* Index (base 0) into GDEF mark glyph sets | |
| 1439 * structure. This field is only present if bit | |
| 1440 * UseMarkFilteringSet of lookup flags is set. */ | |
| 1441 public: | |
| 1442 DEFINE_SIZE_ARRAY (6, subTable); | |
| 1443 }; | |
| 1444 | |
| 1445 template <typename Types> | |
| 1446 using LookupList = List16OfOffsetTo<Lookup, typename Types::HBUINT>; | |
| 1447 | |
| 1448 template <typename TLookup, typename OffsetType> | |
| 1449 struct LookupOffsetList : List16OfOffsetTo<TLookup, OffsetType> | |
| 1450 { | |
| 1451 bool subset (hb_subset_context_t *c, | |
| 1452 hb_subset_layout_context_t *l) const | |
| 1453 { | |
| 1454 TRACE_SUBSET (this); | |
| 1455 auto *out = c->serializer->start_embed (this); | |
| 1456 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 1457 | |
| 1458 + hb_enumerate (*this) | |
| 1459 | hb_filter (l->lookup_index_map, hb_first) | |
| 1460 | hb_map (hb_second) | |
| 1461 | hb_apply (subset_offset_array (c, *out, this)) | |
| 1462 ; | |
| 1463 return_trace (true); | |
| 1464 } | |
| 1465 | |
| 1466 bool sanitize (hb_sanitize_context_t *c) const | |
| 1467 { | |
| 1468 TRACE_SANITIZE (this); | |
| 1469 return_trace (List16OfOffset16To<TLookup>::sanitize (c, this)); | |
| 1470 } | |
| 1471 }; | |
| 1472 | |
| 1473 | |
| 1474 /* | |
| 1475 * Coverage Table | |
| 1476 */ | |
| 1477 | |
| 1478 | |
| 1479 static bool ClassDef_remap_and_serialize (hb_serialize_context_t *c, | |
| 1480 const hb_set_t &klasses, | |
| 1481 bool use_class_zero, | |
| 1482 hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */ | |
| 1483 hb_map_t *klass_map /*IN/OUT*/) | |
| 1484 { | |
| 1485 if (!klass_map) | |
| 1486 return ClassDef_serialize (c, glyph_and_klass.iter ()); | |
| 1487 | |
| 1488 /* any glyph not assigned a class value falls into Class zero (0), | |
| 1489 * if any glyph assigned to class 0, remapping must start with 0->0*/ | |
| 1490 if (!use_class_zero) | |
| 1491 klass_map->set (0, 0); | |
| 1492 | |
| 1493 unsigned idx = klass_map->has (0) ? 1 : 0; | |
| 1494 for (const unsigned k: klasses) | |
| 1495 { | |
| 1496 if (klass_map->has (k)) continue; | |
| 1497 klass_map->set (k, idx); | |
| 1498 idx++; | |
| 1499 } | |
| 1500 | |
| 1501 | |
| 1502 for (unsigned i = 0; i < glyph_and_klass.length; i++) | |
| 1503 { | |
| 1504 hb_codepoint_t klass = glyph_and_klass[i].second; | |
| 1505 glyph_and_klass[i].second = klass_map->get (klass); | |
| 1506 } | |
| 1507 | |
| 1508 c->propagate_error (glyph_and_klass, klasses); | |
| 1509 return ClassDef_serialize (c, glyph_and_klass.iter ()); | |
| 1510 } | |
| 1511 | |
| 1512 /* | |
| 1513 * Class Definition Table | |
| 1514 */ | |
| 1515 | |
| 1516 template <typename Types> | |
| 1517 struct ClassDefFormat1_3 | |
| 1518 { | |
| 1519 friend struct ClassDef; | |
| 1520 | |
| 1521 private: | |
| 1522 unsigned int get_class (hb_codepoint_t glyph_id) const | |
| 1523 { | |
| 1524 return classValue[(unsigned int) (glyph_id - startGlyph)]; | |
| 1525 } | |
| 1526 | |
| 1527 unsigned get_population () const | |
| 1528 { | |
| 1529 return classValue.len; | |
| 1530 } | |
| 1531 | |
| 1532 template<typename Iterator, | |
| 1533 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> | |
| 1534 bool serialize (hb_serialize_context_t *c, | |
| 1535 Iterator it) | |
| 1536 { | |
| 1537 TRACE_SERIALIZE (this); | |
| 1538 if (unlikely (!c->extend_min (this))) return_trace (false); | |
| 1539 | |
| 1540 if (unlikely (!it)) | |
| 1541 { | |
| 1542 classFormat = 1; | |
| 1543 startGlyph = 0; | |
| 1544 classValue.len = 0; | |
| 1545 return_trace (true); | |
| 1546 } | |
| 1547 | |
| 1548 hb_codepoint_t glyph_min = (*it).first; | |
| 1549 hb_codepoint_t glyph_max = + it | |
| 1550 | hb_map (hb_first) | |
| 1551 | hb_reduce (hb_max, 0u); | |
| 1552 unsigned glyph_count = glyph_max - glyph_min + 1; | |
| 1553 | |
| 1554 startGlyph = glyph_min; | |
| 1555 if (unlikely (!classValue.serialize (c, glyph_count))) return_trace (false); | |
| 1556 for (const hb_pair_t<hb_codepoint_t, uint32_t> gid_klass_pair : + it) | |
| 1557 { | |
| 1558 unsigned idx = gid_klass_pair.first - glyph_min; | |
| 1559 classValue[idx] = gid_klass_pair.second; | |
| 1560 } | |
| 1561 return_trace (true); | |
| 1562 } | |
| 1563 | |
| 1564 bool subset (hb_subset_context_t *c, | |
| 1565 hb_map_t *klass_map = nullptr /*OUT*/, | |
| 1566 bool keep_empty_table = true, | |
| 1567 bool use_class_zero = true, | |
| 1568 const Coverage* glyph_filter = nullptr) const | |
| 1569 { | |
| 1570 TRACE_SUBSET (this); | |
| 1571 const hb_map_t &glyph_map = *c->plan->glyph_map_gsub; | |
| 1572 | |
| 1573 hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass; | |
| 1574 hb_set_t orig_klasses; | |
| 1575 | |
| 1576 hb_codepoint_t start = startGlyph; | |
| 1577 hb_codepoint_t end = start + classValue.len; | |
| 1578 | |
| 1579 for (const hb_codepoint_t gid : + hb_range (start, end)) | |
| 1580 { | |
| 1581 hb_codepoint_t new_gid = glyph_map[gid]; | |
| 1582 if (new_gid == HB_MAP_VALUE_INVALID) continue; | |
| 1583 if (glyph_filter && !glyph_filter->has(gid)) continue; | |
| 1584 | |
| 1585 unsigned klass = classValue[gid - start]; | |
| 1586 if (!klass) continue; | |
| 1587 | |
| 1588 glyph_and_klass.push (hb_pair (new_gid, klass)); | |
| 1589 orig_klasses.add (klass); | |
| 1590 } | |
| 1591 | |
| 1592 unsigned glyph_count = glyph_filter | |
| 1593 ? hb_len (hb_iter (glyph_map.keys()) | hb_filter (glyph_filter)) | |
| 1594 : glyph_map.get_population (); | |
| 1595 use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length; | |
| 1596 if (!ClassDef_remap_and_serialize (c->serializer, | |
| 1597 orig_klasses, | |
| 1598 use_class_zero, | |
| 1599 glyph_and_klass, | |
| 1600 klass_map)) | |
| 1601 return_trace (false); | |
| 1602 return_trace (keep_empty_table || (bool) glyph_and_klass); | |
| 1603 } | |
| 1604 | |
| 1605 bool sanitize (hb_sanitize_context_t *c) const | |
| 1606 { | |
| 1607 TRACE_SANITIZE (this); | |
| 1608 return_trace (c->check_struct (this) && classValue.sanitize (c)); | |
| 1609 } | |
| 1610 | |
| 1611 unsigned cost () const { return 1; } | |
| 1612 | |
| 1613 template <typename set_t> | |
| 1614 bool collect_coverage (set_t *glyphs) const | |
| 1615 { | |
| 1616 unsigned int start = 0; | |
| 1617 unsigned int count = classValue.len; | |
| 1618 for (unsigned int i = 0; i < count; i++) | |
| 1619 { | |
| 1620 if (classValue[i]) | |
| 1621 continue; | |
| 1622 | |
| 1623 if (start != i) | |
| 1624 if (unlikely (!glyphs->add_range (startGlyph + start, startGlyph + i))) | |
| 1625 return false; | |
| 1626 | |
| 1627 start = i + 1; | |
| 1628 } | |
| 1629 if (start != count) | |
| 1630 if (unlikely (!glyphs->add_range (startGlyph + start, startGlyph + count))) | |
| 1631 return false; | |
| 1632 | |
| 1633 return true; | |
| 1634 } | |
| 1635 | |
| 1636 template <typename set_t> | |
| 1637 bool collect_class (set_t *glyphs, unsigned klass) const | |
| 1638 { | |
| 1639 unsigned int count = classValue.len; | |
| 1640 for (unsigned int i = 0; i < count; i++) | |
| 1641 if (classValue[i] == klass) glyphs->add (startGlyph + i); | |
| 1642 return true; | |
| 1643 } | |
| 1644 | |
| 1645 bool intersects (const hb_set_t *glyphs) const | |
| 1646 { | |
| 1647 hb_codepoint_t start = startGlyph; | |
| 1648 hb_codepoint_t end = startGlyph + classValue.len; | |
| 1649 for (hb_codepoint_t iter = startGlyph - 1; | |
| 1650 glyphs->next (&iter) && iter < end;) | |
| 1651 if (classValue[iter - start]) return true; | |
| 1652 return false; | |
| 1653 } | |
| 1654 bool intersects_class (const hb_set_t *glyphs, uint16_t klass) const | |
| 1655 { | |
| 1656 unsigned int count = classValue.len; | |
| 1657 if (klass == 0) | |
| 1658 { | |
| 1659 /* Match if there's any glyph that is not listed! */ | |
| 1660 hb_codepoint_t g = HB_SET_VALUE_INVALID; | |
| 1661 if (!glyphs->next (&g)) return false; | |
| 1662 if (g < startGlyph) return true; | |
| 1663 g = startGlyph + count - 1; | |
| 1664 if (glyphs->next (&g)) return true; | |
| 1665 /* Fall through. */ | |
| 1666 } | |
| 1667 /* TODO Speed up, using set overlap first? */ | |
| 1668 /* TODO(iter) Rewrite as dagger. */ | |
| 1669 const HBUINT16 *arr = classValue.arrayZ; | |
| 1670 for (unsigned int i = 0; i < count; i++) | |
| 1671 if (arr[i] == klass && glyphs->has (startGlyph + i)) | |
| 1672 return true; | |
| 1673 return false; | |
| 1674 } | |
| 1675 | |
| 1676 void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const | |
| 1677 { | |
| 1678 unsigned count = classValue.len; | |
| 1679 if (klass == 0) | |
| 1680 { | |
| 1681 unsigned start_glyph = startGlyph; | |
| 1682 for (uint32_t g = HB_SET_VALUE_INVALID; | |
| 1683 glyphs->next (&g) && g < start_glyph;) | |
| 1684 intersect_glyphs->add (g); | |
| 1685 | |
| 1686 for (uint32_t g = startGlyph + count - 1; | |
| 1687 glyphs-> next (&g);) | |
| 1688 intersect_glyphs->add (g); | |
| 1689 | |
| 1690 return; | |
| 1691 } | |
| 1692 | |
| 1693 for (unsigned i = 0; i < count; i++) | |
| 1694 if (classValue[i] == klass && glyphs->has (startGlyph + i)) | |
| 1695 intersect_glyphs->add (startGlyph + i); | |
| 1696 | |
| 1697 #if 0 | |
| 1698 /* The following implementation is faster asymptotically, but slower | |
| 1699 * in practice. */ | |
| 1700 unsigned start_glyph = startGlyph; | |
| 1701 unsigned end_glyph = start_glyph + count; | |
| 1702 for (unsigned g = startGlyph - 1; | |
| 1703 glyphs->next (&g) && g < end_glyph;) | |
| 1704 if (classValue.arrayZ[g - start_glyph] == klass) | |
| 1705 intersect_glyphs->add (g); | |
| 1706 #endif | |
| 1707 } | |
| 1708 | |
| 1709 void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const | |
| 1710 { | |
| 1711 if (glyphs->is_empty ()) return; | |
| 1712 hb_codepoint_t end_glyph = startGlyph + classValue.len - 1; | |
| 1713 if (glyphs->get_min () < startGlyph || | |
| 1714 glyphs->get_max () > end_glyph) | |
| 1715 intersect_classes->add (0); | |
| 1716 | |
| 1717 for (const auto& _ : + hb_enumerate (classValue)) | |
| 1718 { | |
| 1719 hb_codepoint_t g = startGlyph + _.first; | |
| 1720 if (glyphs->has (g)) | |
| 1721 intersect_classes->add (_.second); | |
| 1722 } | |
| 1723 } | |
| 1724 | |
| 1725 protected: | |
| 1726 HBUINT16 classFormat; /* Format identifier--format = 1 */ | |
| 1727 typename Types::HBGlyphID | |
| 1728 startGlyph; /* First GlyphID of the classValueArray */ | |
| 1729 typename Types::template ArrayOf<HBUINT16> | |
| 1730 classValue; /* Array of Class Values--one per GlyphID */ | |
| 1731 public: | |
| 1732 DEFINE_SIZE_ARRAY (2 + 2 * Types::size, classValue); | |
| 1733 }; | |
| 1734 | |
| 1735 template <typename Types> | |
| 1736 struct ClassDefFormat2_4 | |
| 1737 { | |
| 1738 friend struct ClassDef; | |
| 1739 | |
| 1740 private: | |
| 1741 unsigned int get_class (hb_codepoint_t glyph_id) const | |
| 1742 { | |
| 1743 return rangeRecord.bsearch (glyph_id).value; | |
| 1744 } | |
| 1745 | |
| 1746 unsigned get_population () const | |
| 1747 { | |
| 1748 typename Types::large_int ret = 0; | |
| 1749 for (const auto &r : rangeRecord) | |
| 1750 ret += r.get_population (); | |
| 1751 return ret > UINT_MAX ? UINT_MAX : (unsigned) ret; | |
| 1752 } | |
| 1753 | |
| 1754 template<typename Iterator, | |
| 1755 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> | |
| 1756 bool serialize (hb_serialize_context_t *c, | |
| 1757 Iterator it) | |
| 1758 { | |
| 1759 TRACE_SERIALIZE (this); | |
| 1760 if (unlikely (!c->extend_min (this))) return_trace (false); | |
| 1761 | |
| 1762 if (unlikely (!it)) | |
| 1763 { | |
| 1764 classFormat = 2; | |
| 1765 rangeRecord.len = 0; | |
| 1766 return_trace (true); | |
| 1767 } | |
| 1768 | |
| 1769 unsigned num_ranges = 1; | |
| 1770 hb_codepoint_t prev_gid = (*it).first; | |
| 1771 unsigned prev_klass = (*it).second; | |
| 1772 | |
| 1773 RangeRecord<Types> range_rec; | |
| 1774 range_rec.first = prev_gid; | |
| 1775 range_rec.last = prev_gid; | |
| 1776 range_rec.value = prev_klass; | |
| 1777 | |
| 1778 auto *record = c->copy (range_rec); | |
| 1779 if (unlikely (!record)) return_trace (false); | |
| 1780 | |
| 1781 for (const auto gid_klass_pair : + (++it)) | |
| 1782 { | |
| 1783 hb_codepoint_t cur_gid = gid_klass_pair.first; | |
| 1784 unsigned cur_klass = gid_klass_pair.second; | |
| 1785 | |
| 1786 if (cur_gid != prev_gid + 1 || | |
| 1787 cur_klass != prev_klass) | |
| 1788 { | |
| 1789 if (unlikely (!record)) break; | |
| 1790 record->last = prev_gid; | |
| 1791 num_ranges++; | |
| 1792 | |
| 1793 range_rec.first = cur_gid; | |
| 1794 range_rec.last = cur_gid; | |
| 1795 range_rec.value = cur_klass; | |
| 1796 | |
| 1797 record = c->copy (range_rec); | |
| 1798 } | |
| 1799 | |
| 1800 prev_klass = cur_klass; | |
| 1801 prev_gid = cur_gid; | |
| 1802 } | |
| 1803 | |
| 1804 if (likely (record)) record->last = prev_gid; | |
| 1805 rangeRecord.len = num_ranges; | |
| 1806 return_trace (true); | |
| 1807 } | |
| 1808 | |
| 1809 bool subset (hb_subset_context_t *c, | |
| 1810 hb_map_t *klass_map = nullptr /*OUT*/, | |
| 1811 bool keep_empty_table = true, | |
| 1812 bool use_class_zero = true, | |
| 1813 const Coverage* glyph_filter = nullptr) const | |
| 1814 { | |
| 1815 TRACE_SUBSET (this); | |
| 1816 const hb_map_t &glyph_map = *c->plan->glyph_map_gsub; | |
| 1817 const hb_set_t &glyph_set = *c->plan->glyphset_gsub (); | |
| 1818 | |
| 1819 hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass; | |
| 1820 hb_set_t orig_klasses; | |
| 1821 | |
| 1822 if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2 | |
| 1823 < get_population ()) | |
| 1824 { | |
| 1825 for (hb_codepoint_t g : glyph_set) | |
| 1826 { | |
| 1827 unsigned klass = get_class (g); | |
| 1828 if (!klass) continue; | |
| 1829 hb_codepoint_t new_gid = glyph_map[g]; | |
| 1830 if (new_gid == HB_MAP_VALUE_INVALID) continue; | |
| 1831 if (glyph_filter && !glyph_filter->has (g)) continue; | |
| 1832 glyph_and_klass.push (hb_pair (new_gid, klass)); | |
| 1833 orig_klasses.add (klass); | |
| 1834 } | |
| 1835 } | |
| 1836 else | |
| 1837 { | |
| 1838 unsigned num_source_glyphs = c->plan->source->get_num_glyphs (); | |
| 1839 for (auto &range : rangeRecord) | |
| 1840 { | |
| 1841 unsigned klass = range.value; | |
| 1842 if (!klass) continue; | |
| 1843 hb_codepoint_t start = range.first; | |
| 1844 hb_codepoint_t end = hb_min (range.last + 1, num_source_glyphs); | |
| 1845 for (hb_codepoint_t g = start; g < end; g++) | |
| 1846 { | |
| 1847 hb_codepoint_t new_gid = glyph_map[g]; | |
| 1848 if (new_gid == HB_MAP_VALUE_INVALID) continue; | |
| 1849 if (glyph_filter && !glyph_filter->has (g)) continue; | |
| 1850 | |
| 1851 glyph_and_klass.push (hb_pair (new_gid, klass)); | |
| 1852 orig_klasses.add (klass); | |
| 1853 } | |
| 1854 } | |
| 1855 } | |
| 1856 | |
| 1857 const hb_set_t& glyphset = *c->plan->glyphset_gsub (); | |
| 1858 unsigned glyph_count = glyph_filter | |
| 1859 ? hb_len (hb_iter (glyphset) | hb_filter (glyph_filter)) | |
| 1860 : glyph_map.get_population (); | |
| 1861 use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length; | |
| 1862 if (!ClassDef_remap_and_serialize (c->serializer, | |
| 1863 orig_klasses, | |
| 1864 use_class_zero, | |
| 1865 glyph_and_klass, | |
| 1866 klass_map)) | |
| 1867 return_trace (false); | |
| 1868 return_trace (keep_empty_table || (bool) glyph_and_klass); | |
| 1869 } | |
| 1870 | |
| 1871 bool sanitize (hb_sanitize_context_t *c) const | |
| 1872 { | |
| 1873 TRACE_SANITIZE (this); | |
| 1874 return_trace (rangeRecord.sanitize (c)); | |
| 1875 } | |
| 1876 | |
| 1877 unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ } | |
| 1878 | |
| 1879 template <typename set_t> | |
| 1880 bool collect_coverage (set_t *glyphs) const | |
| 1881 { | |
| 1882 for (auto &range : rangeRecord) | |
| 1883 if (range.value) | |
| 1884 if (unlikely (!range.collect_coverage (glyphs))) | |
| 1885 return false; | |
| 1886 return true; | |
| 1887 } | |
| 1888 | |
| 1889 template <typename set_t> | |
| 1890 bool collect_class (set_t *glyphs, unsigned int klass) const | |
| 1891 { | |
| 1892 for (auto &range : rangeRecord) | |
| 1893 { | |
| 1894 if (range.value == klass) | |
| 1895 if (unlikely (!range.collect_coverage (glyphs))) | |
| 1896 return false; | |
| 1897 } | |
| 1898 return true; | |
| 1899 } | |
| 1900 | |
| 1901 bool intersects (const hb_set_t *glyphs) const | |
| 1902 { | |
| 1903 if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) | |
| 1904 { | |
| 1905 for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) | |
| 1906 if (get_class (g)) | |
| 1907 return true; | |
| 1908 return false; | |
| 1909 } | |
| 1910 | |
| 1911 return hb_any (+ hb_iter (rangeRecord) | |
| 1912 | hb_map ([glyphs] (const RangeRecord<Types> &range) { return range.intersects (*glyphs) && range.value; })); | |
| 1913 } | |
| 1914 bool intersects_class (const hb_set_t *glyphs, uint16_t klass) const | |
| 1915 { | |
| 1916 if (klass == 0) | |
| 1917 { | |
| 1918 /* Match if there's any glyph that is not listed! */ | |
| 1919 hb_codepoint_t g = HB_SET_VALUE_INVALID; | |
| 1920 for (auto &range : rangeRecord) | |
| 1921 { | |
| 1922 if (!glyphs->next (&g)) | |
| 1923 break; | |
| 1924 if (g < range.first) | |
| 1925 return true; | |
| 1926 g = range.last; | |
| 1927 } | |
| 1928 if (g != HB_SET_VALUE_INVALID && glyphs->next (&g)) | |
| 1929 return true; | |
| 1930 /* Fall through. */ | |
| 1931 } | |
| 1932 for (const auto &range : rangeRecord) | |
| 1933 if (range.value == klass && range.intersects (*glyphs)) | |
| 1934 return true; | |
| 1935 return false; | |
| 1936 } | |
| 1937 | |
| 1938 void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const | |
| 1939 { | |
| 1940 if (klass == 0) | |
| 1941 { | |
| 1942 hb_codepoint_t g = HB_SET_VALUE_INVALID; | |
| 1943 for (auto &range : rangeRecord) | |
| 1944 { | |
| 1945 if (!glyphs->next (&g)) | |
| 1946 goto done; | |
| 1947 while (g < range.first) | |
| 1948 { | |
| 1949 intersect_glyphs->add (g); | |
| 1950 if (!glyphs->next (&g)) | |
| 1951 goto done; | |
| 1952 } | |
| 1953 g = range.last; | |
| 1954 } | |
| 1955 while (glyphs->next (&g)) | |
| 1956 intersect_glyphs->add (g); | |
| 1957 done: | |
| 1958 | |
| 1959 return; | |
| 1960 } | |
| 1961 | |
| 1962 unsigned count = rangeRecord.len; | |
| 1963 if (count > glyphs->get_population () * hb_bit_storage (count) * 8) | |
| 1964 { | |
| 1965 for (hb_codepoint_t g = HB_SET_VALUE_INVALID; | |
| 1966 glyphs->next (&g);) | |
| 1967 { | |
| 1968 unsigned i; | |
| 1969 if (rangeRecord.as_array ().bfind (g, &i) && | |
| 1970 rangeRecord.arrayZ[i].value == klass) | |
| 1971 intersect_glyphs->add (g); | |
| 1972 } | |
| 1973 return; | |
| 1974 } | |
| 1975 | |
| 1976 for (auto &range : rangeRecord) | |
| 1977 { | |
| 1978 if (range.value != klass) continue; | |
| 1979 | |
| 1980 unsigned end = range.last + 1; | |
| 1981 for (hb_codepoint_t g = range.first - 1; | |
| 1982 glyphs->next (&g) && g < end;) | |
| 1983 intersect_glyphs->add (g); | |
| 1984 } | |
| 1985 } | |
| 1986 | |
| 1987 void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const | |
| 1988 { | |
| 1989 if (glyphs->is_empty ()) return; | |
| 1990 | |
| 1991 hb_codepoint_t g = HB_SET_VALUE_INVALID; | |
| 1992 for (auto &range : rangeRecord) | |
| 1993 { | |
| 1994 if (!glyphs->next (&g)) | |
| 1995 break; | |
| 1996 if (g < range.first) | |
| 1997 { | |
| 1998 intersect_classes->add (0); | |
| 1999 break; | |
| 2000 } | |
| 2001 g = range.last; | |
| 2002 } | |
| 2003 if (g != HB_SET_VALUE_INVALID && glyphs->next (&g)) | |
| 2004 intersect_classes->add (0); | |
| 2005 | |
| 2006 for (const auto& range : rangeRecord) | |
| 2007 if (range.intersects (*glyphs)) | |
| 2008 intersect_classes->add (range.value); | |
| 2009 } | |
| 2010 | |
| 2011 protected: | |
| 2012 HBUINT16 classFormat; /* Format identifier--format = 2 */ | |
| 2013 typename Types::template SortedArrayOf<RangeRecord<Types>> | |
| 2014 rangeRecord; /* Array of glyph ranges--ordered by | |
| 2015 * Start GlyphID */ | |
| 2016 public: | |
| 2017 DEFINE_SIZE_ARRAY (2 + Types::size, rangeRecord); | |
| 2018 }; | |
| 2019 | |
| 2020 struct ClassDef | |
| 2021 { | |
| 2022 /* Has interface. */ | |
| 2023 unsigned operator [] (hb_codepoint_t k) const { return get (k); } | |
| 2024 bool has (hb_codepoint_t k) const { return (*this)[k]; } | |
| 2025 /* Projection. */ | |
| 2026 hb_codepoint_t operator () (hb_codepoint_t k) const { return get (k); } | |
| 2027 | |
| 2028 unsigned int get (hb_codepoint_t k) const { return get_class (k); } | |
| 2029 unsigned int get_class (hb_codepoint_t glyph_id) const | |
| 2030 { | |
| 2031 switch (u.format) { | |
| 2032 case 1: return u.format1.get_class (glyph_id); | |
| 2033 case 2: return u.format2.get_class (glyph_id); | |
| 2034 #ifndef HB_NO_BEYOND_64K | |
| 2035 case 3: return u.format3.get_class (glyph_id); | |
| 2036 case 4: return u.format4.get_class (glyph_id); | |
| 2037 #endif | |
| 2038 default:return 0; | |
| 2039 } | |
| 2040 } | |
| 2041 | |
| 2042 unsigned get_population () const | |
| 2043 { | |
| 2044 switch (u.format) { | |
| 2045 case 1: return u.format1.get_population (); | |
| 2046 case 2: return u.format2.get_population (); | |
| 2047 #ifndef HB_NO_BEYOND_64K | |
| 2048 case 3: return u.format3.get_population (); | |
| 2049 case 4: return u.format4.get_population (); | |
| 2050 #endif | |
| 2051 default:return NOT_COVERED; | |
| 2052 } | |
| 2053 } | |
| 2054 | |
| 2055 template<typename Iterator, | |
| 2056 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> | |
| 2057 bool serialize (hb_serialize_context_t *c, Iterator it_with_class_zero) | |
| 2058 { | |
| 2059 TRACE_SERIALIZE (this); | |
| 2060 if (unlikely (!c->extend_min (this))) return_trace (false); | |
| 2061 | |
| 2062 auto it = + it_with_class_zero | hb_filter (hb_second); | |
| 2063 | |
| 2064 unsigned format = 2; | |
| 2065 hb_codepoint_t glyph_max = 0; | |
| 2066 if (likely (it)) | |
| 2067 { | |
| 2068 hb_codepoint_t glyph_min = (*it).first; | |
| 2069 glyph_max = glyph_min; | |
| 2070 | |
| 2071 unsigned num_glyphs = 0; | |
| 2072 unsigned num_ranges = 1; | |
| 2073 hb_codepoint_t prev_gid = glyph_min; | |
| 2074 unsigned prev_klass = (*it).second; | |
| 2075 | |
| 2076 for (const auto gid_klass_pair : it) | |
| 2077 { | |
| 2078 hb_codepoint_t cur_gid = gid_klass_pair.first; | |
| 2079 unsigned cur_klass = gid_klass_pair.second; | |
| 2080 num_glyphs++; | |
| 2081 if (cur_gid == glyph_min) continue; | |
| 2082 if (cur_gid > glyph_max) glyph_max = cur_gid; | |
| 2083 if (cur_gid != prev_gid + 1 || | |
| 2084 cur_klass != prev_klass) | |
| 2085 num_ranges++; | |
| 2086 | |
| 2087 prev_gid = cur_gid; | |
| 2088 prev_klass = cur_klass; | |
| 2089 } | |
| 2090 | |
| 2091 if (num_glyphs && 1 + (glyph_max - glyph_min + 1) <= num_ranges * 3) | |
| 2092 format = 1; | |
| 2093 } | |
| 2094 | |
| 2095 #ifndef HB_NO_BEYOND_64K | |
| 2096 if (glyph_max > 0xFFFFu) | |
| 2097 format += 2; | |
| 2098 #endif | |
| 2099 | |
| 2100 u.format = format; | |
| 2101 | |
| 2102 switch (u.format) | |
| 2103 { | |
| 2104 case 1: return_trace (u.format1.serialize (c, it)); | |
| 2105 case 2: return_trace (u.format2.serialize (c, it)); | |
| 2106 #ifndef HB_NO_BEYOND_64K | |
| 2107 case 3: return_trace (u.format3.serialize (c, it)); | |
| 2108 case 4: return_trace (u.format4.serialize (c, it)); | |
| 2109 #endif | |
| 2110 default:return_trace (false); | |
| 2111 } | |
| 2112 } | |
| 2113 | |
| 2114 bool subset (hb_subset_context_t *c, | |
| 2115 hb_map_t *klass_map = nullptr /*OUT*/, | |
| 2116 bool keep_empty_table = true, | |
| 2117 bool use_class_zero = true, | |
| 2118 const Coverage* glyph_filter = nullptr) const | |
| 2119 { | |
| 2120 TRACE_SUBSET (this); | |
| 2121 switch (u.format) { | |
| 2122 case 1: return_trace (u.format1.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); | |
| 2123 case 2: return_trace (u.format2.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); | |
| 2124 #ifndef HB_NO_BEYOND_64K | |
| 2125 case 3: return_trace (u.format3.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); | |
| 2126 case 4: return_trace (u.format4.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); | |
| 2127 #endif | |
| 2128 default:return_trace (false); | |
| 2129 } | |
| 2130 } | |
| 2131 | |
| 2132 bool sanitize (hb_sanitize_context_t *c) const | |
| 2133 { | |
| 2134 TRACE_SANITIZE (this); | |
| 2135 if (!u.format.sanitize (c)) return_trace (false); | |
| 2136 switch (u.format) { | |
| 2137 case 1: return_trace (u.format1.sanitize (c)); | |
| 2138 case 2: return_trace (u.format2.sanitize (c)); | |
| 2139 #ifndef HB_NO_BEYOND_64K | |
| 2140 case 3: return_trace (u.format3.sanitize (c)); | |
| 2141 case 4: return_trace (u.format4.sanitize (c)); | |
| 2142 #endif | |
| 2143 default:return_trace (true); | |
| 2144 } | |
| 2145 } | |
| 2146 | |
| 2147 unsigned cost () const | |
| 2148 { | |
| 2149 switch (u.format) { | |
| 2150 case 1: return u.format1.cost (); | |
| 2151 case 2: return u.format2.cost (); | |
| 2152 #ifndef HB_NO_BEYOND_64K | |
| 2153 case 3: return u.format3.cost (); | |
| 2154 case 4: return u.format4.cost (); | |
| 2155 #endif | |
| 2156 default:return 0u; | |
| 2157 } | |
| 2158 } | |
| 2159 | |
| 2160 /* Might return false if array looks unsorted. | |
| 2161 * Used for faster rejection of corrupt data. */ | |
| 2162 template <typename set_t> | |
| 2163 bool collect_coverage (set_t *glyphs) const | |
| 2164 { | |
| 2165 switch (u.format) { | |
| 2166 case 1: return u.format1.collect_coverage (glyphs); | |
| 2167 case 2: return u.format2.collect_coverage (glyphs); | |
| 2168 #ifndef HB_NO_BEYOND_64K | |
| 2169 case 3: return u.format3.collect_coverage (glyphs); | |
| 2170 case 4: return u.format4.collect_coverage (glyphs); | |
| 2171 #endif | |
| 2172 default:return false; | |
| 2173 } | |
| 2174 } | |
| 2175 | |
| 2176 /* Might return false if array looks unsorted. | |
| 2177 * Used for faster rejection of corrupt data. */ | |
| 2178 template <typename set_t> | |
| 2179 bool collect_class (set_t *glyphs, unsigned int klass) const | |
| 2180 { | |
| 2181 switch (u.format) { | |
| 2182 case 1: return u.format1.collect_class (glyphs, klass); | |
| 2183 case 2: return u.format2.collect_class (glyphs, klass); | |
| 2184 #ifndef HB_NO_BEYOND_64K | |
| 2185 case 3: return u.format3.collect_class (glyphs, klass); | |
| 2186 case 4: return u.format4.collect_class (glyphs, klass); | |
| 2187 #endif | |
| 2188 default:return false; | |
| 2189 } | |
| 2190 } | |
| 2191 | |
| 2192 bool intersects (const hb_set_t *glyphs) const | |
| 2193 { | |
| 2194 switch (u.format) { | |
| 2195 case 1: return u.format1.intersects (glyphs); | |
| 2196 case 2: return u.format2.intersects (glyphs); | |
| 2197 #ifndef HB_NO_BEYOND_64K | |
| 2198 case 3: return u.format3.intersects (glyphs); | |
| 2199 case 4: return u.format4.intersects (glyphs); | |
| 2200 #endif | |
| 2201 default:return false; | |
| 2202 } | |
| 2203 } | |
| 2204 bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const | |
| 2205 { | |
| 2206 switch (u.format) { | |
| 2207 case 1: return u.format1.intersects_class (glyphs, klass); | |
| 2208 case 2: return u.format2.intersects_class (glyphs, klass); | |
| 2209 #ifndef HB_NO_BEYOND_64K | |
| 2210 case 3: return u.format3.intersects_class (glyphs, klass); | |
| 2211 case 4: return u.format4.intersects_class (glyphs, klass); | |
| 2212 #endif | |
| 2213 default:return false; | |
| 2214 } | |
| 2215 } | |
| 2216 | |
| 2217 void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const | |
| 2218 { | |
| 2219 switch (u.format) { | |
| 2220 case 1: return u.format1.intersected_class_glyphs (glyphs, klass, intersect_glyphs); | |
| 2221 case 2: return u.format2.intersected_class_glyphs (glyphs, klass, intersect_glyphs); | |
| 2222 #ifndef HB_NO_BEYOND_64K | |
| 2223 case 3: return u.format3.intersected_class_glyphs (glyphs, klass, intersect_glyphs); | |
| 2224 case 4: return u.format4.intersected_class_glyphs (glyphs, klass, intersect_glyphs); | |
| 2225 #endif | |
| 2226 default:return; | |
| 2227 } | |
| 2228 } | |
| 2229 | |
| 2230 void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const | |
| 2231 { | |
| 2232 switch (u.format) { | |
| 2233 case 1: return u.format1.intersected_classes (glyphs, intersect_classes); | |
| 2234 case 2: return u.format2.intersected_classes (glyphs, intersect_classes); | |
| 2235 #ifndef HB_NO_BEYOND_64K | |
| 2236 case 3: return u.format3.intersected_classes (glyphs, intersect_classes); | |
| 2237 case 4: return u.format4.intersected_classes (glyphs, intersect_classes); | |
| 2238 #endif | |
| 2239 default:return; | |
| 2240 } | |
| 2241 } | |
| 2242 | |
| 2243 | |
| 2244 protected: | |
| 2245 union { | |
| 2246 HBUINT16 format; /* Format identifier */ | |
| 2247 ClassDefFormat1_3<SmallTypes> format1; | |
| 2248 ClassDefFormat2_4<SmallTypes> format2; | |
| 2249 #ifndef HB_NO_BEYOND_64K | |
| 2250 ClassDefFormat1_3<MediumTypes>format3; | |
| 2251 ClassDefFormat2_4<MediumTypes>format4; | |
| 2252 #endif | |
| 2253 } u; | |
| 2254 public: | |
| 2255 DEFINE_SIZE_UNION (2, format); | |
| 2256 }; | |
| 2257 | |
| 2258 template<typename Iterator> | |
| 2259 static inline bool ClassDef_serialize (hb_serialize_context_t *c, | |
| 2260 Iterator it) | |
| 2261 { return (c->start_embed<ClassDef> ()->serialize (c, it)); } | |
| 2262 | |
| 2263 | |
| 2264 /* | |
| 2265 * Item Variation Store | |
| 2266 */ | |
| 2267 | |
| 2268 struct VarRegionAxis | |
| 2269 { | |
| 2270 float evaluate (int coord) const | |
| 2271 { | |
| 2272 int start = startCoord, peak = peakCoord, end = endCoord; | |
| 2273 | |
| 2274 /* TODO Move these to sanitize(). */ | |
| 2275 if (unlikely (start > peak || peak > end)) | |
| 2276 return 1.; | |
| 2277 if (unlikely (start < 0 && end > 0 && peak != 0)) | |
| 2278 return 1.; | |
| 2279 | |
| 2280 if (peak == 0 || coord == peak) | |
| 2281 return 1.; | |
| 2282 | |
| 2283 if (coord <= start || end <= coord) | |
| 2284 return 0.; | |
| 2285 | |
| 2286 /* Interpolate */ | |
| 2287 if (coord < peak) | |
| 2288 return float (coord - start) / (peak - start); | |
| 2289 else | |
| 2290 return float (end - coord) / (end - peak); | |
| 2291 } | |
| 2292 | |
| 2293 bool sanitize (hb_sanitize_context_t *c) const | |
| 2294 { | |
| 2295 TRACE_SANITIZE (this); | |
| 2296 return_trace (c->check_struct (this)); | |
| 2297 /* TODO Handle invalid start/peak/end configs, so we don't | |
| 2298 * have to do that at runtime. */ | |
| 2299 } | |
| 2300 | |
| 2301 public: | |
| 2302 F2DOT14 startCoord; | |
| 2303 F2DOT14 peakCoord; | |
| 2304 F2DOT14 endCoord; | |
| 2305 public: | |
| 2306 DEFINE_SIZE_STATIC (6); | |
| 2307 }; | |
| 2308 | |
| 2309 #define REGION_CACHE_ITEM_CACHE_INVALID 2.f | |
| 2310 | |
| 2311 struct VarRegionList | |
| 2312 { | |
| 2313 using cache_t = float; | |
| 2314 | |
| 2315 float evaluate (unsigned int region_index, | |
| 2316 const int *coords, unsigned int coord_len, | |
| 2317 cache_t *cache = nullptr) const | |
| 2318 { | |
| 2319 if (unlikely (region_index >= regionCount)) | |
| 2320 return 0.; | |
| 2321 | |
| 2322 float *cached_value = nullptr; | |
| 2323 if (cache) | |
| 2324 { | |
| 2325 cached_value = &(cache[region_index]); | |
| 2326 if (likely (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)) | |
| 2327 return *cached_value; | |
| 2328 } | |
| 2329 | |
| 2330 const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount); | |
| 2331 | |
| 2332 float v = 1.; | |
| 2333 unsigned int count = axisCount; | |
| 2334 for (unsigned int i = 0; i < count; i++) | |
| 2335 { | |
| 2336 int coord = i < coord_len ? coords[i] : 0; | |
| 2337 float factor = axes[i].evaluate (coord); | |
| 2338 if (factor == 0.f) | |
| 2339 { | |
| 2340 if (cache) | |
| 2341 *cached_value = 0.; | |
| 2342 return 0.; | |
| 2343 } | |
| 2344 v *= factor; | |
| 2345 } | |
| 2346 | |
| 2347 if (cache) | |
| 2348 *cached_value = v; | |
| 2349 return v; | |
| 2350 } | |
| 2351 | |
| 2352 bool sanitize (hb_sanitize_context_t *c) const | |
| 2353 { | |
| 2354 TRACE_SANITIZE (this); | |
| 2355 return_trace (c->check_struct (this) && axesZ.sanitize (c, axisCount * regionCount)); | |
| 2356 } | |
| 2357 | |
| 2358 bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_bimap_t ®ion_map) | |
| 2359 { | |
| 2360 TRACE_SERIALIZE (this); | |
| 2361 if (unlikely (!c->extend_min (this))) return_trace (false); | |
| 2362 axisCount = src->axisCount; | |
| 2363 regionCount = region_map.get_population (); | |
| 2364 if (unlikely (hb_unsigned_mul_overflows (axisCount * regionCount, | |
| 2365 VarRegionAxis::static_size))) return_trace (false); | |
| 2366 if (unlikely (!c->extend (this))) return_trace (false); | |
| 2367 unsigned int region_count = src->regionCount; | |
| 2368 for (unsigned int r = 0; r < regionCount; r++) | |
| 2369 { | |
| 2370 unsigned int backward = region_map.backward (r); | |
| 2371 if (backward >= region_count) return_trace (false); | |
| 2372 hb_memcpy (&axesZ[axisCount * r], &src->axesZ[axisCount * backward], VarRegionAxis::static_size * axisCount); | |
| 2373 } | |
| 2374 | |
| 2375 return_trace (true); | |
| 2376 } | |
| 2377 | |
| 2378 unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; } | |
| 2379 | |
| 2380 public: | |
| 2381 HBUINT16 axisCount; | |
| 2382 HBUINT15 regionCount; | |
| 2383 protected: | |
| 2384 UnsizedArrayOf<VarRegionAxis> | |
| 2385 axesZ; | |
| 2386 public: | |
| 2387 DEFINE_SIZE_ARRAY (4, axesZ); | |
| 2388 }; | |
| 2389 | |
| 2390 struct VarData | |
| 2391 { | |
| 2392 unsigned int get_region_index_count () const | |
| 2393 { return regionIndices.len; } | |
| 2394 | |
| 2395 unsigned int get_row_size () const | |
| 2396 { return (wordCount () + regionIndices.len) * (longWords () ? 2 : 1); } | |
| 2397 | |
| 2398 unsigned int get_size () const | |
| 2399 { return min_size | |
| 2400 - regionIndices.min_size + regionIndices.get_size () | |
| 2401 + itemCount * get_row_size (); | |
| 2402 } | |
| 2403 | |
| 2404 float get_delta (unsigned int inner, | |
| 2405 const int *coords, unsigned int coord_count, | |
| 2406 const VarRegionList ®ions, | |
| 2407 VarRegionList::cache_t *cache = nullptr) const | |
| 2408 { | |
| 2409 if (unlikely (inner >= itemCount)) | |
| 2410 return 0.; | |
| 2411 | |
| 2412 unsigned int count = regionIndices.len; | |
| 2413 bool is_long = longWords (); | |
| 2414 unsigned word_count = wordCount (); | |
| 2415 unsigned int scount = is_long ? count : word_count; | |
| 2416 unsigned int lcount = is_long ? word_count : 0; | |
| 2417 | |
| 2418 const HBUINT8 *bytes = get_delta_bytes (); | |
| 2419 const HBUINT8 *row = bytes + inner * get_row_size (); | |
| 2420 | |
| 2421 float delta = 0.; | |
| 2422 unsigned int i = 0; | |
| 2423 | |
| 2424 const HBINT32 *lcursor = reinterpret_cast<const HBINT32 *> (row); | |
| 2425 for (; i < lcount; i++) | |
| 2426 { | |
| 2427 float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); | |
| 2428 delta += scalar * *lcursor++; | |
| 2429 } | |
| 2430 const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (lcursor); | |
| 2431 for (; i < scount; i++) | |
| 2432 { | |
| 2433 float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); | |
| 2434 delta += scalar * *scursor++; | |
| 2435 } | |
| 2436 const HBINT8 *bcursor = reinterpret_cast<const HBINT8 *> (scursor); | |
| 2437 for (; i < count; i++) | |
| 2438 { | |
| 2439 float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); | |
| 2440 delta += scalar * *bcursor++; | |
| 2441 } | |
| 2442 | |
| 2443 return delta; | |
| 2444 } | |
| 2445 | |
| 2446 void get_region_scalars (const int *coords, unsigned int coord_count, | |
| 2447 const VarRegionList ®ions, | |
| 2448 float *scalars /*OUT */, | |
| 2449 unsigned int num_scalars) const | |
| 2450 { | |
| 2451 unsigned count = hb_min (num_scalars, regionIndices.len); | |
| 2452 for (unsigned int i = 0; i < count; i++) | |
| 2453 scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count); | |
| 2454 for (unsigned int i = count; i < num_scalars; i++) | |
| 2455 scalars[i] = 0.f; | |
| 2456 } | |
| 2457 | |
| 2458 bool sanitize (hb_sanitize_context_t *c) const | |
| 2459 { | |
| 2460 TRACE_SANITIZE (this); | |
| 2461 return_trace (c->check_struct (this) && | |
| 2462 regionIndices.sanitize (c) && | |
| 2463 wordCount () <= regionIndices.len && | |
| 2464 c->check_range (get_delta_bytes (), | |
| 2465 itemCount, | |
| 2466 get_row_size ())); | |
| 2467 } | |
| 2468 | |
| 2469 bool serialize (hb_serialize_context_t *c, | |
| 2470 const VarData *src, | |
| 2471 const hb_inc_bimap_t &inner_map, | |
| 2472 const hb_bimap_t ®ion_map) | |
| 2473 { | |
| 2474 TRACE_SERIALIZE (this); | |
| 2475 if (unlikely (!c->extend_min (this))) return_trace (false); | |
| 2476 itemCount = inner_map.get_next_value (); | |
| 2477 | |
| 2478 /* Optimize word count */ | |
| 2479 unsigned ri_count = src->regionIndices.len; | |
| 2480 enum delta_size_t { kZero=0, kNonWord, kWord }; | |
| 2481 hb_vector_t<delta_size_t> delta_sz; | |
| 2482 hb_vector_t<unsigned int> ri_map; /* maps new index to old index */ | |
| 2483 delta_sz.resize (ri_count); | |
| 2484 ri_map.resize (ri_count); | |
| 2485 unsigned int new_word_count = 0; | |
| 2486 unsigned int r; | |
| 2487 | |
| 2488 const HBUINT8 *src_delta_bytes = src->get_delta_bytes (); | |
| 2489 unsigned src_row_size = src->get_row_size (); | |
| 2490 unsigned src_word_count = src->wordCount (); | |
| 2491 bool src_long_words = src->longWords (); | |
| 2492 | |
| 2493 bool has_long = false; | |
| 2494 if (src_long_words) | |
| 2495 { | |
| 2496 for (r = 0; r < src_word_count; r++) | |
| 2497 { | |
| 2498 for (unsigned int i = 0; i < inner_map.get_next_value (); i++) | |
| 2499 { | |
| 2500 unsigned int old = inner_map.backward (i); | |
| 2501 int32_t delta = src->get_item_delta_fast (old, r, src_delta_bytes, src_row_size); | |
| 2502 if (delta < -65536 || 65535 < delta) | |
| 2503 { | |
| 2504 has_long = true; | |
| 2505 break; | |
| 2506 } | |
| 2507 } | |
| 2508 } | |
| 2509 } | |
| 2510 | |
| 2511 signed min_threshold = has_long ? -65536 : -128; | |
| 2512 signed max_threshold = has_long ? +65535 : +127; | |
| 2513 for (r = 0; r < ri_count; r++) | |
| 2514 { | |
| 2515 bool short_circuit = src_long_words == has_long && src_word_count <= r; | |
| 2516 | |
| 2517 delta_sz[r] = kZero; | |
| 2518 for (unsigned int i = 0; i < inner_map.get_next_value (); i++) | |
| 2519 { | |
| 2520 unsigned int old = inner_map.backward (i); | |
| 2521 int32_t delta = src->get_item_delta_fast (old, r, src_delta_bytes, src_row_size); | |
| 2522 if (delta < min_threshold || max_threshold < delta) | |
| 2523 { | |
| 2524 delta_sz[r] = kWord; | |
| 2525 new_word_count++; | |
| 2526 break; | |
| 2527 } | |
| 2528 else if (delta != 0) | |
| 2529 { | |
| 2530 delta_sz[r] = kNonWord; | |
| 2531 if (short_circuit) | |
| 2532 break; | |
| 2533 } | |
| 2534 } | |
| 2535 } | |
| 2536 | |
| 2537 unsigned int word_index = 0; | |
| 2538 unsigned int non_word_index = new_word_count; | |
| 2539 unsigned int new_ri_count = 0; | |
| 2540 for (r = 0; r < ri_count; r++) | |
| 2541 if (delta_sz[r]) | |
| 2542 { | |
| 2543 unsigned new_r = (delta_sz[r] == kWord)? word_index++ : non_word_index++; | |
| 2544 ri_map[new_r] = r; | |
| 2545 new_ri_count++; | |
| 2546 } | |
| 2547 | |
| 2548 wordSizeCount = new_word_count | (has_long ? 0x8000u /* LONG_WORDS */ : 0); | |
| 2549 | |
| 2550 regionIndices.len = new_ri_count; | |
| 2551 | |
| 2552 if (unlikely (!c->extend (this))) return_trace (false); | |
| 2553 | |
| 2554 for (r = 0; r < new_ri_count; r++) | |
| 2555 regionIndices[r] = region_map[src->regionIndices[ri_map[r]]]; | |
| 2556 | |
| 2557 HBUINT8 *delta_bytes = get_delta_bytes (); | |
| 2558 unsigned row_size = get_row_size (); | |
| 2559 unsigned count = itemCount; | |
| 2560 for (unsigned int i = 0; i < count; i++) | |
| 2561 { | |
| 2562 unsigned int old = inner_map.backward (i); | |
| 2563 for (unsigned int r = 0; r < new_ri_count; r++) | |
| 2564 set_item_delta_fast (i, r, | |
| 2565 src->get_item_delta_fast (old, ri_map[r], | |
| 2566 src_delta_bytes, src_row_size), | |
| 2567 delta_bytes, row_size); | |
| 2568 } | |
| 2569 | |
| 2570 return_trace (true); | |
| 2571 } | |
| 2572 | |
| 2573 void collect_region_refs (hb_set_t ®ion_indices, const hb_inc_bimap_t &inner_map) const | |
| 2574 { | |
| 2575 const HBUINT8 *delta_bytes = get_delta_bytes (); | |
| 2576 unsigned row_size = get_row_size (); | |
| 2577 | |
| 2578 for (unsigned int r = 0; r < regionIndices.len; r++) | |
| 2579 { | |
| 2580 unsigned int region = regionIndices.arrayZ[r]; | |
| 2581 if (region_indices.has (region)) continue; | |
| 2582 for (unsigned int i = 0; i < inner_map.get_next_value (); i++) | |
| 2583 if (get_item_delta_fast (inner_map.backward (i), r, delta_bytes, row_size) != 0) | |
| 2584 { | |
| 2585 region_indices.add (region); | |
| 2586 break; | |
| 2587 } | |
| 2588 } | |
| 2589 } | |
| 2590 | |
| 2591 protected: | |
| 2592 const HBUINT8 *get_delta_bytes () const | |
| 2593 { return &StructAfter<HBUINT8> (regionIndices); } | |
| 2594 | |
| 2595 HBUINT8 *get_delta_bytes () | |
| 2596 { return &StructAfter<HBUINT8> (regionIndices); } | |
| 2597 | |
| 2598 int32_t get_item_delta_fast (unsigned int item, unsigned int region, | |
| 2599 const HBUINT8 *delta_bytes, unsigned row_size) const | |
| 2600 { | |
| 2601 if (unlikely (item >= itemCount || region >= regionIndices.len)) return 0; | |
| 2602 | |
| 2603 const HBINT8 *p = (const HBINT8 *) delta_bytes + item * row_size; | |
| 2604 unsigned word_count = wordCount (); | |
| 2605 bool is_long = longWords (); | |
| 2606 if (is_long) | |
| 2607 { | |
| 2608 if (region < word_count) | |
| 2609 return ((const HBINT32 *) p)[region]; | |
| 2610 else | |
| 2611 return ((const HBINT16 *)(p + HBINT32::static_size * word_count))[region - word_count]; | |
| 2612 } | |
| 2613 else | |
| 2614 { | |
| 2615 if (region < word_count) | |
| 2616 return ((const HBINT16 *) p)[region]; | |
| 2617 else | |
| 2618 return (p + HBINT16::static_size * word_count)[region - word_count]; | |
| 2619 } | |
| 2620 } | |
| 2621 int32_t get_item_delta (unsigned int item, unsigned int region) const | |
| 2622 { | |
| 2623 return get_item_delta_fast (item, region, | |
| 2624 get_delta_bytes (), | |
| 2625 get_row_size ()); | |
| 2626 } | |
| 2627 | |
| 2628 void set_item_delta_fast (unsigned int item, unsigned int region, int32_t delta, | |
| 2629 HBUINT8 *delta_bytes, unsigned row_size) | |
| 2630 { | |
| 2631 HBINT8 *p = (HBINT8 *) delta_bytes + item * row_size; | |
| 2632 unsigned word_count = wordCount (); | |
| 2633 bool is_long = longWords (); | |
| 2634 if (is_long) | |
| 2635 { | |
| 2636 if (region < word_count) | |
| 2637 ((HBINT32 *) p)[region] = delta; | |
| 2638 else | |
| 2639 ((HBINT16 *)(p + HBINT32::static_size * word_count))[region - word_count] = delta; | |
| 2640 } | |
| 2641 else | |
| 2642 { | |
| 2643 if (region < word_count) | |
| 2644 ((HBINT16 *) p)[region] = delta; | |
| 2645 else | |
| 2646 (p + HBINT16::static_size * word_count)[region - word_count] = delta; | |
| 2647 } | |
| 2648 } | |
| 2649 void set_item_delta (unsigned int item, unsigned int region, int32_t delta) | |
| 2650 { | |
| 2651 set_item_delta_fast (item, region, delta, | |
| 2652 get_delta_bytes (), | |
| 2653 get_row_size ()); | |
| 2654 } | |
| 2655 | |
| 2656 bool longWords () const { return wordSizeCount & 0x8000u /* LONG_WORDS */; } | |
| 2657 unsigned wordCount () const { return wordSizeCount & 0x7FFFu /* WORD_DELTA_COUNT_MASK */; } | |
| 2658 | |
| 2659 protected: | |
| 2660 HBUINT16 itemCount; | |
| 2661 HBUINT16 wordSizeCount; | |
| 2662 Array16Of<HBUINT16> regionIndices; | |
| 2663 /*UnsizedArrayOf<HBUINT8>bytesX;*/ | |
| 2664 public: | |
| 2665 DEFINE_SIZE_ARRAY (6, regionIndices); | |
| 2666 }; | |
| 2667 | |
| 2668 struct VariationStore | |
| 2669 { | |
| 2670 using cache_t = VarRegionList::cache_t; | |
| 2671 | |
| 2672 cache_t *create_cache () const | |
| 2673 { | |
| 2674 #ifdef HB_NO_VAR | |
| 2675 return nullptr; | |
| 2676 #endif | |
| 2677 auto &r = this+regions; | |
| 2678 unsigned count = r.regionCount; | |
| 2679 | |
| 2680 float *cache = (float *) hb_malloc (sizeof (float) * count); | |
| 2681 if (unlikely (!cache)) return nullptr; | |
| 2682 | |
| 2683 for (unsigned i = 0; i < count; i++) | |
| 2684 cache[i] = REGION_CACHE_ITEM_CACHE_INVALID; | |
| 2685 | |
| 2686 return cache; | |
| 2687 } | |
| 2688 | |
| 2689 static void destroy_cache (cache_t *cache) { hb_free (cache); } | |
| 2690 | |
| 2691 private: | |
| 2692 float get_delta (unsigned int outer, unsigned int inner, | |
| 2693 const int *coords, unsigned int coord_count, | |
| 2694 VarRegionList::cache_t *cache = nullptr) const | |
| 2695 { | |
| 2696 #ifdef HB_NO_VAR | |
| 2697 return 0.f; | |
| 2698 #endif | |
| 2699 | |
| 2700 if (unlikely (outer >= dataSets.len)) | |
| 2701 return 0.f; | |
| 2702 | |
| 2703 return (this+dataSets[outer]).get_delta (inner, | |
| 2704 coords, coord_count, | |
| 2705 this+regions, | |
| 2706 cache); | |
| 2707 } | |
| 2708 | |
| 2709 public: | |
| 2710 float get_delta (unsigned int index, | |
| 2711 const int *coords, unsigned int coord_count, | |
| 2712 VarRegionList::cache_t *cache = nullptr) const | |
| 2713 { | |
| 2714 unsigned int outer = index >> 16; | |
| 2715 unsigned int inner = index & 0xFFFF; | |
| 2716 return get_delta (outer, inner, coords, coord_count, cache); | |
| 2717 } | |
| 2718 float get_delta (unsigned int index, | |
| 2719 hb_array_t<int> coords, | |
| 2720 VarRegionList::cache_t *cache = nullptr) const | |
| 2721 { | |
| 2722 return get_delta (index, | |
| 2723 coords.arrayZ, coords.length, | |
| 2724 cache); | |
| 2725 } | |
| 2726 | |
| 2727 bool sanitize (hb_sanitize_context_t *c) const | |
| 2728 { | |
| 2729 #ifdef HB_NO_VAR | |
| 2730 return true; | |
| 2731 #endif | |
| 2732 | |
| 2733 TRACE_SANITIZE (this); | |
| 2734 return_trace (c->check_struct (this) && | |
| 2735 format == 1 && | |
| 2736 regions.sanitize (c, this) && | |
| 2737 dataSets.sanitize (c, this)); | |
| 2738 } | |
| 2739 | |
| 2740 bool serialize (hb_serialize_context_t *c, | |
| 2741 const VariationStore *src, | |
| 2742 const hb_array_t <const hb_inc_bimap_t> &inner_maps) | |
| 2743 { | |
| 2744 TRACE_SERIALIZE (this); | |
| 2745 #ifdef HB_NO_VAR | |
| 2746 return_trace (false); | |
| 2747 #endif | |
| 2748 | |
| 2749 if (unlikely (!c->extend_min (this))) return_trace (false); | |
| 2750 | |
| 2751 unsigned int set_count = 0; | |
| 2752 for (unsigned int i = 0; i < inner_maps.length; i++) | |
| 2753 if (inner_maps[i].get_population ()) | |
| 2754 set_count++; | |
| 2755 | |
| 2756 format = 1; | |
| 2757 | |
| 2758 const auto &src_regions = src+src->regions; | |
| 2759 | |
| 2760 hb_set_t region_indices; | |
| 2761 for (unsigned int i = 0; i < inner_maps.length; i++) | |
| 2762 (src+src->dataSets[i]).collect_region_refs (region_indices, inner_maps[i]); | |
| 2763 | |
| 2764 if (region_indices.in_error ()) | |
| 2765 return_trace (false); | |
| 2766 | |
| 2767 region_indices.del_range ((src_regions).regionCount, hb_set_t::INVALID); | |
| 2768 | |
| 2769 /* TODO use constructor when our data-structures support that. */ | |
| 2770 hb_inc_bimap_t region_map; | |
| 2771 + hb_iter (region_indices) | |
| 2772 | hb_apply ([®ion_map] (unsigned _) { region_map.add(_); }) | |
| 2773 ; | |
| 2774 if (region_map.in_error()) | |
| 2775 return_trace (false); | |
| 2776 | |
| 2777 if (unlikely (!regions.serialize_serialize (c, &src_regions, region_map))) | |
| 2778 return_trace (false); | |
| 2779 | |
| 2780 dataSets.len = set_count; | |
| 2781 if (unlikely (!c->extend (dataSets))) return_trace (false); | |
| 2782 | |
| 2783 /* TODO: The following code could be simplified when | |
| 2784 * List16OfOffset16To::subset () can take a custom param to be passed to VarData::serialize () */ | |
| 2785 unsigned int set_index = 0; | |
| 2786 for (unsigned int i = 0; i < inner_maps.length; i++) | |
| 2787 { | |
| 2788 if (!inner_maps[i].get_population ()) continue; | |
| 2789 if (unlikely (!dataSets[set_index++] | |
| 2790 .serialize_serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map))) | |
| 2791 return_trace (false); | |
| 2792 } | |
| 2793 | |
| 2794 return_trace (true); | |
| 2795 } | |
| 2796 | |
| 2797 bool subset (hb_subset_context_t *c, const hb_array_t<const hb_inc_bimap_t> &inner_maps) const | |
| 2798 { | |
| 2799 TRACE_SUBSET (this); | |
| 2800 #ifdef HB_NO_VAR | |
| 2801 return_trace (false); | |
| 2802 #endif | |
| 2803 | |
| 2804 VariationStore *varstore_prime = c->serializer->start_embed<VariationStore> (); | |
| 2805 if (unlikely (!varstore_prime)) return_trace (false); | |
| 2806 | |
| 2807 varstore_prime->serialize (c->serializer, this, inner_maps); | |
| 2808 | |
| 2809 return_trace ( | |
| 2810 !c->serializer->in_error() | |
| 2811 && varstore_prime->dataSets); | |
| 2812 } | |
| 2813 | |
| 2814 unsigned int get_region_index_count (unsigned int major) const | |
| 2815 { | |
| 2816 #ifdef HB_NO_VAR | |
| 2817 return 0; | |
| 2818 #endif | |
| 2819 return (this+dataSets[major]).get_region_index_count (); | |
| 2820 } | |
| 2821 | |
| 2822 void get_region_scalars (unsigned int major, | |
| 2823 const int *coords, unsigned int coord_count, | |
| 2824 float *scalars /*OUT*/, | |
| 2825 unsigned int num_scalars) const | |
| 2826 { | |
| 2827 #ifdef HB_NO_VAR | |
| 2828 for (unsigned i = 0; i < num_scalars; i++) | |
| 2829 scalars[i] = 0.f; | |
| 2830 return; | |
| 2831 #endif | |
| 2832 | |
| 2833 (this+dataSets[major]).get_region_scalars (coords, coord_count, | |
| 2834 this+regions, | |
| 2835 &scalars[0], num_scalars); | |
| 2836 } | |
| 2837 | |
| 2838 unsigned int get_sub_table_count () const | |
| 2839 { | |
| 2840 #ifdef HB_NO_VAR | |
| 2841 return 0; | |
| 2842 #endif | |
| 2843 return dataSets.len; | |
| 2844 } | |
| 2845 | |
| 2846 protected: | |
| 2847 HBUINT16 format; | |
| 2848 Offset32To<VarRegionList> regions; | |
| 2849 Array16OfOffset32To<VarData> dataSets; | |
| 2850 public: | |
| 2851 DEFINE_SIZE_ARRAY_SIZED (8, dataSets); | |
| 2852 }; | |
| 2853 | |
| 2854 #undef REGION_CACHE_ITEM_CACHE_INVALID | |
| 2855 | |
| 2856 /* | |
| 2857 * Feature Variations | |
| 2858 */ | |
| 2859 enum Cond_with_Var_flag_t | |
| 2860 { | |
| 2861 KEEP_COND_WITH_VAR = 0, | |
| 2862 DROP_COND_WITH_VAR = 1, | |
| 2863 DROP_RECORD_WITH_VAR = 2, | |
| 2864 MEM_ERR_WITH_VAR = 3, | |
| 2865 }; | |
| 2866 | |
| 2867 struct ConditionFormat1 | |
| 2868 { | |
| 2869 friend struct Condition; | |
| 2870 | |
| 2871 bool subset (hb_subset_context_t *c) const | |
| 2872 { | |
| 2873 TRACE_SUBSET (this); | |
| 2874 auto *out = c->serializer->embed (this); | |
| 2875 if (unlikely (!out)) return_trace (false); | |
| 2876 | |
| 2877 const hb_map_t *index_map = c->plan->axes_index_map; | |
| 2878 if (index_map->is_empty ()) return_trace (true); | |
| 2879 | |
| 2880 if (!index_map->has (axisIndex)) | |
| 2881 return_trace (false); | |
| 2882 | |
| 2883 return_trace (c->serializer->check_assign (out->axisIndex, index_map->get (axisIndex), | |
| 2884 HB_SERIALIZE_ERROR_INT_OVERFLOW)); | |
| 2885 } | |
| 2886 | |
| 2887 private: | |
| 2888 Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, | |
| 2889 hb_map_t *condition_map /* OUT */) const | |
| 2890 { | |
| 2891 //invalid axis index, drop the entire record | |
| 2892 if (!c->axes_index_tag_map->has (axisIndex)) | |
| 2893 return DROP_RECORD_WITH_VAR; | |
| 2894 | |
| 2895 hb_tag_t axis_tag = c->axes_index_tag_map->get (axisIndex); | |
| 2896 | |
| 2897 //axis not pinned, keep the condition | |
| 2898 if (!c->axes_location->has (axis_tag)) | |
| 2899 { | |
| 2900 // add axisIndex->value into the hashmap so we can check if the record is | |
| 2901 // unique with variations | |
| 2902 int16_t min_val = filterRangeMinValue; | |
| 2903 int16_t max_val = filterRangeMaxValue; | |
| 2904 hb_codepoint_t val = (max_val << 16) + min_val; | |
| 2905 | |
| 2906 condition_map->set (axisIndex, val); | |
| 2907 return KEEP_COND_WITH_VAR; | |
| 2908 } | |
| 2909 | |
| 2910 //axis pinned, check if condition is met | |
| 2911 //TODO: add check for axis Ranges | |
| 2912 int v = c->axes_location->get (axis_tag); | |
| 2913 | |
| 2914 //condition not met, drop the entire record | |
| 2915 if (v < filterRangeMinValue || v > filterRangeMaxValue) | |
| 2916 return DROP_RECORD_WITH_VAR; | |
| 2917 | |
| 2918 //axis pinned and condition met, drop the condition | |
| 2919 return DROP_COND_WITH_VAR; | |
| 2920 } | |
| 2921 | |
| 2922 bool evaluate (const int *coords, unsigned int coord_len) const | |
| 2923 { | |
| 2924 int coord = axisIndex < coord_len ? coords[axisIndex] : 0; | |
| 2925 return filterRangeMinValue <= coord && coord <= filterRangeMaxValue; | |
| 2926 } | |
| 2927 | |
| 2928 bool sanitize (hb_sanitize_context_t *c) const | |
| 2929 { | |
| 2930 TRACE_SANITIZE (this); | |
| 2931 return_trace (c->check_struct (this)); | |
| 2932 } | |
| 2933 | |
| 2934 protected: | |
| 2935 HBUINT16 format; /* Format identifier--format = 1 */ | |
| 2936 HBUINT16 axisIndex; | |
| 2937 F2DOT14 filterRangeMinValue; | |
| 2938 F2DOT14 filterRangeMaxValue; | |
| 2939 public: | |
| 2940 DEFINE_SIZE_STATIC (8); | |
| 2941 }; | |
| 2942 | |
| 2943 struct Condition | |
| 2944 { | |
| 2945 bool evaluate (const int *coords, unsigned int coord_len) const | |
| 2946 { | |
| 2947 switch (u.format) { | |
| 2948 case 1: return u.format1.evaluate (coords, coord_len); | |
| 2949 default:return false; | |
| 2950 } | |
| 2951 } | |
| 2952 | |
| 2953 Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, | |
| 2954 hb_map_t *condition_map /* OUT */) const | |
| 2955 { | |
| 2956 switch (u.format) { | |
| 2957 case 1: return u.format1.keep_with_variations (c, condition_map); | |
| 2958 default:return KEEP_COND_WITH_VAR; | |
| 2959 } | |
| 2960 } | |
| 2961 | |
| 2962 template <typename context_t, typename ...Ts> | |
| 2963 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const | |
| 2964 { | |
| 2965 TRACE_DISPATCH (this, u.format); | |
| 2966 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); | |
| 2967 switch (u.format) { | |
| 2968 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); | |
| 2969 default:return_trace (c->default_return_value ()); | |
| 2970 } | |
| 2971 } | |
| 2972 | |
| 2973 bool sanitize (hb_sanitize_context_t *c) const | |
| 2974 { | |
| 2975 TRACE_SANITIZE (this); | |
| 2976 if (!u.format.sanitize (c)) return_trace (false); | |
| 2977 switch (u.format) { | |
| 2978 case 1: return_trace (u.format1.sanitize (c)); | |
| 2979 default:return_trace (true); | |
| 2980 } | |
| 2981 } | |
| 2982 | |
| 2983 protected: | |
| 2984 union { | |
| 2985 HBUINT16 format; /* Format identifier */ | |
| 2986 ConditionFormat1 format1; | |
| 2987 } u; | |
| 2988 public: | |
| 2989 DEFINE_SIZE_UNION (2, format); | |
| 2990 }; | |
| 2991 | |
| 2992 struct ConditionSet | |
| 2993 { | |
| 2994 bool evaluate (const int *coords, unsigned int coord_len) const | |
| 2995 { | |
| 2996 unsigned int count = conditions.len; | |
| 2997 for (unsigned int i = 0; i < count; i++) | |
| 2998 if (!(this+conditions.arrayZ[i]).evaluate (coords, coord_len)) | |
| 2999 return false; | |
| 3000 return true; | |
| 3001 } | |
| 3002 | |
| 3003 Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const | |
| 3004 { | |
| 3005 hb_map_t *condition_map = hb_map_create (); | |
| 3006 if (unlikely (!condition_map)) return MEM_ERR_WITH_VAR; | |
| 3007 hb::shared_ptr<hb_map_t> p {condition_map}; | |
| 3008 | |
| 3009 hb_set_t *cond_set = hb_set_create (); | |
| 3010 if (unlikely (!cond_set)) return MEM_ERR_WITH_VAR; | |
| 3011 hb::shared_ptr<hb_set_t> s {cond_set}; | |
| 3012 | |
| 3013 unsigned num_kept_cond = 0, cond_idx = 0; | |
| 3014 for (const auto& offset : conditions) | |
| 3015 { | |
| 3016 Cond_with_Var_flag_t ret = (this+offset).keep_with_variations (c, condition_map); | |
| 3017 // one condition is not met, drop the entire record | |
| 3018 if (ret == DROP_RECORD_WITH_VAR) | |
| 3019 return DROP_RECORD_WITH_VAR; | |
| 3020 | |
| 3021 // axis not pinned, keep this condition | |
| 3022 if (ret == KEEP_COND_WITH_VAR) | |
| 3023 { | |
| 3024 cond_set->add (cond_idx); | |
| 3025 num_kept_cond++; | |
| 3026 } | |
| 3027 cond_idx++; | |
| 3028 } | |
| 3029 | |
| 3030 // all conditions met | |
| 3031 if (num_kept_cond == 0) return DROP_COND_WITH_VAR; | |
| 3032 | |
| 3033 //check if condition_set is unique with variations | |
| 3034 if (c->conditionset_map->has (p)) | |
| 3035 //duplicate found, drop the entire record | |
| 3036 return DROP_RECORD_WITH_VAR; | |
| 3037 | |
| 3038 c->conditionset_map->set (p, 1); | |
| 3039 c->record_cond_idx_map->set (c->cur_record_idx, s); | |
| 3040 | |
| 3041 return KEEP_COND_WITH_VAR; | |
| 3042 } | |
| 3043 | |
| 3044 bool subset (hb_subset_context_t *c, | |
| 3045 hb_subset_layout_context_t *l) const | |
| 3046 { | |
| 3047 TRACE_SUBSET (this); | |
| 3048 auto *out = c->serializer->start_embed (this); | |
| 3049 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 3050 | |
| 3051 hb_set_t *retained_cond_set = nullptr; | |
| 3052 if (l->feature_record_cond_idx_map != nullptr) | |
| 3053 retained_cond_set = l->feature_record_cond_idx_map->get (l->cur_feature_var_record_idx); | |
| 3054 | |
| 3055 unsigned int count = conditions.len; | |
| 3056 for (unsigned int i = 0; i < count; i++) | |
| 3057 { | |
| 3058 if (retained_cond_set != nullptr && !retained_cond_set->has (i)) | |
| 3059 continue; | |
| 3060 subset_offset_array (c, out->conditions, this) (conditions[i]); | |
| 3061 } | |
| 3062 | |
| 3063 return_trace (bool (out->conditions)); | |
| 3064 } | |
| 3065 | |
| 3066 bool sanitize (hb_sanitize_context_t *c) const | |
| 3067 { | |
| 3068 TRACE_SANITIZE (this); | |
| 3069 return_trace (conditions.sanitize (c, this)); | |
| 3070 } | |
| 3071 | |
| 3072 protected: | |
| 3073 Array16OfOffset32To<Condition> conditions; | |
| 3074 public: | |
| 3075 DEFINE_SIZE_ARRAY (2, conditions); | |
| 3076 }; | |
| 3077 | |
| 3078 struct FeatureTableSubstitutionRecord | |
| 3079 { | |
| 3080 friend struct FeatureTableSubstitution; | |
| 3081 | |
| 3082 void collect_lookups (const void *base, hb_set_t *lookup_indexes /* OUT */) const | |
| 3083 { | |
| 3084 return (base+feature).add_lookup_indexes_to (lookup_indexes); | |
| 3085 } | |
| 3086 | |
| 3087 void closure_features (const void *base, | |
| 3088 const hb_map_t *lookup_indexes, | |
| 3089 hb_set_t *feature_indexes /* OUT */) const | |
| 3090 { | |
| 3091 if ((base+feature).intersects_lookup_indexes (lookup_indexes)) | |
| 3092 feature_indexes->add (featureIndex); | |
| 3093 } | |
| 3094 | |
| 3095 void collect_feature_substitutes_with_variations (hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map, | |
| 3096 const hb_set_t *feature_indices, | |
| 3097 const void *base) const | |
| 3098 { | |
| 3099 if (feature_indices->has (featureIndex)) | |
| 3100 feature_substitutes_map->set (featureIndex, &(base+feature)); | |
| 3101 } | |
| 3102 | |
| 3103 bool subset (hb_subset_layout_context_t *c, const void *base) const | |
| 3104 { | |
| 3105 TRACE_SUBSET (this); | |
| 3106 if (!c->feature_index_map->has (featureIndex) || | |
| 3107 c->feature_substitutes_map->has (featureIndex)) { | |
| 3108 // Feature that is being substituted is not being retained, so we don't | |
| 3109 // need this. | |
| 3110 return_trace (false); | |
| 3111 } | |
| 3112 | |
| 3113 auto *out = c->subset_context->serializer->embed (this); | |
| 3114 if (unlikely (!out)) return_trace (false); | |
| 3115 | |
| 3116 out->featureIndex = c->feature_index_map->get (featureIndex); | |
| 3117 bool ret = out->feature.serialize_subset (c->subset_context, feature, base, c); | |
| 3118 return_trace (ret); | |
| 3119 } | |
| 3120 | |
| 3121 bool sanitize (hb_sanitize_context_t *c, const void *base) const | |
| 3122 { | |
| 3123 TRACE_SANITIZE (this); | |
| 3124 return_trace (c->check_struct (this) && feature.sanitize (c, base)); | |
| 3125 } | |
| 3126 | |
| 3127 protected: | |
| 3128 HBUINT16 featureIndex; | |
| 3129 Offset32To<Feature> feature; | |
| 3130 public: | |
| 3131 DEFINE_SIZE_STATIC (6); | |
| 3132 }; | |
| 3133 | |
| 3134 struct FeatureTableSubstitution | |
| 3135 { | |
| 3136 const Feature *find_substitute (unsigned int feature_index) const | |
| 3137 { | |
| 3138 unsigned int count = substitutions.len; | |
| 3139 for (unsigned int i = 0; i < count; i++) | |
| 3140 { | |
| 3141 const FeatureTableSubstitutionRecord &record = substitutions.arrayZ[i]; | |
| 3142 if (record.featureIndex == feature_index) | |
| 3143 return &(this+record.feature); | |
| 3144 } | |
| 3145 return nullptr; | |
| 3146 } | |
| 3147 | |
| 3148 void collect_lookups (const hb_set_t *feature_indexes, | |
| 3149 const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map, | |
| 3150 hb_set_t *lookup_indexes /* OUT */) const | |
| 3151 { | |
| 3152 + hb_iter (substitutions) | |
| 3153 | hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex) | |
| 3154 | hb_filter ([feature_substitutes_map] (const FeatureTableSubstitutionRecord& record) | |
| 3155 { | |
| 3156 if (feature_substitutes_map == nullptr) return true; | |
| 3157 return !feature_substitutes_map->has (record.featureIndex); | |
| 3158 }) | |
| 3159 | hb_apply ([this, lookup_indexes] (const FeatureTableSubstitutionRecord& r) | |
| 3160 { r.collect_lookups (this, lookup_indexes); }) | |
| 3161 ; | |
| 3162 } | |
| 3163 | |
| 3164 void closure_features (const hb_map_t *lookup_indexes, | |
| 3165 hb_set_t *feature_indexes /* OUT */) const | |
| 3166 { | |
| 3167 for (const FeatureTableSubstitutionRecord& record : substitutions) | |
| 3168 record.closure_features (this, lookup_indexes, feature_indexes); | |
| 3169 } | |
| 3170 | |
| 3171 bool intersects_features (const hb_map_t *feature_index_map) const | |
| 3172 { | |
| 3173 for (const FeatureTableSubstitutionRecord& record : substitutions) | |
| 3174 { | |
| 3175 if (feature_index_map->has (record.featureIndex)) return true; | |
| 3176 } | |
| 3177 return false; | |
| 3178 } | |
| 3179 | |
| 3180 void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const | |
| 3181 { | |
| 3182 for (const FeatureTableSubstitutionRecord& record : substitutions) | |
| 3183 record.collect_feature_substitutes_with_variations (c->feature_substitutes_map, c->feature_indices, this); | |
| 3184 } | |
| 3185 | |
| 3186 bool subset (hb_subset_context_t *c, | |
| 3187 hb_subset_layout_context_t *l) const | |
| 3188 { | |
| 3189 TRACE_SUBSET (this); | |
| 3190 auto *out = c->serializer->start_embed (*this); | |
| 3191 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 3192 | |
| 3193 out->version.major = version.major; | |
| 3194 out->version.minor = version.minor; | |
| 3195 | |
| 3196 + substitutions.iter () | |
| 3197 | hb_apply (subset_record_array (l, &(out->substitutions), this)) | |
| 3198 ; | |
| 3199 | |
| 3200 return_trace (bool (out->substitutions)); | |
| 3201 } | |
| 3202 | |
| 3203 bool sanitize (hb_sanitize_context_t *c) const | |
| 3204 { | |
| 3205 TRACE_SANITIZE (this); | |
| 3206 return_trace (version.sanitize (c) && | |
| 3207 likely (version.major == 1) && | |
| 3208 substitutions.sanitize (c, this)); | |
| 3209 } | |
| 3210 | |
| 3211 protected: | |
| 3212 FixedVersion<> version; /* Version--0x00010000u */ | |
| 3213 Array16Of<FeatureTableSubstitutionRecord> | |
| 3214 substitutions; | |
| 3215 public: | |
| 3216 DEFINE_SIZE_ARRAY (6, substitutions); | |
| 3217 }; | |
| 3218 | |
| 3219 struct FeatureVariationRecord | |
| 3220 { | |
| 3221 friend struct FeatureVariations; | |
| 3222 | |
| 3223 void collect_lookups (const void *base, | |
| 3224 const hb_set_t *feature_indexes, | |
| 3225 const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map, | |
| 3226 hb_set_t *lookup_indexes /* OUT */) const | |
| 3227 { | |
| 3228 return (base+substitutions).collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes); | |
| 3229 } | |
| 3230 | |
| 3231 void closure_features (const void *base, | |
| 3232 const hb_map_t *lookup_indexes, | |
| 3233 hb_set_t *feature_indexes /* OUT */) const | |
| 3234 { | |
| 3235 (base+substitutions).closure_features (lookup_indexes, feature_indexes); | |
| 3236 } | |
| 3237 | |
| 3238 bool intersects_features (const void *base, const hb_map_t *feature_index_map) const | |
| 3239 { | |
| 3240 return (base+substitutions).intersects_features (feature_index_map); | |
| 3241 } | |
| 3242 | |
| 3243 void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, | |
| 3244 const void *base) const | |
| 3245 { | |
| 3246 // ret == 1, all conditions met | |
| 3247 if ((base+conditions).keep_with_variations (c) == DROP_COND_WITH_VAR && | |
| 3248 c->apply) | |
| 3249 { | |
| 3250 (base+substitutions).collect_feature_substitutes_with_variations (c); | |
| 3251 c->apply = false; // set variations only once | |
| 3252 } | |
| 3253 } | |
| 3254 | |
| 3255 bool subset (hb_subset_layout_context_t *c, const void *base) const | |
| 3256 { | |
| 3257 TRACE_SUBSET (this); | |
| 3258 auto *out = c->subset_context->serializer->embed (this); | |
| 3259 if (unlikely (!out)) return_trace (false); | |
| 3260 | |
| 3261 out->conditions.serialize_subset (c->subset_context, conditions, base, c); | |
| 3262 out->substitutions.serialize_subset (c->subset_context, substitutions, base, c); | |
| 3263 | |
| 3264 return_trace (true); | |
| 3265 } | |
| 3266 | |
| 3267 bool sanitize (hb_sanitize_context_t *c, const void *base) const | |
| 3268 { | |
| 3269 TRACE_SANITIZE (this); | |
| 3270 return_trace (conditions.sanitize (c, base) && | |
| 3271 substitutions.sanitize (c, base)); | |
| 3272 } | |
| 3273 | |
| 3274 protected: | |
| 3275 Offset32To<ConditionSet> | |
| 3276 conditions; | |
| 3277 Offset32To<FeatureTableSubstitution> | |
| 3278 substitutions; | |
| 3279 public: | |
| 3280 DEFINE_SIZE_STATIC (8); | |
| 3281 }; | |
| 3282 | |
| 3283 struct FeatureVariations | |
| 3284 { | |
| 3285 static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFFFFFu; | |
| 3286 | |
| 3287 bool find_index (const int *coords, unsigned int coord_len, | |
| 3288 unsigned int *index) const | |
| 3289 { | |
| 3290 unsigned int count = varRecords.len; | |
| 3291 for (unsigned int i = 0; i < count; i++) | |
| 3292 { | |
| 3293 const FeatureVariationRecord &record = varRecords.arrayZ[i]; | |
| 3294 if ((this+record.conditions).evaluate (coords, coord_len)) | |
| 3295 { | |
| 3296 *index = i; | |
| 3297 return true; | |
| 3298 } | |
| 3299 } | |
| 3300 *index = NOT_FOUND_INDEX; | |
| 3301 return false; | |
| 3302 } | |
| 3303 | |
| 3304 const Feature *find_substitute (unsigned int variations_index, | |
| 3305 unsigned int feature_index) const | |
| 3306 { | |
| 3307 const FeatureVariationRecord &record = varRecords[variations_index]; | |
| 3308 return (this+record.substitutions).find_substitute (feature_index); | |
| 3309 } | |
| 3310 | |
| 3311 void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const | |
| 3312 { | |
| 3313 unsigned int count = varRecords.len; | |
| 3314 for (unsigned int i = 0; i < count; i++) | |
| 3315 { | |
| 3316 c->cur_record_idx = i; | |
| 3317 varRecords[i].collect_feature_substitutes_with_variations (c, this); | |
| 3318 } | |
| 3319 } | |
| 3320 | |
| 3321 FeatureVariations* copy (hb_serialize_context_t *c) const | |
| 3322 { | |
| 3323 TRACE_SERIALIZE (this); | |
| 3324 return_trace (c->embed (*this)); | |
| 3325 } | |
| 3326 | |
| 3327 void collect_lookups (const hb_set_t *feature_indexes, | |
| 3328 const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map, | |
| 3329 hb_set_t *lookup_indexes /* OUT */) const | |
| 3330 { | |
| 3331 for (const FeatureVariationRecord& r : varRecords) | |
| 3332 r.collect_lookups (this, feature_indexes, feature_substitutes_map, lookup_indexes); | |
| 3333 } | |
| 3334 | |
| 3335 void closure_features (const hb_map_t *lookup_indexes, | |
| 3336 const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, | |
| 3337 hb_set_t *feature_indexes /* OUT */) const | |
| 3338 { | |
| 3339 unsigned int count = varRecords.len; | |
| 3340 for (unsigned int i = 0; i < count; i++) | |
| 3341 { | |
| 3342 if (feature_record_cond_idx_map != nullptr && | |
| 3343 !feature_record_cond_idx_map->has (i)) | |
| 3344 continue; | |
| 3345 varRecords[i].closure_features (this, lookup_indexes, feature_indexes); | |
| 3346 } | |
| 3347 } | |
| 3348 | |
| 3349 bool subset (hb_subset_context_t *c, | |
| 3350 hb_subset_layout_context_t *l) const | |
| 3351 { | |
| 3352 TRACE_SUBSET (this); | |
| 3353 auto *out = c->serializer->start_embed (*this); | |
| 3354 if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); | |
| 3355 | |
| 3356 out->version.major = version.major; | |
| 3357 out->version.minor = version.minor; | |
| 3358 | |
| 3359 int keep_up_to = -1; | |
| 3360 for (int i = varRecords.len - 1; i >= 0; i--) { | |
| 3361 if (varRecords[i].intersects_features (this, l->feature_index_map)) { | |
| 3362 keep_up_to = i; | |
| 3363 break; | |
| 3364 } | |
| 3365 } | |
| 3366 | |
| 3367 unsigned count = (unsigned) (keep_up_to + 1); | |
| 3368 for (unsigned i = 0; i < count; i++) | |
| 3369 { | |
| 3370 if (l->feature_record_cond_idx_map != nullptr && | |
| 3371 !l->feature_record_cond_idx_map->has (i)) | |
| 3372 continue; | |
| 3373 | |
| 3374 l->cur_feature_var_record_idx = i; | |
| 3375 subset_record_array (l, &(out->varRecords), this) (varRecords[i]); | |
| 3376 } | |
| 3377 return_trace (bool (out->varRecords)); | |
| 3378 } | |
| 3379 | |
| 3380 bool sanitize (hb_sanitize_context_t *c) const | |
| 3381 { | |
| 3382 TRACE_SANITIZE (this); | |
| 3383 return_trace (version.sanitize (c) && | |
| 3384 likely (version.major == 1) && | |
| 3385 varRecords.sanitize (c, this)); | |
| 3386 } | |
| 3387 | |
| 3388 protected: | |
| 3389 FixedVersion<> version; /* Version--0x00010000u */ | |
| 3390 Array32Of<FeatureVariationRecord> | |
| 3391 varRecords; | |
| 3392 public: | |
| 3393 DEFINE_SIZE_ARRAY_SIZED (8, varRecords); | |
| 3394 }; | |
| 3395 | |
| 3396 | |
| 3397 /* | |
| 3398 * Device Tables | |
| 3399 */ | |
| 3400 | |
| 3401 struct HintingDevice | |
| 3402 { | |
| 3403 friend struct Device; | |
| 3404 | |
| 3405 private: | |
| 3406 | |
| 3407 hb_position_t get_x_delta (hb_font_t *font) const | |
| 3408 { return get_delta (font->x_ppem, font->x_scale); } | |
| 3409 | |
| 3410 hb_position_t get_y_delta (hb_font_t *font) const | |
| 3411 { return get_delta (font->y_ppem, font->y_scale); } | |
| 3412 | |
| 3413 public: | |
| 3414 | |
| 3415 unsigned int get_size () const | |
| 3416 { | |
| 3417 unsigned int f = deltaFormat; | |
| 3418 if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * HBUINT16::static_size; | |
| 3419 return HBUINT16::static_size * (4 + ((endSize - startSize) >> (4 - f))); | |
| 3420 } | |
| 3421 | |
| 3422 bool sanitize (hb_sanitize_context_t *c) const | |
| 3423 { | |
| 3424 TRACE_SANITIZE (this); | |
| 3425 return_trace (c->check_struct (this) && c->check_range (this, this->get_size ())); | |
| 3426 } | |
| 3427 | |
| 3428 HintingDevice* copy (hb_serialize_context_t *c) const | |
| 3429 { | |
| 3430 TRACE_SERIALIZE (this); | |
| 3431 return_trace (c->embed<HintingDevice> (this)); | |
| 3432 } | |
| 3433 | |
| 3434 private: | |
| 3435 | |
| 3436 int get_delta (unsigned int ppem, int scale) const | |
| 3437 { | |
| 3438 if (!ppem) return 0; | |
| 3439 | |
| 3440 int pixels = get_delta_pixels (ppem); | |
| 3441 | |
| 3442 if (!pixels) return 0; | |
| 3443 | |
| 3444 return (int) (pixels * (int64_t) scale / ppem); | |
| 3445 } | |
| 3446 int get_delta_pixels (unsigned int ppem_size) const | |
| 3447 { | |
| 3448 unsigned int f = deltaFormat; | |
| 3449 if (unlikely (f < 1 || f > 3)) | |
| 3450 return 0; | |
| 3451 | |
| 3452 if (ppem_size < startSize || ppem_size > endSize) | |
| 3453 return 0; | |
| 3454 | |
| 3455 unsigned int s = ppem_size - startSize; | |
| 3456 | |
| 3457 unsigned int byte = deltaValueZ[s >> (4 - f)]; | |
| 3458 unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f))); | |
| 3459 unsigned int mask = (0xFFFFu >> (16 - (1 << f))); | |
| 3460 | |
| 3461 int delta = bits & mask; | |
| 3462 | |
| 3463 if ((unsigned int) delta >= ((mask + 1) >> 1)) | |
| 3464 delta -= mask + 1; | |
| 3465 | |
| 3466 return delta; | |
| 3467 } | |
| 3468 | |
| 3469 protected: | |
| 3470 HBUINT16 startSize; /* Smallest size to correct--in ppem */ | |
| 3471 HBUINT16 endSize; /* Largest size to correct--in ppem */ | |
| 3472 HBUINT16 deltaFormat; /* Format of DeltaValue array data: 1, 2, or 3 | |
| 3473 * 1 Signed 2-bit value, 8 values per uint16 | |
| 3474 * 2 Signed 4-bit value, 4 values per uint16 | |
| 3475 * 3 Signed 8-bit value, 2 values per uint16 | |
| 3476 */ | |
| 3477 UnsizedArrayOf<HBUINT16> | |
| 3478 deltaValueZ; /* Array of compressed data */ | |
| 3479 public: | |
| 3480 DEFINE_SIZE_ARRAY (6, deltaValueZ); | |
| 3481 }; | |
| 3482 | |
| 3483 struct VariationDevice | |
| 3484 { | |
| 3485 friend struct Device; | |
| 3486 | |
| 3487 private: | |
| 3488 | |
| 3489 hb_position_t get_x_delta (hb_font_t *font, | |
| 3490 const VariationStore &store, | |
| 3491 VariationStore::cache_t *store_cache = nullptr) const | |
| 3492 { return font->em_scalef_x (get_delta (font, store, store_cache)); } | |
| 3493 | |
| 3494 hb_position_t get_y_delta (hb_font_t *font, | |
| 3495 const VariationStore &store, | |
| 3496 VariationStore::cache_t *store_cache = nullptr) const | |
| 3497 { return font->em_scalef_y (get_delta (font, store, store_cache)); } | |
| 3498 | |
| 3499 VariationDevice* copy (hb_serialize_context_t *c, | |
| 3500 const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const | |
| 3501 { | |
| 3502 TRACE_SERIALIZE (this); | |
| 3503 if (!layout_variation_idx_delta_map) return_trace (nullptr); | |
| 3504 | |
| 3505 hb_pair_t<unsigned, int> *v; | |
| 3506 if (!layout_variation_idx_delta_map->has (varIdx, &v)) | |
| 3507 return_trace (nullptr); | |
| 3508 | |
| 3509 c->start_zerocopy (this->static_size); | |
| 3510 auto *out = c->embed (this); | |
| 3511 if (unlikely (!out)) return_trace (nullptr); | |
| 3512 | |
| 3513 unsigned new_idx = hb_first (*v); | |
| 3514 out->varIdx = new_idx; | |
| 3515 return_trace (out); | |
| 3516 } | |
| 3517 | |
| 3518 void collect_variation_index (hb_collect_variation_indices_context_t *c) const | |
| 3519 { | |
| 3520 c->layout_variation_indices->add (varIdx); | |
| 3521 int delta = 0; | |
| 3522 if (c->font && c->var_store) | |
| 3523 delta = roundf (get_delta (c->font, *c->var_store, c->store_cache)); | |
| 3524 | |
| 3525 /* set new varidx to HB_OT_LAYOUT_NO_VARIATIONS_INDEX here, will remap | |
| 3526 * varidx later*/ | |
| 3527 c->varidx_delta_map->set (varIdx, hb_pair_t<unsigned, int> (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta)); | |
| 3528 } | |
| 3529 | |
| 3530 bool sanitize (hb_sanitize_context_t *c) const | |
| 3531 { | |
| 3532 TRACE_SANITIZE (this); | |
| 3533 return_trace (c->check_struct (this)); | |
| 3534 } | |
| 3535 | |
| 3536 private: | |
| 3537 | |
| 3538 float get_delta (hb_font_t *font, | |
| 3539 const VariationStore &store, | |
| 3540 VariationStore::cache_t *store_cache = nullptr) const | |
| 3541 { | |
| 3542 return store.get_delta (varIdx, font->coords, font->num_coords, (VariationStore::cache_t *) store_cache); | |
| 3543 } | |
| 3544 | |
| 3545 protected: | |
| 3546 VarIdx varIdx; | |
| 3547 HBUINT16 deltaFormat; /* Format identifier for this table: 0x0x8000 */ | |
| 3548 public: | |
| 3549 DEFINE_SIZE_STATIC (6); | |
| 3550 }; | |
| 3551 | |
| 3552 struct DeviceHeader | |
| 3553 { | |
| 3554 protected: | |
| 3555 HBUINT16 reserved1; | |
| 3556 HBUINT16 reserved2; | |
| 3557 public: | |
| 3558 HBUINT16 format; /* Format identifier */ | |
| 3559 public: | |
| 3560 DEFINE_SIZE_STATIC (6); | |
| 3561 }; | |
| 3562 | |
| 3563 struct Device | |
| 3564 { | |
| 3565 hb_position_t get_x_delta (hb_font_t *font, | |
| 3566 const VariationStore &store=Null (VariationStore), | |
| 3567 VariationStore::cache_t *store_cache = nullptr) const | |
| 3568 { | |
| 3569 switch (u.b.format) | |
| 3570 { | |
| 3571 #ifndef HB_NO_HINTING | |
| 3572 case 1: case 2: case 3: | |
| 3573 return u.hinting.get_x_delta (font); | |
| 3574 #endif | |
| 3575 #ifndef HB_NO_VAR | |
| 3576 case 0x8000: | |
| 3577 return u.variation.get_x_delta (font, store, store_cache); | |
| 3578 #endif | |
| 3579 default: | |
| 3580 return 0; | |
| 3581 } | |
| 3582 } | |
| 3583 hb_position_t get_y_delta (hb_font_t *font, | |
| 3584 const VariationStore &store=Null (VariationStore), | |
| 3585 VariationStore::cache_t *store_cache = nullptr) const | |
| 3586 { | |
| 3587 switch (u.b.format) | |
| 3588 { | |
| 3589 case 1: case 2: case 3: | |
| 3590 #ifndef HB_NO_HINTING | |
| 3591 return u.hinting.get_y_delta (font); | |
| 3592 #endif | |
| 3593 #ifndef HB_NO_VAR | |
| 3594 case 0x8000: | |
| 3595 return u.variation.get_y_delta (font, store, store_cache); | |
| 3596 #endif | |
| 3597 default: | |
| 3598 return 0; | |
| 3599 } | |
| 3600 } | |
| 3601 | |
| 3602 bool sanitize (hb_sanitize_context_t *c) const | |
| 3603 { | |
| 3604 TRACE_SANITIZE (this); | |
| 3605 if (!u.b.format.sanitize (c)) return_trace (false); | |
| 3606 switch (u.b.format) { | |
| 3607 #ifndef HB_NO_HINTING | |
| 3608 case 1: case 2: case 3: | |
| 3609 return_trace (u.hinting.sanitize (c)); | |
| 3610 #endif | |
| 3611 #ifndef HB_NO_VAR | |
| 3612 case 0x8000: | |
| 3613 return_trace (u.variation.sanitize (c)); | |
| 3614 #endif | |
| 3615 default: | |
| 3616 return_trace (true); | |
| 3617 } | |
| 3618 } | |
| 3619 | |
| 3620 Device* copy (hb_serialize_context_t *c, | |
| 3621 const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map=nullptr) const | |
| 3622 { | |
| 3623 TRACE_SERIALIZE (this); | |
| 3624 switch (u.b.format) { | |
| 3625 #ifndef HB_NO_HINTING | |
| 3626 case 1: | |
| 3627 case 2: | |
| 3628 case 3: | |
| 3629 return_trace (reinterpret_cast<Device *> (u.hinting.copy (c))); | |
| 3630 #endif | |
| 3631 #ifndef HB_NO_VAR | |
| 3632 case 0x8000: | |
| 3633 return_trace (reinterpret_cast<Device *> (u.variation.copy (c, layout_variation_idx_delta_map))); | |
| 3634 #endif | |
| 3635 default: | |
| 3636 return_trace (nullptr); | |
| 3637 } | |
| 3638 } | |
| 3639 | |
| 3640 void collect_variation_indices (hb_collect_variation_indices_context_t *c) const | |
| 3641 { | |
| 3642 switch (u.b.format) { | |
| 3643 #ifndef HB_NO_HINTING | |
| 3644 case 1: | |
| 3645 case 2: | |
| 3646 case 3: | |
| 3647 return; | |
| 3648 #endif | |
| 3649 #ifndef HB_NO_VAR | |
| 3650 case 0x8000: | |
| 3651 u.variation.collect_variation_index (c); | |
| 3652 return; | |
| 3653 #endif | |
| 3654 default: | |
| 3655 return; | |
| 3656 } | |
| 3657 } | |
| 3658 | |
| 3659 unsigned get_variation_index () const | |
| 3660 { | |
| 3661 switch (u.b.format) { | |
| 3662 #ifndef HB_NO_VAR | |
| 3663 case 0x8000: | |
| 3664 return u.variation.varIdx; | |
| 3665 #endif | |
| 3666 default: | |
| 3667 return HB_OT_LAYOUT_NO_VARIATIONS_INDEX; | |
| 3668 } | |
| 3669 } | |
| 3670 | |
| 3671 protected: | |
| 3672 union { | |
| 3673 DeviceHeader b; | |
| 3674 HintingDevice hinting; | |
| 3675 #ifndef HB_NO_VAR | |
| 3676 VariationDevice variation; | |
| 3677 #endif | |
| 3678 } u; | |
| 3679 public: | |
| 3680 DEFINE_SIZE_UNION (6, b); | |
| 3681 }; | |
| 3682 | |
| 3683 | |
| 3684 } /* namespace OT */ | |
| 3685 | |
| 3686 | |
| 3687 #endif /* HB_OT_LAYOUT_COMMON_HH */ |
