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