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