1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "gpos.h" 6 7#include <limits> 8#include <vector> 9 10#include "gdef.h" 11#include "gsub.h" 12#include "layout.h" 13#include "maxp.h" 14 15// GPOS - The Glyph Positioning Table 16// http://www.microsoft.com/typography/otspec/gpos.htm 17 18namespace { 19 20enum GPOS_TYPE { 21 GPOS_TYPE_SINGLE_ADJUSTMENT = 1, 22 GPOS_TYPE_PAIR_ADJUSTMENT = 2, 23 GPOS_TYPE_CURSIVE_ATTACHMENT = 3, 24 GPOS_TYPE_MARK_TO_BASE_ATTACHMENT = 4, 25 GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5, 26 GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6, 27 GPOS_TYPE_CONTEXT_POSITIONING = 7, 28 GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8, 29 GPOS_TYPE_EXTENSION_POSITIONING = 9, 30 GPOS_TYPE_RESERVED = 10 31}; 32 33// The size of gpos header. 34const unsigned kGposHeaderSize = 10; 35// The maximum format number for anchor tables. 36const uint16_t kMaxAnchorFormat = 3; 37// The maximum number of class value. 38const uint16_t kMaxClassDefValue = 0xFFFF; 39 40// Lookup type parsers. 41bool ParseSingleAdjustment(const ots::OpenTypeFile *file, 42 const uint8_t *data, const size_t length); 43bool ParsePairAdjustment(const ots::OpenTypeFile *file, 44 const uint8_t *data, const size_t length); 45bool ParseCursiveAttachment(const ots::OpenTypeFile *file, 46 const uint8_t *data, const size_t length); 47bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file, 48 const uint8_t *data, const size_t length); 49bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file, 50 const uint8_t *data, const size_t length); 51bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file, 52 const uint8_t *data, const size_t length); 53bool ParseContextPositioning(const ots::OpenTypeFile *file, 54 const uint8_t *data, const size_t length); 55bool ParseChainedContextPositioning(const ots::OpenTypeFile *file, 56 const uint8_t *data, const size_t length); 57bool ParseExtensionPositioning(const ots::OpenTypeFile *file, 58 const uint8_t *data, const size_t length); 59 60const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = { 61 {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment}, 62 {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment}, 63 {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment}, 64 {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment}, 65 {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment}, 66 {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment}, 67 {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning}, 68 {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning}, 69 {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning} 70}; 71 72const ots::LookupSubtableParser kGposLookupSubtableParser = { 73 arraysize(kGposTypeParsers), 74 GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers 75}; 76 77// Shared Tables: ValueRecord, Anchor Table, and MarkArray 78 79bool ParseValueRecord(ots::Buffer* subtable, const uint8_t *data, 80 const size_t length, const uint16_t value_format) { 81 // Check existence of adjustment fields. 82 for (unsigned i = 0; i < 4; ++i) { 83 if ((value_format >> i) & 0x1) { 84 // Just read the field since these fileds could take an arbitrary values. 85 if (!subtable->Skip(2)) { 86 return OTS_FAILURE(); 87 } 88 } 89 } 90 91 // Check existence of offsets to device table. 92 for (unsigned i = 0; i < 4; ++i) { 93 if ((value_format >> (i + 4)) & 0x1) { 94 uint16_t offset = 0; 95 if (!subtable->ReadU16(&offset)) { 96 return OTS_FAILURE(); 97 } 98 if (offset) { 99 // TODO(bashi): Is it possible that device tables locate before 100 // this record? No fonts contain such offset AKAIF. 101 if (offset >= length) { 102 return OTS_FAILURE(); 103 } 104 if (!ots::ParseDeviceTable(data + offset, length - offset)) { 105 return OTS_FAILURE(); 106 } 107 } 108 } 109 } 110 return true; 111} 112 113bool ParseAnchorTable(const uint8_t *data, const size_t length) { 114 ots::Buffer subtable(data, length); 115 116 uint16_t format = 0; 117 // Read format and skip 2 2-byte fields that could be arbitrary values. 118 if (!subtable.ReadU16(&format) || 119 !subtable.Skip(4)) { 120 return OTS_FAILURE(); 121 } 122 123 if (format == 0 || format > kMaxAnchorFormat) { 124 return OTS_FAILURE(); 125 } 126 127 // Format 2 and 3 has additional fields. 128 if (format == 2) { 129 // Format 2 provides an index to a glyph contour point, which will take 130 // arbitrary value. 131 uint16_t anchor_point = 0; 132 if (!subtable.ReadU16(&anchor_point)) { 133 return OTS_FAILURE(); 134 } 135 } else if (format == 3) { 136 uint16_t offset_x_device = 0; 137 uint16_t offset_y_device = 0; 138 if (!subtable.ReadU16(&offset_x_device) || 139 !subtable.ReadU16(&offset_y_device)) { 140 return OTS_FAILURE(); 141 } 142 const unsigned format_end = static_cast<unsigned>(10); 143 if (offset_x_device) { 144 if (offset_x_device < format_end || offset_x_device >= length) { 145 return OTS_FAILURE(); 146 } 147 if (!ots::ParseDeviceTable(data + offset_x_device, 148 length - offset_x_device)) { 149 return OTS_FAILURE(); 150 } 151 } 152 if (offset_y_device) { 153 if (offset_y_device < format_end || offset_y_device >= length) { 154 return OTS_FAILURE(); 155 } 156 if (!ots::ParseDeviceTable(data + offset_y_device, 157 length - offset_y_device)) { 158 return OTS_FAILURE(); 159 } 160 } 161 } 162 return true; 163} 164 165bool ParseMarkArrayTable(const uint8_t *data, const size_t length, 166 const uint16_t class_count) { 167 ots::Buffer subtable(data, length); 168 169 uint16_t mark_count = 0; 170 if (!subtable.ReadU16(&mark_count)) { 171 return OTS_FAILURE(); 172 } 173 174 // MarkRecord consists of 4-bytes. 175 const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2; 176 if (mark_records_end > std::numeric_limits<uint16_t>::max()) { 177 return OTS_FAILURE(); 178 } 179 for (unsigned i = 0; i < mark_count; ++i) { 180 uint16_t class_value = 0; 181 uint16_t offset_mark_anchor = 0; 182 if (!subtable.ReadU16(&class_value) || 183 !subtable.ReadU16(&offset_mark_anchor)) { 184 return OTS_FAILURE(); 185 } 186 // |class_value| may take arbitrary values including 0 here so we don't 187 // check the value. 188 if (offset_mark_anchor < mark_records_end || 189 offset_mark_anchor >= length) { 190 return OTS_FAILURE(); 191 } 192 if (!ParseAnchorTable(data + offset_mark_anchor, 193 length - offset_mark_anchor)) { 194 return OTS_FAILURE(); 195 } 196 } 197 198 return true; 199} 200 201// Lookup Type 1: 202// Single Adjustment Positioning Subtable 203bool ParseSingleAdjustment(const ots::OpenTypeFile *file, const uint8_t *data, 204 const size_t length) { 205 ots::Buffer subtable(data, length); 206 207 uint16_t format = 0; 208 uint16_t offset_coverage = 0; 209 uint16_t value_format = 0; 210 if (!subtable.ReadU16(&format) || 211 !subtable.ReadU16(&offset_coverage) || 212 !subtable.ReadU16(&value_format)) { 213 return OTS_FAILURE(); 214 } 215 216 if (format == 1) { 217 // Format 1 exactly one value record. 218 if (!ParseValueRecord(&subtable, data, length, value_format)) { 219 return OTS_FAILURE(); 220 } 221 } else if (format == 2) { 222 uint16_t value_count = 0; 223 if (!subtable.ReadU16(&value_count)) { 224 return OTS_FAILURE(); 225 } 226 for (unsigned i = 0; i < value_count; ++i) { 227 if (!ParseValueRecord(&subtable, data, length, value_format)) { 228 return OTS_FAILURE(); 229 } 230 } 231 } else { 232 return OTS_FAILURE(); 233 } 234 235 if (offset_coverage < subtable.offset() || offset_coverage >= length) { 236 return OTS_FAILURE(); 237 } 238 239 if (!ots::ParseCoverageTable(data + offset_coverage, 240 length - offset_coverage, 241 file->maxp->num_glyphs)) { 242 return OTS_FAILURE(); 243 } 244 245 return true; 246} 247 248bool ParsePairSetTable(const uint8_t *data, const size_t length, 249 const uint16_t value_format1, 250 const uint16_t value_format2, 251 const uint16_t num_glyphs) { 252 ots::Buffer subtable(data, length); 253 254 uint16_t value_count = 0; 255 if (!subtable.ReadU16(&value_count)) { 256 return OTS_FAILURE(); 257 } 258 for (unsigned i = 0; i < value_count; ++i) { 259 // Check pair value record. 260 uint16_t glyph_id = 0; 261 if (!subtable.ReadU16(&glyph_id)) { 262 return OTS_FAILURE(); 263 } 264 if (glyph_id >= num_glyphs) { 265 return OTS_FAILURE(); 266 } 267 if (!ParseValueRecord(&subtable, data, length, value_format1)) { 268 return OTS_FAILURE(); 269 } 270 if (!ParseValueRecord(&subtable, data, length, value_format2)) { 271 return OTS_FAILURE(); 272 } 273 } 274 return true; 275} 276 277bool ParsePairPosFormat1(const uint8_t *data, const size_t length, 278 const uint16_t value_format1, 279 const uint16_t value_format2, 280 const uint16_t num_glyphs) { 281 ots::Buffer subtable(data, length); 282 283 // Skip 8 bytes that are already read before. 284 if (!subtable.Skip(8)) { 285 return OTS_FAILURE(); 286 } 287 288 uint16_t pair_set_count = 0; 289 if (!subtable.ReadU16(&pair_set_count)) { 290 return OTS_FAILURE(); 291 } 292 293 const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10; 294 if (pair_pos_end > std::numeric_limits<uint16_t>::max()) { 295 return OTS_FAILURE(); 296 } 297 for (unsigned i = 0; i < pair_set_count; ++i) { 298 uint16_t pair_set_offset = 0; 299 if (!subtable.ReadU16(&pair_set_offset)) { 300 return OTS_FAILURE(); 301 } 302 if (pair_set_offset < pair_pos_end || pair_set_offset >= length) { 303 return OTS_FAILURE(); 304 } 305 // Check pair set tables 306 if (!ParsePairSetTable(data + pair_set_offset, length - pair_set_offset, 307 value_format1, value_format2, 308 num_glyphs)) { 309 return OTS_FAILURE(); 310 } 311 } 312 313 return true; 314} 315 316bool ParsePairPosFormat2(const uint8_t *data, const size_t length, 317 const uint16_t value_format1, 318 const uint16_t value_format2, 319 const uint16_t num_glyphs) { 320 ots::Buffer subtable(data, length); 321 322 // Skip 8 bytes that are already read before. 323 if (!subtable.Skip(8)) { 324 return OTS_FAILURE(); 325 } 326 327 uint16_t offset_class_def1 = 0; 328 uint16_t offset_class_def2 = 0; 329 uint16_t class1_count = 0; 330 uint16_t class2_count = 0; 331 if (!subtable.ReadU16(&offset_class_def1) || 332 !subtable.ReadU16(&offset_class_def2) || 333 !subtable.ReadU16(&class1_count) || 334 !subtable.ReadU16(&class2_count)) { 335 return OTS_FAILURE(); 336 } 337 338 // Check class 1 records. 339 for (unsigned i = 0; i < class1_count; ++i) { 340 // Check class 2 records. 341 for (unsigned j = 0; j < class2_count; ++j) { 342 if (value_format1 && !ParseValueRecord(&subtable, data, length, 343 value_format1)) { 344 return OTS_FAILURE(); 345 } 346 if (value_format2 && !ParseValueRecord(&subtable, data, length, 347 value_format2)) { 348 return OTS_FAILURE(); 349 } 350 } 351 } 352 353 // Check class definition tables. 354 if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length || 355 offset_class_def2 < subtable.offset() || offset_class_def2 >= length) { 356 return OTS_FAILURE(); 357 } 358 if (!ots::ParseClassDefTable(data + offset_class_def1, 359 length - offset_class_def1, 360 num_glyphs, kMaxClassDefValue)) { 361 return OTS_FAILURE(); 362 } 363 if (!ots::ParseClassDefTable(data + offset_class_def2, 364 length - offset_class_def2, 365 num_glyphs, kMaxClassDefValue)) { 366 return OTS_FAILURE(); 367 } 368 369 return true; 370} 371 372// Lookup Type 2: 373// Pair Adjustment Positioning Subtable 374bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data, 375 const size_t length) { 376 ots::Buffer subtable(data, length); 377 378 uint16_t format = 0; 379 uint16_t offset_coverage = 0; 380 uint16_t value_format1 = 0; 381 uint16_t value_format2 = 0; 382 if (!subtable.ReadU16(&format) || 383 !subtable.ReadU16(&offset_coverage) || 384 !subtable.ReadU16(&value_format1) || 385 !subtable.ReadU16(&value_format2)) { 386 return OTS_FAILURE(); 387 } 388 389 if (format == 1) { 390 if (!ParsePairPosFormat1(data, length, value_format1, value_format2, 391 file->maxp->num_glyphs)) { 392 return OTS_FAILURE(); 393 } 394 } else if (format == 2) { 395 if (!ParsePairPosFormat2(data, length, value_format1, value_format2, 396 file->maxp->num_glyphs)) { 397 return OTS_FAILURE(); 398 } 399 } else { 400 return OTS_FAILURE(); 401 } 402 403 if (offset_coverage < subtable.offset() || offset_coverage >= length) { 404 return OTS_FAILURE(); 405 } 406 if (!ots::ParseCoverageTable(data + offset_coverage, 407 length - offset_coverage, 408 file->maxp->num_glyphs)) { 409 return OTS_FAILURE(); 410 } 411 412 return true; 413} 414 415// Lookup Type 3 416// Cursive Attachment Positioning Subtable 417bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data, 418 const size_t length) { 419 ots::Buffer subtable(data, length); 420 421 uint16_t format = 0; 422 uint16_t offset_coverage = 0; 423 uint16_t entry_exit_count = 0; 424 if (!subtable.ReadU16(&format) || 425 !subtable.ReadU16(&offset_coverage) || 426 !subtable.ReadU16(&entry_exit_count)) { 427 return OTS_FAILURE(); 428 } 429 430 if (format != 1) { 431 return OTS_FAILURE(); 432 } 433 434 // Check entry exit records. 435 const unsigned entry_exit_records_end = 436 2 * static_cast<unsigned>(entry_exit_count) + 6; 437 if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) { 438 return OTS_FAILURE(); 439 } 440 for (unsigned i = 0; i < entry_exit_count; ++i) { 441 uint16_t offset_entry_anchor = 0; 442 uint16_t offset_exit_anchor = 0; 443 if (!subtable.ReadU16(&offset_entry_anchor) || 444 !subtable.ReadU16(&offset_exit_anchor)) { 445 return OTS_FAILURE(); 446 } 447 // These offsets could be NULL. 448 if (offset_entry_anchor) { 449 if (offset_entry_anchor < entry_exit_records_end || 450 offset_entry_anchor >= length) { 451 return OTS_FAILURE(); 452 } 453 if (!ParseAnchorTable(data + offset_entry_anchor, 454 length - offset_entry_anchor)) { 455 return OTS_FAILURE(); 456 } 457 } 458 if (offset_exit_anchor) { 459 if (offset_exit_anchor < entry_exit_records_end || 460 offset_exit_anchor >= length) { 461 return OTS_FAILURE(); 462 } 463 if (!ParseAnchorTable(data + offset_exit_anchor, 464 length - offset_exit_anchor)) { 465 return OTS_FAILURE(); 466 } 467 } 468 } 469 470 if (offset_coverage < subtable.offset() || offset_coverage >= length) { 471 return OTS_FAILURE(); 472 } 473 if (!ots::ParseCoverageTable(data + offset_coverage, 474 length - offset_coverage, 475 file->maxp->num_glyphs)) { 476 return OTS_FAILURE(); 477 } 478 479 return true; 480} 481 482bool ParseAnchorArrayTable(const uint8_t *data, const size_t length, 483 const uint16_t class_count) { 484 ots::Buffer subtable(data, length); 485 486 uint16_t record_count = 0; 487 if (!subtable.ReadU16(&record_count)) { 488 return OTS_FAILURE(); 489 } 490 491 const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) * 492 static_cast<unsigned>(class_count) + 2; 493 if (anchor_array_end > std::numeric_limits<uint16_t>::max()) { 494 return OTS_FAILURE(); 495 } 496 for (unsigned i = 0; i < record_count; ++i) { 497 for (unsigned j = 0; j < class_count; ++j) { 498 uint16_t offset_record = 0; 499 if (!subtable.ReadU16(&offset_record)) { 500 return OTS_FAILURE(); 501 } 502 // |offset_record| could be NULL. 503 if (offset_record) { 504 if (offset_record < anchor_array_end || offset_record >= length) { 505 return OTS_FAILURE(); 506 } 507 if (!ParseAnchorTable(data + offset_record, 508 length - offset_record)) { 509 return OTS_FAILURE(); 510 } 511 } 512 } 513 } 514 return true; 515} 516 517bool ParseLigatureArrayTable(const uint8_t *data, const size_t length, 518 const uint16_t class_count) { 519 ots::Buffer subtable(data, length); 520 521 uint16_t ligature_count = 0; 522 if (!subtable.ReadU16(&ligature_count)) { 523 return OTS_FAILURE(); 524 } 525 for (unsigned i = 0; i < ligature_count; ++i) { 526 uint16_t offset_ligature_attach = 0; 527 if (!subtable.ReadU16(&offset_ligature_attach)) { 528 return OTS_FAILURE(); 529 } 530 if (offset_ligature_attach < 2 || offset_ligature_attach >= length) { 531 return OTS_FAILURE(); 532 } 533 if (!ParseAnchorArrayTable(data + offset_ligature_attach, 534 length - offset_ligature_attach, class_count)) { 535 return OTS_FAILURE(); 536 } 537 } 538 return true; 539} 540 541// Common parser for Lookup Type 4, 5 and 6. 542bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file, 543 const uint8_t *data, const size_t length, 544 const GPOS_TYPE type) { 545 ots::Buffer subtable(data, length); 546 547 uint16_t format = 0; 548 uint16_t offset_coverage1 = 0; 549 uint16_t offset_coverage2 = 0; 550 uint16_t class_count = 0; 551 uint16_t offset_mark_array = 0; 552 uint16_t offset_type_specific_array = 0; 553 if (!subtable.ReadU16(&format) || 554 !subtable.ReadU16(&offset_coverage1) || 555 !subtable.ReadU16(&offset_coverage2) || 556 !subtable.ReadU16(&class_count) || 557 !subtable.ReadU16(&offset_mark_array) || 558 !subtable.ReadU16(&offset_type_specific_array)) { 559 return OTS_FAILURE(); 560 } 561 562 if (format != 1) { 563 return OTS_FAILURE(); 564 } 565 566 const unsigned header_end = static_cast<unsigned>(subtable.offset()); 567 if (header_end > std::numeric_limits<uint16_t>::max()) { 568 return OTS_FAILURE(); 569 } 570 if (offset_coverage1 < header_end || offset_coverage1 >= length) { 571 return OTS_FAILURE(); 572 } 573 if (!ots::ParseCoverageTable(data + offset_coverage1, 574 length - offset_coverage1, 575 file->maxp->num_glyphs)) { 576 return OTS_FAILURE(); 577 } 578 if (offset_coverage2 < header_end || offset_coverage2 >= length) { 579 return OTS_FAILURE(); 580 } 581 if (!ots::ParseCoverageTable(data + offset_coverage2, 582 length - offset_coverage2, 583 file->maxp->num_glyphs)) { 584 return OTS_FAILURE(); 585 } 586 587 if (offset_mark_array < header_end || offset_mark_array >= length) { 588 return OTS_FAILURE(); 589 } 590 if (!ParseMarkArrayTable(data + offset_mark_array, 591 length - offset_mark_array, class_count)) { 592 return OTS_FAILURE(); 593 } 594 595 if (offset_type_specific_array < header_end || 596 offset_type_specific_array >= length) { 597 return OTS_FAILURE(); 598 } 599 if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT || 600 type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) { 601 if (!ParseAnchorArrayTable(data + offset_type_specific_array, 602 length - offset_type_specific_array, 603 class_count)) { 604 return OTS_FAILURE(); 605 } 606 } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) { 607 if (!ParseLigatureArrayTable(data + offset_type_specific_array, 608 length - offset_type_specific_array, 609 class_count)) { 610 return OTS_FAILURE(); 611 } 612 } else { 613 return OTS_FAILURE(); 614 } 615 616 return true; 617} 618 619// Lookup Type 4: 620// MarkToBase Attachment Positioning Subtable 621bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file, 622 const uint8_t *data, const size_t length) { 623 return ParseMarkToAttachmentSubtables(file, data, length, 624 GPOS_TYPE_MARK_TO_BASE_ATTACHMENT); 625} 626 627// Lookup Type 5: 628// MarkToLigature Attachment Positioning Subtable 629bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file, 630 const uint8_t *data, const size_t length) { 631 return ParseMarkToAttachmentSubtables(file, data, length, 632 GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT); 633} 634 635// Lookup Type 6: 636// MarkToMark Attachment Positioning Subtable 637bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file, 638 const uint8_t *data, const size_t length) { 639 return ParseMarkToAttachmentSubtables(file, data, length, 640 GPOS_TYPE_MARK_TO_MARK_ATTACHMENT); 641} 642 643// Lookup Type 7: 644// Contextual Positioning Subtables 645bool ParseContextPositioning(const ots::OpenTypeFile *file, 646 const uint8_t *data, const size_t length) { 647 return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs, 648 file->gpos->num_lookups); 649} 650 651// Lookup Type 8: 652// Chaining Contexual Positioning Subtable 653bool ParseChainedContextPositioning(const ots::OpenTypeFile *file, 654 const uint8_t *data, const size_t length) { 655 return ots::ParseChainingContextSubtable(data, length, 656 file->maxp->num_glyphs, 657 file->gpos->num_lookups); 658} 659 660// Lookup Type 9: 661// Extension Positioning 662bool ParseExtensionPositioning(const ots::OpenTypeFile *file, 663 const uint8_t *data, const size_t length) { 664 return ots::ParseExtensionSubtable(file, data, length, 665 &kGposLookupSubtableParser); 666} 667 668} // namespace 669 670#define DROP_THIS_TABLE \ 671 do { file->gpos->data = 0; file->gpos->length = 0; } while (0) 672 673namespace ots { 674 675// As far as I checked, following fonts contain invalid GPOS table and 676// OTS will drop their GPOS table. 677// 678// # invalid delta format in device table 679// samanata.ttf 680// 681// # bad size range in device table 682// Sarai_07.ttf 683// 684// # bad offset to PairSetTable 685// chandas1-2.ttf 686// 687// # bad offset to FeatureTable 688// glrso12.ttf 689// gllr12.ttf 690// glbo12.ttf 691// glb12.ttf 692// glro12.ttf 693// glbso12.ttf 694// glrc12.ttf 695// glrsc12.ttf 696// glbs12.ttf 697// glrs12.ttf 698// glr12.ttf 699// 700// # ScriptRecords aren't sorted by tag 701// Garogier_unhinted.otf 702// 703// # bad start coverage index in CoverageFormat2 704// AndBasR.ttf 705// CharisSILB.ttf 706// CharisSILBI.ttf 707// CharisSILI.ttf 708// CharisSILR.ttf 709// DoulosSILR.ttf 710// GenBasBI.ttf 711// GenBasI.ttf 712// GenBkBasI.ttf 713// GenBkBasB.ttf 714// GenBkBasR.ttf 715// Padauk-Bold.ttf 716// Padauk.ttf 717// 718// # Contour point indexes aren't sorted 719// Arial Unicode.ttf 720 721bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { 722 // Parsing GPOS table requires num_glyphs which is contained in maxp table. 723 if (!file->maxp) { 724 return OTS_FAILURE(); 725 } 726 727 Buffer table(data, length); 728 729 OpenTypeGPOS *gpos = new OpenTypeGPOS; 730 file->gpos = gpos; 731 732 uint32_t version = 0; 733 uint16_t offset_script_list = 0; 734 uint16_t offset_feature_list = 0; 735 uint16_t offset_lookup_list = 0; 736 if (!table.ReadU32(&version) || 737 !table.ReadU16(&offset_script_list) || 738 !table.ReadU16(&offset_feature_list) || 739 !table.ReadU16(&offset_lookup_list)) { 740 return OTS_FAILURE(); 741 } 742 743 if (version != 0x00010000) { 744 OTS_WARNING("bad GPOS version"); 745 DROP_THIS_TABLE; 746 return true; 747 } 748 if ((offset_script_list < kGposHeaderSize || 749 offset_script_list >= length) || 750 (offset_feature_list < kGposHeaderSize || 751 offset_feature_list >= length) || 752 (offset_lookup_list < kGposHeaderSize || 753 offset_lookup_list >= length)) { 754 OTS_WARNING("bad offset in GPOS header"); 755 DROP_THIS_TABLE; 756 return true; 757 } 758 759 if (!ParseLookupListTable(file, data + offset_lookup_list, 760 length - offset_lookup_list, 761 &kGposLookupSubtableParser, 762 &gpos->num_lookups)) { 763 OTS_WARNING("faild to parse lookup list table"); 764 DROP_THIS_TABLE; 765 return true; 766 } 767 768 uint16_t num_features = 0; 769 if (!ParseFeatureListTable(data + offset_feature_list, 770 length - offset_feature_list, gpos->num_lookups, 771 &num_features)) { 772 OTS_WARNING("faild to parse feature list table"); 773 DROP_THIS_TABLE; 774 return true; 775 } 776 777 if (!ParseScriptListTable(data + offset_script_list, 778 length - offset_script_list, num_features)) { 779 OTS_WARNING("faild to parse script list table"); 780 DROP_THIS_TABLE; 781 return true; 782 } 783 784 gpos->data = data; 785 gpos->length = length; 786 return true; 787} 788 789bool ots_gpos_should_serialise(OpenTypeFile *file) { 790 const bool needed_tables_dropped = 791 (file->gdef && file->gdef->data == NULL) || 792 (file->gsub && file->gsub->data == NULL); 793 return file->gpos != NULL && file->gpos->data != NULL && 794 !needed_tables_dropped; 795} 796 797bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) { 798 if (!out->Write(file->gpos->data, file->gpos->length)) { 799 return OTS_FAILURE(); 800 } 801 802 return true; 803} 804 805void ots_gpos_free(OpenTypeFile *file) { 806 delete file->gpos; 807} 808 809} // namespace ots 810 811