usp10: Change OpenType_GSUB_GetFontScriptTags to OpenType_GetFontScriptTags and load...
[wine/multimedia.git] / dlls / usp10 / opentype.c
blob1aa77c02c2d3dd9ee9e73c83f84ba24d12bb7409
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->language_count && 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.table = (const BYTE*)table + offset;
1007 if (script->language_count)
1009 TRACE("Deflang %p, LangCount %i\n",script->default_language.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].table = ((const BYTE*)table + offset);
1023 HRESULT OpenType_GSUB_GetFontLanguageTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pLanguageTags, int *pcTags, LPCVOID* language_table)
1025 int i;
1026 HRESULT rc = S_OK;
1027 LoadedScript *script = NULL;
1029 _initialize_script_cache(psc);
1031 for (i = 0; i < psc->script_count; i++)
1033 if (psc->scripts[i].tag == script_tag)
1035 script = &psc->scripts[i];
1036 break;
1040 if (!script)
1041 return E_INVALIDARG;
1043 GSUB_initialize_language_cache(script);
1045 if (!searchingFor && cMaxTags < script->language_count)
1046 rc = E_OUTOFMEMORY;
1047 else if (searchingFor)
1048 rc = E_INVALIDARG;
1050 *pcTags = script->language_count;
1052 for (i = 0; i < script->language_count; i++)
1054 if (i < cMaxTags)
1055 pLanguageTags[i] = script->languages[i].tag;
1057 if (searchingFor)
1059 if (searchingFor == script->languages[i].tag)
1061 pLanguageTags[0] = script->languages[i].tag;
1062 *pcTags = 1;
1063 if (language_table)
1064 *language_table = script->languages[i].table;
1065 rc = S_OK;
1066 break;
1071 if (script->default_language.table)
1073 if (i < cMaxTags)
1074 pLanguageTags[i] = script->default_language.tag;
1076 if (searchingFor && FAILED(rc))
1078 pLanguageTags[0] = script->default_language.tag;
1079 if (language_table)
1080 *language_table = script->default_language.table;
1082 i++;
1083 *pcTags = (*pcTags) + 1;
1086 return rc;
1090 static void GSUB_initialize_feature_cache(LPCVOID table, LoadedLanguage *language)
1092 int i;
1094 if (!language->feature_count)
1096 const OT_LangSys *lang= language->table;
1097 const GSUB_Header *header = (const GSUB_Header *)table;
1098 const OT_FeatureList *feature_list;
1100 language->feature_count = GET_BE_WORD(lang->FeatureCount);
1101 TRACE("%i features\n",language->feature_count);
1103 if (language->feature_count)
1105 language->features = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature)*language->feature_count);
1107 feature_list = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
1109 for (i = 0; i < language->feature_count; i++)
1111 const OT_Feature *feature;
1112 int j;
1113 int index = GET_BE_WORD(lang->FeatureIndex[i]);
1115 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]);
1116 language->features[i].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
1117 feature = (const OT_Feature*)language->features[i].feature;
1118 language->features[i].lookup_count = GET_BE_WORD(feature->LookupCount);
1119 language->features[i].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[i].lookup_count);
1120 for (j = 0; j < language->features[i].lookup_count; j++)
1121 language->features[i].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
1127 HRESULT OpenType_GSUB_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)
1129 int i;
1130 HRESULT rc = S_OK;
1131 LoadedScript *script = NULL;
1132 LoadedLanguage *language = NULL;
1134 _initialize_script_cache(psc);
1136 for (i = 0; i < psc->script_count; i++)
1138 if (psc->scripts[i].tag == script_tag)
1140 script = &psc->scripts[i];
1141 break;
1145 if (!script)
1147 *pcTags = 0;
1148 if (!filtered)
1149 return S_OK;
1150 else
1151 return E_INVALIDARG;
1154 GSUB_initialize_language_cache(script);
1156 if (script->default_language.table && script->default_language.tag == language_tag)
1157 language = &script->default_language;
1158 else
1160 for (i = 0; i < script->language_count; i++)
1162 if (script->languages[i].tag == language_tag)
1164 language = &script->languages[i];
1165 break;
1170 if (!language)
1172 *pcTags = 0;
1173 return S_OK;
1176 GSUB_initialize_feature_cache(psc->GSUB_Table, language);
1178 *pcTags = language->feature_count;
1180 if (!searchingFor && cMaxTags < *pcTags)
1181 rc = E_OUTOFMEMORY;
1182 else if (searchingFor)
1183 rc = E_INVALIDARG;
1185 for (i = 0; i < language->feature_count; i++)
1187 if (i < cMaxTags)
1188 pFeatureTags[i] = language->features[i].tag;
1190 if (searchingFor)
1192 if (searchingFor == language->features[i].tag)
1194 pFeatureTags[0] = language->features[i].tag;
1195 *pcTags = 1;
1196 if (feature)
1197 *feature = &language->features[i];
1198 rc = S_OK;
1199 break;
1203 return rc;