1 /* 2 * Copyright © 2014 Google, Inc. 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 * 24 * Google Author(s): Behdad Esfahbod 25 */ 26 27 #ifndef HB_OT_CMAP_TABLE_HH 28 #define HB_OT_CMAP_TABLE_HH 29 30 #include "hb-open-type-private.hh" 31 32 33 namespace OT { 34 35 36 /* 37 * cmap -- Character To Glyph Index Mapping Table 38 */ 39 40 #define HB_OT_TAG_cmap HB_TAG('c','m','a','p') 41 42 43 struct CmapSubtableFormat0 44 { get_glyphOT::CmapSubtableFormat045 inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const 46 { 47 hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; 48 if (!gid) 49 return false; 50 *glyph = gid; 51 return true; 52 } 53 sanitizeOT::CmapSubtableFormat054 inline bool sanitize (hb_sanitize_context_t *c) const 55 { 56 TRACE_SANITIZE (this); 57 return_trace (c->check_struct (this)); 58 } 59 60 protected: 61 UINT16 format; /* Format number is set to 0. */ 62 UINT16 lengthZ; /* Byte length of this subtable. */ 63 UINT16 languageZ; /* Ignore. */ 64 UINT8 glyphIdArray[256];/* An array that maps character 65 * code to glyph index values. */ 66 public: 67 DEFINE_SIZE_STATIC (6 + 256); 68 }; 69 70 struct CmapSubtableFormat4 71 { 72 struct accelerator_t 73 { initOT::CmapSubtableFormat4::accelerator_t74 inline void init (const CmapSubtableFormat4 *subtable) 75 { 76 segCount = subtable->segCountX2 / 2; 77 endCount = subtable->values; 78 startCount = endCount + segCount + 1; 79 idDelta = startCount + segCount; 80 idRangeOffset = idDelta + segCount; 81 glyphIdArray = idRangeOffset + segCount; 82 glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; 83 } 84 get_glyph_funcOT::CmapSubtableFormat4::accelerator_t85 static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) 86 { 87 const accelerator_t *thiz = (const accelerator_t *) obj; 88 89 /* Custom two-array bsearch. */ 90 int min = 0, max = (int) thiz->segCount - 1; 91 const UINT16 *startCount = thiz->startCount; 92 const UINT16 *endCount = thiz->endCount; 93 unsigned int i; 94 while (min <= max) 95 { 96 int mid = (min + max) / 2; 97 if (codepoint < startCount[mid]) 98 max = mid - 1; 99 else if (codepoint > endCount[mid]) 100 min = mid + 1; 101 else 102 { 103 i = mid; 104 goto found; 105 } 106 } 107 return false; 108 109 found: 110 hb_codepoint_t gid; 111 unsigned int rangeOffset = thiz->idRangeOffset[i]; 112 if (rangeOffset == 0) 113 gid = codepoint + thiz->idDelta[i]; 114 else 115 { 116 /* Somebody has been smoking... */ 117 unsigned int index = rangeOffset / 2 + (codepoint - thiz->startCount[i]) + i - thiz->segCount; 118 if (unlikely (index >= thiz->glyphIdArrayLength)) 119 return false; 120 gid = thiz->glyphIdArray[index]; 121 if (unlikely (!gid)) 122 return false; 123 gid += thiz->idDelta[i]; 124 } 125 126 *glyph = gid & 0xFFFFu; 127 return true; 128 } 129 130 const UINT16 *endCount; 131 const UINT16 *startCount; 132 const UINT16 *idDelta; 133 const UINT16 *idRangeOffset; 134 const UINT16 *glyphIdArray; 135 unsigned int segCount; 136 unsigned int glyphIdArrayLength; 137 }; 138 get_glyphOT::CmapSubtableFormat4139 inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const 140 { 141 accelerator_t accel; 142 accel.init (this); 143 return accel.get_glyph_func (&accel, codepoint, glyph); 144 } 145 sanitizeOT::CmapSubtableFormat4146 inline bool sanitize (hb_sanitize_context_t *c) const 147 { 148 TRACE_SANITIZE (this); 149 if (unlikely (!c->check_struct (this))) 150 return_trace (false); 151 152 if (unlikely (!c->check_range (this, length))) 153 { 154 /* Some broken fonts have too long of a "length" value. 155 * If that is the case, just change the value to truncate 156 * the subtable at the end of the blob. */ 157 uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535, 158 (uintptr_t) (c->end - 159 (char *) this)); 160 if (!c->try_set (&length, new_length)) 161 return_trace (false); 162 } 163 164 return_trace (16 + 4 * (unsigned int) segCountX2 <= length); 165 } 166 167 protected: 168 UINT16 format; /* Format number is set to 4. */ 169 UINT16 length; /* This is the length in bytes of the 170 * subtable. */ 171 UINT16 languageZ; /* Ignore. */ 172 UINT16 segCountX2; /* 2 x segCount. */ 173 UINT16 searchRangeZ; /* 2 * (2**floor(log2(segCount))) */ 174 UINT16 entrySelectorZ; /* log2(searchRange/2) */ 175 UINT16 rangeShiftZ; /* 2 x segCount - searchRange */ 176 177 UINT16 values[VAR]; 178 #if 0 179 UINT16 endCount[segCount]; /* End characterCode for each segment, 180 * last=0xFFFFu. */ 181 UINT16 reservedPad; /* Set to 0. */ 182 UINT16 startCount[segCount]; /* Start character code for each segment. */ 183 INT16 idDelta[segCount]; /* Delta for all character codes in segment. */ 184 UINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ 185 UINT16 glyphIdArray[VAR]; /* Glyph index array (arbitrary length) */ 186 #endif 187 188 public: 189 DEFINE_SIZE_ARRAY (14, values); 190 }; 191 192 struct CmapSubtableLongGroup 193 { 194 friend struct CmapSubtableFormat12; 195 friend struct CmapSubtableFormat13; 196 cmpOT::CmapSubtableLongGroup197 int cmp (hb_codepoint_t codepoint) const 198 { 199 if (codepoint < startCharCode) return -1; 200 if (codepoint > endCharCode) return +1; 201 return 0; 202 } 203 sanitizeOT::CmapSubtableLongGroup204 inline bool sanitize (hb_sanitize_context_t *c) const 205 { 206 TRACE_SANITIZE (this); 207 return_trace (c->check_struct (this)); 208 } 209 210 private: 211 UINT32 startCharCode; /* First character code in this group. */ 212 UINT32 endCharCode; /* Last character code in this group. */ 213 UINT32 glyphID; /* Glyph index; interpretation depends on 214 * subtable format. */ 215 public: 216 DEFINE_SIZE_STATIC (12); 217 }; 218 219 template <typename UINT> 220 struct CmapSubtableTrimmed 221 { get_glyphOT::CmapSubtableTrimmed222 inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const 223 { 224 /* Rely on our implicit array bound-checking. */ 225 hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode]; 226 if (!gid) 227 return false; 228 *glyph = gid; 229 return true; 230 } 231 sanitizeOT::CmapSubtableTrimmed232 inline bool sanitize (hb_sanitize_context_t *c) const 233 { 234 TRACE_SANITIZE (this); 235 return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); 236 } 237 238 protected: 239 UINT formatReserved; /* Subtable format and (maybe) padding. */ 240 UINT lengthZ; /* Byte length of this subtable. */ 241 UINT languageZ; /* Ignore. */ 242 UINT startCharCode; /* First character code covered. */ 243 ArrayOf<GlyphID, UINT> 244 glyphIdArray; /* Array of glyph index values for character 245 * codes in the range. */ 246 public: 247 DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); 248 }; 249 250 struct CmapSubtableFormat6 : CmapSubtableTrimmed<UINT16> {}; 251 struct CmapSubtableFormat10 : CmapSubtableTrimmed<UINT32 > {}; 252 253 template <typename T> 254 struct CmapSubtableLongSegmented 255 { get_glyphOT::CmapSubtableLongSegmented256 inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const 257 { 258 int i = groups.bsearch (codepoint); 259 if (i == -1) 260 return false; 261 *glyph = T::group_get_glyph (groups[i], codepoint); 262 return true; 263 } 264 sanitizeOT::CmapSubtableLongSegmented265 inline bool sanitize (hb_sanitize_context_t *c) const 266 { 267 TRACE_SANITIZE (this); 268 return_trace (c->check_struct (this) && groups.sanitize (c)); 269 } 270 271 protected: 272 UINT16 format; /* Subtable format; set to 12. */ 273 UINT16 reservedZ; /* Reserved; set to 0. */ 274 UINT32 lengthZ; /* Byte length of this subtable. */ 275 UINT32 languageZ; /* Ignore. */ 276 SortedArrayOf<CmapSubtableLongGroup, UINT32> 277 groups; /* Groupings. */ 278 public: 279 DEFINE_SIZE_ARRAY (16, groups); 280 }; 281 282 struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12> 283 { group_get_glyphOT::CmapSubtableFormat12284 static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, 285 hb_codepoint_t u) 286 { return group.glyphID + (u - group.startCharCode); } 287 }; 288 289 struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13> 290 { group_get_glyphOT::CmapSubtableFormat13291 static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, 292 hb_codepoint_t u HB_UNUSED) 293 { return group.glyphID; } 294 }; 295 296 typedef enum 297 { 298 GLYPH_VARIANT_NOT_FOUND = 0, 299 GLYPH_VARIANT_FOUND = 1, 300 GLYPH_VARIANT_USE_DEFAULT = 2 301 } glyph_variant_t; 302 303 struct UnicodeValueRange 304 { cmpOT::UnicodeValueRange305 inline int cmp (const hb_codepoint_t &codepoint) const 306 { 307 if (codepoint < startUnicodeValue) return -1; 308 if (codepoint > startUnicodeValue + additionalCount) return +1; 309 return 0; 310 } 311 sanitizeOT::UnicodeValueRange312 inline bool sanitize (hb_sanitize_context_t *c) const 313 { 314 TRACE_SANITIZE (this); 315 return_trace (c->check_struct (this)); 316 } 317 318 UINT24 startUnicodeValue; /* First value in this range. */ 319 UINT8 additionalCount; /* Number of additional values in this 320 * range. */ 321 public: 322 DEFINE_SIZE_STATIC (4); 323 }; 324 325 typedef SortedArrayOf<UnicodeValueRange, UINT32> DefaultUVS; 326 327 struct UVSMapping 328 { cmpOT::UVSMapping329 inline int cmp (const hb_codepoint_t &codepoint) const 330 { 331 return unicodeValue.cmp (codepoint); 332 } 333 sanitizeOT::UVSMapping334 inline bool sanitize (hb_sanitize_context_t *c) const 335 { 336 TRACE_SANITIZE (this); 337 return_trace (c->check_struct (this)); 338 } 339 340 UINT24 unicodeValue; /* Base Unicode value of the UVS */ 341 GlyphID glyphID; /* Glyph ID of the UVS */ 342 public: 343 DEFINE_SIZE_STATIC (5); 344 }; 345 346 typedef SortedArrayOf<UVSMapping, UINT32> NonDefaultUVS; 347 348 struct VariationSelectorRecord 349 { get_glyphOT::VariationSelectorRecord350 inline glyph_variant_t get_glyph (hb_codepoint_t codepoint, 351 hb_codepoint_t *glyph, 352 const void *base) const 353 { 354 int i; 355 const DefaultUVS &defaults = base+defaultUVS; 356 i = defaults.bsearch (codepoint); 357 if (i != -1) 358 return GLYPH_VARIANT_USE_DEFAULT; 359 const NonDefaultUVS &nonDefaults = base+nonDefaultUVS; 360 i = nonDefaults.bsearch (codepoint); 361 if (i != -1) 362 { 363 *glyph = nonDefaults[i].glyphID; 364 return GLYPH_VARIANT_FOUND; 365 } 366 return GLYPH_VARIANT_NOT_FOUND; 367 } 368 cmpOT::VariationSelectorRecord369 inline int cmp (const hb_codepoint_t &variation_selector) const 370 { 371 return varSelector.cmp (variation_selector); 372 } 373 sanitizeOT::VariationSelectorRecord374 inline bool sanitize (hb_sanitize_context_t *c, const void *base) const 375 { 376 TRACE_SANITIZE (this); 377 return_trace (c->check_struct (this) && 378 defaultUVS.sanitize (c, base) && 379 nonDefaultUVS.sanitize (c, base)); 380 } 381 382 UINT24 varSelector; /* Variation selector. */ 383 LOffsetTo<DefaultUVS> 384 defaultUVS; /* Offset to Default UVS Table. May be 0. */ 385 LOffsetTo<NonDefaultUVS> 386 nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ 387 public: 388 DEFINE_SIZE_STATIC (11); 389 }; 390 391 struct CmapSubtableFormat14 392 { get_glyph_variantOT::CmapSubtableFormat14393 inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, 394 hb_codepoint_t variation_selector, 395 hb_codepoint_t *glyph) const 396 { 397 return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this); 398 } 399 sanitizeOT::CmapSubtableFormat14400 inline bool sanitize (hb_sanitize_context_t *c) const 401 { 402 TRACE_SANITIZE (this); 403 return_trace (c->check_struct (this) && 404 record.sanitize (c, this)); 405 } 406 407 protected: 408 UINT16 format; /* Format number is set to 14. */ 409 UINT32 lengthZ; /* Byte length of this subtable. */ 410 SortedArrayOf<VariationSelectorRecord, UINT32> 411 record; /* Variation selector records; sorted 412 * in increasing order of `varSelector'. */ 413 public: 414 DEFINE_SIZE_ARRAY (10, record); 415 }; 416 417 struct CmapSubtable 418 { 419 /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ 420 get_glyphOT::CmapSubtable421 inline bool get_glyph (hb_codepoint_t codepoint, 422 hb_codepoint_t *glyph) const 423 { 424 switch (u.format) { 425 case 0: return u.format0 .get_glyph(codepoint, glyph); 426 case 4: return u.format4 .get_glyph(codepoint, glyph); 427 case 6: return u.format6 .get_glyph(codepoint, glyph); 428 case 10: return u.format10.get_glyph(codepoint, glyph); 429 case 12: return u.format12.get_glyph(codepoint, glyph); 430 case 13: return u.format13.get_glyph(codepoint, glyph); 431 case 14: 432 default: return false; 433 } 434 } 435 sanitizeOT::CmapSubtable436 inline bool sanitize (hb_sanitize_context_t *c) const 437 { 438 TRACE_SANITIZE (this); 439 if (!u.format.sanitize (c)) return_trace (false); 440 switch (u.format) { 441 case 0: return_trace (u.format0 .sanitize (c)); 442 case 4: return_trace (u.format4 .sanitize (c)); 443 case 6: return_trace (u.format6 .sanitize (c)); 444 case 10: return_trace (u.format10.sanitize (c)); 445 case 12: return_trace (u.format12.sanitize (c)); 446 case 13: return_trace (u.format13.sanitize (c)); 447 case 14: return_trace (u.format14.sanitize (c)); 448 default:return_trace (true); 449 } 450 } 451 452 public: 453 union { 454 UINT16 format; /* Format identifier */ 455 CmapSubtableFormat0 format0; 456 CmapSubtableFormat4 format4; 457 CmapSubtableFormat6 format6; 458 CmapSubtableFormat10 format10; 459 CmapSubtableFormat12 format12; 460 CmapSubtableFormat13 format13; 461 CmapSubtableFormat14 format14; 462 } u; 463 public: 464 DEFINE_SIZE_UNION (2, format); 465 }; 466 467 468 struct EncodingRecord 469 { cmpOT::EncodingRecord470 inline int cmp (const EncodingRecord &other) const 471 { 472 int ret; 473 ret = platformID.cmp (other.platformID); 474 if (ret) return ret; 475 ret = encodingID.cmp (other.encodingID); 476 if (ret) return ret; 477 return 0; 478 } 479 sanitizeOT::EncodingRecord480 inline bool sanitize (hb_sanitize_context_t *c, const void *base) const 481 { 482 TRACE_SANITIZE (this); 483 return_trace (c->check_struct (this) && 484 subtable.sanitize (c, base)); 485 } 486 487 UINT16 platformID; /* Platform ID. */ 488 UINT16 encodingID; /* Platform-specific encoding ID. */ 489 LOffsetTo<CmapSubtable> 490 subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ 491 public: 492 DEFINE_SIZE_STATIC (8); 493 }; 494 495 struct cmap 496 { 497 static const hb_tag_t tableTag = HB_OT_TAG_cmap; 498 sanitizeOT::cmap499 inline bool sanitize (hb_sanitize_context_t *c) const 500 { 501 TRACE_SANITIZE (this); 502 return_trace (c->check_struct (this) && 503 likely (version == 0) && 504 encodingRecord.sanitize (c, this)); 505 } 506 507 struct accelerator_t 508 { initOT::cmap::accelerator_t509 inline void init (hb_face_t *face) 510 { 511 this->blob = OT::Sanitizer<OT::cmap>::sanitize (face->reference_table (HB_OT_TAG_cmap)); 512 const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob); 513 const OT::CmapSubtable *subtable = nullptr; 514 const OT::CmapSubtableFormat14 *subtable_uvs = nullptr; 515 516 bool symbol = false; 517 /* 32-bit subtables. */ 518 if (!subtable) subtable = cmap->find_subtable (3, 10); 519 if (!subtable) subtable = cmap->find_subtable (0, 6); 520 if (!subtable) subtable = cmap->find_subtable (0, 4); 521 /* 16-bit subtables. */ 522 if (!subtable) subtable = cmap->find_subtable (3, 1); 523 if (!subtable) subtable = cmap->find_subtable (0, 3); 524 if (!subtable) subtable = cmap->find_subtable (0, 2); 525 if (!subtable) subtable = cmap->find_subtable (0, 1); 526 if (!subtable) subtable = cmap->find_subtable (0, 0); 527 if (!subtable) 528 { 529 subtable = cmap->find_subtable (3, 0); 530 if (subtable) symbol = true; 531 } 532 /* Meh. */ 533 if (!subtable) subtable = &OT::Null(OT::CmapSubtable); 534 535 /* UVS subtable. */ 536 if (!subtable_uvs) 537 { 538 const OT::CmapSubtable *st = cmap->find_subtable (0, 5); 539 if (st && st->u.format == 14) 540 subtable_uvs = &st->u.format14; 541 } 542 /* Meh. */ 543 if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14); 544 545 this->uvs_table = subtable_uvs; 546 547 this->get_glyph_data = subtable; 548 if (unlikely (symbol)) 549 this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>; 550 else 551 switch (subtable->u.format) { 552 /* Accelerate format 4 and format 12. */ 553 default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>; break; 554 case 12: this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>; break; 555 case 4: 556 { 557 this->format4_accel.init (&subtable->u.format4); 558 this->get_glyph_data = &this->format4_accel; 559 this->get_glyph_func = this->format4_accel.get_glyph_func; 560 } 561 break; 562 } 563 } 564 finiOT::cmap::accelerator_t565 inline void fini (void) 566 { 567 hb_blob_destroy (this->blob); 568 } 569 get_nominal_glyphOT::cmap::accelerator_t570 inline bool get_nominal_glyph (hb_codepoint_t unicode, 571 hb_codepoint_t *glyph) const 572 { 573 return this->get_glyph_func (this->get_glyph_data, unicode, glyph); 574 } 575 get_variation_glyphOT::cmap::accelerator_t576 inline bool get_variation_glyph (hb_codepoint_t unicode, 577 hb_codepoint_t variation_selector, 578 hb_codepoint_t *glyph) const 579 { 580 switch (this->uvs_table->get_glyph_variant (unicode, 581 variation_selector, 582 glyph)) 583 { 584 case OT::GLYPH_VARIANT_NOT_FOUND: return false; 585 case OT::GLYPH_VARIANT_FOUND: return true; 586 case OT::GLYPH_VARIANT_USE_DEFAULT: break; 587 } 588 589 return get_nominal_glyph (unicode, glyph); 590 } 591 592 protected: 593 typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, 594 hb_codepoint_t codepoint, 595 hb_codepoint_t *glyph); 596 597 template <typename Type> get_glyph_fromOT::cmap::accelerator_t598 static inline bool get_glyph_from (const void *obj, 599 hb_codepoint_t codepoint, 600 hb_codepoint_t *glyph) 601 { 602 const Type *typed_obj = (const Type *) obj; 603 return typed_obj->get_glyph (codepoint, glyph); 604 } 605 606 template <typename Type> get_glyph_from_symbolOT::cmap::accelerator_t607 static inline bool get_glyph_from_symbol (const void *obj, 608 hb_codepoint_t codepoint, 609 hb_codepoint_t *glyph) 610 { 611 const Type *typed_obj = (const Type *) obj; 612 if (likely (typed_obj->get_glyph (codepoint, glyph))) 613 return true; 614 615 if (codepoint <= 0x00FFu) 616 { 617 /* For symbol-encoded OpenType fonts, we duplicate the 618 * U+F000..F0FF range at U+0000..U+00FF. That's what 619 * Windows seems to do, and that's hinted about at: 620 * http://www.microsoft.com/typography/otspec/recom.htm 621 * under "Non-Standard (Symbol) Fonts". */ 622 return typed_obj->get_glyph (0xF000u + codepoint, glyph); 623 } 624 625 return false; 626 } 627 628 private: 629 hb_cmap_get_glyph_func_t get_glyph_func; 630 const void *get_glyph_data; 631 OT::CmapSubtableFormat4::accelerator_t format4_accel; 632 633 const OT::CmapSubtableFormat14 *uvs_table; 634 hb_blob_t *blob; 635 }; 636 637 protected: 638 find_subtableOT::cmap639 inline const CmapSubtable *find_subtable (unsigned int platform_id, 640 unsigned int encoding_id) const 641 { 642 EncodingRecord key; 643 key.platformID.set (platform_id); 644 key.encodingID.set (encoding_id); 645 646 /* Note: We can use bsearch, but since it has no performance 647 * implications, we use lsearch and as such accept fonts with 648 * unsorted subtable list. */ 649 int result = encodingRecord./*bsearch*/lsearch (key); 650 if (result == -1 || !encodingRecord[result].subtable) 651 return nullptr; 652 653 return &(this+encodingRecord[result].subtable); 654 } 655 656 protected: 657 UINT16 version; /* Table version number (0). */ 658 SortedArrayOf<EncodingRecord> 659 encodingRecord; /* Encoding tables. */ 660 public: 661 DEFINE_SIZE_ARRAY (4, encodingRecord); 662 }; 663 664 665 } /* namespace OT */ 666 667 668 #endif /* HB_OT_CMAP_TABLE_HH */ 669