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