usp10: Replace GSUB_get_script_table with GSUB_GetFontScriptTags.
[wine.git] / dlls / usp10 / shape.c
blobe9e57218d5d293fb4f303c3abd9f262c36999ac4
1 /*
2 * Implementation of Shaping for the Uniscribe Script Processor (usp10.dll)
4 * Copyright 2010 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 #define FIRST_ARABIC_CHAR 0x0600
39 #define LAST_ARABIC_CHAR 0x06ff
41 typedef VOID (*ContextualShapingProc)(HDC, ScriptCache*, SCRIPT_ANALYSIS*,
42 WCHAR*, INT, WORD*, INT*, INT, WORD*);
44 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
45 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
46 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
47 static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
48 static void ContextualShape_Devanagari(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
49 static void ContextualShape_Bengali(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
50 static void ContextualShape_Gurmukhi(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
51 static void ContextualShape_Gujarati(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
52 static void ContextualShape_Oriya(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
53 static void ContextualShape_Tamil(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
54 static void ContextualShape_Telugu(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
55 static void ContextualShape_Kannada(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
56 static void ContextualShape_Malayalam(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
57 static void ContextualShape_Khmer(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
58 static void ContextualShape_Mongolian(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
60 typedef VOID (*ShapeCharGlyphPropProc)( HDC , ScriptCache*, SCRIPT_ANALYSIS*, const WCHAR*, const INT, const WORD*, const INT, WORD*, SCRIPT_CHARPROP*, SCRIPT_GLYPHPROP*);
62 static void ShapeCharGlyphProp_Default( HDC hdc, ScriptCache* psc, SCRIPT_ANALYSIS* psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD* pwLogClust, SCRIPT_CHARPROP* pCharProp, SCRIPT_GLYPHPROP* pGlyphProp);
63 static void ShapeCharGlyphProp_Arabic( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP* pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
64 static void ShapeCharGlyphProp_Thai( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
65 static void ShapeCharGlyphProp_None( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
66 static void ShapeCharGlyphProp_Tibet( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
67 static void ShapeCharGlyphProp_Sinhala( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
68 static void ShapeCharGlyphProp_Devanagari( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
69 static void ShapeCharGlyphProp_Bengali( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
70 static void ShapeCharGlyphProp_Gurmukhi( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
71 static void ShapeCharGlyphProp_Gujarati( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
72 static void ShapeCharGlyphProp_Oriya( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
73 static void ShapeCharGlyphProp_Tamil( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
74 static void ShapeCharGlyphProp_Telugu( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
75 static void ShapeCharGlyphProp_Kannada( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
76 static void ShapeCharGlyphProp_Malayalam( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
77 static void ShapeCharGlyphProp_Khmer( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
79 extern const unsigned short indic_syllabic_table[];
80 extern const unsigned short wine_shaping_table[];
81 extern const unsigned short wine_shaping_forms[LAST_ARABIC_CHAR - FIRST_ARABIC_CHAR + 1][4];
83 enum joining_types {
84 jtU,
85 jtT,
86 jtR,
87 jtL,
88 jtD,
89 jtC
92 enum joined_forms {
93 Xn=0,
94 Xr,
95 Xl,
96 Xm,
97 /* Syriac Alaph */
98 Afj,
99 Afn,
103 #ifdef WORDS_BIGENDIAN
104 #define GET_BE_WORD(x) (x)
105 #define GET_BE_DWORD(x) (x)
106 #else
107 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
108 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
109 #endif
111 /* These are all structures needed for the GSUB table */
112 #define GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B')
113 #define GSUB_E_NOFEATURE -2
114 #define GSUB_E_NOGLYPH -1
116 typedef struct {
117 DWORD version;
118 WORD ScriptList;
119 WORD FeatureList;
120 WORD LookupList;
121 } GSUB_Header;
123 typedef struct {
124 CHAR ScriptTag[4];
125 WORD Script;
126 } GSUB_ScriptRecord;
128 typedef struct {
129 WORD ScriptCount;
130 GSUB_ScriptRecord ScriptRecord[1];
131 } GSUB_ScriptList;
133 typedef struct {
134 CHAR LangSysTag[4];
135 WORD LangSys;
136 } GSUB_LangSysRecord;
138 typedef struct {
139 WORD DefaultLangSys;
140 WORD LangSysCount;
141 GSUB_LangSysRecord LangSysRecord[1];
142 } GSUB_Script;
144 typedef struct {
145 WORD LookupOrder; /* Reserved */
146 WORD ReqFeatureIndex;
147 WORD FeatureCount;
148 WORD FeatureIndex[1];
149 } GSUB_LangSys;
151 typedef struct {
152 CHAR FeatureTag[4];
153 WORD Feature;
154 } GSUB_FeatureRecord;
156 typedef struct {
157 WORD FeatureCount;
158 GSUB_FeatureRecord FeatureRecord[1];
159 } GSUB_FeatureList;
161 typedef struct {
162 WORD FeatureParams; /* Reserved */
163 WORD LookupCount;
164 WORD LookupListIndex[1];
165 } GSUB_Feature;
167 typedef struct {
168 WORD LookupCount;
169 WORD Lookup[1];
170 } GSUB_LookupList;
172 typedef struct {
173 WORD LookupType;
174 WORD LookupFlag;
175 WORD SubTableCount;
176 WORD SubTable[1];
177 } GSUB_LookupTable;
179 typedef struct {
180 WORD CoverageFormat;
181 WORD GlyphCount;
182 WORD GlyphArray[1];
183 } GSUB_CoverageFormat1;
185 typedef struct {
186 WORD Start;
187 WORD End;
188 WORD StartCoverageIndex;
189 } GSUB_RangeRecord;
191 typedef struct {
192 WORD CoverageFormat;
193 WORD RangeCount;
194 GSUB_RangeRecord RangeRecord[1];
195 } GSUB_CoverageFormat2;
197 typedef struct {
198 WORD SubstFormat; /* = 1 */
199 WORD Coverage;
200 WORD DeltaGlyphID;
201 } GSUB_SingleSubstFormat1;
203 typedef struct {
204 WORD SubstFormat; /* = 2 */
205 WORD Coverage;
206 WORD GlyphCount;
207 WORD Substitute[1];
208 }GSUB_SingleSubstFormat2;
210 typedef struct {
211 WORD SubstFormat; /* = 1 */
212 WORD Coverage;
213 WORD SequenceCount;
214 WORD Sequence[1];
215 }GSUB_MultipleSubstFormat1;
217 typedef struct {
218 WORD GlyphCount;
219 WORD Substitute[1];
220 }GSUB_Sequence;
222 typedef struct {
223 WORD SubstFormat; /* = 1 */
224 WORD Coverage;
225 WORD LigSetCount;
226 WORD LigatureSet[1];
227 }GSUB_LigatureSubstFormat1;
229 typedef struct {
230 WORD LigatureCount;
231 WORD Ligature[1];
232 }GSUB_LigatureSet;
234 typedef struct{
235 WORD LigGlyph;
236 WORD CompCount;
237 WORD Component[1];
238 }GSUB_Ligature;
240 typedef struct{
241 WORD SequenceIndex;
242 WORD LookupListIndex;
244 }GSUB_SubstLookupRecord;
246 typedef struct{
247 WORD SubstFormat; /* = 1 */
248 WORD Coverage;
249 WORD ChainSubRuleSetCount;
250 WORD ChainSubRuleSet[1];
251 }GSUB_ChainContextSubstFormat1;
253 typedef struct {
254 WORD SubstFormat; /* = 3 */
255 WORD BacktrackGlyphCount;
256 WORD Coverage[1];
257 }GSUB_ChainContextSubstFormat3_1;
259 typedef struct{
260 WORD InputGlyphCount;
261 WORD Coverage[1];
262 }GSUB_ChainContextSubstFormat3_2;
264 typedef struct{
265 WORD LookaheadGlyphCount;
266 WORD Coverage[1];
267 }GSUB_ChainContextSubstFormat3_3;
269 typedef struct{
270 WORD SubstCount;
271 GSUB_SubstLookupRecord SubstLookupRecord[1];
272 }GSUB_ChainContextSubstFormat3_4;
274 typedef struct {
275 WORD SubstFormat; /* = 1 */
276 WORD Coverage;
277 WORD AlternateSetCount;
278 WORD AlternateSet[1];
279 } GSUB_AlternateSubstFormat1;
281 typedef struct{
282 WORD GlyphCount;
283 WORD Alternate[1];
284 } GSUB_AlternateSet;
286 /* These are all structures needed for the GDEF table */
287 #define GDEF_TAG MS_MAKE_TAG('G', 'D', 'E', 'F')
289 enum {BaseGlyph=1, LigatureGlyph, MarkGlyph, ComponentGlyph};
291 typedef struct {
292 DWORD Version;
293 WORD GlyphClassDef;
294 WORD AttachList;
295 WORD LigCaretList;
296 WORD MarkAttachClassDef;
297 } GDEF_Header;
299 typedef struct {
300 WORD ClassFormat;
301 WORD StartGlyph;
302 WORD GlyphCount;
303 WORD ClassValueArray[1];
304 } GDEF_ClassDefFormat1;
306 typedef struct {
307 WORD Start;
308 WORD End;
309 WORD Class;
310 } GDEF_ClassRangeRecord;
312 typedef struct {
313 WORD ClassFormat;
314 WORD ClassRangeCount;
315 GDEF_ClassRangeRecord ClassRangeRecord[1];
316 } GDEF_ClassDefFormat2;
319 /* These are all structures needed for the cmap format 12 table */
320 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p')
322 typedef struct {
323 WORD platformID;
324 WORD encodingID;
325 DWORD offset;
326 } CMAP_EncodingRecord;
328 typedef struct {
329 WORD version;
330 WORD numTables;
331 CMAP_EncodingRecord tables[1];
332 } CMAP_Header;
334 typedef struct {
335 DWORD startCharCode;
336 DWORD endCharCode;
337 DWORD startGlyphID;
338 } CMAP_SegmentedCoverage_group;
340 typedef struct {
341 WORD format;
342 WORD reserved;
343 DWORD length;
344 DWORD language;
345 DWORD nGroups;
346 CMAP_SegmentedCoverage_group groups[1];
347 } CMAP_SegmentedCoverage;
349 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
350 static HRESULT GSUB_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags, LPCVOID* script_table);
352 typedef struct tagVowelComponents
354 WCHAR base;
355 WCHAR parts[3];
356 } VowelComponents;
358 typedef struct tagConsonantComponents
360 WCHAR parts[3];
361 WCHAR output;
362 } ConsonantComponents;
364 typedef void (*second_reorder_function)(LPWSTR pwChar, IndicSyllable *syllable,WORD* pwGlyphs, IndicSyllable* glyph_index, lexical_function lex);
366 /* the orders of joined_forms and contextual_features need to line up */
367 static const char* contextual_features[] =
369 "isol",
370 "fina",
371 "init",
372 "medi",
373 /* Syriac Alaph */
374 "med2",
375 "fin2",
376 "fin3"
379 static OPENTYPE_FEATURE_RECORD standard_features[] =
381 { MS_MAKE_TAG('c','c','m','p'), 1},
382 { MS_MAKE_TAG('l','o','c','l'), 1},
385 static OPENTYPE_FEATURE_RECORD latin_features[] =
387 { MS_MAKE_TAG('l','i','g','a'), 1},
388 { MS_MAKE_TAG('c','l','i','g'), 1},
391 static OPENTYPE_FEATURE_RECORD arabic_features[] =
393 { MS_MAKE_TAG('r','l','i','g'), 1},
394 { MS_MAKE_TAG('c','a','l','t'), 1},
395 { MS_MAKE_TAG('l','i','g','a'), 1},
396 { MS_MAKE_TAG('d','l','i','g'), 1},
397 { MS_MAKE_TAG('c','s','w','h'), 1},
398 { MS_MAKE_TAG('m','s','e','t'), 1},
401 static const char* required_arabic_features[] =
403 "fina",
404 "init",
405 "medi",
406 "rlig",
407 NULL
410 static OPENTYPE_FEATURE_RECORD hebrew_features[] =
412 { MS_MAKE_TAG('d','l','i','g'), 0},
415 static OPENTYPE_FEATURE_RECORD syriac_features[] =
417 { MS_MAKE_TAG('r','l','i','g'), 1},
418 { MS_MAKE_TAG('c','a','l','t'), 1},
419 { MS_MAKE_TAG('l','i','g','a'), 1},
420 { MS_MAKE_TAG('d','l','i','g'), 1},
423 static const char* required_syriac_features[] =
425 "fina",
426 "fin2",
427 "fin3",
428 "init",
429 "medi",
430 "med2",
431 "rlig",
432 NULL
435 static OPENTYPE_FEATURE_RECORD sinhala_features[] =
437 /* Presentation forms */
438 { MS_MAKE_TAG('b','l','w','s'), 1},
439 { MS_MAKE_TAG('a','b','v','s'), 1},
440 { MS_MAKE_TAG('p','s','t','s'), 1},
443 static OPENTYPE_FEATURE_RECORD tibetan_features[] =
445 { MS_MAKE_TAG('a','b','v','s'), 1},
446 { MS_MAKE_TAG('b','l','w','s'), 1},
449 static OPENTYPE_FEATURE_RECORD phags_features[] =
451 { MS_MAKE_TAG('a','b','v','s'), 1},
452 { MS_MAKE_TAG('b','l','w','s'), 1},
453 { MS_MAKE_TAG('c','a','l','t'), 1},
456 static OPENTYPE_FEATURE_RECORD thai_features[] =
458 { MS_MAKE_TAG('c','c','m','p'), 1},
461 static const char* required_lao_features[] =
463 "ccmp",
464 NULL
467 static const char* required_devanagari_features[] =
469 "nukt",
470 "akhn",
471 "rphf",
472 "blwf",
473 "half",
474 "vatu",
475 "pres",
476 "abvs",
477 "blws",
478 "psts",
479 "haln",
480 NULL
483 static OPENTYPE_FEATURE_RECORD devanagari_features[] =
485 { MS_MAKE_TAG('p','r','e','s'), 1},
486 { MS_MAKE_TAG('a','b','v','s'), 1},
487 { MS_MAKE_TAG('b','l','w','s'), 1},
488 { MS_MAKE_TAG('p','s','t','s'), 1},
489 { MS_MAKE_TAG('h','a','l','n'), 1},
490 { MS_MAKE_TAG('c','a','l','t'), 1},
493 static OPENTYPE_FEATURE_RECORD myanmar_features[] =
495 { MS_MAKE_TAG('l','i','g','a'), 1},
496 { MS_MAKE_TAG('c','l','i','g'), 1},
499 static const char* required_bengali_features[] =
501 "nukt",
502 "akhn",
503 "rphf",
504 "blwf",
505 "half",
506 "vatu",
507 "pstf",
508 "init",
509 "abvs",
510 "blws",
511 "psts",
512 "haln",
513 NULL
516 static const char* required_gurmukhi_features[] =
518 "nukt",
519 "akhn",
520 "rphf",
521 "blwf",
522 "half",
523 "pstf",
524 "vatu",
525 "cjct",
526 "pres",
527 "abvs",
528 "blws",
529 "psts",
530 "haln",
531 "calt",
532 NULL
535 static const char* required_oriya_features[] =
537 "nukt",
538 "akhn",
539 "rphf",
540 "blwf",
541 "pstf",
542 "cjct",
543 "pres",
544 "abvs",
545 "blws",
546 "psts",
547 "haln",
548 "calt",
549 NULL
552 static const char* required_tamil_features[] =
554 "nukt",
555 "akhn",
556 "rphf",
557 "pref",
558 "half",
559 "pres",
560 "abvs",
561 "blws",
562 "psts",
563 "haln",
564 "calt",
565 NULL
568 static const char* required_telugu_features[] =
570 "nukt",
571 "akhn",
572 "rphf",
573 "pref",
574 "half",
575 "pstf",
576 "cjct",
577 "pres",
578 "abvs",
579 "blws",
580 "psts",
581 "haln",
582 "calt",
583 NULL
586 static OPENTYPE_FEATURE_RECORD khmer_features[] =
588 { MS_MAKE_TAG('p','r','e','s'), 1},
589 { MS_MAKE_TAG('b','l','w','s'), 1},
590 { MS_MAKE_TAG('a','b','v','s'), 1},
591 { MS_MAKE_TAG('p','s','t','s'), 1},
592 { MS_MAKE_TAG('c','l','i','g'), 1},
595 static const char* required_khmer_features[] =
597 "pref",
598 "blwf",
599 "abvf",
600 "pstf",
601 "pres",
602 "blws",
603 "abvs",
604 "psts",
605 "clig",
606 NULL
609 static OPENTYPE_FEATURE_RECORD no_features[] =
610 { };
612 static OPENTYPE_FEATURE_RECORD ethiopic_features[] =
614 { MS_MAKE_TAG('c','c','m','p'), 1},
615 { MS_MAKE_TAG('l','o','c','l'), 1},
616 { MS_MAKE_TAG('c','a','l','t'), 1},
617 { MS_MAKE_TAG('l','i','g','a'), 1},
620 static OPENTYPE_FEATURE_RECORD mongolian_features[] =
622 { MS_MAKE_TAG('c','c','m','p'), 1},
623 { MS_MAKE_TAG('l','o','c','l'), 1},
624 { MS_MAKE_TAG('c','a','l','t'), 1},
625 { MS_MAKE_TAG('r','l','i','g'), 1},
628 typedef struct ScriptShapeDataTag {
629 TEXTRANGE_PROPERTIES defaultTextRange;
630 const char** requiredFeatures;
631 CHAR otTag[5];
632 CHAR newOtTag[5];
633 ContextualShapingProc contextProc;
634 ShapeCharGlyphPropProc charGlyphPropProc;
635 } ScriptShapeData;
637 /* in order of scripts */
638 static const ScriptShapeData ShapingData[] =
640 {{ standard_features, 2}, NULL, "", "", NULL, NULL},
641 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
642 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
643 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
644 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
645 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
646 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
647 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
648 {{ hebrew_features, 1}, NULL, "hebr", "", NULL, NULL},
649 {{ syriac_features, 4}, required_syriac_features, "syrc", "", ContextualShape_Syriac, ShapeCharGlyphProp_None},
650 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
651 {{ NULL, 0}, NULL, "thaa", "", NULL, ShapeCharGlyphProp_None},
652 {{ standard_features, 2}, NULL, "grek", "", NULL, NULL},
653 {{ standard_features, 2}, NULL, "cyrl", "", NULL, NULL},
654 {{ standard_features, 2}, NULL, "armn", "", NULL, NULL},
655 {{ standard_features, 2}, NULL, "geor", "", NULL, NULL},
656 {{ sinhala_features, 3}, NULL, "sinh", "", ContextualShape_Sinhala, ShapeCharGlyphProp_Sinhala},
657 {{ tibetan_features, 2}, NULL, "tibt", "", NULL, ShapeCharGlyphProp_Tibet},
658 {{ tibetan_features, 2}, NULL, "tibt", "", NULL, ShapeCharGlyphProp_Tibet},
659 {{ phags_features, 3}, NULL, "phag", "", ContextualShape_Phags_pa, ShapeCharGlyphProp_Thai},
660 {{ thai_features, 1}, NULL, "thai", "", NULL, ShapeCharGlyphProp_Thai},
661 {{ thai_features, 1}, NULL, "thai", "", NULL, ShapeCharGlyphProp_Thai},
662 {{ thai_features, 1}, required_lao_features, "lao", "", NULL, ShapeCharGlyphProp_Thai},
663 {{ thai_features, 1}, required_lao_features, "lao", "", NULL, ShapeCharGlyphProp_Thai},
664 {{ devanagari_features, 6}, required_devanagari_features, "deva", "dev2", ContextualShape_Devanagari, ShapeCharGlyphProp_Devanagari},
665 {{ devanagari_features, 6}, required_devanagari_features, "deva", "dev2", ContextualShape_Devanagari, ShapeCharGlyphProp_Devanagari},
666 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
667 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
668 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
669 {{ devanagari_features, 6}, required_gurmukhi_features, "guru", "gur2", ContextualShape_Gurmukhi, ShapeCharGlyphProp_Gurmukhi},
670 {{ devanagari_features, 6}, required_gurmukhi_features, "guru", "gur2", ContextualShape_Gurmukhi, ShapeCharGlyphProp_Gurmukhi},
671 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
672 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
673 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
674 {{ devanagari_features, 6}, required_oriya_features, "orya", "ory2", ContextualShape_Oriya, ShapeCharGlyphProp_Oriya},
675 {{ devanagari_features, 6}, required_oriya_features, "orya", "ory2", ContextualShape_Oriya, ShapeCharGlyphProp_Oriya},
676 {{ devanagari_features, 6}, required_tamil_features, "taml", "tam2", ContextualShape_Tamil, ShapeCharGlyphProp_Tamil},
677 {{ devanagari_features, 6}, required_tamil_features, "taml", "tam2", ContextualShape_Tamil, ShapeCharGlyphProp_Tamil},
678 {{ devanagari_features, 6}, required_telugu_features, "telu", "tel2", ContextualShape_Telugu, ShapeCharGlyphProp_Telugu},
679 {{ devanagari_features, 6}, required_telugu_features, "telu", "tel2", ContextualShape_Telugu, ShapeCharGlyphProp_Telugu},
680 {{ devanagari_features, 6}, required_telugu_features, "knda", "knd2", ContextualShape_Kannada, ShapeCharGlyphProp_Kannada},
681 {{ devanagari_features, 6}, required_telugu_features, "knda", "knd2", ContextualShape_Kannada, ShapeCharGlyphProp_Kannada},
682 {{ devanagari_features, 6}, required_telugu_features, "mlym", "mlm2", ContextualShape_Malayalam, ShapeCharGlyphProp_Malayalam},
683 {{ devanagari_features, 6}, required_telugu_features, "mlym", "mlm2", ContextualShape_Malayalam, ShapeCharGlyphProp_Malayalam},
684 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
685 {{ latin_features, 2}, NULL, "latn" , "", NULL, NULL},
686 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
687 {{ myanmar_features, 2}, NULL, "mymr", "", NULL, NULL},
688 {{ myanmar_features, 2}, NULL, "mymr", "", NULL, NULL},
689 {{ standard_features, 2}, NULL, "tale", "", NULL, NULL},
690 {{ standard_features, 2}, NULL, "talu", "", NULL, NULL},
691 {{ standard_features, 2}, NULL, "talu", "", NULL, NULL},
692 {{ khmer_features, 5}, required_khmer_features, "khmr", "", ContextualShape_Khmer, ShapeCharGlyphProp_Khmer},
693 {{ khmer_features, 5}, required_khmer_features, "khmr", "", ContextualShape_Khmer, ShapeCharGlyphProp_Khmer},
694 {{ no_features, 0}, NULL, "hani", "", NULL, NULL},
695 {{ no_features, 0}, NULL, "hani", "", NULL, NULL},
696 {{ no_features, 0}, NULL, "bopo", "", NULL, NULL},
697 {{ no_features, 0}, NULL, "kana", "", NULL, NULL},
698 {{ no_features, 0}, NULL, "hang", "", NULL, NULL},
699 {{ no_features, 0}, NULL, "yi ", "", NULL, NULL},
700 {{ ethiopic_features, 4}, NULL, "ethi", "", NULL, NULL},
701 {{ ethiopic_features, 4}, NULL, "ethi", "", NULL, NULL},
702 {{ mongolian_features, 4}, NULL, "mong", "", ContextualShape_Mongolian, NULL},
703 {{ mongolian_features, 4}, NULL, "mong", "", ContextualShape_Mongolian, NULL},
704 {{ no_features, 0}, NULL, "tfng", "", NULL, NULL},
705 {{ no_features, 0}, NULL, "nko ", "", NULL, NULL},
706 {{ no_features, 0}, NULL, "vai ", "", NULL, NULL},
707 {{ no_features, 0}, NULL, "vai ", "", NULL, NULL},
708 {{ no_features, 0}, NULL, "cher", "", NULL, NULL},
709 {{ no_features, 0}, NULL, "cans", "", NULL, NULL},
710 {{ no_features, 0}, NULL, "ogam", "", NULL, NULL},
711 {{ no_features, 0}, NULL, "runr", "", NULL, NULL},
712 {{ no_features, 0}, NULL, "brai", "", NULL, NULL},
713 {{ no_features, 0}, NULL, "", "", NULL, NULL},
714 {{ no_features, 0}, NULL, "", "", NULL, NULL},
715 {{ no_features, 0}, NULL, "dsrt", "", NULL, NULL},
716 {{ no_features, 0}, NULL, "osma", "", NULL, NULL},
717 {{ no_features, 0}, NULL, "osma", "", NULL, NULL},
718 {{ no_features, 0}, NULL, "math", "", NULL, NULL},
719 {{ hebrew_features, 1}, NULL, "hebr", "", NULL, NULL},
720 {{ latin_features, 2}, NULL, "latn" , "", NULL, NULL},
721 {{ thai_features, 1}, NULL, "thai", "", NULL, ShapeCharGlyphProp_Thai},
724 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
726 const GSUB_CoverageFormat1* cf1;
728 cf1 = table;
730 if (GET_BE_WORD(cf1->CoverageFormat) == 1)
732 int count = GET_BE_WORD(cf1->GlyphCount);
733 int i;
734 TRACE("Coverage Format 1, %i glyphs\n",count);
735 for (i = 0; i < count; i++)
736 if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
737 return i;
738 return -1;
740 else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
742 const GSUB_CoverageFormat2* cf2;
743 int i;
744 int count;
745 cf2 = (const GSUB_CoverageFormat2*)cf1;
747 count = GET_BE_WORD(cf2->RangeCount);
748 TRACE("Coverage Format 2, %i ranges\n",count);
749 for (i = 0; i < count; i++)
751 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
752 return -1;
753 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
754 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
756 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
757 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
760 return -1;
762 else
763 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
765 return -1;
768 static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
770 int i;
771 int offset;
772 const GSUB_LangSys *Lang;
774 TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
776 for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
778 offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
779 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
781 if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
782 return Lang;
784 offset = GET_BE_WORD(script->DefaultLangSys);
785 if (offset)
787 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
788 return Lang;
790 return NULL;
793 static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
795 int i;
796 const GSUB_FeatureList *feature;
797 feature = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
799 TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
800 for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
802 int index = GET_BE_WORD(lang->FeatureIndex[i]);
803 if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
805 const GSUB_Feature *feat;
806 feat = (const GSUB_Feature*)((const BYTE*)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
807 return feat;
810 return NULL;
813 static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
815 int j;
816 TRACE("Single Substitution Subtable\n");
818 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
820 int offset;
821 const GSUB_SingleSubstFormat1 *ssf1;
822 offset = GET_BE_WORD(look->SubTable[j]);
823 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
824 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
826 int offset = GET_BE_WORD(ssf1->Coverage);
827 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
828 if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
830 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
831 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
832 TRACE(" 0x%x\n",glyphs[glyph_index]);
833 return glyph_index + write_dir;
836 else
838 const GSUB_SingleSubstFormat2 *ssf2;
839 INT index;
840 INT offset;
842 ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
843 offset = GET_BE_WORD(ssf1->Coverage);
844 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
845 index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
846 TRACE(" Coverage index %i\n",index);
847 if (index != -1)
849 if (glyphs[glyph_index] == GET_BE_WORD(ssf2->Substitute[index]))
850 return GSUB_E_NOGLYPH;
852 TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]);
853 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
854 TRACE("0x%x\n",glyphs[glyph_index]);
855 return glyph_index + write_dir;
859 return GSUB_E_NOGLYPH;
862 static INT GSUB_apply_MultipleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
864 int j;
865 TRACE("Multiple Substitution Subtable\n");
867 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
869 int offset, index;
870 const GSUB_MultipleSubstFormat1 *msf1;
871 offset = GET_BE_WORD(look->SubTable[j]);
872 msf1 = (const GSUB_MultipleSubstFormat1*)((const BYTE*)look+offset);
874 offset = GET_BE_WORD(msf1->Coverage);
875 index = GSUB_is_glyph_covered((const BYTE*)msf1+offset, glyphs[glyph_index]);
876 if (index != -1)
878 const GSUB_Sequence *seq;
879 int sub_count;
880 int j;
881 offset = GET_BE_WORD(msf1->Sequence[index]);
882 seq = (const GSUB_Sequence*)((const BYTE*)msf1+offset);
883 sub_count = GET_BE_WORD(seq->GlyphCount);
884 TRACE(" Glyph 0x%x (+%i)->",glyphs[glyph_index],(sub_count-1));
886 for (j = (*glyph_count)+(sub_count-1); j > glyph_index; j--)
887 glyphs[j] =glyphs[j-(sub_count-1)];
889 for (j = 0; j < sub_count; j++)
890 if (write_dir < 0)
891 glyphs[glyph_index + (sub_count-1) - j] = GET_BE_WORD(seq->Substitute[j]);
892 else
893 glyphs[glyph_index + j] = GET_BE_WORD(seq->Substitute[j]);
895 *glyph_count = *glyph_count + (sub_count - 1);
897 if (TRACE_ON(uniscribe))
899 for (j = 0; j < sub_count; j++)
900 TRACE(" 0x%x",glyphs[glyph_index+j]);
901 TRACE("\n");
904 return glyph_index + (sub_count * write_dir);
907 return GSUB_E_NOGLYPH;
910 static INT GSUB_apply_AlternateSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
912 int j;
913 TRACE("Alternate Substitution Subtable\n");
915 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
917 int offset;
918 const GSUB_AlternateSubstFormat1 *asf1;
919 INT index;
921 offset = GET_BE_WORD(look->SubTable[j]);
922 asf1 = (const GSUB_AlternateSubstFormat1*)((const BYTE*)look+offset);
923 offset = GET_BE_WORD(asf1->Coverage);
925 index = GSUB_is_glyph_covered((const BYTE*)asf1+offset, glyphs[glyph_index]);
926 if (index != -1)
928 const GSUB_AlternateSet *as;
929 offset = GET_BE_WORD(asf1->AlternateSet[index]);
930 as = (const GSUB_AlternateSet*)((const BYTE*)asf1+offset);
931 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as->GlyphCount));
932 if (glyphs[glyph_index] == GET_BE_WORD(as->Alternate[0]))
933 return GSUB_E_NOGLYPH;
935 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
936 glyphs[glyph_index] = GET_BE_WORD(as->Alternate[0]);
937 TRACE(" 0x%x\n",glyphs[glyph_index]);
938 return glyph_index + write_dir;
941 return GSUB_E_NOGLYPH;
944 static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
946 int j;
948 TRACE("Ligature Substitution Subtable\n");
949 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
951 const GSUB_LigatureSubstFormat1 *lsf1;
952 int offset,index;
954 offset = GET_BE_WORD(look->SubTable[j]);
955 lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
956 offset = GET_BE_WORD(lsf1->Coverage);
957 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
958 TRACE(" Coverage index %i\n",index);
959 if (index != -1)
961 const GSUB_LigatureSet *ls;
962 int k, count;
964 offset = GET_BE_WORD(lsf1->LigatureSet[index]);
965 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
966 count = GET_BE_WORD(ls->LigatureCount);
967 TRACE(" LigatureSet has %i members\n",count);
968 for (k = 0; k < count; k++)
970 const GSUB_Ligature *lig;
971 int CompCount,l,CompIndex;
973 offset = GET_BE_WORD(ls->Ligature[k]);
974 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
975 CompCount = GET_BE_WORD(lig->CompCount) - 1;
976 CompIndex = glyph_index+write_dir;
977 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
979 int CompGlyph;
980 CompGlyph = GET_BE_WORD(lig->Component[l]);
981 if (CompGlyph != glyphs[CompIndex])
982 break;
983 CompIndex += write_dir;
985 if (l == CompCount)
987 int replaceIdx = glyph_index;
988 if (write_dir < 0)
989 replaceIdx = glyph_index - CompCount;
991 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
992 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
993 TRACE("0x%x\n",glyphs[replaceIdx]);
994 if (CompCount > 0)
996 int j;
997 for (j = replaceIdx + 1; j < *glyph_count; j++)
998 glyphs[j] =glyphs[j+CompCount];
999 *glyph_count = *glyph_count - CompCount;
1001 return replaceIdx + write_dir;
1006 return GSUB_E_NOGLYPH;
1009 static INT GSUB_apply_ChainContextSubst(const GSUB_LookupList* lookup, const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1011 int j;
1012 BOOL done = FALSE;
1014 TRACE("Chaining Contextual Substitution Subtable\n");
1015 for (j = 0; j < GET_BE_WORD(look->SubTableCount) && !done; j++)
1017 const GSUB_ChainContextSubstFormat1 *ccsf1;
1018 int offset;
1019 int dirLookahead = write_dir;
1020 int dirBacktrack = -1 * write_dir;
1022 offset = GET_BE_WORD(look->SubTable[j]);
1023 ccsf1 = (const GSUB_ChainContextSubstFormat1*)((const BYTE*)look+offset);
1024 if (GET_BE_WORD(ccsf1->SubstFormat) == 1)
1026 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
1027 continue;
1029 else if (GET_BE_WORD(ccsf1->SubstFormat) == 2)
1031 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
1032 continue;
1034 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
1036 int k;
1037 int indexGlyphs;
1038 const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
1039 const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
1040 const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
1041 const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
1042 int newIndex = glyph_index;
1044 ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
1046 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
1048 for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
1050 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
1051 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
1052 break;
1054 if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
1055 continue;
1056 TRACE("Matched Backtrack\n");
1058 ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)(((LPBYTE)ccsf1)+sizeof(GSUB_ChainContextSubstFormat3_1) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)-1)));
1060 indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
1061 for (k = 0; k < indexGlyphs; k++)
1063 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
1064 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
1065 break;
1067 if (k != indexGlyphs)
1068 continue;
1069 TRACE("Matched IndexGlyphs\n");
1071 ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)(((LPBYTE)ccsf3_2)+sizeof(GSUB_ChainContextSubstFormat3_2) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_2->InputGlyphCount)-1)));
1073 for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
1075 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
1076 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
1077 break;
1079 if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
1080 continue;
1081 TRACE("Matched LookAhead\n");
1083 ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)(((LPBYTE)ccsf3_3)+sizeof(GSUB_ChainContextSubstFormat3_3) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)-1)));
1085 if (GET_BE_WORD(ccsf3_4->SubstCount))
1087 for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
1089 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
1090 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
1092 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
1093 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
1094 if (newIndex == -1)
1096 ERR("Chain failed to generate a glyph\n");
1097 continue;
1100 return newIndex;
1102 else return GSUB_E_NOGLYPH;
1105 return -1;
1108 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1110 int offset;
1111 const GSUB_LookupTable *look;
1113 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
1114 look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
1115 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
1116 switch(GET_BE_WORD(look->LookupType))
1118 case 1:
1119 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1120 case 2:
1121 return GSUB_apply_MultipleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1122 case 3:
1123 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1124 case 4:
1125 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1126 case 6:
1127 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
1128 default:
1129 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
1131 return GSUB_E_NOGLYPH;
1134 static INT GSUB_apply_feature_all_lookups(const GSUB_Header * header, const GSUB_Feature* feature, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1136 int i;
1137 int out_index = GSUB_E_NOGLYPH;
1138 const GSUB_LookupList *lookup;
1140 lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1142 TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
1143 for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
1145 out_index = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[i]), glyphs, glyph_index, write_dir, glyph_count);
1146 if (out_index != GSUB_E_NOGLYPH)
1147 break;
1149 if (out_index == GSUB_E_NOGLYPH)
1150 TRACE("lookups found no glyphs\n");
1151 else
1153 int out2;
1154 out2 = GSUB_apply_feature_all_lookups(header, feature, glyphs, glyph_index, write_dir, glyph_count);
1155 if (out2!=GSUB_E_NOGLYPH)
1156 out_index = out2;
1158 return out_index;
1161 static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, BOOL tryNew)
1163 UINT charset;
1165 if (psc->userScript != 0)
1167 if (tryNew && ShapingData[psa->eScript].newOtTag[0] != 0 && strncmp((char*)&psc->userScript,ShapingData[psa->eScript].otTag,4)==0)
1168 return ShapingData[psa->eScript].newOtTag;
1169 else
1170 return (char*)&psc->userScript;
1173 if (tryNew && ShapingData[psa->eScript].newOtTag[0] != 0)
1174 return ShapingData[psa->eScript].newOtTag;
1176 if (ShapingData[psa->eScript].otTag[0] != 0)
1177 return ShapingData[psa->eScript].otTag;
1180 * fall back to the font charset
1182 charset = GetTextCharsetInfo(hdc, NULL, 0x0);
1183 switch (charset)
1185 case ANSI_CHARSET: return "latn";
1186 case BALTIC_CHARSET: return "latn"; /* ?? */
1187 case CHINESEBIG5_CHARSET: return "hani";
1188 case EASTEUROPE_CHARSET: return "latn"; /* ?? */
1189 case GB2312_CHARSET: return "hani";
1190 case GREEK_CHARSET: return "grek";
1191 case HANGUL_CHARSET: return "hang";
1192 case RUSSIAN_CHARSET: return "cyrl";
1193 case SHIFTJIS_CHARSET: return "kana";
1194 case TURKISH_CHARSET: return "latn"; /* ?? */
1195 case VIETNAMESE_CHARSET: return "latn";
1196 case JOHAB_CHARSET: return "latn"; /* ?? */
1197 case ARABIC_CHARSET: return "arab";
1198 case HEBREW_CHARSET: return "hebr";
1199 case THAI_CHARSET: return "thai";
1200 default: return "latn";
1204 static LPCVOID load_GSUB_feature(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, const char* feat)
1206 const GSUB_Feature *feature;
1207 const char* script;
1208 int i;
1210 script = get_opentype_script(hdc,psa,psc,FALSE);
1212 for (i = 0; i < psc->feature_count; i++)
1214 if (strncmp(psc->features[i].tag,feat,4)==0 && strncmp(psc->features[i].script,script,4)==0)
1215 return psc->features[i].feature;
1218 feature = NULL;
1220 if (psc->GSUB_Table)
1222 const GSUB_Script *script = NULL;
1223 const GSUB_LangSys *language;
1224 int attempt = 2;
1225 OPENTYPE_TAG scriptTags;
1226 int cTags;
1227 HRESULT hr;
1231 const char* tag;
1233 tag = get_opentype_script(hdc,psa,psc,(attempt==2));
1234 hr = GSUB_GetFontScriptTags(psc, MS_MAKE_TAG(tag[0],tag[1],tag[2],tag[3]), 1, &scriptTags, &cTags, (LPCVOID*)&script);
1236 attempt--;
1237 if (SUCCEEDED(hr) && cTags && script)
1239 if (psc->userLang != 0)
1240 language = GSUB_get_lang_table(script,(char*)&psc->userLang);
1241 else
1242 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
1243 if (language)
1244 feature = GSUB_get_feature(psc->GSUB_Table, language, feat);
1246 } while(attempt && !feature);
1248 /* try in the default (latin) table */
1249 if (!feature)
1251 hr = GSUB_GetFontScriptTags(psc, MS_MAKE_TAG('l','a','t','n'), 1, &scriptTags, &cTags, (LPCVOID*)&script);
1252 if (SUCCEEDED(hr) && script)
1254 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
1255 if (language)
1256 feature = GSUB_get_feature(psc->GSUB_Table, language, feat);
1261 TRACE("Feature %s located at %p\n",debugstr_an(feat,4),feature);
1263 psc->feature_count++;
1265 if (psc->features)
1266 psc->features = HeapReAlloc(GetProcessHeap(), 0, psc->features, psc->feature_count * sizeof(LoadedFeature));
1267 else
1268 psc->features = HeapAlloc(GetProcessHeap(), 0, psc->feature_count * sizeof(LoadedFeature));
1270 lstrcpynA(psc->features[psc->feature_count - 1].tag, feat, 5);
1271 lstrcpynA(psc->features[psc->feature_count - 1].script, script, 5);
1272 psc->features[psc->feature_count - 1].feature = feature;
1273 return feature;
1276 static INT apply_GSUB_feature_to_glyph(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, WORD *glyphs, INT index, INT write_dir, INT* glyph_count, const char* feat)
1278 const GSUB_Feature *feature;
1280 feature = load_GSUB_feature(hdc, psa, psc, feat);
1281 if (!feature)
1282 return GSUB_E_NOFEATURE;
1284 TRACE("applying feature %s\n",feat);
1285 return GSUB_apply_feature_all_lookups(psc->GSUB_Table, feature, glyphs, index, write_dir, glyph_count);
1288 static VOID *load_gsub_table(HDC hdc)
1290 VOID* GSUB_Table = NULL;
1291 int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
1292 if (length != GDI_ERROR)
1294 GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
1295 GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
1296 TRACE("Loaded GSUB table of %i bytes\n",length);
1298 return GSUB_Table;
1301 INT SHAPE_does_GSUB_feature_apply_to_chars(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, const WCHAR *chars, INT write_dir, INT count, const char* feature)
1303 WORD *glyphs;
1304 INT glyph_count = count;
1305 INT rc;
1307 glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD)*(count*2));
1308 GetGlyphIndicesW(hdc, chars, count, glyphs, 0);
1309 rc = apply_GSUB_feature_to_glyph(hdc, psa, psc, glyphs, 0, write_dir, &glyph_count, feature);
1310 if (rc > GSUB_E_NOGLYPH)
1311 rc = count - glyph_count;
1312 else
1313 rc = 0;
1315 HeapFree(GetProcessHeap(),0,glyphs);
1316 return rc;
1319 static WORD GDEF_get_glyph_class(const GDEF_Header *header, WORD glyph)
1321 int offset;
1322 WORD class = 0;
1323 const GDEF_ClassDefFormat1 *cf1;
1325 if (!header)
1326 return 0;
1328 offset = GET_BE_WORD(header->GlyphClassDef);
1329 if (!offset)
1330 return 0;
1332 cf1 = (GDEF_ClassDefFormat1*)(((BYTE*)header)+offset);
1333 if (GET_BE_WORD(cf1->ClassFormat) == 1)
1335 if (glyph >= GET_BE_WORD(cf1->StartGlyph))
1337 int index = glyph - GET_BE_WORD(cf1->StartGlyph);
1338 if (index < GET_BE_WORD(cf1->GlyphCount))
1339 class = GET_BE_WORD(cf1->ClassValueArray[index]);
1342 else if (GET_BE_WORD(cf1->ClassFormat) == 2)
1344 const GDEF_ClassDefFormat2 *cf2 = (GDEF_ClassDefFormat2*)cf1;
1345 int i, top;
1346 top = GET_BE_WORD(cf2->ClassRangeCount);
1347 for (i = 0; i < top; i++)
1349 if (glyph >= GET_BE_WORD(cf2->ClassRangeRecord[i].Start) &&
1350 glyph <= GET_BE_WORD(cf2->ClassRangeRecord[i].End))
1352 class = GET_BE_WORD(cf2->ClassRangeRecord[i].Class);
1353 break;
1357 else
1358 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1->ClassFormat));
1360 return class;
1363 static VOID *load_gdef_table(HDC hdc)
1365 VOID* GDEF_Table = NULL;
1366 int length = GetFontData(hdc, GDEF_TAG , 0, NULL, 0);
1367 if (length != GDI_ERROR)
1369 GDEF_Table = HeapAlloc(GetProcessHeap(),0,length);
1370 GetFontData(hdc, GDEF_TAG , 0, GDEF_Table, length);
1371 TRACE("Loaded GDEF table of %i bytes\n",length);
1373 return GDEF_Table;
1376 static void GDEF_UpdateGlyphProps(HDC hdc, ScriptCache *psc, const WORD *pwGlyphs, const WORD cGlyphs, WORD* pwLogClust, const WORD cChars, SCRIPT_GLYPHPROP *pGlyphProp)
1378 int i;
1380 if (!psc->GDEF_Table)
1381 psc->GDEF_Table = load_gdef_table(hdc);
1383 for (i = 0; i < cGlyphs; i++)
1385 WORD class;
1386 int char_count = 0;
1387 int k;
1389 for (k = 0; k < cChars; k++)
1390 if (pwLogClust[k] == i)
1391 char_count++;
1393 class = GDEF_get_glyph_class(psc->GDEF_Table, pwGlyphs[i]);
1395 switch (class)
1397 case 0:
1398 case BaseGlyph:
1399 pGlyphProp[i].sva.fClusterStart = 1;
1400 pGlyphProp[i].sva.fDiacritic = 0;
1401 pGlyphProp[i].sva.fZeroWidth = 0;
1402 break;
1403 case LigatureGlyph:
1404 pGlyphProp[i].sva.fClusterStart = 1;
1405 pGlyphProp[i].sva.fDiacritic = 0;
1406 pGlyphProp[i].sva.fZeroWidth = 0;
1407 break;
1408 case MarkGlyph:
1409 pGlyphProp[i].sva.fClusterStart = 0;
1410 pGlyphProp[i].sva.fDiacritic = 1;
1411 pGlyphProp[i].sva.fZeroWidth = 1;
1412 break;
1413 case ComponentGlyph:
1414 pGlyphProp[i].sva.fClusterStart = 0;
1415 pGlyphProp[i].sva.fDiacritic = 0;
1416 pGlyphProp[i].sva.fZeroWidth = 0;
1417 break;
1418 default:
1419 ERR("Unknown glyph class %i\n",class);
1420 pGlyphProp[i].sva.fClusterStart = 1;
1421 pGlyphProp[i].sva.fDiacritic = 0;
1422 pGlyphProp[i].sva.fZeroWidth = 0;
1425 if (char_count == 0)
1426 pGlyphProp[i].sva.fClusterStart = 0;
1430 static void UpdateClustersFromGlyphProp(const int cGlyphs, const int cChars, WORD* pwLogClust, SCRIPT_GLYPHPROP *pGlyphProp)
1432 int i;
1434 for (i = 0; i < cGlyphs; i++)
1436 if (!pGlyphProp[i].sva.fClusterStart)
1438 int j;
1439 for (j = 0; j < cChars; j++)
1441 if (pwLogClust[j] == i)
1443 int k = j;
1444 while (!pGlyphProp[pwLogClust[k]].sva.fClusterStart && k >= 0 && k <cChars)
1445 k-=1;
1446 if (pGlyphProp[pwLogClust[k]].sva.fClusterStart)
1447 pwLogClust[j] = pwLogClust[k];
1454 static void UpdateClusters(int nextIndex, int changeCount, int write_dir, int chars, WORD* pwLogClust )
1456 if (changeCount == 0)
1457 return;
1458 else
1460 int i;
1461 int target_glyph = nextIndex - write_dir;
1462 int seeking_glyph;
1463 int target_index = -1;
1464 int replacing_glyph = -1;
1465 int changed = 0;
1466 int top_logclust = 0;
1468 if (changeCount > 0)
1470 if (write_dir > 0)
1471 target_glyph = nextIndex - changeCount;
1472 else
1473 target_glyph = nextIndex + (changeCount + 1);
1476 seeking_glyph = target_glyph;
1477 for (i = 0; i < chars; i++)
1478 if (pwLogClust[i] > top_logclust)
1479 top_logclust = pwLogClust[i];
1481 do {
1482 if (write_dir > 0)
1483 for (i = 0; i < chars; i++)
1485 if (pwLogClust[i] == seeking_glyph)
1487 target_index = i;
1488 break;
1491 else
1492 for (i = chars - 1; i >= 0; i--)
1494 if (pwLogClust[i] == seeking_glyph)
1496 target_index = i;
1497 break;
1500 if (target_index == -1)
1501 seeking_glyph ++;
1503 while (target_index == -1 && seeking_glyph <= top_logclust);
1505 if (target_index == -1)
1507 ERR("Unable to find target glyph\n");
1508 return;
1511 if (changeCount < 0)
1513 /* merge glyphs */
1514 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1516 if (pwLogClust[i] == target_glyph)
1517 continue;
1518 if(pwLogClust[i] == replacing_glyph)
1519 pwLogClust[i] = target_glyph;
1520 else
1522 changed--;
1523 if (changed >= changeCount)
1525 replacing_glyph = pwLogClust[i];
1526 pwLogClust[i] = target_glyph;
1528 else
1529 break;
1533 /* renumber trailing indexes*/
1534 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1536 if (pwLogClust[i] != target_glyph)
1537 pwLogClust[i] += changeCount;
1540 else
1542 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1543 pwLogClust[i] += changeCount;
1548 static int apply_GSUB_feature(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, WORD *pwOutGlyphs, int write_dir, INT* pcGlyphs, INT cChars, const char* feat, WORD *pwLogClust )
1550 if (psc->GSUB_Table)
1552 const GSUB_Feature *feature;
1553 const GSUB_LookupList *lookup;
1554 const GSUB_Header *header = psc->GSUB_Table;
1555 int lookup_index, lookup_count;
1557 feature = load_GSUB_feature(hdc, psa, psc, feat);
1558 if (!feature)
1559 return GSUB_E_NOFEATURE;
1561 TRACE("applying feature %s\n",debugstr_an(feat,4));
1562 lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1563 lookup_count = GET_BE_WORD(feature->LookupCount);
1564 TRACE("%i lookups\n", lookup_count);
1565 for (lookup_index = 0; lookup_index < lookup_count; lookup_index++)
1567 int i;
1569 if (write_dir > 0)
1570 i = 0;
1571 else
1572 i = *pcGlyphs-1;
1573 TRACE("applying lookup (%i/%i)\n",lookup_index,lookup_count);
1574 while(i < *pcGlyphs && i >= 0)
1576 INT nextIndex;
1577 INT prevCount = *pcGlyphs;
1579 nextIndex = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[lookup_index]), pwOutGlyphs, i, write_dir, pcGlyphs);
1580 if (*pcGlyphs != prevCount)
1582 UpdateClusters(nextIndex, *pcGlyphs - prevCount, write_dir, cChars, pwLogClust);
1583 i = nextIndex;
1585 else
1586 i+=write_dir;
1589 return *pcGlyphs;
1591 return GSUB_E_NOFEATURE;
1594 static inline BOOL get_GSUB_Indic2(SCRIPT_ANALYSIS *psa, ScriptCache *psc)
1596 OPENTYPE_TAG tag;
1597 HRESULT hr;
1598 int count = 0;
1600 hr = GSUB_GetFontScriptTags(psc, MS_MAKE_TAG(ShapingData[psa->eScript].newOtTag[0],ShapingData[psa->eScript].newOtTag[1],ShapingData[psa->eScript].newOtTag[2],ShapingData[psa->eScript].newOtTag[3]), 1, &tag, &count, NULL);
1602 return(SUCCEEDED(hr));
1605 static WCHAR neighbour_char(int i, int delta, const WCHAR* chars, INT cchLen)
1607 if (i + delta < 0)
1608 return 0;
1609 if ( i+ delta >= cchLen)
1610 return 0;
1612 i += delta;
1614 return chars[i];
1617 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
1619 if (i + delta < 0)
1621 if (psa->fLinkBefore)
1622 return jtR;
1623 else
1624 return jtU;
1626 if ( i+ delta >= cchLen)
1628 if (psa->fLinkAfter)
1629 return jtL;
1630 else
1631 return jtU;
1634 i += delta;
1636 if (context_type[i] == jtT)
1637 return neighbour_joining_type(i,delta,context_type,cchLen,psa);
1638 else
1639 return context_type[i];
1642 static inline BOOL right_join_causing(CHAR joining_type)
1644 return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
1647 static inline BOOL left_join_causing(CHAR joining_type)
1649 return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
1652 static inline BOOL word_break_causing(WCHAR chr)
1654 /* we are working within a string of characters already guareented to
1655 be within one script, Syriac, so we do not worry about any character
1656 other than the space character outside of that range */
1657 return (chr == 0 || chr == 0x20 );
1661 * ContextualShape_Arabic
1663 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1665 CHAR *context_type;
1666 INT *context_shape;
1667 INT dirR, dirL;
1668 int i;
1670 if (*pcGlyphs != cChars)
1672 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1673 return;
1676 if (!psa->fLogicalOrder && psa->fRTL)
1678 dirR = 1;
1679 dirL = -1;
1681 else
1683 dirR = -1;
1684 dirL = 1;
1687 if (!psc->GSUB_Table)
1688 psc->GSUB_Table = load_gsub_table(hdc);
1690 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1691 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1693 for (i = 0; i < cChars; i++)
1694 context_type[i] = get_table_entry( wine_shaping_table, pwcChars[i] );
1696 for (i = 0; i < cChars; i++)
1698 if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1699 context_shape[i] = Xr;
1700 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1701 context_shape[i] = Xl;
1702 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)) && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1703 context_shape[i] = Xm;
1704 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1705 context_shape[i] = Xr;
1706 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1707 context_shape[i] = Xl;
1708 else
1709 context_shape[i] = Xn;
1712 /* Contextual Shaping */
1713 i = 0;
1714 while(i < *pcGlyphs)
1716 BOOL shaped = FALSE;
1718 if (psc->GSUB_Table)
1720 INT nextIndex;
1721 INT prevCount = *pcGlyphs;
1722 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1723 if (nextIndex > GSUB_E_NOGLYPH)
1725 i = nextIndex;
1726 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1728 shaped = (nextIndex > GSUB_E_NOGLYPH);
1731 if (!shaped)
1733 if (context_shape[i] == Xn)
1735 WORD newGlyph = pwOutGlyphs[i];
1736 if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
1738 /* fall back to presentation form B */
1739 WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
1740 if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
1741 pwOutGlyphs[i] = newGlyph;
1744 i++;
1748 HeapFree(GetProcessHeap(),0,context_shape);
1749 HeapFree(GetProcessHeap(),0,context_type);
1753 * ContextualShape_Syriac
1756 #define ALAPH 0x710
1757 #define DALATH 0x715
1758 #define RISH 0x72A
1760 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1762 CHAR *context_type;
1763 INT *context_shape;
1764 INT dirR, dirL;
1765 int i;
1767 if (*pcGlyphs != cChars)
1769 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1770 return;
1773 if (!psa->fLogicalOrder && psa->fRTL)
1775 dirR = 1;
1776 dirL = -1;
1778 else
1780 dirR = -1;
1781 dirL = 1;
1784 if (!psc->GSUB_Table)
1785 psc->GSUB_Table = load_gsub_table(hdc);
1787 if (!psc->GSUB_Table)
1788 return;
1790 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1791 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1793 for (i = 0; i < cChars; i++)
1794 context_type[i] = get_table_entry( wine_shaping_table, pwcChars[i] );
1796 for (i = 0; i < cChars; i++)
1798 if (pwcChars[i] == ALAPH)
1800 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1802 if (left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1803 context_shape[i] = Afj;
1804 else if ( rchar != DALATH && rchar != RISH &&
1805 !left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) &&
1806 word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1807 context_shape[i] = Afn;
1808 else if ( (rchar == DALATH || rchar == RISH) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1809 context_shape[i] = Afx;
1810 else
1811 context_shape[i] = Xn;
1813 else if (context_type[i] == jtR &&
1814 right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1815 context_shape[i] = Xr;
1816 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1817 context_shape[i] = Xl;
1818 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)) && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1819 context_shape[i] = Xm;
1820 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1821 context_shape[i] = Xr;
1822 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1823 context_shape[i] = Xl;
1824 else
1825 context_shape[i] = Xn;
1828 /* Contextual Shaping */
1829 i = 0;
1830 while(i < *pcGlyphs)
1832 INT nextIndex;
1833 INT prevCount = *pcGlyphs;
1834 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1835 if (nextIndex > GSUB_E_NOGLYPH)
1837 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1838 i = nextIndex;
1840 else
1841 i++;
1844 HeapFree(GetProcessHeap(),0,context_shape);
1845 HeapFree(GetProcessHeap(),0,context_type);
1849 * ContextualShape_Phags_pa
1852 #define phags_pa_CANDRABINDU 0xA873
1853 #define phags_pa_START 0xA840
1854 #define phags_pa_END 0xA87F
1856 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1858 INT *context_shape;
1859 INT dirR, dirL;
1860 int i;
1862 if (*pcGlyphs != cChars)
1864 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1865 return;
1868 if (!psa->fLogicalOrder && psa->fRTL)
1870 dirR = 1;
1871 dirL = -1;
1873 else
1875 dirR = -1;
1876 dirL = 1;
1879 if (!psc->GSUB_Table)
1880 psc->GSUB_Table = load_gsub_table(hdc);
1882 if (!psc->GSUB_Table)
1883 return;
1885 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1887 for (i = 0; i < cChars; i++)
1889 if (pwcChars[i] >= phags_pa_START && pwcChars[i] <= phags_pa_END)
1891 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1892 WCHAR lchar = neighbour_char(i,dirL,pwcChars,cChars);
1893 BOOL jrchar = (rchar != phags_pa_CANDRABINDU && rchar >= phags_pa_START && rchar <= phags_pa_END);
1894 BOOL jlchar = (lchar != phags_pa_CANDRABINDU && lchar >= phags_pa_START && lchar <= phags_pa_END);
1896 if (jrchar && jlchar)
1897 context_shape[i] = Xm;
1898 else if (jrchar)
1899 context_shape[i] = Xr;
1900 else if (jlchar)
1901 context_shape[i] = Xl;
1902 else
1903 context_shape[i] = Xn;
1905 else
1906 context_shape[i] = -1;
1909 /* Contextual Shaping */
1910 i = 0;
1911 while(i < *pcGlyphs)
1913 if (context_shape[i] >= 0)
1915 INT nextIndex;
1916 INT prevCount = *pcGlyphs;
1917 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1918 if (nextIndex > GSUB_E_NOGLYPH)
1920 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1921 i = nextIndex;
1923 else
1924 i++;
1926 else
1927 i++;
1930 HeapFree(GetProcessHeap(),0,context_shape);
1933 static void ReplaceInsertChars(HDC hdc, INT cWalk, INT* pcChars, WCHAR *pwOutChars, const WCHAR *replacements)
1935 int i;
1937 /* Replace */
1938 pwOutChars[cWalk] = replacements[0];
1939 cWalk=cWalk+1;
1941 /* Insert */
1942 for (i = 1; replacements[i] != 0x0000 && i < 3; i++)
1944 int j;
1945 for (j = *pcChars; j > cWalk; j--)
1946 pwOutChars[j] = pwOutChars[j-1];
1947 *pcChars= *pcChars+1;
1948 pwOutChars[cWalk] = replacements[i];
1949 cWalk = cWalk+1;
1953 static void DecomposeVowels(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const VowelComponents vowels[], WORD* pwLogClust, INT cChars)
1955 int i;
1956 int cWalk;
1958 for (cWalk = 0; cWalk < *pcChars; cWalk++)
1960 for (i = 0; vowels[i].base != 0x0; i++)
1962 if (pwOutChars[cWalk] == vowels[i].base)
1964 int o = 0;
1965 ReplaceInsertChars(hdc, cWalk, pcChars, pwOutChars, vowels[i].parts);
1966 if (vowels[i].parts[1]) { cWalk++; o++; }
1967 if (vowels[i].parts[2]) { cWalk++; o++; }
1968 UpdateClusters(cWalk, o, 1, cChars, pwLogClust);
1969 break;
1975 static void ComposeConsonants(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const ConsonantComponents consonants[], WORD* pwLogClust)
1977 int i;
1978 int offset = 0;
1979 int cWalk;
1981 for (cWalk = 0; cWalk < *pcChars; cWalk++)
1983 for (i = 0; consonants[i].output!= 0x0; i++)
1985 int j;
1986 for (j = 0; j + cWalk < *pcChars && consonants[i].parts[j]!=0x0; j++)
1987 if (pwOutChars[cWalk+j] != consonants[i].parts[j])
1988 break;
1990 if (consonants[i].parts[j]==0x0) /* matched all */
1992 int k;
1993 j--;
1994 pwOutChars[cWalk] = consonants[i].output;
1995 for(k = cWalk+1; k < *pcChars - j; k++)
1996 pwOutChars[k] = pwOutChars[k+j];
1997 *pcChars = *pcChars - j;
1998 for (k = j ; k > 0; k--)
1999 pwLogClust[cWalk + k + offset] = pwLogClust[cWalk + offset];
2000 offset += j;
2001 for (k = cWalk + j + offset; k < *pcChars + offset; k++)
2002 pwLogClust[k]--;
2003 break;
2006 cWalk++;
2010 static void Reorder_Ra_follows_base(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2012 if (s->ralf >= 0)
2014 int j;
2015 WORD Ra = pwChar[s->start];
2016 WORD H = pwChar[s->start+1];
2018 TRACE("Doing reorder of Ra to %i\n",s->base);
2019 for (j = s->start; j < s->base-1; j++)
2020 pwChar[j] = pwChar[j+2];
2021 pwChar[s->base-1] = Ra;
2022 pwChar[s->base] = H;
2024 s->ralf = s->base-1;
2025 s->base -= 2;
2029 static void Reorder_Ra_follows_matra(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2031 if (s->ralf >= 0)
2033 int j,loc;
2034 int stop = (s->blwf >=0)? s->blwf+1 : s->base;
2035 WORD Ra = pwChar[s->start];
2036 WORD H = pwChar[s->start+1];
2037 for (loc = s->end; loc > stop; loc--)
2038 if (lexical(pwChar[loc]) == lex_Matra_post || lexical(pwChar[loc]) == lex_Matra_below)
2039 break;
2041 TRACE("Doing reorder of Ra to %i\n",loc);
2042 for (j = s->start; j < loc-1; j++)
2043 pwChar[j] = pwChar[j+2];
2044 pwChar[loc-1] = Ra;
2045 pwChar[loc] = H;
2047 s->ralf = loc-1;
2048 s->base -= 2;
2049 if (s->blwf >= 0) s->blwf -= 2;
2050 if (s->pref >= 0) s->pref -= 2;
2054 static void Reorder_Ra_follows_syllable(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2056 if (s->ralf >= 0)
2058 int j;
2059 WORD Ra = pwChar[s->start];
2060 WORD H = pwChar[s->start+1];
2062 TRACE("Doing reorder of Ra to %i\n",s->end-1);
2063 for (j = s->start; j < s->end-1; j++)
2064 pwChar[j] = pwChar[j+2];
2065 pwChar[s->end-1] = Ra;
2066 pwChar[s->end] = H;
2068 s->ralf = s->end-1;
2069 s->base -= 2;
2070 if (s->blwf >= 0) s->blwf -= 2;
2071 if (s->pref >= 0) s->pref -= 2;
2075 static void Reorder_Matra_precede_base(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2077 int i;
2079 /* reorder Matras */
2080 if (s->end > s->base)
2082 for (i = 1; i <= s->end-s->base; i++)
2084 if (lexical(pwChar[s->base+i]) == lex_Matra_pre)
2086 int j;
2087 WCHAR c = pwChar[s->base+i];
2088 TRACE("Doing reorder of %x %x\n",c,pwChar[s->base]);
2089 for (j = s->base+i; j > s->base; j--)
2090 pwChar[j] = pwChar[j-1];
2091 pwChar[s->base] = c;
2093 if (s->ralf >= s->base) s->ralf++;
2094 if (s->blwf >= s->base) s->blwf++;
2095 if (s->pref >= s->base) s->pref++;
2096 s->base ++;
2102 static void Reorder_Matra_precede_syllable(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2104 int i;
2106 /* reorder Matras */
2107 if (s->end > s->base)
2109 for (i = 1; i <= s->end-s->base; i++)
2111 if (lexical(pwChar[s->base+i]) == lex_Matra_pre)
2113 int j;
2114 WCHAR c = pwChar[s->base+i];
2115 TRACE("Doing reorder of %x to %i\n",c,s->start);
2116 for (j = s->base+i; j > s->start; j--)
2117 pwChar[j] = pwChar[j-1];
2118 pwChar[s->start] = c;
2120 if (s->ralf >= 0) s->ralf++;
2121 if (s->blwf >= 0) s->blwf++;
2122 if (s->pref >= 0) s->pref++;
2123 s->base ++;
2129 static void SecondReorder_Blwf_follows_matra(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2131 if (s->blwf >= 0 && g->blwf > g->base)
2133 int j,loc;
2134 int g_offset;
2135 for (loc = s->end; loc > s->blwf; loc--)
2136 if (lexical(pwChar[loc]) == lex_Matra_below || lexical(pwChar[loc]) == lex_Matra_above || lexical(pwChar[loc]) == lex_Matra_post)
2137 break;
2139 g_offset = (loc - s->blwf) - 1;
2141 if (loc != s->blwf)
2143 WORD blwf = glyphs[g->blwf];
2144 TRACE("Doing reorder of Below-base to %i (glyph offset %i)\n",loc,g_offset);
2145 /* do not care about the pwChar array anymore, just the glyphs */
2146 for (j = 0; j < g_offset; j++)
2147 glyphs[g->blwf + j] = glyphs[g->blwf + j + 1];
2148 glyphs[g->blwf + g_offset] = blwf;
2153 static void SecondReorder_Matra_precede_base(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2155 int i;
2157 /* reorder previously moved Matras to correct position*/
2158 for (i = s->start; i < s->base; i++)
2160 if (lexical(pwChar[i]) == lex_Matra_pre)
2162 int j;
2163 int g_start = g->start + i - s->start;
2164 if (g_start < g->base -1 )
2166 WCHAR og = glyphs[g_start];
2167 TRACE("Doing reorder of matra from %i to %i\n",g_start,g->base);
2168 for (j = g_start; j < g->base-1; j++)
2169 glyphs[j] = glyphs[j+1];
2170 glyphs[g->base-1] = og;
2176 static void SecondReorder_Pref_precede_base(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2178 if (s->pref >= 0 && g->pref > g->base)
2180 int j;
2181 WCHAR og = glyphs[g->pref];
2182 TRACE("Doing reorder of pref from %i to %i\n",g->pref,g->base);
2183 for (j = g->pref; j > g->base; j--)
2184 glyphs[j] = glyphs[j-1];
2185 glyphs[g->base] = og;
2189 static void Reorder_Like_Sinhala(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2191 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2192 if (s->start == s->base && s->base == s->end) return;
2193 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2195 Reorder_Ra_follows_base(pwChar, s, lexical);
2196 Reorder_Matra_precede_base(pwChar, s, lexical);
2199 static void Reorder_Like_Devanagari(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2201 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2202 if (s->start == s->base && s->base == s->end) return;
2203 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2205 Reorder_Ra_follows_matra(pwChar, s, lexical);
2206 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2209 static void Reorder_Like_Bengali(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2211 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2212 if (s->start == s->base && s->base == s->end) return;
2213 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2215 Reorder_Ra_follows_base(pwChar, s, lexical);
2216 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2219 static void Reorder_Like_Kannada(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2221 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2222 if (s->start == s->base && s->base == s->end) return;
2223 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2225 Reorder_Ra_follows_syllable(pwChar, s, lexical);
2226 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2229 static void SecondReorder_Like_Telugu(LPWSTR pwChar, IndicSyllable *s, WORD* pwGlyphs, IndicSyllable *g, lexical_function lexical)
2231 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2232 TRACE("Glyphs (%i..%i..%i)\n",g->start,g->base,g->end);
2233 if (s->start == s->base && s->base == s->end) return;
2234 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2236 SecondReorder_Blwf_follows_matra(pwChar, s, pwGlyphs, g, lexical);
2239 static void SecondReorder_Like_Tamil(LPWSTR pwChar, IndicSyllable *s, WORD* pwGlyphs, IndicSyllable *g, lexical_function lexical)
2241 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2242 TRACE("Glyphs (%i..%i..%i)\n",g->start,g->base,g->end);
2243 if (s->start == s->base && s->base == s->end) return;
2244 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2246 SecondReorder_Matra_precede_base(pwChar, s, pwGlyphs, g, lexical);
2247 SecondReorder_Pref_precede_base(pwChar, s, pwGlyphs, g, lexical);
2251 static inline void shift_syllable_glyph_indexs(IndicSyllable *glyph_index, INT index, INT shift)
2253 if (shift == 0)
2254 return;
2256 if (glyph_index->start > index)
2257 glyph_index->start += shift;
2258 if (glyph_index->base > index)
2259 glyph_index->base+= shift;
2260 if (glyph_index->end > index)
2261 glyph_index->end+= shift;
2262 if (glyph_index->ralf > index)
2263 glyph_index->ralf+= shift;
2264 if (glyph_index->blwf > index)
2265 glyph_index->blwf+= shift;
2266 if (glyph_index->pref > index)
2267 glyph_index->pref+= shift;
2270 static void Apply_Indic_BasicForm(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwChars, INT cChars, IndicSyllable *syllable, WORD *pwOutGlyphs, INT* pcGlyphs, WORD *pwLogClust, lexical_function lexical, IndicSyllable *glyph_index, const GSUB_Feature *feature )
2272 int index = glyph_index->start;
2274 if (!feature)
2275 return;
2277 while(index <= glyph_index->end)
2279 INT nextIndex;
2280 INT prevCount = *pcGlyphs;
2281 nextIndex = GSUB_apply_feature_all_lookups(psc->GSUB_Table, feature, pwOutGlyphs, index, 1, pcGlyphs);
2282 if (nextIndex > GSUB_E_NOGLYPH)
2284 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2285 shift_syllable_glyph_indexs(glyph_index,index,*pcGlyphs - prevCount);
2286 index = nextIndex;
2288 else
2289 index++;
2293 static inline INT find_consonant_halant(WCHAR* pwChars, INT index, INT end, lexical_function lexical)
2295 int i = 0;
2296 while (i + index < end - 1 && !(is_consonant(lexical(pwChars[index+i])) && (lexical(pwChars[index+i+1]) == lex_Halant || (index + i < end - 2 && lexical(pwChars[index+i+1]) == lex_Nukta && lexical(pwChars[index+i+2] == lex_Halant)))))
2297 i++;
2298 if (index + i <= end-1)
2299 return index + i;
2300 else
2301 return -1;
2304 static void Apply_Indic_PreBase(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwChars, INT cChars, IndicSyllable *syllable, WORD *pwOutGlyphs, INT* pcGlyphs, WORD *pwLogClust, lexical_function lexical, IndicSyllable *glyph_index, const char* feature)
2306 INT index, nextIndex;
2307 INT count,g_offset;
2309 count = syllable->base - syllable->start;
2311 g_offset = 0;
2312 index = find_consonant_halant(&pwChars[syllable->start], 0, count, lexical);
2313 while (index >= 0 && index + g_offset < (glyph_index->base - glyph_index->start))
2315 INT prevCount = *pcGlyphs;
2316 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, index+glyph_index->start+g_offset, 1, pcGlyphs, feature);
2317 if (nextIndex > GSUB_E_NOGLYPH)
2319 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2320 shift_syllable_glyph_indexs(glyph_index, index + glyph_index->start + g_offset, (*pcGlyphs - prevCount));
2321 g_offset += (*pcGlyphs - prevCount);
2324 index+=2;
2325 index = find_consonant_halant(&pwChars[syllable->start], index, count, lexical);
2329 static void Apply_Indic_Rphf(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwChars, INT cChars, IndicSyllable *syllable, WORD *pwOutGlyphs, INT* pcGlyphs, WORD *pwLogClust, lexical_function lexical, IndicSyllable *glyph_index)
2331 INT nextIndex;
2332 INT prevCount = *pcGlyphs;
2334 if (syllable->ralf >= 0)
2336 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, glyph_index->ralf, 1, pcGlyphs, "rphf");
2337 if (nextIndex > GSUB_E_NOGLYPH)
2339 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2340 shift_syllable_glyph_indexs(glyph_index,glyph_index->ralf,*pcGlyphs - prevCount);
2345 static inline INT find_halant_consonant(WCHAR* pwChars, INT index, INT end, lexical_function lexical)
2347 int i = 0;
2348 while (index + i < end-1 && !(lexical(pwChars[index+i]) == lex_Halant &&
2349 ((index + i < end-2 && lexical(pwChars[index+i]) == lex_Nukta && is_consonant(lexical(pwChars[index+i+1]))) ||
2350 is_consonant(lexical(pwChars[index+i+1])))))
2351 i++;
2352 if (index + i <= end-1)
2353 return index+i;
2354 else
2355 return -1;
2358 static void Apply_Indic_PostBase(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwChars, INT cChars, IndicSyllable *syllable, WORD *pwOutGlyphs, INT* pcGlyphs, WORD *pwLogClust, lexical_function lexical, IndicSyllable *glyph_index, BOOL modern, const char* feat)
2360 INT index, nextIndex;
2361 INT count, g_offset=0;
2362 INT ralf = syllable->ralf;
2364 count = syllable->end - syllable->base;
2366 index = find_halant_consonant(&pwChars[syllable->base], 0, count, lexical);
2368 while (index >= 0)
2370 INT prevCount = *pcGlyphs;
2371 if (ralf >=0 && ralf < index)
2373 g_offset--;
2374 ralf = -1;
2377 if (!modern)
2379 WORD g = pwOutGlyphs[index+glyph_index->base+g_offset];
2380 pwOutGlyphs[index+glyph_index->base+g_offset] = pwOutGlyphs[index+glyph_index->base+g_offset+1];
2381 pwOutGlyphs[index+glyph_index->base+g_offset+1] = g;
2384 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, index+glyph_index->base+g_offset, 1, pcGlyphs, feat);
2385 if (nextIndex > GSUB_E_NOGLYPH)
2387 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2388 shift_syllable_glyph_indexs(glyph_index,index+glyph_index->start+g_offset, (*pcGlyphs - prevCount));
2389 g_offset += (*pcGlyphs - prevCount);
2391 else if (!modern)
2393 WORD g = pwOutGlyphs[index+glyph_index->base+g_offset];
2394 pwOutGlyphs[index+glyph_index->base+g_offset] = pwOutGlyphs[index+glyph_index->base+g_offset+1];
2395 pwOutGlyphs[index+glyph_index->base+g_offset+1] = g;
2398 index+=2;
2399 index = find_halant_consonant(&pwChars[syllable->base], index, count, lexical);
2403 static void ShapeIndicSyllables(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwChars, INT cChars, IndicSyllable *syllables, INT syllable_count, WORD *pwOutGlyphs, INT* pcGlyphs, WORD *pwLogClust, lexical_function lexical, second_reorder_function second_reorder, BOOL modern)
2405 int c;
2406 int overall_shift = 0;
2407 const GSUB_Feature *locl = (modern)?load_GSUB_feature(hdc, psa, psc, "locl"):NULL;
2408 const GSUB_Feature *nukt = load_GSUB_feature(hdc, psa, psc, "nukt");
2409 const GSUB_Feature *akhn = load_GSUB_feature(hdc, psa, psc, "akhn");
2410 const GSUB_Feature *rkrf = (modern)?load_GSUB_feature(hdc, psa, psc, "rkrf"):NULL;
2411 const GSUB_Feature *pstf = load_GSUB_feature(hdc, psa, psc, "pstf");
2412 const GSUB_Feature *vatu = (!rkrf)?load_GSUB_feature(hdc, psa, psc, "vatu"):NULL;
2413 const GSUB_Feature *cjct = (modern)?load_GSUB_feature(hdc, psa, psc, "cjct"):NULL;
2414 BOOL rphf = (load_GSUB_feature(hdc, psa, psc, "rphf") != NULL);
2415 BOOL pref = (load_GSUB_feature(hdc, psa, psc, "pref") != NULL);
2416 BOOL blwf = (load_GSUB_feature(hdc, psa, psc, "blwf") != NULL);
2417 BOOL half = (load_GSUB_feature(hdc, psa, psc, "half") != NULL);
2418 IndicSyllable glyph_indexs;
2420 for (c = 0; c < syllable_count; c++)
2422 int old_end;
2423 memcpy(&glyph_indexs, &syllables[c], sizeof(IndicSyllable));
2424 shift_syllable_glyph_indexs(&glyph_indexs, -1, overall_shift);
2425 old_end = glyph_indexs.end;
2427 if (locl)
2429 TRACE("applying feature locl\n");
2430 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, locl);
2432 if (nukt)
2434 TRACE("applying feature nukt\n");
2435 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, nukt);
2437 if (akhn)
2439 TRACE("applying feature akhn\n");
2440 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, akhn);
2443 if (rphf)
2444 Apply_Indic_Rphf(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs);
2445 if (rkrf)
2447 TRACE("applying feature rkrf\n");
2448 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, rkrf);
2450 if (pref)
2451 Apply_Indic_PostBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, modern, "pref");
2452 if (blwf)
2454 if (!modern)
2455 Apply_Indic_PreBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, "blwf");
2457 Apply_Indic_PostBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, modern, "blwf");
2460 if (half)
2461 Apply_Indic_PreBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, "half");
2462 if (pstf)
2464 TRACE("applying feature pstf\n");
2465 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, pstf);
2467 if (vatu)
2469 TRACE("applying feature vatu\n");
2470 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, vatu);
2472 if (cjct)
2474 TRACE("applying feature cjct\n");
2475 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, cjct);
2478 if (second_reorder)
2479 second_reorder(pwChars, &syllables[c], pwOutGlyphs, &glyph_indexs, lexical);
2481 overall_shift += glyph_indexs.end - old_end;
2485 static inline int unicode_lex(WCHAR c)
2487 int type;
2489 if (!c) return lex_Generic;
2490 if (c == 0x200D) return lex_ZWJ;
2491 if (c == 0x200C) return lex_ZWNJ;
2492 if (c == 0x00A0) return lex_NBSP;
2494 type = get_table_entry( indic_syllabic_table, c );
2496 if ((type & 0x00ff) != 0x0007) type = type & 0x00ff;
2498 switch( type )
2500 case 0x0d07: /* Unknown */
2501 case 0x0e07: /* Unknwon */
2502 default: return lex_Generic;
2503 case 0x0001:
2504 case 0x0002:
2505 case 0x0011:
2506 case 0x0012:
2507 case 0x0013:
2508 case 0x0014: return lex_Modifier;
2509 case 0x0003:
2510 case 0x0009:
2511 case 0x000a:
2512 case 0x000b:
2513 case 0x000d:
2514 case 0x000e:
2515 case 0x000f:
2516 case 0x0010: return lex_Consonant;
2517 case 0x0004: return lex_Nukta;
2518 case 0x0005: return lex_Halant;
2519 case 0x0006:
2520 case 0x0008: return lex_Vowel;
2521 case 0x0007:
2522 case 0x0107: return lex_Matra_post;
2523 case 0x0207:
2524 case 0x0307: return lex_Matra_pre;
2525 case 0x0807:
2526 case 0x0907:
2527 case 0x0a07:
2528 case 0x0b07:
2529 case 0x0c07:
2530 case 0x0407: return lex_Composed_Vowel;
2531 case 0x0507: return lex_Matra_above;
2532 case 0x0607: return lex_Matra_below;
2533 case 0x000c: return lex_Ra;
2537 static int sinhala_lex(WCHAR c)
2539 switch (c)
2541 case 0x0DDA:
2542 case 0x0DDD:
2543 case 0x0DDC:
2544 case 0x0DDE: return lex_Matra_post;
2545 default:
2546 return unicode_lex(c);
2550 static const VowelComponents Sinhala_vowels[] = {
2551 {0x0DDA, {0x0DD9,0x0DDA,0x0}},
2552 {0x0DDC, {0x0DD9,0x0DDC,0x0}},
2553 {0x0DDD, {0x0DD9,0x0DDD,0x0}},
2554 {0x0DDE, {0x0DD9,0x0DDE,0x0}},
2555 {0x0000, {0x0000,0x0000,0x0}}};
2557 static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2559 int cCount = cChars;
2560 int i;
2561 WCHAR *input;
2562 IndicSyllable *syllables = NULL;
2563 int syllable_count = 0;
2565 if (*pcGlyphs != cChars)
2567 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2568 return;
2571 input = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR) * (cChars * 3));
2573 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2575 /* Step 1: Decompose multi part vowels */
2576 DecomposeVowels(hdc, input, &cCount, Sinhala_vowels, pwLogClust, cChars);
2578 TRACE("New double vowel expanded string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2580 /* Step 2: Reorder within Syllables */
2581 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, sinhala_lex, Reorder_Like_Sinhala, TRUE);
2582 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2584 /* Step 3: Strip dangling joiners */
2585 for (i = 0; i < cCount; i++)
2587 if ((input[i] == 0x200D || input[i] == 0x200C) &&
2588 (i == 0 || input[i-1] == 0x0020 || i == cCount-1 || input[i+1] == 0x0020))
2589 input[i] = 0x0020;
2592 /* Step 4: Base Form application to syllables */
2593 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2594 *pcGlyphs = cCount;
2595 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, sinhala_lex, NULL, TRUE);
2597 HeapFree(GetProcessHeap(),0,input);
2598 HeapFree(GetProcessHeap(),0,syllables);
2601 static int devanagari_lex(WCHAR c)
2603 switch (c)
2605 case 0x0930: return lex_Ra;
2606 default:
2607 return unicode_lex(c);
2611 static const ConsonantComponents Devanagari_consonants[] ={
2612 {{0x0928, 0x093C, 0x00000}, 0x0929},
2613 {{0x0930, 0x093C, 0x00000}, 0x0931},
2614 {{0x0933, 0x093C, 0x00000}, 0x0934},
2615 {{0x0915, 0x093C, 0x00000}, 0x0958},
2616 {{0x0916, 0x093C, 0x00000}, 0x0959},
2617 {{0x0917, 0x093C, 0x00000}, 0x095A},
2618 {{0x091C, 0x093C, 0x00000}, 0x095B},
2619 {{0x0921, 0x093C, 0x00000}, 0x095C},
2620 {{0x0922, 0x093C, 0x00000}, 0x095D},
2621 {{0x092B, 0x093C, 0x00000}, 0x095E},
2622 {{0x092F, 0x093C, 0x00000}, 0x095F}};
2624 static void ContextualShape_Devanagari(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2626 int cCount = cChars;
2627 WCHAR *input;
2628 IndicSyllable *syllables = NULL;
2629 int syllable_count = 0;
2630 BOOL modern = get_GSUB_Indic2(psa, psc);
2632 if (*pcGlyphs != cChars)
2634 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2635 return;
2638 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2639 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2641 /* Step 1: Compose Consonant and Nukta */
2642 ComposeConsonants(hdc, input, &cCount, Devanagari_consonants, pwLogClust);
2643 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2645 /* Step 2: Reorder within Syllables */
2646 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, devanagari_lex, Reorder_Like_Devanagari, modern);
2647 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2648 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2649 *pcGlyphs = cCount;
2651 /* Step 3: Base Form application to syllables */
2652 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, devanagari_lex, NULL, modern);
2654 HeapFree(GetProcessHeap(),0,input);
2655 HeapFree(GetProcessHeap(),0,syllables);
2658 static int bengali_lex(WCHAR c)
2660 switch (c)
2662 case 0x09B0: return lex_Ra;
2663 default:
2664 return unicode_lex(c);
2668 static const VowelComponents Bengali_vowels[] = {
2669 {0x09CB, {0x09C7,0x09BE,0x0000}},
2670 {0x09CC, {0x09C7,0x09D7,0x0000}},
2671 {0x0000, {0x0000,0x0000,0x0000}}};
2673 static const ConsonantComponents Bengali_consonants[] = {
2674 {{0x09A4,0x09CD,0x200D}, 0x09CE},
2675 {{0x09A1,0x09BC,0x0000}, 0x09DC},
2676 {{0x09A2,0x09BC,0x0000}, 0x09DD},
2677 {{0x09AF,0x09BC,0x0000}, 0x09DF},
2678 {{0x0000,0x0000,0x0000}, 0x0000}};
2680 static void ContextualShape_Bengali(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2682 int cCount = cChars;
2683 WCHAR *input;
2684 IndicSyllable *syllables = NULL;
2685 int syllable_count = 0;
2686 BOOL modern = get_GSUB_Indic2(psa, psc);
2688 if (*pcGlyphs != cChars)
2690 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2691 return;
2694 input = HeapAlloc(GetProcessHeap(), 0, (cChars * 2) * sizeof(WCHAR));
2695 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2697 /* Step 1: Decompose Vowels and Compose Consonents */
2698 DecomposeVowels(hdc, input, &cCount, Bengali_vowels, pwLogClust, cChars);
2699 ComposeConsonants(hdc, input, &cCount, Bengali_consonants, pwLogClust);
2700 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2702 /* Step 2: Reorder within Syllables */
2703 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, bengali_lex, Reorder_Like_Bengali, modern);
2704 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2705 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2706 *pcGlyphs = cCount;
2708 /* Step 3: Initial form is only applied to the beginning of words */
2709 for (cCount = cCount - 1 ; cCount >= 0; cCount --)
2711 if (cCount == 0 || input[cCount] == 0x0020) /* space */
2713 int index = cCount;
2714 int gCount = 1;
2715 if (index > 0) index++;
2717 apply_GSUB_feature_to_glyph(hdc, psa, psc, &pwOutGlyphs[index], 0, 1, &gCount, "init");
2721 /* Step 4: Base Form application to syllables */
2722 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, bengali_lex, NULL, modern);
2724 HeapFree(GetProcessHeap(),0,input);
2725 HeapFree(GetProcessHeap(),0,syllables);
2728 static int gurmukhi_lex(WCHAR c)
2730 if (c == 0x0A71)
2731 return lex_Modifier;
2732 else
2733 return unicode_lex(c);
2736 static const ConsonantComponents Gurmukhi_consonants[] = {
2737 {{0x0A32,0x0A3C,0x0000}, 0x0A33},
2738 {{0x0A16,0x0A3C,0x0000}, 0x0A59},
2739 {{0x0A17,0x0A3C,0x0000}, 0x0A5A},
2740 {{0x0A1C,0x0A3C,0x0000}, 0x0A5B},
2741 {{0x0A2B,0x0A3C,0x0000}, 0x0A5E},
2742 {{0x0000,0x0000,0x0000}, 0x0000}};
2744 static void ContextualShape_Gurmukhi(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2746 int cCount = cChars;
2747 WCHAR *input;
2748 IndicSyllable *syllables = NULL;
2749 int syllable_count = 0;
2750 BOOL modern = get_GSUB_Indic2(psa, psc);
2752 if (*pcGlyphs != cChars)
2754 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2755 return;
2758 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2759 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2761 /* Step 1: Compose Consonents */
2762 ComposeConsonants(hdc, input, &cCount, Gurmukhi_consonants, pwLogClust);
2763 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2765 /* Step 2: Reorder within Syllables */
2766 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, gurmukhi_lex, Reorder_Like_Bengali, modern);
2767 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2768 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2769 *pcGlyphs = cCount;
2771 /* Step 3: Base Form application to syllables */
2772 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, gurmukhi_lex, NULL, modern);
2774 HeapFree(GetProcessHeap(),0,input);
2775 HeapFree(GetProcessHeap(),0,syllables);
2778 static int gujarati_lex(WCHAR c)
2780 switch (c)
2782 case 0x0AB0: return lex_Ra;
2783 default:
2784 return unicode_lex(c);
2788 static void ContextualShape_Gujarati(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2790 int cCount = cChars;
2791 WCHAR *input;
2792 IndicSyllable *syllables = NULL;
2793 int syllable_count = 0;
2794 BOOL modern = get_GSUB_Indic2(psa, psc);
2796 if (*pcGlyphs != cChars)
2798 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2799 return;
2802 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2803 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2805 /* Step 1: Reorder within Syllables */
2806 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, gujarati_lex, Reorder_Like_Devanagari, modern);
2807 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2808 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2809 *pcGlyphs = cCount;
2811 /* Step 2: Base Form application to syllables */
2812 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, gujarati_lex, NULL, modern);
2814 HeapFree(GetProcessHeap(),0,input);
2815 HeapFree(GetProcessHeap(),0,syllables);
2818 static int oriya_lex(WCHAR c)
2820 switch (c)
2822 case 0x0B30: return lex_Ra;
2823 default:
2824 return unicode_lex(c);
2828 static const VowelComponents Oriya_vowels[] = {
2829 {0x0B48, {0x0B47,0x0B56,0x0000}},
2830 {0x0B4B, {0x0B47,0x0B3E,0x0000}},
2831 {0x0B4C, {0x0B47,0x0B57,0x0000}},
2832 {0x0000, {0x0000,0x0000,0x0000}}};
2834 static const ConsonantComponents Oriya_consonants[] = {
2835 {{0x0B21,0x0B3C,0x0000}, 0x0B5C},
2836 {{0x0B22,0x0B3C,0x0000}, 0x0B5D},
2837 {{0x0000,0x0000,0x0000}, 0x0000}};
2839 static void ContextualShape_Oriya(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2841 int cCount = cChars;
2842 WCHAR *input;
2843 IndicSyllable *syllables = NULL;
2844 int syllable_count = 0;
2845 BOOL modern = get_GSUB_Indic2(psa, psc);
2847 if (*pcGlyphs != cChars)
2849 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2850 return;
2853 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2854 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2856 /* Step 1: Decompose Vowels and Compose Consonents */
2857 DecomposeVowels(hdc, input, &cCount, Oriya_vowels, pwLogClust, cChars);
2858 ComposeConsonants(hdc, input, &cCount, Oriya_consonants, pwLogClust);
2859 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2861 /* Step 2: Reorder within Syllables */
2862 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, oriya_lex, Reorder_Like_Bengali, modern);
2863 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2864 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2865 *pcGlyphs = cCount;
2867 /* Step 3: Base Form application to syllables */
2868 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, oriya_lex, NULL, modern);
2870 HeapFree(GetProcessHeap(),0,input);
2871 HeapFree(GetProcessHeap(),0,syllables);
2874 static int tamil_lex(WCHAR c)
2876 return unicode_lex(c);
2879 static const VowelComponents Tamil_vowels[] = {
2880 {0x0BCA, {0x0BC6,0x0BBE,0x0000}},
2881 {0x0BCB, {0x0BC7,0x0BBE,0x0000}},
2882 {0x0BCB, {0x0BC6,0x0BD7,0x0000}},
2883 {0x0000, {0x0000,0x0000,0x0000}}};
2885 static const ConsonantComponents Tamil_consonants[] = {
2886 {{0x0B92,0x0BD7,0x0000}, 0x0B94},
2887 {{0x0000,0x0000,0x0000}, 0x0000}};
2889 static void ContextualShape_Tamil(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2891 int cCount = cChars;
2892 WCHAR *input;
2893 IndicSyllable *syllables = NULL;
2894 int syllable_count = 0;
2895 BOOL modern = get_GSUB_Indic2(psa, psc);
2897 if (*pcGlyphs != cChars)
2899 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2900 return;
2903 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2904 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2906 /* Step 1: Decompose Vowels and Compose Consonents */
2907 DecomposeVowels(hdc, input, &cCount, Tamil_vowels, pwLogClust, cChars);
2908 ComposeConsonants(hdc, input, &cCount, Tamil_consonants, pwLogClust);
2909 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2911 /* Step 2: Reorder within Syllables */
2912 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, tamil_lex, Reorder_Like_Bengali, modern);
2913 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2914 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2915 *pcGlyphs = cCount;
2917 /* Step 3: Base Form application to syllables */
2918 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, tamil_lex, SecondReorder_Like_Tamil, modern);
2920 HeapFree(GetProcessHeap(),0,input);
2921 HeapFree(GetProcessHeap(),0,syllables);
2924 static int telugu_lex(WCHAR c)
2926 switch (c)
2928 case 0x0C43:
2929 case 0x0C44: return lex_Modifier;
2930 default:
2931 return unicode_lex(c);
2935 static const VowelComponents Telugu_vowels[] = {
2936 {0x0C48, {0x0C46,0x0C56,0x0000}},
2937 {0x0000, {0x0000,0x0000,0x0000}}};
2939 static void ContextualShape_Telugu(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2941 int cCount = cChars;
2942 WCHAR *input;
2943 IndicSyllable *syllables = NULL;
2944 int syllable_count = 0;
2945 BOOL modern = get_GSUB_Indic2(psa, psc);
2947 if (*pcGlyphs != cChars)
2949 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2950 return;
2953 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2954 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2956 /* Step 1: Decompose Vowels */
2957 DecomposeVowels(hdc, input, &cCount, Telugu_vowels, pwLogClust, cChars);
2958 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2960 /* Step 2: Reorder within Syllables */
2961 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, telugu_lex, Reorder_Like_Bengali, modern);
2962 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2963 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2964 *pcGlyphs = cCount;
2966 /* Step 3: Base Form application to syllables */
2967 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, telugu_lex, SecondReorder_Like_Telugu, modern);
2969 HeapFree(GetProcessHeap(),0,input);
2970 HeapFree(GetProcessHeap(),0,syllables);
2973 static int kannada_lex(WCHAR c)
2975 switch (c)
2977 case 0x0CB0: return lex_Ra;
2978 default:
2979 return unicode_lex(c);
2983 static const VowelComponents Kannada_vowels[] = {
2984 {0x0CC0, {0x0CBF,0x0CD5,0x0000}},
2985 {0x0CC7, {0x0CC6,0x0CD5,0x0000}},
2986 {0x0CC8, {0x0CC6,0x0CD6,0x0000}},
2987 {0x0CCA, {0x0CC6,0x0CC2,0x0000}},
2988 {0x0CCB, {0x0CC6,0x0CC2,0x0CD5}},
2989 {0x0000, {0x0000,0x0000,0x0000}}};
2991 static void ContextualShape_Kannada(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2993 int cCount = cChars;
2994 WCHAR *input;
2995 IndicSyllable *syllables = NULL;
2996 int syllable_count = 0;
2997 BOOL modern = get_GSUB_Indic2(psa, psc);
2999 if (*pcGlyphs != cChars)
3001 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3002 return;
3005 input = HeapAlloc(GetProcessHeap(), 0, (cChars*3) * sizeof(WCHAR));
3006 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
3008 /* Step 1: Decompose Vowels */
3009 DecomposeVowels(hdc, input, &cCount, Kannada_vowels, pwLogClust, cChars);
3010 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
3012 /* Step 2: Reorder within Syllables */
3013 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, kannada_lex, Reorder_Like_Kannada, modern);
3014 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
3015 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
3016 *pcGlyphs = cCount;
3018 /* Step 3: Base Form application to syllables */
3019 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, kannada_lex, SecondReorder_Like_Telugu, modern);
3021 HeapFree(GetProcessHeap(),0,input);
3022 HeapFree(GetProcessHeap(),0,syllables);
3025 static int malayalam_lex(WCHAR c)
3027 return unicode_lex(c);
3030 static const VowelComponents Malayalam_vowels[] = {
3031 {0x0D4A, {0x0D46,0x0D3E,0x0000}},
3032 {0x0D4B, {0x0D47,0x0D3E,0x0000}},
3033 {0x0D4C, {0x0D46,0x0D57,0x0000}},
3034 {0x0000, {0x0000,0x0000,0x0000}}};
3036 static void ContextualShape_Malayalam(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3038 int cCount = cChars;
3039 WCHAR *input;
3040 IndicSyllable *syllables = NULL;
3041 int syllable_count = 0;
3042 BOOL modern = get_GSUB_Indic2(psa, psc);
3044 if (*pcGlyphs != cChars)
3046 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3047 return;
3050 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
3051 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
3053 /* Step 1: Decompose Vowels */
3054 DecomposeVowels(hdc, input, &cCount, Malayalam_vowels, pwLogClust, cChars);
3055 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
3057 /* Step 2: Reorder within Syllables */
3058 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, malayalam_lex, Reorder_Like_Devanagari, modern);
3059 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
3060 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
3061 *pcGlyphs = cCount;
3063 /* Step 3: Base Form application to syllables */
3064 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, malayalam_lex, SecondReorder_Like_Tamil, modern);
3066 HeapFree(GetProcessHeap(),0,input);
3067 HeapFree(GetProcessHeap(),0,syllables);
3070 static int khmer_lex(WCHAR c)
3072 return unicode_lex(c);
3075 static void ContextualShape_Khmer(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3077 int cCount = cChars;
3078 WCHAR *input;
3079 IndicSyllable *syllables = NULL;
3080 int syllable_count = 0;
3082 if (*pcGlyphs != cChars)
3084 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3085 return;
3088 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
3089 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
3091 /* Step 1: Reorder within Syllables */
3092 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, khmer_lex, Reorder_Like_Devanagari, FALSE);
3093 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
3094 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
3095 *pcGlyphs = cCount;
3097 /* Step 2: Base Form application to syllables */
3098 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, khmer_lex, NULL, FALSE);
3100 HeapFree(GetProcessHeap(),0,input);
3101 HeapFree(GetProcessHeap(),0,syllables);
3104 static inline BOOL mongolian_wordbreak(WCHAR chr)
3106 return ((chr == 0x0020) || (chr == 0x200C) || (chr == 0x202F) || (chr == 0x180E) || (chr == 0x1800) || (chr == 0x1802) || (chr == 0x1803) || (chr == 0x1805) || (chr == 0x1808) || (chr == 0x1809) || (chr == 0x1807));
3109 static void ContextualShape_Mongolian(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3111 INT *context_shape;
3112 INT dirL;
3113 int i;
3115 if (*pcGlyphs != cChars)
3117 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3118 return;
3121 if (!psa->fLogicalOrder && psa->fRTL)
3122 dirL = -1;
3123 else
3124 dirL = 1;
3126 if (!psc->GSUB_Table)
3127 psc->GSUB_Table = load_gsub_table(hdc);
3129 if (!psc->GSUB_Table)
3130 return;
3132 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
3134 for (i = 0; i < cChars; i++)
3136 if (i == 0 || mongolian_wordbreak(pwcChars[i-1]))
3138 if ((i == cChars-1) || mongolian_wordbreak(pwcChars[i+1]))
3139 context_shape[i] = Xn;
3140 else
3141 context_shape[i] = Xl;
3143 else
3145 if ((i == cChars-1) || mongolian_wordbreak(pwcChars[i+1]))
3146 context_shape[i] = Xr;
3147 else
3148 context_shape[i] = Xm;
3152 /* Contextual Shaping */
3153 i = 0;
3154 while(i < *pcGlyphs)
3156 INT nextIndex;
3157 INT prevCount = *pcGlyphs;
3158 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
3159 if (nextIndex > GSUB_E_NOGLYPH)
3161 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
3162 i = nextIndex;
3164 else
3165 i++;
3168 HeapFree(GetProcessHeap(),0,context_shape);
3171 static void ShapeCharGlyphProp_Default( HDC hdc, ScriptCache* psc, SCRIPT_ANALYSIS* psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD* pwLogClust, SCRIPT_CHARPROP* pCharProp, SCRIPT_GLYPHPROP* pGlyphProp)
3173 int i,k;
3175 for (i = 0; i < cGlyphs; i++)
3177 int char_index[20];
3178 int char_count = 0;
3180 for (k = 0; k < cChars; k++)
3182 if (pwLogClust[k] == i)
3184 char_index[char_count] = k;
3185 char_count++;
3189 if (char_count == 0)
3190 continue;
3192 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3194 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3195 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3197 else
3198 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3201 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3202 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3205 static void ShapeCharGlyphProp_Arabic( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3207 int i,k;
3208 int initGlyph, finaGlyph;
3209 INT dirR, dirL;
3210 BYTE *spaces;
3212 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
3213 memset(spaces,0,cGlyphs);
3215 if (!psa->fLogicalOrder && psa->fRTL)
3217 initGlyph = cGlyphs-1;
3218 finaGlyph = 0;
3219 dirR = 1;
3220 dirL = -1;
3222 else
3224 initGlyph = 0;
3225 finaGlyph = cGlyphs-1;
3226 dirR = -1;
3227 dirL = 1;
3230 for (i = 0; i < cGlyphs; i++)
3232 for (k = 0; k < cChars; k++)
3233 if (pwLogClust[k] == i)
3235 if (pwcChars[k] == 0x0020)
3236 spaces[i] = 1;
3240 for (i = 0; i < cGlyphs; i++)
3242 int char_index[20];
3243 int char_count = 0;
3244 BOOL isInit, isFinal;
3246 for (k = 0; k < cChars; k++)
3248 if (pwLogClust[k] == i)
3250 char_index[char_count] = k;
3251 char_count++;
3255 isInit = (i == initGlyph || (i+dirR > 0 && i+dirR < cGlyphs && spaces[i+dirR]));
3256 isFinal = (i == finaGlyph || (i+dirL > 0 && i+dirL < cGlyphs && spaces[i+dirL]));
3258 if (char_count == 0)
3259 continue;
3261 if (char_count == 1)
3263 if (pwcChars[char_index[0]] == 0x0020) /* space */
3265 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BLANK;
3266 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3268 else if (pwcChars[char_index[0]] == 0x0640) /* kashida */
3269 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
3270 else if (pwcChars[char_index[0]] == 0x0633) /* SEEN */
3272 if (!isInit && !isFinal)
3273 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN_M;
3274 else if (isInit)
3275 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN;
3276 else
3277 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3279 else if (!isInit)
3281 if (pwcChars[char_index[0]] == 0x0628 ) /* BA */
3282 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BA;
3283 else if (pwcChars[char_index[0]] == 0x0631 ) /* RA */
3284 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_RA;
3285 else if (pwcChars[char_index[0]] == 0x0647 ) /* HA */
3286 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_HA;
3287 else if ((pwcChars[char_index[0]] == 0x0627 || pwcChars[char_index[0]] == 0x0625 || pwcChars[char_index[0]] == 0x0623 || pwcChars[char_index[0]] == 0x0622) ) /* alef-like */
3288 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_ALEF;
3289 else
3290 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3292 else if (!isInit && !isFinal)
3293 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3294 else
3295 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3297 else if (char_count == 2)
3299 if ((pwcChars[char_index[0]] == 0x0628 && pwcChars[char_index[1]]== 0x0631) || (pwcChars[char_index[0]] == 0x0631 && pwcChars[char_index[1]]== 0x0628)) /* BA+RA */
3300 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BARA;
3301 else if (!isInit)
3302 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3303 else
3304 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3306 else if (!isInit && !isFinal)
3307 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3308 else
3309 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3312 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3313 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3314 HeapFree(GetProcessHeap(),0,spaces);
3317 static void ShapeCharGlyphProp_Thai( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3319 int i,k;
3320 int finaGlyph;
3321 INT dirL;
3322 BYTE *spaces;
3324 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
3325 memset(spaces,0,cGlyphs);
3327 if (!psa->fLogicalOrder && psa->fRTL)
3329 finaGlyph = 0;
3330 dirL = -1;
3332 else
3334 finaGlyph = cGlyphs-1;
3335 dirL = 1;
3338 for (i = 0; i < cGlyphs; i++)
3340 for (k = 0; k < cChars; k++)
3341 if (pwLogClust[k] == i)
3343 if (pwcChars[k] == 0x0020)
3344 spaces[i] = 1;
3348 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3350 for (i = 0; i < cGlyphs; i++)
3352 int char_index[20];
3353 int char_count = 0;
3355 for (k = 0; k < cChars; k++)
3357 if (pwLogClust[k] == i)
3359 char_index[char_count] = k;
3360 char_count++;
3364 if (char_count == 0)
3365 continue;
3367 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3369 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3370 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3372 else if (i == finaGlyph)
3373 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3374 else
3375 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3377 /* handle Thai SARA AM (U+0E33) differently than GDEF */
3378 if (char_count == 1 && pwcChars[char_index[0]] == 0x0e33)
3379 pGlyphProp[i].sva.fClusterStart = 0;
3382 HeapFree(GetProcessHeap(),0,spaces);
3383 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3385 /* Do not allow justification between marks and their base */
3386 for (i = 0; i < cGlyphs; i++)
3388 if (!pGlyphProp[i].sva.fClusterStart)
3389 pGlyphProp[i-dirL].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3393 static void ShapeCharGlyphProp_None( HDC hdc, ScriptCache* psc, SCRIPT_ANALYSIS* psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD* pwLogClust, SCRIPT_CHARPROP* pCharProp, SCRIPT_GLYPHPROP* pGlyphProp)
3395 int i,k;
3397 for (i = 0; i < cGlyphs; i++)
3399 int char_index[20];
3400 int char_count = 0;
3402 for (k = 0; k < cChars; k++)
3404 if (pwLogClust[k] == i)
3406 char_index[char_count] = k;
3407 char_count++;
3411 if (char_count == 0)
3412 continue;
3414 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3416 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3417 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3419 else
3420 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3422 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3423 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3426 static void ShapeCharGlyphProp_Tibet( HDC hdc, ScriptCache* psc, SCRIPT_ANALYSIS* psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD* pwLogClust, SCRIPT_CHARPROP* pCharProp, SCRIPT_GLYPHPROP* pGlyphProp)
3428 int i,k;
3430 for (i = 0; i < cGlyphs; i++)
3432 int char_index[20];
3433 int char_count = 0;
3435 for (k = 0; k < cChars; k++)
3437 if (pwLogClust[k] == i)
3439 char_index[char_count] = k;
3440 char_count++;
3444 if (char_count == 0)
3445 continue;
3447 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3449 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3450 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3452 else
3453 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3455 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3456 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3458 /* Tibeten script does not set sva.fDiacritic or sva.fZeroWidth */
3459 for (i = 0; i < cGlyphs; i++)
3461 if (!pGlyphProp[i].sva.fClusterStart)
3463 pGlyphProp[i].sva.fDiacritic = 0;
3464 pGlyphProp[i].sva.fZeroWidth = 0;
3469 static void ShapeCharGlyphProp_BaseIndic( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp, lexical_function lexical, BOOL use_syllables, BOOL override_gsub)
3471 int i,k;
3473 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3474 for (i = 0; i < cGlyphs; i++)
3476 int char_index[20];
3477 int char_count = 0;
3479 for (k = 0; k < cChars; k++)
3481 if (pwLogClust[k] == i)
3483 char_index[char_count] = k;
3484 char_count++;
3488 if (override_gsub)
3490 /* Most indic scripts do not set fDiacritic or fZeroWidth */
3491 pGlyphProp[i].sva.fDiacritic = FALSE;
3492 pGlyphProp[i].sva.fZeroWidth = FALSE;
3495 if (char_count == 0)
3496 continue;
3498 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3500 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3501 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3503 else
3504 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3506 pGlyphProp[i].sva.fClusterStart = 0;
3507 for (k = 0; k < char_count && !pGlyphProp[i].sva.fClusterStart; k++)
3508 switch (lexical(pwcChars[char_index[k]]))
3510 case lex_Matra_pre:
3511 case lex_Matra_post:
3512 case lex_Matra_above:
3513 case lex_Matra_below:
3514 case lex_Modifier:
3515 case lex_Halant:
3516 break;
3517 case lex_ZWJ:
3518 case lex_ZWNJ:
3519 /* check for dangling joiners */
3520 if (pwcChars[char_index[k]-1] == 0x0020 || pwcChars[char_index[k]+1] == 0x0020)
3521 pGlyphProp[i].sva.fClusterStart = 1;
3522 else
3523 k = char_count;
3524 break;
3525 default:
3526 pGlyphProp[i].sva.fClusterStart = 1;
3527 break;
3531 if (use_syllables)
3533 IndicSyllable *syllables = NULL;
3534 int syllable_count = 0;
3535 BOOL modern = get_GSUB_Indic2(psa, psc);
3537 Indic_ParseSyllables( hdc, psa, psc, pwcChars, cChars, &syllables, &syllable_count, lexical, modern);
3539 for (i = 0; i < syllable_count; i++)
3541 int j;
3542 WORD g = pwLogClust[syllables[i].start];
3543 for (j = syllables[i].start+1; j <= syllables[i].end; j++)
3545 if (pwLogClust[j] != g)
3547 pGlyphProp[pwLogClust[j]].sva.fClusterStart = 0;
3548 pwLogClust[j] = g;
3553 HeapFree(GetProcessHeap(), 0, syllables);
3556 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3559 static void ShapeCharGlyphProp_Sinhala( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3561 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, sinhala_lex, FALSE, FALSE);
3564 static void ShapeCharGlyphProp_Devanagari( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3566 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, devanagari_lex, TRUE, TRUE);
3569 static void ShapeCharGlyphProp_Bengali( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3571 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, bengali_lex, TRUE, TRUE);
3574 static void ShapeCharGlyphProp_Gurmukhi( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3576 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, gurmukhi_lex, TRUE, TRUE);
3579 static void ShapeCharGlyphProp_Gujarati( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3581 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, gujarati_lex, TRUE, TRUE);
3584 static void ShapeCharGlyphProp_Oriya( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3586 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, oriya_lex, TRUE, TRUE);
3589 static void ShapeCharGlyphProp_Tamil( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3591 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, tamil_lex, TRUE, TRUE);
3594 static void ShapeCharGlyphProp_Telugu( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3596 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, telugu_lex, TRUE, TRUE);
3599 static void ShapeCharGlyphProp_Kannada( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3601 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, kannada_lex, TRUE, TRUE);
3604 static void ShapeCharGlyphProp_Malayalam( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3606 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, malayalam_lex, TRUE, TRUE);
3609 static void ShapeCharGlyphProp_Khmer( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp )
3611 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, khmer_lex, TRUE, TRUE);
3614 void SHAPE_CharGlyphProp(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp)
3616 if (ShapingData[psa->eScript].charGlyphPropProc)
3617 ShapingData[psa->eScript].charGlyphPropProc(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
3618 else
3619 ShapeCharGlyphProp_Default(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
3622 void SHAPE_ContextualShaping(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3624 if (!psc->GSUB_Table)
3625 psc->GSUB_Table = load_gsub_table(hdc);
3627 if (ShapingData[psa->eScript].contextProc)
3628 ShapingData[psa->eScript].contextProc(hdc, psc, psa, pwcChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3631 static void SHAPE_ApplyOpenTypeFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, INT cChars, const TEXTRANGE_PROPERTIES *rpRangeProperties, WORD *pwLogClust)
3633 int i;
3634 INT dirL;
3636 if (!rpRangeProperties)
3637 return;
3639 if (!psc->GSUB_Table)
3640 psc->GSUB_Table = load_gsub_table(hdc);
3642 if (!psc->GSUB_Table)
3643 return;
3645 if (!psa->fLogicalOrder && psa->fRTL)
3646 dirL = -1;
3647 else
3648 dirL = 1;
3650 for (i = 0; i < rpRangeProperties->cotfRecords; i++)
3652 if (rpRangeProperties->potfRecords[i].lParameter > 0)
3653 apply_GSUB_feature(hdc, psa, psc, pwOutGlyphs, dirL, pcGlyphs, cChars, (const char*)&rpRangeProperties->potfRecords[i].tagFeature, pwLogClust);
3657 void SHAPE_ApplyDefaultOpentypeFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, INT cChars, WORD *pwLogClust)
3659 const TEXTRANGE_PROPERTIES *rpRangeProperties;
3660 rpRangeProperties = &ShapingData[psa->eScript].defaultTextRange;
3662 SHAPE_ApplyOpenTypeFeatures(hdc, psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, rpRangeProperties, pwLogClust);
3665 HRESULT SHAPE_CheckFontForRequiredFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa)
3667 const GSUB_Feature *feature;
3668 int i;
3670 if (!ShapingData[psa->eScript].requiredFeatures)
3671 return S_OK;
3673 if (!psc->GSUB_Table)
3674 psc->GSUB_Table = load_gsub_table(hdc);
3676 /* we need to have at least one of the required features */
3677 i = 0;
3678 while (ShapingData[psa->eScript].requiredFeatures[i])
3680 feature = load_GSUB_feature(hdc, psa, psc, ShapingData[psa->eScript].requiredFeatures[i]);
3681 if (feature)
3682 return S_OK;
3683 i++;
3686 return USP_E_SCRIPT_NOT_IN_FONT;
3689 static VOID *load_CMAP_format12_table(HDC hdc, ScriptCache *psc)
3691 CMAP_Header *CMAP_Table = NULL;
3692 int length;
3693 int i;
3695 if (!psc->CMAP_Table)
3697 length = GetFontData(hdc, CMAP_TAG , 0, NULL, 0);
3698 if (length != GDI_ERROR)
3700 psc->CMAP_Table = HeapAlloc(GetProcessHeap(),0,length);
3701 GetFontData(hdc, CMAP_TAG , 0, psc->CMAP_Table, length);
3702 TRACE("Loaded cmap table of %i bytes\n",length);
3704 else
3705 return NULL;
3708 CMAP_Table = psc->CMAP_Table;
3710 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++)
3712 if ( (GET_BE_WORD(CMAP_Table->tables[i].platformID) == 3) &&
3713 (GET_BE_WORD(CMAP_Table->tables[i].encodingID) == 10) )
3715 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
3716 if (GET_BE_WORD(format->format) == 12)
3717 return format;
3720 return NULL;
3723 static int compare_group(const void *a, const void* b)
3725 const DWORD *chr = a;
3726 const CMAP_SegmentedCoverage_group *group = b;
3728 if (*chr < GET_BE_DWORD(group->startCharCode))
3729 return -1;
3730 if (*chr > GET_BE_DWORD(group->endCharCode))
3731 return 1;
3732 return 0;
3735 DWORD CMAP_GetGlyphIndex(HDC hdc, ScriptCache *psc, DWORD utf32c, LPWORD pgi, DWORD flags)
3737 /* BMP: use gdi32 for ease */
3738 if (utf32c < 0x10000)
3740 WCHAR ch = utf32c;
3741 return GetGlyphIndicesW(hdc,&ch, 1, pgi, flags);
3744 if (!psc->CMAP_format12_Table)
3745 psc->CMAP_format12_Table = load_CMAP_format12_table(hdc, psc);
3747 if (flags & GGI_MARK_NONEXISTING_GLYPHS)
3748 *pgi = 0xffff;
3749 else
3750 *pgi = 0;
3752 if (psc->CMAP_format12_Table)
3754 CMAP_SegmentedCoverage *format = NULL;
3755 CMAP_SegmentedCoverage_group *group = NULL;
3757 format = (CMAP_SegmentedCoverage *)psc->CMAP_format12_Table;
3759 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups),
3760 sizeof(CMAP_SegmentedCoverage_group), compare_group);
3762 if (group)
3764 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode);
3765 *pgi = GET_BE_DWORD(group->startGlyphID) + offset;
3766 return 0;
3769 return 0;
3772 static void GSUB_initialize_script_cache(ScriptCache *psc)
3774 int i;
3776 if (!psc->script_count)
3778 const GSUB_ScriptList *script;
3779 const GSUB_Header* header = (const GSUB_Header*)psc->GSUB_Table;
3780 script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
3781 psc->script_count = GET_BE_WORD(script->ScriptCount);
3782 TRACE("initializing %i scripts in this font\n",psc->script_count);
3783 psc->scripts = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedScript) * psc->script_count);
3784 for (i = 0; i < psc->script_count; i++)
3786 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
3787 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]);
3788 psc->scripts[i].table = ((const BYTE*)script + offset);
3793 static HRESULT GSUB_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags, LPCVOID* script_table)
3795 int i;
3796 HRESULT rc = S_OK;
3798 GSUB_initialize_script_cache(psc);
3799 *pcTags = psc->script_count;
3801 if (!searchingFor && cMaxTags < *pcTags)
3802 rc = E_OUTOFMEMORY;
3803 else if (searchingFor)
3804 rc = USP_E_SCRIPT_NOT_IN_FONT;
3806 for (i = 0; i < psc->script_count; i++)
3808 if (i < cMaxTags)
3809 pScriptTags[i] = psc->scripts[i].tag;
3811 if (searchingFor)
3813 if (searchingFor == psc->scripts[i].tag)
3815 pScriptTags[0] = psc->scripts[i].tag;
3816 *pcTags = 1;
3817 if (script_table)
3818 *script_table = psc->scripts[i].table;
3819 rc = S_OK;
3820 break;
3824 return rc;
3827 HRESULT SHAPE_GetFontScriptTags( HDC hdc, ScriptCache *psc,
3828 SCRIPT_ANALYSIS *psa, int cMaxTags,
3829 OPENTYPE_TAG *pScriptTags, int *pcTags)
3831 HRESULT hr;
3832 OPENTYPE_TAG searching = 0x00000000;
3834 if (!psc->GSUB_Table)
3835 psc->GSUB_Table = load_gsub_table(hdc);
3837 if (psa)
3839 if (ShapingData[psa->eScript].otTag[0] != 0)
3840 searching = MS_MAKE_TAG(ShapingData[psa->eScript].otTag[0], ShapingData[psa->eScript].otTag[1], ShapingData[psa->eScript].otTag[2], ShapingData[psa->eScript].otTag[3]);
3843 hr = GSUB_GetFontScriptTags(psc, searching, cMaxTags, pScriptTags, pcTags, NULL);
3844 if (FAILED(hr))
3845 *pcTags = 0;
3846 return hr;