usp10: MarkToMark calculations may need to happen even if the offset is 0.
[wine/multimedia.git] / dlls / usp10 / opentype.c
blobca4050cb9775b07176eb3a799cba07ebb02efc31
1 /*
2 * Opentype font interfaces for the Uniscribe Script Processor (usp10.dll)
4 * Copyright 2012 CodeWeavers, Aric Stewart
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.
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.
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
21 #include <stdarg.h>
22 #include <stdlib.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winnls.h"
29 #include "usp10.h"
30 #include "winternl.h"
32 #include "usp10_internal.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
38 #ifdef WORDS_BIGENDIAN
39 #define GET_BE_WORD(x) (x)
40 #define GET_BE_DWORD(x) (x)
41 #else
42 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
43 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
44 #endif
46 #define round(x) (((x) < 0) ? (int)((x) - 0.5) : (int)((x) + 0.5))
48 /* These are all structures needed for the cmap format 12 table */
49 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p')
51 typedef struct {
52 WORD platformID;
53 WORD encodingID;
54 DWORD offset;
55 } CMAP_EncodingRecord;
57 typedef struct {
58 WORD version;
59 WORD numTables;
60 CMAP_EncodingRecord tables[1];
61 } CMAP_Header;
63 typedef struct {
64 DWORD startCharCode;
65 DWORD endCharCode;
66 DWORD startGlyphID;
67 } CMAP_SegmentedCoverage_group;
69 typedef struct {
70 WORD format;
71 WORD reserved;
72 DWORD length;
73 DWORD language;
74 DWORD nGroups;
75 CMAP_SegmentedCoverage_group groups[1];
76 } CMAP_SegmentedCoverage;
78 /* These are all structures needed for the GDEF table */
79 #define GDEF_TAG MS_MAKE_TAG('G', 'D', 'E', 'F')
81 enum {BaseGlyph=1, LigatureGlyph, MarkGlyph, ComponentGlyph};
83 typedef struct {
84 DWORD Version;
85 WORD GlyphClassDef;
86 WORD AttachList;
87 WORD LigCaretList;
88 WORD MarkAttachClassDef;
89 } GDEF_Header;
91 typedef struct {
92 WORD ClassFormat;
93 WORD StartGlyph;
94 WORD GlyphCount;
95 WORD ClassValueArray[1];
96 } OT_ClassDefFormat1;
98 typedef struct {
99 WORD Start;
100 WORD End;
101 WORD Class;
102 } OT_ClassRangeRecord;
104 typedef struct {
105 WORD ClassFormat;
106 WORD ClassRangeCount;
107 OT_ClassRangeRecord ClassRangeRecord[1];
108 } OT_ClassDefFormat2;
110 /* These are all structures needed for the GSUB table */
112 typedef struct {
113 DWORD version;
114 WORD ScriptList;
115 WORD FeatureList;
116 WORD LookupList;
117 } GSUB_Header;
119 typedef struct {
120 CHAR ScriptTag[4];
121 WORD Script;
122 } OT_ScriptRecord;
124 typedef struct {
125 WORD ScriptCount;
126 OT_ScriptRecord ScriptRecord[1];
127 } OT_ScriptList;
129 typedef struct {
130 CHAR LangSysTag[4];
131 WORD LangSys;
132 } OT_LangSysRecord;
134 typedef struct {
135 WORD DefaultLangSys;
136 WORD LangSysCount;
137 OT_LangSysRecord LangSysRecord[1];
138 } OT_Script;
140 typedef struct {
141 WORD LookupOrder; /* Reserved */
142 WORD ReqFeatureIndex;
143 WORD FeatureCount;
144 WORD FeatureIndex[1];
145 } OT_LangSys;
147 typedef struct {
148 CHAR FeatureTag[4];
149 WORD Feature;
150 } OT_FeatureRecord;
152 typedef struct {
153 WORD FeatureCount;
154 OT_FeatureRecord FeatureRecord[1];
155 } OT_FeatureList;
157 typedef struct {
158 WORD FeatureParams; /* Reserved */
159 WORD LookupCount;
160 WORD LookupListIndex[1];
161 } OT_Feature;
163 typedef struct {
164 WORD LookupCount;
165 WORD Lookup[1];
166 } OT_LookupList;
168 typedef struct {
169 WORD LookupType;
170 WORD LookupFlag;
171 WORD SubTableCount;
172 WORD SubTable[1];
173 } OT_LookupTable;
175 typedef struct {
176 WORD CoverageFormat;
177 WORD GlyphCount;
178 WORD GlyphArray[1];
179 } OT_CoverageFormat1;
181 typedef struct {
182 WORD Start;
183 WORD End;
184 WORD StartCoverageIndex;
185 } OT_RangeRecord;
187 typedef struct {
188 WORD CoverageFormat;
189 WORD RangeCount;
190 OT_RangeRecord RangeRecord[1];
191 } OT_CoverageFormat2;
193 typedef struct {
194 WORD SubstFormat; /* = 1 */
195 WORD Coverage;
196 WORD DeltaGlyphID;
197 } GSUB_SingleSubstFormat1;
199 typedef struct {
200 WORD SubstFormat; /* = 2 */
201 WORD Coverage;
202 WORD GlyphCount;
203 WORD Substitute[1];
204 }GSUB_SingleSubstFormat2;
206 typedef struct {
207 WORD SubstFormat; /* = 1 */
208 WORD Coverage;
209 WORD SequenceCount;
210 WORD Sequence[1];
211 }GSUB_MultipleSubstFormat1;
213 typedef struct {
214 WORD GlyphCount;
215 WORD Substitute[1];
216 }GSUB_Sequence;
218 typedef struct {
219 WORD SubstFormat; /* = 1 */
220 WORD Coverage;
221 WORD LigSetCount;
222 WORD LigatureSet[1];
223 }GSUB_LigatureSubstFormat1;
225 typedef struct {
226 WORD LigatureCount;
227 WORD Ligature[1];
228 }GSUB_LigatureSet;
230 typedef struct{
231 WORD LigGlyph;
232 WORD CompCount;
233 WORD Component[1];
234 }GSUB_Ligature;
236 typedef struct{
237 WORD SequenceIndex;
238 WORD LookupListIndex;
240 }GSUB_SubstLookupRecord;
242 typedef struct{
243 WORD SubstFormat; /* = 1 */
244 WORD Coverage;
245 WORD ChainSubRuleSetCount;
246 WORD ChainSubRuleSet[1];
247 }GSUB_ChainContextSubstFormat1;
249 typedef struct {
250 WORD SubstFormat; /* = 3 */
251 WORD BacktrackGlyphCount;
252 WORD Coverage[1];
253 }GSUB_ChainContextSubstFormat3_1;
255 typedef struct{
256 WORD InputGlyphCount;
257 WORD Coverage[1];
258 }GSUB_ChainContextSubstFormat3_2;
260 typedef struct{
261 WORD LookaheadGlyphCount;
262 WORD Coverage[1];
263 }GSUB_ChainContextSubstFormat3_3;
265 typedef struct{
266 WORD SubstCount;
267 GSUB_SubstLookupRecord SubstLookupRecord[1];
268 }GSUB_ChainContextSubstFormat3_4;
270 typedef struct {
271 WORD SubstFormat; /* = 1 */
272 WORD Coverage;
273 WORD AlternateSetCount;
274 WORD AlternateSet[1];
275 } GSUB_AlternateSubstFormat1;
277 typedef struct{
278 WORD GlyphCount;
279 WORD Alternate[1];
280 } GSUB_AlternateSet;
282 /* These are all structures needed for the GPOS table */
284 typedef struct {
285 DWORD version;
286 WORD ScriptList;
287 WORD FeatureList;
288 WORD LookupList;
289 } GPOS_Header;
291 typedef struct {
292 WORD StartSize;
293 WORD EndSize;
294 WORD DeltaFormat;
295 WORD DeltaValue[1];
296 } OT_DeviceTable;
298 typedef struct {
299 WORD AnchorFormat;
300 WORD XCoordinate;
301 WORD YCoordinate;
302 } GPOS_AnchorFormat1;
304 typedef struct {
305 WORD AnchorFormat;
306 WORD XCoordinate;
307 WORD YCoordinate;
308 WORD AnchorPoint;
309 } GPOS_AnchorFormat2;
311 typedef struct {
312 WORD AnchorFormat;
313 WORD XCoordinate;
314 WORD YCoordinate;
315 WORD XDeviceTable;
316 WORD YDeviceTable;
317 } GPOS_AnchorFormat3;
319 typedef struct {
320 WORD XPlacement;
321 WORD YPlacement;
322 WORD XAdvance;
323 WORD YAdvance;
324 WORD XPlaDevice;
325 WORD YPlaDevice;
326 WORD XAdvDevice;
327 WORD YAdvDevice;
328 } GPOS_ValueRecord;
330 typedef struct {
331 WORD PosFormat;
332 WORD Coverage;
333 WORD ValueFormat;
334 WORD Value[1];
335 } GPOS_SinglePosFormat1;
337 typedef struct {
338 WORD PosFormat;
339 WORD Coverage;
340 WORD ValueFormat;
341 WORD ValueCount;
342 WORD Value[1];
343 } GPOS_SinglePosFormat2;
345 typedef struct {
346 WORD PosFormat;
347 WORD Coverage;
348 WORD ValueFormat1;
349 WORD ValueFormat2;
350 WORD PairSetCount;
351 WORD PairSetOffset[1];
352 } GPOS_PairPosFormat1;
354 typedef struct {
355 WORD PosFormat;
356 WORD Coverage;
357 WORD ValueFormat1;
358 WORD ValueFormat2;
359 WORD ClassDef1;
360 WORD ClassDef2;
361 WORD Class1Count;
362 WORD Class2Count;
363 WORD Class1Record[1];
364 } GPOS_PairPosFormat2;
366 typedef struct {
367 WORD SecondGlyph;
368 WORD Value1[1];
369 WORD Value2[1];
370 } GPOS_PairValueRecord;
372 typedef struct {
373 WORD PairValueCount;
374 GPOS_PairValueRecord PairValueRecord[1];
375 } GPOS_PairSet;
377 typedef struct {
378 WORD EntryAnchor;
379 WORD ExitAnchor;
380 } GPOS_EntryExitRecord;
382 typedef struct {
383 WORD PosFormat;
384 WORD Coverage;
385 WORD EntryExitCount;
386 GPOS_EntryExitRecord EntryExitRecord[1];
387 } GPOS_CursivePosFormat1;
389 typedef struct {
390 WORD PosFormat;
391 WORD MarkCoverage;
392 WORD BaseCoverage;
393 WORD ClassCount;
394 WORD MarkArray;
395 WORD BaseArray;
396 } GPOS_MarkBasePosFormat1;
398 typedef struct {
399 WORD BaseAnchor[1];
400 } GPOS_BaseRecord;
402 typedef struct {
403 WORD BaseCount;
404 GPOS_BaseRecord BaseRecord[1];
405 } GPOS_BaseArray;
407 typedef struct {
408 WORD Class;
409 WORD MarkAnchor;
410 } GPOS_MarkRecord;
412 typedef struct {
413 WORD MarkCount;
414 GPOS_MarkRecord MarkRecord[1];
415 } GPOS_MarkArray;
417 typedef struct {
418 WORD PosFormat;
419 WORD MarkCoverage;
420 WORD LigatureCoverage;
421 WORD ClassCount;
422 WORD MarkArray;
423 WORD LigatureArray;
424 } GPOS_MarkLigPosFormat1;
426 typedef struct {
427 WORD LigatureCount;
428 WORD LigatureAttach[1];
429 } GPOS_LigatureArray;
431 typedef struct {
432 WORD LigatureAnchor[1];
433 } GPOS_ComponentRecord;
435 typedef struct {
436 WORD ComponentCount;
437 GPOS_ComponentRecord ComponentRecord[1];
438 } GPOS_LigatureAttach;
440 typedef struct {
441 WORD PosFormat;
442 WORD Mark1Coverage;
443 WORD Mark2Coverage;
444 WORD ClassCount;
445 WORD Mark1Array;
446 WORD Mark2Array;
447 } GPOS_MarkMarkPosFormat1;
449 typedef struct {
450 WORD Mark2Anchor[1];
451 } GPOS_Mark2Record;
453 typedef struct {
454 WORD Mark2Count;
455 GPOS_Mark2Record Mark2Record[1];
456 } GPOS_Mark2Array;
458 typedef struct {
459 WORD SequenceIndex;
460 WORD LookupListIndex;
461 } GPOS_PosLookupRecord;
463 typedef struct {
464 WORD PosFormat;
465 WORD BacktrackGlyphCount;
466 WORD Coverage[1];
467 } GPOS_ChainContextPosFormat3_1;
469 typedef struct {
470 WORD InputGlyphCount;
471 WORD Coverage[1];
472 } GPOS_ChainContextPosFormat3_2;
474 typedef struct {
475 WORD LookaheadGlyphCount;
476 WORD Coverage[1];
477 } GPOS_ChainContextPosFormat3_3;
479 typedef struct {
480 WORD PosCount;
481 GPOS_PosLookupRecord PosLookupRecord[1];
482 } GPOS_ChainContextPosFormat3_4;
484 /**********
485 * CMAP
486 **********/
488 static VOID *load_CMAP_format12_table(HDC hdc, ScriptCache *psc)
490 CMAP_Header *CMAP_Table = NULL;
491 int length;
492 int i;
494 if (!psc->CMAP_Table)
496 length = GetFontData(hdc, CMAP_TAG , 0, NULL, 0);
497 if (length != GDI_ERROR)
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);
503 else
504 return NULL;
507 CMAP_Table = psc->CMAP_Table;
509 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++)
511 if ( (GET_BE_WORD(CMAP_Table->tables[i].platformID) == 3) &&
512 (GET_BE_WORD(CMAP_Table->tables[i].encodingID) == 10) )
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;
519 return NULL;
522 static int compare_group(const void *a, const void* b)
524 const DWORD *chr = a;
525 const CMAP_SegmentedCoverage_group *group = b;
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;
534 DWORD OpenType_CMAP_GetGlyphIndex(HDC hdc, ScriptCache *psc, DWORD utf32c, LPWORD pgi, DWORD flags)
536 /* BMP: use gdi32 for ease */
537 if (utf32c < 0x10000)
539 WCHAR ch = utf32c;
540 return GetGlyphIndicesW(hdc,&ch, 1, pgi, flags);
543 if (!psc->CMAP_format12_Table)
544 psc->CMAP_format12_Table = load_CMAP_format12_table(hdc, psc);
546 if (flags & GGI_MARK_NONEXISTING_GLYPHS)
547 *pgi = 0xffff;
548 else
549 *pgi = 0;
551 if (psc->CMAP_format12_Table)
553 CMAP_SegmentedCoverage *format = NULL;
554 CMAP_SegmentedCoverage_group *group = NULL;
556 format = (CMAP_SegmentedCoverage *)psc->CMAP_format12_Table;
558 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups),
559 sizeof(CMAP_SegmentedCoverage_group), compare_group);
561 if (group)
563 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode);
564 *pgi = GET_BE_DWORD(group->startGlyphID) + offset;
565 return 0;
568 return 0;
571 /**********
572 * GDEF
573 **********/
575 static WORD OT_get_glyph_class(const void *table, WORD glyph)
577 WORD class = 0;
578 const OT_ClassDefFormat1 *cf1 = table;
580 if (!table) return 0;
582 if (GET_BE_WORD(cf1->ClassFormat) == 1)
584 if (glyph >= GET_BE_WORD(cf1->StartGlyph))
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]);
591 else if (GET_BE_WORD(cf1->ClassFormat) == 2)
593 const OT_ClassDefFormat2 *cf2 = table;
594 int i, top;
595 top = GET_BE_WORD(cf2->ClassRangeCount);
596 for (i = 0; i < top; i++)
598 if (glyph >= GET_BE_WORD(cf2->ClassRangeRecord[i].Start) &&
599 glyph <= GET_BE_WORD(cf2->ClassRangeRecord[i].End))
601 class = GET_BE_WORD(cf2->ClassRangeRecord[i].Class);
602 break;
606 else
607 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1->ClassFormat));
609 return class;
612 static VOID *load_gdef_table(HDC hdc)
614 VOID* GDEF_Table = NULL;
615 int length = GetFontData(hdc, GDEF_TAG , 0, NULL, 0);
616 if (length != GDI_ERROR)
618 GDEF_Table = HeapAlloc(GetProcessHeap(),0,length);
619 GetFontData(hdc, GDEF_TAG , 0, GDEF_Table, length);
620 TRACE("Loaded GDEF table of %i bytes\n",length);
622 return GDEF_Table;
625 void OpenType_GDEF_UpdateGlyphProps(HDC hdc, ScriptCache *psc, const WORD *pwGlyphs, const WORD cGlyphs, WORD* pwLogClust, const WORD cChars, SCRIPT_GLYPHPROP *pGlyphProp)
627 int i;
628 void *glyph_class_table = NULL;
630 if (!psc->GDEF_Table)
631 psc->GDEF_Table = load_gdef_table(hdc);
633 if (psc->GDEF_Table)
635 const GDEF_Header *header = psc->GDEF_Table;
636 WORD offset = GET_BE_WORD( header->GlyphClassDef );
637 if (offset)
638 glyph_class_table = (BYTE *)psc->GDEF_Table + offset;
641 for (i = 0; i < cGlyphs; i++)
643 WORD class;
644 int char_count = 0;
645 int k;
647 k = USP10_FindGlyphInLogClust(pwLogClust, cChars, i);
648 if (k >= 0)
650 for (; k < cChars && pwLogClust[k] == i; k++)
651 char_count++;
654 class = OT_get_glyph_class( glyph_class_table, pwGlyphs[i] );
656 switch (class)
658 case 0:
659 case BaseGlyph:
660 pGlyphProp[i].sva.fClusterStart = 1;
661 pGlyphProp[i].sva.fDiacritic = 0;
662 pGlyphProp[i].sva.fZeroWidth = 0;
663 break;
664 case LigatureGlyph:
665 pGlyphProp[i].sva.fClusterStart = 1;
666 pGlyphProp[i].sva.fDiacritic = 0;
667 pGlyphProp[i].sva.fZeroWidth = 0;
668 break;
669 case MarkGlyph:
670 pGlyphProp[i].sva.fClusterStart = 0;
671 pGlyphProp[i].sva.fDiacritic = 1;
672 pGlyphProp[i].sva.fZeroWidth = 1;
673 break;
674 case ComponentGlyph:
675 pGlyphProp[i].sva.fClusterStart = 0;
676 pGlyphProp[i].sva.fDiacritic = 0;
677 pGlyphProp[i].sva.fZeroWidth = 0;
678 break;
679 default:
680 ERR("Unknown glyph class %i\n",class);
681 pGlyphProp[i].sva.fClusterStart = 1;
682 pGlyphProp[i].sva.fDiacritic = 0;
683 pGlyphProp[i].sva.fZeroWidth = 0;
686 if (char_count == 0)
687 pGlyphProp[i].sva.fClusterStart = 0;
691 /**********
692 * GSUB
693 **********/
694 static INT GSUB_apply_lookup(const OT_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
696 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
698 const OT_CoverageFormat1* cf1;
700 cf1 = table;
702 if (GET_BE_WORD(cf1->CoverageFormat) == 1)
704 int count = GET_BE_WORD(cf1->GlyphCount);
705 int i;
706 TRACE("Coverage Format 1, %i glyphs\n",count);
707 for (i = 0; i < count; i++)
708 if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
709 return i;
710 return -1;
712 else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
714 const OT_CoverageFormat2* cf2;
715 int i;
716 int count;
717 cf2 = (const OT_CoverageFormat2*)cf1;
719 count = GET_BE_WORD(cf2->RangeCount);
720 TRACE("Coverage Format 2, %i ranges\n",count);
721 for (i = 0; i < count; i++)
723 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
724 return -1;
725 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
726 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
728 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
729 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
732 return -1;
734 else
735 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
737 return -1;
740 static INT GSUB_apply_SingleSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
742 int j;
743 TRACE("Single Substitution Subtable\n");
745 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
747 int offset;
748 const GSUB_SingleSubstFormat1 *ssf1;
749 offset = GET_BE_WORD(look->SubTable[j]);
750 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
751 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
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)
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;
763 else
765 const GSUB_SingleSubstFormat2 *ssf2;
766 INT index;
767 INT offset;
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)
776 if (glyphs[glyph_index] == GET_BE_WORD(ssf2->Substitute[index]))
777 return GSUB_E_NOGLYPH;
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;
786 return GSUB_E_NOGLYPH;
789 static INT GSUB_apply_MultipleSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
791 int j;
792 TRACE("Multiple Substitution Subtable\n");
794 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
796 int offset, index;
797 const GSUB_MultipleSubstFormat1 *msf1;
798 offset = GET_BE_WORD(look->SubTable[j]);
799 msf1 = (const GSUB_MultipleSubstFormat1*)((const BYTE*)look+offset);
801 offset = GET_BE_WORD(msf1->Coverage);
802 index = GSUB_is_glyph_covered((const BYTE*)msf1+offset, glyphs[glyph_index]);
803 if (index != -1)
805 const GSUB_Sequence *seq;
806 int sub_count;
807 int j;
808 offset = GET_BE_WORD(msf1->Sequence[index]);
809 seq = (const GSUB_Sequence*)((const BYTE*)msf1+offset);
810 sub_count = GET_BE_WORD(seq->GlyphCount);
811 TRACE(" Glyph 0x%x (+%i)->",glyphs[glyph_index],(sub_count-1));
813 for (j = (*glyph_count)+(sub_count-1); j > glyph_index; j--)
814 glyphs[j] =glyphs[j-(sub_count-1)];
816 for (j = 0; j < sub_count; j++)
817 if (write_dir < 0)
818 glyphs[glyph_index + (sub_count-1) - j] = GET_BE_WORD(seq->Substitute[j]);
819 else
820 glyphs[glyph_index + j] = GET_BE_WORD(seq->Substitute[j]);
822 *glyph_count = *glyph_count + (sub_count - 1);
824 if (TRACE_ON(uniscribe))
826 for (j = 0; j < sub_count; j++)
827 TRACE(" 0x%x",glyphs[glyph_index+j]);
828 TRACE("\n");
831 return glyph_index + (sub_count * write_dir);
834 return GSUB_E_NOGLYPH;
837 static INT GSUB_apply_AlternateSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
839 int j;
840 TRACE("Alternate Substitution Subtable\n");
842 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
844 int offset;
845 const GSUB_AlternateSubstFormat1 *asf1;
846 INT index;
848 offset = GET_BE_WORD(look->SubTable[j]);
849 asf1 = (const GSUB_AlternateSubstFormat1*)((const BYTE*)look+offset);
850 offset = GET_BE_WORD(asf1->Coverage);
852 index = GSUB_is_glyph_covered((const BYTE*)asf1+offset, glyphs[glyph_index]);
853 if (index != -1)
855 const GSUB_AlternateSet *as;
856 offset = GET_BE_WORD(asf1->AlternateSet[index]);
857 as = (const GSUB_AlternateSet*)((const BYTE*)asf1+offset);
858 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as->GlyphCount));
859 if (glyphs[glyph_index] == GET_BE_WORD(as->Alternate[0]))
860 return GSUB_E_NOGLYPH;
862 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
863 glyphs[glyph_index] = GET_BE_WORD(as->Alternate[0]);
864 TRACE(" 0x%x\n",glyphs[glyph_index]);
865 return glyph_index + write_dir;
868 return GSUB_E_NOGLYPH;
871 static INT GSUB_apply_LigatureSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
873 int j;
875 TRACE("Ligature Substitution Subtable\n");
876 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
878 const GSUB_LigatureSubstFormat1 *lsf1;
879 int offset,index;
881 offset = GET_BE_WORD(look->SubTable[j]);
882 lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
883 offset = GET_BE_WORD(lsf1->Coverage);
884 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
885 TRACE(" Coverage index %i\n",index);
886 if (index != -1)
888 const GSUB_LigatureSet *ls;
889 int k, count;
891 offset = GET_BE_WORD(lsf1->LigatureSet[index]);
892 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
893 count = GET_BE_WORD(ls->LigatureCount);
894 TRACE(" LigatureSet has %i members\n",count);
895 for (k = 0; k < count; k++)
897 const GSUB_Ligature *lig;
898 int CompCount,l,CompIndex;
900 offset = GET_BE_WORD(ls->Ligature[k]);
901 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
902 CompCount = GET_BE_WORD(lig->CompCount) - 1;
903 CompIndex = glyph_index+write_dir;
904 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
906 int CompGlyph;
907 CompGlyph = GET_BE_WORD(lig->Component[l]);
908 if (CompGlyph != glyphs[CompIndex])
909 break;
910 CompIndex += write_dir;
912 if (l == CompCount)
914 int replaceIdx = glyph_index;
915 if (write_dir < 0)
916 replaceIdx = glyph_index - CompCount;
918 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
919 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
920 TRACE("0x%x\n",glyphs[replaceIdx]);
921 if (CompCount > 0)
923 int j;
924 for (j = replaceIdx + 1; j < *glyph_count; j++)
925 glyphs[j] =glyphs[j+CompCount];
926 *glyph_count = *glyph_count - CompCount;
928 return replaceIdx + write_dir;
933 return GSUB_E_NOGLYPH;
936 static INT GSUB_apply_ChainContextSubst(const OT_LookupList* lookup, const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
938 int j;
939 BOOL done = FALSE;
941 TRACE("Chaining Contextual Substitution Subtable\n");
942 for (j = 0; j < GET_BE_WORD(look->SubTableCount) && !done; j++)
944 const GSUB_ChainContextSubstFormat1 *ccsf1;
945 int offset;
946 int dirLookahead = write_dir;
947 int dirBacktrack = -1 * write_dir;
949 offset = GET_BE_WORD(look->SubTable[j]);
950 ccsf1 = (const GSUB_ChainContextSubstFormat1*)((const BYTE*)look+offset);
951 if (GET_BE_WORD(ccsf1->SubstFormat) == 1)
953 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
954 continue;
956 else if (GET_BE_WORD(ccsf1->SubstFormat) == 2)
958 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
959 continue;
961 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
963 int k;
964 int indexGlyphs;
965 const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
966 const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
967 const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
968 const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
969 int newIndex = glyph_index;
971 ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
973 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
975 for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
977 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
978 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
979 break;
981 if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
982 continue;
983 TRACE("Matched Backtrack\n");
985 ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)((BYTE *)ccsf1 +
986 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_1, Coverage[GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)]));
988 indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
989 for (k = 0; k < indexGlyphs; k++)
991 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
992 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
993 break;
995 if (k != indexGlyphs)
996 continue;
997 TRACE("Matched IndexGlyphs\n");
999 ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)((BYTE *)ccsf3_2 +
1000 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_2, Coverage[GET_BE_WORD(ccsf3_2->InputGlyphCount)]));
1002 for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
1004 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
1005 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
1006 break;
1008 if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
1009 continue;
1010 TRACE("Matched LookAhead\n");
1012 ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)((BYTE *)ccsf3_3 +
1013 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_3, Coverage[GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)]));
1015 if (GET_BE_WORD(ccsf3_4->SubstCount))
1017 for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
1019 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
1020 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
1022 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
1023 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
1024 if (newIndex == -1)
1026 ERR("Chain failed to generate a glyph\n");
1027 continue;
1030 return newIndex;
1032 else return GSUB_E_NOGLYPH;
1035 return -1;
1038 static INT GSUB_apply_lookup(const OT_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1040 int offset;
1041 const OT_LookupTable *look;
1043 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
1044 look = (const OT_LookupTable*)((const BYTE*)lookup + offset);
1045 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
1046 switch(GET_BE_WORD(look->LookupType))
1048 case 1:
1049 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1050 case 2:
1051 return GSUB_apply_MultipleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1052 case 3:
1053 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1054 case 4:
1055 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1056 case 6:
1057 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
1058 default:
1059 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
1061 return GSUB_E_NOGLYPH;
1064 INT OpenType_apply_GSUB_lookup(LPCVOID table, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1066 const GSUB_Header *header = (const GSUB_Header *)table;
1067 const OT_LookupList *lookup = (const OT_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1069 return GSUB_apply_lookup(lookup, lookup_index, glyphs, glyph_index, write_dir, glyph_count);
1072 /**********
1073 * GPOS
1074 **********/
1075 static INT GPOS_apply_lookup(LPOUTLINETEXTMETRICW lpotm, LPLOGFONTW lplogfont, const SCRIPT_ANALYSIS *analysis, INT* piAdvance,
1076 const OT_LookupList* lookup, INT lookup_index, const WORD *glyphs, INT glyph_index, INT glyph_count, GOFFSET *pGoffset);
1078 static INT GPOS_get_device_table_value(const OT_DeviceTable *DeviceTable, WORD ppem)
1080 static const WORD mask[3] = {3,0xf,0xff};
1081 if (DeviceTable && ppem >= GET_BE_WORD(DeviceTable->StartSize) && ppem <= GET_BE_WORD(DeviceTable->EndSize))
1083 int format = GET_BE_WORD(DeviceTable->DeltaFormat);
1084 int index = ppem - GET_BE_WORD(DeviceTable->StartSize);
1085 int value;
1086 TRACE("device table, format %i, index %i\n",format, index);
1087 index = index << format;
1088 value = (DeviceTable->DeltaValue[index/sizeof(WORD)] << (index%sizeof(WORD)))&mask[format-1];
1089 TRACE("offset %i, value %i\n",index, value);
1090 if (value > mask[format-1]/2)
1091 value = -1 * ((mask[format-1]+1) - value);
1092 return value;
1094 return 0;
1097 static VOID GPOS_get_anchor_values(LPCVOID table, LPPOINT pt, WORD ppem)
1099 const GPOS_AnchorFormat1* anchor1 = (const GPOS_AnchorFormat1*)table;
1101 switch (GET_BE_WORD(anchor1->AnchorFormat))
1103 case 1:
1105 TRACE("Anchor Format 1\n");
1106 pt->x = (short)GET_BE_WORD(anchor1->XCoordinate);
1107 pt->y = (short)GET_BE_WORD(anchor1->YCoordinate);
1108 break;
1110 case 2:
1112 const GPOS_AnchorFormat2* anchor2 = (const GPOS_AnchorFormat2*)table;
1113 TRACE("Anchor Format 2\n");
1114 pt->x = (short)GET_BE_WORD(anchor2->XCoordinate);
1115 pt->y = (short)GET_BE_WORD(anchor2->YCoordinate);
1116 break;
1118 case 3:
1120 int offset;
1121 const GPOS_AnchorFormat3* anchor3 = (const GPOS_AnchorFormat3*)table;
1122 TRACE("Anchor Format 3\n");
1123 pt->x = (short)GET_BE_WORD(anchor3->XCoordinate);
1124 pt->y = (short)GET_BE_WORD(anchor3->YCoordinate);
1125 offset = GET_BE_WORD(anchor3->XDeviceTable);
1126 TRACE("ppem %i\n",ppem);
1127 if (offset)
1129 const OT_DeviceTable* DeviceTableX = NULL;
1130 DeviceTableX = (const OT_DeviceTable*)((const BYTE*)anchor3 + offset);
1131 pt->x += GPOS_get_device_table_value(DeviceTableX, ppem);
1133 offset = GET_BE_WORD(anchor3->YDeviceTable);
1134 if (offset)
1136 const OT_DeviceTable* DeviceTableY = NULL;
1137 DeviceTableY = (const OT_DeviceTable*)((const BYTE*)anchor3 + offset);
1138 pt->y += GPOS_get_device_table_value(DeviceTableY, ppem);
1140 break;
1142 default:
1143 ERR("Unknown Anchor Format %i\n",GET_BE_WORD(anchor1->AnchorFormat));
1144 pt->x = 0;
1145 pt->y = 0;
1149 static void GPOS_convert_design_units_to_device(LPOUTLINETEXTMETRICW lpotm, LPLOGFONTW lplogfont, int desX, int desY, double *devX, double *devY)
1151 int emHeight = lpotm->otmTextMetrics.tmAscent + lpotm->otmTextMetrics.tmDescent - lpotm->otmTextMetrics.tmInternalLeading;
1153 TRACE("emHeight %i lfWidth %i\n",emHeight, lplogfont->lfWidth);
1154 *devX = (desX * emHeight) / (double)lpotm->otmEMSquare;
1155 *devY = (desY * emHeight) / (double)lpotm->otmEMSquare;
1156 if (lplogfont->lfWidth)
1157 FIXME("Font with lfWidth set no handled properly\n");
1160 static INT GPOS_get_value_record(WORD ValueFormat, const WORD data[], GPOS_ValueRecord *record)
1162 INT offset = 0;
1163 if (ValueFormat & 0x0001) { if (data) record->XPlacement = GET_BE_WORD(data[offset]); offset++; }
1164 if (ValueFormat & 0x0002) { if (data) record->YPlacement = GET_BE_WORD(data[offset]); offset++; }
1165 if (ValueFormat & 0x0004) { if (data) record->XAdvance = GET_BE_WORD(data[offset]); offset++; }
1166 if (ValueFormat & 0x0008) { if (data) record->YAdvance = GET_BE_WORD(data[offset]); offset++; }
1167 if (ValueFormat & 0x0010) { if (data) record->XPlaDevice = GET_BE_WORD(data[offset]); offset++; }
1168 if (ValueFormat & 0x0020) { if (data) record->YPlaDevice = GET_BE_WORD(data[offset]); offset++; }
1169 if (ValueFormat & 0x0040) { if (data) record->XAdvDevice = GET_BE_WORD(data[offset]); offset++; }
1170 if (ValueFormat & 0x0080) { if (data) record->YAdvDevice = GET_BE_WORD(data[offset]); offset++; }
1171 return offset;
1174 static VOID GPOS_get_value_record_offsets(const BYTE* head, GPOS_ValueRecord *ValueRecord, WORD ValueFormat, INT ppem, LPPOINT ptPlacement, LPPOINT ptAdvance)
1176 if (ValueFormat & 0x0001) ptPlacement->x += (short)ValueRecord->XPlacement;
1177 if (ValueFormat & 0x0002) ptPlacement->y += (short)ValueRecord->YPlacement;
1178 if (ValueFormat & 0x0004) ptAdvance->x += (short)ValueRecord->XAdvance;
1179 if (ValueFormat & 0x0008) ptAdvance->y += (short)ValueRecord->YAdvance;
1180 if (ValueFormat & 0x0010) ptPlacement->x += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->XPlaDevice), ppem);
1181 if (ValueFormat & 0x0020) ptPlacement->y += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->YPlaDevice), ppem);
1182 if (ValueFormat & 0x0040) ptAdvance->x += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->XAdvDevice), ppem);
1183 if (ValueFormat & 0x0080) ptAdvance->y += GPOS_get_device_table_value((const OT_DeviceTable*)(head + ValueRecord->YAdvDevice), ppem);
1184 if (ValueFormat & 0xFF00) FIXME("Unhandled Value Format %x\n",ValueFormat&0xFF00);
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)
1190 int j;
1192 TRACE("Single Adjustment Positioning Subtable\n");
1194 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1196 const GPOS_SinglePosFormat1 *spf1;
1197 WORD offset = GET_BE_WORD(look->SubTable[j]);
1198 spf1 = (const GPOS_SinglePosFormat1*)((const BYTE*)look+offset);
1199 if (GET_BE_WORD(spf1->PosFormat) == 1)
1201 offset = GET_BE_WORD(spf1->Coverage);
1202 if (GSUB_is_glyph_covered((const BYTE*)spf1+offset, glyphs[glyph_index]) != -1)
1204 GPOS_ValueRecord ValueRecord = {0,0,0,0,0,0,0,0};
1205 WORD ValueFormat = GET_BE_WORD(spf1->ValueFormat);
1206 GPOS_get_value_record(ValueFormat, spf1->Value, &ValueRecord);
1207 GPOS_get_value_record_offsets((const BYTE*)spf1, &ValueRecord, ValueFormat, ppem, ptAdjust, ptAdvance);
1208 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord.XPlacement,ValueRecord.YPlacement);
1211 else if (GET_BE_WORD(spf1->PosFormat) == 2)
1213 int index;
1214 const GPOS_SinglePosFormat2 *spf2;
1215 spf2 = (const GPOS_SinglePosFormat2*)spf1;
1216 offset = GET_BE_WORD(spf2->Coverage);
1217 index = GSUB_is_glyph_covered((const BYTE*)spf2+offset, glyphs[glyph_index]);
1218 if (index != -1)
1220 int size;
1221 GPOS_ValueRecord ValueRecord = {0,0,0,0,0,0,0,0};
1222 WORD ValueFormat = GET_BE_WORD(spf2->ValueFormat);
1223 size = GPOS_get_value_record(ValueFormat, spf2->Value, &ValueRecord);
1224 if (index > 0)
1226 offset = size * index;
1227 GPOS_get_value_record(ValueFormat, &spf2->Value[offset], &ValueRecord);
1229 GPOS_get_value_record_offsets((const BYTE*)spf2, &ValueRecord, ValueFormat, ppem, ptAdjust, ptAdvance);
1230 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord.XPlacement,ValueRecord.YPlacement);
1233 else
1234 FIXME("Single Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(spf1->PosFormat));
1238 static void apply_pair_value( const void *pos_table, WORD val_fmt1, WORD val_fmt2, const WORD *pair,
1239 INT ppem, POINT *adjust, POINT *advance )
1241 GPOS_ValueRecord val_rec1 = {0,0,0,0,0,0,0,0};
1242 GPOS_ValueRecord val_rec2 = {0,0,0,0,0,0,0,0};
1243 INT size;
1245 size = GPOS_get_value_record( val_fmt1, pair, &val_rec1 );
1246 GPOS_get_value_record( val_fmt2, pair + size, &val_rec2 );
1248 if (val_fmt1)
1250 GPOS_get_value_record_offsets( pos_table, &val_rec1, val_fmt1, ppem, adjust, advance );
1251 TRACE( "Glyph 1 resulting cumulative offset is %i,%i design units\n", adjust[0].x, adjust[0].y );
1252 TRACE( "Glyph 1 resulting cumulative advance is %i,%i design units\n", advance[0].x, advance[0].y );
1254 if (val_fmt2)
1256 GPOS_get_value_record_offsets( pos_table, &val_rec2, val_fmt2, ppem, adjust + 1, advance + 1 );
1257 TRACE( "Glyph 2 resulting cumulative offset is %i,%i design units\n", adjust[1].x, adjust[1].y );
1258 TRACE( "Glyph 2 resulting cumulative advance is %i,%i design units\n", advance[1].x, advance[1].y );
1262 static INT GPOS_apply_PairAdjustment(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1263 INT glyph_count, INT ppem, LPPOINT ptAdjust, LPPOINT ptAdvance)
1265 int j;
1266 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1268 if (glyph_index + write_dir < 0 || glyph_index + write_dir >= glyph_count) return glyph_index + 1;
1270 TRACE("Pair Adjustment Positioning Subtable\n");
1272 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1274 const GPOS_PairPosFormat1 *ppf1;
1275 WORD offset = GET_BE_WORD(look->SubTable[j]);
1276 ppf1 = (const GPOS_PairPosFormat1*)((const BYTE*)look+offset);
1277 if (GET_BE_WORD(ppf1->PosFormat) == 1)
1279 int index;
1280 WORD ValueFormat1 = GET_BE_WORD(ppf1->ValueFormat1);
1281 WORD ValueFormat2 = GET_BE_WORD(ppf1->ValueFormat2);
1282 INT val_fmt1_size = GPOS_get_value_record( ValueFormat1, NULL, NULL );
1283 INT val_fmt2_size = GPOS_get_value_record( ValueFormat2, NULL, NULL );
1284 offset = GET_BE_WORD(ppf1->Coverage);
1285 index = GSUB_is_glyph_covered((const BYTE*)ppf1+offset, glyphs[glyph_index]);
1286 if (index != -1 && index < GET_BE_WORD(ppf1->PairSetCount))
1288 int k;
1289 int pair_count;
1290 const GPOS_PairSet *ps;
1291 const GPOS_PairValueRecord *pair_val_rec;
1292 offset = GET_BE_WORD(ppf1->PairSetOffset[index]);
1293 ps = (const GPOS_PairSet*)((const BYTE*)ppf1+offset);
1294 pair_count = GET_BE_WORD(ps->PairValueCount);
1295 pair_val_rec = ps->PairValueRecord;
1296 for (k = 0; k < pair_count; k++)
1298 WORD second_glyph = GET_BE_WORD(pair_val_rec->SecondGlyph);
1299 if (glyphs[glyph_index+write_dir] == second_glyph)
1301 int next = 1;
1302 TRACE("Format 1: Found Pair %x,%x\n",glyphs[glyph_index],glyphs[glyph_index+write_dir]);
1303 apply_pair_value( ppf1, ValueFormat1, ValueFormat2, pair_val_rec->Value1, ppem, ptAdjust, ptAdvance );
1304 if (ValueFormat2) next++;
1305 return glyph_index + next;
1307 pair_val_rec = (const GPOS_PairValueRecord *)(pair_val_rec->Value1 + val_fmt1_size + val_fmt2_size);
1311 else if (GET_BE_WORD(ppf1->PosFormat) == 2)
1313 const GPOS_PairPosFormat2 *ppf2 = (const GPOS_PairPosFormat2*)((const BYTE*)look + offset);
1314 int index;
1315 WORD ValueFormat1 = GET_BE_WORD( ppf2->ValueFormat1 );
1316 WORD ValueFormat2 = GET_BE_WORD( ppf2->ValueFormat2 );
1317 INT val_fmt1_size = GPOS_get_value_record( ValueFormat1, NULL, NULL );
1318 INT val_fmt2_size = GPOS_get_value_record( ValueFormat2, NULL, NULL );
1319 WORD class1_count = GET_BE_WORD( ppf2->Class1Count );
1320 WORD class2_count = GET_BE_WORD( ppf2->Class2Count );
1322 offset = GET_BE_WORD( ppf2->Coverage );
1323 index = GSUB_is_glyph_covered( (const BYTE*)ppf2 + offset, glyphs[glyph_index] );
1324 if (index != -1)
1326 WORD class1, class2;
1327 class1 = OT_get_glyph_class( (const BYTE *)ppf2 + GET_BE_WORD(ppf2->ClassDef1), glyphs[glyph_index] );
1328 class2 = OT_get_glyph_class( (const BYTE *)ppf2 + GET_BE_WORD(ppf2->ClassDef2), glyphs[glyph_index + write_dir] );
1329 if (class1 < class1_count && class2 < class2_count)
1331 const WORD *pair_val = ppf2->Class1Record + (class1 * class2_count + class2) * (val_fmt1_size + val_fmt2_size);
1332 int next = 1;
1334 TRACE( "Format 2: Found Pair %x,%x\n", glyphs[glyph_index], glyphs[glyph_index + write_dir] );
1336 apply_pair_value( ppf2, ValueFormat1, ValueFormat2, pair_val, ppem, ptAdjust, ptAdvance );
1337 if (ValueFormat2) next++;
1338 return glyph_index + next;
1342 else
1343 FIXME("Pair Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(ppf1->PosFormat));
1345 return glyph_index+1;
1348 static VOID GPOS_apply_CursiveAttachment(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1349 INT glyph_count, INT ppem, LPPOINT pt)
1351 int j;
1352 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1354 if (glyph_index + write_dir < 0 || glyph_index + write_dir >= glyph_count) return;
1356 TRACE("Cursive Attachment Positioning Subtable\n");
1358 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1360 const GPOS_CursivePosFormat1 *cpf1;
1361 WORD offset = GET_BE_WORD(look->SubTable[j]);
1362 cpf1 = (const GPOS_CursivePosFormat1*)((const BYTE*)look+offset);
1363 if (GET_BE_WORD(cpf1->PosFormat) == 1)
1365 int index_exit, index_entry;
1366 offset = GET_BE_WORD( cpf1->Coverage );
1367 index_exit = GSUB_is_glyph_covered((const BYTE*)cpf1+offset, glyphs[glyph_index]);
1368 if (index_exit != -1 && cpf1->EntryExitRecord[index_exit].ExitAnchor!= 0)
1370 index_entry = GSUB_is_glyph_covered((const BYTE*)cpf1+offset, glyphs[glyph_index+write_dir]);
1371 if (index_entry != -1 && cpf1->EntryExitRecord[index_entry].EntryAnchor != 0)
1373 POINT exit_pt, entry_pt;
1374 offset = GET_BE_WORD(cpf1->EntryExitRecord[index_exit].ExitAnchor);
1375 GPOS_get_anchor_values((const BYTE*)cpf1 + offset, &exit_pt, ppem);
1376 offset = GET_BE_WORD(cpf1->EntryExitRecord[index_entry].EntryAnchor);
1377 GPOS_get_anchor_values((const BYTE*)cpf1 + offset, &entry_pt, ppem);
1378 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);
1379 pt->x = entry_pt.x - exit_pt.x;
1380 pt->y = entry_pt.y - exit_pt.y;
1381 return;
1385 else
1386 FIXME("Cursive Attachment Positioning: Format %i Unhandled\n",GET_BE_WORD(cpf1->PosFormat));
1388 return;
1391 static VOID GPOS_apply_MarkToBase(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1392 INT glyph_count, INT ppem, LPPOINT pt)
1394 int j;
1395 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1397 TRACE("MarkToBase Attachment Positioning Subtable\n");
1399 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1401 int offset;
1402 const GPOS_MarkBasePosFormat1 *mbpf1;
1403 offset = GET_BE_WORD(look->SubTable[j]);
1404 mbpf1 = (const GPOS_MarkBasePosFormat1*)((const BYTE*)look+offset);
1405 if (GET_BE_WORD(mbpf1->PosFormat) == 1)
1407 int offset = GET_BE_WORD(mbpf1->MarkCoverage);
1408 int mark_index;
1409 mark_index = GSUB_is_glyph_covered((const BYTE*)mbpf1+offset, glyphs[glyph_index]);
1410 if (mark_index != -1)
1412 int base_index;
1413 offset = GET_BE_WORD(mbpf1->BaseCoverage);
1414 base_index = GSUB_is_glyph_covered((const BYTE*)mbpf1+offset, glyphs[glyph_index - write_dir]);
1415 if (base_index != -1)
1417 const GPOS_MarkArray *ma;
1418 const GPOS_MarkRecord *mr;
1419 const GPOS_BaseArray *ba;
1420 const GPOS_BaseRecord *br;
1421 int mark_class;
1422 int class_count = GET_BE_WORD(mbpf1->ClassCount);
1423 int baserecord_size;
1424 POINT base_pt;
1425 POINT mark_pt;
1426 TRACE("Mark %x(%i) and base %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], base_index);
1427 offset = GET_BE_WORD(mbpf1->MarkArray);
1428 ma = (const GPOS_MarkArray*)((const BYTE*)mbpf1 + offset);
1429 if (mark_index > GET_BE_WORD(ma->MarkCount))
1431 ERR("Mark index exeeded mark count\n");
1432 return;
1434 mr = &ma->MarkRecord[mark_index];
1435 mark_class = GET_BE_WORD(mr->Class);
1436 TRACE("Mark Class %i total classes %i\n",mark_class,class_count);
1437 offset = GET_BE_WORD(mbpf1->BaseArray);
1438 ba = (const GPOS_BaseArray*)((const BYTE*)mbpf1 + offset);
1439 baserecord_size = class_count * sizeof(WORD);
1440 br = (const GPOS_BaseRecord*)((const BYTE*)ba + sizeof(WORD) + (baserecord_size * base_index));
1441 offset = GET_BE_WORD(br->BaseAnchor[mark_class]);
1442 GPOS_get_anchor_values((const BYTE*)ba + offset, &base_pt, ppem);
1443 offset = GET_BE_WORD(mr->MarkAnchor);
1444 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem);
1445 TRACE("Offset on base is %i,%i design units\n",base_pt.x,base_pt.y);
1446 TRACE("Offset on mark is %i,%i design units\n",mark_pt.x, mark_pt.y);
1447 pt->x += base_pt.x - mark_pt.x;
1448 pt->y += base_pt.y - mark_pt.y;
1449 TRACE("Resulting cumulative offset is %i,%i design units\n",pt->x,pt->y);
1453 else
1454 FIXME("Unhandled Mark To Base Format %i\n",GET_BE_WORD(mbpf1->PosFormat));
1458 static VOID GPOS_apply_MarkToLigature(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1459 INT glyph_count, INT ppem, LPPOINT pt)
1461 int j;
1462 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1464 TRACE("MarkToLigature Attachment Positioning Subtable\n");
1466 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1468 int offset;
1469 const GPOS_MarkLigPosFormat1 *mlpf1;
1470 offset = GET_BE_WORD(look->SubTable[j]);
1471 mlpf1 = (const GPOS_MarkLigPosFormat1*)((const BYTE*)look+offset);
1472 if (GET_BE_WORD(mlpf1->PosFormat) == 1)
1474 int offset = GET_BE_WORD(mlpf1->MarkCoverage);
1475 int mark_index;
1476 mark_index = GSUB_is_glyph_covered((const BYTE*)mlpf1+offset, glyphs[glyph_index]);
1477 if (mark_index != -1)
1479 int ligature_index;
1480 offset = GET_BE_WORD(mlpf1->LigatureCoverage);
1481 ligature_index = GSUB_is_glyph_covered((const BYTE*)mlpf1+offset, glyphs[glyph_index - write_dir]);
1482 if (ligature_index != -1)
1484 const GPOS_MarkArray *ma;
1485 const GPOS_MarkRecord *mr;
1487 const GPOS_LigatureArray *la;
1488 const GPOS_LigatureAttach *lt;
1489 int mark_class;
1490 int class_count = GET_BE_WORD(mlpf1->ClassCount);
1491 int component_count;
1492 int component_size;
1493 int i;
1494 POINT ligature_pt;
1495 POINT mark_pt;
1497 TRACE("Mark %x(%i) and ligature %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], ligature_index);
1498 offset = GET_BE_WORD(mlpf1->MarkArray);
1499 ma = (const GPOS_MarkArray*)((const BYTE*)mlpf1 + offset);
1500 if (mark_index > GET_BE_WORD(ma->MarkCount))
1502 ERR("Mark index exeeded mark count\n");
1503 return;
1505 mr = &ma->MarkRecord[mark_index];
1506 mark_class = GET_BE_WORD(mr->Class);
1507 TRACE("Mark Class %i total classes %i\n",mark_class,class_count);
1508 offset = GET_BE_WORD(mlpf1->LigatureArray);
1509 la = (const GPOS_LigatureArray*)((const BYTE*)mlpf1 + offset);
1510 if (ligature_index > GET_BE_WORD(la->LigatureCount))
1512 ERR("Ligature index exeeded ligature count\n");
1513 return;
1515 offset = GET_BE_WORD(la->LigatureAttach[ligature_index]);
1516 lt = (const GPOS_LigatureAttach*)((const BYTE*)la + offset);
1518 component_count = GET_BE_WORD(lt->ComponentCount);
1519 component_size = class_count * sizeof(WORD);
1520 offset = 0;
1521 for (i = 0; i < component_count && !offset; i++)
1523 int k;
1524 const GPOS_ComponentRecord *cr = (const GPOS_ComponentRecord*)((const BYTE*)lt->ComponentRecord + (component_size * i));
1525 for (k = 0; k < class_count && !offset; k++)
1526 offset = GET_BE_WORD(cr->LigatureAnchor[k]);
1527 cr = (const GPOS_ComponentRecord*)((const BYTE*)cr + component_size);
1529 if (!offset)
1531 ERR("Failed to find avalible ligature connection point\n");
1532 return;
1535 GPOS_get_anchor_values((const BYTE*)lt + offset, &ligature_pt, ppem);
1536 offset = GET_BE_WORD(mr->MarkAnchor);
1537 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem);
1538 TRACE("Offset on ligature is %i,%i design units\n",ligature_pt.x,ligature_pt.y);
1539 TRACE("Offset on mark is %i,%i design units\n",mark_pt.x, mark_pt.y);
1540 pt->x += ligature_pt.x - mark_pt.x;
1541 pt->y += ligature_pt.y - mark_pt.y;
1542 TRACE("Resulting cumulative offset is %i,%i design units\n",pt->x,pt->y);
1546 else
1547 FIXME("Unhandled Mark To Ligature Format %i\n",GET_BE_WORD(mlpf1->PosFormat));
1551 static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALYSIS *analysis, const WORD *glyphs, INT glyph_index,
1552 INT glyph_count, INT ppem, LPPOINT pt)
1554 int j;
1555 BOOL rc = FALSE;
1556 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1558 TRACE("MarkToMark Attachment Positioning Subtable\n");
1560 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1562 int offset;
1563 const GPOS_MarkMarkPosFormat1 *mmpf1;
1564 offset = GET_BE_WORD(look->SubTable[j]);
1565 mmpf1 = (const GPOS_MarkMarkPosFormat1*)((const BYTE*)look+offset);
1566 if (GET_BE_WORD(mmpf1->PosFormat) == 1)
1568 int offset = GET_BE_WORD(mmpf1->Mark1Coverage);
1569 int mark_index;
1570 mark_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[glyph_index]);
1571 if (mark_index != -1)
1573 int mark2_index;
1574 offset = GET_BE_WORD(mmpf1->Mark2Coverage);
1575 mark2_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[glyph_index - write_dir]);
1576 if (mark2_index != -1)
1578 const GPOS_MarkArray *ma;
1579 const GPOS_MarkRecord *mr;
1580 const GPOS_Mark2Array *m2a;
1581 const GPOS_Mark2Record *m2r;
1582 int mark_class;
1583 int class_count = GET_BE_WORD(mmpf1->ClassCount);
1584 int mark2record_size;
1585 POINT mark2_pt;
1586 POINT mark_pt;
1587 TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], mark2_index);
1588 offset = GET_BE_WORD(mmpf1->Mark1Array);
1589 ma = (const GPOS_MarkArray*)((const BYTE*)mmpf1 + offset);
1590 if (mark_index > GET_BE_WORD(ma->MarkCount))
1592 ERR("Mark index exeeded mark count\n");
1593 return FALSE;
1595 mr = &ma->MarkRecord[mark_index];
1596 mark_class = GET_BE_WORD(mr->Class);
1597 TRACE("Mark Class %i total classes %i\n",mark_class,class_count);
1598 offset = GET_BE_WORD(mmpf1->Mark2Array);
1599 m2a = (const GPOS_Mark2Array*)((const BYTE*)mmpf1 + offset);
1600 mark2record_size = class_count * sizeof(WORD);
1601 m2r = (const GPOS_Mark2Record*)((const BYTE*)m2a + sizeof(WORD) + (mark2record_size * mark2_index));
1602 offset = GET_BE_WORD(m2r->Mark2Anchor[mark_class]);
1603 GPOS_get_anchor_values((const BYTE*)m2a + offset, &mark2_pt, ppem);
1604 offset = GET_BE_WORD(mr->MarkAnchor);
1605 GPOS_get_anchor_values((const BYTE*)ma + offset, &mark_pt, ppem);
1606 TRACE("Offset on mark2 is %i,%i design units\n",mark2_pt.x,mark2_pt.y);
1607 TRACE("Offset on mark is %i,%i design units\n",mark_pt.x, mark_pt.y);
1608 pt->x += mark2_pt.x - mark_pt.x;
1609 pt->y += mark2_pt.y - mark_pt.y;
1610 TRACE("Resulting cumulative offset is %i,%i design units\n",pt->x,pt->y);
1611 rc = TRUE;
1615 else
1616 FIXME("Unhandled Mark To Mark Format %i\n",GET_BE_WORD(mmpf1->PosFormat));
1618 return rc;
1621 static INT GPOS_apply_ChainContextPos(LPOUTLINETEXTMETRICW lpotm, LPLOGFONTW lplogfont, const SCRIPT_ANALYSIS *analysis, INT* piAdvance,
1622 const OT_LookupList *lookup, const OT_LookupTable *look, const WORD *glyphs, INT glyph_index,
1623 INT glyph_count, INT ppem, GOFFSET *pGoffset)
1625 int j;
1626 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1628 TRACE("Chaining Contextual Positioning Subtable\n");
1630 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
1632 int offset;
1633 const GPOS_ChainContextPosFormat3_1 *ccpf3;
1634 int dirLookahead = write_dir;
1635 int dirBacktrack = -1 * write_dir;
1637 offset = GET_BE_WORD(look->SubTable[j]);
1638 ccpf3 = (const GPOS_ChainContextPosFormat3_1*)((const BYTE*)look+offset);
1640 if (GET_BE_WORD(ccpf3->PosFormat) == 1)
1642 FIXME(" TODO: subtype 1 (Simple Chaining Context Glyph Positioning)\n");
1643 continue;
1645 else if (GET_BE_WORD(ccpf3->PosFormat) == 2)
1647 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Positioning)\n");
1648 continue;
1650 else if (GET_BE_WORD(ccpf3->PosFormat) == 3)
1652 int k;
1653 int indexGlyphs;
1654 const GPOS_ChainContextPosFormat3_2 *ccpf3_2;
1655 const GPOS_ChainContextPosFormat3_3 *ccpf3_3;
1656 const GPOS_ChainContextPosFormat3_4 *ccpf3_4;
1658 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Positioning)\n");
1660 for (k = 0; k < GET_BE_WORD(ccpf3->BacktrackGlyphCount); k++)
1662 offset = GET_BE_WORD(ccpf3->Coverage[k]);
1663 if (GSUB_is_glyph_covered((const BYTE*)ccpf3+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
1664 break;
1666 if (k != GET_BE_WORD(ccpf3->BacktrackGlyphCount))
1667 continue;
1668 TRACE("Matched Backtrack\n");
1670 ccpf3_2 = (const GPOS_ChainContextPosFormat3_2*)((BYTE *)ccpf3 +
1671 FIELD_OFFSET(GPOS_ChainContextPosFormat3_1, Coverage[GET_BE_WORD(ccpf3->BacktrackGlyphCount)]));
1673 indexGlyphs = GET_BE_WORD(ccpf3_2->InputGlyphCount);
1674 for (k = 0; k < indexGlyphs; k++)
1676 offset = GET_BE_WORD(ccpf3_2->Coverage[k]);
1677 if (GSUB_is_glyph_covered((const BYTE*)ccpf3+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
1678 break;
1680 if (k != indexGlyphs)
1681 continue;
1682 TRACE("Matched IndexGlyphs\n");
1684 ccpf3_3 = (const GPOS_ChainContextPosFormat3_3*)((BYTE *)ccpf3_2 +
1685 FIELD_OFFSET(GPOS_ChainContextPosFormat3_2, Coverage[GET_BE_WORD(ccpf3_2->InputGlyphCount)]));
1687 for (k = 0; k < GET_BE_WORD(ccpf3_3->LookaheadGlyphCount); k++)
1689 offset = GET_BE_WORD(ccpf3_3->Coverage[k]);
1690 if (GSUB_is_glyph_covered((const BYTE*)ccpf3+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
1691 break;
1693 if (k != GET_BE_WORD(ccpf3_3->LookaheadGlyphCount))
1694 continue;
1695 TRACE("Matched LookAhead\n");
1697 ccpf3_4 = (const GPOS_ChainContextPosFormat3_4*)((BYTE *)ccpf3_3 +
1698 FIELD_OFFSET(GPOS_ChainContextPosFormat3_3, Coverage[GET_BE_WORD(ccpf3_3->LookaheadGlyphCount)]));
1700 if (GET_BE_WORD(ccpf3_4->PosCount))
1702 for (k = 0; k < GET_BE_WORD(ccpf3_4->PosCount); k++)
1704 int lookupIndex = GET_BE_WORD(ccpf3_4->PosLookupRecord[k].LookupListIndex);
1705 int SequenceIndex = GET_BE_WORD(ccpf3_4->PosLookupRecord[k].SequenceIndex) * write_dir;
1707 TRACE("Position: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
1708 GPOS_apply_lookup(lpotm, lplogfont, analysis, piAdvance, lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, glyph_count, pGoffset);
1710 return glyph_index + indexGlyphs + GET_BE_WORD(ccpf3_3->LookaheadGlyphCount);
1712 else return glyph_index + 1;
1714 else
1715 FIXME("Unhandled Chaining Contextual Positioning Format %i\n",GET_BE_WORD(ccpf3->PosFormat));
1717 return glyph_index + 1;
1720 static INT GPOS_apply_lookup(LPOUTLINETEXTMETRICW lpotm, LPLOGFONTW lplogfont, const SCRIPT_ANALYSIS *analysis, INT* piAdvance,
1721 const OT_LookupList* lookup, INT lookup_index, const WORD *glyphs, INT glyph_index, INT glyph_count, GOFFSET *pGoffset)
1723 int offset;
1724 const OT_LookupTable *look;
1725 int ppem = lpotm->otmTextMetrics.tmAscent + lpotm->otmTextMetrics.tmDescent - lpotm->otmTextMetrics.tmInternalLeading;
1727 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
1728 look = (const OT_LookupTable*)((const BYTE*)lookup + offset);
1729 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
1730 switch(GET_BE_WORD(look->LookupType))
1732 case 1:
1734 double devX, devY;
1735 POINT adjust = {0,0};
1736 POINT advance = {0,0};
1737 GPOS_apply_SingleAdjustment(look, analysis, glyphs, glyph_index, glyph_count, ppem, &adjust, &advance);
1738 if (adjust.x || adjust.y)
1740 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust.x, adjust.y, &devX, &devY);
1741 pGoffset[glyph_index].du += round(devX);
1742 pGoffset[glyph_index].dv += round(devY);
1744 if (advance.x || advance.y)
1746 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance.x, advance.y, &devX, &devY);
1747 piAdvance[glyph_index] += round(devX);
1748 if (advance.y)
1749 FIXME("Unhandled adjustment to Y advancement\n");
1751 break;
1753 case 2:
1755 POINT advance[2]= {{0,0},{0,0}};
1756 POINT adjust[2]= {{0,0},{0,0}};
1757 double devX, devY;
1758 int index;
1759 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1760 int offset_sign = (analysis->fRTL && analysis->fLogicalOrder) ? -1 : 1;
1762 index = GPOS_apply_PairAdjustment(look, analysis, glyphs, glyph_index, glyph_count, ppem, adjust, advance);
1763 if (adjust[0].x || adjust[0].y)
1765 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust[0].x, adjust[0].y, &devX, &devY);
1766 pGoffset[glyph_index].du += round(devX) * offset_sign;
1767 pGoffset[glyph_index].dv += round(devY);
1769 if (advance[0].x || advance[0].y)
1771 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance[0].x, advance[0].y, &devX, &devY);
1772 piAdvance[glyph_index] += round(devX);
1774 if (adjust[1].x || adjust[1].y)
1776 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust[1].x, adjust[1].y, &devX, &devY);
1777 pGoffset[glyph_index + write_dir].du += round(devX) * offset_sign;
1778 pGoffset[glyph_index + write_dir].dv += round(devY);
1780 if (advance[1].x || advance[1].y)
1782 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance[1].x, advance[1].y, &devX, &devY);
1783 piAdvance[glyph_index + write_dir] += round(devX);
1785 return index;
1787 case 3:
1789 POINT desU = {0,0};
1790 double devX, devY;
1791 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1793 GPOS_apply_CursiveAttachment(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU);
1794 if (desU.x || desU.y)
1796 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
1797 /* Windows does not appear to apply X offsets here */
1798 pGoffset[glyph_index].dv = round(devY) + pGoffset[glyph_index+write_dir].dv;
1800 break;
1802 case 4:
1804 double devX, devY;
1805 POINT desU = {0,0};
1806 GPOS_apply_MarkToBase(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU);
1807 if (desU.x || desU.y)
1809 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
1810 if (!analysis->fRTL) pGoffset[glyph_index].du = round(devX) - piAdvance[glyph_index-1];
1811 else
1813 if (analysis->fLogicalOrder) devX *= -1;
1814 pGoffset[glyph_index].du = round(devX);
1816 pGoffset[glyph_index].dv = round(devY);
1818 break;
1820 case 5:
1822 double devX, devY;
1823 POINT desU = {0,0};
1824 GPOS_apply_MarkToLigature(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU);
1825 if (desU.x || desU.y)
1827 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
1828 pGoffset[glyph_index].du = (round(devX) - piAdvance[glyph_index-1]);
1829 pGoffset[glyph_index].dv = round(devY);
1831 break;
1833 case 6:
1835 double devX, devY;
1836 POINT desU = {0,0};
1837 int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
1838 if (GPOS_apply_MarkToMark(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU))
1840 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
1841 if (analysis->fRTL && analysis->fLogicalOrder) devX *= -1;
1842 pGoffset[glyph_index].du = round(devX) + pGoffset[glyph_index - write_dir].du;
1843 pGoffset[glyph_index].dv = round(devY) + pGoffset[glyph_index - write_dir].dv;
1845 break;
1847 case 8:
1849 return GPOS_apply_ChainContextPos(lpotm, lplogfont, analysis, piAdvance, lookup, look, glyphs, glyph_index, glyph_count, ppem, pGoffset);
1851 default:
1852 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
1854 return glyph_index+1;
1857 INT OpenType_apply_GPOS_lookup(LPOUTLINETEXTMETRICW lpotm, LPLOGFONTW lplogfont, const SCRIPT_ANALYSIS *analysis, INT* piAdvance, LPCVOID table,
1858 INT lookup_index, const WORD *glyphs, INT glyph_index, INT glyph_count, GOFFSET *pGoffset)
1860 const GPOS_Header *header = (const GPOS_Header *)table;
1861 const OT_LookupList *lookup = (const OT_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1863 return GPOS_apply_lookup(lpotm, lplogfont, analysis, piAdvance, lookup, lookup_index, glyphs, glyph_index, glyph_count, pGoffset);
1866 static void GSUB_initialize_script_cache(ScriptCache *psc)
1868 int i;
1870 if (psc->GSUB_Table)
1872 const OT_ScriptList *script;
1873 const GSUB_Header* header = (const GSUB_Header*)psc->GSUB_Table;
1874 script = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
1875 psc->script_count = GET_BE_WORD(script->ScriptCount);
1876 TRACE("initializing %i scripts in this font\n",psc->script_count);
1877 if (psc->script_count)
1879 psc->scripts = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(LoadedScript) * psc->script_count);
1880 for (i = 0; i < psc->script_count; i++)
1882 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
1883 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]);
1884 psc->scripts[i].gsub_table = ((const BYTE*)script + offset);
1890 static void GPOS_expand_script_cache(ScriptCache *psc)
1892 int i, count;
1893 const OT_ScriptList *script;
1894 const GPOS_Header* header = (const GPOS_Header*)psc->GPOS_Table;
1896 if (!header)
1897 return;
1899 script = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
1900 count = GET_BE_WORD(script->ScriptCount);
1902 if (!psc->script_count)
1904 psc->script_count = count;
1905 TRACE("initializing %i scripts in this font\n",psc->script_count);
1906 if (psc->script_count)
1908 psc->scripts = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(LoadedScript) * psc->script_count);
1909 for (i = 0; i < psc->script_count; i++)
1911 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
1912 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]);
1913 psc->scripts[i].gpos_table = ((const BYTE*)script + offset);
1917 else
1919 for (i = 0; i < count; i++)
1921 int j;
1922 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
1923 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]);
1924 for (j = 0; j < psc->script_count; j++)
1926 if (psc->scripts[j].tag == tag)
1928 psc->scripts[j].gpos_table = ((const BYTE*)script + offset);
1929 break;
1932 if (j == psc->script_count)
1934 psc->script_count++;
1935 psc->scripts = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,psc->scripts, sizeof(LoadedScript) * psc->script_count);
1936 psc->scripts[j].tag = tag;
1937 psc->scripts[j].gpos_table = ((const BYTE*)script + offset);
1943 static void _initialize_script_cache(ScriptCache *psc)
1945 if (!psc->script_count)
1947 GSUB_initialize_script_cache(psc);
1948 GPOS_expand_script_cache(psc);
1952 HRESULT OpenType_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
1954 int i;
1955 HRESULT rc = S_OK;
1957 _initialize_script_cache(psc);
1959 *pcTags = psc->script_count;
1961 if (!searchingFor && cMaxTags < *pcTags)
1962 rc = E_OUTOFMEMORY;
1963 else if (searchingFor)
1964 rc = USP_E_SCRIPT_NOT_IN_FONT;
1966 for (i = 0; i < psc->script_count; i++)
1968 if (i < cMaxTags)
1969 pScriptTags[i] = psc->scripts[i].tag;
1971 if (searchingFor)
1973 if (searchingFor == psc->scripts[i].tag)
1975 pScriptTags[0] = psc->scripts[i].tag;
1976 *pcTags = 1;
1977 rc = S_OK;
1978 break;
1982 return rc;
1985 static void GSUB_initialize_language_cache(LoadedScript *script)
1987 int i;
1989 if (script->gsub_table)
1991 DWORD offset;
1992 const OT_Script* table = script->gsub_table;
1993 script->language_count = GET_BE_WORD(table->LangSysCount);
1994 offset = GET_BE_WORD(table->DefaultLangSys);
1995 if (offset)
1997 script->default_language.tag = MS_MAKE_TAG('d','f','l','t');
1998 script->default_language.gsub_table = (const BYTE*)table + offset;
2001 if (script->language_count)
2003 TRACE("Deflang %p, LangCount %i\n",script->default_language.gsub_table, script->language_count);
2005 script->languages = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LoadedLanguage) * script->language_count);
2007 for (i = 0; i < script->language_count; i++)
2009 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
2010 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]);
2011 script->languages[i].gsub_table = ((const BYTE*)table + offset);
2017 static void GPOS_expand_language_cache(LoadedScript *script)
2019 int count;
2020 const OT_Script* table = script->gpos_table;
2021 DWORD offset;
2023 if (!table)
2024 return;
2026 offset = GET_BE_WORD(table->DefaultLangSys);
2027 if (offset)
2028 script->default_language.gpos_table = (const BYTE*)table + offset;
2030 count = GET_BE_WORD(table->LangSysCount);
2032 TRACE("Deflang %p, LangCount %i\n",script->default_language.gpos_table, count);
2033 if (!script->language_count)
2035 int i;
2036 script->language_count = count;
2038 script->languages = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LoadedLanguage) * script->language_count);
2040 for (i = 0; i < script->language_count; i++)
2042 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
2043 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]);
2044 script->languages[i].gpos_table = ((const BYTE*)table + offset);
2047 else if (count)
2049 int i,j;
2050 for (i = 0; i < count; i++)
2052 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
2053 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]);
2055 for (j = 0; j < script->language_count; j++)
2057 if (script->languages[j].tag == tag)
2059 script->languages[j].gpos_table = ((const BYTE*)table + offset);
2060 break;
2063 if (j == script->language_count)
2065 script->language_count++;
2066 script->languages = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,script->languages, sizeof(LoadedLanguage) * script->language_count);
2067 script->languages[j].tag = tag;
2068 script->languages[j].gpos_table = ((const BYTE*)table + offset);
2074 static void _initialize_language_cache(LoadedScript *script)
2076 if (!script->language_count)
2078 GSUB_initialize_language_cache(script);
2079 GPOS_expand_language_cache(script);
2083 HRESULT OpenType_GetFontLanguageTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pLanguageTags, int *pcTags)
2085 int i;
2086 HRESULT rc = S_OK;
2087 LoadedScript *script = NULL;
2089 _initialize_script_cache(psc);
2091 for (i = 0; i < psc->script_count; i++)
2093 if (psc->scripts[i].tag == script_tag)
2095 script = &psc->scripts[i];
2096 break;
2100 if (!script)
2101 return E_INVALIDARG;
2103 _initialize_language_cache(script);
2105 if (!searchingFor && cMaxTags < script->language_count)
2106 rc = E_OUTOFMEMORY;
2107 else if (searchingFor)
2108 rc = E_INVALIDARG;
2110 *pcTags = script->language_count;
2112 for (i = 0; i < script->language_count; i++)
2114 if (i < cMaxTags)
2115 pLanguageTags[i] = script->languages[i].tag;
2117 if (searchingFor)
2119 if (searchingFor == script->languages[i].tag)
2121 pLanguageTags[0] = script->languages[i].tag;
2122 *pcTags = 1;
2123 rc = S_OK;
2124 break;
2129 if (script->default_language.gsub_table)
2131 if (i < cMaxTags)
2132 pLanguageTags[i] = script->default_language.tag;
2134 if (searchingFor && FAILED(rc))
2136 pLanguageTags[0] = script->default_language.tag;
2138 i++;
2139 *pcTags = (*pcTags) + 1;
2142 return rc;
2146 static void GSUB_initialize_feature_cache(LPCVOID table, LoadedLanguage *language)
2148 int i;
2150 if (language->gsub_table)
2152 const OT_LangSys *lang = language->gsub_table;
2153 const GSUB_Header *header = (const GSUB_Header *)table;
2154 const OT_FeatureList *feature_list;
2156 language->feature_count = GET_BE_WORD(lang->FeatureCount);
2157 TRACE("%i features\n",language->feature_count);
2159 if (language->feature_count)
2161 language->features = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature)*language->feature_count);
2163 feature_list = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
2165 for (i = 0; i < language->feature_count; i++)
2167 const OT_Feature *feature;
2168 int j;
2169 int index = GET_BE_WORD(lang->FeatureIndex[i]);
2171 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]);
2172 language->features[i].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
2173 feature = (const OT_Feature*)language->features[i].feature;
2174 language->features[i].lookup_count = GET_BE_WORD(feature->LookupCount);
2175 language->features[i].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[i].lookup_count);
2176 for (j = 0; j < language->features[i].lookup_count; j++)
2177 language->features[i].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
2178 language->features[i].tableType = FEATURE_GSUB_TABLE;
2184 static void GPOS_expand_feature_cache(LPCVOID table, LoadedLanguage *language)
2186 int i, count;
2187 const OT_LangSys *lang = language->gpos_table;
2188 const GPOS_Header *header = (const GPOS_Header *)table;
2189 const OT_FeatureList *feature_list;
2191 if (!lang)
2192 return;
2194 count = GET_BE_WORD(lang->FeatureCount);
2195 feature_list = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
2197 TRACE("%i features\n",count);
2198 if (!language->feature_count)
2200 language->feature_count = count;
2202 if (language->feature_count)
2204 language->features = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature)*language->feature_count);
2206 for (i = 0; i < language->feature_count; i++)
2208 const OT_Feature *feature;
2209 int j;
2210 int index = GET_BE_WORD(lang->FeatureIndex[i]);
2212 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]);
2213 language->features[i].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
2214 feature = (const OT_Feature*)language->features[i].feature;
2215 language->features[i].lookup_count = GET_BE_WORD(feature->LookupCount);
2216 language->features[i].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[i].lookup_count);
2217 for (j = 0; j < language->features[i].lookup_count; j++)
2218 language->features[i].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
2219 language->features[i].tableType = FEATURE_GPOS_TABLE;
2223 else if (count)
2225 language->features = HeapReAlloc(GetProcessHeap(),0,language->features, sizeof(LoadedFeature)*(language->feature_count + count));
2227 for (i = 0; i < count; i++)
2229 const OT_Feature *feature;
2230 int j;
2231 int index = GET_BE_WORD(lang->FeatureIndex[i]);
2232 int idx = language->feature_count + i;
2234 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]);
2235 language->features[idx].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
2236 feature = (const OT_Feature*)language->features[idx].feature;
2237 language->features[idx].lookup_count = GET_BE_WORD(feature->LookupCount);
2238 language->features[idx].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[idx].lookup_count);
2239 for (j = 0; j < language->features[idx].lookup_count; j++)
2240 language->features[idx].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
2241 language->features[idx].tableType = FEATURE_GPOS_TABLE;
2243 language->feature_count += count;
2247 static void _initialize_feature_cache(ScriptCache *psc, LoadedLanguage *language)
2249 if (!language->feature_count)
2251 GSUB_initialize_feature_cache(psc->GSUB_Table, language);
2252 GPOS_expand_feature_cache(psc->GPOS_Table, language);
2256 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)
2258 int i;
2259 HRESULT rc = S_OK;
2260 LoadedScript *script = NULL;
2261 LoadedLanguage *language = NULL;
2263 _initialize_script_cache(psc);
2265 for (i = 0; i < psc->script_count; i++)
2267 if (psc->scripts[i].tag == script_tag)
2269 script = &psc->scripts[i];
2270 break;
2274 if (!script)
2276 *pcTags = 0;
2277 if (!filtered)
2278 return S_OK;
2279 else
2280 return E_INVALIDARG;
2283 _initialize_language_cache(script);
2285 if ((script->default_language.gsub_table || script->default_language.gpos_table) && script->default_language.tag == language_tag)
2286 language = &script->default_language;
2287 else
2289 for (i = 0; i < script->language_count; i++)
2291 if (script->languages[i].tag == language_tag)
2293 language = &script->languages[i];
2294 break;
2299 if (!language)
2301 *pcTags = 0;
2302 return S_OK;
2305 _initialize_feature_cache(psc, language);
2307 if (tableType)
2309 *pcTags = 0;
2310 for (i = 0; i < language->feature_count; i++)
2311 if (language->features[i].tableType == tableType)
2312 *pcTags = (*pcTags)+1;
2314 else
2315 *pcTags = language->feature_count;
2317 if (!searchingFor && cMaxTags < *pcTags)
2318 rc = E_OUTOFMEMORY;
2319 else if (searchingFor)
2320 rc = E_INVALIDARG;
2322 for (i = 0; i < language->feature_count; i++)
2324 if (i < cMaxTags)
2326 if (!tableType || language->features[i].tableType == tableType)
2327 pFeatureTags[i] = language->features[i].tag;
2330 if (searchingFor)
2332 if ((searchingFor == language->features[i].tag) &&
2333 (!tableType || language->features[i].tableType == tableType))
2335 pFeatureTags[0] = language->features[i].tag;
2336 *pcTags = 1;
2337 if (feature)
2338 *feature = &language->features[i];
2339 rc = S_OK;
2340 break;
2344 return rc;