1 /* 2 * Opentype font interfaces for the Uniscribe Script Processor (usp10.dll) 3 * 4 * Copyright 2012 CodeWeavers, Aric Stewart 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 * 20 */ 21 #include <stdarg.h> 22 #include <stdlib.h> 23 24 #include "windef.h" 25 #include "winbase.h" 26 #include "wingdi.h" 27 #include "winuser.h" 28 #include "winnls.h" 29 #include "usp10.h" 30 #include "winternl.h" 31 32 #include "usp10_internal.h" 33 34 #include "wine/debug.h" 35 #include "wine/heap.h" 36 37 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe); 38 39 #ifdef WORDS_BIGENDIAN 40 #define GET_BE_WORD(x) (x) 41 #define GET_BE_DWORD(x) (x) 42 #else 43 #define GET_BE_WORD(x) RtlUshortByteSwap(x) 44 #define GET_BE_DWORD(x) RtlUlongByteSwap(x) 45 #endif 46 47 #define round(x) (((x) < 0) ? (int)((x) - 0.5) : (int)((x) + 0.5)) 48 49 /* These are all structures needed for the cmap format 12 table */ 50 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p') 51 52 enum gpos_lookup_type 53 { 54 GPOS_LOOKUP_ADJUST_SINGLE = 0x1, 55 GPOS_LOOKUP_ADJUST_PAIR = 0x2, 56 GPOS_LOOKUP_ATTACH_CURSIVE = 0x3, 57 GPOS_LOOKUP_ATTACH_MARK_TO_BASE = 0x4, 58 GPOS_LOOKUP_ATTACH_MARK_TO_LIGATURE = 0x5, 59 GPOS_LOOKUP_ATTACH_MARK_TO_MARK = 0x6, 60 GPOS_LOOKUP_POSITION_CONTEXT = 0x7, 61 GPOS_LOOKUP_POSITION_CONTEXT_CHAINED = 0x8, 62 GPOS_LOOKUP_POSITION_EXTENSION = 0x9, 63 }; 64 65 enum gsub_lookup_type 66 { 67 GSUB_LOOKUP_SINGLE = 0x1, 68 GSUB_LOOKUP_MULTIPLE = 0x2, 69 GSUB_LOOKUP_ALTERNATE = 0x3, 70 GSUB_LOOKUP_LIGATURE = 0x4, 71 GSUB_LOOKUP_CONTEXT = 0x5, 72 GSUB_LOOKUP_CONTEXT_CHAINED = 0x6, 73 GSUB_LOOKUP_EXTENSION = 0x7, 74 GSUB_LOOKUP_CONTEXT_CHAINED_REVERSE = 0x8, 75 }; 76 77 typedef struct { 78 WORD platformID; 79 WORD encodingID; 80 DWORD offset; 81 } CMAP_EncodingRecord; 82 83 typedef struct { 84 WORD version; 85 WORD numTables; 86 CMAP_EncodingRecord tables[1]; 87 } CMAP_Header; 88 89 typedef struct { 90 DWORD startCharCode; 91 DWORD endCharCode; 92 DWORD startGlyphID; 93 } CMAP_SegmentedCoverage_group; 94 95 typedef struct { 96 WORD format; 97 WORD reserved; 98 DWORD length; 99 DWORD language; 100 DWORD nGroups; 101 CMAP_SegmentedCoverage_group groups[1]; 102 } CMAP_SegmentedCoverage; 103 104 /* These are all structures needed for the GDEF table */ 105 enum {BaseGlyph=1, LigatureGlyph, MarkGlyph, ComponentGlyph}; 106 107 typedef struct { 108 DWORD Version; 109 WORD GlyphClassDef; 110 WORD AttachList; 111 WORD LigCaretList; 112 WORD MarkAttachClassDef; 113 } GDEF_Header; 114 115 typedef struct { 116 WORD ClassFormat; 117 WORD StartGlyph; 118 WORD GlyphCount; 119 WORD ClassValueArray[1]; 120 } OT_ClassDefFormat1; 121 122 typedef struct { 123 WORD Start; 124 WORD End; 125 WORD Class; 126 } OT_ClassRangeRecord; 127 128 typedef struct { 129 WORD ClassFormat; 130 WORD ClassRangeCount; 131 OT_ClassRangeRecord ClassRangeRecord[1]; 132 } OT_ClassDefFormat2; 133 134 /* These are all structures needed for the GSUB table */ 135 136 typedef struct { 137 DWORD version; 138 WORD ScriptList; 139 WORD FeatureList; 140 WORD LookupList; 141 } GSUB_Header; 142 143 typedef struct { 144 CHAR ScriptTag[4]; 145 WORD Script; 146 } OT_ScriptRecord; 147 148 typedef struct { 149 WORD ScriptCount; 150 OT_ScriptRecord ScriptRecord[1]; 151 } OT_ScriptList; 152 153 typedef struct { 154 CHAR LangSysTag[4]; 155 WORD LangSys; 156 } OT_LangSysRecord; 157 158 typedef struct { 159 WORD DefaultLangSys; 160 WORD LangSysCount; 161 OT_LangSysRecord LangSysRecord[1]; 162 } OT_Script; 163 164 typedef struct { 165 WORD LookupOrder; /* Reserved */ 166 WORD ReqFeatureIndex; 167 WORD FeatureCount; 168 WORD FeatureIndex[1]; 169 } OT_LangSys; 170 171 typedef struct { 172 CHAR FeatureTag[4]; 173 WORD Feature; 174 } OT_FeatureRecord; 175 176 typedef struct { 177 WORD FeatureCount; 178 OT_FeatureRecord FeatureRecord[1]; 179 } OT_FeatureList; 180 181 typedef struct { 182 WORD FeatureParams; /* Reserved */ 183 WORD LookupCount; 184 WORD LookupListIndex[1]; 185 } OT_Feature; 186 187 typedef struct { 188 WORD LookupCount; 189 WORD Lookup[1]; 190 } OT_LookupList; 191 192 typedef struct { 193 WORD LookupType; 194 WORD LookupFlag; 195 WORD SubTableCount; 196 WORD SubTable[1]; 197 } OT_LookupTable; 198 199 typedef struct { 200 WORD CoverageFormat; 201 WORD GlyphCount; 202 WORD GlyphArray[1]; 203 } OT_CoverageFormat1; 204 205 typedef struct { 206 WORD Start; 207 WORD End; 208 WORD StartCoverageIndex; 209 } OT_RangeRecord; 210 211 typedef struct { 212 WORD CoverageFormat; 213 WORD RangeCount; 214 OT_RangeRecord RangeRecord[1]; 215 } OT_CoverageFormat2; 216 217 typedef struct { 218 WORD SubstFormat; /* = 1 */ 219 WORD Coverage; 220 WORD DeltaGlyphID; 221 } GSUB_SingleSubstFormat1; 222 223 typedef struct { 224 WORD SubstFormat; /* = 2 */ 225 WORD Coverage; 226 WORD GlyphCount; 227 WORD Substitute[1]; 228 }GSUB_SingleSubstFormat2; 229 230 typedef struct { 231 WORD SubstFormat; /* = 1 */ 232 WORD Coverage; 233 WORD SequenceCount; 234 WORD Sequence[1]; 235 }GSUB_MultipleSubstFormat1; 236 237 typedef struct { 238 WORD GlyphCount; 239 WORD Substitute[1]; 240 }GSUB_Sequence; 241 242 typedef struct { 243 WORD SubstFormat; /* = 1 */ 244 WORD Coverage; 245 WORD LigSetCount; 246 WORD LigatureSet[1]; 247 }GSUB_LigatureSubstFormat1; 248 249 typedef struct { 250 WORD LigatureCount; 251 WORD Ligature[1]; 252 }GSUB_LigatureSet; 253 254 typedef struct{ 255 WORD LigGlyph; 256 WORD CompCount; 257 WORD Component[1]; 258 }GSUB_Ligature; 259 260 typedef struct{ 261 WORD SequenceIndex; 262 WORD LookupListIndex; 263 264 }GSUB_SubstLookupRecord; 265 266 typedef struct{ 267 WORD SubstFormat; 268 WORD Coverage; 269 WORD SubRuleSetCount; 270 WORD SubRuleSet[1]; 271 }GSUB_ContextSubstFormat1; 272 273 typedef struct{ 274 WORD SubRuleCount; 275 WORD SubRule[1]; 276 }GSUB_SubRuleSet; 277 278 typedef struct { 279 WORD GlyphCount; 280 WORD SubstCount; 281 WORD Input[1]; 282 }GSUB_SubRule_1; 283 284 typedef struct { 285 GSUB_SubstLookupRecord SubstLookupRecord[1]; 286 }GSUB_SubRule_2; 287 288 typedef struct { 289 WORD SubstFormat; 290 WORD Coverage; 291 WORD ClassDef; 292 WORD SubClassSetCnt; 293 WORD SubClassSet[1]; 294 }GSUB_ContextSubstFormat2; 295 296 typedef struct { 297 WORD SubClassRuleCnt; 298 WORD SubClassRule[1]; 299 }GSUB_SubClassSet; 300 301 typedef struct { 302 WORD GlyphCount; 303 WORD SubstCount; 304 WORD Class[1]; 305 }GSUB_SubClassRule_1; 306 307 typedef struct { 308 GSUB_SubstLookupRecord SubstLookupRecord[1]; 309 }GSUB_SubClassRule_2; 310 311 typedef struct{ 312 WORD SubstFormat; /* = 1 */ 313 WORD Coverage; 314 WORD ChainSubRuleSetCount; 315 WORD ChainSubRuleSet[1]; 316 }GSUB_ChainContextSubstFormat1; 317 318 typedef struct { 319 WORD SubstFormat; /* = 2 */ 320 WORD Coverage; 321 WORD BacktrackClassDef; 322 WORD InputClassDef; 323 WORD LookaheadClassDef; 324 WORD ChainSubClassSetCnt; 325 WORD ChainSubClassSet[1]; 326 }GSUB_ChainContextSubstFormat2; 327 328 typedef struct { 329 WORD ChainSubClassRuleCnt; 330 WORD ChainSubClassRule[1]; 331 }GSUB_ChainSubClassSet; 332 333 typedef struct { 334 WORD BacktrackGlyphCount; 335 WORD Backtrack[1]; 336 }GSUB_ChainSubClassRule_1; 337 338 typedef struct { 339 WORD InputGlyphCount; 340 WORD Input[1]; 341 }GSUB_ChainSubClassRule_2; 342 343 typedef struct { 344 WORD LookaheadGlyphCount; 345 WORD LookAhead[1]; 346 }GSUB_ChainSubClassRule_3; 347 348 typedef struct { 349 WORD SubstCount; 350 GSUB_SubstLookupRecord SubstLookupRecord[1]; 351 }GSUB_ChainSubClassRule_4; 352 353 typedef struct { 354 WORD SubstFormat; /* = 3 */ 355 WORD BacktrackGlyphCount; 356 WORD Coverage[1]; 357 }GSUB_ChainContextSubstFormat3_1; 358 359 typedef struct{ 360 WORD InputGlyphCount; 361 WORD Coverage[1]; 362 }GSUB_ChainContextSubstFormat3_2; 363 364 typedef struct{ 365 WORD LookaheadGlyphCount; 366 WORD Coverage[1]; 367 }GSUB_ChainContextSubstFormat3_3; 368 369 typedef struct{ 370 WORD SubstCount; 371 GSUB_SubstLookupRecord SubstLookupRecord[1]; 372 }GSUB_ChainContextSubstFormat3_4; 373 374 typedef struct { 375 WORD SubstFormat; /* = 1 */ 376 WORD Coverage; 377 WORD AlternateSetCount; 378 WORD AlternateSet[1]; 379 } GSUB_AlternateSubstFormat1; 380 381 typedef struct{ 382 WORD GlyphCount; 383 WORD Alternate[1]; 384 } GSUB_AlternateSet; 385 386 typedef struct { 387 WORD SubstFormat; 388 WORD ExtensionLookupType; 389 DWORD ExtensionOffset; 390 } GSUB_ExtensionPosFormat1; 391 392 /* These are all structures needed for the GPOS table */ 393 394 typedef struct { 395 DWORD version; 396 WORD ScriptList; 397 WORD FeatureList; 398 WORD LookupList; 399 } GPOS_Header; 400 401 typedef struct { 402 WORD StartSize; 403 WORD EndSize; 404 WORD DeltaFormat; 405 WORD DeltaValue[1]; 406 } OT_DeviceTable; 407 408 typedef struct { 409 WORD AnchorFormat; 410 WORD XCoordinate; 411 WORD YCoordinate; 412 } GPOS_AnchorFormat1; 413 414 typedef struct { 415 WORD AnchorFormat; 416 WORD XCoordinate; 417 WORD YCoordinate; 418 WORD AnchorPoint; 419 } GPOS_AnchorFormat2; 420 421 typedef struct { 422 WORD AnchorFormat; 423 WORD XCoordinate; 424 WORD YCoordinate; 425 WORD XDeviceTable; 426 WORD YDeviceTable; 427 } GPOS_AnchorFormat3; 428 429 typedef struct { 430 WORD XPlacement; 431 WORD YPlacement; 432 WORD XAdvance; 433 WORD YAdvance; 434 WORD XPlaDevice; 435 WORD YPlaDevice; 436 WORD XAdvDevice; 437 WORD YAdvDevice; 438 } GPOS_ValueRecord; 439 440 typedef struct { 441 WORD PosFormat; 442 WORD Coverage; 443 WORD ValueFormat; 444 WORD Value[1]; 445 } GPOS_SinglePosFormat1; 446 447 typedef struct { 448 WORD PosFormat; 449 WORD Coverage; 450 WORD ValueFormat; 451 WORD ValueCount; 452 WORD Value[1]; 453 } GPOS_SinglePosFormat2; 454 455 typedef struct { 456 WORD PosFormat; 457 WORD Coverage; 458 WORD ValueFormat1; 459 WORD ValueFormat2; 460 WORD PairSetCount; 461 WORD PairSetOffset[1]; 462 } GPOS_PairPosFormat1; 463 464 typedef struct { 465 WORD PosFormat; 466 WORD Coverage; 467 WORD ValueFormat1; 468 WORD ValueFormat2; 469 WORD ClassDef1; 470 WORD ClassDef2; 471 WORD Class1Count; 472 WORD Class2Count; 473 WORD Class1Record[1]; 474 } GPOS_PairPosFormat2; 475 476 typedef struct { 477 WORD SecondGlyph; 478 WORD Value1[1]; 479 WORD Value2[1]; 480 } GPOS_PairValueRecord; 481 482 typedef struct { 483 WORD PairValueCount; 484 GPOS_PairValueRecord PairValueRecord[1]; 485 } GPOS_PairSet; 486 487 typedef struct { 488 WORD EntryAnchor; 489 WORD ExitAnchor; 490 } GPOS_EntryExitRecord; 491 492 typedef struct { 493 WORD PosFormat; 494 WORD Coverage; 495 WORD EntryExitCount; 496 GPOS_EntryExitRecord EntryExitRecord[1]; 497 } GPOS_CursivePosFormat1; 498 499 typedef struct { 500 WORD PosFormat; 501 WORD MarkCoverage; 502 WORD BaseCoverage; 503 WORD ClassCount; 504 WORD MarkArray; 505 WORD BaseArray; 506 } GPOS_MarkBasePosFormat1; 507 508 typedef struct { 509 WORD BaseAnchor[1]; 510 } GPOS_BaseRecord; 511 512 typedef struct { 513 WORD BaseCount; 514 GPOS_BaseRecord BaseRecord[1]; 515 } GPOS_BaseArray; 516 517 typedef struct { 518 WORD Class; 519 WORD MarkAnchor; 520 } GPOS_MarkRecord; 521 522 typedef struct { 523 WORD MarkCount; 524 GPOS_MarkRecord MarkRecord[1]; 525 } GPOS_MarkArray; 526 527 typedef struct { 528 WORD PosFormat; 529 WORD MarkCoverage; 530 WORD LigatureCoverage; 531 WORD ClassCount; 532 WORD MarkArray; 533 WORD LigatureArray; 534 } GPOS_MarkLigPosFormat1; 535 536 typedef struct { 537 WORD LigatureCount; 538 WORD LigatureAttach[1]; 539 } GPOS_LigatureArray; 540 541 typedef struct { 542 WORD LigatureAnchor[1]; 543 } GPOS_ComponentRecord; 544 545 typedef struct { 546 WORD ComponentCount; 547 GPOS_ComponentRecord ComponentRecord[1]; 548 } GPOS_LigatureAttach; 549 550 typedef struct { 551 WORD PosFormat; 552 WORD Mark1Coverage; 553 WORD Mark2Coverage; 554 WORD ClassCount; 555 WORD Mark1Array; 556 WORD Mark2Array; 557 } GPOS_MarkMarkPosFormat1; 558 559 typedef struct { 560 WORD Mark2Anchor[1]; 561 } GPOS_Mark2Record; 562 563 typedef struct { 564 WORD Mark2Count; 565 GPOS_Mark2Record Mark2Record[1]; 566 } GPOS_Mark2Array; 567 568 typedef struct { 569 WORD SequenceIndex; 570 WORD LookupListIndex; 571 } GPOS_PosLookupRecord; 572 573 typedef struct { 574 WORD PosFormat; 575 WORD Coverage; 576 WORD ClassDef; 577 WORD PosClassSetCnt; 578 WORD PosClassSet[1]; 579 } GPOS_ContextPosFormat2; 580 581 typedef struct { 582 WORD PosClassRuleCnt; 583 WORD PosClassRule[1]; 584 } GPOS_PosClassSet; 585 586 typedef struct { 587 WORD GlyphCount; 588 WORD PosCount; 589 WORD Class[1]; 590 } GPOS_PosClassRule_1; 591 592 typedef struct { 593 GPOS_PosLookupRecord PosLookupRecord[1]; 594 } GPOS_PosClassRule_2; 595 596 typedef struct { 597 WORD PosFormat; 598 WORD BacktrackGlyphCount; 599 WORD Coverage[1]; 600 } GPOS_ChainContextPosFormat3_1; 601 602 typedef struct { 603 WORD InputGlyphCount; 604 WORD Coverage[1]; 605 } GPOS_ChainContextPosFormat3_2; 606 607 typedef struct { 608 WORD LookaheadGlyphCount; 609 WORD Coverage[1]; 610 } GPOS_ChainContextPosFormat3_3; 611 612 typedef struct { 613 WORD PosCount; 614 GPOS_PosLookupRecord PosLookupRecord[1]; 615 } GPOS_ChainContextPosFormat3_4; 616 617 typedef struct { 618 WORD PosFormat; 619 WORD ExtensionLookupType; 620 DWORD ExtensionOffset; 621 } GPOS_ExtensionPosFormat1; 622 623 /********** 624 * CMAP 625 **********/ 626 627 static VOID *load_CMAP_format12_table(HDC hdc, ScriptCache *psc) 628 { 629 CMAP_Header *CMAP_Table = NULL; 630 int length; 631 int i; 632 633 if (!psc->CMAP_Table) 634 { 635 length = GetFontData(hdc, CMAP_TAG , 0, NULL, 0); 636 if (length != GDI_ERROR) 637 { 638 psc->CMAP_Table = heap_alloc(length); 639 GetFontData(hdc, CMAP_TAG , 0, psc->CMAP_Table, length); 640 TRACE("Loaded cmap table of %i bytes\n",length); 641 } 642 else 643 return NULL; 644 } 645 646 CMAP_Table = psc->CMAP_Table; 647 648 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++) 649 { 650 if ( (GET_BE_WORD(CMAP_Table->tables[i].platformID) == 3) && 651 (GET_BE_WORD(CMAP_Table->tables[i].encodingID) == 10) ) 652 { 653 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset)); 654 if (GET_BE_WORD(format->format) == 12) 655 return format; 656 } 657 } 658 return NULL; 659 } 660 661 static int compare_group(const void *a, const void* b) 662 { 663 const DWORD *chr = a; 664 const CMAP_SegmentedCoverage_group *group = b; 665 666 if (*chr < GET_BE_DWORD(group->startCharCode)) 667 return -1; 668 if (*chr > GET_BE_DWORD(group->endCharCode)) 669 return 1; 670 return 0; 671 } 672 673 DWORD OpenType_CMAP_GetGlyphIndex(HDC hdc, ScriptCache *psc, DWORD utf32c, WORD *glyph_index, DWORD flags) 674 { 675 /* BMP: use gdi32 for ease */ 676 if (utf32c < 0x10000) 677 { 678 WCHAR ch = utf32c; 679 return GetGlyphIndicesW(hdc, &ch, 1, glyph_index, flags); 680 } 681 682 if (!psc->CMAP_format12_Table) 683 psc->CMAP_format12_Table = load_CMAP_format12_table(hdc, psc); 684 685 if (flags & GGI_MARK_NONEXISTING_GLYPHS) 686 *glyph_index = 0xffffu; 687 else 688 *glyph_index = 0u; 689 690 if (psc->CMAP_format12_Table) 691 { 692 CMAP_SegmentedCoverage *format = NULL; 693 CMAP_SegmentedCoverage_group *group = NULL; 694 695 format = (CMAP_SegmentedCoverage *)psc->CMAP_format12_Table; 696 697 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups), 698 sizeof(CMAP_SegmentedCoverage_group), compare_group); 699 700 if (group) 701 { 702 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode); 703 *glyph_index = GET_BE_DWORD(group->startGlyphID) + offset; 704 return 0; 705 } 706 } 707 return 0; 708 } 709 710 /********** 711 * GDEF 712 **********/ 713 714 static WORD OT_get_glyph_class(const void *table, WORD glyph) 715 { 716 WORD class = 0; 717 const OT_ClassDefFormat1 *cf1 = table; 718 719 if (!table) return 0; 720 721 if (GET_BE_WORD(cf1->ClassFormat) == 1) 722 { 723 if (glyph >= GET_BE_WORD(cf1->StartGlyph)) 724 { 725 int index = glyph - GET_BE_WORD(cf1->StartGlyph); 726 if (index < GET_BE_WORD(cf1->GlyphCount)) 727 class = GET_BE_WORD(cf1->ClassValueArray[index]); 728 } 729 } 730 else if (GET_BE_WORD(cf1->ClassFormat) == 2) 731 { 732 const OT_ClassDefFormat2 *cf2 = table; 733 int i, top; 734 top = GET_BE_WORD(cf2->ClassRangeCount); 735 for (i = 0; i < top; i++) 736 { 737 if (glyph >= GET_BE_WORD(cf2->ClassRangeRecord[i].Start) && 738 glyph <= GET_BE_WORD(cf2->ClassRangeRecord[i].End)) 739 { 740 class = GET_BE_WORD(cf2->ClassRangeRecord[i].Class); 741 break; 742 } 743 } 744 } 745 else 746 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1->ClassFormat)); 747 748 return class; 749 } 750 751 void OpenType_GDEF_UpdateGlyphProps(ScriptCache *psc, const WORD *pwGlyphs, const WORD cGlyphs, WORD* pwLogClust, const WORD cChars, SCRIPT_GLYPHPROP *pGlyphProp) 752 { 753 int i; 754 void *glyph_class_table = NULL; 755 756 if (psc->GDEF_Table) 757 { 758 const GDEF_Header *header = psc->GDEF_Table; 759 WORD offset = GET_BE_WORD( header->GlyphClassDef ); 760 if (offset) 761 glyph_class_table = (BYTE *)psc->GDEF_Table + offset; 762 } 763 764 for (i = 0; i < cGlyphs; i++) 765 { 766 WORD class; 767 int char_count = 0; 768 int k; 769 770 k = USP10_FindGlyphInLogClust(pwLogClust, cChars, i); 771 if (k >= 0) 772 { 773 for (; k < cChars && pwLogClust[k] == i; k++) 774 char_count++; 775 } 776 777 class = OT_get_glyph_class( glyph_class_table, pwGlyphs[i] ); 778 779 switch (class) 780 { 781 case 0: 782 case BaseGlyph: 783 pGlyphProp[i].sva.fClusterStart = 1; 784 pGlyphProp[i].sva.fDiacritic = 0; 785 pGlyphProp[i].sva.fZeroWidth = 0; 786 break; 787 case LigatureGlyph: 788 pGlyphProp[i].sva.fClusterStart = 1; 789 pGlyphProp[i].sva.fDiacritic = 0; 790 pGlyphProp[i].sva.fZeroWidth = 0; 791 break; 792 case MarkGlyph: 793 pGlyphProp[i].sva.fClusterStart = 0; 794 pGlyphProp[i].sva.fDiacritic = 1; 795 pGlyphProp[i].sva.fZeroWidth = 1; 796 break; 797 case ComponentGlyph: 798 pGlyphProp[i].sva.fClusterStart = 0; 799 pGlyphProp[i].sva.fDiacritic = 0; 800 pGlyphProp[i].sva.fZeroWidth = 0; 801 break; 802 default: 803 ERR("Unknown glyph class %i\n",class); 804 pGlyphProp[i].sva.fClusterStart = 1; 805 pGlyphProp[i].sva.fDiacritic = 0; 806 pGlyphProp[i].sva.fZeroWidth = 0; 807 } 808 809 if (char_count == 0) 810 pGlyphProp[i].sva.fClusterStart = 0; 811 } 812 } 813 814 /********** 815 * GSUB 816 **********/ 817 static INT GSUB_apply_lookup(const OT_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count); 818 819 static int GSUB_is_glyph_covered(const void *table, unsigned int glyph) 820 { 821 const OT_CoverageFormat1* cf1; 822 823 cf1 = table; 824 825 if (GET_BE_WORD(cf1->CoverageFormat) == 1) 826 { 827 int count = GET_BE_WORD(cf1->GlyphCount); 828 int i; 829 TRACE("Coverage Format 1, %i glyphs\n",count); 830 for (i = 0; i < count; i++) 831 if (glyph == GET_BE_WORD(cf1->GlyphArray[i])) 832 return i; 833 return -1; 834 } 835 else if (GET_BE_WORD(cf1->CoverageFormat) == 2) 836 { 837 const OT_CoverageFormat2* cf2; 838 int i; 839 int count; 840 cf2 = (const OT_CoverageFormat2*)cf1; 841 842 count = GET_BE_WORD(cf2->RangeCount); 843 TRACE("Coverage Format 2, %i ranges\n",count); 844 for (i = 0; i < count; i++) 845 { 846 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start)) 847 return -1; 848 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) && 849 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End))) 850 { 851 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) + 852 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start)); 853 } 854 } 855 return -1; 856 } 857 else 858 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat)); 859 860 return -1; 861 } 862 863 static const BYTE *GSUB_get_subtable(const OT_LookupTable *look, int index) 864 { 865 int offset = GET_BE_WORD(look->SubTable[index]); 866 867 if (GET_BE_WORD(look->LookupType) == GSUB_LOOKUP_EXTENSION) 868 { 869 const GSUB_ExtensionPosFormat1 *ext = (const GSUB_ExtensionPosFormat1 *)((const BYTE *)look + offset); 870 if (GET_BE_WORD(ext->SubstFormat) == 1) 871 { 872 offset += GET_BE_DWORD(ext->ExtensionOffset); 873 } 874 else 875 { 876 FIXME("Unhandled Extension Substitution Format %i\n",GET_BE_WORD(ext->SubstFormat)); 877 } 878 } 879 return (const BYTE *)look + offset; 880 } 881 882 static INT GSUB_apply_SingleSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count) 883 { 884 int j; 885 TRACE("Single Substitution Subtable\n"); 886 887 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 888 { 889 const GSUB_SingleSubstFormat1 *ssf1 = (const GSUB_SingleSubstFormat1*)GSUB_get_subtable(look, j); 890 if (GET_BE_WORD(ssf1->SubstFormat) == 1) 891 { 892 int offset = GET_BE_WORD(ssf1->Coverage); 893 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID)); 894 if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1) 895 { 896 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]); 897 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID); 898 TRACE(" 0x%x\n",glyphs[glyph_index]); 899 return glyph_index + write_dir; 900 } 901 } 902 else 903 { 904 const GSUB_SingleSubstFormat2 *ssf2; 905 INT index; 906 INT offset; 907 908 ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1; 909 offset = GET_BE_WORD(ssf1->Coverage); 910 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount)); 911 index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]); 912 TRACE(" Coverage index %i\n",index); 913 if (index != -1) 914 { 915 if (glyphs[glyph_index] == GET_BE_WORD(ssf2->Substitute[index])) 916 return GSUB_E_NOGLYPH; 917 918 TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]); 919 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]); 920 TRACE("0x%x\n",glyphs[glyph_index]); 921 return glyph_index + write_dir; 922 } 923 } 924 } 925 return GSUB_E_NOGLYPH; 926 } 927 928 static INT GSUB_apply_MultipleSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count) 929 { 930 int j; 931 TRACE("Multiple Substitution Subtable\n"); 932 933 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 934 { 935 int offset, index; 936 const GSUB_MultipleSubstFormat1 *msf1; 937 msf1 = (const GSUB_MultipleSubstFormat1*)GSUB_get_subtable(look, j); 938 939 offset = GET_BE_WORD(msf1->Coverage); 940 index = GSUB_is_glyph_covered((const BYTE*)msf1+offset, glyphs[glyph_index]); 941 if (index != -1) 942 { 943 const GSUB_Sequence *seq; 944 int sub_count; 945 int j; 946 offset = GET_BE_WORD(msf1->Sequence[index]); 947 seq = (const GSUB_Sequence*)((const BYTE*)msf1+offset); 948 sub_count = GET_BE_WORD(seq->GlyphCount); 949 TRACE(" Glyph 0x%x (+%i)->",glyphs[glyph_index],(sub_count-1)); 950 951 for (j = (*glyph_count)+(sub_count-1); j > glyph_index; j--) 952 glyphs[j] =glyphs[j-(sub_count-1)]; 953 954 for (j = 0; j < sub_count; j++) 955 if (write_dir < 0) 956 glyphs[glyph_index + (sub_count-1) - j] = GET_BE_WORD(seq->Substitute[j]); 957 else 958 glyphs[glyph_index + j] = GET_BE_WORD(seq->Substitute[j]); 959 960 *glyph_count = *glyph_count + (sub_count - 1); 961 962 if (TRACE_ON(uniscribe)) 963 { 964 for (j = 0; j < sub_count; j++) 965 TRACE(" 0x%x",glyphs[glyph_index+j]); 966 TRACE("\n"); 967 } 968 969 if (write_dir > 0) 970 return glyph_index + sub_count; 971 else 972 return glyph_index - 1; 973 } 974 } 975 return GSUB_E_NOGLYPH; 976 } 977 978 static INT GSUB_apply_AlternateSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count) 979 { 980 int j; 981 TRACE("Alternate Substitution Subtable\n"); 982 983 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 984 { 985 int offset; 986 const GSUB_AlternateSubstFormat1 *asf1; 987 INT index; 988 989 asf1 = (const GSUB_AlternateSubstFormat1*)GSUB_get_subtable(look, j); 990 offset = GET_BE_WORD(asf1->Coverage); 991 992 index = GSUB_is_glyph_covered((const BYTE*)asf1+offset, glyphs[glyph_index]); 993 if (index != -1) 994 { 995 const GSUB_AlternateSet *as; 996 offset = GET_BE_WORD(asf1->AlternateSet[index]); 997 as = (const GSUB_AlternateSet*)((const BYTE*)asf1+offset); 998 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as->GlyphCount)); 999 if (glyphs[glyph_index] == GET_BE_WORD(as->Alternate[0])) 1000 return GSUB_E_NOGLYPH; 1001 1002 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]); 1003 glyphs[glyph_index] = GET_BE_WORD(as->Alternate[0]); 1004 TRACE(" 0x%x\n",glyphs[glyph_index]); 1005 return glyph_index + write_dir; 1006 } 1007 } 1008 return GSUB_E_NOGLYPH; 1009 } 1010 1011 static INT GSUB_apply_LigatureSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count) 1012 { 1013 int j; 1014 1015 TRACE("Ligature Substitution Subtable\n"); 1016 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 1017 { 1018 const GSUB_LigatureSubstFormat1 *lsf1; 1019 int offset,index; 1020 1021 lsf1 = (const GSUB_LigatureSubstFormat1*)GSUB_get_subtable(look, j); 1022 offset = GET_BE_WORD(lsf1->Coverage); 1023 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]); 1024 TRACE(" Coverage index %i\n",index); 1025 if (index != -1) 1026 { 1027 const GSUB_LigatureSet *ls; 1028 int k, count; 1029 1030 offset = GET_BE_WORD(lsf1->LigatureSet[index]); 1031 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset); 1032 count = GET_BE_WORD(ls->LigatureCount); 1033 TRACE(" LigatureSet has %i members\n",count); 1034 for (k = 0; k < count; k++) 1035 { 1036 const GSUB_Ligature *lig; 1037 int CompCount,l,CompIndex; 1038 1039 offset = GET_BE_WORD(ls->Ligature[k]); 1040 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset); 1041 CompCount = GET_BE_WORD(lig->CompCount) - 1; 1042 CompIndex = glyph_index+write_dir; 1043 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++) 1044 { 1045 int CompGlyph; 1046 CompGlyph = GET_BE_WORD(lig->Component[l]); 1047 if (CompGlyph != glyphs[CompIndex]) 1048 break; 1049 CompIndex += write_dir; 1050 } 1051 if (l == CompCount) 1052 { 1053 int replaceIdx = glyph_index; 1054 if (write_dir < 0) 1055 replaceIdx = glyph_index - CompCount; 1056 1057 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount); 1058 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph); 1059 TRACE("0x%x\n",glyphs[replaceIdx]); 1060 if (CompCount > 0) 1061 { 1062 unsigned int j = replaceIdx + 1; 1063 memmove(&glyphs[j], &glyphs[j + CompCount], (*glyph_count - j) * sizeof(*glyphs)); 1064 *glyph_count = *glyph_count - CompCount; 1065 } 1066 return replaceIdx + write_dir; 1067 } 1068 } 1069 } 1070 } 1071 return GSUB_E_NOGLYPH; 1072 } 1073 1074 static INT GSUB_apply_ContextSubst(const OT_LookupList* lookup, const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count) 1075 { 1076 int j; 1077 TRACE("Context Substitution Subtable\n"); 1078 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 1079 { 1080 const GSUB_ContextSubstFormat1 *csf1; 1081 1082 csf1 = (const GSUB_ContextSubstFormat1*)GSUB_get_subtable(look, j); 1083 if (GET_BE_WORD(csf1->SubstFormat) == 1) 1084 { 1085 int offset, index; 1086 TRACE("Context Substitution Subtable: Class 1\n"); 1087 offset = GET_BE_WORD(csf1->Coverage); 1088 index = GSUB_is_glyph_covered((const BYTE*)csf1+offset, glyphs[glyph_index]); 1089 TRACE(" Coverage index %i\n",index); 1090 if (index != -1) 1091 { 1092 int k, count; 1093 const GSUB_SubRuleSet *srs; 1094 offset = GET_BE_WORD(csf1->SubRuleSet[index]); 1095 srs = (const GSUB_SubRuleSet*)((const BYTE*)csf1+offset); 1096 count = GET_BE_WORD(srs->SubRuleCount); 1097 TRACE(" SubRuleSet has %i members\n",count); 1098 for (k = 0; k < count; k++) 1099 { 1100 const GSUB_SubRule_1 *sr; 1101 const GSUB_SubRule_2 *sr_2; 1102 unsigned int g; 1103 int g_count, l; 1104 int newIndex = glyph_index; 1105 1106 offset = GET_BE_WORD(srs->SubRule[k]); 1107 sr = (const GSUB_SubRule_1*)((const BYTE*)srs+offset); 1108 g_count = GET_BE_WORD(sr->GlyphCount); 1109 TRACE(" SubRule has %i glyphs\n",g_count); 1110 1111 g = glyph_index + write_dir * (g_count - 1); 1112 if (g >= *glyph_count) 1113 continue; 1114 1115 for (l = 0; l < g_count-1; l++) 1116 if (glyphs[glyph_index + (write_dir * (l+1))] != GET_BE_WORD(sr->Input[l])) break; 1117 1118 if (l < g_count-1) 1119 { 1120 TRACE(" Rule does not match\n"); 1121 continue; 1122 } 1123 1124 TRACE(" Rule matches\n"); 1125 sr_2 = (const GSUB_SubRule_2 *)&sr->Input[g_count - 1]; 1126 1127 for (l = 0; l < GET_BE_WORD(sr->SubstCount); l++) 1128 { 1129 unsigned int lookup_index = GET_BE_WORD(sr_2->SubstLookupRecord[l].LookupListIndex); 1130 unsigned int sequence_index = GET_BE_WORD(sr_2->SubstLookupRecord[l].SequenceIndex); 1131 1132 g = glyph_index + write_dir * sequence_index; 1133 if (g >= *glyph_count) 1134 { 1135 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n", 1136 sequence_index, glyph_index, write_dir); 1137 continue; 1138 } 1139 1140 TRACE(" SUBST: %u -> %u %u.\n", l, sequence_index, lookup_index); 1141 newIndex = GSUB_apply_lookup(lookup, lookup_index, glyphs, g, write_dir, glyph_count); 1142 if (newIndex == GSUB_E_NOGLYPH) 1143 { 1144 ERR(" Chain failed to generate a glyph\n"); 1145 continue; 1146 } 1147 } 1148 return newIndex; 1149 } 1150 } 1151 } 1152 else if (GET_BE_WORD(csf1->SubstFormat) == 2) 1153 { 1154 const GSUB_ContextSubstFormat2 *csf2; 1155 const void *glyph_class_table; 1156 int offset, index; 1157 1158 csf2 = (const GSUB_ContextSubstFormat2*)csf1; 1159 TRACE("Context Substitution Subtable: Class 2\n"); 1160 offset = GET_BE_WORD(csf2->Coverage); 1161 index = GSUB_is_glyph_covered((const BYTE*)csf2+offset, glyphs[glyph_index]); 1162 TRACE(" Coverage index %i\n",index); 1163 if (index != -1) 1164 { 1165 int k, count, class; 1166 const GSUB_SubClassSet *scs; 1167 1168 offset = GET_BE_WORD(csf2->ClassDef); 1169 glyph_class_table = (const BYTE *)csf2 + offset; 1170 1171 class = OT_get_glyph_class(glyph_class_table,glyphs[glyph_index]); 1172 1173 offset = GET_BE_WORD(csf2->SubClassSet[class]); 1174 if (offset == 0) 1175 { 1176 TRACE(" No class rule table for class %i\n",class); 1177 continue; 1178 } 1179 scs = (const GSUB_SubClassSet*)((const BYTE*)csf2+offset); 1180 count = GET_BE_WORD(scs->SubClassRuleCnt); 1181 TRACE(" SubClassSet has %i members\n",count); 1182 for (k = 0; k < count; k++) 1183 { 1184 const GSUB_SubClassRule_1 *sr; 1185 const GSUB_SubClassRule_2 *sr_2; 1186 unsigned int g; 1187 int g_count, l; 1188 int newIndex = glyph_index; 1189 1190 offset = GET_BE_WORD(scs->SubClassRule[k]); 1191 sr = (const GSUB_SubClassRule_1*)((const BYTE*)scs+offset); 1192 g_count = GET_BE_WORD(sr->GlyphCount); 1193 TRACE(" SubClassRule has %i glyphs classes\n",g_count); 1194 1195 g = glyph_index + write_dir * (g_count - 1); 1196 if (g >= *glyph_count) 1197 continue; 1198 1199 for (l = 0; l < g_count-1; l++) 1200 { 1201 int g_class = OT_get_glyph_class(glyph_class_table, glyphs[glyph_index + (write_dir * (l+1))]); 1202 if (g_class != GET_BE_WORD(sr->Class[l])) break; 1203 } 1204 1205 if (l < g_count-1) 1206 { 1207 TRACE(" Rule does not match\n"); 1208 continue; 1209 } 1210 1211 TRACE(" Rule matches\n"); 1212 sr_2 = (const GSUB_SubClassRule_2 *)&sr->Class[g_count - 1]; 1213 1214 for (l = 0; l < GET_BE_WORD(sr->SubstCount); l++) 1215 { 1216 unsigned int lookup_index = GET_BE_WORD(sr_2->SubstLookupRecord[l].LookupListIndex); 1217 unsigned int sequence_index = GET_BE_WORD(sr_2->SubstLookupRecord[l].SequenceIndex); 1218 1219 g = glyph_index + write_dir * sequence_index; 1220 if (g >= *glyph_count) 1221 { 1222 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n", 1223 sequence_index, glyph_index, write_dir); 1224 continue; 1225 } 1226 1227 TRACE(" SUBST: %u -> %u %u.\n", l, sequence_index, lookup_index); 1228 newIndex = GSUB_apply_lookup(lookup, lookup_index, glyphs, g, write_dir, glyph_count); 1229 if (newIndex == GSUB_E_NOGLYPH) 1230 { 1231 ERR(" Chain failed to generate a glyph\n"); 1232 continue; 1233 } 1234 } 1235 return newIndex; 1236 } 1237 } 1238 } 1239 else 1240 FIXME("Unhandled Context Substitution Format %i\n", GET_BE_WORD(csf1->SubstFormat)); 1241 } 1242 return GSUB_E_NOGLYPH; 1243 } 1244 1245 static INT GSUB_apply_ChainContextSubst(const OT_LookupList* lookup, const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count) 1246 { 1247 int j; 1248 1249 TRACE("Chaining Contextual Substitution Subtable\n"); 1250 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 1251 { 1252 const GSUB_ChainContextSubstFormat1 *ccsf1; 1253 int offset; 1254 int dirLookahead = write_dir; 1255 int dirBacktrack = -1 * write_dir; 1256 1257 ccsf1 = (const GSUB_ChainContextSubstFormat1*)GSUB_get_subtable(look, j); 1258 if (GET_BE_WORD(ccsf1->SubstFormat) == 1) 1259 { 1260 static int once; 1261 if (!once++) 1262 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n"); 1263 continue; 1264 } 1265 else if (GET_BE_WORD(ccsf1->SubstFormat) == 2) 1266 { 1267 WORD offset, count; 1268 const void *backtrack_class_table; 1269 const void *input_class_table; 1270 const void *lookahead_class_table; 1271 int i; 1272 WORD class; 1273 1274 const GSUB_ChainContextSubstFormat2 *ccsf2 = (const GSUB_ChainContextSubstFormat2*)ccsf1; 1275 const GSUB_ChainSubClassSet *csc; 1276 1277 TRACE(" subtype 2 (Class-based Chaining Context Glyph Substitution)\n"); 1278 1279 offset = GET_BE_WORD(ccsf2->Coverage); 1280 1281 if (GSUB_is_glyph_covered((const BYTE*)ccsf2+offset, glyphs[glyph_index]) == -1) 1282 { 1283 TRACE("Glyph not covered\n"); 1284 continue; 1285 } 1286 offset = GET_BE_WORD(ccsf2->BacktrackClassDef); 1287 backtrack_class_table = (const BYTE*)ccsf2+offset; 1288 offset = GET_BE_WORD(ccsf2->InputClassDef); 1289 input_class_table = (const BYTE*)ccsf2+offset; 1290 offset = GET_BE_WORD(ccsf2->LookaheadClassDef); 1291 lookahead_class_table = (const BYTE*)ccsf2+offset; 1292 count = GET_BE_WORD(ccsf2->ChainSubClassSetCnt); 1293 1294 class = OT_get_glyph_class(input_class_table, glyphs[glyph_index]); 1295 offset = GET_BE_WORD(ccsf2->ChainSubClassSet[class]); 1296 1297 if (offset == 0) 1298 { 1299 TRACE("No rules for class\n"); 1300 continue; 1301 } 1302 1303 csc = (const GSUB_ChainSubClassSet*)((BYTE*)ccsf2+offset); 1304 count = GET_BE_WORD(csc->ChainSubClassRuleCnt); 1305 1306 TRACE("%i rules to check\n",count); 1307 1308 for (i = 0; i < count; i++) 1309 { 1310 WORD backtrack_count, input_count, lookahead_count, substitute_count; 1311 int k; 1312 const GSUB_ChainSubClassRule_1 *backtrack; 1313 const GSUB_ChainSubClassRule_2 *input; 1314 const GSUB_ChainSubClassRule_3 *lookahead; 1315 const GSUB_ChainSubClassRule_4 *substitute; 1316 int new_index = GSUB_E_NOGLYPH; 1317 1318 offset = GET_BE_WORD(csc->ChainSubClassRule[i]); 1319 backtrack = (const GSUB_ChainSubClassRule_1 *)((BYTE *)csc + offset); 1320 backtrack_count = GET_BE_WORD(backtrack->BacktrackGlyphCount); 1321 k = glyph_index + dirBacktrack * backtrack_count; 1322 if (k < 0 || k >= *glyph_count) 1323 continue; 1324 1325 input = (const GSUB_ChainSubClassRule_2 *)&backtrack->Backtrack[backtrack_count]; 1326 input_count = GET_BE_WORD(input->InputGlyphCount) - 1; 1327 k = glyph_index + write_dir * input_count; 1328 if (k < 0 || k >= *glyph_count) 1329 continue; 1330 1331 lookahead = (const GSUB_ChainSubClassRule_3 *)&input->Input[input_count]; 1332 lookahead_count = GET_BE_WORD(lookahead->LookaheadGlyphCount); 1333 k = glyph_index + dirLookahead * (input_count + lookahead_count); 1334 if (k < 0 || k >= *glyph_count) 1335 continue; 1336 1337 substitute = (const GSUB_ChainSubClassRule_4 *)&lookahead->LookAhead[lookahead_count]; 1338 1339 for (k = 0; k < backtrack_count; ++k) 1340 { 1341 WORD target_class = GET_BE_WORD(backtrack->Backtrack[k]); 1342 WORD glyph_class = OT_get_glyph_class(backtrack_class_table, glyphs[glyph_index + (dirBacktrack * (k+1))]); 1343 if (target_class != glyph_class) 1344 break; 1345 } 1346 if (k != backtrack_count) 1347 continue; 1348 TRACE("Matched Backtrack\n"); 1349 1350 for (k = 0; k < input_count; ++k) 1351 { 1352 WORD target_class = GET_BE_WORD(input->Input[k]); 1353 WORD glyph_class = OT_get_glyph_class(input_class_table, glyphs[glyph_index + (write_dir * (k+1))]); 1354 if (target_class != glyph_class) 1355 break; 1356 } 1357 if (k != input_count) 1358 continue; 1359 TRACE("Matched IndexGlyphs\n"); 1360 1361 for (k = 0; k < lookahead_count; ++k) 1362 { 1363 WORD target_class = GET_BE_WORD(lookahead->LookAhead[k]); 1364 WORD glyph_class = OT_get_glyph_class(lookahead_class_table, 1365 glyphs[glyph_index + (dirLookahead * (input_count + k + 1))]); 1366 if (target_class != glyph_class) 1367 break; 1368 } 1369 if (k != lookahead_count) 1370 continue; 1371 TRACE("Matched LookAhead\n"); 1372 1373 substitute_count = GET_BE_WORD(substitute->SubstCount); 1374 for (k = 0; k < substitute_count; ++k) 1375 { 1376 unsigned int lookup_index = GET_BE_WORD(substitute->SubstLookupRecord[k].LookupListIndex); 1377 unsigned int sequence_index = GET_BE_WORD(substitute->SubstLookupRecord[k].SequenceIndex); 1378 unsigned int g = glyph_index + write_dir * sequence_index; 1379 1380 if (g >= *glyph_count) 1381 { 1382 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n", 1383 sequence_index, glyph_index, write_dir); 1384 continue; 1385 } 1386 1387 TRACE("SUBST: %u -> %u %u.\n", k, sequence_index, lookup_index); 1388 new_index = GSUB_apply_lookup(lookup, lookup_index, glyphs, g, write_dir, glyph_count); 1389 if (new_index == GSUB_E_NOGLYPH) 1390 ERR("Chain failed to generate a glyph.\n"); 1391 } 1392 return new_index; 1393 } 1394 } 1395 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3) 1396 { 1397 WORD backtrack_count, input_count, lookahead_count, substitution_count; 1398 int k; 1399 const GSUB_ChainContextSubstFormat3_1 *backtrack; 1400 const GSUB_ChainContextSubstFormat3_2 *input; 1401 const GSUB_ChainContextSubstFormat3_3 *lookahead; 1402 const GSUB_ChainContextSubstFormat3_4 *substitute; 1403 int new_index = GSUB_E_NOGLYPH; 1404 1405 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n"); 1406 1407 backtrack = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1; 1408 backtrack_count = GET_BE_WORD(backtrack->BacktrackGlyphCount); 1409 k = glyph_index + dirBacktrack * backtrack_count; 1410 if (k < 0 || k >= *glyph_count) 1411 continue; 1412 1413 input = (const GSUB_ChainContextSubstFormat3_2 *)&backtrack->Coverage[backtrack_count]; 1414 input_count = GET_BE_WORD(input->InputGlyphCount); 1415 k = glyph_index + write_dir * (input_count - 1); 1416 if (k < 0 || k >= *glyph_count) 1417 continue; 1418 1419 lookahead = (const GSUB_ChainContextSubstFormat3_3 *)&input->Coverage[input_count]; 1420 lookahead_count = GET_BE_WORD(lookahead->LookaheadGlyphCount); 1421 k = glyph_index + dirLookahead * (input_count + lookahead_count - 1); 1422 if (k < 0 || k >= *glyph_count) 1423 continue; 1424 1425 substitute = (const GSUB_ChainContextSubstFormat3_4 *)&lookahead->Coverage[lookahead_count]; 1426 1427 for (k = 0; k < backtrack_count; ++k) 1428 { 1429 offset = GET_BE_WORD(backtrack->Coverage[k]); 1430 if (GSUB_is_glyph_covered((const BYTE *)ccsf1 + offset, 1431 glyphs[glyph_index + (dirBacktrack * (k + 1))]) == -1) 1432 break; 1433 } 1434 if (k != backtrack_count) 1435 continue; 1436 TRACE("Matched Backtrack\n"); 1437 1438 for (k = 0; k < input_count; ++k) 1439 { 1440 offset = GET_BE_WORD(input->Coverage[k]); 1441 if (GSUB_is_glyph_covered((const BYTE *)ccsf1 + offset, 1442 glyphs[glyph_index + (write_dir * k)]) == -1) 1443 break; 1444 } 1445 if (k != input_count) 1446 continue; 1447 TRACE("Matched IndexGlyphs\n"); 1448 1449 for (k = 0; k < lookahead_count; ++k) 1450 { 1451 offset = GET_BE_WORD(lookahead->Coverage[k]); 1452 if (GSUB_is_glyph_covered((const BYTE *)ccsf1 + offset, 1453 glyphs[glyph_index + (dirLookahead * (input_count + k))]) == -1) 1454 break; 1455 } 1456 if (k != lookahead_count) 1457 continue; 1458 TRACE("Matched LookAhead\n"); 1459 1460 substitution_count = GET_BE_WORD(substitute->SubstCount); 1461 for (k = 0; k < substitution_count; ++k) 1462 { 1463 unsigned int lookup_index = GET_BE_WORD(substitute->SubstLookupRecord[k].LookupListIndex); 1464 unsigned int sequence_index = GET_BE_WORD(substitute->SubstLookupRecord[k].SequenceIndex); 1465 unsigned int g = glyph_index + write_dir * sequence_index; 1466 1467 if (g >= *glyph_count) 1468 { 1469 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n", 1470 sequence_index, glyph_index, write_dir); 1471 continue; 1472 } 1473 1474 TRACE("SUBST: %u -> %u %u.\n", k, sequence_index, lookup_index); 1475 new_index = GSUB_apply_lookup(lookup, lookup_index, glyphs, g, write_dir, glyph_count); 1476 if (new_index == GSUB_E_NOGLYPH) 1477 ERR("Chain failed to generate a glyph.\n"); 1478 } 1479 return new_index; 1480 } 1481 } 1482 return GSUB_E_NOGLYPH; 1483 } 1484 1485 static INT GSUB_apply_lookup(const OT_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count) 1486 { 1487 int offset; 1488 enum gsub_lookup_type type; 1489 const OT_LookupTable *look; 1490 1491 offset = GET_BE_WORD(lookup->Lookup[lookup_index]); 1492 look = (const OT_LookupTable*)((const BYTE*)lookup + offset); 1493 type = GET_BE_WORD(look->LookupType); 1494 TRACE("type %#x, flag %#x, subtables %u.\n", type, 1495 GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount)); 1496 1497 if (type == GSUB_LOOKUP_EXTENSION) 1498 { 1499 if (GET_BE_WORD(look->SubTableCount)) 1500 { 1501 const GSUB_ExtensionPosFormat1 *ext = (const GSUB_ExtensionPosFormat1 *)((const BYTE *)look + GET_BE_WORD(look->SubTable[0])); 1502 if (GET_BE_WORD(ext->SubstFormat) == 1) 1503 { 1504 type = GET_BE_WORD(ext->ExtensionLookupType); 1505 TRACE("extension type %i\n",type); 1506 } 1507 else 1508 { 1509 FIXME("Unhandled Extension Substitution Format %i\n",GET_BE_WORD(ext->SubstFormat)); 1510 } 1511 } 1512 else 1513 { 1514 WARN("lookup type is Extension Substitution but no extension subtable exists\n"); 1515 } 1516 } 1517 switch(type) 1518 { 1519 case GSUB_LOOKUP_SINGLE: 1520 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count); 1521 case GSUB_LOOKUP_MULTIPLE: 1522 return GSUB_apply_MultipleSubst(look, glyphs, glyph_index, write_dir, glyph_count); 1523 case GSUB_LOOKUP_ALTERNATE: 1524 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count); 1525 case GSUB_LOOKUP_LIGATURE: 1526 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count); 1527 case GSUB_LOOKUP_CONTEXT: 1528 return GSUB_apply_ContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count); 1529 case GSUB_LOOKUP_CONTEXT_CHAINED: 1530 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count); 1531 case GSUB_LOOKUP_EXTENSION: 1532 FIXME("Extension Substitution types not valid here\n"); 1533 break; 1534 default: 1535 FIXME("Unhandled GSUB lookup type %#x.\n", type); 1536 } 1537 return GSUB_E_NOGLYPH; 1538 } 1539 1540 int OpenType_apply_GSUB_lookup(const void *table, unsigned int lookup_index, WORD *glyphs, 1541 unsigned int glyph_index, int write_dir, int *glyph_count) 1542 { 1543 const GSUB_Header *header = (const GSUB_Header *)table; 1544 const OT_LookupList *lookup = (const OT_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList)); 1545 1546 return GSUB_apply_lookup(lookup, lookup_index, glyphs, glyph_index, write_dir, glyph_count); 1547 } 1548 1549 /********** 1550 * GPOS 1551 **********/ 1552 static unsigned int GPOS_apply_lookup(const ScriptCache *script_cache, const OUTLINETEXTMETRICW *otm, 1553 const LOGFONTW *logfont, const SCRIPT_ANALYSIS *analysis, int *advance, const OT_LookupList *lookup, 1554 unsigned int lookup_index, const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, 1555 GOFFSET *goffset); 1556 1557 static INT GPOS_get_device_table_value(const OT_DeviceTable *DeviceTable, WORD ppem) 1558 { 1559 static const WORD mask[3] = {3,0xf,0xff}; 1560 if (DeviceTable && ppem >= GET_BE_WORD(DeviceTable->StartSize) && ppem <= GET_BE_WORD(DeviceTable->EndSize)) 1561 { 1562 WORD format = GET_BE_WORD(DeviceTable->DeltaFormat); 1563 int index = ppem - GET_BE_WORD(DeviceTable->StartSize); 1564 int value; 1565 1566 TRACE("device table, format %#x, index %i\n", format, index); 1567 1568 if (format < 1 || format > 3) 1569 { 1570 WARN("invalid delta format %#x\n", format); 1571 return 0; 1572 } 1573 1574 index = index << format; 1575 value = (DeviceTable->DeltaValue[index/sizeof(WORD)] << (index%sizeof(WORD)))&mask[format-1]; 1576 TRACE("offset %i, value %i\n",index, value); 1577 if (value > mask[format-1]/2) 1578 value = -1 * ((mask[format-1]+1) - value); 1579 return value; 1580 } 1581 return 0; 1582 } 1583 1584 static void GPOS_get_anchor_values(const void *table, POINT *pt, WORD ppem) 1585 { 1586 const GPOS_AnchorFormat1* anchor1 = (const GPOS_AnchorFormat1*)table; 1587 1588 switch (GET_BE_WORD(anchor1->AnchorFormat)) 1589 { 1590 case 1: 1591 { 1592 TRACE("Anchor Format 1\n"); 1593 pt->x = (short)GET_BE_WORD(anchor1->XCoordinate); 1594 pt->y = (short)GET_BE_WORD(anchor1->YCoordinate); 1595 break; 1596 } 1597 case 2: 1598 { 1599 const GPOS_AnchorFormat2* anchor2 = (const GPOS_AnchorFormat2*)table; 1600 TRACE("Anchor Format 2\n"); 1601 pt->x = (short)GET_BE_WORD(anchor2->XCoordinate); 1602 pt->y = (short)GET_BE_WORD(anchor2->YCoordinate); 1603 break; 1604 } 1605 case 3: 1606 { 1607 int offset; 1608 const GPOS_AnchorFormat3* anchor3 = (const GPOS_AnchorFormat3*)table; 1609 TRACE("Anchor Format 3\n"); 1610 pt->x = (short)GET_BE_WORD(anchor3->XCoordinate); 1611 pt->y = (short)GET_BE_WORD(anchor3->YCoordinate); 1612 offset = GET_BE_WORD(anchor3->XDeviceTable); 1613 TRACE("ppem %i\n",ppem); 1614 if (offset) 1615 { 1616 const OT_DeviceTable* DeviceTableX = NULL; 1617 DeviceTableX = (const OT_DeviceTable*)((const BYTE*)anchor3 + offset); 1618 pt->x += GPOS_get_device_table_value(DeviceTableX, ppem); 1619 } 1620 offset = GET_BE_WORD(anchor3->YDeviceTable); 1621 if (offset) 1622 { 1623 const OT_DeviceTable* DeviceTableY = NULL; 1624 DeviceTableY = (const OT_DeviceTable*)((const BYTE*)anchor3 + offset); 1625 pt->y += GPOS_get_device_table_value(DeviceTableY, ppem); 1626 } 1627 break; 1628 } 1629 default: 1630 ERR("Unknown Anchor Format %i\n",GET_BE_WORD(anchor1->AnchorFormat)); 1631 pt->x = 0; 1632 pt->y = 0; 1633 } 1634 } 1635 1636 static void GPOS_convert_design_units_to_device(const OUTLINETEXTMETRICW *otm, const LOGFONTW *logfont, 1637 int desX, int desY, double *devX, double *devY) 1638 { 1639 int emHeight = otm->otmTextMetrics.tmAscent + otm->otmTextMetrics.tmDescent - otm->otmTextMetrics.tmInternalLeading; 1640 1641 TRACE("emHeight %i lfWidth %i\n",emHeight, logfont->lfWidth); 1642 *devX = (desX * emHeight) / (double)otm->otmEMSquare; 1643 *devY = (desY * emHeight) / (double)otm->otmEMSquare; 1644 if (logfont->lfWidth) 1645 FIXME("Font with lfWidth set not handled properly.\n"); 1646 } 1647 1648 static INT GPOS_get_value_record(WORD ValueFormat, const WORD data[], GPOS_ValueRecord *record) 1649 { 1650 INT offset = 0; 1651 if (ValueFormat & 0x0001) { if (data) record->XPlacement = GET_BE_WORD(data[offset]); offset++; } 1652 if (ValueFormat & 0x0002) { if (data) record->YPlacement = GET_BE_WORD(data[offset]); offset++; } 1653 if (ValueFormat & 0x0004) { if (data) record->XAdvance = GET_BE_WORD(data[offset]); offset++; } 1654 if (ValueFormat & 0x0008) { if (data) record->YAdvance = GET_BE_WORD(data[offset]); offset++; } 1655 if (ValueFormat & 0x0010) { if (data) record->XPlaDevice = GET_BE_WORD(data[offset]); offset++; } 1656 if (ValueFormat & 0x0020) { if (data) record->YPlaDevice = GET_BE_WORD(data[offset]); offset++; } 1657 if (ValueFormat & 0x0040) { if (data) record->XAdvDevice = GET_BE_WORD(data[offset]); offset++; } 1658 if (ValueFormat & 0x0080) { if (data) record->YAdvDevice = GET_BE_WORD(data[offset]); offset++; } 1659 return offset; 1660 } 1661 1662 static void GPOS_get_value_record_offsets(const BYTE *head, GPOS_ValueRecord *ValueRecord, 1663 WORD ValueFormat, unsigned int ppem, POINT *ptPlacement, POINT *ptAdvance) 1664 { 1665 if (ValueFormat & 0x0001) ptPlacement->x += (short)ValueRecord->XPlacement; 1666 if (ValueFormat & 0x0002) ptPlacement->y += (short)ValueRecord->YPlacement; 1667 if (ValueFormat & 0x0004) ptAdvance->x += (short)ValueRecord->XAdvance; 1668 if (ValueFormat & 0x0008) ptAdvance->y += (short)ValueRecord->YAdvance; 1669 if (ValueFormat & 0x0010) ptPlacement->x += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->XPlaDevice), ppem); 1670 if (ValueFormat & 0x0020) ptPlacement->y += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->YPlaDevice), ppem); 1671 if (ValueFormat & 0x0040) ptAdvance->x += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->XAdvDevice), ppem); 1672 if (ValueFormat & 0x0080) ptAdvance->y += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->YAdvDevice), ppem); 1673 if (ValueFormat & 0xFF00) FIXME("Unhandled Value Format %x\n",ValueFormat&0xFF00); 1674 } 1675 1676 static const BYTE *GPOS_get_subtable(const OT_LookupTable *look, int index) 1677 { 1678 int offset = GET_BE_WORD(look->SubTable[index]); 1679 1680 if (GET_BE_WORD(look->LookupType) == GPOS_LOOKUP_POSITION_EXTENSION) 1681 { 1682 const GPOS_ExtensionPosFormat1 *ext = (const GPOS_ExtensionPosFormat1 *)((const BYTE *)look + offset); 1683 if (GET_BE_WORD(ext->PosFormat) == 1) 1684 { 1685 offset += GET_BE_DWORD(ext->ExtensionOffset); 1686 } 1687 else 1688 { 1689 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext->PosFormat)); 1690 } 1691 } 1692 return (const BYTE *)look + offset; 1693 } 1694 1695 static void GPOS_apply_SingleAdjustment(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, 1696 const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, unsigned int ppem, 1697 POINT *adjust, POINT *advance) 1698 { 1699 int j; 1700 1701 TRACE("Single Adjustment Positioning Subtable\n"); 1702 1703 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 1704 { 1705 const GPOS_SinglePosFormat1 *spf1 = (const GPOS_SinglePosFormat1*)GPOS_get_subtable(look, j); 1706 WORD offset; 1707 if (GET_BE_WORD(spf1->PosFormat) == 1) 1708 { 1709 offset = GET_BE_WORD(spf1->Coverage); 1710 if (GSUB_is_glyph_covered((const BYTE*)spf1+offset, glyphs[glyph_index]) != -1) 1711 { 1712 GPOS_ValueRecord ValueRecord = {0,0,0,0,0,0,0,0}; 1713 WORD ValueFormat = GET_BE_WORD(spf1->ValueFormat); 1714 GPOS_get_value_record(ValueFormat, spf1->Value, &ValueRecord); 1715 GPOS_get_value_record_offsets((const BYTE *)spf1, &ValueRecord, ValueFormat, ppem, adjust, advance); 1716 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord.XPlacement,ValueRecord.YPlacement); 1717 } 1718 } 1719 else if (GET_BE_WORD(spf1->PosFormat) == 2) 1720 { 1721 int index; 1722 const GPOS_SinglePosFormat2 *spf2; 1723 spf2 = (const GPOS_SinglePosFormat2*)spf1; 1724 offset = GET_BE_WORD(spf2->Coverage); 1725 index = GSUB_is_glyph_covered((const BYTE*)spf2+offset, glyphs[glyph_index]); 1726 if (index != -1) 1727 { 1728 int size; 1729 GPOS_ValueRecord ValueRecord = {0,0,0,0,0,0,0,0}; 1730 WORD ValueFormat = GET_BE_WORD(spf2->ValueFormat); 1731 size = GPOS_get_value_record(ValueFormat, spf2->Value, &ValueRecord); 1732 if (index > 0) 1733 { 1734 offset = size * index; 1735 GPOS_get_value_record(ValueFormat, &spf2->Value[offset], &ValueRecord); 1736 } 1737 GPOS_get_value_record_offsets((const BYTE *)spf2, &ValueRecord, ValueFormat, ppem, adjust, advance); 1738 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord.XPlacement,ValueRecord.YPlacement); 1739 } 1740 } 1741 else 1742 FIXME("Single Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(spf1->PosFormat)); 1743 } 1744 } 1745 1746 static void apply_pair_value( const void *pos_table, WORD val_fmt1, WORD val_fmt2, const WORD *pair, 1747 INT ppem, POINT *adjust, POINT *advance ) 1748 { 1749 GPOS_ValueRecord val_rec1 = {0,0,0,0,0,0,0,0}; 1750 GPOS_ValueRecord val_rec2 = {0,0,0,0,0,0,0,0}; 1751 INT size; 1752 1753 size = GPOS_get_value_record( val_fmt1, pair, &val_rec1 ); 1754 GPOS_get_value_record( val_fmt2, pair + size, &val_rec2 ); 1755 1756 if (val_fmt1) 1757 { 1758 GPOS_get_value_record_offsets( pos_table, &val_rec1, val_fmt1, ppem, adjust, advance ); 1759 TRACE( "Glyph 1 resulting cumulative offset is %s design units\n", wine_dbgstr_point(&adjust[0]) ); 1760 TRACE( "Glyph 1 resulting cumulative advance is %s design units\n", wine_dbgstr_point(&advance[0]) ); 1761 } 1762 if (val_fmt2) 1763 { 1764 GPOS_get_value_record_offsets( pos_table, &val_rec2, val_fmt2, ppem, adjust + 1, advance + 1 ); 1765 TRACE( "Glyph 2 resulting cumulative offset is %s design units\n", wine_dbgstr_point(&adjust[1]) ); 1766 TRACE( "Glyph 2 resulting cumulative advance is %s design units\n", wine_dbgstr_point(&advance[1]) ); 1767 } 1768 } 1769 1770 static int GPOS_apply_PairAdjustment(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, 1771 const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, unsigned int ppem, 1772 POINT *adjust, POINT *advance) 1773 { 1774 int j; 1775 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 1776 1777 if (glyph_index + write_dir >= glyph_count) 1778 return 1; 1779 1780 TRACE("Pair Adjustment Positioning Subtable\n"); 1781 1782 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 1783 { 1784 const GPOS_PairPosFormat1 *ppf1 = (const GPOS_PairPosFormat1*)GPOS_get_subtable(look, j); 1785 WORD offset; 1786 if (GET_BE_WORD(ppf1->PosFormat) == 1) 1787 { 1788 int index; 1789 WORD ValueFormat1 = GET_BE_WORD(ppf1->ValueFormat1); 1790 WORD ValueFormat2 = GET_BE_WORD(ppf1->ValueFormat2); 1791 INT val_fmt1_size = GPOS_get_value_record( ValueFormat1, NULL, NULL ); 1792 INT val_fmt2_size = GPOS_get_value_record( ValueFormat2, NULL, NULL ); 1793 offset = GET_BE_WORD(ppf1->Coverage); 1794 index = GSUB_is_glyph_covered((const BYTE*)ppf1+offset, glyphs[glyph_index]); 1795 if (index != -1 && index < GET_BE_WORD(ppf1->PairSetCount)) 1796 { 1797 int k; 1798 int pair_count; 1799 const GPOS_PairSet *ps; 1800 const GPOS_PairValueRecord *pair_val_rec; 1801 offset = GET_BE_WORD(ppf1->PairSetOffset[index]); 1802 ps = (const GPOS_PairSet*)((const BYTE*)ppf1+offset); 1803 pair_count = GET_BE_WORD(ps->PairValueCount); 1804 pair_val_rec = ps->PairValueRecord; 1805 for (k = 0; k < pair_count; k++) 1806 { 1807 WORD second_glyph = GET_BE_WORD(pair_val_rec->SecondGlyph); 1808 if (glyphs[glyph_index+write_dir] == second_glyph) 1809 { 1810 int next = 1; 1811 TRACE("Format 1: Found Pair %x,%x\n",glyphs[glyph_index],glyphs[glyph_index+write_dir]); 1812 apply_pair_value(ppf1, ValueFormat1, ValueFormat2, 1813 pair_val_rec->Value1, ppem, adjust, advance); 1814 if (ValueFormat2) next++; 1815 return next; 1816 } 1817 pair_val_rec = (const GPOS_PairValueRecord *)(pair_val_rec->Value1 + val_fmt1_size + val_fmt2_size); 1818 } 1819 } 1820 } 1821 else if (GET_BE_WORD(ppf1->PosFormat) == 2) 1822 { 1823 const GPOS_PairPosFormat2 *ppf2 = (const GPOS_PairPosFormat2*)ppf1; 1824 int index; 1825 WORD ValueFormat1 = GET_BE_WORD( ppf2->ValueFormat1 ); 1826 WORD ValueFormat2 = GET_BE_WORD( ppf2->ValueFormat2 ); 1827 INT val_fmt1_size = GPOS_get_value_record( ValueFormat1, NULL, NULL ); 1828 INT val_fmt2_size = GPOS_get_value_record( ValueFormat2, NULL, NULL ); 1829 WORD class1_count = GET_BE_WORD( ppf2->Class1Count ); 1830 WORD class2_count = GET_BE_WORD( ppf2->Class2Count ); 1831 1832 offset = GET_BE_WORD( ppf2->Coverage ); 1833 index = GSUB_is_glyph_covered( (const BYTE*)ppf2 + offset, glyphs[glyph_index] ); 1834 if (index != -1) 1835 { 1836 WORD class1, class2; 1837 class1 = OT_get_glyph_class( (const BYTE *)ppf2 + GET_BE_WORD(ppf2->ClassDef1), glyphs[glyph_index] ); 1838 class2 = OT_get_glyph_class( (const BYTE *)ppf2 + GET_BE_WORD(ppf2->ClassDef2), glyphs[glyph_index + write_dir] ); 1839 if (class1 < class1_count && class2 < class2_count) 1840 { 1841 const WORD *pair_val = ppf2->Class1Record + (class1 * class2_count + class2) * (val_fmt1_size + val_fmt2_size); 1842 int next = 1; 1843 1844 TRACE( "Format 2: Found Pair %x,%x\n", glyphs[glyph_index], glyphs[glyph_index + write_dir] ); 1845 1846 apply_pair_value(ppf2, ValueFormat1, ValueFormat2, pair_val, ppem, adjust, advance); 1847 if (ValueFormat2) next++; 1848 return next; 1849 } 1850 } 1851 } 1852 else 1853 FIXME("Pair Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(ppf1->PosFormat)); 1854 } 1855 return 1; 1856 } 1857 1858 static void GPOS_apply_CursiveAttachment(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, 1859 const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, unsigned int ppem, POINT *pt) 1860 { 1861 int j; 1862 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 1863 1864 if (glyph_index + write_dir >= glyph_count) 1865 return; 1866 1867 TRACE("Cursive Attachment Positioning Subtable\n"); 1868 1869 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 1870 { 1871 const GPOS_CursivePosFormat1 *cpf1 = (const GPOS_CursivePosFormat1 *)GPOS_get_subtable(look, j); 1872 if (GET_BE_WORD(cpf1->PosFormat) == 1) 1873 { 1874 int index_exit, index_entry; 1875 WORD offset = GET_BE_WORD( cpf1->Coverage ); 1876 index_exit = GSUB_is_glyph_covered((const BYTE*)cpf1+offset, glyphs[glyph_index]); 1877 if (index_exit != -1 && cpf1->EntryExitRecord[index_exit].ExitAnchor!= 0) 1878 { 1879 index_entry = GSUB_is_glyph_covered((const BYTE*)cpf1+offset, glyphs[glyph_index+write_dir]); 1880 if (index_entry != -1 && cpf1->EntryExitRecord[index_entry].EntryAnchor != 0) 1881 { 1882 POINT exit_pt, entry_pt; 1883 offset = GET_BE_WORD(cpf1->EntryExitRecord[index_exit].ExitAnchor); 1884 GPOS_get_anchor_values((const BYTE*)cpf1 + offset, &exit_pt, ppem); 1885 offset = GET_BE_WORD(cpf1->EntryExitRecord[index_entry].EntryAnchor); 1886 GPOS_get_anchor_values((const BYTE*)cpf1 + offset, &entry_pt, ppem); 1887 TRACE("Found linkage %x[%s] %x[%s]\n",glyphs[glyph_index], wine_dbgstr_point(&exit_pt), glyphs[glyph_index+write_dir], wine_dbgstr_point(&entry_pt)); 1888 pt->x = entry_pt.x - exit_pt.x; 1889 pt->y = entry_pt.y - exit_pt.y; 1890 return; 1891 } 1892 } 1893 } 1894 else 1895 FIXME("Cursive Attachment Positioning: Format %i Unhandled\n",GET_BE_WORD(cpf1->PosFormat)); 1896 } 1897 return; 1898 } 1899 1900 static int GPOS_apply_MarkToBase(const ScriptCache *script_cache, const OT_LookupTable *look, 1901 const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, unsigned int glyph_index, 1902 unsigned int glyph_count, unsigned int ppem, POINT *pt) 1903 { 1904 int j; 1905 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 1906 const void *glyph_class_table = NULL; 1907 int rc = -1; 1908 1909 if (script_cache->GDEF_Table) 1910 { 1911 const GDEF_Header *header = script_cache->GDEF_Table; 1912 WORD offset = GET_BE_WORD( header->GlyphClassDef ); 1913 if (offset) 1914 glyph_class_table = (const BYTE *)script_cache->GDEF_Table + offset; 1915 } 1916 1917 TRACE("MarkToBase Attachment Positioning Subtable\n"); 1918 1919 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 1920 { 1921 const GPOS_MarkBasePosFormat1 *mbpf1 = (const GPOS_MarkBasePosFormat1 *)GPOS_get_subtable(look, j); 1922 if (GET_BE_WORD(mbpf1->PosFormat) == 1) 1923 { 1924 int offset = GET_BE_WORD(mbpf1->MarkCoverage); 1925 int mark_index; 1926 mark_index = GSUB_is_glyph_covered((const BYTE*)mbpf1+offset, glyphs[glyph_index]); 1927 if (mark_index != -1) 1928 { 1929 int base_index; 1930 int base_glyph = glyph_index - write_dir; 1931 1932 if (glyph_class_table) 1933 { 1934 while (OT_get_glyph_class(glyph_class_table, glyphs[base_glyph]) == MarkGlyph && base_glyph > 0 && base_glyph < glyph_count) 1935 base_glyph -= write_dir; 1936 } 1937 1938 offset = GET_BE_WORD(mbpf1->BaseCoverage); 1939 base_index = GSUB_is_glyph_covered((const BYTE*)mbpf1+offset, glyphs[base_glyph]); 1940 if (base_index != -1) 1941 { 1942 const GPOS_MarkArray *ma; 1943 const GPOS_MarkRecord *mr; 1944 const GPOS_BaseArray *ba; 1945 const GPOS_BaseRecord *br; 1946 int mark_class; 1947 int class_count = GET_BE_WORD(mbpf1->ClassCount); 1948 int baserecord_size; 1949 POINT base_pt; 1950 POINT mark_pt; 1951 TRACE("Mark %x(%i) and base %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[base_glyph], base_index); 1952 offset = GET_BE_WORD(mbpf1->MarkArray); 1953 ma = (const GPOS_MarkArray*)((const BYTE*)mbpf1 + offset); 1954 if (mark_index > GET_BE_WORD(ma->MarkCount)) 1955 { 1956 ERR("Mark index exceeded mark count\n"); 1957 return -1; 1958 } 1959 mr = &ma->MarkRecord[mark_index]; 1960 mark_class = GET_BE_WORD(mr->Class); 1961 TRACE("Mark Class %i total classes %i\n",mark_class,class_count); 1962 offset = GET_BE_WORD(mbpf1->BaseArray); 1963 ba = (const GPOS_BaseArray*)((const BYTE*)mbpf1 + offset); 1964 baserecord_size = class_count * sizeof(WORD); 1965 br = (const GPOS_BaseRecord*)((const BYTE*)ba + sizeof(WORD) + (baserecord_size * base_index)); 1966 offset = GET_BE_WORD(br->BaseAnchor[mark_class]); 1967 GPOS_get_anchor_values((const BYTE*)ba + offset, &base_pt, ppem); 1968 offset = GET_BE_WORD(mr->MarkAnchor); 1969 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem); 1970 TRACE("Offset on base is %s design units\n",wine_dbgstr_point(&base_pt)); 1971 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt)); 1972 pt->x += base_pt.x - mark_pt.x; 1973 pt->y += base_pt.y - mark_pt.y; 1974 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt)); 1975 rc = base_glyph; 1976 } 1977 } 1978 } 1979 else 1980 FIXME("Unhandled Mark To Base Format %i\n",GET_BE_WORD(mbpf1->PosFormat)); 1981 } 1982 return rc; 1983 } 1984 1985 static void GPOS_apply_MarkToLigature(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, 1986 const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, unsigned int ppem, POINT *pt) 1987 { 1988 int j; 1989 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 1990 1991 TRACE("MarkToLigature Attachment Positioning Subtable\n"); 1992 1993 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 1994 { 1995 const GPOS_MarkLigPosFormat1 *mlpf1 = (const GPOS_MarkLigPosFormat1 *)GPOS_get_subtable(look, j); 1996 if (GET_BE_WORD(mlpf1->PosFormat) == 1) 1997 { 1998 int offset = GET_BE_WORD(mlpf1->MarkCoverage); 1999 int mark_index; 2000 mark_index = GSUB_is_glyph_covered((const BYTE*)mlpf1+offset, glyphs[glyph_index]); 2001 if (mark_index != -1) 2002 { 2003 int ligature_index; 2004 offset = GET_BE_WORD(mlpf1->LigatureCoverage); 2005 ligature_index = GSUB_is_glyph_covered((const BYTE*)mlpf1+offset, glyphs[glyph_index - write_dir]); 2006 if (ligature_index != -1) 2007 { 2008 const GPOS_MarkArray *ma; 2009 const GPOS_MarkRecord *mr; 2010 2011 const GPOS_LigatureArray *la; 2012 const GPOS_LigatureAttach *lt; 2013 int mark_class; 2014 int class_count = GET_BE_WORD(mlpf1->ClassCount); 2015 int component_count; 2016 int component_size; 2017 int i; 2018 POINT ligature_pt; 2019 POINT mark_pt; 2020 2021 TRACE("Mark %x(%i) and ligature %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], ligature_index); 2022 offset = GET_BE_WORD(mlpf1->MarkArray); 2023 ma = (const GPOS_MarkArray*)((const BYTE*)mlpf1 + offset); 2024 if (mark_index > GET_BE_WORD(ma->MarkCount)) 2025 { 2026 ERR("Mark index exceeded mark count\n"); 2027 return; 2028 } 2029 mr = &ma->MarkRecord[mark_index]; 2030 mark_class = GET_BE_WORD(mr->Class); 2031 TRACE("Mark Class %i total classes %i\n",mark_class,class_count); 2032 offset = GET_BE_WORD(mlpf1->LigatureArray); 2033 la = (const GPOS_LigatureArray*)((const BYTE*)mlpf1 + offset); 2034 if (ligature_index > GET_BE_WORD(la->LigatureCount)) 2035 { 2036 ERR("Ligature index exceeded ligature count\n"); 2037 return; 2038 } 2039 offset = GET_BE_WORD(la->LigatureAttach[ligature_index]); 2040 lt = (const GPOS_LigatureAttach*)((const BYTE*)la + offset); 2041 2042 component_count = GET_BE_WORD(lt->ComponentCount); 2043 component_size = class_count * sizeof(WORD); 2044 offset = 0; 2045 for (i = 0; i < component_count && !offset; i++) 2046 { 2047 int k; 2048 const GPOS_ComponentRecord *cr = (const GPOS_ComponentRecord*)((const BYTE*)lt->ComponentRecord + (component_size * i)); 2049 for (k = 0; k < class_count && !offset; k++) 2050 offset = GET_BE_WORD(cr->LigatureAnchor[k]); 2051 cr = (const GPOS_ComponentRecord*)((const BYTE*)cr + component_size); 2052 } 2053 if (!offset) 2054 { 2055 ERR("Failed to find available ligature connection point\n"); 2056 return; 2057 } 2058 2059 GPOS_get_anchor_values((const BYTE*)lt + offset, &ligature_pt, ppem); 2060 offset = GET_BE_WORD(mr->MarkAnchor); 2061 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem); 2062 TRACE("Offset on ligature is %s design units\n",wine_dbgstr_point(&ligature_pt)); 2063 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt)); 2064 pt->x += ligature_pt.x - mark_pt.x; 2065 pt->y += ligature_pt.y - mark_pt.y; 2066 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt)); 2067 } 2068 } 2069 } 2070 else 2071 FIXME("Unhandled Mark To Ligature Format %i\n",GET_BE_WORD(mlpf1->PosFormat)); 2072 } 2073 } 2074 2075 static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, 2076 const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, unsigned int ppem, POINT *pt) 2077 { 2078 int j; 2079 BOOL rc = FALSE; 2080 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 2081 2082 TRACE("MarkToMark Attachment Positioning Subtable\n"); 2083 2084 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 2085 { 2086 const GPOS_MarkMarkPosFormat1 *mmpf1 = (const GPOS_MarkMarkPosFormat1 *)GPOS_get_subtable(look, j); 2087 if (GET_BE_WORD(mmpf1->PosFormat) == 1) 2088 { 2089 int offset = GET_BE_WORD(mmpf1->Mark1Coverage); 2090 int mark_index; 2091 mark_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[glyph_index]); 2092 if (mark_index != -1) 2093 { 2094 int mark2_index; 2095 offset = GET_BE_WORD(mmpf1->Mark2Coverage); 2096 mark2_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[glyph_index - write_dir]); 2097 if (mark2_index != -1) 2098 { 2099 const GPOS_MarkArray *ma; 2100 const GPOS_MarkRecord *mr; 2101 const GPOS_Mark2Array *m2a; 2102 const GPOS_Mark2Record *m2r; 2103 int mark_class; 2104 int class_count = GET_BE_WORD(mmpf1->ClassCount); 2105 int mark2record_size; 2106 POINT mark2_pt; 2107 POINT mark_pt; 2108 TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], mark2_index); 2109 offset = GET_BE_WORD(mmpf1->Mark1Array); 2110 ma = (const GPOS_MarkArray*)((const BYTE*)mmpf1 + offset); 2111 if (mark_index > GET_BE_WORD(ma->MarkCount)) 2112 { 2113 ERR("Mark index exceeded mark count\n"); 2114 return FALSE; 2115 } 2116 mr = &ma->MarkRecord[mark_index]; 2117 mark_class = GET_BE_WORD(mr->Class); 2118 TRACE("Mark Class %i total classes %i\n",mark_class,class_count); 2119 offset = GET_BE_WORD(mmpf1->Mark2Array); 2120 m2a = (const GPOS_Mark2Array*)((const BYTE*)mmpf1 + offset); 2121 mark2record_size = class_count * sizeof(WORD); 2122 m2r = (const GPOS_Mark2Record*)((const BYTE*)m2a + sizeof(WORD) + (mark2record_size * mark2_index)); 2123 offset = GET_BE_WORD(m2r->Mark2Anchor[mark_class]); 2124 GPOS_get_anchor_values((const BYTE*)m2a + offset, &mark2_pt, ppem); 2125 offset = GET_BE_WORD(mr->MarkAnchor); 2126 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem); 2127 TRACE("Offset on mark2 is %s design units\n",wine_dbgstr_point(&mark2_pt)); 2128 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt)); 2129 pt->x += mark2_pt.x - mark_pt.x; 2130 pt->y += mark2_pt.y - mark_pt.y; 2131 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt)); 2132 rc = TRUE; 2133 } 2134 } 2135 } 2136 else 2137 FIXME("Unhandled Mark To Mark Format %i\n",GET_BE_WORD(mmpf1->PosFormat)); 2138 } 2139 return rc; 2140 } 2141 2142 static unsigned int GPOS_apply_ContextPos(const ScriptCache *script_cache, const OUTLINETEXTMETRICW *otm, 2143 const LOGFONTW *logfont, const SCRIPT_ANALYSIS *analysis, int *advance, const OT_LookupList *lookup, 2144 const OT_LookupTable *look, const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, 2145 GOFFSET *goffset) 2146 { 2147 int j; 2148 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 2149 2150 TRACE("Contextual Positioning Subtable\n"); 2151 2152 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 2153 { 2154 const GPOS_ContextPosFormat2 *cpf2 = (GPOS_ContextPosFormat2*)GPOS_get_subtable(look, j); 2155 2156 if (GET_BE_WORD(cpf2->PosFormat) == 1) 2157 { 2158 static int once; 2159 if (!once++) 2160 FIXME(" TODO: subtype 1\n"); 2161 continue; 2162 } 2163 else if (GET_BE_WORD(cpf2->PosFormat) == 2) 2164 { 2165 WORD offset = GET_BE_WORD(cpf2->Coverage); 2166 int index; 2167 2168 TRACE("Contextual Positioning Subtable: Format 2\n"); 2169 2170 index = GSUB_is_glyph_covered((const BYTE*)cpf2+offset, glyphs[glyph_index]); 2171 TRACE("Coverage index %i\n",index); 2172 if (index != -1) 2173 { 2174 int k, count, class; 2175 const GPOS_PosClassSet *pcs; 2176 const void *glyph_class_table = NULL; 2177 2178 offset = GET_BE_WORD(cpf2->ClassDef); 2179 glyph_class_table = (const BYTE *)cpf2 + offset; 2180 2181 class = OT_get_glyph_class(glyph_class_table,glyphs[glyph_index]); 2182 2183 offset = GET_BE_WORD(cpf2->PosClassSet[class]); 2184 if (offset == 0) 2185 { 2186 TRACE("No class rule table for class %i\n",class); 2187 continue; 2188 } 2189 pcs = (const GPOS_PosClassSet*)((const BYTE*)cpf2+offset); 2190 count = GET_BE_WORD(pcs->PosClassRuleCnt); 2191 TRACE("PosClassSet has %i members\n",count); 2192 for (k = 0; k < count; k++) 2193 { 2194 const GPOS_PosClassRule_1 *pr; 2195 const GPOS_PosClassRule_2 *pr_2; 2196 unsigned int g; 2197 int g_count, l; 2198 2199 offset = GET_BE_WORD(pcs->PosClassRule[k]); 2200 pr = (const GPOS_PosClassRule_1*)((const BYTE*)pcs+offset); 2201 g_count = GET_BE_WORD(pr->GlyphCount); 2202 TRACE("PosClassRule has %i glyphs classes\n",g_count); 2203 2204 g = glyph_index + write_dir * (g_count - 1); 2205 if (g >= glyph_count) 2206 continue; 2207 2208 for (l = 0; l < g_count-1; l++) 2209 { 2210 int g_class = OT_get_glyph_class(glyph_class_table, glyphs[glyph_index + (write_dir * (l+1))]); 2211 if (g_class != GET_BE_WORD(pr->Class[l])) break; 2212 } 2213 2214 if (l < g_count-1) 2215 { 2216 TRACE("Rule does not match\n"); 2217 continue; 2218 } 2219 2220 TRACE("Rule matches\n"); 2221 pr_2 = (const GPOS_PosClassRule_2 *)&pr->Class[g_count - 1]; 2222 2223 for (l = 0; l < GET_BE_WORD(pr->PosCount); l++) 2224 { 2225 unsigned int lookup_index = GET_BE_WORD(pr_2->PosLookupRecord[l].LookupListIndex); 2226 unsigned int sequence_index = GET_BE_WORD(pr_2->PosLookupRecord[l].SequenceIndex); 2227 2228 g = glyph_index + write_dir * sequence_index; 2229 if (g >= glyph_count) 2230 { 2231 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n", 2232 sequence_index, glyph_index, write_dir); 2233 continue; 2234 } 2235 2236 TRACE("Position: %u -> %u %u.\n", l, sequence_index, lookup_index); 2237 2238 GPOS_apply_lookup(script_cache, otm, logfont, analysis, advance, 2239 lookup, lookup_index, glyphs, g, glyph_count, goffset); 2240 } 2241 return 1; 2242 } 2243 } 2244 2245 TRACE("Not covered\n"); 2246 continue; 2247 } 2248 else if (GET_BE_WORD(cpf2->PosFormat) == 3) 2249 { 2250 static int once; 2251 if (!once++) 2252 FIXME(" TODO: subtype 3\n"); 2253 continue; 2254 } 2255 else 2256 FIXME("Unhandled Contextual Positioning Format %i\n",GET_BE_WORD(cpf2->PosFormat)); 2257 } 2258 return 1; 2259 } 2260 2261 static unsigned int GPOS_apply_ChainContextPos(const ScriptCache *script_cache, const OUTLINETEXTMETRICW *otm, 2262 const LOGFONTW *logfont, const SCRIPT_ANALYSIS *analysis, int *advance, const OT_LookupList *lookup, 2263 const OT_LookupTable *look, const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, 2264 GOFFSET *goffset) 2265 { 2266 int j; 2267 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 2268 2269 TRACE("Chaining Contextual Positioning Subtable\n"); 2270 2271 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) 2272 { 2273 int offset; 2274 const GPOS_ChainContextPosFormat3_1 *backtrack = (GPOS_ChainContextPosFormat3_1 *)GPOS_get_subtable(look, j); 2275 int dirLookahead = write_dir; 2276 int dirBacktrack = -1 * write_dir; 2277 2278 if (GET_BE_WORD(backtrack->PosFormat) == 1) 2279 { 2280 static int once; 2281 if (!once++) 2282 FIXME(" TODO: subtype 1 (Simple Chaining Context Glyph Positioning)\n"); 2283 continue; 2284 } 2285 else if (GET_BE_WORD(backtrack->PosFormat) == 2) 2286 { 2287 static int once; 2288 if (!once++) 2289 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Positioning)\n"); 2290 continue; 2291 } 2292 else if (GET_BE_WORD(backtrack->PosFormat) == 3) 2293 { 2294 WORD backtrack_count, input_count, lookahead_count, positioning_count; 2295 int k; 2296 const GPOS_ChainContextPosFormat3_2 *input; 2297 const GPOS_ChainContextPosFormat3_3 *lookahead; 2298 const GPOS_ChainContextPosFormat3_4 *positioning; 2299 2300 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Positioning)\n"); 2301 2302 backtrack_count = GET_BE_WORD(backtrack->BacktrackGlyphCount); 2303 k = glyph_index + dirBacktrack * backtrack_count; 2304 if (k < 0 || k >= glyph_count) 2305 continue; 2306 2307 input = (const GPOS_ChainContextPosFormat3_2 *)&backtrack->Coverage[backtrack_count]; 2308 input_count = GET_BE_WORD(input->InputGlyphCount); 2309 k = glyph_index + write_dir * (input_count - 1); 2310 if (k < 0 || k >= glyph_count) 2311 continue; 2312 2313 lookahead = (const GPOS_ChainContextPosFormat3_3 *)&input->Coverage[input_count]; 2314 lookahead_count = GET_BE_WORD(lookahead->LookaheadGlyphCount); 2315 k = glyph_index + dirLookahead * (input_count + lookahead_count - 1); 2316 if (k < 0 || k >= glyph_count) 2317 continue; 2318 2319 positioning = (const GPOS_ChainContextPosFormat3_4 *)&lookahead->Coverage[lookahead_count]; 2320 2321 for (k = 0; k < backtrack_count; ++k) 2322 { 2323 offset = GET_BE_WORD(backtrack->Coverage[k]); 2324 if (GSUB_is_glyph_covered((const BYTE *)backtrack + offset, 2325 glyphs[glyph_index + (dirBacktrack * (k + 1))]) == -1) 2326 break; 2327 } 2328 if (k != backtrack_count) 2329 continue; 2330 TRACE("Matched Backtrack\n"); 2331 2332 for (k = 0; k < input_count; ++k) 2333 { 2334 offset = GET_BE_WORD(input->Coverage[k]); 2335 if (GSUB_is_glyph_covered((const BYTE *)backtrack + offset, 2336 glyphs[glyph_index + (write_dir * k)]) == -1) 2337 break; 2338 } 2339 if (k != input_count) 2340 continue; 2341 TRACE("Matched IndexGlyphs\n"); 2342 2343 for (k = 0; k < lookahead_count; ++k) 2344 { 2345 offset = GET_BE_WORD(lookahead->Coverage[k]); 2346 if (GSUB_is_glyph_covered((const BYTE *)backtrack + offset, 2347 glyphs[glyph_index + (dirLookahead * (input_count + k))]) == -1) 2348 break; 2349 } 2350 if (k != lookahead_count) 2351 continue; 2352 TRACE("Matched LookAhead\n"); 2353 2354 if (!(positioning_count = GET_BE_WORD(positioning->PosCount))) 2355 return 1; 2356 2357 for (k = 0; k < positioning_count; ++k) 2358 { 2359 unsigned int lookup_index = GET_BE_WORD(positioning->PosLookupRecord[k].LookupListIndex); 2360 unsigned int sequence_index = GET_BE_WORD(positioning->PosLookupRecord[k].SequenceIndex); 2361 unsigned int g = glyph_index + write_dir * sequence_index; 2362 2363 if (g >= glyph_count) 2364 { 2365 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n", 2366 sequence_index, glyph_index, write_dir); 2367 continue; 2368 } 2369 2370 TRACE("Position: %u -> %u %u.\n", k, sequence_index, lookup_index); 2371 GPOS_apply_lookup(script_cache, otm, logfont, analysis, advance, lookup, lookup_index, 2372 glyphs, g, glyph_count, goffset); 2373 } 2374 return input_count + lookahead_count; 2375 } 2376 else 2377 FIXME("Unhandled Chaining Contextual Positioning Format %#x.\n", GET_BE_WORD(backtrack->PosFormat)); 2378 } 2379 return 1; 2380 } 2381 2382 static unsigned int GPOS_apply_lookup(const ScriptCache *script_cache, const OUTLINETEXTMETRICW *lpotm, 2383 const LOGFONTW *lplogfont, const SCRIPT_ANALYSIS *analysis, int *piAdvance, const OT_LookupList *lookup, 2384 unsigned int lookup_index, const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, 2385 GOFFSET *pGoffset) 2386 { 2387 int offset; 2388 const OT_LookupTable *look; 2389 int ppem = lpotm->otmTextMetrics.tmAscent + lpotm->otmTextMetrics.tmDescent - lpotm->otmTextMetrics.tmInternalLeading; 2390 enum gpos_lookup_type type; 2391 2392 offset = GET_BE_WORD(lookup->Lookup[lookup_index]); 2393 look = (const OT_LookupTable*)((const BYTE*)lookup + offset); 2394 type = GET_BE_WORD(look->LookupType); 2395 TRACE("type %#x, flag %#x, subtables %u.\n", type, 2396 GET_BE_WORD(look->LookupFlag), GET_BE_WORD(look->SubTableCount)); 2397 2398 if (type == GPOS_LOOKUP_POSITION_EXTENSION) 2399 { 2400 if (GET_BE_WORD(look->SubTableCount)) 2401 { 2402 const GPOS_ExtensionPosFormat1 *ext = (const GPOS_ExtensionPosFormat1 *)((const BYTE *)look + GET_BE_WORD(look->SubTable[0])); 2403 if (GET_BE_WORD(ext->PosFormat) == 1) 2404 { 2405 type = GET_BE_WORD(ext->ExtensionLookupType); 2406 TRACE("extension type %i\n",type); 2407 } 2408 else 2409 { 2410 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext->PosFormat)); 2411 } 2412 } 2413 else 2414 { 2415 WARN("lookup type is Extension Positioning but no extension subtable exists\n"); 2416 } 2417 } 2418 switch (type) 2419 { 2420 case GPOS_LOOKUP_ADJUST_SINGLE: 2421 { 2422 double devX, devY; 2423 POINT adjust = {0,0}; 2424 POINT advance = {0,0}; 2425 GPOS_apply_SingleAdjustment(look, analysis, glyphs, glyph_index, glyph_count, ppem, &adjust, &advance); 2426 if (adjust.x || adjust.y) 2427 { 2428 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust.x, adjust.y, &devX, &devY); 2429 pGoffset[glyph_index].du += round(devX); 2430 pGoffset[glyph_index].dv += round(devY); 2431 } 2432 if (advance.x || advance.y) 2433 { 2434 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance.x, advance.y, &devX, &devY); 2435 piAdvance[glyph_index] += round(devX); 2436 if (advance.y) 2437 FIXME("Unhandled adjustment to Y advancement\n"); 2438 } 2439 break; 2440 } 2441 2442 case GPOS_LOOKUP_ADJUST_PAIR: 2443 { 2444 POINT advance[2]= {{0,0},{0,0}}; 2445 POINT adjust[2]= {{0,0},{0,0}}; 2446 double devX, devY; 2447 int index_offset; 2448 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 2449 int offset_sign = (analysis->fRTL && analysis->fLogicalOrder) ? -1 : 1; 2450 2451 index_offset = GPOS_apply_PairAdjustment(look, analysis, glyphs, 2452 glyph_index, glyph_count, ppem, adjust, advance); 2453 if (adjust[0].x || adjust[0].y) 2454 { 2455 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust[0].x, adjust[0].y, &devX, &devY); 2456 pGoffset[glyph_index].du += round(devX) * offset_sign; 2457 pGoffset[glyph_index].dv += round(devY); 2458 } 2459 if (advance[0].x || advance[0].y) 2460 { 2461 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance[0].x, advance[0].y, &devX, &devY); 2462 piAdvance[glyph_index] += round(devX); 2463 } 2464 if (adjust[1].x || adjust[1].y) 2465 { 2466 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust[1].x, adjust[1].y, &devX, &devY); 2467 pGoffset[glyph_index + write_dir].du += round(devX) * offset_sign; 2468 pGoffset[glyph_index + write_dir].dv += round(devY); 2469 } 2470 if (advance[1].x || advance[1].y) 2471 { 2472 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance[1].x, advance[1].y, &devX, &devY); 2473 piAdvance[glyph_index + write_dir] += round(devX); 2474 } 2475 return index_offset; 2476 } 2477 2478 case GPOS_LOOKUP_ATTACH_CURSIVE: 2479 { 2480 POINT desU = {0,0}; 2481 double devX, devY; 2482 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 2483 2484 GPOS_apply_CursiveAttachment(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU); 2485 if (desU.x || desU.y) 2486 { 2487 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY); 2488 /* Windows does not appear to apply X offsets here */ 2489 pGoffset[glyph_index].dv = round(devY) + pGoffset[glyph_index+write_dir].dv; 2490 } 2491 break; 2492 } 2493 2494 case GPOS_LOOKUP_ATTACH_MARK_TO_BASE: 2495 { 2496 double devX, devY; 2497 POINT desU = {0,0}; 2498 int base_index = GPOS_apply_MarkToBase(script_cache, look, analysis, 2499 glyphs, glyph_index, glyph_count, ppem, &desU); 2500 if (base_index != -1) 2501 { 2502 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY); 2503 if (!analysis->fRTL) pGoffset[glyph_index].du = round(devX) - piAdvance[base_index]; 2504 else 2505 { 2506 if (analysis->fLogicalOrder) devX *= -1; 2507 pGoffset[glyph_index].du = round(devX); 2508 } 2509 pGoffset[glyph_index].dv = round(devY); 2510 } 2511 break; 2512 } 2513 2514 case GPOS_LOOKUP_ATTACH_MARK_TO_LIGATURE: 2515 { 2516 double devX, devY; 2517 POINT desU = {0,0}; 2518 GPOS_apply_MarkToLigature(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU); 2519 if (desU.x || desU.y) 2520 { 2521 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY); 2522 pGoffset[glyph_index].du = (round(devX) - piAdvance[glyph_index-1]); 2523 pGoffset[glyph_index].dv = round(devY); 2524 } 2525 break; 2526 } 2527 2528 case GPOS_LOOKUP_ATTACH_MARK_TO_MARK: 2529 { 2530 double devX, devY; 2531 POINT desU = {0,0}; 2532 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; 2533 if (GPOS_apply_MarkToMark(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU)) 2534 { 2535 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY); 2536 if (analysis->fRTL && analysis->fLogicalOrder) devX *= -1; 2537 pGoffset[glyph_index].du = round(devX) + pGoffset[glyph_index - write_dir].du; 2538 pGoffset[glyph_index].dv = round(devY) + pGoffset[glyph_index - write_dir].dv; 2539 } 2540 break; 2541 } 2542 2543 case GPOS_LOOKUP_POSITION_CONTEXT: 2544 return GPOS_apply_ContextPos(script_cache, lpotm, lplogfont, analysis, piAdvance, 2545 lookup, look, glyphs, glyph_index, glyph_count, pGoffset); 2546 2547 case GPOS_LOOKUP_POSITION_CONTEXT_CHAINED: 2548 return GPOS_apply_ChainContextPos(script_cache, lpotm, lplogfont, analysis, piAdvance, 2549 lookup, look, glyphs, glyph_index, glyph_count, pGoffset); 2550 2551 default: 2552 FIXME("Unhandled GPOS lookup type %#x.\n", type); 2553 } 2554 return 1; 2555 } 2556 2557 unsigned int OpenType_apply_GPOS_lookup(const ScriptCache *script_cache, const OUTLINETEXTMETRICW *otm, 2558 const LOGFONTW *logfont, const SCRIPT_ANALYSIS *analysis, int *advance, unsigned int lookup_index, 2559 const WORD *glyphs, unsigned int glyph_index, unsigned int glyph_count, GOFFSET *goffset) 2560 { 2561 const GPOS_Header *header = (const GPOS_Header *)script_cache->GPOS_Table; 2562 const OT_LookupList *lookup = (const OT_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList)); 2563 2564 return GPOS_apply_lookup(script_cache, otm, logfont, analysis, advance, lookup, 2565 lookup_index, glyphs, glyph_index, glyph_count, goffset); 2566 } 2567 2568 static LoadedScript *usp10_script_cache_get_script(ScriptCache *script_cache, OPENTYPE_TAG tag) 2569 { 2570 size_t i; 2571 2572 for (i = 0; i < script_cache->script_count; ++i) 2573 { 2574 if (script_cache->scripts[i].tag == tag) 2575 return &script_cache->scripts[i]; 2576 } 2577 2578 return NULL; 2579 } 2580 2581 static void GSUB_initialize_script_cache(ScriptCache *psc) 2582 { 2583 int i; 2584 2585 if (psc->GSUB_Table) 2586 { 2587 const OT_ScriptList *script; 2588 const GSUB_Header* header = (const GSUB_Header*)psc->GSUB_Table; 2589 script = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList)); 2590 psc->script_count = GET_BE_WORD(script->ScriptCount); 2591 TRACE("initializing %li scripts in this font\n",psc->script_count); 2592 if (psc->script_count) 2593 { 2594 psc->scripts = heap_alloc_zero(psc->script_count * sizeof(*psc->scripts)); 2595 for (i = 0; i < psc->script_count; i++) 2596 { 2597 int offset = GET_BE_WORD(script->ScriptRecord[i].Script); 2598 psc->scripts[i].tag = MS_MAKE_TAG(script->ScriptRecord[i].ScriptTag[0], script->ScriptRecord[i].ScriptTag[1], script->ScriptRecord[i].ScriptTag[2], script->ScriptRecord[i].ScriptTag[3]); 2599 psc->scripts[i].gsub_table = ((const BYTE*)script + offset); 2600 } 2601 } 2602 } 2603 } 2604 2605 static void GPOS_expand_script_cache(ScriptCache *psc) 2606 { 2607 int i, count; 2608 const OT_ScriptList *script; 2609 const GPOS_Header* header = (const GPOS_Header*)psc->GPOS_Table; 2610 LoadedScript *loaded_script; 2611 2612 if (!header) 2613 return; 2614 2615 script = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList)); 2616 count = GET_BE_WORD(script->ScriptCount); 2617 2618 if (!count) 2619 return; 2620 2621 if (!psc->script_count) 2622 { 2623 psc->script_count = count; 2624 TRACE("initializing %li scripts in this font\n",psc->script_count); 2625 if (psc->script_count) 2626 { 2627 psc->scripts = heap_alloc_zero(psc->script_count * sizeof(*psc->scripts)); 2628 for (i = 0; i < psc->script_count; i++) 2629 { 2630 int offset = GET_BE_WORD(script->ScriptRecord[i].Script); 2631 psc->scripts[i].tag = MS_MAKE_TAG(script->ScriptRecord[i].ScriptTag[0], script->ScriptRecord[i].ScriptTag[1], script->ScriptRecord[i].ScriptTag[2], script->ScriptRecord[i].ScriptTag[3]); 2632 psc->scripts[i].gpos_table = ((const BYTE*)script + offset); 2633 } 2634 } 2635 } 2636 else 2637 { 2638 for (i = 0; i < count; i++) 2639 { 2640 int offset = GET_BE_WORD(script->ScriptRecord[i].Script); 2641 OPENTYPE_TAG tag = MS_MAKE_TAG(script->ScriptRecord[i].ScriptTag[0], script->ScriptRecord[i].ScriptTag[1], script->ScriptRecord[i].ScriptTag[2], script->ScriptRecord[i].ScriptTag[3]); 2642 2643 if (!(loaded_script = usp10_script_cache_get_script(psc, tag))) 2644 { 2645 if (!usp10_array_reserve((void **)&psc->scripts, &psc->scripts_size, 2646 psc->script_count + 1, sizeof(*psc->scripts))) 2647 { 2648 ERR("Failed grow scripts array.\n"); 2649 return; 2650 } 2651 2652 loaded_script = &psc->scripts[psc->script_count]; 2653 ++psc->script_count; 2654 loaded_script->tag = tag; 2655 } 2656 loaded_script->gpos_table = (const BYTE *)script + offset; 2657 } 2658 } 2659 } 2660 2661 static void _initialize_script_cache(ScriptCache *psc) 2662 { 2663 if (!psc->scripts_initialized) 2664 { 2665 GSUB_initialize_script_cache(psc); 2666 GPOS_expand_script_cache(psc); 2667 psc->scripts_initialized = TRUE; 2668 } 2669 } 2670 2671 HRESULT OpenType_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags) 2672 { 2673 int i; 2674 const LoadedScript *script; 2675 HRESULT rc = S_OK; 2676 2677 _initialize_script_cache(psc); 2678 2679 *pcTags = psc->script_count; 2680 2681 if (searchingFor) 2682 { 2683 if (!(script = usp10_script_cache_get_script(psc, searchingFor))) 2684 return USP_E_SCRIPT_NOT_IN_FONT; 2685 2686 *pScriptTags = script->tag; 2687 *pcTags = 1; 2688 return S_OK; 2689 } 2690 2691 if (cMaxTags < *pcTags) 2692 rc = E_OUTOFMEMORY; 2693 2694 cMaxTags = min(cMaxTags, psc->script_count); 2695 for (i = 0; i < cMaxTags; ++i) 2696 { 2697 pScriptTags[i] = psc->scripts[i].tag; 2698 } 2699 return rc; 2700 } 2701 2702 static LoadedLanguage *usp10_script_get_language(LoadedScript *script, OPENTYPE_TAG tag) 2703 { 2704 size_t i; 2705 2706 for (i = 0; i < script->language_count; ++i) 2707 { 2708 if (script->languages[i].tag == tag) 2709 return &script->languages[i]; 2710 } 2711 2712 return NULL; 2713 } 2714 2715 static void GSUB_initialize_language_cache(LoadedScript *script) 2716 { 2717 int i; 2718 2719 if (script->gsub_table) 2720 { 2721 DWORD offset; 2722 const OT_Script* table = script->gsub_table; 2723 script->language_count = GET_BE_WORD(table->LangSysCount); 2724 offset = GET_BE_WORD(table->DefaultLangSys); 2725 if (offset) 2726 { 2727 script->default_language.tag = MS_MAKE_TAG('d','f','l','t'); 2728 script->default_language.gsub_table = (const BYTE*)table + offset; 2729 } 2730 2731 if (script->language_count) 2732 { 2733 TRACE("Deflang %p, LangCount %li\n",script->default_language.gsub_table, script->language_count); 2734 2735 script->languages = heap_alloc_zero(script->language_count * sizeof(*script->languages)); 2736 2737 for (i = 0; i < script->language_count; i++) 2738 { 2739 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys); 2740 script->languages[i].tag = MS_MAKE_TAG(table->LangSysRecord[i].LangSysTag[0], table->LangSysRecord[i].LangSysTag[1], table->LangSysRecord[i].LangSysTag[2], table->LangSysRecord[i].LangSysTag[3]); 2741 script->languages[i].gsub_table = ((const BYTE*)table + offset); 2742 } 2743 } 2744 } 2745 } 2746 2747 static void GPOS_expand_language_cache(LoadedScript *script) 2748 { 2749 int count; 2750 const OT_Script* table = script->gpos_table; 2751 LoadedLanguage *language; 2752 DWORD offset; 2753 2754 if (!table) 2755 return; 2756 2757 offset = GET_BE_WORD(table->DefaultLangSys); 2758 if (offset) 2759 script->default_language.gpos_table = (const BYTE*)table + offset; 2760 2761 count = GET_BE_WORD(table->LangSysCount); 2762 2763 TRACE("Deflang %p, LangCount %i\n",script->default_language.gpos_table, count); 2764 2765 if (!count) 2766 return; 2767 2768 if (!script->language_count) 2769 { 2770 int i; 2771 script->language_count = count; 2772 2773 script->languages = heap_alloc_zero(script->language_count * sizeof(*script->languages)); 2774 2775 for (i = 0; i < script->language_count; i++) 2776 { 2777 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys); 2778 script->languages[i].tag = MS_MAKE_TAG(table->LangSysRecord[i].LangSysTag[0], table->LangSysRecord[i].LangSysTag[1], table->LangSysRecord[i].LangSysTag[2], table->LangSysRecord[i].LangSysTag[3]); 2779 script->languages[i].gpos_table = ((const BYTE*)table + offset); 2780 } 2781 } 2782 else if (count) 2783 { 2784 int i; 2785 for (i = 0; i < count; i++) 2786 { 2787 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys); 2788 OPENTYPE_TAG tag = MS_MAKE_TAG(table->LangSysRecord[i].LangSysTag[0], table->LangSysRecord[i].LangSysTag[1], table->LangSysRecord[i].LangSysTag[2], table->LangSysRecord[i].LangSysTag[3]); 2789 2790 if (!(language = usp10_script_get_language(script, tag))) 2791 { 2792 if (!usp10_array_reserve((void **)&script->languages, &script->languages_size, 2793 script->language_count + 1, sizeof(*script->languages))) 2794 { 2795 ERR("Failed grow languages array.\n"); 2796 return; 2797 } 2798 2799 language = &script->languages[script->language_count]; 2800 ++script->language_count; 2801 language->tag = tag; 2802 } 2803 language->gpos_table = (const BYTE *)table + offset; 2804 } 2805 } 2806 } 2807 2808 static void _initialize_language_cache(LoadedScript *script) 2809 { 2810 if (!script->languages_initialized) 2811 { 2812 GSUB_initialize_language_cache(script); 2813 GPOS_expand_language_cache(script); 2814 script->languages_initialized = TRUE; 2815 } 2816 } 2817 2818 HRESULT OpenType_GetFontLanguageTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pLanguageTags, int *pcTags) 2819 { 2820 int i; 2821 HRESULT rc = S_OK; 2822 LoadedScript *script = NULL; 2823 2824 _initialize_script_cache(psc); 2825 if (!(script = usp10_script_cache_get_script(psc, script_tag))) 2826 return E_INVALIDARG; 2827 2828 _initialize_language_cache(script); 2829 2830 if (!searchingFor && cMaxTags < script->language_count) 2831 rc = E_OUTOFMEMORY; 2832 else if (searchingFor) 2833 rc = E_INVALIDARG; 2834 2835 *pcTags = script->language_count; 2836 2837 for (i = 0; i < script->language_count; i++) 2838 { 2839 if (i < cMaxTags) 2840 pLanguageTags[i] = script->languages[i].tag; 2841 2842 if (searchingFor) 2843 { 2844 if (searchingFor == script->languages[i].tag) 2845 { 2846 pLanguageTags[0] = script->languages[i].tag; 2847 *pcTags = 1; 2848 rc = S_OK; 2849 break; 2850 } 2851 } 2852 } 2853 2854 if (script->default_language.gsub_table) 2855 { 2856 if (i < cMaxTags) 2857 pLanguageTags[i] = script->default_language.tag; 2858 2859 if (searchingFor && FAILED(rc)) 2860 { 2861 pLanguageTags[0] = script->default_language.tag; 2862 } 2863 i++; 2864 *pcTags = (*pcTags) + 1; 2865 } 2866 2867 return rc; 2868 } 2869 2870 static void usp10_language_add_feature_list(LoadedLanguage *language, char table_type, 2871 const OT_LangSys *lang, const OT_FeatureList *feature_list) 2872 { 2873 unsigned int count = GET_BE_WORD(lang->FeatureCount); 2874 unsigned int i, j; 2875 2876 TRACE("table_type %#x, %u features.\n", table_type, count); 2877 2878 if (!count) 2879 return; 2880 2881 if (!language->feature_count) 2882 language->features = heap_alloc(count * sizeof(*language->features)); 2883 else 2884 language->features = HeapReAlloc(GetProcessHeap(), 0, language->features, 2885 (language->feature_count + count) * sizeof(*language->features)); 2886 2887 for (i = 0; i < count; ++i) 2888 { 2889 const OT_FeatureRecord *record; 2890 LoadedFeature *loaded_feature; 2891 const OT_Feature *feature; 2892 2893 record = &feature_list->FeatureRecord[GET_BE_WORD(lang->FeatureIndex[i])]; 2894 feature = (const OT_Feature *)((const BYTE *)feature_list + GET_BE_WORD(record->Feature)); 2895 2896 loaded_feature = &language->features[language->feature_count + i]; 2897 loaded_feature->tag = MS_MAKE_TAG(record->FeatureTag[0], record->FeatureTag[1], 2898 record->FeatureTag[2], record->FeatureTag[3]); 2899 loaded_feature->tableType = table_type; 2900 loaded_feature->feature = feature; 2901 loaded_feature->lookup_count = GET_BE_WORD(feature->LookupCount); 2902 loaded_feature->lookups = heap_alloc(loaded_feature->lookup_count * sizeof(*loaded_feature->lookups)); 2903 for (j = 0; j < loaded_feature->lookup_count; ++j) 2904 loaded_feature->lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]); 2905 } 2906 language->feature_count += count; 2907 } 2908 2909 static void _initialize_feature_cache(ScriptCache *psc, LoadedLanguage *language) 2910 { 2911 const GSUB_Header *gsub_header = psc->GSUB_Table; 2912 const GPOS_Header *gpos_header = psc->GPOS_Table; 2913 const OT_FeatureList *feature_list; 2914 const OT_LangSys *lang; 2915 2916 if (language->features_initialized) 2917 return; 2918 2919 if ((lang = language->gsub_table)) 2920 { 2921 feature_list = (const OT_FeatureList *)((const BYTE *)gsub_header + GET_BE_WORD(gsub_header->FeatureList)); 2922 usp10_language_add_feature_list(language, FEATURE_GSUB_TABLE, lang, feature_list); 2923 } 2924 2925 if ((lang = language->gpos_table)) 2926 { 2927 feature_list = (const OT_FeatureList *)((const BYTE *)gpos_header + GET_BE_WORD(gpos_header->FeatureList)); 2928 usp10_language_add_feature_list(language, FEATURE_GPOS_TABLE, lang, feature_list); 2929 } 2930 2931 language->features_initialized = TRUE; 2932 } 2933 2934 HRESULT OpenType_GetFontFeatureTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG language_tag, BOOL filtered, OPENTYPE_TAG searchingFor, char tableType, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags, LoadedFeature** feature) 2935 { 2936 int i; 2937 LoadedScript *script; 2938 HRESULT rc = S_OK; 2939 LoadedLanguage *language = NULL; 2940 2941 _initialize_script_cache(psc); 2942 if (!(script = usp10_script_cache_get_script(psc, script_tag))) 2943 { 2944 *pcTags = 0; 2945 if (!filtered) 2946 return S_OK; 2947 else 2948 return E_INVALIDARG; 2949 } 2950 2951 _initialize_language_cache(script); 2952 2953 if ((script->default_language.gsub_table || script->default_language.gpos_table) && script->default_language.tag == language_tag) 2954 language = &script->default_language; 2955 else 2956 language = usp10_script_get_language(script, language_tag); 2957 2958 if (!language) 2959 { 2960 *pcTags = 0; 2961 return S_OK; 2962 } 2963 2964 _initialize_feature_cache(psc, language); 2965 2966 if (tableType) 2967 { 2968 *pcTags = 0; 2969 for (i = 0; i < language->feature_count; i++) 2970 if (language->features[i].tableType == tableType) 2971 *pcTags = (*pcTags)+1; 2972 } 2973 else 2974 *pcTags = language->feature_count; 2975 2976 if (!searchingFor && cMaxTags < *pcTags) 2977 rc = E_OUTOFMEMORY; 2978 else if (searchingFor) 2979 rc = E_INVALIDARG; 2980 2981 for (i = 0; i < language->feature_count; i++) 2982 { 2983 if (i < cMaxTags) 2984 { 2985 if (!tableType || language->features[i].tableType == tableType) 2986 pFeatureTags[i] = language->features[i].tag; 2987 } 2988 2989 if (searchingFor) 2990 { 2991 if ((searchingFor == language->features[i].tag) && 2992 (!tableType || language->features[i].tableType == tableType)) 2993 { 2994 pFeatureTags[0] = language->features[i].tag; 2995 *pcTags = 1; 2996 if (feature) 2997 *feature = &language->features[i]; 2998 rc = S_OK; 2999 break; 3000 } 3001 } 3002 } 3003 return rc; 3004 } 3005