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