Sync with trunk revision 64099.
[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 #ifndef __REACTOS__
937 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
938 #endif
939 continue;
940 }
941 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
942 {
943 int k;
944 int indexGlyphs;
945 const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
946 const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
947 const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
948 const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
949 int newIndex = glyph_index;
950
951 ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
952
953 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
954
955 for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
956 {
957 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
958 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
959 break;
960 }
961 if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
962 continue;
963 TRACE("Matched Backtrack\n");
964
965 ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)((BYTE *)ccsf1 +
966 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_1, Coverage[GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)]));
967
968 indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
969 for (k = 0; k < indexGlyphs; k++)
970 {
971 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
972 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
973 break;
974 }
975 if (k != indexGlyphs)
976 continue;
977 TRACE("Matched IndexGlyphs\n");
978
979 ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)((BYTE *)ccsf3_2 +
980 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_2, Coverage[GET_BE_WORD(ccsf3_2->InputGlyphCount)]));
981
982 for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
983 {
984 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
985 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
986 break;
987 }
988 if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
989 continue;
990 TRACE("Matched LookAhead\n");
991
992 ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)((BYTE *)ccsf3_3 +
993 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_3, Coverage[GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)]));
994
995 if (GET_BE_WORD(ccsf3_4->SubstCount))
996 {
997 for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
998 {
999 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
1000 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
1001
1002 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
1003 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
1004 if (newIndex == -1)
1005 {
1006 ERR("Chain failed to generate a glyph\n");
1007 continue;
1008 }
1009 }
1010 return newIndex;
1011 }
1012 else return GSUB_E_NOGLYPH;
1013 }
1014 }
1015 return -1;
1016 }
1017
1018 static INT GSUB_apply_lookup(const OT_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1019 {
1020 int offset;
1021 const OT_LookupTable *look;
1022
1023 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
1024 look = (const OT_LookupTable*)((const BYTE*)lookup + offset);
1025 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
1026 switch(GET_BE_WORD(look->LookupType))
1027 {
1028 case 1:
1029 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1030 case 2:
1031 return GSUB_apply_MultipleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1032 case 3:
1033 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1034 case 4:
1035 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1036 case 6:
1037 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
1038 default:
1039 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
1040 }
1041 return GSUB_E_NOGLYPH;
1042 }
1043
1044 INT OpenType_apply_GSUB_lookup(LPCVOID table, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1045 {
1046 const GSUB_Header *header = (const GSUB_Header *)table;
1047 const OT_LookupList *lookup = (const OT_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1048
1049 return GSUB_apply_lookup(lookup, lookup_index, glyphs, glyph_index, write_dir, glyph_count);
1050 }
1051
1052 /**********
1053 * GPOS
1054 **********/
1055 static INT GPOS_apply_lookup(ScriptCache *psc, LPOUTLINETEXTMETRICW lpotm, LPLOGFONTW lplogfont, const SCRIPT_ANALYSIS *analysis, INT* piAdvance,
1056 const OT_LookupList* lookup, INT lookup_index, const WORD *glyphs, INT glyph_index, INT glyph_count, GOFFSET *pGoffset);
1057
1058 static INT GPOS_get_device_table_value(const OT_DeviceTable *DeviceTable, WORD ppem)
1059 {
1060 static const WORD mask[3] = {3,0xf,0xff};
1061 if (DeviceTable && ppem >= GET_BE_WORD(DeviceTable->StartSize) && ppem <= GET_BE_WORD(DeviceTable->EndSize))
1062 {
1063 int format = GET_BE_WORD(DeviceTable->DeltaFormat);
1064 int index = ppem - GET_BE_WORD(DeviceTable->StartSize);
1065 int value;
1066 TRACE("device table, format %i, index %i\n",format, index);
1067 index = index << format;
1068 value = (DeviceTable->DeltaValue[index/sizeof(WORD)] << (index%sizeof(WORD)))&mask[format-1];
1069 TRACE("offset %i, value %i\n",index, value);
1070 if (value > mask[format-1]/2)
1071 value = -1 * ((mask[format-1]+1) - value);
1072 return value;
1073 }
1074 return 0;
1075 }
1076
1077 static VOID GPOS_get_anchor_values(LPCVOID table, LPPOINT pt, WORD ppem)
1078 {
1079 const GPOS_AnchorFormat1* anchor1 = (const GPOS_AnchorFormat1*)table;
1080
1081 switch (GET_BE_WORD(anchor1->AnchorFormat))
1082 {
1083 case 1:
1084 {
1085 TRACE("Anchor Format 1\n");
1086 pt->x = (short)GET_BE_WORD(anchor1->XCoordinate);
1087 pt->y = (short)GET_BE_WORD(anchor1->YCoordinate);
1088 break;
1089 }
1090 case 2:
1091 {
1092 const GPOS_AnchorFormat2* anchor2 = (const GPOS_AnchorFormat2*)table;
1093 TRACE("Anchor Format 2\n");
1094 pt->x = (short)GET_BE_WORD(anchor2->XCoordinate);
1095 pt->y = (short)GET_BE_WORD(anchor2->YCoordinate);
1096 break;
1097 }
1098 case 3:
1099 {
1100 int offset;
1101 const GPOS_AnchorFormat3* anchor3 = (const GPOS_AnchorFormat3*)table;
1102 TRACE("Anchor Format 3\n");
1103 pt->x = (short)GET_BE_WORD(anchor3->XCoordinate);
1104 pt->y = (short)GET_BE_WORD(anchor3->YCoordinate);
1105 offset = GET_BE_WORD(anchor3->XDeviceTable);
1106 TRACE("ppem %i\n",ppem);
1107 if (offset)
1108 {
1109 const OT_DeviceTable* DeviceTableX = NULL;
1110 DeviceTableX = (const OT_DeviceTable*)((const BYTE*)anchor3 + offset);
1111 pt->x += GPOS_get_device_table_value(DeviceTableX, ppem);
1112 }
1113 offset = GET_BE_WORD(anchor3->YDeviceTable);
1114 if (offset)
1115 {
1116 const OT_DeviceTable* DeviceTableY = NULL;
1117 DeviceTableY = (const OT_DeviceTable*)((const BYTE*)anchor3 + offset);
1118 pt->y += GPOS_get_device_table_value(DeviceTableY, ppem);
1119 }
1120 break;
1121 }
1122 default:
1123 ERR("Unknown Anchor Format %i\n",GET_BE_WORD(anchor1->AnchorFormat));
1124 pt->x = 0;
1125 pt->y = 0;
1126 }
1127 }
1128
1129 static void GPOS_convert_design_units_to_device(LPOUTLINETEXTMETRICW lpotm, LPLOGFONTW lplogfont, int desX, int desY, double *devX, double *devY)
1130 {
1131 int emHeight = lpotm->otmTextMetrics.tmAscent + lpotm->otmTextMetrics.tmDescent - lpotm->otmTextMetrics.tmInternalLeading;
1132
1133 TRACE("emHeight %i lfWidth %i\n",emHeight, lplogfont->lfWidth);
1134 *devX = (desX * emHeight) / (double)lpotm->otmEMSquare;
1135 *devY = (desY * emHeight) / (double)lpotm->otmEMSquare;
1136 if (lplogfont->lfWidth)
1137 FIXME("Font with lfWidth set no handled properly\n");
1138 }
1139
1140 static INT GPOS_get_value_record(WORD ValueFormat, const WORD data[], GPOS_ValueRecord *record)
1141 {
1142 INT offset = 0;
1143 if (ValueFormat & 0x0001) { if (data) record->XPlacement = GET_BE_WORD(data[offset]); offset++; }
1144 if (ValueFormat & 0x0002) { if (data) record->YPlacement = GET_BE_WORD(data[offset]); offset++; }
1145 if (ValueFormat & 0x0004) { if (data) record->XAdvance = GET_BE_WORD(data[offset]); offset++; }
1146 if (ValueFormat & 0x0008) { if (data) record->YAdvance = GET_BE_WORD(data[offset]); offset++; }
1147 if (ValueFormat & 0x0010) { if (data) record->XPlaDevice = GET_BE_WORD(data[offset]); offset++; }
1148 if (ValueFormat & 0x0020) { if (data) record->YPlaDevice = GET_BE_WORD(data[offset]); offset++; }
1149 if (ValueFormat & 0x0040) { if (data) record->XAdvDevice = GET_BE_WORD(data[offset]); offset++; }
1150 if (ValueFormat & 0x0080) { if (data) record->YAdvDevice = GET_BE_WORD(data[offset]); offset++; }
1151 return offset;
1152 }
1153
1154 static VOID GPOS_get_value_record_offsets(const BYTE* head, GPOS_ValueRecord *ValueRecord, WORD ValueFormat, INT ppem, LPPOINT ptPlacement, LPPOINT ptAdvance)
1155 {
1156 if (ValueFormat & 0x0001) ptPlacement->x += (short)ValueRecord->XPlacement;
1157 if (ValueFormat & 0x0002) ptPlacement->y += (short)ValueRecord->YPlacement;
1158 if (ValueFormat & 0x0004) ptAdvance->x += (short)ValueRecord->XAdvance;
1159 if (ValueFormat & 0x0008) ptAdvance->y += (short)ValueRecord->YAdvance;
1160 if (ValueFormat & 0x0010) ptPlacement->x += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->XPlaDevice), ppem);
1161 if (ValueFormat & 0x0020) ptPlacement->y += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->YPlaDevice), ppem);
1162 if (ValueFormat & 0x0040) ptAdvance->x += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->XAdvDevice), ppem);
1163 if (ValueFormat & 0x0080) ptAdvance->y += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->YAdvDevice), ppem);
1164 if (ValueFormat & 0xFF00) FIXME("Unhandled Value Format %x\n",ValueFormat&0xFF00);
1165 }
1166
1167 static const BYTE *GPOS_get_subtable(const OT_LookupTable *look, int index)
1168 {
1169 int offset = GET_BE_WORD(look->SubTable[index]);
1170
1171 if (GET_BE_WORD(look->LookupType) == 9)
1172 {
1173 const GPOS_ExtensionPosFormat1 *ext = (const GPOS_ExtensionPosFormat1 *)((const BYTE *)look + offset);
1174 if (GET_BE_WORD(ext->PosFormat) == 1)
1175 {
1176 offset += GET_BE_DWORD(ext->ExtensionOffset);
1177 }
1178 else
1179 {
1180 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext->PosFormat));
1181 }
1182 }
1183 return (const BYTE *)look + offset;
1184 }
1185
1186 static VOID GPOS_apply_SingleAdjustment(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1187 INT glyph_count, INT ppem, LPPOINT ptAdjust, LPPOINT ptAdvance)
1188 {
1189 int j;
1190
1191 TRACE("Single Adjustment Positioning Subtable\n");
1192
1193 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1194 {
1195 const GPOS_SinglePosFormat1 *spf1 = (const GPOS_SinglePosFormat1*)GPOS_get_subtable(look, j);
1196 WORD offset;
1197 if (GET_BE_WORD(spf1->PosFormat) == 1)
1198 {
1199 offset = GET_BE_WORD(spf1->Coverage);
1200 if (GSUB_is_glyph_covered((const BYTE*)spf1+offset, glyphs[glyph_index]) != -1)
1201 {
1202 GPOS_ValueRecord ValueRecord = {0,0,0,0,0,0,0,0};
1203 WORD ValueFormat = GET_BE_WORD(spf1->ValueFormat);
1204 GPOS_get_value_record(ValueFormat, spf1->Value, &ValueRecord);
1205 GPOS_get_value_record_offsets((const BYTE*)spf1, &ValueRecord, ValueFormat, ppem, ptAdjust, ptAdvance);
1206 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord.XPlacement,ValueRecord.YPlacement);
1207 }
1208 }
1209 else if (GET_BE_WORD(spf1->PosFormat) == 2)
1210 {
1211 int index;
1212 const GPOS_SinglePosFormat2 *spf2;
1213 spf2 = (const GPOS_SinglePosFormat2*)spf1;
1214 offset = GET_BE_WORD(spf2->Coverage);
1215 index = GSUB_is_glyph_covered((const BYTE*)spf2+offset, glyphs[glyph_index]);
1216 if (index != -1)
1217 {
1218 int size;
1219 GPOS_ValueRecord ValueRecord = {0,0,0,0,0,0,0,0};
1220 WORD ValueFormat = GET_BE_WORD(spf2->ValueFormat);
1221 size = GPOS_get_value_record(ValueFormat, spf2->Value, &ValueRecord);
1222 if (index > 0)
1223 {
1224 offset = size * index;
1225 GPOS_get_value_record(ValueFormat, &spf2->Value[offset], &ValueRecord);
1226 }
1227 GPOS_get_value_record_offsets((const BYTE*)spf2, &ValueRecord, ValueFormat, ppem, ptAdjust, ptAdvance);
1228 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord.XPlacement,ValueRecord.YPlacement);
1229 }
1230 }
1231 else
1232 FIXME("Single Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(spf1->PosFormat));
1233 }
1234 }
1235
1236 static void apply_pair_value( const void *pos_table, WORD val_fmt1, WORD val_fmt2, const WORD *pair,
1237 INT ppem, POINT *adjust, POINT *advance )
1238 {
1239 GPOS_ValueRecord val_rec1 = {0,0,0,0,0,0,0,0};
1240 GPOS_ValueRecord val_rec2 = {0,0,0,0,0,0,0,0};
1241 INT size;
1242
1243 size = GPOS_get_value_record( val_fmt1, pair, &val_rec1 );
1244 GPOS_get_value_record( val_fmt2, pair + size, &val_rec2 );
1245
1246 if (val_fmt1)
1247 {
1248 GPOS_get_value_record_offsets( pos_table, &val_rec1, val_fmt1, ppem, adjust, advance );
1249 TRACE( "Glyph 1 resulting cumulative offset is %i,%i design units\n", adjust[0].x, adjust[0].y );
1250 TRACE( "Glyph 1 resulting cumulative advance is %i,%i design units\n", advance[0].x, advance[0].y );
1251 }
1252 if (val_fmt2)
1253 {
1254 GPOS_get_value_record_offsets( pos_table, &val_rec2, val_fmt2, ppem, adjust + 1, advance + 1 );
1255 TRACE( "Glyph 2 resulting cumulative offset is %i,%i design units\n", adjust[1].x, adjust[1].y );
1256 TRACE( "Glyph 2 resulting cumulative advance is %i,%i design units\n", advance[1].x, advance[1].y );
1257 }
1258 }
1259
1260 static INT GPOS_apply_PairAdjustment(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1261 INT glyph_count, INT ppem, LPPOINT ptAdjust, LPPOINT ptAdvance)
1262 {
1263 int j;
1264 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1265
1266 if (glyph_index + write_dir < 0 || glyph_index + write_dir >= glyph_count) return glyph_index + 1;
1267
1268 TRACE("Pair Adjustment Positioning Subtable\n");
1269
1270 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1271 {
1272 const GPOS_PairPosFormat1 *ppf1 = (const GPOS_PairPosFormat1*)GPOS_get_subtable(look, j);
1273 WORD offset;
1274 if (GET_BE_WORD(ppf1->PosFormat) == 1)
1275 {
1276 int index;
1277 WORD ValueFormat1 = GET_BE_WORD(ppf1->ValueFormat1);
1278 WORD ValueFormat2 = GET_BE_WORD(ppf1->ValueFormat2);
1279 INT val_fmt1_size = GPOS_get_value_record( ValueFormat1, NULL, NULL );
1280 INT val_fmt2_size = GPOS_get_value_record( ValueFormat2, NULL, NULL );
1281 offset = GET_BE_WORD(ppf1->Coverage);
1282 index = GSUB_is_glyph_covered((const BYTE*)ppf1+offset, glyphs[glyph_index]);
1283 if (index != -1 && index < GET_BE_WORD(ppf1->PairSetCount))
1284 {
1285 int k;
1286 int pair_count;
1287 const GPOS_PairSet *ps;
1288 const GPOS_PairValueRecord *pair_val_rec;
1289 offset = GET_BE_WORD(ppf1->PairSetOffset[index]);
1290 ps = (const GPOS_PairSet*)((const BYTE*)ppf1+offset);
1291 pair_count = GET_BE_WORD(ps->PairValueCount);
1292 pair_val_rec = ps->PairValueRecord;
1293 for (k = 0; k < pair_count; k++)
1294 {
1295 WORD second_glyph = GET_BE_WORD(pair_val_rec->SecondGlyph);
1296 if (glyphs[glyph_index+write_dir] == second_glyph)
1297 {
1298 int next = 1;
1299 TRACE("Format 1: Found Pair %x,%x\n",glyphs[glyph_index],glyphs[glyph_index+write_dir]);
1300 apply_pair_value( ppf1, ValueFormat1, ValueFormat2, pair_val_rec->Value1, ppem, ptAdjust, ptAdvance );
1301 if (ValueFormat2) next++;
1302 return glyph_index + next;
1303 }
1304 pair_val_rec = (const GPOS_PairValueRecord *)(pair_val_rec->Value1 + val_fmt1_size + val_fmt2_size);
1305 }
1306 }
1307 }
1308 else if (GET_BE_WORD(ppf1->PosFormat) == 2)
1309 {
1310 const GPOS_PairPosFormat2 *ppf2 = (const GPOS_PairPosFormat2*)ppf1;
1311 int index;
1312 WORD ValueFormat1 = GET_BE_WORD( ppf2->ValueFormat1 );
1313 WORD ValueFormat2 = GET_BE_WORD( ppf2->ValueFormat2 );
1314 INT val_fmt1_size = GPOS_get_value_record( ValueFormat1, NULL, NULL );
1315 INT val_fmt2_size = GPOS_get_value_record( ValueFormat2, NULL, NULL );
1316 WORD class1_count = GET_BE_WORD( ppf2->Class1Count );
1317 WORD class2_count = GET_BE_WORD( ppf2->Class2Count );
1318
1319 offset = GET_BE_WORD( ppf2->Coverage );
1320 index = GSUB_is_glyph_covered( (const BYTE*)ppf2 + offset, glyphs[glyph_index] );
1321 if (index != -1)
1322 {
1323 WORD class1, class2;
1324 class1 = OT_get_glyph_class( (const BYTE *)ppf2 + GET_BE_WORD(ppf2->ClassDef1), glyphs[glyph_index] );
1325 class2 = OT_get_glyph_class( (const BYTE *)ppf2 + GET_BE_WORD(ppf2->ClassDef2), glyphs[glyph_index + write_dir] );
1326 if (class1 < class1_count && class2 < class2_count)
1327 {
1328 const WORD *pair_val = ppf2->Class1Record + (class1 * class2_count + class2) * (val_fmt1_size + val_fmt2_size);
1329 int next = 1;
1330
1331 TRACE( "Format 2: Found Pair %x,%x\n", glyphs[glyph_index], glyphs[glyph_index + write_dir] );
1332
1333 apply_pair_value( ppf2, ValueFormat1, ValueFormat2, pair_val, ppem, ptAdjust, ptAdvance );
1334 if (ValueFormat2) next++;
1335 return glyph_index + next;
1336 }
1337 }
1338 }
1339 else
1340 FIXME("Pair Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(ppf1->PosFormat));
1341 }
1342 return glyph_index+1;
1343 }
1344
1345 static VOID GPOS_apply_CursiveAttachment(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1346 INT glyph_count, INT ppem, LPPOINT pt)
1347 {
1348 int j;
1349 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1350
1351 if (glyph_index + write_dir < 0 || glyph_index + write_dir >= glyph_count) return;
1352
1353 TRACE("Cursive Attachment Positioning Subtable\n");
1354
1355 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1356 {
1357 const GPOS_CursivePosFormat1 *cpf1 = (const GPOS_CursivePosFormat1 *)GPOS_get_subtable(look, j);
1358 if (GET_BE_WORD(cpf1->PosFormat) == 1)
1359 {
1360 int index_exit, index_entry;
1361 WORD offset = GET_BE_WORD( cpf1->Coverage );
1362 index_exit = GSUB_is_glyph_covered((const BYTE*)cpf1+offset, glyphs[glyph_index]);
1363 if (index_exit != -1 && cpf1->EntryExitRecord[index_exit].ExitAnchor!= 0)
1364 {
1365 index_entry = GSUB_is_glyph_covered((const BYTE*)cpf1+offset, glyphs[glyph_index+write_dir]);
1366 if (index_entry != -1 && cpf1->EntryExitRecord[index_entry].EntryAnchor != 0)
1367 {
1368 POINT exit_pt, entry_pt;
1369 offset = GET_BE_WORD(cpf1->EntryExitRecord[index_exit].ExitAnchor);
1370 GPOS_get_anchor_values((const BYTE*)cpf1 + offset, &exit_pt, ppem);
1371 offset = GET_BE_WORD(cpf1->EntryExitRecord[index_entry].EntryAnchor);
1372 GPOS_get_anchor_values((const BYTE*)cpf1 + offset, &entry_pt, ppem);
1373 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);
1374 pt->x = entry_pt.x - exit_pt.x;
1375 pt->y = entry_pt.y - exit_pt.y;
1376 return;
1377 }
1378 }
1379 }
1380 else
1381 FIXME("Cursive Attachment Positioning: Format %i Unhandled\n",GET_BE_WORD(cpf1->PosFormat));
1382 }
1383 return;
1384 }
1385
1386 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)
1387 {
1388 int j;
1389 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1390 void *glyph_class_table = NULL;
1391 int rc = -1;
1392
1393 if (psc->GDEF_Table)
1394 {
1395 const GDEF_Header *header = psc->GDEF_Table;
1396 WORD offset = GET_BE_WORD( header->GlyphClassDef );
1397 if (offset)
1398 glyph_class_table = (BYTE *)psc->GDEF_Table + offset;
1399 }
1400
1401 TRACE("MarkToBase Attachment Positioning Subtable\n");
1402
1403 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1404 {
1405 const GPOS_MarkBasePosFormat1 *mbpf1 = (const GPOS_MarkBasePosFormat1 *)GPOS_get_subtable(look, j);
1406 if (GET_BE_WORD(mbpf1->PosFormat) == 1)
1407 {
1408 int offset = GET_BE_WORD(mbpf1->MarkCoverage);
1409 int mark_index;
1410 mark_index = GSUB_is_glyph_covered((const BYTE*)mbpf1+offset, glyphs[glyph_index]);
1411 if (mark_index != -1)
1412 {
1413 int base_index;
1414 int base_glyph = glyph_index - write_dir;
1415
1416 if (glyph_class_table)
1417 {
1418 while (OT_get_glyph_class(glyph_class_table, glyphs[base_glyph]) == MarkGlyph && base_glyph > 0 && base_glyph < glyph_count)
1419 base_glyph -= write_dir;
1420 }
1421
1422 offset = GET_BE_WORD(mbpf1->BaseCoverage);
1423 base_index = GSUB_is_glyph_covered((const BYTE*)mbpf1+offset, glyphs[base_glyph]);
1424 if (base_index != -1)
1425 {
1426 const GPOS_MarkArray *ma;
1427 const GPOS_MarkRecord *mr;
1428 const GPOS_BaseArray *ba;
1429 const GPOS_BaseRecord *br;
1430 int mark_class;
1431 int class_count = GET_BE_WORD(mbpf1->ClassCount);
1432 int baserecord_size;
1433 POINT base_pt;
1434 POINT mark_pt;
1435 TRACE("Mark %x(%i) and base %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[base_glyph], base_index);
1436 offset = GET_BE_WORD(mbpf1->MarkArray);
1437 ma = (const GPOS_MarkArray*)((const BYTE*)mbpf1 + offset);
1438 if (mark_index > GET_BE_WORD(ma->MarkCount))
1439 {
1440 ERR("Mark index exeeded mark count\n");
1441 return -1;
1442 }
1443 mr = &ma->MarkRecord[mark_index];
1444 mark_class = GET_BE_WORD(mr->Class);
1445 TRACE("Mark Class %i total classes %i\n",mark_class,class_count);
1446 offset = GET_BE_WORD(mbpf1->BaseArray);
1447 ba = (const GPOS_BaseArray*)((const BYTE*)mbpf1 + offset);
1448 baserecord_size = class_count * sizeof(WORD);
1449 br = (const GPOS_BaseRecord*)((const BYTE*)ba + sizeof(WORD) + (baserecord_size * base_index));
1450 offset = GET_BE_WORD(br->BaseAnchor[mark_class]);
1451 GPOS_get_anchor_values((const BYTE*)ba + offset, &base_pt, ppem);
1452 offset = GET_BE_WORD(mr->MarkAnchor);
1453 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem);
1454 TRACE("Offset on base is %i,%i design units\n",base_pt.x,base_pt.y);
1455 TRACE("Offset on mark is %i,%i design units\n",mark_pt.x, mark_pt.y);
1456 pt->x += base_pt.x - mark_pt.x;
1457 pt->y += base_pt.y - mark_pt.y;
1458 TRACE("Resulting cumulative offset is %i,%i design units\n",pt->x,pt->y);
1459 rc = base_glyph;
1460 }
1461 }
1462 }
1463 else
1464 FIXME("Unhandled Mark To Base Format %i\n",GET_BE_WORD(mbpf1->PosFormat));
1465 }
1466 return rc;
1467 }
1468
1469 static VOID GPOS_apply_MarkToLigature(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1470 INT glyph_count, INT ppem, LPPOINT pt)
1471 {
1472 int j;
1473 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1474
1475 TRACE("MarkToLigature Attachment Positioning Subtable\n");
1476
1477 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1478 {
1479 const GPOS_MarkLigPosFormat1 *mlpf1 = (const GPOS_MarkLigPosFormat1 *)GPOS_get_subtable(look, j);
1480 if (GET_BE_WORD(mlpf1->PosFormat) == 1)
1481 {
1482 int offset = GET_BE_WORD(mlpf1->MarkCoverage);
1483 int mark_index;
1484 mark_index = GSUB_is_glyph_covered((const BYTE*)mlpf1+offset, glyphs[glyph_index]);
1485 if (mark_index != -1)
1486 {
1487 int ligature_index;
1488 offset = GET_BE_WORD(mlpf1->LigatureCoverage);
1489 ligature_index = GSUB_is_glyph_covered((const BYTE*)mlpf1+offset, glyphs[glyph_index - write_dir]);
1490 if (ligature_index != -1)
1491 {
1492 const GPOS_MarkArray *ma;
1493 const GPOS_MarkRecord *mr;
1494
1495 const GPOS_LigatureArray *la;
1496 const GPOS_LigatureAttach *lt;
1497 int mark_class;
1498 int class_count = GET_BE_WORD(mlpf1->ClassCount);
1499 int component_count;
1500 int component_size;
1501 int i;
1502 POINT ligature_pt;
1503 POINT mark_pt;
1504
1505 TRACE("Mark %x(%i) and ligature %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], ligature_index);
1506 offset = GET_BE_WORD(mlpf1->MarkArray);
1507 ma = (const GPOS_MarkArray*)((const BYTE*)mlpf1 + offset);
1508 if (mark_index > GET_BE_WORD(ma->MarkCount))
1509 {
1510 ERR("Mark index exeeded mark count\n");
1511 return;
1512 }
1513 mr = &ma->MarkRecord[mark_index];
1514 mark_class = GET_BE_WORD(mr->Class);
1515 TRACE("Mark Class %i total classes %i\n",mark_class,class_count);
1516 offset = GET_BE_WORD(mlpf1->LigatureArray);
1517 la = (const GPOS_LigatureArray*)((const BYTE*)mlpf1 + offset);
1518 if (ligature_index > GET_BE_WORD(la->LigatureCount))
1519 {
1520 ERR("Ligature index exeeded ligature count\n");
1521 return;
1522 }
1523 offset = GET_BE_WORD(la->LigatureAttach[ligature_index]);
1524 lt = (const GPOS_LigatureAttach*)((const BYTE*)la + offset);
1525
1526 component_count = GET_BE_WORD(lt->ComponentCount);
1527 component_size = class_count * sizeof(WORD);
1528 offset = 0;
1529 for (i = 0; i < component_count && !offset; i++)
1530 {
1531 int k;
1532 const GPOS_ComponentRecord *cr = (const GPOS_ComponentRecord*)((const BYTE*)lt->ComponentRecord + (component_size * i));
1533 for (k = 0; k < class_count && !offset; k++)
1534 offset = GET_BE_WORD(cr->LigatureAnchor[k]);
1535 cr = (const GPOS_ComponentRecord*)((const BYTE*)cr + component_size);
1536 }
1537 if (!offset)
1538 {
1539 ERR("Failed to find avalible ligature connection point\n");
1540 return;
1541 }
1542
1543 GPOS_get_anchor_values((const BYTE*)lt + offset, &ligature_pt, ppem);
1544 offset = GET_BE_WORD(mr->MarkAnchor);
1545 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem);
1546 TRACE("Offset on ligature is %i,%i design units\n",ligature_pt.x,ligature_pt.y);
1547 TRACE("Offset on mark is %i,%i design units\n",mark_pt.x, mark_pt.y);
1548 pt->x += ligature_pt.x - mark_pt.x;
1549 pt->y += ligature_pt.y - mark_pt.y;
1550 TRACE("Resulting cumulative offset is %i,%i design units\n",pt->x,pt->y);
1551 }
1552 }
1553 }
1554 else
1555 FIXME("Unhandled Mark To Ligature Format %i\n",GET_BE_WORD(mlpf1->PosFormat));
1556 }
1557 }
1558
1559 static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1560 INT glyph_count, INT ppem, LPPOINT pt)
1561 {
1562 int j;
1563 BOOL rc = FALSE;
1564 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1565
1566 TRACE("MarkToMark Attachment Positioning Subtable\n");
1567
1568 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1569 {
1570 const GPOS_MarkMarkPosFormat1 *mmpf1 = (const GPOS_MarkMarkPosFormat1 *)GPOS_get_subtable(look, j);
1571 if (GET_BE_WORD(mmpf1->PosFormat) == 1)
1572 {
1573 int offset = GET_BE_WORD(mmpf1->Mark1Coverage);
1574 int mark_index;
1575 mark_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[glyph_index]);
1576 if (mark_index != -1)
1577 {
1578 int mark2_index;
1579 offset = GET_BE_WORD(mmpf1->Mark2Coverage);
1580 mark2_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[glyph_index - write_dir]);
1581 if (mark2_index != -1)
1582 {
1583 const GPOS_MarkArray *ma;
1584 const GPOS_MarkRecord *mr;
1585 const GPOS_Mark2Array *m2a;
1586 const GPOS_Mark2Record *m2r;
1587 int mark_class;
1588 int class_count = GET_BE_WORD(mmpf1->ClassCount);
1589 int mark2record_size;
1590 POINT mark2_pt;
1591 POINT mark_pt;
1592 TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], mark2_index);
1593 offset = GET_BE_WORD(mmpf1->Mark1Array);
1594 ma = (const GPOS_MarkArray*)((const BYTE*)mmpf1 + offset);
1595 if (mark_index > GET_BE_WORD(ma->MarkCount))
1596 {
1597 ERR("Mark index exceeded mark count\n");
1598 return FALSE;
1599 }
1600 mr = &ma->MarkRecord[mark_index];
1601 mark_class = GET_BE_WORD(mr->Class);
1602 TRACE("Mark Class %i total classes %i\n",mark_class,class_count);
1603 offset = GET_BE_WORD(mmpf1->Mark2Array);
1604 m2a = (const GPOS_Mark2Array*)((const BYTE*)mmpf1 + offset);
1605 mark2record_size = class_count * sizeof(WORD);
1606 m2r = (const GPOS_Mark2Record*)((const BYTE*)m2a + sizeof(WORD) + (mark2record_size * mark2_index));
1607 offset = GET_BE_WORD(m2r->Mark2Anchor[mark_class]);
1608 GPOS_get_anchor_values((const BYTE*)m2a + offset, &mark2_pt, ppem);
1609 offset = GET_BE_WORD(mr->MarkAnchor);
1610 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem);
1611 TRACE("Offset on mark2 is %i,%i design units\n",mark2_pt.x,mark2_pt.y);
1612 TRACE("Offset on mark is %i,%i design units\n",mark_pt.x, mark_pt.y);
1613 pt->x += mark2_pt.x - mark_pt.x;
1614 pt->y += mark2_pt.y - mark_pt.y;
1615 TRACE("Resulting cumulative offset is %i,%i design units\n",pt->x,pt->y);
1616 rc = TRUE;
1617 }
1618 }
1619 }
1620 else
1621 FIXME("Unhandled Mark To Mark Format %i\n",GET_BE_WORD(mmpf1->PosFormat));
1622 }
1623 return rc;
1624 }
1625
1626 static INT GPOS_apply_ChainContextPos(ScriptCache *psc, LPOUTLINETEXTMETRICW lpotm, LPLOGFONTW lplogfont, const SCRIPT_ANALYSIS *analysis, INT* piAdvance,
1627 const OT_LookupList *lookup, const OT_LookupTable *look, const WORD *glyphs, INT glyph_index,
1628 INT glyph_count, INT ppem, GOFFSET *pGoffset)
1629 {
1630 int j;
1631 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1632
1633 TRACE("Chaining Contextual Positioning Subtable\n");
1634
1635 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1636 {
1637 int offset;
1638 const GPOS_ChainContextPosFormat3_1 *ccpf3 = (GPOS_ChainContextPosFormat3_1 *)GPOS_get_subtable(look, j);
1639 int dirLookahead = write_dir;
1640 int dirBacktrack = -1 * write_dir;
1641
1642 if (GET_BE_WORD(ccpf3->PosFormat) == 1)
1643 {
1644 FIXME(" TODO: subtype 1 (Simple Chaining Context Glyph Positioning)\n");
1645 continue;
1646 }
1647 else if (GET_BE_WORD(ccpf3->PosFormat) == 2)
1648 {
1649 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Positioning)\n");
1650 continue;
1651 }
1652 else if (GET_BE_WORD(ccpf3->PosFormat) == 3)
1653 {
1654 int k;
1655 int indexGlyphs;
1656 const GPOS_ChainContextPosFormat3_2 *ccpf3_2;
1657 const GPOS_ChainContextPosFormat3_3 *ccpf3_3;
1658 const GPOS_ChainContextPosFormat3_4 *ccpf3_4;
1659
1660 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Positioning)\n");
1661
1662 for (k = 0; k < GET_BE_WORD(ccpf3->BacktrackGlyphCount); k++)
1663 {
1664 offset = GET_BE_WORD(ccpf3->Coverage[k]);
1665 if (GSUB_is_glyph_covered((const BYTE*)ccpf3+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
1666 break;
1667 }
1668 if (k != GET_BE_WORD(ccpf3->BacktrackGlyphCount))
1669 continue;
1670 TRACE("Matched Backtrack\n");
1671
1672 ccpf3_2 = (const GPOS_ChainContextPosFormat3_2*)((BYTE *)ccpf3 +
1673 FIELD_OFFSET(GPOS_ChainContextPosFormat3_1, Coverage[GET_BE_WORD(ccpf3->BacktrackGlyphCount)]));
1674
1675 indexGlyphs = GET_BE_WORD(ccpf3_2->InputGlyphCount);
1676 for (k = 0; k < indexGlyphs; k++)
1677 {
1678 offset = GET_BE_WORD(ccpf3_2->Coverage[k]);
1679 if (GSUB_is_glyph_covered((const BYTE*)ccpf3+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
1680 break;
1681 }
1682 if (k != indexGlyphs)
1683 continue;
1684 TRACE("Matched IndexGlyphs\n");
1685
1686 ccpf3_3 = (const GPOS_ChainContextPosFormat3_3*)((BYTE *)ccpf3_2 +
1687 FIELD_OFFSET(GPOS_ChainContextPosFormat3_2, Coverage[GET_BE_WORD(ccpf3_2->InputGlyphCount)]));
1688
1689 for (k = 0; k < GET_BE_WORD(ccpf3_3->LookaheadGlyphCount); k++)
1690 {
1691 offset = GET_BE_WORD(ccpf3_3->Coverage[k]);
1692 if (GSUB_is_glyph_covered((const BYTE*)ccpf3+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
1693 break;
1694 }
1695 if (k != GET_BE_WORD(ccpf3_3->LookaheadGlyphCount))
1696 continue;
1697 TRACE("Matched LookAhead\n");
1698
1699 ccpf3_4 = (const GPOS_ChainContextPosFormat3_4*)((BYTE *)ccpf3_3 +
1700 FIELD_OFFSET(GPOS_ChainContextPosFormat3_3, Coverage[GET_BE_WORD(ccpf3_3->LookaheadGlyphCount)]));
1701
1702 if (GET_BE_WORD(ccpf3_4->PosCount))
1703 {
1704 for (k = 0; k < GET_BE_WORD(ccpf3_4->PosCount); k++)
1705 {
1706 int lookupIndex = GET_BE_WORD(ccpf3_4->PosLookupRecord[k].LookupListIndex);
1707 int SequenceIndex = GET_BE_WORD(ccpf3_4->PosLookupRecord[k].SequenceIndex) * write_dir;
1708
1709 TRACE("Position: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
1710 GPOS_apply_lookup(psc, lpotm, lplogfont, analysis, piAdvance, lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, glyph_count, pGoffset);
1711 }
1712 return glyph_index + indexGlyphs + GET_BE_WORD(ccpf3_3->LookaheadGlyphCount);
1713 }
1714 else return glyph_index + 1;
1715 }
1716 else
1717 FIXME("Unhandled Chaining Contextual Positioning Format %i\n",GET_BE_WORD(ccpf3->PosFormat));
1718 }
1719 return glyph_index + 1;
1720 }
1721
1722 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)
1723 {
1724 int offset;
1725 const OT_LookupTable *look;
1726 int ppem = lpotm->otmTextMetrics.tmAscent + lpotm->otmTextMetrics.tmDescent - lpotm->otmTextMetrics.tmInternalLeading;
1727 WORD type;
1728
1729 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
1730 look = (const OT_LookupTable*)((const BYTE*)lookup + offset);
1731 type = GET_BE_WORD(look->LookupType);
1732 TRACE("type %i, flag %x, subtables %i\n",type,GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
1733 if (type == 9)
1734 {
1735 if (GET_BE_WORD(look->SubTableCount))
1736 {
1737 const GPOS_ExtensionPosFormat1 *ext = (const GPOS_ExtensionPosFormat1 *)((const BYTE *)look + GET_BE_WORD(look->SubTable[0]));
1738 if (GET_BE_WORD(ext->PosFormat) == 1)
1739 {
1740 type = GET_BE_WORD(ext->ExtensionLookupType);
1741 TRACE("extension type %i\n",type);
1742 }
1743 else
1744 {
1745 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext->PosFormat));
1746 }
1747 }
1748 else
1749 {
1750 WARN("lookup type is Extension Positioning but no extension subtable exists\n");
1751 }
1752 }
1753 switch (type)
1754 {
1755 case 1:
1756 {
1757 double devX, devY;
1758 POINT adjust = {0,0};
1759 POINT advance = {0,0};
1760 GPOS_apply_SingleAdjustment(look, analysis, glyphs, glyph_index, glyph_count, ppem, &adjust, &advance);
1761 if (adjust.x || adjust.y)
1762 {
1763 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust.x, adjust.y, &devX, &devY);
1764 pGoffset[glyph_index].du += round(devX);
1765 pGoffset[glyph_index].dv += round(devY);
1766 }
1767 if (advance.x || advance.y)
1768 {
1769 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance.x, advance.y, &devX, &devY);
1770 piAdvance[glyph_index] += round(devX);
1771 if (advance.y)
1772 FIXME("Unhandled adjustment to Y advancement\n");
1773 }
1774 break;
1775 }
1776 case 2:
1777 {
1778 POINT advance[2]= {{0,0},{0,0}};
1779 POINT adjust[2]= {{0,0},{0,0}};
1780 double devX, devY;
1781 int index;
1782 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1783 int offset_sign = (analysis->fRTL && analysis->fLogicalOrder) ? -1 : 1;
1784
1785 index = GPOS_apply_PairAdjustment(look, analysis, glyphs, glyph_index, glyph_count, ppem, adjust, advance);
1786 if (adjust[0].x || adjust[0].y)
1787 {
1788 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust[0].x, adjust[0].y, &devX, &devY);
1789 pGoffset[glyph_index].du += round(devX) * offset_sign;
1790 pGoffset[glyph_index].dv += round(devY);
1791 }
1792 if (advance[0].x || advance[0].y)
1793 {
1794 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance[0].x, advance[0].y, &devX, &devY);
1795 piAdvance[glyph_index] += round(devX);
1796 }
1797 if (adjust[1].x || adjust[1].y)
1798 {
1799 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust[1].x, adjust[1].y, &devX, &devY);
1800 pGoffset[glyph_index + write_dir].du += round(devX) * offset_sign;
1801 pGoffset[glyph_index + write_dir].dv += round(devY);
1802 }
1803 if (advance[1].x || advance[1].y)
1804 {
1805 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance[1].x, advance[1].y, &devX, &devY);
1806 piAdvance[glyph_index + write_dir] += round(devX);
1807 }
1808 return index;
1809 }
1810 case 3:
1811 {
1812 POINT desU = {0,0};
1813 double devX, devY;
1814 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1815
1816 GPOS_apply_CursiveAttachment(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU);
1817 if (desU.x || desU.y)
1818 {
1819 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
1820 /* Windows does not appear to apply X offsets here */
1821 pGoffset[glyph_index].dv = round(devY) + pGoffset[glyph_index+write_dir].dv;
1822 }
1823 break;
1824 }
1825 case 4:
1826 {
1827 double devX, devY;
1828 POINT desU = {0,0};
1829 int base_index = GPOS_apply_MarkToBase(psc, look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU);
1830 if (base_index != -1)
1831 {
1832 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
1833 if (!analysis->fRTL) pGoffset[glyph_index].du = round(devX) - piAdvance[base_index];
1834 else
1835 {
1836 if (analysis->fLogicalOrder) devX *= -1;
1837 pGoffset[glyph_index].du = round(devX);
1838 }
1839 pGoffset[glyph_index].dv = round(devY);
1840 }
1841 break;
1842 }
1843 case 5:
1844 {
1845 double devX, devY;
1846 POINT desU = {0,0};
1847 GPOS_apply_MarkToLigature(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU);
1848 if (desU.x || desU.y)
1849 {
1850 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
1851 pGoffset[glyph_index].du = (round(devX) - piAdvance[glyph_index-1]);
1852 pGoffset[glyph_index].dv = round(devY);
1853 }
1854 break;
1855 }
1856 case 6:
1857 {
1858 double devX, devY;
1859 POINT desU = {0,0};
1860 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1861 if (GPOS_apply_MarkToMark(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU))
1862 {
1863 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
1864 if (analysis->fRTL && analysis->fLogicalOrder) devX *= -1;
1865 pGoffset[glyph_index].du = round(devX) + pGoffset[glyph_index - write_dir].du;
1866 pGoffset[glyph_index].dv = round(devY) + pGoffset[glyph_index - write_dir].dv;
1867 }
1868 break;
1869 }
1870 case 8:
1871 {
1872 return GPOS_apply_ChainContextPos(psc, lpotm, lplogfont, analysis, piAdvance, lookup, look, glyphs, glyph_index, glyph_count, ppem, pGoffset);
1873 }
1874 default:
1875 FIXME("We do not handle SubType %i\n",type);
1876 }
1877 return glyph_index+1;
1878 }
1879
1880 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)
1881 {
1882 const GPOS_Header *header = (const GPOS_Header *)psc->GPOS_Table;
1883 const OT_LookupList *lookup = (const OT_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1884
1885 return GPOS_apply_lookup(psc, lpotm, lplogfont, analysis, piAdvance, lookup, lookup_index, glyphs, glyph_index, glyph_count, pGoffset);
1886 }
1887
1888 static void GSUB_initialize_script_cache(ScriptCache *psc)
1889 {
1890 int i;
1891
1892 if (psc->GSUB_Table)
1893 {
1894 const OT_ScriptList *script;
1895 const GSUB_Header* header = (const GSUB_Header*)psc->GSUB_Table;
1896 script = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
1897 psc->script_count = GET_BE_WORD(script->ScriptCount);
1898 TRACE("initializing %i scripts in this font\n",psc->script_count);
1899 if (psc->script_count)
1900 {
1901 psc->scripts = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(LoadedScript) * psc->script_count);
1902 for (i = 0; i < psc->script_count; i++)
1903 {
1904 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
1905 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]);
1906 psc->scripts[i].gsub_table = ((const BYTE*)script + offset);
1907 }
1908 }
1909 }
1910 }
1911
1912 static void GPOS_expand_script_cache(ScriptCache *psc)
1913 {
1914 int i, count;
1915 const OT_ScriptList *script;
1916 const GPOS_Header* header = (const GPOS_Header*)psc->GPOS_Table;
1917
1918 if (!header)
1919 return;
1920
1921 script = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
1922 count = GET_BE_WORD(script->ScriptCount);
1923
1924 if (!count)
1925 return;
1926
1927 if (!psc->script_count)
1928 {
1929 psc->script_count = count;
1930 TRACE("initializing %i scripts in this font\n",psc->script_count);
1931 if (psc->script_count)
1932 {
1933 psc->scripts = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(LoadedScript) * psc->script_count);
1934 for (i = 0; i < psc->script_count; i++)
1935 {
1936 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
1937 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]);
1938 psc->scripts[i].gpos_table = ((const BYTE*)script + offset);
1939 }
1940 }
1941 }
1942 else
1943 {
1944 for (i = 0; i < count; i++)
1945 {
1946 int j;
1947 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
1948 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]);
1949 for (j = 0; j < psc->script_count; j++)
1950 {
1951 if (psc->scripts[j].tag == tag)
1952 {
1953 psc->scripts[j].gpos_table = ((const BYTE*)script + offset);
1954 break;
1955 }
1956 }
1957 if (j == psc->script_count)
1958 {
1959 psc->script_count++;
1960 psc->scripts = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,psc->scripts, sizeof(LoadedScript) * psc->script_count);
1961 psc->scripts[j].tag = tag;
1962 psc->scripts[j].gpos_table = ((const BYTE*)script + offset);
1963 }
1964 }
1965 }
1966 }
1967
1968 static void _initialize_script_cache(ScriptCache *psc)
1969 {
1970 if (!psc->scripts_initialized)
1971 {
1972 GSUB_initialize_script_cache(psc);
1973 GPOS_expand_script_cache(psc);
1974 psc->scripts_initialized = TRUE;
1975 }
1976 }
1977
1978 HRESULT OpenType_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
1979 {
1980 int i;
1981 HRESULT rc = S_OK;
1982
1983 _initialize_script_cache(psc);
1984
1985 *pcTags = psc->script_count;
1986
1987 if (!searchingFor && cMaxTags < *pcTags)
1988 rc = E_OUTOFMEMORY;
1989 else if (searchingFor)
1990 rc = USP_E_SCRIPT_NOT_IN_FONT;
1991
1992 for (i = 0; i < psc->script_count; i++)
1993 {
1994 if (i < cMaxTags)
1995 pScriptTags[i] = psc->scripts[i].tag;
1996
1997 if (searchingFor)
1998 {
1999 if (searchingFor == psc->scripts[i].tag)
2000 {
2001 pScriptTags[0] = psc->scripts[i].tag;
2002 *pcTags = 1;
2003 rc = S_OK;
2004 break;
2005 }
2006 }
2007 }
2008 return rc;
2009 }
2010
2011 static void GSUB_initialize_language_cache(LoadedScript *script)
2012 {
2013 int i;
2014
2015 if (script->gsub_table)
2016 {
2017 DWORD offset;
2018 const OT_Script* table = script->gsub_table;
2019 script->language_count = GET_BE_WORD(table->LangSysCount);
2020 offset = GET_BE_WORD(table->DefaultLangSys);
2021 if (offset)
2022 {
2023 script->default_language.tag = MS_MAKE_TAG('d','f','l','t');
2024 script->default_language.gsub_table = (const BYTE*)table + offset;
2025 }
2026
2027 if (script->language_count)
2028 {
2029 TRACE("Deflang %p, LangCount %i\n",script->default_language.gsub_table, script->language_count);
2030
2031 script->languages = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LoadedLanguage) * script->language_count);
2032
2033 for (i = 0; i < script->language_count; i++)
2034 {
2035 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
2036 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]);
2037 script->languages[i].gsub_table = ((const BYTE*)table + offset);
2038 }
2039 }
2040 }
2041 }
2042
2043 static void GPOS_expand_language_cache(LoadedScript *script)
2044 {
2045 int count;
2046 const OT_Script* table = script->gpos_table;
2047 DWORD offset;
2048
2049 if (!table)
2050 return;
2051
2052 offset = GET_BE_WORD(table->DefaultLangSys);
2053 if (offset)
2054 script->default_language.gpos_table = (const BYTE*)table + offset;
2055
2056 count = GET_BE_WORD(table->LangSysCount);
2057
2058 TRACE("Deflang %p, LangCount %i\n",script->default_language.gpos_table, count);
2059
2060 if (!count)
2061 return;
2062
2063 if (!script->language_count)
2064 {
2065 int i;
2066 script->language_count = count;
2067
2068 script->languages = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LoadedLanguage) * script->language_count);
2069
2070 for (i = 0; i < script->language_count; i++)
2071 {
2072 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
2073 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]);
2074 script->languages[i].gpos_table = ((const BYTE*)table + offset);
2075 }
2076 }
2077 else if (count)
2078 {
2079 int i,j;
2080 for (i = 0; i < count; i++)
2081 {
2082 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
2083 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]);
2084
2085 for (j = 0; j < script->language_count; j++)
2086 {
2087 if (script->languages[j].tag == tag)
2088 {
2089 script->languages[j].gpos_table = ((const BYTE*)table + offset);
2090 break;
2091 }
2092 }
2093 if (j == script->language_count)
2094 {
2095 script->language_count++;
2096 script->languages = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,script->languages, sizeof(LoadedLanguage) * script->language_count);
2097 script->languages[j].tag = tag;
2098 script->languages[j].gpos_table = ((const BYTE*)table + offset);
2099 }
2100 }
2101 }
2102 }
2103
2104 static void _initialize_language_cache(LoadedScript *script)
2105 {
2106 if (!script->languages_initialized)
2107 {
2108 GSUB_initialize_language_cache(script);
2109 GPOS_expand_language_cache(script);
2110 script->languages_initialized = TRUE;
2111 }
2112 }
2113
2114 HRESULT OpenType_GetFontLanguageTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pLanguageTags, int *pcTags)
2115 {
2116 int i;
2117 HRESULT rc = S_OK;
2118 LoadedScript *script = NULL;
2119
2120 _initialize_script_cache(psc);
2121
2122 for (i = 0; i < psc->script_count; i++)
2123 {
2124 if (psc->scripts[i].tag == script_tag)
2125 {
2126 script = &psc->scripts[i];
2127 break;
2128 }
2129 }
2130
2131 if (!script)
2132 return E_INVALIDARG;
2133
2134 _initialize_language_cache(script);
2135
2136 if (!searchingFor && cMaxTags < script->language_count)
2137 rc = E_OUTOFMEMORY;
2138 else if (searchingFor)
2139 rc = E_INVALIDARG;
2140
2141 *pcTags = script->language_count;
2142
2143 for (i = 0; i < script->language_count; i++)
2144 {
2145 if (i < cMaxTags)
2146 pLanguageTags[i] = script->languages[i].tag;
2147
2148 if (searchingFor)
2149 {
2150 if (searchingFor == script->languages[i].tag)
2151 {
2152 pLanguageTags[0] = script->languages[i].tag;
2153 *pcTags = 1;
2154 rc = S_OK;
2155 break;
2156 }
2157 }
2158 }
2159
2160 if (script->default_language.gsub_table)
2161 {
2162 if (i < cMaxTags)
2163 pLanguageTags[i] = script->default_language.tag;
2164
2165 if (searchingFor && FAILED(rc))
2166 {
2167 pLanguageTags[0] = script->default_language.tag;
2168 }
2169 i++;
2170 *pcTags = (*pcTags) + 1;
2171 }
2172
2173 return rc;
2174 }
2175
2176
2177 static void GSUB_initialize_feature_cache(LPCVOID table, LoadedLanguage *language)
2178 {
2179 int i;
2180
2181 if (language->gsub_table)
2182 {
2183 const OT_LangSys *lang = language->gsub_table;
2184 const GSUB_Header *header = (const GSUB_Header *)table;
2185 const OT_FeatureList *feature_list;
2186
2187 language->feature_count = GET_BE_WORD(lang->FeatureCount);
2188 TRACE("%i features\n",language->feature_count);
2189
2190 if (language->feature_count)
2191 {
2192 language->features = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature)*language->feature_count);
2193
2194 feature_list = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
2195
2196 for (i = 0; i < language->feature_count; i++)
2197 {
2198 const OT_Feature *feature;
2199 int j;
2200 int index = GET_BE_WORD(lang->FeatureIndex[i]);
2201
2202 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]);
2203 language->features[i].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
2204 feature = (const OT_Feature*)language->features[i].feature;
2205 language->features[i].lookup_count = GET_BE_WORD(feature->LookupCount);
2206 language->features[i].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[i].lookup_count);
2207 for (j = 0; j < language->features[i].lookup_count; j++)
2208 language->features[i].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
2209 language->features[i].tableType = FEATURE_GSUB_TABLE;
2210 }
2211 }
2212 }
2213 }
2214
2215 static void GPOS_expand_feature_cache(LPCVOID table, LoadedLanguage *language)
2216 {
2217 int i, count;
2218 const OT_LangSys *lang = language->gpos_table;
2219 const GPOS_Header *header = (const GPOS_Header *)table;
2220 const OT_FeatureList *feature_list;
2221
2222 if (!lang)
2223 return;
2224
2225 count = GET_BE_WORD(lang->FeatureCount);
2226 feature_list = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
2227
2228 TRACE("%i features\n",count);
2229
2230 if (!count)
2231 return;
2232
2233 if (!language->feature_count)
2234 {
2235 language->feature_count = count;
2236
2237 if (language->feature_count)
2238 {
2239 language->features = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature)*language->feature_count);
2240
2241 for (i = 0; i < language->feature_count; i++)
2242 {
2243 const OT_Feature *feature;
2244 int j;
2245 int index = GET_BE_WORD(lang->FeatureIndex[i]);
2246
2247 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]);
2248 language->features[i].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
2249 feature = (const OT_Feature*)language->features[i].feature;
2250 language->features[i].lookup_count = GET_BE_WORD(feature->LookupCount);
2251 language->features[i].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[i].lookup_count);
2252 for (j = 0; j < language->features[i].lookup_count; j++)
2253 language->features[i].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
2254 language->features[i].tableType = FEATURE_GPOS_TABLE;
2255 }
2256 }
2257 }
2258 else
2259 {
2260 language->features = HeapReAlloc(GetProcessHeap(),0,language->features, sizeof(LoadedFeature)*(language->feature_count + count));
2261
2262 for (i = 0; i < count; i++)
2263 {
2264 const OT_Feature *feature;
2265 int j;
2266 int index = GET_BE_WORD(lang->FeatureIndex[i]);
2267 int idx = language->feature_count + i;
2268
2269 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]);
2270 language->features[idx].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
2271 feature = (const OT_Feature*)language->features[idx].feature;
2272 language->features[idx].lookup_count = GET_BE_WORD(feature->LookupCount);
2273 language->features[idx].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[idx].lookup_count);
2274 for (j = 0; j < language->features[idx].lookup_count; j++)
2275 language->features[idx].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
2276 language->features[idx].tableType = FEATURE_GPOS_TABLE;
2277 }
2278 language->feature_count += count;
2279 }
2280 }
2281
2282 static void _initialize_feature_cache(ScriptCache *psc, LoadedLanguage *language)
2283 {
2284 if (!language->features_initialized)
2285 {
2286 GSUB_initialize_feature_cache(psc->GSUB_Table, language);
2287 GPOS_expand_feature_cache(psc->GPOS_Table, language);
2288 language->features_initialized = TRUE;
2289 }
2290 }
2291
2292 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)
2293 {
2294 int i;
2295 HRESULT rc = S_OK;
2296 LoadedScript *script = NULL;
2297 LoadedLanguage *language = NULL;
2298
2299 _initialize_script_cache(psc);
2300
2301 for (i = 0; i < psc->script_count; i++)
2302 {
2303 if (psc->scripts[i].tag == script_tag)
2304 {
2305 script = &psc->scripts[i];
2306 break;
2307 }
2308 }
2309
2310 if (!script)
2311 {
2312 *pcTags = 0;
2313 if (!filtered)
2314 return S_OK;
2315 else
2316 return E_INVALIDARG;
2317 }
2318
2319 _initialize_language_cache(script);
2320
2321 if ((script->default_language.gsub_table || script->default_language.gpos_table) && script->default_language.tag == language_tag)
2322 language = &script->default_language;
2323 else
2324 {
2325 for (i = 0; i < script->language_count; i++)
2326 {
2327 if (script->languages[i].tag == language_tag)
2328 {
2329 language = &script->languages[i];
2330 break;
2331 }
2332 }
2333 }
2334
2335 if (!language)
2336 {
2337 *pcTags = 0;
2338 return S_OK;
2339 }
2340
2341 _initialize_feature_cache(psc, language);
2342
2343 if (tableType)
2344 {
2345 *pcTags = 0;
2346 for (i = 0; i < language->feature_count; i++)
2347 if (language->features[i].tableType == tableType)
2348 *pcTags = (*pcTags)+1;
2349 }
2350 else
2351 *pcTags = language->feature_count;
2352
2353 if (!searchingFor && cMaxTags < *pcTags)
2354 rc = E_OUTOFMEMORY;
2355 else if (searchingFor)
2356 rc = E_INVALIDARG;
2357
2358 for (i = 0; i < language->feature_count; i++)
2359 {
2360 if (i < cMaxTags)
2361 {
2362 if (!tableType || language->features[i].tableType == tableType)
2363 pFeatureTags[i] = language->features[i].tag;
2364 }
2365
2366 if (searchingFor)
2367 {
2368 if ((searchingFor == language->features[i].tag) &&
2369 (!tableType || language->features[i].tableType == tableType))
2370 {
2371 pFeatureTags[0] = language->features[i].tag;
2372 *pcTags = 1;
2373 if (feature)
2374 *feature = &language->features[i];
2375 rc = S_OK;
2376 break;
2377 }
2378 }
2379 }
2380 return rc;
2381 }