hb-ot-layout-common-private.hh revision bea34c7cbb583cf7660776e95cab3171590b8427
1/* 2 * Copyright (C) 2007,2008,2009 Red Hat, Inc. 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 * 24 * Red Hat Author(s): Behdad Esfahbod 25 */ 26 27#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH 28#define HB_OT_LAYOUT_COMMON_PRIVATE_HH 29 30#include "hb-ot-layout-private.h" 31 32#include "hb-open-type-private.hh" 33 34 35#define NO_CONTEXT ((unsigned int) 0x110000) 36#define NOT_COVERED ((unsigned int) 0x110000) 37#define MAX_NESTING_LEVEL 8 38 39 40/* 41 * 42 * OpenType Layout Common Table Formats 43 * 44 */ 45 46 47/* 48 * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList 49 */ 50 51template <typename Type> 52struct Record 53{ 54 inline bool sanitize (hb_sanitize_context_t *context, void *base) { 55 TRACE_SANITIZE (); 56 return context->check_struct (this) 57 && offset.sanitize (context, base); 58 } 59 60 Tag tag; /* 4-byte Tag identifier */ 61 OffsetTo<Type> 62 offset; /* Offset from beginning of object holding 63 * the Record */ 64 public: 65 DEFINE_SIZE_STATIC (6); 66}; 67 68template <typename Type> 69struct RecordArrayOf : ArrayOf<Record<Type> > { 70 inline const Tag& get_tag (unsigned int i) const 71 { 72 if (unlikely (i >= this->len)) return Null(Tag); 73 return (*this)[i].tag; 74 } 75 inline unsigned int get_tags (unsigned int start_offset, 76 unsigned int *record_count /* IN/OUT */, 77 hb_tag_t *record_tags /* OUT */) const 78 { 79 if (record_count) { 80 const Record<Type> *array = this->sub_array (start_offset, record_count); 81 unsigned int count = *record_count; 82 for (unsigned int i = 0; i < count; i++) 83 record_tags[i] = array[i].tag; 84 } 85 return this->len; 86 } 87 inline bool find_index (hb_tag_t tag, unsigned int *index) const 88 { 89 Tag t; 90 t.set (tag); 91 /* TODO: bsearch (need to sort in sanitize) */ 92 const Record<Type> *a = this->array(); 93 unsigned int count = this->len; 94 for (unsigned int i = 0; i < count; i++) 95 { 96 if (t == a[i].tag) 97 { 98 if (index) *index = i; 99 return true; 100 } 101 } 102 if (index) *index = NO_INDEX; 103 return false; 104 } 105}; 106 107template <typename Type> 108struct RecordListOf : RecordArrayOf<Type> 109{ 110 inline const Type& operator [] (unsigned int i) const 111 { return this+RecordArrayOf<Type>::operator [](i).offset; } 112 113 inline bool sanitize (hb_sanitize_context_t *context) { 114 TRACE_SANITIZE (); 115 return RecordArrayOf<Type>::sanitize (context, CharP(this)); 116 } 117}; 118 119 120struct IndexArray : ArrayOf<USHORT> 121{ 122 inline unsigned int operator [] (unsigned int i) const 123 { 124 if (unlikely (i >= this->len)) 125 return NO_INDEX; 126 return this->array()[i]; 127 } 128 inline unsigned int get_indexes (unsigned int start_offset, 129 unsigned int *_count /* IN/OUT */, 130 unsigned int *_indexes /* OUT */) const 131 { 132 if (_count) { 133 const USHORT *array = this->sub_array (start_offset, _count); 134 unsigned int count = *_count; 135 for (unsigned int i = 0; i < count; i++) 136 _indexes[i] = array[i]; 137 } 138 return this->len; 139 } 140}; 141 142 143struct Script; 144struct LangSys; 145struct Feature; 146 147 148struct LangSys 149{ 150 inline unsigned int get_feature_count (void) const 151 { return featureIndex.len; } 152 inline hb_tag_t get_feature_index (unsigned int i) const 153 { return featureIndex[i]; } 154 inline unsigned int get_feature_indexes (unsigned int start_offset, 155 unsigned int *feature_count /* IN/OUT */, 156 unsigned int *feature_indexes /* OUT */) const 157 { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } 158 159 inline bool has_required_feature (void) const { return reqFeatureIndex != 0xffff; } 160 inline int get_required_feature_index (void) const 161 { 162 if (reqFeatureIndex == 0xffff) 163 return NO_INDEX; 164 return reqFeatureIndex;; 165 } 166 167 inline bool sanitize (hb_sanitize_context_t *context) { 168 TRACE_SANITIZE (); 169 return context->check_struct (this) 170 && featureIndex.sanitize (context); 171 } 172 173 Offset lookupOrder; /* = Null (reserved for an offset to a 174 * reordering table) */ 175 USHORT reqFeatureIndex;/* Index of a feature required for this 176 * language system--if no required features 177 * = 0xFFFF */ 178 IndexArray featureIndex; /* Array of indices into the FeatureList */ 179 public: 180 DEFINE_SIZE_VAR (6, USHORT); 181}; 182DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF"); 183 184 185struct Script 186{ 187 inline unsigned int get_lang_sys_count (void) const 188 { return langSys.len; } 189 inline const Tag& get_lang_sys_tag (unsigned int i) const 190 { return langSys.get_tag (i); } 191 inline unsigned int get_lang_sys_tags (unsigned int start_offset, 192 unsigned int *lang_sys_count /* IN/OUT */, 193 hb_tag_t *lang_sys_tags /* OUT */) const 194 { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } 195 inline const LangSys& get_lang_sys (unsigned int i) const 196 { 197 if (i == NO_INDEX) return get_default_lang_sys (); 198 return this+langSys[i].offset; 199 } 200 inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const 201 { return langSys.find_index (tag, index); } 202 203 inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; } 204 inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } 205 206 inline bool sanitize (hb_sanitize_context_t *context) { 207 TRACE_SANITIZE (); 208 return defaultLangSys.sanitize (context, this) 209 && langSys.sanitize (context, this); 210 } 211 212 private: 213 OffsetTo<LangSys> 214 defaultLangSys; /* Offset to DefaultLangSys table--from 215 * beginning of Script table--may be Null */ 216 RecordArrayOf<LangSys> 217 langSys; /* Array of LangSysRecords--listed 218 * alphabetically by LangSysTag */ 219 public: 220 DEFINE_SIZE_VAR (4, Record<LangSys>); 221}; 222 223typedef RecordListOf<Script> ScriptList; 224 225 226struct Feature 227{ 228 inline unsigned int get_lookup_count (void) const 229 { return lookupIndex.len; } 230 inline hb_tag_t get_lookup_index (unsigned int i) const 231 { return lookupIndex[i]; } 232 inline unsigned int get_lookup_indexes (unsigned int start_index, 233 unsigned int *lookup_count /* IN/OUT */, 234 unsigned int *lookup_tags /* OUT */) const 235 { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); } 236 237 inline bool sanitize (hb_sanitize_context_t *context) { 238 TRACE_SANITIZE (); 239 return context->check_struct (this) 240 && lookupIndex.sanitize (context); 241 } 242 243 /* LONGTERMTODO: implement get_feature_parameters() */ 244 /* LONGTERMTODO: implement FeatureSize and other special features? */ 245 Offset featureParams; /* Offset to Feature Parameters table (if one 246 * has been defined for the feature), relative 247 * to the beginning of the Feature Table; = Null 248 * if not required */ 249 IndexArray lookupIndex; /* Array of LookupList indices */ 250 public: 251 DEFINE_SIZE_VAR (4, USHORT); 252}; 253 254typedef RecordListOf<Feature> FeatureList; 255 256 257struct LookupFlag : USHORT 258{ 259 enum { 260 RightToLeft = 0x0001u, 261 IgnoreBaseGlyphs = 0x0002u, 262 IgnoreLigatures = 0x0004u, 263 IgnoreMarks = 0x0008u, 264 IgnoreFlags = 0x000Eu, 265 UseMarkFilteringSet = 0x0010u, 266 Reserved = 0x00E0u, 267 MarkAttachmentType = 0xFF00u 268 }; 269 public: 270 DEFINE_SIZE_STATIC (2); 271}; 272 273struct Lookup 274{ 275 inline unsigned int get_subtable_count (void) const { return subTable.len; } 276 277 inline unsigned int get_type (void) const { return lookupType; } 278 inline unsigned int get_flag (void) const 279 { 280 unsigned int flag = lookupFlag; 281 if (unlikely (flag & LookupFlag::UseMarkFilteringSet)) 282 { 283 const USHORT &markFilteringSet = StructAfter<USHORT> (subTable); 284 flag += (markFilteringSet << 16); 285 } 286 return flag; 287 } 288 289 inline bool sanitize (hb_sanitize_context_t *context) { 290 TRACE_SANITIZE (); 291 /* Real sanitize of the subtables is done by GSUB/GPOS/... */ 292 if (!(context->check_struct (this) 293 && likely (subTable.sanitize (context)))) return false; 294 if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet)) 295 { 296 USHORT &markFilteringSet = StructAfter<USHORT> (subTable); 297 if (!markFilteringSet.sanitize (context)) return false; 298 } 299 return true; 300 } 301 302 USHORT lookupType; /* Different enumerations for GSUB and GPOS */ 303 USHORT lookupFlag; /* Lookup qualifiers */ 304 ArrayOf<Offset> 305 subTable; /* Array of SubTables */ 306 USHORT markFilteringSetX[VAR]; /* Index (base 0) into GDEF mark glyph sets 307 * structure. This field is only present if bit 308 * UseMarkFilteringSet of lookup flags is set. */ 309 public: 310 DEFINE_SIZE_VAR2 (6, Offset, USHORT); 311}; 312 313typedef OffsetListOf<Lookup> LookupList; 314 315 316/* 317 * Coverage Table 318 */ 319 320struct CoverageFormat1 321{ 322 friend struct Coverage; 323 324 private: 325 inline unsigned int get_coverage (hb_codepoint_t glyph_id) const 326 { 327 if (unlikely (glyph_id > 0xFFFF)) 328 return NOT_COVERED; 329 GlyphID gid; 330 gid.set (glyph_id); 331 /* TODO: bsearch (need to sort in sanitize) */ 332 unsigned int num_glyphs = glyphArray.len; 333 for (unsigned int i = 0; i < num_glyphs; i++) 334 if (gid == glyphArray[i]) 335 return i; 336 return NOT_COVERED; 337 } 338 339 inline bool sanitize (hb_sanitize_context_t *context) { 340 TRACE_SANITIZE (); 341 return glyphArray.sanitize (context); 342 } 343 344 private: 345 USHORT coverageFormat; /* Format identifier--format = 1 */ 346 ArrayOf<GlyphID> 347 glyphArray; /* Array of GlyphIDs--in numerical order */ 348 public: 349 DEFINE_SIZE_VAR (4, GlyphID); 350}; 351 352struct CoverageRangeRecord 353{ 354 friend struct CoverageFormat2; 355 356 private: 357 inline unsigned int get_coverage (hb_codepoint_t glyph_id) const 358 { 359 if (glyph_id >= start && glyph_id <= end) 360 return (unsigned int) startCoverageIndex + (glyph_id - start); 361 return NOT_COVERED; 362 } 363 364 public: 365 inline bool sanitize (hb_sanitize_context_t *context) { 366 TRACE_SANITIZE (); 367 return context->check_struct (this); 368 } 369 370 private: 371 GlyphID start; /* First GlyphID in the range */ 372 GlyphID end; /* Last GlyphID in the range */ 373 USHORT startCoverageIndex; /* Coverage Index of first GlyphID in 374 * range */ 375 public: 376 DEFINE_SIZE_STATIC (6); 377}; 378DEFINE_NULL_DATA (CoverageRangeRecord, "\000\001"); 379 380struct CoverageFormat2 381{ 382 friend struct Coverage; 383 384 private: 385 inline unsigned int get_coverage (hb_codepoint_t glyph_id) const 386 { 387 /* TODO: bsearch (need to sort in sanitize) */ 388 unsigned int count = rangeRecord.len; 389 for (unsigned int i = 0; i < count; i++) 390 { 391 unsigned int coverage = rangeRecord[i].get_coverage (glyph_id); 392 if (coverage != NOT_COVERED) 393 return coverage; 394 } 395 return NOT_COVERED; 396 } 397 398 inline bool sanitize (hb_sanitize_context_t *context) { 399 TRACE_SANITIZE (); 400 return rangeRecord.sanitize (context); 401 } 402 403 private: 404 USHORT coverageFormat; /* Format identifier--format = 2 */ 405 ArrayOf<CoverageRangeRecord> 406 rangeRecord; /* Array of glyph ranges--ordered by 407 * Start GlyphID. rangeCount entries 408 * long */ 409 public: 410 DEFINE_SIZE_VAR (4, CoverageRangeRecord); 411}; 412 413struct Coverage 414{ 415 inline unsigned int operator () (hb_codepoint_t glyph_id) const { return get_coverage (glyph_id); } 416 417 inline unsigned int get_coverage (hb_codepoint_t glyph_id) const 418 { 419 switch (u.format) { 420 case 1: return u.format1->get_coverage(glyph_id); 421 case 2: return u.format2->get_coverage(glyph_id); 422 default:return NOT_COVERED; 423 } 424 } 425 426 inline bool sanitize (hb_sanitize_context_t *context) { 427 TRACE_SANITIZE (); 428 if (!u.format.sanitize (context)) return false; 429 switch (u.format) { 430 case 1: return u.format1->sanitize (context); 431 case 2: return u.format2->sanitize (context); 432 default:return true; 433 } 434 } 435 436 private: 437 union { 438 USHORT format; /* Format identifier */ 439 CoverageFormat1 format1[VAR]; 440 CoverageFormat2 format2[VAR]; 441 } u; 442}; 443 444 445/* 446 * Class Definition Table 447 */ 448 449struct ClassDefFormat1 450{ 451 friend struct ClassDef; 452 453 private: 454 inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const 455 { 456 if ((unsigned int) (glyph_id - startGlyph) < classValue.len) 457 return classValue[glyph_id - startGlyph]; 458 return 0; 459 } 460 461 inline bool sanitize (hb_sanitize_context_t *context) { 462 TRACE_SANITIZE (); 463 return context->check_struct (this) 464 && classValue.sanitize (context); 465 } 466 467 USHORT classFormat; /* Format identifier--format = 1 */ 468 GlyphID startGlyph; /* First GlyphID of the classValueArray */ 469 ArrayOf<USHORT> 470 classValue; /* Array of Class Values--one per GlyphID */ 471 public: 472 DEFINE_SIZE_VAR (6, USHORT); 473}; 474 475struct ClassRangeRecord 476{ 477 friend struct ClassDefFormat2; 478 479 private: 480 inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const 481 { 482 if (glyph_id >= start && glyph_id <= end) 483 return classValue; 484 return 0; 485 } 486 487 public: 488 inline bool sanitize (hb_sanitize_context_t *context) { 489 TRACE_SANITIZE (); 490 return context->check_struct (this); 491 } 492 493 private: 494 GlyphID start; /* First GlyphID in the range */ 495 GlyphID end; /* Last GlyphID in the range */ 496 USHORT classValue; /* Applied to all glyphs in the range */ 497 public: 498 DEFINE_SIZE_STATIC (6); 499}; 500DEFINE_NULL_DATA (ClassRangeRecord, "\000\001"); 501 502struct ClassDefFormat2 503{ 504 friend struct ClassDef; 505 506 private: 507 inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const 508 { 509 /* TODO: bsearch (need to sort in sanitize) */ 510 unsigned int count = rangeRecord.len; 511 for (unsigned int i = 0; i < count; i++) 512 { 513 int classValue = rangeRecord[i].get_class (glyph_id); 514 if (classValue > 0) 515 return classValue; 516 } 517 return 0; 518 } 519 520 inline bool sanitize (hb_sanitize_context_t *context) { 521 TRACE_SANITIZE (); 522 return rangeRecord.sanitize (context); 523 } 524 525 USHORT classFormat; /* Format identifier--format = 2 */ 526 ArrayOf<ClassRangeRecord> 527 rangeRecord; /* Array of glyph ranges--ordered by 528 * Start GlyphID */ 529 public: 530 DEFINE_SIZE_VAR (4, ClassRangeRecord); 531}; 532 533struct ClassDef 534{ 535 inline hb_ot_layout_class_t operator () (hb_codepoint_t glyph_id) const { return get_class (glyph_id); } 536 537 inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const 538 { 539 switch (u.format) { 540 case 1: return u.format1->get_class(glyph_id); 541 case 2: return u.format2->get_class(glyph_id); 542 default:return 0; 543 } 544 } 545 546 inline bool sanitize (hb_sanitize_context_t *context) { 547 TRACE_SANITIZE (); 548 if (!u.format.sanitize (context)) return false; 549 switch (u.format) { 550 case 1: return u.format1->sanitize (context); 551 case 2: return u.format2->sanitize (context); 552 default:return true; 553 } 554 } 555 556 private: 557 union { 558 USHORT format; /* Format identifier */ 559 ClassDefFormat1 format1[VAR]; 560 ClassDefFormat2 format2[VAR]; 561 } u; 562}; 563 564 565/* 566 * Device Tables 567 */ 568 569struct Device 570{ 571 inline int operator () (unsigned int ppem_size) const { return get_delta (ppem_size); } 572 573 inline int get_delta (unsigned int ppem_size) const 574 { 575 unsigned int f = deltaFormat; 576 if (unlikely (f < 1 || f > 3)) 577 return 0; 578 579 if (ppem_size < startSize || ppem_size > endSize) 580 return 0; 581 582 unsigned int s = ppem_size - startSize; 583 584 unsigned int byte = deltaValue[s >> (4 - f)]; 585 unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f))); 586 unsigned int mask = (0xFFFF >> (16 - (1 << f))); 587 588 int delta = bits & mask; 589 590 if ((unsigned int) delta >= ((mask + 1) >> 1)) 591 delta -= mask + 1; 592 593 return delta; 594 } 595 596 inline unsigned int get_size () const 597 { 598 unsigned int f = deltaFormat; 599 if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::get_size (); 600 return USHORT::get_size () * (4 + ((endSize - startSize) >> (4 - f))); 601 } 602 603 inline bool sanitize (hb_sanitize_context_t *context) { 604 TRACE_SANITIZE (); 605 return context->check_struct (this) 606 && context->check_range (this, this->get_size ()); 607 } 608 609 private: 610 USHORT startSize; /* Smallest size to correct--in ppem */ 611 USHORT endSize; /* Largest size to correct--in ppem */ 612 USHORT deltaFormat; /* Format of DeltaValue array data: 1, 2, or 3 613 * 1 Signed 2-bit value, 8 values per uint16 614 * 2 Signed 4-bit value, 4 values per uint16 615 * 3 Signed 8-bit value, 2 values per uint16 616 */ 617 USHORT deltaValue[VAR]; /* Array of compressed data */ 618 public: 619 DEFINE_SIZE_VAR (6, USHORT); 620}; 621 622 623#endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */ 624