usp10: Move GDEF functions to opentype.c.
[wine/multimedia.git] / dlls / usp10 / shape.c
blob151f9a4a48d2381e676ec454f6bb0ee30ba7fa19
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 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
287 static HRESULT GSUB_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags, LPCVOID* script_table);
288 static HRESULT GSUB_GetFontLanguageTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pLanguageTags, int *pcTags, LPCVOID* language_table);
289 static HRESULT GSUB_GetFontFeatureTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG language_tag, BOOL filtered, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags, LoadedFeature** feature);
291 typedef struct tagVowelComponents
293 WCHAR base;
294 WCHAR parts[3];
295 } VowelComponents;
297 typedef struct tagConsonantComponents
299 WCHAR parts[3];
300 WCHAR output;
301 } ConsonantComponents;
303 typedef void (*second_reorder_function)(LPWSTR pwChar, IndicSyllable *syllable,WORD* pwGlyphs, IndicSyllable* glyph_index, lexical_function lex);
305 /* the orders of joined_forms and contextual_features need to line up */
306 static const char* contextual_features[] =
308 "isol",
309 "fina",
310 "init",
311 "medi",
312 /* Syriac Alaph */
313 "med2",
314 "fin2",
315 "fin3"
318 static OPENTYPE_FEATURE_RECORD standard_features[] =
320 { MS_MAKE_TAG('c','c','m','p'), 1},
321 { MS_MAKE_TAG('l','o','c','l'), 1},
324 static OPENTYPE_FEATURE_RECORD latin_features[] =
326 { MS_MAKE_TAG('l','i','g','a'), 1},
327 { MS_MAKE_TAG('c','l','i','g'), 1},
330 static OPENTYPE_FEATURE_RECORD arabic_features[] =
332 { MS_MAKE_TAG('r','l','i','g'), 1},
333 { MS_MAKE_TAG('c','a','l','t'), 1},
334 { MS_MAKE_TAG('l','i','g','a'), 1},
335 { MS_MAKE_TAG('d','l','i','g'), 1},
336 { MS_MAKE_TAG('c','s','w','h'), 1},
337 { MS_MAKE_TAG('m','s','e','t'), 1},
340 static const char* required_arabic_features[] =
342 "fina",
343 "init",
344 "medi",
345 "rlig",
346 NULL
349 static OPENTYPE_FEATURE_RECORD hebrew_features[] =
351 { MS_MAKE_TAG('d','l','i','g'), 0},
354 static OPENTYPE_FEATURE_RECORD syriac_features[] =
356 { MS_MAKE_TAG('r','l','i','g'), 1},
357 { MS_MAKE_TAG('c','a','l','t'), 1},
358 { MS_MAKE_TAG('l','i','g','a'), 1},
359 { MS_MAKE_TAG('d','l','i','g'), 1},
362 static const char* required_syriac_features[] =
364 "fina",
365 "fin2",
366 "fin3",
367 "init",
368 "medi",
369 "med2",
370 "rlig",
371 NULL
374 static OPENTYPE_FEATURE_RECORD sinhala_features[] =
376 /* Presentation forms */
377 { MS_MAKE_TAG('b','l','w','s'), 1},
378 { MS_MAKE_TAG('a','b','v','s'), 1},
379 { MS_MAKE_TAG('p','s','t','s'), 1},
382 static OPENTYPE_FEATURE_RECORD tibetan_features[] =
384 { MS_MAKE_TAG('a','b','v','s'), 1},
385 { MS_MAKE_TAG('b','l','w','s'), 1},
388 static OPENTYPE_FEATURE_RECORD phags_features[] =
390 { MS_MAKE_TAG('a','b','v','s'), 1},
391 { MS_MAKE_TAG('b','l','w','s'), 1},
392 { MS_MAKE_TAG('c','a','l','t'), 1},
395 static OPENTYPE_FEATURE_RECORD thai_features[] =
397 { MS_MAKE_TAG('c','c','m','p'), 1},
400 static const char* required_lao_features[] =
402 "ccmp",
403 NULL
406 static const char* required_devanagari_features[] =
408 "nukt",
409 "akhn",
410 "rphf",
411 "blwf",
412 "half",
413 "vatu",
414 "pres",
415 "abvs",
416 "blws",
417 "psts",
418 "haln",
419 NULL
422 static OPENTYPE_FEATURE_RECORD devanagari_features[] =
424 { MS_MAKE_TAG('p','r','e','s'), 1},
425 { MS_MAKE_TAG('a','b','v','s'), 1},
426 { MS_MAKE_TAG('b','l','w','s'), 1},
427 { MS_MAKE_TAG('p','s','t','s'), 1},
428 { MS_MAKE_TAG('h','a','l','n'), 1},
429 { MS_MAKE_TAG('c','a','l','t'), 1},
432 static OPENTYPE_FEATURE_RECORD myanmar_features[] =
434 { MS_MAKE_TAG('l','i','g','a'), 1},
435 { MS_MAKE_TAG('c','l','i','g'), 1},
438 static const char* required_bengali_features[] =
440 "nukt",
441 "akhn",
442 "rphf",
443 "blwf",
444 "half",
445 "vatu",
446 "pstf",
447 "init",
448 "abvs",
449 "blws",
450 "psts",
451 "haln",
452 NULL
455 static const char* required_gurmukhi_features[] =
457 "nukt",
458 "akhn",
459 "rphf",
460 "blwf",
461 "half",
462 "pstf",
463 "vatu",
464 "cjct",
465 "pres",
466 "abvs",
467 "blws",
468 "psts",
469 "haln",
470 "calt",
471 NULL
474 static const char* required_oriya_features[] =
476 "nukt",
477 "akhn",
478 "rphf",
479 "blwf",
480 "pstf",
481 "cjct",
482 "pres",
483 "abvs",
484 "blws",
485 "psts",
486 "haln",
487 "calt",
488 NULL
491 static const char* required_tamil_features[] =
493 "nukt",
494 "akhn",
495 "rphf",
496 "pref",
497 "half",
498 "pres",
499 "abvs",
500 "blws",
501 "psts",
502 "haln",
503 "calt",
504 NULL
507 static const char* required_telugu_features[] =
509 "nukt",
510 "akhn",
511 "rphf",
512 "pref",
513 "half",
514 "pstf",
515 "cjct",
516 "pres",
517 "abvs",
518 "blws",
519 "psts",
520 "haln",
521 "calt",
522 NULL
525 static OPENTYPE_FEATURE_RECORD khmer_features[] =
527 { MS_MAKE_TAG('p','r','e','s'), 1},
528 { MS_MAKE_TAG('b','l','w','s'), 1},
529 { MS_MAKE_TAG('a','b','v','s'), 1},
530 { MS_MAKE_TAG('p','s','t','s'), 1},
531 { MS_MAKE_TAG('c','l','i','g'), 1},
534 static const char* required_khmer_features[] =
536 "pref",
537 "blwf",
538 "abvf",
539 "pstf",
540 "pres",
541 "blws",
542 "abvs",
543 "psts",
544 "clig",
545 NULL
548 static OPENTYPE_FEATURE_RECORD no_features[] =
549 { };
551 static OPENTYPE_FEATURE_RECORD ethiopic_features[] =
553 { MS_MAKE_TAG('c','c','m','p'), 1},
554 { MS_MAKE_TAG('l','o','c','l'), 1},
555 { MS_MAKE_TAG('c','a','l','t'), 1},
556 { MS_MAKE_TAG('l','i','g','a'), 1},
559 static OPENTYPE_FEATURE_RECORD mongolian_features[] =
561 { MS_MAKE_TAG('c','c','m','p'), 1},
562 { MS_MAKE_TAG('l','o','c','l'), 1},
563 { MS_MAKE_TAG('c','a','l','t'), 1},
564 { MS_MAKE_TAG('r','l','i','g'), 1},
567 typedef struct ScriptShapeDataTag {
568 TEXTRANGE_PROPERTIES defaultTextRange;
569 const char** requiredFeatures;
570 OPENTYPE_TAG newOtTag;
571 ContextualShapingProc contextProc;
572 ShapeCharGlyphPropProc charGlyphPropProc;
573 } ScriptShapeData;
575 /* in order of scripts */
576 static const ScriptShapeData ShapingData[] =
578 {{ standard_features, 2}, NULL, 0, NULL, NULL},
579 {{ latin_features, 2}, NULL, 0, NULL, NULL},
580 {{ latin_features, 2}, NULL, 0, NULL, NULL},
581 {{ latin_features, 2}, NULL, 0, NULL, NULL},
582 {{ standard_features, 2}, NULL, 0, NULL, NULL},
583 {{ latin_features, 2}, NULL, 0, NULL, NULL},
584 {{ arabic_features, 6}, required_arabic_features, 0, ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
585 {{ arabic_features, 6}, required_arabic_features, 0, ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
586 {{ hebrew_features, 1}, NULL, 0, NULL, NULL},
587 {{ syriac_features, 4}, required_syriac_features, 0, ContextualShape_Syriac, ShapeCharGlyphProp_None},
588 {{ arabic_features, 6}, required_arabic_features, 0, ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
589 {{ NULL, 0}, NULL, 0, NULL, ShapeCharGlyphProp_None},
590 {{ standard_features, 2}, NULL, 0, NULL, NULL},
591 {{ standard_features, 2}, NULL, 0, NULL, NULL},
592 {{ standard_features, 2}, NULL, 0, NULL, NULL},
593 {{ standard_features, 2}, NULL, 0, NULL, NULL},
594 {{ sinhala_features, 3}, NULL, 0, ContextualShape_Sinhala, ShapeCharGlyphProp_Sinhala},
595 {{ tibetan_features, 2}, NULL, 0, NULL, ShapeCharGlyphProp_Tibet},
596 {{ tibetan_features, 2}, NULL, 0, NULL, ShapeCharGlyphProp_Tibet},
597 {{ phags_features, 3}, NULL, 0, ContextualShape_Phags_pa, ShapeCharGlyphProp_Thai},
598 {{ thai_features, 1}, NULL, 0, NULL, ShapeCharGlyphProp_Thai},
599 {{ thai_features, 1}, NULL, 0, NULL, ShapeCharGlyphProp_Thai},
600 {{ thai_features, 1}, required_lao_features, 0, NULL, ShapeCharGlyphProp_Thai},
601 {{ thai_features, 1}, required_lao_features, 0, NULL, ShapeCharGlyphProp_Thai},
602 {{ devanagari_features, 6}, required_devanagari_features, MS_MAKE_TAG('d','e','v','2'), ContextualShape_Devanagari, ShapeCharGlyphProp_Devanagari},
603 {{ devanagari_features, 6}, required_devanagari_features, MS_MAKE_TAG('d','e','v','2'), ContextualShape_Devanagari, ShapeCharGlyphProp_Devanagari},
604 {{ devanagari_features, 6}, required_bengali_features, MS_MAKE_TAG('b','n','g','2'), ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
605 {{ devanagari_features, 6}, required_bengali_features, MS_MAKE_TAG('b','n','g','2'), ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
606 {{ devanagari_features, 6}, required_bengali_features, MS_MAKE_TAG('b','n','g','2'), ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
607 {{ devanagari_features, 6}, required_gurmukhi_features, MS_MAKE_TAG('g','u','r','2'), ContextualShape_Gurmukhi, ShapeCharGlyphProp_Gurmukhi},
608 {{ devanagari_features, 6}, required_gurmukhi_features, MS_MAKE_TAG('g','u','r','2'), ContextualShape_Gurmukhi, ShapeCharGlyphProp_Gurmukhi},
609 {{ devanagari_features, 6}, required_devanagari_features, MS_MAKE_TAG('g','j','r','2'), ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
610 {{ devanagari_features, 6}, required_devanagari_features, MS_MAKE_TAG('g','j','r','2'), ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
611 {{ devanagari_features, 6}, required_devanagari_features, MS_MAKE_TAG('g','j','r','2'), ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
612 {{ devanagari_features, 6}, required_oriya_features, MS_MAKE_TAG('o','r','y','2'), ContextualShape_Oriya, ShapeCharGlyphProp_Oriya},
613 {{ devanagari_features, 6}, required_oriya_features, MS_MAKE_TAG('o','r','y','2'), ContextualShape_Oriya, ShapeCharGlyphProp_Oriya},
614 {{ devanagari_features, 6}, required_tamil_features, MS_MAKE_TAG('t','a','m','2'), ContextualShape_Tamil, ShapeCharGlyphProp_Tamil},
615 {{ devanagari_features, 6}, required_tamil_features, MS_MAKE_TAG('t','a','m','2'), ContextualShape_Tamil, ShapeCharGlyphProp_Tamil},
616 {{ devanagari_features, 6}, required_telugu_features, MS_MAKE_TAG('t','e','l','2'), ContextualShape_Telugu, ShapeCharGlyphProp_Telugu},
617 {{ devanagari_features, 6}, required_telugu_features, MS_MAKE_TAG('t','e','l','2'), ContextualShape_Telugu, ShapeCharGlyphProp_Telugu},
618 {{ devanagari_features, 6}, required_telugu_features, MS_MAKE_TAG('k','n','d','2'), ContextualShape_Kannada, ShapeCharGlyphProp_Kannada},
619 {{ devanagari_features, 6}, required_telugu_features, MS_MAKE_TAG('k','n','d','2'), ContextualShape_Kannada, ShapeCharGlyphProp_Kannada},
620 {{ devanagari_features, 6}, required_telugu_features, MS_MAKE_TAG('m','l','m','2'), ContextualShape_Malayalam, ShapeCharGlyphProp_Malayalam},
621 {{ devanagari_features, 6}, required_telugu_features, MS_MAKE_TAG('m','l','m','2'), ContextualShape_Malayalam, ShapeCharGlyphProp_Malayalam},
622 {{ standard_features, 2}, NULL, 0, NULL, NULL},
623 {{ latin_features, 2}, NULL, 0, NULL, NULL},
624 {{ standard_features, 2}, NULL, 0, NULL, NULL},
625 {{ myanmar_features, 2}, NULL, 0, NULL, NULL},
626 {{ myanmar_features, 2}, NULL, 0, NULL, NULL},
627 {{ standard_features, 2}, NULL, 0, NULL, NULL},
628 {{ standard_features, 2}, NULL, 0, NULL, NULL},
629 {{ standard_features, 2}, NULL, 0, NULL, NULL},
630 {{ khmer_features, 5}, required_khmer_features, 0, ContextualShape_Khmer, ShapeCharGlyphProp_Khmer},
631 {{ khmer_features, 5}, required_khmer_features, 0, ContextualShape_Khmer, ShapeCharGlyphProp_Khmer},
632 {{ no_features, 0}, NULL, 0, NULL, NULL},
633 {{ no_features, 0}, NULL, 0, NULL, NULL},
634 {{ no_features, 0}, NULL, 0, NULL, NULL},
635 {{ no_features, 0}, NULL, 0, NULL, NULL},
636 {{ no_features, 0}, NULL, 0, NULL, NULL},
637 {{ no_features, 0}, NULL, 0, NULL, NULL},
638 {{ ethiopic_features, 4}, NULL, 0, NULL, NULL},
639 {{ ethiopic_features, 4}, NULL, 0, NULL, NULL},
640 {{ mongolian_features, 4}, NULL, 0, ContextualShape_Mongolian, NULL},
641 {{ mongolian_features, 4}, NULL, 0, ContextualShape_Mongolian, NULL},
642 {{ no_features, 0}, NULL, 0, NULL, NULL},
643 {{ no_features, 0}, NULL, 0, NULL, NULL},
644 {{ no_features, 0}, NULL, 0, NULL, NULL},
645 {{ no_features, 0}, NULL, 0, NULL, NULL},
646 {{ no_features, 0}, NULL, 0, NULL, NULL},
647 {{ no_features, 0}, NULL, 0, NULL, NULL},
648 {{ no_features, 0}, NULL, 0, NULL, NULL},
649 {{ no_features, 0}, NULL, 0, NULL, NULL},
650 {{ no_features, 0}, NULL, 0, NULL, NULL},
651 {{ no_features, 0}, NULL, 0, NULL, NULL},
652 {{ no_features, 0}, NULL, 0, NULL, NULL},
653 {{ no_features, 0}, NULL, 0, NULL, NULL},
654 {{ no_features, 0}, NULL, 0, NULL, NULL},
655 {{ no_features, 0}, NULL, 0, NULL, NULL},
656 {{ no_features, 0}, NULL, 0, NULL, NULL},
657 {{ hebrew_features, 1}, NULL, 0, NULL, NULL},
658 {{ latin_features, 2}, NULL, 0, NULL, NULL},
659 {{ thai_features, 1}, NULL, 0, NULL, ShapeCharGlyphProp_Thai},
662 extern scriptData scriptInformation[];
664 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
666 const GSUB_CoverageFormat1* cf1;
668 cf1 = table;
670 if (GET_BE_WORD(cf1->CoverageFormat) == 1)
672 int count = GET_BE_WORD(cf1->GlyphCount);
673 int i;
674 TRACE("Coverage Format 1, %i glyphs\n",count);
675 for (i = 0; i < count; i++)
676 if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
677 return i;
678 return -1;
680 else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
682 const GSUB_CoverageFormat2* cf2;
683 int i;
684 int count;
685 cf2 = (const GSUB_CoverageFormat2*)cf1;
687 count = GET_BE_WORD(cf2->RangeCount);
688 TRACE("Coverage Format 2, %i ranges\n",count);
689 for (i = 0; i < count; i++)
691 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
692 return -1;
693 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
694 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
696 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
697 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
700 return -1;
702 else
703 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
705 return -1;
708 static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
710 int j;
711 TRACE("Single Substitution Subtable\n");
713 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
715 int offset;
716 const GSUB_SingleSubstFormat1 *ssf1;
717 offset = GET_BE_WORD(look->SubTable[j]);
718 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
719 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
721 int offset = GET_BE_WORD(ssf1->Coverage);
722 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
723 if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
725 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
726 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
727 TRACE(" 0x%x\n",glyphs[glyph_index]);
728 return glyph_index + write_dir;
731 else
733 const GSUB_SingleSubstFormat2 *ssf2;
734 INT index;
735 INT offset;
737 ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
738 offset = GET_BE_WORD(ssf1->Coverage);
739 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
740 index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
741 TRACE(" Coverage index %i\n",index);
742 if (index != -1)
744 if (glyphs[glyph_index] == GET_BE_WORD(ssf2->Substitute[index]))
745 return GSUB_E_NOGLYPH;
747 TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]);
748 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
749 TRACE("0x%x\n",glyphs[glyph_index]);
750 return glyph_index + write_dir;
754 return GSUB_E_NOGLYPH;
757 static INT GSUB_apply_MultipleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
759 int j;
760 TRACE("Multiple Substitution Subtable\n");
762 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
764 int offset, index;
765 const GSUB_MultipleSubstFormat1 *msf1;
766 offset = GET_BE_WORD(look->SubTable[j]);
767 msf1 = (const GSUB_MultipleSubstFormat1*)((const BYTE*)look+offset);
769 offset = GET_BE_WORD(msf1->Coverage);
770 index = GSUB_is_glyph_covered((const BYTE*)msf1+offset, glyphs[glyph_index]);
771 if (index != -1)
773 const GSUB_Sequence *seq;
774 int sub_count;
775 int j;
776 offset = GET_BE_WORD(msf1->Sequence[index]);
777 seq = (const GSUB_Sequence*)((const BYTE*)msf1+offset);
778 sub_count = GET_BE_WORD(seq->GlyphCount);
779 TRACE(" Glyph 0x%x (+%i)->",glyphs[glyph_index],(sub_count-1));
781 for (j = (*glyph_count)+(sub_count-1); j > glyph_index; j--)
782 glyphs[j] =glyphs[j-(sub_count-1)];
784 for (j = 0; j < sub_count; j++)
785 if (write_dir < 0)
786 glyphs[glyph_index + (sub_count-1) - j] = GET_BE_WORD(seq->Substitute[j]);
787 else
788 glyphs[glyph_index + j] = GET_BE_WORD(seq->Substitute[j]);
790 *glyph_count = *glyph_count + (sub_count - 1);
792 if (TRACE_ON(uniscribe))
794 for (j = 0; j < sub_count; j++)
795 TRACE(" 0x%x",glyphs[glyph_index+j]);
796 TRACE("\n");
799 return glyph_index + (sub_count * write_dir);
802 return GSUB_E_NOGLYPH;
805 static INT GSUB_apply_AlternateSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
807 int j;
808 TRACE("Alternate Substitution Subtable\n");
810 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
812 int offset;
813 const GSUB_AlternateSubstFormat1 *asf1;
814 INT index;
816 offset = GET_BE_WORD(look->SubTable[j]);
817 asf1 = (const GSUB_AlternateSubstFormat1*)((const BYTE*)look+offset);
818 offset = GET_BE_WORD(asf1->Coverage);
820 index = GSUB_is_glyph_covered((const BYTE*)asf1+offset, glyphs[glyph_index]);
821 if (index != -1)
823 const GSUB_AlternateSet *as;
824 offset = GET_BE_WORD(asf1->AlternateSet[index]);
825 as = (const GSUB_AlternateSet*)((const BYTE*)asf1+offset);
826 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as->GlyphCount));
827 if (glyphs[glyph_index] == GET_BE_WORD(as->Alternate[0]))
828 return GSUB_E_NOGLYPH;
830 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
831 glyphs[glyph_index] = GET_BE_WORD(as->Alternate[0]);
832 TRACE(" 0x%x\n",glyphs[glyph_index]);
833 return glyph_index + write_dir;
836 return GSUB_E_NOGLYPH;
839 static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
841 int j;
843 TRACE("Ligature Substitution Subtable\n");
844 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
846 const GSUB_LigatureSubstFormat1 *lsf1;
847 int offset,index;
849 offset = GET_BE_WORD(look->SubTable[j]);
850 lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
851 offset = GET_BE_WORD(lsf1->Coverage);
852 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
853 TRACE(" Coverage index %i\n",index);
854 if (index != -1)
856 const GSUB_LigatureSet *ls;
857 int k, count;
859 offset = GET_BE_WORD(lsf1->LigatureSet[index]);
860 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
861 count = GET_BE_WORD(ls->LigatureCount);
862 TRACE(" LigatureSet has %i members\n",count);
863 for (k = 0; k < count; k++)
865 const GSUB_Ligature *lig;
866 int CompCount,l,CompIndex;
868 offset = GET_BE_WORD(ls->Ligature[k]);
869 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
870 CompCount = GET_BE_WORD(lig->CompCount) - 1;
871 CompIndex = glyph_index+write_dir;
872 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
874 int CompGlyph;
875 CompGlyph = GET_BE_WORD(lig->Component[l]);
876 if (CompGlyph != glyphs[CompIndex])
877 break;
878 CompIndex += write_dir;
880 if (l == CompCount)
882 int replaceIdx = glyph_index;
883 if (write_dir < 0)
884 replaceIdx = glyph_index - CompCount;
886 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
887 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
888 TRACE("0x%x\n",glyphs[replaceIdx]);
889 if (CompCount > 0)
891 int j;
892 for (j = replaceIdx + 1; j < *glyph_count; j++)
893 glyphs[j] =glyphs[j+CompCount];
894 *glyph_count = *glyph_count - CompCount;
896 return replaceIdx + write_dir;
901 return GSUB_E_NOGLYPH;
904 static INT GSUB_apply_ChainContextSubst(const GSUB_LookupList* lookup, const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
906 int j;
907 BOOL done = FALSE;
909 TRACE("Chaining Contextual Substitution Subtable\n");
910 for (j = 0; j < GET_BE_WORD(look->SubTableCount) && !done; j++)
912 const GSUB_ChainContextSubstFormat1 *ccsf1;
913 int offset;
914 int dirLookahead = write_dir;
915 int dirBacktrack = -1 * write_dir;
917 offset = GET_BE_WORD(look->SubTable[j]);
918 ccsf1 = (const GSUB_ChainContextSubstFormat1*)((const BYTE*)look+offset);
919 if (GET_BE_WORD(ccsf1->SubstFormat) == 1)
921 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
922 continue;
924 else if (GET_BE_WORD(ccsf1->SubstFormat) == 2)
926 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
927 continue;
929 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
931 int k;
932 int indexGlyphs;
933 const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
934 const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
935 const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
936 const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
937 int newIndex = glyph_index;
939 ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
941 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
943 for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
945 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
946 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
947 break;
949 if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
950 continue;
951 TRACE("Matched Backtrack\n");
953 ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)(((LPBYTE)ccsf1)+sizeof(GSUB_ChainContextSubstFormat3_1) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)-1)));
955 indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
956 for (k = 0; k < indexGlyphs; k++)
958 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
959 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
960 break;
962 if (k != indexGlyphs)
963 continue;
964 TRACE("Matched IndexGlyphs\n");
966 ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)(((LPBYTE)ccsf3_2)+sizeof(GSUB_ChainContextSubstFormat3_2) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_2->InputGlyphCount)-1)));
968 for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
970 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
971 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
972 break;
974 if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
975 continue;
976 TRACE("Matched LookAhead\n");
978 ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)(((LPBYTE)ccsf3_3)+sizeof(GSUB_ChainContextSubstFormat3_3) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)-1)));
980 if (GET_BE_WORD(ccsf3_4->SubstCount))
982 for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
984 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
985 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
987 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
988 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
989 if (newIndex == -1)
991 ERR("Chain failed to generate a glyph\n");
992 continue;
995 return newIndex;
997 else return GSUB_E_NOGLYPH;
1000 return -1;
1003 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1005 int offset;
1006 const GSUB_LookupTable *look;
1008 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
1009 look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
1010 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
1011 switch(GET_BE_WORD(look->LookupType))
1013 case 1:
1014 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1015 case 2:
1016 return GSUB_apply_MultipleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1017 case 3:
1018 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1019 case 4:
1020 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1021 case 6:
1022 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
1023 default:
1024 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
1026 return GSUB_E_NOGLYPH;
1029 static INT apply_GSUB_lookup(LPCVOID table, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1031 const GSUB_Header *header = (const GSUB_Header *)table;
1032 const GSUB_LookupList *lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1034 return GSUB_apply_lookup(lookup, lookup_index, glyphs, glyph_index, write_dir, glyph_count);
1037 static INT GSUB_apply_feature_all_lookups(LPCVOID header, LoadedFeature *feature, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1039 int i;
1040 int out_index = GSUB_E_NOGLYPH;
1042 TRACE("%i lookups\n", feature->lookup_count);
1043 for (i = 0; i < feature->lookup_count; i++)
1045 out_index = apply_GSUB_lookup(header, feature->lookups[i], glyphs, glyph_index, write_dir, glyph_count);
1046 if (out_index != GSUB_E_NOGLYPH)
1047 break;
1049 if (out_index == GSUB_E_NOGLYPH)
1050 TRACE("lookups found no glyphs\n");
1051 else
1053 int out2;
1054 out2 = GSUB_apply_feature_all_lookups(header, feature, glyphs, glyph_index, write_dir, glyph_count);
1055 if (out2!=GSUB_E_NOGLYPH)
1056 out_index = out2;
1058 return out_index;
1061 static OPENTYPE_TAG get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, BOOL tryNew)
1063 UINT charset;
1065 if (psc->userScript != 0)
1067 if (tryNew && ShapingData[psa->eScript].newOtTag != 0 && psc->userScript == scriptInformation[psa->eScript].scriptTag)
1068 return ShapingData[psa->eScript].newOtTag;
1069 else
1070 return psc->userScript;
1073 if (tryNew && ShapingData[psa->eScript].newOtTag != 0)
1074 return ShapingData[psa->eScript].newOtTag;
1076 if (scriptInformation[psa->eScript].scriptTag)
1077 return scriptInformation[psa->eScript].scriptTag;
1080 * fall back to the font charset
1082 charset = GetTextCharsetInfo(hdc, NULL, 0x0);
1083 switch (charset)
1085 case ANSI_CHARSET:
1086 case BALTIC_CHARSET: return MS_MAKE_TAG('l','a','t','n');
1087 case CHINESEBIG5_CHARSET: return MS_MAKE_TAG('h','a','n','i');
1088 case EASTEUROPE_CHARSET: return MS_MAKE_TAG('l','a','t','n'); /* ?? */
1089 case GB2312_CHARSET: return MS_MAKE_TAG('h','a','n','i');
1090 case GREEK_CHARSET: return MS_MAKE_TAG('g','r','e','k');
1091 case HANGUL_CHARSET: return MS_MAKE_TAG('h','a','n','g');
1092 case RUSSIAN_CHARSET: return MS_MAKE_TAG('c','y','r','l');
1093 case SHIFTJIS_CHARSET: return MS_MAKE_TAG('k','a','n','a');
1094 case TURKISH_CHARSET: return MS_MAKE_TAG('l','a','t','n'); /* ?? */
1095 case VIETNAMESE_CHARSET: return MS_MAKE_TAG('l','a','t','n');
1096 case JOHAB_CHARSET: return MS_MAKE_TAG('l','a','t','n'); /* ?? */
1097 case ARABIC_CHARSET: return MS_MAKE_TAG('a','r','a','b');
1098 case HEBREW_CHARSET: return MS_MAKE_TAG('h','e','b','r');
1099 case THAI_CHARSET: return MS_MAKE_TAG('t','h','a','i');
1100 default: return MS_MAKE_TAG('l','a','t','n');
1104 static LoadedFeature* load_GSUB_feature(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, const char* feat)
1106 LoadedFeature *feature = NULL;
1108 if (psc->GSUB_Table)
1110 int attempt = 2;
1111 OPENTYPE_TAG tags;
1112 OPENTYPE_TAG language;
1113 OPENTYPE_TAG script;
1114 int cTags;
1118 script = get_opentype_script(hdc,psa,psc,(attempt==2));
1119 if (psc->userLang != 0)
1120 language = psc->userLang;
1121 else
1122 language = MS_MAKE_TAG('d','f','l','t');
1123 attempt--;
1125 GSUB_GetFontFeatureTags(psc, script, language, FALSE, MS_MAKE_TAG(feat[0],feat[1],feat[2],feat[3]), 1, &tags, &cTags, &feature);
1127 } while(attempt && !feature);
1129 /* try in the default (latin) table */
1130 if (!feature)
1131 GSUB_GetFontFeatureTags(psc, MS_MAKE_TAG('l','a','t','n'), MS_MAKE_TAG('d','f','l','t'), FALSE, MS_MAKE_TAG(feat[0],feat[1],feat[2],feat[3]), 1, &tags, &cTags, &feature);
1134 TRACE("Feature %s located at %p\n",debugstr_an(feat,4),feature);
1135 return feature;
1138 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)
1140 LoadedFeature *feature;
1142 feature = load_GSUB_feature(hdc, psa, psc, feat);
1143 if (!feature)
1144 return GSUB_E_NOFEATURE;
1146 TRACE("applying feature %s\n",feat);
1147 return GSUB_apply_feature_all_lookups(psc->GSUB_Table, feature, glyphs, index, write_dir, glyph_count);
1150 static VOID *load_gsub_table(HDC hdc)
1152 VOID* GSUB_Table = NULL;
1153 int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
1154 if (length != GDI_ERROR)
1156 GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
1157 GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
1158 TRACE("Loaded GSUB table of %i bytes\n",length);
1160 return GSUB_Table;
1163 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)
1165 WORD *glyphs;
1166 INT glyph_count = count;
1167 INT rc;
1169 glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD)*(count*2));
1170 GetGlyphIndicesW(hdc, chars, count, glyphs, 0);
1171 rc = apply_GSUB_feature_to_glyph(hdc, psa, psc, glyphs, 0, write_dir, &glyph_count, feature);
1172 if (rc > GSUB_E_NOGLYPH)
1173 rc = count - glyph_count;
1174 else
1175 rc = 0;
1177 HeapFree(GetProcessHeap(),0,glyphs);
1178 return rc;
1181 static void UpdateClustersFromGlyphProp(const int cGlyphs, const int cChars, WORD* pwLogClust, SCRIPT_GLYPHPROP *pGlyphProp)
1183 int i;
1185 for (i = 0; i < cGlyphs; i++)
1187 if (!pGlyphProp[i].sva.fClusterStart)
1189 int j;
1190 for (j = 0; j < cChars; j++)
1192 if (pwLogClust[j] == i)
1194 int k = j;
1195 while (!pGlyphProp[pwLogClust[k]].sva.fClusterStart && k >= 0 && k <cChars)
1196 k-=1;
1197 if (pGlyphProp[pwLogClust[k]].sva.fClusterStart)
1198 pwLogClust[j] = pwLogClust[k];
1205 static void UpdateClusters(int nextIndex, int changeCount, int write_dir, int chars, WORD* pwLogClust )
1207 if (changeCount == 0)
1208 return;
1209 else
1211 int i;
1212 int target_glyph = nextIndex - write_dir;
1213 int seeking_glyph;
1214 int target_index = -1;
1215 int replacing_glyph = -1;
1216 int changed = 0;
1217 int top_logclust = 0;
1219 if (changeCount > 0)
1221 if (write_dir > 0)
1222 target_glyph = nextIndex - changeCount;
1223 else
1224 target_glyph = nextIndex + (changeCount + 1);
1227 seeking_glyph = target_glyph;
1228 for (i = 0; i < chars; i++)
1229 if (pwLogClust[i] > top_logclust)
1230 top_logclust = pwLogClust[i];
1232 do {
1233 if (write_dir > 0)
1234 for (i = 0; i < chars; i++)
1236 if (pwLogClust[i] == seeking_glyph)
1238 target_index = i;
1239 break;
1242 else
1243 for (i = chars - 1; i >= 0; i--)
1245 if (pwLogClust[i] == seeking_glyph)
1247 target_index = i;
1248 break;
1251 if (target_index == -1)
1252 seeking_glyph ++;
1254 while (target_index == -1 && seeking_glyph <= top_logclust);
1256 if (target_index == -1)
1258 ERR("Unable to find target glyph\n");
1259 return;
1262 if (changeCount < 0)
1264 /* merge glyphs */
1265 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1267 if (pwLogClust[i] == target_glyph)
1268 continue;
1269 if(pwLogClust[i] == replacing_glyph)
1270 pwLogClust[i] = target_glyph;
1271 else
1273 changed--;
1274 if (changed >= changeCount)
1276 replacing_glyph = pwLogClust[i];
1277 pwLogClust[i] = target_glyph;
1279 else
1280 break;
1284 /* renumber trailing indexes*/
1285 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1287 if (pwLogClust[i] != target_glyph)
1288 pwLogClust[i] += changeCount;
1291 else
1293 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1294 pwLogClust[i] += changeCount;
1299 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 )
1301 if (psc->GSUB_Table)
1303 LoadedFeature *feature;
1304 int lookup_index;
1306 feature = load_GSUB_feature(hdc, psa, psc, feat);
1307 if (!feature)
1308 return GSUB_E_NOFEATURE;
1310 TRACE("applying feature %s: %i lookups\n",debugstr_an(feat,4),feature->lookup_count);
1311 for (lookup_index = 0; lookup_index < feature->lookup_count; lookup_index++)
1313 int i;
1315 if (write_dir > 0)
1316 i = 0;
1317 else
1318 i = *pcGlyphs-1;
1319 TRACE("applying lookup (%i/%i)\n",lookup_index,feature->lookup_count);
1320 while(i < *pcGlyphs && i >= 0)
1322 INT nextIndex;
1323 INT prevCount = *pcGlyphs;
1325 nextIndex = apply_GSUB_lookup(psc->GSUB_Table, feature->lookups[lookup_index], pwOutGlyphs, i, write_dir, pcGlyphs);
1326 if (*pcGlyphs != prevCount)
1328 UpdateClusters(nextIndex, *pcGlyphs - prevCount, write_dir, cChars, pwLogClust);
1329 i = nextIndex;
1331 else
1332 i+=write_dir;
1335 return *pcGlyphs;
1337 return GSUB_E_NOFEATURE;
1340 static inline BOOL get_GSUB_Indic2(SCRIPT_ANALYSIS *psa, ScriptCache *psc)
1342 OPENTYPE_TAG tag;
1343 HRESULT hr;
1344 int count = 0;
1346 hr = GSUB_GetFontScriptTags(psc, ShapingData[psa->eScript].newOtTag, 1, &tag, &count, NULL);
1348 return(SUCCEEDED(hr));
1351 static WCHAR neighbour_char(int i, int delta, const WCHAR* chars, INT cchLen)
1353 if (i + delta < 0)
1354 return 0;
1355 if ( i+ delta >= cchLen)
1356 return 0;
1358 i += delta;
1360 return chars[i];
1363 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
1365 if (i + delta < 0)
1367 if (psa->fLinkBefore)
1368 return jtR;
1369 else
1370 return jtU;
1372 if ( i+ delta >= cchLen)
1374 if (psa->fLinkAfter)
1375 return jtL;
1376 else
1377 return jtU;
1380 i += delta;
1382 if (context_type[i] == jtT)
1383 return neighbour_joining_type(i,delta,context_type,cchLen,psa);
1384 else
1385 return context_type[i];
1388 static inline BOOL right_join_causing(CHAR joining_type)
1390 return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
1393 static inline BOOL left_join_causing(CHAR joining_type)
1395 return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
1398 static inline BOOL word_break_causing(WCHAR chr)
1400 /* we are working within a string of characters already guareented to
1401 be within one script, Syriac, so we do not worry about any character
1402 other than the space character outside of that range */
1403 return (chr == 0 || chr == 0x20 );
1407 * ContextualShape_Arabic
1409 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1411 CHAR *context_type;
1412 INT *context_shape;
1413 INT dirR, dirL;
1414 int i;
1416 if (*pcGlyphs != cChars)
1418 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1419 return;
1422 if (!psa->fLogicalOrder && psa->fRTL)
1424 dirR = 1;
1425 dirL = -1;
1427 else
1429 dirR = -1;
1430 dirL = 1;
1433 if (!psc->GSUB_Table)
1434 psc->GSUB_Table = load_gsub_table(hdc);
1436 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1437 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1439 for (i = 0; i < cChars; i++)
1440 context_type[i] = get_table_entry( wine_shaping_table, pwcChars[i] );
1442 for (i = 0; i < cChars; i++)
1444 if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1445 context_shape[i] = Xr;
1446 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1447 context_shape[i] = Xl;
1448 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)))
1449 context_shape[i] = Xm;
1450 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1451 context_shape[i] = Xr;
1452 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1453 context_shape[i] = Xl;
1454 else
1455 context_shape[i] = Xn;
1458 /* Contextual Shaping */
1459 i = 0;
1460 while(i < *pcGlyphs)
1462 BOOL shaped = FALSE;
1464 if (psc->GSUB_Table)
1466 INT nextIndex;
1467 INT prevCount = *pcGlyphs;
1468 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1469 if (nextIndex > GSUB_E_NOGLYPH)
1471 i = nextIndex;
1472 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1474 shaped = (nextIndex > GSUB_E_NOGLYPH);
1477 if (!shaped)
1479 if (context_shape[i] == Xn)
1481 WORD newGlyph = pwOutGlyphs[i];
1482 if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
1484 /* fall back to presentation form B */
1485 WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
1486 if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
1487 pwOutGlyphs[i] = newGlyph;
1490 i++;
1494 HeapFree(GetProcessHeap(),0,context_shape);
1495 HeapFree(GetProcessHeap(),0,context_type);
1499 * ContextualShape_Syriac
1502 #define ALAPH 0x710
1503 #define DALATH 0x715
1504 #define RISH 0x72A
1506 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1508 CHAR *context_type;
1509 INT *context_shape;
1510 INT dirR, dirL;
1511 int i;
1513 if (*pcGlyphs != cChars)
1515 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1516 return;
1519 if (!psa->fLogicalOrder && psa->fRTL)
1521 dirR = 1;
1522 dirL = -1;
1524 else
1526 dirR = -1;
1527 dirL = 1;
1530 if (!psc->GSUB_Table)
1531 psc->GSUB_Table = load_gsub_table(hdc);
1533 if (!psc->GSUB_Table)
1534 return;
1536 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1537 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1539 for (i = 0; i < cChars; i++)
1540 context_type[i] = get_table_entry( wine_shaping_table, pwcChars[i] );
1542 for (i = 0; i < cChars; i++)
1544 if (pwcChars[i] == ALAPH)
1546 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1548 if (left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1549 context_shape[i] = Afj;
1550 else if ( rchar != DALATH && rchar != RISH &&
1551 !left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) &&
1552 word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1553 context_shape[i] = Afn;
1554 else if ( (rchar == DALATH || rchar == RISH) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1555 context_shape[i] = Afx;
1556 else
1557 context_shape[i] = Xn;
1559 else if (context_type[i] == jtR &&
1560 right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1561 context_shape[i] = Xr;
1562 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1563 context_shape[i] = Xl;
1564 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)))
1565 context_shape[i] = Xm;
1566 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1567 context_shape[i] = Xr;
1568 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1569 context_shape[i] = Xl;
1570 else
1571 context_shape[i] = Xn;
1574 /* Contextual Shaping */
1575 i = 0;
1576 while(i < *pcGlyphs)
1578 INT nextIndex;
1579 INT prevCount = *pcGlyphs;
1580 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1581 if (nextIndex > GSUB_E_NOGLYPH)
1583 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1584 i = nextIndex;
1586 else
1587 i++;
1590 HeapFree(GetProcessHeap(),0,context_shape);
1591 HeapFree(GetProcessHeap(),0,context_type);
1595 * ContextualShape_Phags_pa
1598 #define phags_pa_CANDRABINDU 0xA873
1599 #define phags_pa_START 0xA840
1600 #define phags_pa_END 0xA87F
1602 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1604 INT *context_shape;
1605 INT dirR, dirL;
1606 int i;
1608 if (*pcGlyphs != cChars)
1610 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1611 return;
1614 if (!psa->fLogicalOrder && psa->fRTL)
1616 dirR = 1;
1617 dirL = -1;
1619 else
1621 dirR = -1;
1622 dirL = 1;
1625 if (!psc->GSUB_Table)
1626 psc->GSUB_Table = load_gsub_table(hdc);
1628 if (!psc->GSUB_Table)
1629 return;
1631 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1633 for (i = 0; i < cChars; i++)
1635 if (pwcChars[i] >= phags_pa_START && pwcChars[i] <= phags_pa_END)
1637 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1638 WCHAR lchar = neighbour_char(i,dirL,pwcChars,cChars);
1639 BOOL jrchar = (rchar != phags_pa_CANDRABINDU && rchar >= phags_pa_START && rchar <= phags_pa_END);
1640 BOOL jlchar = (lchar != phags_pa_CANDRABINDU && lchar >= phags_pa_START && lchar <= phags_pa_END);
1642 if (jrchar && jlchar)
1643 context_shape[i] = Xm;
1644 else if (jrchar)
1645 context_shape[i] = Xr;
1646 else if (jlchar)
1647 context_shape[i] = Xl;
1648 else
1649 context_shape[i] = Xn;
1651 else
1652 context_shape[i] = -1;
1655 /* Contextual Shaping */
1656 i = 0;
1657 while(i < *pcGlyphs)
1659 if (context_shape[i] >= 0)
1661 INT nextIndex;
1662 INT prevCount = *pcGlyphs;
1663 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1664 if (nextIndex > GSUB_E_NOGLYPH)
1666 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1667 i = nextIndex;
1669 else
1670 i++;
1672 else
1673 i++;
1676 HeapFree(GetProcessHeap(),0,context_shape);
1679 static void ReplaceInsertChars(HDC hdc, INT cWalk, INT* pcChars, WCHAR *pwOutChars, const WCHAR *replacements)
1681 int i;
1683 /* Replace */
1684 pwOutChars[cWalk] = replacements[0];
1685 cWalk=cWalk+1;
1687 /* Insert */
1688 for (i = 1; replacements[i] != 0x0000 && i < 3; i++)
1690 int j;
1691 for (j = *pcChars; j > cWalk; j--)
1692 pwOutChars[j] = pwOutChars[j-1];
1693 *pcChars= *pcChars+1;
1694 pwOutChars[cWalk] = replacements[i];
1695 cWalk = cWalk+1;
1699 static void DecomposeVowels(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const VowelComponents vowels[], WORD* pwLogClust, INT cChars)
1701 int i;
1702 int cWalk;
1704 for (cWalk = 0; cWalk < *pcChars; cWalk++)
1706 for (i = 0; vowels[i].base != 0x0; i++)
1708 if (pwOutChars[cWalk] == vowels[i].base)
1710 int o = 0;
1711 ReplaceInsertChars(hdc, cWalk, pcChars, pwOutChars, vowels[i].parts);
1712 if (vowels[i].parts[1]) { cWalk++; o++; }
1713 if (vowels[i].parts[2]) { cWalk++; o++; }
1714 UpdateClusters(cWalk, o, 1, cChars, pwLogClust);
1715 break;
1721 static void ComposeConsonants(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const ConsonantComponents consonants[], WORD* pwLogClust)
1723 int i;
1724 int offset = 0;
1725 int cWalk;
1727 for (cWalk = 0; cWalk < *pcChars; cWalk++)
1729 for (i = 0; consonants[i].output!= 0x0; i++)
1731 int j;
1732 for (j = 0; j + cWalk < *pcChars && consonants[i].parts[j]!=0x0; j++)
1733 if (pwOutChars[cWalk+j] != consonants[i].parts[j])
1734 break;
1736 if (consonants[i].parts[j]==0x0) /* matched all */
1738 int k;
1739 j--;
1740 pwOutChars[cWalk] = consonants[i].output;
1741 for(k = cWalk+1; k < *pcChars - j; k++)
1742 pwOutChars[k] = pwOutChars[k+j];
1743 *pcChars = *pcChars - j;
1744 for (k = j ; k > 0; k--)
1745 pwLogClust[cWalk + k + offset] = pwLogClust[cWalk + offset];
1746 offset += j;
1747 for (k = cWalk + j + offset; k < *pcChars + offset; k++)
1748 pwLogClust[k]--;
1749 break;
1752 cWalk++;
1756 static void Reorder_Ra_follows_base(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1758 if (s->ralf >= 0)
1760 int j;
1761 WORD Ra = pwChar[s->start];
1762 WORD H = pwChar[s->start+1];
1764 TRACE("Doing reorder of Ra to %i\n",s->base);
1765 for (j = s->start; j < s->base-1; j++)
1766 pwChar[j] = pwChar[j+2];
1767 pwChar[s->base-1] = Ra;
1768 pwChar[s->base] = H;
1770 s->ralf = s->base-1;
1771 s->base -= 2;
1775 static void Reorder_Ra_follows_matra(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1777 if (s->ralf >= 0)
1779 int j,loc;
1780 int stop = (s->blwf >=0)? s->blwf+1 : s->base;
1781 WORD Ra = pwChar[s->start];
1782 WORD H = pwChar[s->start+1];
1783 for (loc = s->end; loc > stop; loc--)
1784 if (lexical(pwChar[loc]) == lex_Matra_post || lexical(pwChar[loc]) == lex_Matra_below)
1785 break;
1787 TRACE("Doing reorder of Ra to %i\n",loc);
1788 for (j = s->start; j < loc-1; j++)
1789 pwChar[j] = pwChar[j+2];
1790 pwChar[loc-1] = Ra;
1791 pwChar[loc] = H;
1793 s->ralf = loc-1;
1794 s->base -= 2;
1795 if (s->blwf >= 0) s->blwf -= 2;
1796 if (s->pref >= 0) s->pref -= 2;
1800 static void Reorder_Ra_follows_syllable(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1802 if (s->ralf >= 0)
1804 int j;
1805 WORD Ra = pwChar[s->start];
1806 WORD H = pwChar[s->start+1];
1808 TRACE("Doing reorder of Ra to %i\n",s->end-1);
1809 for (j = s->start; j < s->end-1; j++)
1810 pwChar[j] = pwChar[j+2];
1811 pwChar[s->end-1] = Ra;
1812 pwChar[s->end] = H;
1814 s->ralf = s->end-1;
1815 s->base -= 2;
1816 if (s->blwf >= 0) s->blwf -= 2;
1817 if (s->pref >= 0) s->pref -= 2;
1821 static void Reorder_Matra_precede_base(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1823 int i;
1825 /* reorder Matras */
1826 if (s->end > s->base)
1828 for (i = 1; i <= s->end-s->base; i++)
1830 if (lexical(pwChar[s->base+i]) == lex_Matra_pre)
1832 int j;
1833 WCHAR c = pwChar[s->base+i];
1834 TRACE("Doing reorder of %x %x\n",c,pwChar[s->base]);
1835 for (j = s->base+i; j > s->base; j--)
1836 pwChar[j] = pwChar[j-1];
1837 pwChar[s->base] = c;
1839 if (s->ralf >= s->base) s->ralf++;
1840 if (s->blwf >= s->base) s->blwf++;
1841 if (s->pref >= s->base) s->pref++;
1842 s->base ++;
1848 static void Reorder_Matra_precede_syllable(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1850 int i;
1852 /* reorder Matras */
1853 if (s->end > s->base)
1855 for (i = 1; i <= s->end-s->base; i++)
1857 if (lexical(pwChar[s->base+i]) == lex_Matra_pre)
1859 int j;
1860 WCHAR c = pwChar[s->base+i];
1861 TRACE("Doing reorder of %x to %i\n",c,s->start);
1862 for (j = s->base+i; j > s->start; j--)
1863 pwChar[j] = pwChar[j-1];
1864 pwChar[s->start] = c;
1866 if (s->ralf >= 0) s->ralf++;
1867 if (s->blwf >= 0) s->blwf++;
1868 if (s->pref >= 0) s->pref++;
1869 s->base ++;
1875 static void SecondReorder_Blwf_follows_matra(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
1877 if (s->blwf >= 0 && g->blwf > g->base)
1879 int j,loc;
1880 int g_offset;
1881 for (loc = s->end; loc > s->blwf; loc--)
1882 if (lexical(pwChar[loc]) == lex_Matra_below || lexical(pwChar[loc]) == lex_Matra_above || lexical(pwChar[loc]) == lex_Matra_post)
1883 break;
1885 g_offset = (loc - s->blwf) - 1;
1887 if (loc != s->blwf)
1889 WORD blwf = glyphs[g->blwf];
1890 TRACE("Doing reorder of Below-base to %i (glyph offset %i)\n",loc,g_offset);
1891 /* do not care about the pwChar array anymore, just the glyphs */
1892 for (j = 0; j < g_offset; j++)
1893 glyphs[g->blwf + j] = glyphs[g->blwf + j + 1];
1894 glyphs[g->blwf + g_offset] = blwf;
1899 static void SecondReorder_Matra_precede_base(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
1901 int i;
1903 /* reorder previously moved Matras to correct position*/
1904 for (i = s->start; i < s->base; i++)
1906 if (lexical(pwChar[i]) == lex_Matra_pre)
1908 int j;
1909 int g_start = g->start + i - s->start;
1910 if (g_start < g->base -1 )
1912 WCHAR og = glyphs[g_start];
1913 TRACE("Doing reorder of matra from %i to %i\n",g_start,g->base);
1914 for (j = g_start; j < g->base-1; j++)
1915 glyphs[j] = glyphs[j+1];
1916 glyphs[g->base-1] = og;
1922 static void SecondReorder_Pref_precede_base(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
1924 if (s->pref >= 0 && g->pref > g->base)
1926 int j;
1927 WCHAR og = glyphs[g->pref];
1928 TRACE("Doing reorder of pref from %i to %i\n",g->pref,g->base);
1929 for (j = g->pref; j > g->base; j--)
1930 glyphs[j] = glyphs[j-1];
1931 glyphs[g->base] = og;
1935 static void Reorder_Like_Sinhala(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1937 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
1938 if (s->start == s->base && s->base == s->end) return;
1939 if (lexical(pwChar[s->base]) == lex_Vowel) return;
1941 Reorder_Ra_follows_base(pwChar, s, lexical);
1942 Reorder_Matra_precede_base(pwChar, s, lexical);
1945 static void Reorder_Like_Devanagari(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1947 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
1948 if (s->start == s->base && s->base == s->end) return;
1949 if (lexical(pwChar[s->base]) == lex_Vowel) return;
1951 Reorder_Ra_follows_matra(pwChar, s, lexical);
1952 Reorder_Matra_precede_syllable(pwChar, s, lexical);
1955 static void Reorder_Like_Bengali(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1957 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
1958 if (s->start == s->base && s->base == s->end) return;
1959 if (lexical(pwChar[s->base]) == lex_Vowel) return;
1961 Reorder_Ra_follows_base(pwChar, s, lexical);
1962 Reorder_Matra_precede_syllable(pwChar, s, lexical);
1965 static void Reorder_Like_Kannada(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1967 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
1968 if (s->start == s->base && s->base == s->end) return;
1969 if (lexical(pwChar[s->base]) == lex_Vowel) return;
1971 Reorder_Ra_follows_syllable(pwChar, s, lexical);
1972 Reorder_Matra_precede_syllable(pwChar, s, lexical);
1975 static void SecondReorder_Like_Telugu(LPWSTR pwChar, IndicSyllable *s, WORD* pwGlyphs, IndicSyllable *g, lexical_function lexical)
1977 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
1978 TRACE("Glyphs (%i..%i..%i)\n",g->start,g->base,g->end);
1979 if (s->start == s->base && s->base == s->end) return;
1980 if (lexical(pwChar[s->base]) == lex_Vowel) return;
1982 SecondReorder_Blwf_follows_matra(pwChar, s, pwGlyphs, g, lexical);
1985 static void SecondReorder_Like_Tamil(LPWSTR pwChar, IndicSyllable *s, WORD* pwGlyphs, IndicSyllable *g, lexical_function lexical)
1987 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
1988 TRACE("Glyphs (%i..%i..%i)\n",g->start,g->base,g->end);
1989 if (s->start == s->base && s->base == s->end) return;
1990 if (lexical(pwChar[s->base]) == lex_Vowel) return;
1992 SecondReorder_Matra_precede_base(pwChar, s, pwGlyphs, g, lexical);
1993 SecondReorder_Pref_precede_base(pwChar, s, pwGlyphs, g, lexical);
1997 static inline void shift_syllable_glyph_indexs(IndicSyllable *glyph_index, INT index, INT shift)
1999 if (shift == 0)
2000 return;
2002 if (glyph_index->start > index)
2003 glyph_index->start += shift;
2004 if (glyph_index->base > index)
2005 glyph_index->base+= shift;
2006 if (glyph_index->end > index)
2007 glyph_index->end+= shift;
2008 if (glyph_index->ralf > index)
2009 glyph_index->ralf+= shift;
2010 if (glyph_index->blwf > index)
2011 glyph_index->blwf+= shift;
2012 if (glyph_index->pref > index)
2013 glyph_index->pref+= shift;
2016 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, LoadedFeature *feature )
2018 int index = glyph_index->start;
2020 if (!feature)
2021 return;
2023 while(index <= glyph_index->end)
2025 INT nextIndex;
2026 INT prevCount = *pcGlyphs;
2027 nextIndex = GSUB_apply_feature_all_lookups(psc->GSUB_Table, feature, pwOutGlyphs, index, 1, pcGlyphs);
2028 if (nextIndex > GSUB_E_NOGLYPH)
2030 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2031 shift_syllable_glyph_indexs(glyph_index,index,*pcGlyphs - prevCount);
2032 index = nextIndex;
2034 else
2035 index++;
2039 static inline INT find_consonant_halant(WCHAR* pwChars, INT index, INT end, lexical_function lexical)
2041 int i = 0;
2042 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)))))
2043 i++;
2044 if (index + i <= end-1)
2045 return index + i;
2046 else
2047 return -1;
2050 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)
2052 INT index, nextIndex;
2053 INT count,g_offset;
2055 count = syllable->base - syllable->start;
2057 g_offset = 0;
2058 index = find_consonant_halant(&pwChars[syllable->start], 0, count, lexical);
2059 while (index >= 0 && index + g_offset < (glyph_index->base - glyph_index->start))
2061 INT prevCount = *pcGlyphs;
2062 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, index+glyph_index->start+g_offset, 1, pcGlyphs, feature);
2063 if (nextIndex > GSUB_E_NOGLYPH)
2065 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2066 shift_syllable_glyph_indexs(glyph_index, index + glyph_index->start + g_offset, (*pcGlyphs - prevCount));
2067 g_offset += (*pcGlyphs - prevCount);
2070 index+=2;
2071 index = find_consonant_halant(&pwChars[syllable->start], index, count, lexical);
2075 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)
2077 INT nextIndex;
2078 INT prevCount = *pcGlyphs;
2080 if (syllable->ralf >= 0)
2082 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, glyph_index->ralf, 1, pcGlyphs, "rphf");
2083 if (nextIndex > GSUB_E_NOGLYPH)
2085 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2086 shift_syllable_glyph_indexs(glyph_index,glyph_index->ralf,*pcGlyphs - prevCount);
2091 static inline INT find_halant_consonant(WCHAR* pwChars, INT index, INT end, lexical_function lexical)
2093 int i = 0;
2094 while (index + i < end-1 && !(lexical(pwChars[index+i]) == lex_Halant &&
2095 ((index + i < end-2 && lexical(pwChars[index+i]) == lex_Nukta && is_consonant(lexical(pwChars[index+i+1]))) ||
2096 is_consonant(lexical(pwChars[index+i+1])))))
2097 i++;
2098 if (index + i <= end-1)
2099 return index+i;
2100 else
2101 return -1;
2104 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)
2106 INT index, nextIndex;
2107 INT count, g_offset=0;
2108 INT ralf = syllable->ralf;
2110 count = syllable->end - syllable->base;
2112 index = find_halant_consonant(&pwChars[syllable->base], 0, count, lexical);
2114 while (index >= 0)
2116 INT prevCount = *pcGlyphs;
2117 if (ralf >=0 && ralf < index)
2119 g_offset--;
2120 ralf = -1;
2123 if (!modern)
2125 WORD g = pwOutGlyphs[index+glyph_index->base+g_offset];
2126 pwOutGlyphs[index+glyph_index->base+g_offset] = pwOutGlyphs[index+glyph_index->base+g_offset+1];
2127 pwOutGlyphs[index+glyph_index->base+g_offset+1] = g;
2130 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, index+glyph_index->base+g_offset, 1, pcGlyphs, feat);
2131 if (nextIndex > GSUB_E_NOGLYPH)
2133 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2134 shift_syllable_glyph_indexs(glyph_index,index+glyph_index->start+g_offset, (*pcGlyphs - prevCount));
2135 g_offset += (*pcGlyphs - prevCount);
2137 else if (!modern)
2139 WORD g = pwOutGlyphs[index+glyph_index->base+g_offset];
2140 pwOutGlyphs[index+glyph_index->base+g_offset] = pwOutGlyphs[index+glyph_index->base+g_offset+1];
2141 pwOutGlyphs[index+glyph_index->base+g_offset+1] = g;
2144 index+=2;
2145 index = find_halant_consonant(&pwChars[syllable->base], index, count, lexical);
2149 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)
2151 int c;
2152 int overall_shift = 0;
2153 LoadedFeature *locl = (modern)?load_GSUB_feature(hdc, psa, psc, "locl"):NULL;
2154 LoadedFeature *nukt = load_GSUB_feature(hdc, psa, psc, "nukt");
2155 LoadedFeature *akhn = load_GSUB_feature(hdc, psa, psc, "akhn");
2156 LoadedFeature *rkrf = (modern)?load_GSUB_feature(hdc, psa, psc, "rkrf"):NULL;
2157 LoadedFeature *pstf = load_GSUB_feature(hdc, psa, psc, "pstf");
2158 LoadedFeature *vatu = (!rkrf)?load_GSUB_feature(hdc, psa, psc, "vatu"):NULL;
2159 LoadedFeature *cjct = (modern)?load_GSUB_feature(hdc, psa, psc, "cjct"):NULL;
2160 BOOL rphf = (load_GSUB_feature(hdc, psa, psc, "rphf") != NULL);
2161 BOOL pref = (load_GSUB_feature(hdc, psa, psc, "pref") != NULL);
2162 BOOL blwf = (load_GSUB_feature(hdc, psa, psc, "blwf") != NULL);
2163 BOOL half = (load_GSUB_feature(hdc, psa, psc, "half") != NULL);
2164 IndicSyllable glyph_indexs;
2166 for (c = 0; c < syllable_count; c++)
2168 int old_end;
2169 memcpy(&glyph_indexs, &syllables[c], sizeof(IndicSyllable));
2170 shift_syllable_glyph_indexs(&glyph_indexs, -1, overall_shift);
2171 old_end = glyph_indexs.end;
2173 if (locl)
2175 TRACE("applying feature locl\n");
2176 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, locl);
2178 if (nukt)
2180 TRACE("applying feature nukt\n");
2181 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, nukt);
2183 if (akhn)
2185 TRACE("applying feature akhn\n");
2186 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, akhn);
2189 if (rphf)
2190 Apply_Indic_Rphf(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs);
2191 if (rkrf)
2193 TRACE("applying feature rkrf\n");
2194 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, rkrf);
2196 if (pref)
2197 Apply_Indic_PostBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, modern, "pref");
2198 if (blwf)
2200 if (!modern)
2201 Apply_Indic_PreBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, "blwf");
2203 Apply_Indic_PostBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, modern, "blwf");
2206 if (half)
2207 Apply_Indic_PreBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, "half");
2208 if (pstf)
2210 TRACE("applying feature pstf\n");
2211 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, pstf);
2213 if (vatu)
2215 TRACE("applying feature vatu\n");
2216 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, vatu);
2218 if (cjct)
2220 TRACE("applying feature cjct\n");
2221 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, cjct);
2224 if (second_reorder)
2225 second_reorder(pwChars, &syllables[c], pwOutGlyphs, &glyph_indexs, lexical);
2227 overall_shift += glyph_indexs.end - old_end;
2231 static inline int unicode_lex(WCHAR c)
2233 int type;
2235 if (!c) return lex_Generic;
2236 if (c == 0x200D) return lex_ZWJ;
2237 if (c == 0x200C) return lex_ZWNJ;
2238 if (c == 0x00A0) return lex_NBSP;
2240 type = get_table_entry( indic_syllabic_table, c );
2242 if ((type & 0x00ff) != 0x0007) type = type & 0x00ff;
2244 switch( type )
2246 case 0x0d07: /* Unknown */
2247 case 0x0e07: /* Unknwon */
2248 default: return lex_Generic;
2249 case 0x0001:
2250 case 0x0002:
2251 case 0x0011:
2252 case 0x0012:
2253 case 0x0013:
2254 case 0x0014: return lex_Modifier;
2255 case 0x0003:
2256 case 0x0009:
2257 case 0x000a:
2258 case 0x000b:
2259 case 0x000d:
2260 case 0x000e:
2261 case 0x000f:
2262 case 0x0010: return lex_Consonant;
2263 case 0x0004: return lex_Nukta;
2264 case 0x0005: return lex_Halant;
2265 case 0x0006:
2266 case 0x0008: return lex_Vowel;
2267 case 0x0007:
2268 case 0x0107: return lex_Matra_post;
2269 case 0x0207:
2270 case 0x0307: return lex_Matra_pre;
2271 case 0x0807:
2272 case 0x0907:
2273 case 0x0a07:
2274 case 0x0b07:
2275 case 0x0c07:
2276 case 0x0407: return lex_Composed_Vowel;
2277 case 0x0507: return lex_Matra_above;
2278 case 0x0607: return lex_Matra_below;
2279 case 0x000c: return lex_Ra;
2283 static int sinhala_lex(WCHAR c)
2285 switch (c)
2287 case 0x0DDA:
2288 case 0x0DDD:
2289 case 0x0DDC:
2290 case 0x0DDE: return lex_Matra_post;
2291 default:
2292 return unicode_lex(c);
2296 static const VowelComponents Sinhala_vowels[] = {
2297 {0x0DDA, {0x0DD9,0x0DDA,0x0}},
2298 {0x0DDC, {0x0DD9,0x0DDC,0x0}},
2299 {0x0DDD, {0x0DD9,0x0DDD,0x0}},
2300 {0x0DDE, {0x0DD9,0x0DDE,0x0}},
2301 {0x0000, {0x0000,0x0000,0x0}}};
2303 static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2305 int cCount = cChars;
2306 int i;
2307 WCHAR *input;
2308 IndicSyllable *syllables = NULL;
2309 int syllable_count = 0;
2311 if (*pcGlyphs != cChars)
2313 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2314 return;
2317 input = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR) * (cChars * 3));
2319 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2321 /* Step 1: Decompose multi part vowels */
2322 DecomposeVowels(hdc, input, &cCount, Sinhala_vowels, pwLogClust, cChars);
2324 TRACE("New double vowel expanded string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2326 /* Step 2: Reorder within Syllables */
2327 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, sinhala_lex, Reorder_Like_Sinhala, TRUE);
2328 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2330 /* Step 3: Strip dangling joiners */
2331 for (i = 0; i < cCount; i++)
2333 if ((input[i] == 0x200D || input[i] == 0x200C) &&
2334 (i == 0 || input[i-1] == 0x0020 || i == cCount-1 || input[i+1] == 0x0020))
2335 input[i] = 0x0020;
2338 /* Step 4: Base Form application to syllables */
2339 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2340 *pcGlyphs = cCount;
2341 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, sinhala_lex, NULL, TRUE);
2343 HeapFree(GetProcessHeap(),0,input);
2344 HeapFree(GetProcessHeap(),0,syllables);
2347 static int devanagari_lex(WCHAR c)
2349 switch (c)
2351 case 0x0930: return lex_Ra;
2352 default:
2353 return unicode_lex(c);
2357 static const ConsonantComponents Devanagari_consonants[] ={
2358 {{0x0928, 0x093C, 0x00000}, 0x0929},
2359 {{0x0930, 0x093C, 0x00000}, 0x0931},
2360 {{0x0933, 0x093C, 0x00000}, 0x0934},
2361 {{0x0915, 0x093C, 0x00000}, 0x0958},
2362 {{0x0916, 0x093C, 0x00000}, 0x0959},
2363 {{0x0917, 0x093C, 0x00000}, 0x095A},
2364 {{0x091C, 0x093C, 0x00000}, 0x095B},
2365 {{0x0921, 0x093C, 0x00000}, 0x095C},
2366 {{0x0922, 0x093C, 0x00000}, 0x095D},
2367 {{0x092B, 0x093C, 0x00000}, 0x095E},
2368 {{0x092F, 0x093C, 0x00000}, 0x095F}};
2370 static void ContextualShape_Devanagari(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2372 int cCount = cChars;
2373 WCHAR *input;
2374 IndicSyllable *syllables = NULL;
2375 int syllable_count = 0;
2376 BOOL modern = get_GSUB_Indic2(psa, psc);
2378 if (*pcGlyphs != cChars)
2380 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2381 return;
2384 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2385 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2387 /* Step 1: Compose Consonant and Nukta */
2388 ComposeConsonants(hdc, input, &cCount, Devanagari_consonants, pwLogClust);
2389 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2391 /* Step 2: Reorder within Syllables */
2392 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, devanagari_lex, Reorder_Like_Devanagari, modern);
2393 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2394 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2395 *pcGlyphs = cCount;
2397 /* Step 3: Base Form application to syllables */
2398 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, devanagari_lex, NULL, modern);
2400 HeapFree(GetProcessHeap(),0,input);
2401 HeapFree(GetProcessHeap(),0,syllables);
2404 static int bengali_lex(WCHAR c)
2406 switch (c)
2408 case 0x09B0: return lex_Ra;
2409 default:
2410 return unicode_lex(c);
2414 static const VowelComponents Bengali_vowels[] = {
2415 {0x09CB, {0x09C7,0x09BE,0x0000}},
2416 {0x09CC, {0x09C7,0x09D7,0x0000}},
2417 {0x0000, {0x0000,0x0000,0x0000}}};
2419 static const ConsonantComponents Bengali_consonants[] = {
2420 {{0x09A4,0x09CD,0x200D}, 0x09CE},
2421 {{0x09A1,0x09BC,0x0000}, 0x09DC},
2422 {{0x09A2,0x09BC,0x0000}, 0x09DD},
2423 {{0x09AF,0x09BC,0x0000}, 0x09DF},
2424 {{0x0000,0x0000,0x0000}, 0x0000}};
2426 static void ContextualShape_Bengali(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2428 int cCount = cChars;
2429 WCHAR *input;
2430 IndicSyllable *syllables = NULL;
2431 int syllable_count = 0;
2432 BOOL modern = get_GSUB_Indic2(psa, psc);
2434 if (*pcGlyphs != cChars)
2436 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2437 return;
2440 input = HeapAlloc(GetProcessHeap(), 0, (cChars * 2) * sizeof(WCHAR));
2441 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2443 /* Step 1: Decompose Vowels and Compose Consonents */
2444 DecomposeVowels(hdc, input, &cCount, Bengali_vowels, pwLogClust, cChars);
2445 ComposeConsonants(hdc, input, &cCount, Bengali_consonants, pwLogClust);
2446 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2448 /* Step 2: Reorder within Syllables */
2449 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, bengali_lex, Reorder_Like_Bengali, modern);
2450 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2451 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2452 *pcGlyphs = cCount;
2454 /* Step 3: Initial form is only applied to the beginning of words */
2455 for (cCount = cCount - 1 ; cCount >= 0; cCount --)
2457 if (cCount == 0 || input[cCount] == 0x0020) /* space */
2459 int index = cCount;
2460 int gCount = 1;
2461 if (index > 0) index++;
2463 apply_GSUB_feature_to_glyph(hdc, psa, psc, &pwOutGlyphs[index], 0, 1, &gCount, "init");
2467 /* Step 4: Base Form application to syllables */
2468 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, bengali_lex, NULL, modern);
2470 HeapFree(GetProcessHeap(),0,input);
2471 HeapFree(GetProcessHeap(),0,syllables);
2474 static int gurmukhi_lex(WCHAR c)
2476 if (c == 0x0A71)
2477 return lex_Modifier;
2478 else
2479 return unicode_lex(c);
2482 static const ConsonantComponents Gurmukhi_consonants[] = {
2483 {{0x0A32,0x0A3C,0x0000}, 0x0A33},
2484 {{0x0A16,0x0A3C,0x0000}, 0x0A59},
2485 {{0x0A17,0x0A3C,0x0000}, 0x0A5A},
2486 {{0x0A1C,0x0A3C,0x0000}, 0x0A5B},
2487 {{0x0A2B,0x0A3C,0x0000}, 0x0A5E},
2488 {{0x0000,0x0000,0x0000}, 0x0000}};
2490 static void ContextualShape_Gurmukhi(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2492 int cCount = cChars;
2493 WCHAR *input;
2494 IndicSyllable *syllables = NULL;
2495 int syllable_count = 0;
2496 BOOL modern = get_GSUB_Indic2(psa, psc);
2498 if (*pcGlyphs != cChars)
2500 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2501 return;
2504 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2505 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2507 /* Step 1: Compose Consonents */
2508 ComposeConsonants(hdc, input, &cCount, Gurmukhi_consonants, pwLogClust);
2509 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2511 /* Step 2: Reorder within Syllables */
2512 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, gurmukhi_lex, Reorder_Like_Bengali, modern);
2513 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2514 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2515 *pcGlyphs = cCount;
2517 /* Step 3: Base Form application to syllables */
2518 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, gurmukhi_lex, NULL, modern);
2520 HeapFree(GetProcessHeap(),0,input);
2521 HeapFree(GetProcessHeap(),0,syllables);
2524 static int gujarati_lex(WCHAR c)
2526 switch (c)
2528 case 0x0AB0: return lex_Ra;
2529 default:
2530 return unicode_lex(c);
2534 static void ContextualShape_Gujarati(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2536 int cCount = cChars;
2537 WCHAR *input;
2538 IndicSyllable *syllables = NULL;
2539 int syllable_count = 0;
2540 BOOL modern = get_GSUB_Indic2(psa, psc);
2542 if (*pcGlyphs != cChars)
2544 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2545 return;
2548 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2549 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2551 /* Step 1: Reorder within Syllables */
2552 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, gujarati_lex, Reorder_Like_Devanagari, modern);
2553 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2554 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2555 *pcGlyphs = cCount;
2557 /* Step 2: Base Form application to syllables */
2558 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, gujarati_lex, NULL, modern);
2560 HeapFree(GetProcessHeap(),0,input);
2561 HeapFree(GetProcessHeap(),0,syllables);
2564 static int oriya_lex(WCHAR c)
2566 switch (c)
2568 case 0x0B30: return lex_Ra;
2569 default:
2570 return unicode_lex(c);
2574 static const VowelComponents Oriya_vowels[] = {
2575 {0x0B48, {0x0B47,0x0B56,0x0000}},
2576 {0x0B4B, {0x0B47,0x0B3E,0x0000}},
2577 {0x0B4C, {0x0B47,0x0B57,0x0000}},
2578 {0x0000, {0x0000,0x0000,0x0000}}};
2580 static const ConsonantComponents Oriya_consonants[] = {
2581 {{0x0B21,0x0B3C,0x0000}, 0x0B5C},
2582 {{0x0B22,0x0B3C,0x0000}, 0x0B5D},
2583 {{0x0000,0x0000,0x0000}, 0x0000}};
2585 static void ContextualShape_Oriya(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2587 int cCount = cChars;
2588 WCHAR *input;
2589 IndicSyllable *syllables = NULL;
2590 int syllable_count = 0;
2591 BOOL modern = get_GSUB_Indic2(psa, psc);
2593 if (*pcGlyphs != cChars)
2595 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2596 return;
2599 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2600 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2602 /* Step 1: Decompose Vowels and Compose Consonents */
2603 DecomposeVowels(hdc, input, &cCount, Oriya_vowels, pwLogClust, cChars);
2604 ComposeConsonants(hdc, input, &cCount, Oriya_consonants, pwLogClust);
2605 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2607 /* Step 2: Reorder within Syllables */
2608 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, oriya_lex, Reorder_Like_Bengali, modern);
2609 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2610 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2611 *pcGlyphs = cCount;
2613 /* Step 3: Base Form application to syllables */
2614 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, oriya_lex, NULL, modern);
2616 HeapFree(GetProcessHeap(),0,input);
2617 HeapFree(GetProcessHeap(),0,syllables);
2620 static int tamil_lex(WCHAR c)
2622 return unicode_lex(c);
2625 static const VowelComponents Tamil_vowels[] = {
2626 {0x0BCA, {0x0BC6,0x0BBE,0x0000}},
2627 {0x0BCB, {0x0BC7,0x0BBE,0x0000}},
2628 {0x0BCB, {0x0BC6,0x0BD7,0x0000}},
2629 {0x0000, {0x0000,0x0000,0x0000}}};
2631 static const ConsonantComponents Tamil_consonants[] = {
2632 {{0x0B92,0x0BD7,0x0000}, 0x0B94},
2633 {{0x0000,0x0000,0x0000}, 0x0000}};
2635 static void ContextualShape_Tamil(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2637 int cCount = cChars;
2638 WCHAR *input;
2639 IndicSyllable *syllables = NULL;
2640 int syllable_count = 0;
2641 BOOL modern = get_GSUB_Indic2(psa, psc);
2643 if (*pcGlyphs != cChars)
2645 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2646 return;
2649 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2650 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2652 /* Step 1: Decompose Vowels and Compose Consonents */
2653 DecomposeVowels(hdc, input, &cCount, Tamil_vowels, pwLogClust, cChars);
2654 ComposeConsonants(hdc, input, &cCount, Tamil_consonants, pwLogClust);
2655 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2657 /* Step 2: Reorder within Syllables */
2658 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, tamil_lex, Reorder_Like_Bengali, modern);
2659 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2660 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2661 *pcGlyphs = cCount;
2663 /* Step 3: Base Form application to syllables */
2664 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, tamil_lex, SecondReorder_Like_Tamil, modern);
2666 HeapFree(GetProcessHeap(),0,input);
2667 HeapFree(GetProcessHeap(),0,syllables);
2670 static int telugu_lex(WCHAR c)
2672 switch (c)
2674 case 0x0C43:
2675 case 0x0C44: return lex_Modifier;
2676 default:
2677 return unicode_lex(c);
2681 static const VowelComponents Telugu_vowels[] = {
2682 {0x0C48, {0x0C46,0x0C56,0x0000}},
2683 {0x0000, {0x0000,0x0000,0x0000}}};
2685 static void ContextualShape_Telugu(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2687 int cCount = cChars;
2688 WCHAR *input;
2689 IndicSyllable *syllables = NULL;
2690 int syllable_count = 0;
2691 BOOL modern = get_GSUB_Indic2(psa, psc);
2693 if (*pcGlyphs != cChars)
2695 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2696 return;
2699 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2700 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2702 /* Step 1: Decompose Vowels */
2703 DecomposeVowels(hdc, input, &cCount, Telugu_vowels, pwLogClust, cChars);
2704 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2706 /* Step 2: Reorder within Syllables */
2707 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, telugu_lex, Reorder_Like_Bengali, modern);
2708 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2709 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2710 *pcGlyphs = cCount;
2712 /* Step 3: Base Form application to syllables */
2713 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, telugu_lex, SecondReorder_Like_Telugu, modern);
2715 HeapFree(GetProcessHeap(),0,input);
2716 HeapFree(GetProcessHeap(),0,syllables);
2719 static int kannada_lex(WCHAR c)
2721 switch (c)
2723 case 0x0CB0: return lex_Ra;
2724 default:
2725 return unicode_lex(c);
2729 static const VowelComponents Kannada_vowels[] = {
2730 {0x0CC0, {0x0CBF,0x0CD5,0x0000}},
2731 {0x0CC7, {0x0CC6,0x0CD5,0x0000}},
2732 {0x0CC8, {0x0CC6,0x0CD6,0x0000}},
2733 {0x0CCA, {0x0CC6,0x0CC2,0x0000}},
2734 {0x0CCB, {0x0CC6,0x0CC2,0x0CD5}},
2735 {0x0000, {0x0000,0x0000,0x0000}}};
2737 static void ContextualShape_Kannada(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2739 int cCount = cChars;
2740 WCHAR *input;
2741 IndicSyllable *syllables = NULL;
2742 int syllable_count = 0;
2743 BOOL modern = get_GSUB_Indic2(psa, psc);
2745 if (*pcGlyphs != cChars)
2747 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2748 return;
2751 input = HeapAlloc(GetProcessHeap(), 0, (cChars*3) * sizeof(WCHAR));
2752 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2754 /* Step 1: Decompose Vowels */
2755 DecomposeVowels(hdc, input, &cCount, Kannada_vowels, pwLogClust, cChars);
2756 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2758 /* Step 2: Reorder within Syllables */
2759 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, kannada_lex, Reorder_Like_Kannada, modern);
2760 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2761 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2762 *pcGlyphs = cCount;
2764 /* Step 3: Base Form application to syllables */
2765 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, kannada_lex, SecondReorder_Like_Telugu, modern);
2767 HeapFree(GetProcessHeap(),0,input);
2768 HeapFree(GetProcessHeap(),0,syllables);
2771 static int malayalam_lex(WCHAR c)
2773 return unicode_lex(c);
2776 static const VowelComponents Malayalam_vowels[] = {
2777 {0x0D4A, {0x0D46,0x0D3E,0x0000}},
2778 {0x0D4B, {0x0D47,0x0D3E,0x0000}},
2779 {0x0D4C, {0x0D46,0x0D57,0x0000}},
2780 {0x0000, {0x0000,0x0000,0x0000}}};
2782 static void ContextualShape_Malayalam(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2784 int cCount = cChars;
2785 WCHAR *input;
2786 IndicSyllable *syllables = NULL;
2787 int syllable_count = 0;
2788 BOOL modern = get_GSUB_Indic2(psa, psc);
2790 if (*pcGlyphs != cChars)
2792 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2793 return;
2796 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2797 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2799 /* Step 1: Decompose Vowels */
2800 DecomposeVowels(hdc, input, &cCount, Malayalam_vowels, pwLogClust, cChars);
2801 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2803 /* Step 2: Reorder within Syllables */
2804 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, malayalam_lex, Reorder_Like_Devanagari, modern);
2805 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2806 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2807 *pcGlyphs = cCount;
2809 /* Step 3: Base Form application to syllables */
2810 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, malayalam_lex, SecondReorder_Like_Tamil, modern);
2812 HeapFree(GetProcessHeap(),0,input);
2813 HeapFree(GetProcessHeap(),0,syllables);
2816 static int khmer_lex(WCHAR c)
2818 return unicode_lex(c);
2821 static void ContextualShape_Khmer(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2823 int cCount = cChars;
2824 WCHAR *input;
2825 IndicSyllable *syllables = NULL;
2826 int syllable_count = 0;
2828 if (*pcGlyphs != cChars)
2830 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2831 return;
2834 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2835 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2837 /* Step 1: Reorder within Syllables */
2838 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, khmer_lex, Reorder_Like_Devanagari, FALSE);
2839 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2840 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2841 *pcGlyphs = cCount;
2843 /* Step 2: Base Form application to syllables */
2844 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, khmer_lex, NULL, FALSE);
2846 HeapFree(GetProcessHeap(),0,input);
2847 HeapFree(GetProcessHeap(),0,syllables);
2850 static inline BOOL mongolian_wordbreak(WCHAR chr)
2852 return ((chr == 0x0020) || (chr == 0x200C) || (chr == 0x202F) || (chr == 0x180E) || (chr == 0x1800) || (chr == 0x1802) || (chr == 0x1803) || (chr == 0x1805) || (chr == 0x1808) || (chr == 0x1809) || (chr == 0x1807));
2855 static void ContextualShape_Mongolian(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2857 INT *context_shape;
2858 INT dirL;
2859 int i;
2861 if (*pcGlyphs != cChars)
2863 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2864 return;
2867 if (!psa->fLogicalOrder && psa->fRTL)
2868 dirL = -1;
2869 else
2870 dirL = 1;
2872 if (!psc->GSUB_Table)
2873 psc->GSUB_Table = load_gsub_table(hdc);
2875 if (!psc->GSUB_Table)
2876 return;
2878 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
2880 for (i = 0; i < cChars; i++)
2882 if (i == 0 || mongolian_wordbreak(pwcChars[i-1]))
2884 if ((i == cChars-1) || mongolian_wordbreak(pwcChars[i+1]))
2885 context_shape[i] = Xn;
2886 else
2887 context_shape[i] = Xl;
2889 else
2891 if ((i == cChars-1) || mongolian_wordbreak(pwcChars[i+1]))
2892 context_shape[i] = Xr;
2893 else
2894 context_shape[i] = Xm;
2898 /* Contextual Shaping */
2899 i = 0;
2900 while(i < *pcGlyphs)
2902 INT nextIndex;
2903 INT prevCount = *pcGlyphs;
2904 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
2905 if (nextIndex > GSUB_E_NOGLYPH)
2907 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
2908 i = nextIndex;
2910 else
2911 i++;
2914 HeapFree(GetProcessHeap(),0,context_shape);
2917 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)
2919 int i,k;
2921 for (i = 0; i < cGlyphs; i++)
2923 int char_index[20];
2924 int char_count = 0;
2926 for (k = 0; k < cChars; k++)
2928 if (pwLogClust[k] == i)
2930 char_index[char_count] = k;
2931 char_count++;
2935 if (char_count == 0)
2936 continue;
2938 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
2940 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
2941 pCharProp[char_index[0]].fCanGlyphAlone = 1;
2943 else
2944 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
2947 OpenType_GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
2948 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
2951 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 )
2953 int i,k;
2954 int initGlyph, finaGlyph;
2955 INT dirR, dirL;
2956 BYTE *spaces;
2958 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
2959 memset(spaces,0,cGlyphs);
2961 if (!psa->fLogicalOrder && psa->fRTL)
2963 initGlyph = cGlyphs-1;
2964 finaGlyph = 0;
2965 dirR = 1;
2966 dirL = -1;
2968 else
2970 initGlyph = 0;
2971 finaGlyph = cGlyphs-1;
2972 dirR = -1;
2973 dirL = 1;
2976 for (i = 0; i < cGlyphs; i++)
2978 for (k = 0; k < cChars; k++)
2979 if (pwLogClust[k] == i)
2981 if (pwcChars[k] == 0x0020)
2982 spaces[i] = 1;
2986 for (i = 0; i < cGlyphs; i++)
2988 int char_index[20];
2989 int char_count = 0;
2990 BOOL isInit, isFinal;
2992 for (k = 0; k < cChars; k++)
2994 if (pwLogClust[k] == i)
2996 char_index[char_count] = k;
2997 char_count++;
3001 isInit = (i == initGlyph || (i+dirR > 0 && i+dirR < cGlyphs && spaces[i+dirR]));
3002 isFinal = (i == finaGlyph || (i+dirL > 0 && i+dirL < cGlyphs && spaces[i+dirL]));
3004 if (char_count == 0)
3005 continue;
3007 if (char_count == 1)
3009 if (pwcChars[char_index[0]] == 0x0020) /* space */
3011 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BLANK;
3012 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3014 else if (pwcChars[char_index[0]] == 0x0640) /* kashida */
3015 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
3016 else if (pwcChars[char_index[0]] == 0x0633) /* SEEN */
3018 if (!isInit && !isFinal)
3019 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN_M;
3020 else if (isInit)
3021 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN;
3022 else
3023 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3025 else if (!isInit)
3027 if (pwcChars[char_index[0]] == 0x0628 ) /* BA */
3028 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BA;
3029 else if (pwcChars[char_index[0]] == 0x0631 ) /* RA */
3030 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_RA;
3031 else if (pwcChars[char_index[0]] == 0x0647 ) /* HA */
3032 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_HA;
3033 else if ((pwcChars[char_index[0]] == 0x0627 || pwcChars[char_index[0]] == 0x0625 || pwcChars[char_index[0]] == 0x0623 || pwcChars[char_index[0]] == 0x0622) ) /* alef-like */
3034 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_ALEF;
3035 else
3036 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3038 else if (!isInit && !isFinal)
3039 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3040 else
3041 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3043 else if (char_count == 2)
3045 if ((pwcChars[char_index[0]] == 0x0628 && pwcChars[char_index[1]]== 0x0631) || (pwcChars[char_index[0]] == 0x0631 && pwcChars[char_index[1]]== 0x0628)) /* BA+RA */
3046 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BARA;
3047 else if (!isInit)
3048 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3049 else
3050 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3052 else if (!isInit && !isFinal)
3053 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3054 else
3055 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3058 OpenType_GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3059 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3060 HeapFree(GetProcessHeap(),0,spaces);
3063 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 )
3065 int i,k;
3066 int finaGlyph;
3067 INT dirL;
3068 BYTE *spaces;
3070 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
3071 memset(spaces,0,cGlyphs);
3073 if (!psa->fLogicalOrder && psa->fRTL)
3075 finaGlyph = 0;
3076 dirL = -1;
3078 else
3080 finaGlyph = cGlyphs-1;
3081 dirL = 1;
3084 for (i = 0; i < cGlyphs; i++)
3086 for (k = 0; k < cChars; k++)
3087 if (pwLogClust[k] == i)
3089 if (pwcChars[k] == 0x0020)
3090 spaces[i] = 1;
3094 OpenType_GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3096 for (i = 0; i < cGlyphs; i++)
3098 int char_index[20];
3099 int char_count = 0;
3101 for (k = 0; k < cChars; k++)
3103 if (pwLogClust[k] == i)
3105 char_index[char_count] = k;
3106 char_count++;
3110 if (char_count == 0)
3111 continue;
3113 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3115 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3116 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3118 else if (i == finaGlyph)
3119 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3120 else
3121 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3123 /* handle Thai SARA AM (U+0E33) differently than GDEF */
3124 if (char_count == 1 && pwcChars[char_index[0]] == 0x0e33)
3125 pGlyphProp[i].sva.fClusterStart = 0;
3128 HeapFree(GetProcessHeap(),0,spaces);
3129 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3131 /* Do not allow justification between marks and their base */
3132 for (i = 0; i < cGlyphs; i++)
3134 if (!pGlyphProp[i].sva.fClusterStart)
3135 pGlyphProp[i-dirL].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3139 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)
3141 int i,k;
3143 for (i = 0; i < cGlyphs; i++)
3145 int char_index[20];
3146 int char_count = 0;
3148 for (k = 0; k < cChars; k++)
3150 if (pwLogClust[k] == i)
3152 char_index[char_count] = k;
3153 char_count++;
3157 if (char_count == 0)
3158 continue;
3160 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3162 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3163 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3165 else
3166 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3168 OpenType_GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3169 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3172 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)
3174 int i,k;
3176 for (i = 0; i < cGlyphs; i++)
3178 int char_index[20];
3179 int char_count = 0;
3181 for (k = 0; k < cChars; k++)
3183 if (pwLogClust[k] == i)
3185 char_index[char_count] = k;
3186 char_count++;
3190 if (char_count == 0)
3191 continue;
3193 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3195 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3196 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3198 else
3199 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3201 OpenType_GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3202 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3204 /* Tibeten script does not set sva.fDiacritic or sva.fZeroWidth */
3205 for (i = 0; i < cGlyphs; i++)
3207 if (!pGlyphProp[i].sva.fClusterStart)
3209 pGlyphProp[i].sva.fDiacritic = 0;
3210 pGlyphProp[i].sva.fZeroWidth = 0;
3215 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)
3217 int i,k;
3219 OpenType_GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3220 for (i = 0; i < cGlyphs; i++)
3222 int char_index[20];
3223 int char_count = 0;
3225 for (k = 0; k < cChars; k++)
3227 if (pwLogClust[k] == i)
3229 char_index[char_count] = k;
3230 char_count++;
3234 if (override_gsub)
3236 /* Most indic scripts do not set fDiacritic or fZeroWidth */
3237 pGlyphProp[i].sva.fDiacritic = FALSE;
3238 pGlyphProp[i].sva.fZeroWidth = FALSE;
3241 if (char_count == 0)
3242 continue;
3244 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3246 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3247 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3249 else
3250 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3252 pGlyphProp[i].sva.fClusterStart = 0;
3253 for (k = 0; k < char_count && !pGlyphProp[i].sva.fClusterStart; k++)
3254 switch (lexical(pwcChars[char_index[k]]))
3256 case lex_Matra_pre:
3257 case lex_Matra_post:
3258 case lex_Matra_above:
3259 case lex_Matra_below:
3260 case lex_Modifier:
3261 case lex_Halant:
3262 break;
3263 case lex_ZWJ:
3264 case lex_ZWNJ:
3265 /* check for dangling joiners */
3266 if (pwcChars[char_index[k]-1] == 0x0020 || pwcChars[char_index[k]+1] == 0x0020)
3267 pGlyphProp[i].sva.fClusterStart = 1;
3268 else
3269 k = char_count;
3270 break;
3271 default:
3272 pGlyphProp[i].sva.fClusterStart = 1;
3273 break;
3277 if (use_syllables)
3279 IndicSyllable *syllables = NULL;
3280 int syllable_count = 0;
3281 BOOL modern = get_GSUB_Indic2(psa, psc);
3283 Indic_ParseSyllables( hdc, psa, psc, pwcChars, cChars, &syllables, &syllable_count, lexical, modern);
3285 for (i = 0; i < syllable_count; i++)
3287 int j;
3288 WORD g = pwLogClust[syllables[i].start];
3289 for (j = syllables[i].start+1; j <= syllables[i].end; j++)
3291 if (pwLogClust[j] != g)
3293 pGlyphProp[pwLogClust[j]].sva.fClusterStart = 0;
3294 pwLogClust[j] = g;
3299 HeapFree(GetProcessHeap(), 0, syllables);
3302 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3305 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 )
3307 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, sinhala_lex, FALSE, FALSE);
3310 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 )
3312 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, devanagari_lex, TRUE, TRUE);
3315 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 )
3317 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, bengali_lex, TRUE, TRUE);
3320 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 )
3322 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, gurmukhi_lex, TRUE, TRUE);
3325 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 )
3327 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, gujarati_lex, TRUE, TRUE);
3330 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 )
3332 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, oriya_lex, TRUE, TRUE);
3335 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 )
3337 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, tamil_lex, TRUE, TRUE);
3340 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 )
3342 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, telugu_lex, TRUE, TRUE);
3345 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 )
3347 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, kannada_lex, TRUE, TRUE);
3350 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 )
3352 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, malayalam_lex, TRUE, TRUE);
3355 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 )
3357 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, khmer_lex, TRUE, TRUE);
3360 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)
3362 if (ShapingData[psa->eScript].charGlyphPropProc)
3363 ShapingData[psa->eScript].charGlyphPropProc(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
3364 else
3365 ShapeCharGlyphProp_Default(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
3368 void SHAPE_ContextualShaping(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3370 if (!psc->GSUB_Table)
3371 psc->GSUB_Table = load_gsub_table(hdc);
3373 if (ShapingData[psa->eScript].contextProc)
3374 ShapingData[psa->eScript].contextProc(hdc, psc, psa, pwcChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3377 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)
3379 int i;
3380 INT dirL;
3382 if (!rpRangeProperties)
3383 return;
3385 if (!psc->GSUB_Table)
3386 psc->GSUB_Table = load_gsub_table(hdc);
3388 if (!psc->GSUB_Table)
3389 return;
3391 if (!psa->fLogicalOrder && psa->fRTL)
3392 dirL = -1;
3393 else
3394 dirL = 1;
3396 for (i = 0; i < rpRangeProperties->cotfRecords; i++)
3398 if (rpRangeProperties->potfRecords[i].lParameter > 0)
3399 apply_GSUB_feature(hdc, psa, psc, pwOutGlyphs, dirL, pcGlyphs, cChars, (const char*)&rpRangeProperties->potfRecords[i].tagFeature, pwLogClust);
3403 void SHAPE_ApplyDefaultOpentypeFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, INT cChars, WORD *pwLogClust)
3405 const TEXTRANGE_PROPERTIES *rpRangeProperties;
3406 rpRangeProperties = &ShapingData[psa->eScript].defaultTextRange;
3408 SHAPE_ApplyOpenTypeFeatures(hdc, psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, rpRangeProperties, pwLogClust);
3411 HRESULT SHAPE_CheckFontForRequiredFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa)
3413 LoadedFeature *feature;
3414 int i;
3416 if (!ShapingData[psa->eScript].requiredFeatures)
3417 return S_OK;
3419 if (!psc->GSUB_Table)
3420 psc->GSUB_Table = load_gsub_table(hdc);
3422 /* we need to have at least one of the required features */
3423 i = 0;
3424 while (ShapingData[psa->eScript].requiredFeatures[i])
3426 feature = load_GSUB_feature(hdc, psa, psc, ShapingData[psa->eScript].requiredFeatures[i]);
3427 if (feature)
3428 return S_OK;
3429 i++;
3432 return USP_E_SCRIPT_NOT_IN_FONT;
3435 static void GSUB_initialize_script_cache(ScriptCache *psc)
3437 int i;
3439 if (!psc->script_count)
3441 const GSUB_ScriptList *script;
3442 const GSUB_Header* header = (const GSUB_Header*)psc->GSUB_Table;
3443 script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
3444 psc->script_count = GET_BE_WORD(script->ScriptCount);
3445 TRACE("initializing %i scripts in this font\n",psc->script_count);
3446 psc->scripts = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(LoadedScript) * psc->script_count);
3447 for (i = 0; i < psc->script_count; i++)
3449 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
3450 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]);
3451 psc->scripts[i].table = ((const BYTE*)script + offset);
3456 static HRESULT GSUB_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags, LPCVOID* script_table)
3458 int i;
3459 HRESULT rc = S_OK;
3461 GSUB_initialize_script_cache(psc);
3462 *pcTags = psc->script_count;
3464 if (!searchingFor && cMaxTags < *pcTags)
3465 rc = E_OUTOFMEMORY;
3466 else if (searchingFor)
3467 rc = USP_E_SCRIPT_NOT_IN_FONT;
3469 for (i = 0; i < psc->script_count; i++)
3471 if (i < cMaxTags)
3472 pScriptTags[i] = psc->scripts[i].tag;
3474 if (searchingFor)
3476 if (searchingFor == psc->scripts[i].tag)
3478 pScriptTags[0] = psc->scripts[i].tag;
3479 *pcTags = 1;
3480 if (script_table)
3481 *script_table = psc->scripts[i].table;
3482 rc = S_OK;
3483 break;
3487 return rc;
3490 static void GSUB_initialize_language_cache(LoadedScript *script)
3492 int i;
3494 if (!script->language_count)
3496 const GSUB_Script* table = script->table;
3497 script->language_count = GET_BE_WORD(table->LangSysCount);
3498 script->default_language.tag = MS_MAKE_TAG('d','f','l','t');
3499 script->default_language.table = (const BYTE*)table + GET_BE_WORD(table->DefaultLangSys);
3501 TRACE("Deflang %p, LangCount %i\n",script->default_language.table, script->language_count);
3503 script->languages = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LoadedLanguage) * script->language_count);
3505 for (i = 0; i < script->language_count; i++)
3507 int offset = GET_BE_WORD(table->LangSysRecord[i].LangSys);
3508 script->languages[i].tag = MS_MAKE_TAG(table->LangSysRecord[i].LangSysTag[0], table->LangSysRecord[i].LangSysTag[1], table->LangSysRecord[i].LangSysTag[2], table->LangSysRecord[i].LangSysTag[3]);
3509 script->languages[i].table = ((const BYTE*)table + offset);
3514 static HRESULT GSUB_GetFontLanguageTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pLanguageTags, int *pcTags, LPCVOID* language_table)
3516 int i;
3517 HRESULT rc = S_OK;
3518 LoadedScript *script = NULL;
3520 GSUB_initialize_script_cache(psc);
3522 for (i = 0; i < psc->script_count; i++)
3524 if (psc->scripts[i].tag == script_tag)
3526 script = &psc->scripts[i];
3527 break;
3531 if (!script)
3532 return E_INVALIDARG;
3534 GSUB_initialize_language_cache(script);
3536 if (!searchingFor && cMaxTags < script->language_count)
3537 rc = E_OUTOFMEMORY;
3538 else if (searchingFor)
3539 rc = E_INVALIDARG;
3541 *pcTags = script->language_count;
3543 for (i = 0; i < script->language_count; i++)
3545 if (i < cMaxTags)
3546 pLanguageTags[i] = script->languages[i].tag;
3548 if (searchingFor)
3550 if (searchingFor == script->languages[i].tag)
3552 pLanguageTags[0] = script->languages[i].tag;
3553 *pcTags = 1;
3554 if (language_table)
3555 *language_table = script->languages[i].table;
3556 rc = S_OK;
3557 break;
3562 if (script->default_language.table)
3564 if (i < cMaxTags)
3565 pLanguageTags[i] = script->default_language.tag;
3567 if (searchingFor && FAILED(rc))
3569 pLanguageTags[0] = script->default_language.tag;
3570 if (language_table)
3571 *language_table = script->default_language.table;
3573 i++;
3574 *pcTags = (*pcTags) + 1;
3577 return rc;
3580 static void GSUB_initialize_feature_cache(LPCVOID table, LoadedLanguage *language)
3582 int i;
3584 if (!language->feature_count)
3586 const GSUB_LangSys *lang= language->table;
3587 const GSUB_Header *header = (const GSUB_Header *)table;
3588 const GSUB_FeatureList *feature_list;
3590 language->feature_count = GET_BE_WORD(lang->FeatureCount);
3591 TRACE("%i features\n",language->feature_count);
3593 language->features = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature)*language->feature_count);
3595 feature_list = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
3597 for (i = 0; i < language->feature_count; i++)
3599 const GSUB_Feature *feature;
3600 int j;
3601 int index = GET_BE_WORD(lang->FeatureIndex[i]);
3603 language->features[i].tag = MS_MAKE_TAG(feature_list->FeatureRecord[index].FeatureTag[0], feature_list->FeatureRecord[index].FeatureTag[1], feature_list->FeatureRecord[index].FeatureTag[2], feature_list->FeatureRecord[index].FeatureTag[3]);
3604 language->features[i].feature = ((const BYTE*)feature_list + GET_BE_WORD(feature_list->FeatureRecord[index].Feature));
3605 feature = (const GSUB_Feature*)language->features[i].feature;
3606 language->features[i].lookup_count = GET_BE_WORD(feature->LookupCount);
3607 language->features[i].lookups = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * language->features[i].lookup_count);
3608 for (j = 0; j < language->features[i].lookup_count; j++)
3609 language->features[i].lookups[j] = GET_BE_WORD(feature->LookupListIndex[j]);
3614 static HRESULT GSUB_GetFontFeatureTags(ScriptCache *psc, OPENTYPE_TAG script_tag, OPENTYPE_TAG language_tag, BOOL filtered, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags, LoadedFeature** feature)
3616 int i;
3617 HRESULT rc = S_OK;
3618 LoadedScript *script = NULL;
3619 LoadedLanguage *language = NULL;
3621 GSUB_initialize_script_cache(psc);
3623 for (i = 0; i < psc->script_count; i++)
3625 if (psc->scripts[i].tag == script_tag)
3627 script = &psc->scripts[i];
3628 break;
3632 if (!script)
3634 *pcTags = 0;
3635 if (!filtered)
3636 return S_OK;
3637 else
3638 return E_INVALIDARG;
3641 GSUB_initialize_language_cache(script);
3643 if (script->default_language.table && script->default_language.tag == language_tag)
3644 language = &script->default_language;
3645 else
3647 for (i = 0; i < script->language_count; i++)
3649 if (script->languages[i].tag == language_tag)
3651 language = &script->languages[i];
3652 break;
3657 if (!language)
3659 *pcTags = 0;
3660 return S_OK;
3663 GSUB_initialize_feature_cache(psc->GSUB_Table, language);
3665 *pcTags = language->feature_count;
3667 if (!searchingFor && cMaxTags < *pcTags)
3668 rc = E_OUTOFMEMORY;
3669 else if (searchingFor)
3670 rc = E_INVALIDARG;
3672 for (i = 0; i < language->feature_count; i++)
3674 if (i < cMaxTags)
3675 pFeatureTags[i] = language->features[i].tag;
3677 if (searchingFor)
3679 if (searchingFor == language->features[i].tag)
3681 pFeatureTags[0] = language->features[i].tag;
3682 *pcTags = 1;
3683 if (feature)
3684 *feature = &language->features[i];
3685 rc = S_OK;
3686 break;
3690 return rc;
3693 HRESULT SHAPE_GetFontScriptTags( HDC hdc, ScriptCache *psc,
3694 SCRIPT_ANALYSIS *psa, int cMaxTags,
3695 OPENTYPE_TAG *pScriptTags, int *pcTags)
3697 HRESULT hr;
3698 OPENTYPE_TAG searching = 0x00000000;
3700 if (!psc->GSUB_Table)
3701 psc->GSUB_Table = load_gsub_table(hdc);
3703 if (psa && scriptInformation[psa->eScript].scriptTag)
3704 searching = scriptInformation[psa->eScript].scriptTag;
3706 hr = GSUB_GetFontScriptTags(psc, searching, cMaxTags, pScriptTags, pcTags, NULL);
3707 if (FAILED(hr))
3708 *pcTags = 0;
3709 return hr;
3712 HRESULT SHAPE_GetFontLanguageTags( HDC hdc, ScriptCache *psc,
3713 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
3714 int cMaxTags, OPENTYPE_TAG *pLangSysTags,
3715 int *pcTags)
3717 HRESULT hr;
3718 OPENTYPE_TAG searching = 0x00000000;
3719 BOOL fellback = FALSE;
3721 if (!psc->GSUB_Table)
3722 psc->GSUB_Table = load_gsub_table(hdc);
3724 if (psa && psc->userLang != 0)
3725 searching = psc->userLang;
3727 hr = GSUB_GetFontLanguageTags(psc, tagScript, searching, cMaxTags, pLangSysTags, pcTags, NULL);
3728 if (FAILED(hr))
3730 fellback = TRUE;
3731 hr = GSUB_GetFontLanguageTags(psc, MS_MAKE_TAG('l','a','t','n'), searching, cMaxTags, pLangSysTags, pcTags, NULL);
3734 if (FAILED(hr) || fellback)
3735 *pcTags = 0;
3736 if (SUCCEEDED(hr) && fellback && psa)
3737 hr = E_INVALIDARG;
3738 return hr;
3741 HRESULT SHAPE_GetFontFeatureTags( HDC hdc, ScriptCache *psc,
3742 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
3743 OPENTYPE_TAG tagLangSys, int cMaxTags,
3744 OPENTYPE_TAG *pFeatureTags, int *pcTags)
3746 HRESULT hr;
3747 BOOL filter = FALSE;
3749 if (!psc->GSUB_Table)
3750 psc->GSUB_Table = load_gsub_table(hdc);
3752 if (psa && scriptInformation[psa->eScript].scriptTag)
3754 FIXME("Filtering not implemented\n");
3755 filter = TRUE;
3758 hr = GSUB_GetFontFeatureTags(psc, tagScript, tagLangSys, filter, 0x00000000, cMaxTags, pFeatureTags, pcTags, NULL);
3760 if (FAILED(hr))
3761 *pcTags = 0;
3762 return hr;