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