usp10: Change OpenType_GSUB_GetFontFeatureTags to OpenType_GetFontFeatureTags and...
[wine/multimedia.git] / dlls / usp10 / opentype.c
blobfa160b76540c458cffcb4ec15daccf39006309c4
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 /* These are all structures needed for the cmap format 12 table */
47 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p')
49 typedef struct {
50 WORD platformID;
51 WORD encodingID;
52 DWORD offset;
53 } CMAP_EncodingRecord;
55 typedef struct {
56 WORD version;
57 WORD numTables;
58 CMAP_EncodingRecord tables[1];
59 } CMAP_Header;
61 typedef struct {
62 DWORD startCharCode;
63 DWORD endCharCode;
64 DWORD startGlyphID;
65 } CMAP_SegmentedCoverage_group;
67 typedef struct {
68 WORD format;
69 WORD reserved;
70 DWORD length;
71 DWORD language;
72 DWORD nGroups;
73 CMAP_SegmentedCoverage_group groups[1];
74 } CMAP_SegmentedCoverage;
76 /* These are all structures needed for the GDEF table */
77 #define GDEF_TAG MS_MAKE_TAG('G', 'D', 'E', 'F')
79 enum {BaseGlyph=1, LigatureGlyph, MarkGlyph, ComponentGlyph};
81 typedef struct {
82 DWORD Version;
83 WORD GlyphClassDef;
84 WORD AttachList;
85 WORD LigCaretList;
86 WORD MarkAttachClassDef;
87 } GDEF_Header;
89 typedef struct {
90 WORD ClassFormat;
91 WORD StartGlyph;
92 WORD GlyphCount;
93 WORD ClassValueArray[1];
94 } GDEF_ClassDefFormat1;
96 typedef struct {
97 WORD Start;
98 WORD End;
99 WORD Class;
100 } GDEF_ClassRangeRecord;
102 typedef struct {
103 WORD ClassFormat;
104 WORD ClassRangeCount;
105 GDEF_ClassRangeRecord ClassRangeRecord[1];
106 } GDEF_ClassDefFormat2;
108 /* These are all structures needed for the GSUB table */
110 typedef struct {
111 DWORD version;
112 WORD ScriptList;
113 WORD FeatureList;
114 WORD LookupList;
115 } GSUB_Header;
117 typedef struct {
118 CHAR ScriptTag[4];
119 WORD Script;
120 } OT_ScriptRecord;
122 typedef struct {
123 WORD ScriptCount;
124 OT_ScriptRecord ScriptRecord[1];
125 } OT_ScriptList;
127 typedef struct {
128 CHAR LangSysTag[4];
129 WORD LangSys;
130 } OT_LangSysRecord;
132 typedef struct {
133 WORD DefaultLangSys;
134 WORD LangSysCount;
135 OT_LangSysRecord LangSysRecord[1];
136 } OT_Script;
138 typedef struct {
139 WORD LookupOrder; /* Reserved */
140 WORD ReqFeatureIndex;
141 WORD FeatureCount;
142 WORD FeatureIndex[1];
143 } OT_LangSys;
145 typedef struct {
146 CHAR FeatureTag[4];
147 WORD Feature;
148 } OT_FeatureRecord;
150 typedef struct {
151 WORD FeatureCount;
152 OT_FeatureRecord FeatureRecord[1];
153 } OT_FeatureList;
155 typedef struct {
156 WORD FeatureParams; /* Reserved */
157 WORD LookupCount;
158 WORD LookupListIndex[1];
159 } OT_Feature;
161 typedef struct {
162 WORD LookupCount;
163 WORD Lookup[1];
164 } OT_LookupList;
166 typedef struct {
167 WORD LookupType;
168 WORD LookupFlag;
169 WORD SubTableCount;
170 WORD SubTable[1];
171 } OT_LookupTable;
173 typedef struct {
174 WORD CoverageFormat;
175 WORD GlyphCount;
176 WORD GlyphArray[1];
177 } OT_CoverageFormat1;
179 typedef struct {
180 WORD Start;
181 WORD End;
182 WORD StartCoverageIndex;
183 } OT_RangeRecord;
185 typedef struct {
186 WORD CoverageFormat;
187 WORD RangeCount;
188 OT_RangeRecord RangeRecord[1];
189 } OT_CoverageFormat2;
191 typedef struct {
192 WORD SubstFormat; /* = 1 */
193 WORD Coverage;
194 WORD DeltaGlyphID;
195 } GSUB_SingleSubstFormat1;
197 typedef struct {
198 WORD SubstFormat; /* = 2 */
199 WORD Coverage;
200 WORD GlyphCount;
201 WORD Substitute[1];
202 }GSUB_SingleSubstFormat2;
204 typedef struct {
205 WORD SubstFormat; /* = 1 */
206 WORD Coverage;
207 WORD SequenceCount;
208 WORD Sequence[1];
209 }GSUB_MultipleSubstFormat1;
211 typedef struct {
212 WORD GlyphCount;
213 WORD Substitute[1];
214 }GSUB_Sequence;
216 typedef struct {
217 WORD SubstFormat; /* = 1 */
218 WORD Coverage;
219 WORD LigSetCount;
220 WORD LigatureSet[1];
221 }GSUB_LigatureSubstFormat1;
223 typedef struct {
224 WORD LigatureCount;
225 WORD Ligature[1];
226 }GSUB_LigatureSet;
228 typedef struct{
229 WORD LigGlyph;
230 WORD CompCount;
231 WORD Component[1];
232 }GSUB_Ligature;
234 typedef struct{
235 WORD SequenceIndex;
236 WORD LookupListIndex;
238 }GSUB_SubstLookupRecord;
240 typedef struct{
241 WORD SubstFormat; /* = 1 */
242 WORD Coverage;
243 WORD ChainSubRuleSetCount;
244 WORD ChainSubRuleSet[1];
245 }GSUB_ChainContextSubstFormat1;
247 typedef struct {
248 WORD SubstFormat; /* = 3 */
249 WORD BacktrackGlyphCount;
250 WORD Coverage[1];
251 }GSUB_ChainContextSubstFormat3_1;
253 typedef struct{
254 WORD InputGlyphCount;
255 WORD Coverage[1];
256 }GSUB_ChainContextSubstFormat3_2;
258 typedef struct{
259 WORD LookaheadGlyphCount;
260 WORD Coverage[1];
261 }GSUB_ChainContextSubstFormat3_3;
263 typedef struct{
264 WORD SubstCount;
265 GSUB_SubstLookupRecord SubstLookupRecord[1];
266 }GSUB_ChainContextSubstFormat3_4;
268 typedef struct {
269 WORD SubstFormat; /* = 1 */
270 WORD Coverage;
271 WORD AlternateSetCount;
272 WORD AlternateSet[1];
273 } GSUB_AlternateSubstFormat1;
275 typedef struct{
276 WORD GlyphCount;
277 WORD Alternate[1];
278 } GSUB_AlternateSet;
280 /* These are all structures needed for the GPOS table */
282 typedef struct {
283 DWORD version;
284 WORD ScriptList;
285 WORD FeatureList;
286 WORD LookupList;
287 } GPOS_Header;
289 /**********
290 * CMAP
291 **********/
293 static VOID *load_CMAP_format12_table(HDC hdc, ScriptCache *psc)
295 CMAP_Header *CMAP_Table = NULL;
296 int length;
297 int i;
299 if (!psc->CMAP_Table)
301 length = GetFontData(hdc, CMAP_TAG , 0, NULL, 0);
302 if (length != GDI_ERROR)
304 psc->CMAP_Table = HeapAlloc(GetProcessHeap(),0,length);
305 GetFontData(hdc, CMAP_TAG , 0, psc->CMAP_Table, length);
306 TRACE("Loaded cmap table of %i bytes\n",length);
308 else
309 return NULL;
312 CMAP_Table = psc->CMAP_Table;
314 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++)
316 if ( (GET_BE_WORD(CMAP_Table->tables[i].platformID) == 3) &&
317 (GET_BE_WORD(CMAP_Table->tables[i].encodingID) == 10) )
319 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
320 if (GET_BE_WORD(format->format) == 12)
321 return format;
324 return NULL;
327 static int compare_group(const void *a, const void* b)
329 const DWORD *chr = a;
330 const CMAP_SegmentedCoverage_group *group = b;
332 if (*chr < GET_BE_DWORD(group->startCharCode))
333 return -1;
334 if (*chr > GET_BE_DWORD(group->endCharCode))
335 return 1;
336 return 0;
339 DWORD OpenType_CMAP_GetGlyphIndex(HDC hdc, ScriptCache *psc, DWORD utf32c, LPWORD pgi, DWORD flags)
341 /* BMP: use gdi32 for ease */
342 if (utf32c < 0x10000)
344 WCHAR ch = utf32c;
345 return GetGlyphIndicesW(hdc,&ch, 1, pgi, flags);
348 if (!psc->CMAP_format12_Table)
349 psc->CMAP_format12_Table = load_CMAP_format12_table(hdc, psc);
351 if (flags & GGI_MARK_NONEXISTING_GLYPHS)
352 *pgi = 0xffff;
353 else
354 *pgi = 0;
356 if (psc->CMAP_format12_Table)
358 CMAP_SegmentedCoverage *format = NULL;
359 CMAP_SegmentedCoverage_group *group = NULL;
361 format = (CMAP_SegmentedCoverage *)psc->CMAP_format12_Table;
363 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups),
364 sizeof(CMAP_SegmentedCoverage_group), compare_group);
366 if (group)
368 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode);
369 *pgi = GET_BE_DWORD(group->startGlyphID) + offset;
370 return 0;
373 return 0;
376 /**********
377 * GDEF
378 **********/
380 static WORD GDEF_get_glyph_class(const GDEF_Header *header, WORD glyph)
382 int offset;
383 WORD class = 0;
384 const GDEF_ClassDefFormat1 *cf1;
386 if (!header)
387 return 0;
389 offset = GET_BE_WORD(header->GlyphClassDef);
390 if (!offset)
391 return 0;
393 cf1 = (GDEF_ClassDefFormat1*)(((BYTE*)header)+offset);
394 if (GET_BE_WORD(cf1->ClassFormat) == 1)
396 if (glyph >= GET_BE_WORD(cf1->StartGlyph))
398 int index = glyph - GET_BE_WORD(cf1->StartGlyph);
399 if (index < GET_BE_WORD(cf1->GlyphCount))
400 class = GET_BE_WORD(cf1->ClassValueArray[index]);
403 else if (GET_BE_WORD(cf1->ClassFormat) == 2)
405 const GDEF_ClassDefFormat2 *cf2 = (GDEF_ClassDefFormat2*)cf1;
406 int i, top;
407 top = GET_BE_WORD(cf2->ClassRangeCount);
408 for (i = 0; i < top; i++)
410 if (glyph >= GET_BE_WORD(cf2->ClassRangeRecord[i].Start) &&
411 glyph <= GET_BE_WORD(cf2->ClassRangeRecord[i].End))
413 class = GET_BE_WORD(cf2->ClassRangeRecord[i].Class);
414 break;
418 else
419 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1->ClassFormat));
421 return class;
424 static VOID *load_gdef_table(HDC hdc)
426 VOID* GDEF_Table = NULL;
427 int length = GetFontData(hdc, GDEF_TAG , 0, NULL, 0);
428 if (length != GDI_ERROR)
430 GDEF_Table = HeapAlloc(GetProcessHeap(),0,length);
431 GetFontData(hdc, GDEF_TAG , 0, GDEF_Table, length);
432 TRACE("Loaded GDEF table of %i bytes\n",length);
434 return GDEF_Table;
437 void OpenType_GDEF_UpdateGlyphProps(HDC hdc, ScriptCache *psc, const WORD *pwGlyphs, const WORD cGlyphs, WORD* pwLogClust, const WORD cChars, SCRIPT_GLYPHPROP *pGlyphProp)
439 int i;
441 if (!psc->GDEF_Table)
442 psc->GDEF_Table = load_gdef_table(hdc);
444 for (i = 0; i < cGlyphs; i++)
446 WORD class;
447 int char_count = 0;
448 int k;
450 k = USP10_FindGlyphInLogClust(pwLogClust, cChars, i);
451 if (k >= 0)
453 for (; k < cChars && pwLogClust[k] == i; k++)
454 char_count++;
457 class = GDEF_get_glyph_class(psc->GDEF_Table, pwGlyphs[i]);
459 switch (class)
461 case 0:
462 case BaseGlyph:
463 pGlyphProp[i].sva.fClusterStart = 1;
464 pGlyphProp[i].sva.fDiacritic = 0;
465 pGlyphProp[i].sva.fZeroWidth = 0;
466 break;
467 case LigatureGlyph:
468 pGlyphProp[i].sva.fClusterStart = 1;
469 pGlyphProp[i].sva.fDiacritic = 0;
470 pGlyphProp[i].sva.fZeroWidth = 0;
471 break;
472 case MarkGlyph:
473 pGlyphProp[i].sva.fClusterStart = 0;
474 pGlyphProp[i].sva.fDiacritic = 1;
475 pGlyphProp[i].sva.fZeroWidth = 1;
476 break;
477 case ComponentGlyph:
478 pGlyphProp[i].sva.fClusterStart = 0;
479 pGlyphProp[i].sva.fDiacritic = 0;
480 pGlyphProp[i].sva.fZeroWidth = 0;
481 break;
482 default:
483 ERR("Unknown glyph class %i\n",class);
484 pGlyphProp[i].sva.fClusterStart = 1;
485 pGlyphProp[i].sva.fDiacritic = 0;
486 pGlyphProp[i].sva.fZeroWidth = 0;
489 if (char_count == 0)
490 pGlyphProp[i].sva.fClusterStart = 0;
494 /**********
495 * GSUB
496 **********/
497 static INT GSUB_apply_lookup(const OT_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
499 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
501 const OT_CoverageFormat1* cf1;
503 cf1 = table;
505 if (GET_BE_WORD(cf1->CoverageFormat) == 1)
507 int count = GET_BE_WORD(cf1->GlyphCount);
508 int i;
509 TRACE("Coverage Format 1, %i glyphs\n",count);
510 for (i = 0; i < count; i++)
511 if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
512 return i;
513 return -1;
515 else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
517 const OT_CoverageFormat2* cf2;
518 int i;
519 int count;
520 cf2 = (const OT_CoverageFormat2*)cf1;
522 count = GET_BE_WORD(cf2->RangeCount);
523 TRACE("Coverage Format 2, %i ranges\n",count);
524 for (i = 0; i < count; i++)
526 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
527 return -1;
528 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
529 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
531 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
532 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
535 return -1;
537 else
538 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
540 return -1;
543 static INT GSUB_apply_SingleSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
545 int j;
546 TRACE("Single Substitution Subtable\n");
548 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
550 int offset;
551 const GSUB_SingleSubstFormat1 *ssf1;
552 offset = GET_BE_WORD(look->SubTable[j]);
553 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
554 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
556 int offset = GET_BE_WORD(ssf1->Coverage);
557 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
558 if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
560 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
561 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
562 TRACE(" 0x%x\n",glyphs[glyph_index]);
563 return glyph_index + write_dir;
566 else
568 const GSUB_SingleSubstFormat2 *ssf2;
569 INT index;
570 INT offset;
572 ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
573 offset = GET_BE_WORD(ssf1->Coverage);
574 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
575 index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
576 TRACE(" Coverage index %i\n",index);
577 if (index != -1)
579 if (glyphs[glyph_index] == GET_BE_WORD(ssf2->Substitute[index]))
580 return GSUB_E_NOGLYPH;
582 TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]);
583 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
584 TRACE("0x%x\n",glyphs[glyph_index]);
585 return glyph_index + write_dir;
589 return GSUB_E_NOGLYPH;
592 static INT GSUB_apply_MultipleSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
594 int j;
595 TRACE("Multiple Substitution Subtable\n");
597 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
599 int offset, index;
600 const GSUB_MultipleSubstFormat1 *msf1;
601 offset = GET_BE_WORD(look->SubTable[j]);
602 msf1 = (const GSUB_MultipleSubstFormat1*)((const BYTE*)look+offset);
604 offset = GET_BE_WORD(msf1->Coverage);
605 index = GSUB_is_glyph_covered((const BYTE*)msf1+offset, glyphs[glyph_index]);
606 if (index != -1)
608 const GSUB_Sequence *seq;
609 int sub_count;
610 int j;
611 offset = GET_BE_WORD(msf1->Sequence[index]);
612 seq = (const GSUB_Sequence*)((const BYTE*)msf1+offset);
613 sub_count = GET_BE_WORD(seq->GlyphCount);
614 TRACE(" Glyph 0x%x (+%i)->",glyphs[glyph_index],(sub_count-1));
616 for (j = (*glyph_count)+(sub_count-1); j > glyph_index; j--)
617 glyphs[j] =glyphs[j-(sub_count-1)];
619 for (j = 0; j < sub_count; j++)
620 if (write_dir < 0)
621 glyphs[glyph_index + (sub_count-1) - j] = GET_BE_WORD(seq->Substitute[j]);
622 else
623 glyphs[glyph_index + j] = GET_BE_WORD(seq->Substitute[j]);
625 *glyph_count = *glyph_count + (sub_count - 1);
627 if (TRACE_ON(uniscribe))
629 for (j = 0; j < sub_count; j++)
630 TRACE(" 0x%x",glyphs[glyph_index+j]);
631 TRACE("\n");
634 return glyph_index + (sub_count * write_dir);
637 return GSUB_E_NOGLYPH;
640 static INT GSUB_apply_AlternateSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
642 int j;
643 TRACE("Alternate Substitution Subtable\n");
645 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
647 int offset;
648 const GSUB_AlternateSubstFormat1 *asf1;
649 INT index;
651 offset = GET_BE_WORD(look->SubTable[j]);
652 asf1 = (const GSUB_AlternateSubstFormat1*)((const BYTE*)look+offset);
653 offset = GET_BE_WORD(asf1->Coverage);
655 index = GSUB_is_glyph_covered((const BYTE*)asf1+offset, glyphs[glyph_index]);
656 if (index != -1)
658 const GSUB_AlternateSet *as;
659 offset = GET_BE_WORD(asf1->AlternateSet[index]);
660 as = (const GSUB_AlternateSet*)((const BYTE*)asf1+offset);
661 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as->GlyphCount));
662 if (glyphs[glyph_index] == GET_BE_WORD(as->Alternate[0]))
663 return GSUB_E_NOGLYPH;
665 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
666 glyphs[glyph_index] = GET_BE_WORD(as->Alternate[0]);
667 TRACE(" 0x%x\n",glyphs[glyph_index]);
668 return glyph_index + write_dir;
671 return GSUB_E_NOGLYPH;
674 static INT GSUB_apply_LigatureSubst(const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
676 int j;
678 TRACE("Ligature Substitution Subtable\n");
679 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
681 const GSUB_LigatureSubstFormat1 *lsf1;
682 int offset,index;
684 offset = GET_BE_WORD(look->SubTable[j]);
685 lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
686 offset = GET_BE_WORD(lsf1->Coverage);
687 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
688 TRACE(" Coverage index %i\n",index);
689 if (index != -1)
691 const GSUB_LigatureSet *ls;
692 int k, count;
694 offset = GET_BE_WORD(lsf1->LigatureSet[index]);
695 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
696 count = GET_BE_WORD(ls->LigatureCount);
697 TRACE(" LigatureSet has %i members\n",count);
698 for (k = 0; k < count; k++)
700 const GSUB_Ligature *lig;
701 int CompCount,l,CompIndex;
703 offset = GET_BE_WORD(ls->Ligature[k]);
704 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
705 CompCount = GET_BE_WORD(lig->CompCount) - 1;
706 CompIndex = glyph_index+write_dir;
707 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
709 int CompGlyph;
710 CompGlyph = GET_BE_WORD(lig->Component[l]);
711 if (CompGlyph != glyphs[CompIndex])
712 break;
713 CompIndex += write_dir;
715 if (l == CompCount)
717 int replaceIdx = glyph_index;
718 if (write_dir < 0)
719 replaceIdx = glyph_index - CompCount;
721 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
722 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
723 TRACE("0x%x\n",glyphs[replaceIdx]);
724 if (CompCount > 0)
726 int j;
727 for (j = replaceIdx + 1; j < *glyph_count; j++)
728 glyphs[j] =glyphs[j+CompCount];
729 *glyph_count = *glyph_count - CompCount;
731 return replaceIdx + write_dir;
736 return GSUB_E_NOGLYPH;
739 static INT GSUB_apply_ChainContextSubst(const OT_LookupList* lookup, const OT_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
741 int j;
742 BOOL done = FALSE;
744 TRACE("Chaining Contextual Substitution Subtable\n");
745 for (j = 0; j < GET_BE_WORD(look->SubTableCount) && !done; j++)
747 const GSUB_ChainContextSubstFormat1 *ccsf1;
748 int offset;
749 int dirLookahead = write_dir;
750 int dirBacktrack = -1 * write_dir;
752 offset = GET_BE_WORD(look->SubTable[j]);
753 ccsf1 = (const GSUB_ChainContextSubstFormat1*)((const BYTE*)look+offset);
754 if (GET_BE_WORD(ccsf1->SubstFormat) == 1)
756 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
757 continue;
759 else if (GET_BE_WORD(ccsf1->SubstFormat) == 2)
761 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
762 continue;
764 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
766 int k;
767 int indexGlyphs;
768 const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
769 const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
770 const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
771 const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
772 int newIndex = glyph_index;
774 ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
776 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
778 for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
780 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
781 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
782 break;
784 if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
785 continue;
786 TRACE("Matched Backtrack\n");
788 ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)(((LPBYTE)ccsf1)+sizeof(GSUB_ChainContextSubstFormat3_1) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)-1)));
790 indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
791 for (k = 0; k < indexGlyphs; k++)
793 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
794 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
795 break;
797 if (k != indexGlyphs)
798 continue;
799 TRACE("Matched IndexGlyphs\n");
801 ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)(((LPBYTE)ccsf3_2)+sizeof(GSUB_ChainContextSubstFormat3_2) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_2->InputGlyphCount)-1)));
803 for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
805 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
806 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
807 break;
809 if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
810 continue;
811 TRACE("Matched LookAhead\n");
813 ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)(((LPBYTE)ccsf3_3)+sizeof(GSUB_ChainContextSubstFormat3_3) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)-1)));
815 if (GET_BE_WORD(ccsf3_4->SubstCount))
817 for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
819 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
820 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
822 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
823 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
824 if (newIndex == -1)
826 ERR("Chain failed to generate a glyph\n");
827 continue;
830 return newIndex;
832 else return GSUB_E_NOGLYPH;
835 return -1;
838 static INT GSUB_apply_lookup(const OT_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
840 int offset;
841 const OT_LookupTable *look;
843 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
844 look = (const OT_LookupTable*)((const BYTE*)lookup + offset);
845 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
846 switch(GET_BE_WORD(look->LookupType))
848 case 1:
849 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
850 case 2:
851 return GSUB_apply_MultipleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
852 case 3:
853 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count);
854 case 4:
855 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
856 case 6:
857 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
858 default:
859 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
861 return GSUB_E_NOGLYPH;
864 INT OpenType_apply_GSUB_lookup(LPCVOID table, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
866 const GSUB_Header *header = (const GSUB_Header *)table;
867 const OT_LookupList *lookup = (const OT_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
869 return GSUB_apply_lookup(lookup, lookup_index, glyphs, glyph_index, write_dir, glyph_count);
872 static void GSUB_initialize_script_cache(ScriptCache *psc)
874 int i;
876 if (psc->GSUB_Table)
878 const OT_ScriptList *script;
879 const GSUB_Header* header = (const GSUB_Header*)psc->GSUB_Table;
880 script = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
881 psc->script_count = GET_BE_WORD(script->ScriptCount);
882 TRACE("initializing %i scripts in this font\n",psc->script_count);
883 if (psc->script_count)
885 psc->scripts = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(LoadedScript) * psc->script_count);
886 for (i = 0; i < psc->script_count; i++)
888 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
889 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]);
890 psc->scripts[i].gsub_table = ((const BYTE*)script + offset);
896 static void GPOS_expand_script_cache(ScriptCache *psc)
898 int i, count;
899 const OT_ScriptList *script;
900 const GPOS_Header* header = (const GPOS_Header*)psc->GPOS_Table;
902 if (!header)
903 return;
905 script = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
906 count = GET_BE_WORD(script->ScriptCount);
908 if (!psc->script_count)
910 psc->script_count = count;
911 TRACE("initializing %i scripts in this font\n",psc->script_count);
912 if (psc->script_count)
914 psc->scripts = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(LoadedScript) * psc->script_count);
915 for (i = 0; i < psc->script_count; i++)
917 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
918 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]);
919 psc->scripts[i].gpos_table = ((const BYTE*)script + offset);
923 else
925 for (i = 0; i < count; i++)
927 int j;
928 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
929 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]);
930 for (j = 0; j < psc->script_count; j++)
932 if (psc->scripts[j].tag == tag)
934 psc->scripts[j].gpos_table = ((const BYTE*)script + offset);
935 break;
938 if (j == psc->script_count)
940 psc->script_count++;
941 psc->scripts = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,psc->scripts, sizeof(LoadedScript) * psc->script_count);
942 psc->scripts[j].tag = tag;
943 psc->scripts[j].gpos_table = ((const BYTE*)script + offset);
949 static void _initialize_script_cache(ScriptCache *psc)
951 if (!psc->script_count)
953 GSUB_initialize_script_cache(psc);
954 GPOS_expand_script_cache(psc);
958 HRESULT OpenType_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
960 int i;
961 HRESULT rc = S_OK;
963 _initialize_script_cache(psc);
965 *pcTags = psc->script_count;
967 if (!searchingFor && cMaxTags < *pcTags)
968 rc = E_OUTOFMEMORY;
969 else if (searchingFor)
970 rc = USP_E_SCRIPT_NOT_IN_FONT;
972 for (i = 0; i < psc->script_count; i++)
974 if (i < cMaxTags)
975 pScriptTags[i] = psc->scripts[i].tag;
977 if (searchingFor)
979 if (searchingFor == psc->scripts[i].tag)
981 pScriptTags[0] = psc->scripts[i].tag;
982 *pcTags = 1;
983 rc = S_OK;
984 break;
988 return rc;
991 static void GSUB_initialize_language_cache(LoadedScript *script)
993 int i;
995 if (script->gsub_table)
997 DWORD offset;
998 const OT_Script* table = script->gsub_table;
999 script->language_count = GET_BE_WORD(table->LangSysCount);
1000 offset = GET_BE_WORD(table->DefaultLangSys);
1001 if (offset)
1003 script->default_language.tag = MS_MAKE_TAG('d','f','l','t');
1004 script->default_language.gsub_table = (const BYTE*)table + offset;
1007 if (script->language_count)
1009 TRACE("Deflang %p, LangCount %i\n",script->default_language.gsub_table, script->language_count);
1011 script->languages = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LoadedLanguage) * script->language_count);
1013 for (i = 0; i < script->language_count; i++)
1015 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
1016 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]);
1017 script->languages[i].gsub_table = ((const BYTE*)table + offset);
1023 static void GPOS_expand_language_cache(LoadedScript *script)
1025 int count;
1026 const OT_Script* table = script->gpos_table;
1027 DWORD offset;
1029 if (!table)
1030 return;
1032 offset = GET_BE_WORD(table->DefaultLangSys);
1033 if (offset)
1034 script->default_language.gpos_table = (const BYTE*)table + offset;
1036 count = GET_BE_WORD(table->LangSysCount);
1038 TRACE("Deflang %p, LangCount %i\n",script->default_language.gpos_table, count);
1039 if (!script->language_count)
1041 int i;
1042 script->language_count = count;
1044 script->languages = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LoadedLanguage) * script->language_count);
1046 for (i = 0; i < script->language_count; i++)
1048 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
1049 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]);
1050 script->languages[i].gpos_table = ((const BYTE*)table + offset);
1053 else if (count)
1055 int i,j;
1056 for (i = 0; i < count; i++)
1058 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
1059 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]);
1061 for (j = 0; j < script->language_count; j++)
1063 if (script->languages[j].tag == tag)
1065 script->languages[j].gpos_table = ((const BYTE*)table + offset);
1066 break;
1069 if (j == script->language_count)
1071 script->language_count++;
1072 script->languages = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,script->languages, sizeof(LoadedLanguage) * script->language_count);
1073 script->languages[j].tag = tag;
1074 script->languages[j].gpos_table = ((const BYTE*)table + offset);
1080 static void _initialize_language_cache(LoadedScript *script)
1082 if (!script->language_count)
1084 GSUB_initialize_language_cache(script);
1085 GPOS_expand_language_cache(script);
1089 HRESULT OpenType_GetFontLanguageTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pLanguageTags, int *pcTags)
1091 int i;
1092 HRESULT rc = S_OK;
1093 LoadedScript *script = NULL;
1095 _initialize_script_cache(psc);
1097 for (i = 0; i < psc->script_count; i++)
1099 if (psc->scripts[i].tag == script_tag)
1101 script = &psc->scripts[i];
1102 break;
1106 if (!script)
1107 return E_INVALIDARG;
1109 _initialize_language_cache(script);
1111 if (!searchingFor && cMaxTags < script->language_count)
1112 rc = E_OUTOFMEMORY;
1113 else if (searchingFor)
1114 rc = E_INVALIDARG;
1116 *pcTags = script->language_count;
1118 for (i = 0; i < script->language_count; i++)
1120 if (i < cMaxTags)
1121 pLanguageTags[i] = script->languages[i].tag;
1123 if (searchingFor)
1125 if (searchingFor == script->languages[i].tag)
1127 pLanguageTags[0] = script->languages[i].tag;
1128 *pcTags = 1;
1129 rc = S_OK;
1130 break;
1135 if (script->default_language.gsub_table)
1137 if (i < cMaxTags)
1138 pLanguageTags[i] = script->default_language.tag;
1140 if (searchingFor && FAILED(rc))
1142 pLanguageTags[0] = script->default_language.tag;
1144 i++;
1145 *pcTags = (*pcTags) + 1;
1148 return rc;
1152 static void GSUB_initialize_feature_cache(LPCVOID table, LoadedLanguage *language)
1154 int i;
1156 if (language->gsub_table)
1158 const OT_LangSys *lang = language->gsub_table;
1159 const GSUB_Header *header = (const GSUB_Header *)table;
1160 const OT_FeatureList *feature_list;
1162 language->feature_count = GET_BE_WORD(lang->FeatureCount);
1163 TRACE("%i features\n",language->feature_count);
1165 if (language->feature_count)
1167 language->features = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature)*language->feature_count);
1169 feature_list = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
1171 for (i = 0; i < language->feature_count; i++)
1173 const OT_Feature *feature;
1174 int j;
1175 int index = GET_BE_WORD(lang->FeatureIndex[i]);
1177 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]);
1178 language->features[i].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
1179 feature = (const OT_Feature*)language->features[i].feature;
1180 language->features[i].lookup_count = GET_BE_WORD(feature->LookupCount);
1181 language->features[i].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[i].lookup_count);
1182 for (j = 0; j < language->features[i].lookup_count; j++)
1183 language->features[i].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
1189 static void GPOS_expand_feature_cache(LPCVOID table, LoadedLanguage *language)
1191 int i, count;
1192 const OT_LangSys *lang = language->gpos_table;
1193 const GPOS_Header *header = (const GPOS_Header *)table;
1194 const OT_FeatureList *feature_list;
1196 if (!lang)
1197 return;
1199 count = GET_BE_WORD(lang->FeatureCount);
1200 feature_list = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
1202 TRACE("%i features\n",count);
1203 if (!language->feature_count)
1205 language->feature_count = count;
1207 if (language->feature_count)
1209 language->features = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature)*language->feature_count);
1211 for (i = 0; i < language->feature_count; i++)
1213 const OT_Feature *feature;
1214 int j;
1215 int index = GET_BE_WORD(lang->FeatureIndex[i]);
1217 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]);
1218 language->features[i].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
1219 feature = (const OT_Feature*)language->features[i].feature;
1220 language->features[i].lookup_count = GET_BE_WORD(feature->LookupCount);
1221 language->features[i].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[i].lookup_count);
1222 for (j = 0; j < language->features[i].lookup_count; j++)
1223 language->features[i].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
1227 else if (count)
1229 language->features = HeapReAlloc(GetProcessHeap(),0,language->features, sizeof(LoadedFeature)*(language->feature_count + count));
1231 for (i = 0; i < count; i++)
1233 const OT_Feature *feature;
1234 int j;
1235 int index = GET_BE_WORD(lang->FeatureIndex[i]);
1236 int idx = language->feature_count + i;
1238 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]);
1239 language->features[idx].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
1240 feature = (const OT_Feature*)language->features[idx].feature;
1241 language->features[idx].lookup_count = GET_BE_WORD(feature->LookupCount);
1242 language->features[idx].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[idx].lookup_count);
1243 for (j = 0; j < language->features[idx].lookup_count; j++)
1244 language->features[idx].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
1246 language->feature_count += count;
1250 static void _initialize_feature_cache(ScriptCache *psc, LoadedLanguage *language)
1252 if (!language->feature_count)
1254 GSUB_initialize_feature_cache(psc->GSUB_Table, language);
1255 GPOS_expand_feature_cache(psc->GPOS_Table, language);
1259 HRESULT OpenType_GetFontFeatureTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG language_tag, BOOL filtered, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags, LoadedFeature** feature)
1261 int i;
1262 HRESULT rc = S_OK;
1263 LoadedScript *script = NULL;
1264 LoadedLanguage *language = NULL;
1266 _initialize_script_cache(psc);
1268 for (i = 0; i < psc->script_count; i++)
1270 if (psc->scripts[i].tag == script_tag)
1272 script = &psc->scripts[i];
1273 break;
1277 if (!script)
1279 *pcTags = 0;
1280 if (!filtered)
1281 return S_OK;
1282 else
1283 return E_INVALIDARG;
1286 _initialize_language_cache(script);
1288 if ((script->default_language.gsub_table || script->default_language.gpos_table) && script->default_language.tag == language_tag)
1289 language = &script->default_language;
1290 else
1292 for (i = 0; i < script->language_count; i++)
1294 if (script->languages[i].tag == language_tag)
1296 language = &script->languages[i];
1297 break;
1302 if (!language)
1304 *pcTags = 0;
1305 return S_OK;
1308 _initialize_feature_cache(psc, language);
1310 *pcTags = language->feature_count;
1312 if (!searchingFor && cMaxTags < *pcTags)
1313 rc = E_OUTOFMEMORY;
1314 else if (searchingFor)
1315 rc = E_INVALIDARG;
1317 for (i = 0; i < language->feature_count; i++)
1319 if (i < cMaxTags)
1320 pFeatureTags[i] = language->features[i].tag;
1322 if (searchingFor)
1324 if (searchingFor == language->features[i].tag)
1326 pFeatureTags[0] = language->features[i].tag;
1327 *pcTags = 1;
1328 if (feature)
1329 *feature = &language->features[i];
1330 rc = S_OK;
1331 break;
1335 return rc;