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