usp10: Add Vai script.
[wine.git] / dlls / usp10 / shape.c
blobacf1fdf606cd6ecebdde3ab008894c093a57b58f
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>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "usp10.h"
29 #include "winternl.h"
31 #include "usp10_internal.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
37 #define FIRST_ARABIC_CHAR 0x0600
38 #define LAST_ARABIC_CHAR 0x06ff
40 typedef VOID (*ContextualShapingProc)(HDC, ScriptCache*, SCRIPT_ANALYSIS*,
41 WCHAR*, INT, WORD*, INT*, INT, WORD*);
43 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
44 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
45 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
46 static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
47 static void ContextualShape_Devanagari(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
48 static void ContextualShape_Bengali(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
49 static void ContextualShape_Gurmukhi(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
50 static void ContextualShape_Gujarati(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
51 static void ContextualShape_Oriya(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
52 static void ContextualShape_Tamil(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
53 static void ContextualShape_Telugu(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
54 static void ContextualShape_Kannada(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
55 static void ContextualShape_Malayalam(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
56 static void ContextualShape_Khmer(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
57 static void ContextualShape_Mongolian(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
59 typedef VOID (*ShapeCharGlyphPropProc)( HDC , ScriptCache*, SCRIPT_ANALYSIS*, const WCHAR*, const INT, const WORD*, const INT, WORD*, SCRIPT_CHARPROP*, SCRIPT_GLYPHPROP*);
61 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);
62 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 );
63 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 );
64 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 );
65 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 );
66 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 );
67 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 );
68 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 );
69 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 );
70 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 );
71 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 );
72 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 );
73 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 );
74 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 );
75 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 );
76 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 );
78 extern const unsigned short indic_syllabic_table[];
79 extern const unsigned short wine_shaping_table[];
80 extern const unsigned short wine_shaping_forms[LAST_ARABIC_CHAR - FIRST_ARABIC_CHAR + 1][4];
82 enum joining_types {
83 jtU,
84 jtT,
85 jtR,
86 jtL,
87 jtD,
88 jtC
91 enum joined_forms {
92 Xn=0,
93 Xr,
94 Xl,
95 Xm,
96 /* Syriac Alaph */
97 Afj,
98 Afn,
99 Afx
102 #ifdef WORDS_BIGENDIAN
103 #define GET_BE_WORD(x) (x)
104 #else
105 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
106 #endif
108 /* These are all structures needed for the GSUB table */
109 #define GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B')
110 #define GSUB_E_NOFEATURE -2
111 #define GSUB_E_NOGLYPH -1
113 typedef struct {
114 DWORD version;
115 WORD ScriptList;
116 WORD FeatureList;
117 WORD LookupList;
118 } GSUB_Header;
120 typedef struct {
121 CHAR ScriptTag[4];
122 WORD Script;
123 } GSUB_ScriptRecord;
125 typedef struct {
126 WORD ScriptCount;
127 GSUB_ScriptRecord ScriptRecord[1];
128 } GSUB_ScriptList;
130 typedef struct {
131 CHAR LangSysTag[4];
132 WORD LangSys;
133 } GSUB_LangSysRecord;
135 typedef struct {
136 WORD DefaultLangSys;
137 WORD LangSysCount;
138 GSUB_LangSysRecord LangSysRecord[1];
139 } GSUB_Script;
141 typedef struct {
142 WORD LookupOrder; /* Reserved */
143 WORD ReqFeatureIndex;
144 WORD FeatureCount;
145 WORD FeatureIndex[1];
146 } GSUB_LangSys;
148 typedef struct {
149 CHAR FeatureTag[4];
150 WORD Feature;
151 } GSUB_FeatureRecord;
153 typedef struct {
154 WORD FeatureCount;
155 GSUB_FeatureRecord FeatureRecord[1];
156 } GSUB_FeatureList;
158 typedef struct {
159 WORD FeatureParams; /* Reserved */
160 WORD LookupCount;
161 WORD LookupListIndex[1];
162 } GSUB_Feature;
164 typedef struct {
165 WORD LookupCount;
166 WORD Lookup[1];
167 } GSUB_LookupList;
169 typedef struct {
170 WORD LookupType;
171 WORD LookupFlag;
172 WORD SubTableCount;
173 WORD SubTable[1];
174 } GSUB_LookupTable;
176 typedef struct {
177 WORD CoverageFormat;
178 WORD GlyphCount;
179 WORD GlyphArray[1];
180 } GSUB_CoverageFormat1;
182 typedef struct {
183 WORD Start;
184 WORD End;
185 WORD StartCoverageIndex;
186 } GSUB_RangeRecord;
188 typedef struct {
189 WORD CoverageFormat;
190 WORD RangeCount;
191 GSUB_RangeRecord RangeRecord[1];
192 } GSUB_CoverageFormat2;
194 typedef struct {
195 WORD SubstFormat; /* = 1 */
196 WORD Coverage;
197 WORD DeltaGlyphID;
198 } GSUB_SingleSubstFormat1;
200 typedef struct {
201 WORD SubstFormat; /* = 2 */
202 WORD Coverage;
203 WORD GlyphCount;
204 WORD Substitute[1];
205 }GSUB_SingleSubstFormat2;
207 typedef struct {
208 WORD SubstFormat; /* = 1 */
209 WORD Coverage;
210 WORD SequenceCount;
211 WORD Sequence[1];
212 }GSUB_MultipleSubstFormat1;
214 typedef struct {
215 WORD GlyphCount;
216 WORD Substitute[1];
217 }GSUB_Sequence;
219 typedef struct {
220 WORD SubstFormat; /* = 1 */
221 WORD Coverage;
222 WORD LigSetCount;
223 WORD LigatureSet[1];
224 }GSUB_LigatureSubstFormat1;
226 typedef struct {
227 WORD LigatureCount;
228 WORD Ligature[1];
229 }GSUB_LigatureSet;
231 typedef struct{
232 WORD LigGlyph;
233 WORD CompCount;
234 WORD Component[1];
235 }GSUB_Ligature;
237 typedef struct{
238 WORD SequenceIndex;
239 WORD LookupListIndex;
241 }GSUB_SubstLookupRecord;
243 typedef struct{
244 WORD SubstFormat; /* = 1 */
245 WORD Coverage;
246 WORD ChainSubRuleSetCount;
247 WORD ChainSubRuleSet[1];
248 }GSUB_ChainContextSubstFormat1;
250 typedef struct {
251 WORD SubstFormat; /* = 3 */
252 WORD BacktrackGlyphCount;
253 WORD Coverage[1];
254 }GSUB_ChainContextSubstFormat3_1;
256 typedef struct{
257 WORD InputGlyphCount;
258 WORD Coverage[1];
259 }GSUB_ChainContextSubstFormat3_2;
261 typedef struct{
262 WORD LookaheadGlyphCount;
263 WORD Coverage[1];
264 }GSUB_ChainContextSubstFormat3_3;
266 typedef struct{
267 WORD SubstCount;
268 GSUB_SubstLookupRecord SubstLookupRecord[1];
269 }GSUB_ChainContextSubstFormat3_4;
271 typedef struct {
272 WORD SubstFormat; /* = 1 */
273 WORD Coverage;
274 WORD AlternateSetCount;
275 WORD AlternateSet[1];
276 } GSUB_AlternateSubstFormat1;
278 typedef struct{
279 WORD GlyphCount;
280 WORD Alternate[1];
281 } GSUB_AlternateSet;
283 /* These are all structures needed for the GDEF table */
284 #define GDEF_TAG MS_MAKE_TAG('G', 'D', 'E', 'F')
286 enum {BaseGlyph=1, LigatureGlyph, MarkGlyph, ComponentGlyph};
288 typedef struct {
289 DWORD Version;
290 WORD GlyphClassDef;
291 WORD AttachList;
292 WORD LigCaretList;
293 WORD MarkAttachClassDef;
294 } GDEF_Header;
296 typedef struct {
297 WORD ClassFormat;
298 WORD StartGlyph;
299 WORD GlyphCount;
300 WORD ClassValueArray[1];
301 } GDEF_ClassDefFormat1;
303 typedef struct {
304 WORD Start;
305 WORD End;
306 WORD Class;
307 } GDEF_ClassRangeRecord;
309 typedef struct {
310 WORD ClassFormat;
311 WORD ClassRangeCount;
312 GDEF_ClassRangeRecord ClassRangeRecord[1];
313 } GDEF_ClassDefFormat2;
315 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
317 typedef struct tagVowelComponents
319 WCHAR base;
320 WCHAR parts[3];
321 } VowelComponents;
323 typedef struct tagConsonantComponents
325 WCHAR parts[3];
326 WCHAR output;
327 } ConsonantComponents;
329 typedef void (*second_reorder_function)(LPWSTR pwChar, IndicSyllable *syllable,WORD* pwGlyphs, IndicSyllable* glyph_index, lexical_function lex);
331 /* the orders of joined_forms and contextual_features need to line up */
332 static const char* contextual_features[] =
334 "isol",
335 "fina",
336 "init",
337 "medi",
338 /* Syriac Alaph */
339 "med2",
340 "fin2",
341 "fin3"
344 static OPENTYPE_FEATURE_RECORD standard_features[] =
346 { MS_MAKE_TAG('c','c','m','p'), 1},
347 { MS_MAKE_TAG('l','o','c','l'), 1},
350 static OPENTYPE_FEATURE_RECORD latin_features[] =
352 { MS_MAKE_TAG('l','i','g','a'), 1},
353 { MS_MAKE_TAG('c','l','i','g'), 1},
356 static OPENTYPE_FEATURE_RECORD arabic_features[] =
358 { MS_MAKE_TAG('r','l','i','g'), 1},
359 { MS_MAKE_TAG('c','a','l','t'), 1},
360 { MS_MAKE_TAG('l','i','g','a'), 1},
361 { MS_MAKE_TAG('d','l','i','g'), 1},
362 { MS_MAKE_TAG('c','s','w','h'), 1},
363 { MS_MAKE_TAG('m','s','e','t'), 1},
366 static const char* required_arabic_features[] =
368 "fina",
369 "init",
370 "medi",
371 "rlig",
372 NULL
375 static OPENTYPE_FEATURE_RECORD hebrew_features[] =
377 { MS_MAKE_TAG('d','l','i','g'), 0},
380 static OPENTYPE_FEATURE_RECORD syriac_features[] =
382 { MS_MAKE_TAG('r','l','i','g'), 1},
383 { MS_MAKE_TAG('c','a','l','t'), 1},
384 { MS_MAKE_TAG('l','i','g','a'), 1},
385 { MS_MAKE_TAG('d','l','i','g'), 1},
388 static const char* required_syriac_features[] =
390 "fina",
391 "fin2",
392 "fin3",
393 "init",
394 "medi",
395 "med2",
396 "rlig",
397 NULL
400 static OPENTYPE_FEATURE_RECORD sinhala_features[] =
402 /* Presentation forms */
403 { MS_MAKE_TAG('b','l','w','s'), 1},
404 { MS_MAKE_TAG('a','b','v','s'), 1},
405 { MS_MAKE_TAG('p','s','t','s'), 1},
408 static OPENTYPE_FEATURE_RECORD tibetan_features[] =
410 { MS_MAKE_TAG('a','b','v','s'), 1},
411 { MS_MAKE_TAG('b','l','w','s'), 1},
414 static OPENTYPE_FEATURE_RECORD phags_features[] =
416 { MS_MAKE_TAG('a','b','v','s'), 1},
417 { MS_MAKE_TAG('b','l','w','s'), 1},
418 { MS_MAKE_TAG('c','a','l','t'), 1},
421 static OPENTYPE_FEATURE_RECORD thai_features[] =
423 { MS_MAKE_TAG('c','c','m','p'), 1},
426 static const char* required_lao_features[] =
428 "ccmp",
429 NULL
432 static const char* required_devanagari_features[] =
434 "nukt",
435 "akhn",
436 "rphf",
437 "blwf",
438 "half",
439 "vatu",
440 "pres",
441 "abvs",
442 "blws",
443 "psts",
444 "haln",
445 NULL
448 static OPENTYPE_FEATURE_RECORD devanagari_features[] =
450 { MS_MAKE_TAG('p','r','e','s'), 1},
451 { MS_MAKE_TAG('a','b','v','s'), 1},
452 { MS_MAKE_TAG('b','l','w','s'), 1},
453 { MS_MAKE_TAG('p','s','t','s'), 1},
454 { MS_MAKE_TAG('h','a','l','n'), 1},
455 { MS_MAKE_TAG('c','a','l','t'), 1},
458 static OPENTYPE_FEATURE_RECORD myanmar_features[] =
460 { MS_MAKE_TAG('l','i','g','a'), 1},
461 { MS_MAKE_TAG('c','l','i','g'), 1},
464 static const char* required_bengali_features[] =
466 "nukt",
467 "akhn",
468 "rphf",
469 "blwf",
470 "half",
471 "vatu",
472 "pstf",
473 "init",
474 "abvs",
475 "blws",
476 "psts",
477 "haln",
478 NULL
481 static const char* required_gurmukhi_features[] =
483 "nukt",
484 "akhn",
485 "rphf",
486 "blwf",
487 "half",
488 "pstf",
489 "vatu",
490 "cjct",
491 "pres",
492 "abvs",
493 "blws",
494 "psts",
495 "haln",
496 "calt",
497 NULL
500 static const char* required_oriya_features[] =
502 "nukt",
503 "akhn",
504 "rphf",
505 "blwf",
506 "pstf",
507 "cjct",
508 "pres",
509 "abvs",
510 "blws",
511 "psts",
512 "haln",
513 "calt",
514 NULL
517 static const char* required_tamil_features[] =
519 "nukt",
520 "akhn",
521 "rphf",
522 "pref",
523 "half",
524 "pres",
525 "abvs",
526 "blws",
527 "psts",
528 "haln",
529 "calt",
530 NULL
533 static const char* required_telugu_features[] =
535 "nukt",
536 "akhn",
537 "rphf",
538 "pref",
539 "half",
540 "pstf",
541 "cjct",
542 "pres",
543 "abvs",
544 "blws",
545 "psts",
546 "haln",
547 "calt",
548 NULL
551 static OPENTYPE_FEATURE_RECORD khmer_features[] =
553 { MS_MAKE_TAG('p','r','e','s'), 1},
554 { MS_MAKE_TAG('b','l','w','s'), 1},
555 { MS_MAKE_TAG('a','b','v','s'), 1},
556 { MS_MAKE_TAG('p','s','t','s'), 1},
557 { MS_MAKE_TAG('c','l','i','g'), 1},
560 static const char* required_khmer_features[] =
562 "pref",
563 "blwf",
564 "abvf",
565 "pstf",
566 "pres",
567 "blws",
568 "abvs",
569 "psts",
570 "clig",
571 NULL
574 static OPENTYPE_FEATURE_RECORD no_features[] =
575 { };
577 static OPENTYPE_FEATURE_RECORD ethiopic_features[] =
579 { MS_MAKE_TAG('c','c','m','p'), 1},
580 { MS_MAKE_TAG('l','o','c','l'), 1},
581 { MS_MAKE_TAG('c','a','l','t'), 1},
582 { MS_MAKE_TAG('l','i','g','a'), 1},
585 static OPENTYPE_FEATURE_RECORD mongolian_features[] =
587 { MS_MAKE_TAG('c','c','m','p'), 1},
588 { MS_MAKE_TAG('l','o','c','l'), 1},
589 { MS_MAKE_TAG('c','a','l','t'), 1},
590 { MS_MAKE_TAG('r','l','i','g'), 1},
593 typedef struct ScriptShapeDataTag {
594 TEXTRANGE_PROPERTIES defaultTextRange;
595 const char** requiredFeatures;
596 CHAR otTag[5];
597 CHAR newOtTag[5];
598 ContextualShapingProc contextProc;
599 ShapeCharGlyphPropProc charGlyphPropProc;
600 } ScriptShapeData;
602 /* in order of scripts */
603 static const ScriptShapeData ShapingData[] =
605 {{ standard_features, 2}, NULL, "", "", NULL, NULL},
606 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
607 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
608 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
609 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
610 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
611 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
612 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
613 {{ hebrew_features, 1}, NULL, "hebr", "", NULL, NULL},
614 {{ syriac_features, 4}, required_syriac_features, "syrc", "", ContextualShape_Syriac, ShapeCharGlyphProp_None},
615 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
616 {{ NULL, 0}, NULL, "thaa", "", NULL, ShapeCharGlyphProp_None},
617 {{ standard_features, 2}, NULL, "grek", "", NULL, NULL},
618 {{ standard_features, 2}, NULL, "cyrl", "", NULL, NULL},
619 {{ standard_features, 2}, NULL, "armn", "", NULL, NULL},
620 {{ standard_features, 2}, NULL, "geor", "", NULL, NULL},
621 {{ sinhala_features, 3}, NULL, "sinh", "", ContextualShape_Sinhala, ShapeCharGlyphProp_Sinhala},
622 {{ tibetan_features, 2}, NULL, "tibt", "", NULL, ShapeCharGlyphProp_Tibet},
623 {{ tibetan_features, 2}, NULL, "tibt", "", NULL, ShapeCharGlyphProp_Tibet},
624 {{ phags_features, 3}, NULL, "phag", "", ContextualShape_Phags_pa, ShapeCharGlyphProp_Thai},
625 {{ thai_features, 1}, NULL, "thai", "", NULL, ShapeCharGlyphProp_Thai},
626 {{ thai_features, 1}, NULL, "thai", "", NULL, ShapeCharGlyphProp_Thai},
627 {{ thai_features, 1}, required_lao_features, "lao", "", NULL, ShapeCharGlyphProp_Thai},
628 {{ thai_features, 1}, required_lao_features, "lao", "", NULL, ShapeCharGlyphProp_Thai},
629 {{ devanagari_features, 6}, required_devanagari_features, "deva", "dev2", ContextualShape_Devanagari, ShapeCharGlyphProp_Devanagari},
630 {{ devanagari_features, 6}, required_devanagari_features, "deva", "dev2", ContextualShape_Devanagari, ShapeCharGlyphProp_Devanagari},
631 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
632 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
633 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
634 {{ devanagari_features, 6}, required_gurmukhi_features, "guru", "gur2", ContextualShape_Gurmukhi, ShapeCharGlyphProp_Gurmukhi},
635 {{ devanagari_features, 6}, required_gurmukhi_features, "guru", "gur2", ContextualShape_Gurmukhi, ShapeCharGlyphProp_Gurmukhi},
636 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
637 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
638 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
639 {{ devanagari_features, 6}, required_oriya_features, "orya", "ory2", ContextualShape_Oriya, ShapeCharGlyphProp_Oriya},
640 {{ devanagari_features, 6}, required_oriya_features, "orya", "ory2", ContextualShape_Oriya, ShapeCharGlyphProp_Oriya},
641 {{ devanagari_features, 6}, required_tamil_features, "taml", "tam2", ContextualShape_Tamil, ShapeCharGlyphProp_Tamil},
642 {{ devanagari_features, 6}, required_tamil_features, "taml", "tam2", ContextualShape_Tamil, ShapeCharGlyphProp_Tamil},
643 {{ devanagari_features, 6}, required_telugu_features, "telu", "tel2", ContextualShape_Telugu, ShapeCharGlyphProp_Telugu},
644 {{ devanagari_features, 6}, required_telugu_features, "telu", "tel2", ContextualShape_Telugu, ShapeCharGlyphProp_Telugu},
645 {{ devanagari_features, 6}, required_telugu_features, "knda", "knd2", ContextualShape_Kannada, ShapeCharGlyphProp_Kannada},
646 {{ devanagari_features, 6}, required_telugu_features, "knda", "knd2", ContextualShape_Kannada, ShapeCharGlyphProp_Kannada},
647 {{ devanagari_features, 6}, required_telugu_features, "mlym", "mlm2", ContextualShape_Malayalam, ShapeCharGlyphProp_Malayalam},
648 {{ devanagari_features, 6}, required_telugu_features, "mlym", "mlm2", ContextualShape_Malayalam, ShapeCharGlyphProp_Malayalam},
649 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
650 {{ latin_features, 2}, NULL, "latn" , "", NULL, NULL},
651 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
652 {{ myanmar_features, 2}, NULL, "mymr", "", NULL, NULL},
653 {{ myanmar_features, 2}, NULL, "mymr", "", NULL, NULL},
654 {{ standard_features, 2}, NULL, "tale", "", NULL, NULL},
655 {{ standard_features, 2}, NULL, "talu", "", NULL, NULL},
656 {{ standard_features, 2}, NULL, "talu", "", NULL, NULL},
657 {{ khmer_features, 5}, required_khmer_features, "khmr", "", ContextualShape_Khmer, ShapeCharGlyphProp_Khmer},
658 {{ khmer_features, 5}, required_khmer_features, "khmr", "", ContextualShape_Khmer, ShapeCharGlyphProp_Khmer},
659 {{ no_features, 0}, NULL, "hani", "", NULL, NULL},
660 {{ no_features, 0}, NULL, "hani", "", NULL, NULL},
661 {{ no_features, 0}, NULL, "bopo", "", NULL, NULL},
662 {{ no_features, 0}, NULL, "kana", "", NULL, NULL},
663 {{ no_features, 0}, NULL, "hang", "", NULL, NULL},
664 {{ no_features, 0}, NULL, "yi ", "", NULL, NULL},
665 {{ ethiopic_features, 4}, NULL, "ethi", "", NULL, NULL},
666 {{ ethiopic_features, 4}, NULL, "ethi", "", NULL, NULL},
667 {{ mongolian_features, 4}, NULL, "mong", "", ContextualShape_Mongolian, NULL},
668 {{ mongolian_features, 4}, NULL, "mong", "", ContextualShape_Mongolian, NULL},
669 {{ no_features, 0}, NULL, "tfng", "", NULL, NULL},
670 {{ no_features, 0}, NULL, "nko ", "", NULL, NULL},
671 {{ no_features, 0}, NULL, "vai ", "", NULL, NULL},
672 {{ no_features, 0}, NULL, "vai ", "", NULL, NULL},
675 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
677 const GSUB_CoverageFormat1* cf1;
679 cf1 = table;
681 if (GET_BE_WORD(cf1->CoverageFormat) == 1)
683 int count = GET_BE_WORD(cf1->GlyphCount);
684 int i;
685 TRACE("Coverage Format 1, %i glyphs\n",count);
686 for (i = 0; i < count; i++)
687 if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
688 return i;
689 return -1;
691 else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
693 const GSUB_CoverageFormat2* cf2;
694 int i;
695 int count;
696 cf2 = (const GSUB_CoverageFormat2*)cf1;
698 count = GET_BE_WORD(cf2->RangeCount);
699 TRACE("Coverage Format 2, %i ranges\n",count);
700 for (i = 0; i < count; i++)
702 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
703 return -1;
704 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
705 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
707 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
708 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
711 return -1;
713 else
714 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
716 return -1;
719 static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
721 const GSUB_ScriptList *script;
722 const GSUB_Script *deflt = NULL;
723 int i;
724 script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
726 TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
727 for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
729 const GSUB_Script *scr;
730 int offset;
732 offset = GET_BE_WORD(script->ScriptRecord[i].Script);
733 scr = (const GSUB_Script*)((const BYTE*)script + offset);
735 if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
736 return scr;
737 if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
738 deflt = scr;
740 return deflt;
743 static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
745 int i;
746 int offset;
747 const GSUB_LangSys *Lang;
749 TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
751 for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
753 offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
754 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
756 if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
757 return Lang;
759 offset = GET_BE_WORD(script->DefaultLangSys);
760 if (offset)
762 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
763 return Lang;
765 return NULL;
768 static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
770 int i;
771 const GSUB_FeatureList *feature;
772 feature = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
774 TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
775 for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
777 int index = GET_BE_WORD(lang->FeatureIndex[i]);
778 if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
780 const GSUB_Feature *feat;
781 feat = (const GSUB_Feature*)((const BYTE*)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
782 return feat;
785 return NULL;
788 static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
790 int j;
791 TRACE("Single Substitution Subtable\n");
793 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
795 int offset;
796 const GSUB_SingleSubstFormat1 *ssf1;
797 offset = GET_BE_WORD(look->SubTable[j]);
798 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
799 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
801 int offset = GET_BE_WORD(ssf1->Coverage);
802 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
803 if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
805 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
806 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
807 TRACE(" 0x%x\n",glyphs[glyph_index]);
808 return glyph_index + write_dir;
811 else
813 const GSUB_SingleSubstFormat2 *ssf2;
814 INT index;
815 INT offset;
817 ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
818 offset = GET_BE_WORD(ssf1->Coverage);
819 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
820 index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
821 TRACE(" Coverage index %i\n",index);
822 if (index != -1)
824 if (glyphs[glyph_index] == GET_BE_WORD(ssf2->Substitute[index]))
825 return GSUB_E_NOGLYPH;
827 TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]);
828 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
829 TRACE("0x%x\n",glyphs[glyph_index]);
830 return glyph_index + write_dir;
834 return GSUB_E_NOGLYPH;
837 static INT GSUB_apply_MultipleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
839 int j;
840 TRACE("Multiple Substitution Subtable\n");
842 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
844 int offset, index;
845 const GSUB_MultipleSubstFormat1 *msf1;
846 offset = GET_BE_WORD(look->SubTable[j]);
847 msf1 = (const GSUB_MultipleSubstFormat1*)((const BYTE*)look+offset);
849 offset = GET_BE_WORD(msf1->Coverage);
850 index = GSUB_is_glyph_covered((const BYTE*)msf1+offset, glyphs[glyph_index]);
851 if (index != -1)
853 const GSUB_Sequence *seq;
854 int sub_count;
855 int j;
856 offset = GET_BE_WORD(msf1->Sequence[index]);
857 seq = (const GSUB_Sequence*)((const BYTE*)msf1+offset);
858 sub_count = GET_BE_WORD(seq->GlyphCount);
859 TRACE(" Glyph 0x%x (+%i)->",glyphs[glyph_index],(sub_count-1));
861 for (j = (*glyph_count)+(sub_count-1); j > glyph_index; j--)
862 glyphs[j] =glyphs[j-(sub_count-1)];
864 for (j = 0; j < sub_count; j++)
865 if (write_dir < 0)
866 glyphs[glyph_index + (sub_count-1) - j] = GET_BE_WORD(seq->Substitute[j]);
867 else
868 glyphs[glyph_index + j] = GET_BE_WORD(seq->Substitute[j]);
870 *glyph_count = *glyph_count + (sub_count - 1);
872 if (TRACE_ON(uniscribe))
874 for (j = 0; j < sub_count; j++)
875 TRACE(" 0x%x",glyphs[glyph_index+j]);
876 TRACE("\n");
879 return glyph_index + (sub_count * write_dir);
882 return GSUB_E_NOGLYPH;
885 static INT GSUB_apply_AlternateSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
887 int j;
888 TRACE("Alternate Substitution Subtable\n");
890 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
892 int offset;
893 const GSUB_AlternateSubstFormat1 *asf1;
894 INT index;
896 offset = GET_BE_WORD(look->SubTable[j]);
897 asf1 = (const GSUB_AlternateSubstFormat1*)((const BYTE*)look+offset);
898 offset = GET_BE_WORD(asf1->Coverage);
900 index = GSUB_is_glyph_covered((const BYTE*)asf1+offset, glyphs[glyph_index]);
901 if (index != -1)
903 const GSUB_AlternateSet *as;
904 offset = GET_BE_WORD(asf1->AlternateSet[index]);
905 as = (const GSUB_AlternateSet*)((const BYTE*)asf1+offset);
906 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as->GlyphCount));
907 if (glyphs[glyph_index] == GET_BE_WORD(as->Alternate[0]))
908 return GSUB_E_NOGLYPH;
910 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
911 glyphs[glyph_index] = GET_BE_WORD(as->Alternate[0]);
912 TRACE(" 0x%x\n",glyphs[glyph_index]);
913 return glyph_index + write_dir;
916 return GSUB_E_NOGLYPH;
919 static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
921 int j;
923 TRACE("Ligature Substitution Subtable\n");
924 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
926 const GSUB_LigatureSubstFormat1 *lsf1;
927 int offset,index;
929 offset = GET_BE_WORD(look->SubTable[j]);
930 lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
931 offset = GET_BE_WORD(lsf1->Coverage);
932 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
933 TRACE(" Coverage index %i\n",index);
934 if (index != -1)
936 const GSUB_LigatureSet *ls;
937 int k, count;
939 offset = GET_BE_WORD(lsf1->LigatureSet[index]);
940 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
941 count = GET_BE_WORD(ls->LigatureCount);
942 TRACE(" LigatureSet has %i members\n",count);
943 for (k = 0; k < count; k++)
945 const GSUB_Ligature *lig;
946 int CompCount,l,CompIndex;
948 offset = GET_BE_WORD(ls->Ligature[k]);
949 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
950 CompCount = GET_BE_WORD(lig->CompCount) - 1;
951 CompIndex = glyph_index+write_dir;
952 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
954 int CompGlyph;
955 CompGlyph = GET_BE_WORD(lig->Component[l]);
956 if (CompGlyph != glyphs[CompIndex])
957 break;
958 CompIndex += write_dir;
960 if (l == CompCount)
962 int replaceIdx = glyph_index;
963 if (write_dir < 0)
964 replaceIdx = glyph_index - CompCount;
966 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
967 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
968 TRACE("0x%x\n",glyphs[replaceIdx]);
969 if (CompCount > 0)
971 int j;
972 for (j = replaceIdx + 1; j < *glyph_count; j++)
973 glyphs[j] =glyphs[j+CompCount];
974 *glyph_count = *glyph_count - CompCount;
976 return replaceIdx + write_dir;
981 return GSUB_E_NOGLYPH;
984 static INT GSUB_apply_ChainContextSubst(const GSUB_LookupList* lookup, const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
986 int j;
987 BOOL done = FALSE;
989 TRACE("Chaining Contextual Substitution Subtable\n");
990 for (j = 0; j < GET_BE_WORD(look->SubTableCount) && !done; j++)
992 const GSUB_ChainContextSubstFormat1 *ccsf1;
993 int offset;
994 int dirLookahead = write_dir;
995 int dirBacktrack = -1 * write_dir;
997 offset = GET_BE_WORD(look->SubTable[j]);
998 ccsf1 = (const GSUB_ChainContextSubstFormat1*)((const BYTE*)look+offset);
999 if (GET_BE_WORD(ccsf1->SubstFormat) == 1)
1001 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
1002 continue;
1004 else if (GET_BE_WORD(ccsf1->SubstFormat) == 2)
1006 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
1007 continue;
1009 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
1011 int k;
1012 int indexGlyphs;
1013 const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
1014 const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
1015 const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
1016 const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
1017 int newIndex = glyph_index;
1019 ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
1021 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
1023 for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
1025 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
1026 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
1027 break;
1029 if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
1030 continue;
1031 TRACE("Matched Backtrack\n");
1033 ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)(((LPBYTE)ccsf1)+sizeof(GSUB_ChainContextSubstFormat3_1) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)-1)));
1035 indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
1036 for (k = 0; k < indexGlyphs; k++)
1038 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
1039 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
1040 break;
1042 if (k != indexGlyphs)
1043 continue;
1044 TRACE("Matched IndexGlyphs\n");
1046 ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)(((LPBYTE)ccsf3_2)+sizeof(GSUB_ChainContextSubstFormat3_2) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_2->InputGlyphCount)-1)));
1048 for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
1050 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
1051 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
1052 break;
1054 if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
1055 continue;
1056 TRACE("Matched LookAhead\n");
1058 ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)(((LPBYTE)ccsf3_3)+sizeof(GSUB_ChainContextSubstFormat3_3) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)-1)));
1060 if (GET_BE_WORD(ccsf3_4->SubstCount))
1062 for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
1064 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
1065 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
1067 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
1068 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
1069 if (newIndex == -1)
1071 ERR("Chain failed to generate a glyph\n");
1072 continue;
1075 return newIndex;
1077 else return GSUB_E_NOGLYPH;
1080 return -1;
1083 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1085 int offset;
1086 const GSUB_LookupTable *look;
1088 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
1089 look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
1090 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
1091 switch(GET_BE_WORD(look->LookupType))
1093 case 1:
1094 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1095 case 2:
1096 return GSUB_apply_MultipleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1097 case 3:
1098 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1099 case 4:
1100 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1101 case 6:
1102 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
1103 default:
1104 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
1106 return GSUB_E_NOGLYPH;
1109 static INT GSUB_apply_feature_all_lookups(const GSUB_Header * header, const GSUB_Feature* feature, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1111 int i;
1112 int out_index = GSUB_E_NOGLYPH;
1113 const GSUB_LookupList *lookup;
1115 lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1117 TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
1118 for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
1120 out_index = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[i]), glyphs, glyph_index, write_dir, glyph_count);
1121 if (out_index != GSUB_E_NOGLYPH)
1122 break;
1124 if (out_index == GSUB_E_NOGLYPH)
1125 TRACE("lookups found no glyphs\n");
1126 else
1128 int out2;
1129 out2 = GSUB_apply_feature_all_lookups(header, feature, glyphs, glyph_index, write_dir, glyph_count);
1130 if (out2!=GSUB_E_NOGLYPH)
1131 out_index = out2;
1133 return out_index;
1136 static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, BOOL tryNew)
1138 UINT charset;
1140 if (psc->userScript != 0)
1142 if (tryNew && ShapingData[psa->eScript].newOtTag[0] != 0 && strncmp((char*)&psc->userScript,ShapingData[psa->eScript].otTag,4)==0)
1143 return ShapingData[psa->eScript].newOtTag;
1144 else
1145 return (char*)&psc->userScript;
1148 if (tryNew && ShapingData[psa->eScript].newOtTag[0] != 0)
1149 return ShapingData[psa->eScript].newOtTag;
1151 if (ShapingData[psa->eScript].otTag[0] != 0)
1152 return ShapingData[psa->eScript].otTag;
1155 * fall back to the font charset
1157 charset = GetTextCharsetInfo(hdc, NULL, 0x0);
1158 switch (charset)
1160 case ANSI_CHARSET: return "latn";
1161 case BALTIC_CHARSET: return "latn"; /* ?? */
1162 case CHINESEBIG5_CHARSET: return "hani";
1163 case EASTEUROPE_CHARSET: return "latn"; /* ?? */
1164 case GB2312_CHARSET: return "hani";
1165 case GREEK_CHARSET: return "grek";
1166 case HANGUL_CHARSET: return "hang";
1167 case RUSSIAN_CHARSET: return "cyrl";
1168 case SHIFTJIS_CHARSET: return "kana";
1169 case TURKISH_CHARSET: return "latn"; /* ?? */
1170 case VIETNAMESE_CHARSET: return "latn";
1171 case JOHAB_CHARSET: return "latn"; /* ?? */
1172 case ARABIC_CHARSET: return "arab";
1173 case HEBREW_CHARSET: return "hebr";
1174 case THAI_CHARSET: return "thai";
1175 default: return "latn";
1179 static LPCVOID load_GSUB_feature(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, const char* feat)
1181 const GSUB_Feature *feature;
1182 const char* script;
1183 int i;
1185 script = get_opentype_script(hdc,psa,psc,FALSE);
1187 for (i = 0; i < psc->feature_count; i++)
1189 if (strncmp(psc->features[i].tag,feat,4)==0 && strncmp(psc->features[i].script,script,4)==0)
1190 return psc->features[i].feature;
1193 feature = NULL;
1195 if (psc->GSUB_Table)
1197 const GSUB_Script *script;
1198 const GSUB_LangSys *language;
1199 int attempt = 2;
1203 script = GSUB_get_script_table(psc->GSUB_Table, get_opentype_script(hdc,psa,psc,(attempt==2)));
1204 attempt--;
1205 if (script)
1207 if (psc->userLang != 0)
1208 language = GSUB_get_lang_table(script,(char*)&psc->userLang);
1209 else
1210 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
1211 if (language)
1212 feature = GSUB_get_feature(psc->GSUB_Table, language, feat);
1214 } while(attempt && !feature);
1216 /* try in the default (latin) table */
1217 if (!feature)
1219 script = GSUB_get_script_table(psc->GSUB_Table, "latn");
1220 if (script)
1222 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
1223 if (language)
1224 feature = GSUB_get_feature(psc->GSUB_Table, language, feat);
1229 TRACE("Feature %s located at %p\n",debugstr_an(feat,4),feature);
1231 psc->feature_count++;
1233 if (psc->features)
1234 psc->features = HeapReAlloc(GetProcessHeap(), 0, psc->features, psc->feature_count * sizeof(LoadedFeature));
1235 else
1236 psc->features = HeapAlloc(GetProcessHeap(), 0, psc->feature_count * sizeof(LoadedFeature));
1238 lstrcpynA(psc->features[psc->feature_count - 1].tag, feat, 5);
1239 lstrcpynA(psc->features[psc->feature_count - 1].script, script, 5);
1240 psc->features[psc->feature_count - 1].feature = feature;
1241 return feature;
1244 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)
1246 const GSUB_Feature *feature;
1248 feature = load_GSUB_feature(hdc, psa, psc, feat);
1249 if (!feature)
1250 return GSUB_E_NOFEATURE;
1252 TRACE("applying feature %s\n",feat);
1253 return GSUB_apply_feature_all_lookups(psc->GSUB_Table, feature, glyphs, index, write_dir, glyph_count);
1256 static VOID *load_gsub_table(HDC hdc)
1258 VOID* GSUB_Table = NULL;
1259 int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
1260 if (length != GDI_ERROR)
1262 GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
1263 GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
1264 TRACE("Loaded GSUB table of %i bytes\n",length);
1266 return GSUB_Table;
1269 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)
1271 WORD *glyphs;
1272 INT glyph_count = count;
1273 INT rc;
1275 glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD)*(count*2));
1276 GetGlyphIndicesW(hdc, chars, count, glyphs, 0);
1277 rc = apply_GSUB_feature_to_glyph(hdc, psa, psc, glyphs, 0, write_dir, &glyph_count, feature);
1278 if (rc > GSUB_E_NOGLYPH)
1279 rc = count - glyph_count;
1280 else
1281 rc = 0;
1283 HeapFree(GetProcessHeap(),0,glyphs);
1284 return rc;
1287 static WORD GDEF_get_glyph_class(const GDEF_Header *header, WORD glyph)
1289 int offset;
1290 WORD class = 0;
1291 const GDEF_ClassDefFormat1 *cf1;
1293 if (!header)
1294 return 0;
1296 offset = GET_BE_WORD(header->GlyphClassDef);
1297 if (!offset)
1298 return 0;
1300 cf1 = (GDEF_ClassDefFormat1*)(((BYTE*)header)+offset);
1301 if (GET_BE_WORD(cf1->ClassFormat) == 1)
1303 if (glyph >= GET_BE_WORD(cf1->StartGlyph))
1305 int index = glyph - GET_BE_WORD(cf1->StartGlyph);
1306 if (index < GET_BE_WORD(cf1->GlyphCount))
1307 class = GET_BE_WORD(cf1->ClassValueArray[index]);
1310 else if (GET_BE_WORD(cf1->ClassFormat) == 2)
1312 const GDEF_ClassDefFormat2 *cf2 = (GDEF_ClassDefFormat2*)cf1;
1313 int i, top;
1314 top = GET_BE_WORD(cf2->ClassRangeCount);
1315 for (i = 0; i < top; i++)
1317 if (glyph >= GET_BE_WORD(cf2->ClassRangeRecord[i].Start) &&
1318 glyph <= GET_BE_WORD(cf2->ClassRangeRecord[i].End))
1320 class = GET_BE_WORD(cf2->ClassRangeRecord[i].Class);
1321 break;
1325 else
1326 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1->ClassFormat));
1328 return class;
1331 static VOID *load_gdef_table(HDC hdc)
1333 VOID* GDEF_Table = NULL;
1334 int length = GetFontData(hdc, GDEF_TAG , 0, NULL, 0);
1335 if (length != GDI_ERROR)
1337 GDEF_Table = HeapAlloc(GetProcessHeap(),0,length);
1338 GetFontData(hdc, GDEF_TAG , 0, GDEF_Table, length);
1339 TRACE("Loaded GDEF table of %i bytes\n",length);
1341 return GDEF_Table;
1344 static void GDEF_UpdateGlyphProps(HDC hdc, ScriptCache *psc, const WORD *pwGlyphs, const WORD cGlyphs, WORD* pwLogClust, const WORD cChars, SCRIPT_GLYPHPROP *pGlyphProp)
1346 int i;
1348 if (!psc->GDEF_Table)
1349 psc->GDEF_Table = load_gdef_table(hdc);
1351 for (i = 0; i < cGlyphs; i++)
1353 WORD class;
1354 int char_count = 0;
1355 int k;
1357 for (k = 0; k < cChars; k++)
1358 if (pwLogClust[k] == i)
1359 char_count++;
1361 class = GDEF_get_glyph_class(psc->GDEF_Table, pwGlyphs[i]);
1363 switch (class)
1365 case 0:
1366 case BaseGlyph:
1367 pGlyphProp[i].sva.fClusterStart = 1;
1368 pGlyphProp[i].sva.fDiacritic = 0;
1369 pGlyphProp[i].sva.fZeroWidth = 0;
1370 break;
1371 case LigatureGlyph:
1372 pGlyphProp[i].sva.fClusterStart = 1;
1373 pGlyphProp[i].sva.fDiacritic = 0;
1374 pGlyphProp[i].sva.fZeroWidth = 0;
1375 break;
1376 case MarkGlyph:
1377 pGlyphProp[i].sva.fClusterStart = 0;
1378 pGlyphProp[i].sva.fDiacritic = 1;
1379 pGlyphProp[i].sva.fZeroWidth = 1;
1380 break;
1381 case ComponentGlyph:
1382 pGlyphProp[i].sva.fClusterStart = 0;
1383 pGlyphProp[i].sva.fDiacritic = 0;
1384 pGlyphProp[i].sva.fZeroWidth = 0;
1385 break;
1386 default:
1387 ERR("Unknown glyph class %i\n",class);
1388 pGlyphProp[i].sva.fClusterStart = 1;
1389 pGlyphProp[i].sva.fDiacritic = 0;
1390 pGlyphProp[i].sva.fZeroWidth = 0;
1393 if (char_count == 0)
1394 pGlyphProp[i].sva.fClusterStart = 0;
1398 static void UpdateClustersFromGlyphProp(const int cGlyphs, const int cChars, WORD* pwLogClust, SCRIPT_GLYPHPROP *pGlyphProp)
1400 int i;
1402 for (i = 0; i < cGlyphs; i++)
1404 if (!pGlyphProp[i].sva.fClusterStart)
1406 int j;
1407 for (j = 0; j < cChars; j++)
1409 if (pwLogClust[j] == i)
1411 int k = j;
1412 while (!pGlyphProp[pwLogClust[k]].sva.fClusterStart && k >= 0 && k <cChars)
1413 k-=1;
1414 if (pGlyphProp[pwLogClust[k]].sva.fClusterStart)
1415 pwLogClust[j] = pwLogClust[k];
1422 static void UpdateClusters(int nextIndex, int changeCount, int write_dir, int chars, WORD* pwLogClust )
1424 if (changeCount == 0)
1425 return;
1426 else
1428 int i;
1429 int target_glyph = nextIndex - write_dir;
1430 int seeking_glyph;
1431 int target_index = -1;
1432 int replacing_glyph = -1;
1433 int changed = 0;
1434 int top_logclust = 0;
1436 if (changeCount > 0)
1438 if (write_dir > 0)
1439 target_glyph = nextIndex - changeCount;
1440 else
1441 target_glyph = nextIndex + (changeCount + 1);
1444 seeking_glyph = target_glyph;
1445 for (i = 0; i < chars; i++)
1446 if (pwLogClust[i] > top_logclust)
1447 top_logclust = pwLogClust[i];
1449 do {
1450 if (write_dir > 0)
1451 for (i = 0; i < chars; i++)
1453 if (pwLogClust[i] == seeking_glyph)
1455 target_index = i;
1456 break;
1459 else
1460 for (i = chars - 1; i >= 0; i--)
1462 if (pwLogClust[i] == seeking_glyph)
1464 target_index = i;
1465 break;
1468 if (target_index == -1)
1469 seeking_glyph ++;
1471 while (target_index == -1 && seeking_glyph <= top_logclust);
1473 if (target_index == -1)
1475 ERR("Unable to find target glyph\n");
1476 return;
1479 if (changeCount < 0)
1481 /* merge glyphs */
1482 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1484 if (pwLogClust[i] == target_glyph)
1485 continue;
1486 if(pwLogClust[i] == replacing_glyph)
1487 pwLogClust[i] = target_glyph;
1488 else
1490 changed--;
1491 if (changed >= changeCount)
1493 replacing_glyph = pwLogClust[i];
1494 pwLogClust[i] = target_glyph;
1496 else
1497 break;
1501 /* renumber trailing indexes*/
1502 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1504 if (pwLogClust[i] != target_glyph)
1505 pwLogClust[i] += changeCount;
1508 else
1510 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1511 pwLogClust[i] += changeCount;
1516 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 )
1518 if (psc->GSUB_Table)
1520 const GSUB_Feature *feature;
1521 const GSUB_LookupList *lookup;
1522 const GSUB_Header *header = psc->GSUB_Table;
1523 int lookup_index, lookup_count;
1525 feature = load_GSUB_feature(hdc, psa, psc, feat);
1526 if (!feature)
1527 return GSUB_E_NOFEATURE;
1529 TRACE("applying feature %s\n",debugstr_an(feat,4));
1530 lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1531 lookup_count = GET_BE_WORD(feature->LookupCount);
1532 TRACE("%i lookups\n", lookup_count);
1533 for (lookup_index = 0; lookup_index < lookup_count; lookup_index++)
1535 int i;
1537 if (write_dir > 0)
1538 i = 0;
1539 else
1540 i = *pcGlyphs-1;
1541 TRACE("applying lookup (%i/%i)\n",lookup_index,lookup_count);
1542 while(i < *pcGlyphs && i >= 0)
1544 INT nextIndex;
1545 INT prevCount = *pcGlyphs;
1547 nextIndex = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[lookup_index]), pwOutGlyphs, i, write_dir, pcGlyphs);
1548 if (*pcGlyphs != prevCount)
1550 UpdateClusters(nextIndex, *pcGlyphs - prevCount, write_dir, cChars, pwLogClust);
1551 i = nextIndex;
1553 else
1554 i+=write_dir;
1557 return *pcGlyphs;
1559 return GSUB_E_NOFEATURE;
1562 static inline BOOL get_GSUB_Indic2(SCRIPT_ANALYSIS *psa, ScriptCache *psc)
1564 return(GSUB_get_script_table(psc->GSUB_Table,ShapingData[psa->eScript].newOtTag)!=NULL);
1567 static WCHAR neighbour_char(int i, int delta, const WCHAR* chars, INT cchLen)
1569 if (i + delta < 0)
1570 return 0;
1571 if ( i+ delta >= cchLen)
1572 return 0;
1574 i += delta;
1576 return chars[i];
1579 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
1581 if (i + delta < 0)
1583 if (psa->fLinkBefore)
1584 return jtR;
1585 else
1586 return jtU;
1588 if ( i+ delta >= cchLen)
1590 if (psa->fLinkAfter)
1591 return jtL;
1592 else
1593 return jtU;
1596 i += delta;
1598 if (context_type[i] == jtT)
1599 return neighbour_joining_type(i,delta,context_type,cchLen,psa);
1600 else
1601 return context_type[i];
1604 static inline BOOL right_join_causing(CHAR joining_type)
1606 return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
1609 static inline BOOL left_join_causing(CHAR joining_type)
1611 return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
1614 static inline BOOL word_break_causing(WCHAR chr)
1616 /* we are working within a string of characters already guareented to
1617 be within one script, Syriac, so we do not worry about any character
1618 other than the space character outside of that range */
1619 return (chr == 0 || chr == 0x20 );
1623 * ContextualShape_Arabic
1625 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1627 CHAR *context_type;
1628 INT *context_shape;
1629 INT dirR, dirL;
1630 int i;
1632 if (*pcGlyphs != cChars)
1634 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1635 return;
1638 if (!psa->fLogicalOrder && psa->fRTL)
1640 dirR = 1;
1641 dirL = -1;
1643 else
1645 dirR = -1;
1646 dirL = 1;
1649 if (!psc->GSUB_Table)
1650 psc->GSUB_Table = load_gsub_table(hdc);
1652 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1653 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1655 for (i = 0; i < cChars; i++)
1656 context_type[i] = get_table_entry( wine_shaping_table, pwcChars[i] );
1658 for (i = 0; i < cChars; i++)
1660 if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1661 context_shape[i] = Xr;
1662 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1663 context_shape[i] = Xl;
1664 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)))
1665 context_shape[i] = Xm;
1666 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1667 context_shape[i] = Xr;
1668 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1669 context_shape[i] = Xl;
1670 else
1671 context_shape[i] = Xn;
1674 /* Contextual Shaping */
1675 i = 0;
1676 while(i < *pcGlyphs)
1678 BOOL shaped = FALSE;
1680 if (psc->GSUB_Table)
1682 INT nextIndex;
1683 INT prevCount = *pcGlyphs;
1684 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1685 if (nextIndex > GSUB_E_NOGLYPH)
1687 i = nextIndex;
1688 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1690 shaped = (nextIndex > GSUB_E_NOGLYPH);
1693 if (!shaped)
1695 if (context_shape[i] == Xn)
1697 WORD newGlyph = pwOutGlyphs[i];
1698 if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
1700 /* fall back to presentation form B */
1701 WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
1702 if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
1703 pwOutGlyphs[i] = newGlyph;
1706 i++;
1710 HeapFree(GetProcessHeap(),0,context_shape);
1711 HeapFree(GetProcessHeap(),0,context_type);
1715 * ContextualShape_Syriac
1718 #define ALAPH 0x710
1719 #define DALATH 0x715
1720 #define RISH 0x72A
1722 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1724 CHAR *context_type;
1725 INT *context_shape;
1726 INT dirR, dirL;
1727 int i;
1729 if (*pcGlyphs != cChars)
1731 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1732 return;
1735 if (!psa->fLogicalOrder && psa->fRTL)
1737 dirR = 1;
1738 dirL = -1;
1740 else
1742 dirR = -1;
1743 dirL = 1;
1746 if (!psc->GSUB_Table)
1747 psc->GSUB_Table = load_gsub_table(hdc);
1749 if (!psc->GSUB_Table)
1750 return;
1752 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1753 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1755 for (i = 0; i < cChars; i++)
1756 context_type[i] = get_table_entry( wine_shaping_table, pwcChars[i] );
1758 for (i = 0; i < cChars; i++)
1760 if (pwcChars[i] == ALAPH)
1762 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1764 if (left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1765 context_shape[i] = Afj;
1766 else if ( rchar != DALATH && rchar != RISH &&
1767 !left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) &&
1768 word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1769 context_shape[i] = Afn;
1770 else if ( (rchar == DALATH || rchar == RISH) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1771 context_shape[i] = Afx;
1772 else
1773 context_shape[i] = Xn;
1775 else if (context_type[i] == jtR &&
1776 right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1777 context_shape[i] = Xr;
1778 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1779 context_shape[i] = Xl;
1780 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)))
1781 context_shape[i] = Xm;
1782 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1783 context_shape[i] = Xr;
1784 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1785 context_shape[i] = Xl;
1786 else
1787 context_shape[i] = Xn;
1790 /* Contextual Shaping */
1791 i = 0;
1792 while(i < *pcGlyphs)
1794 INT nextIndex;
1795 INT prevCount = *pcGlyphs;
1796 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1797 if (nextIndex > GSUB_E_NOGLYPH)
1799 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1800 i = nextIndex;
1802 else
1803 i++;
1806 HeapFree(GetProcessHeap(),0,context_shape);
1807 HeapFree(GetProcessHeap(),0,context_type);
1811 * ContextualShape_Phags_pa
1814 #define phags_pa_CANDRABINDU 0xA873
1815 #define phags_pa_START 0xA840
1816 #define phags_pa_END 0xA87F
1818 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1820 INT *context_shape;
1821 INT dirR, dirL;
1822 int i;
1824 if (*pcGlyphs != cChars)
1826 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1827 return;
1830 if (!psa->fLogicalOrder && psa->fRTL)
1832 dirR = 1;
1833 dirL = -1;
1835 else
1837 dirR = -1;
1838 dirL = 1;
1841 if (!psc->GSUB_Table)
1842 psc->GSUB_Table = load_gsub_table(hdc);
1844 if (!psc->GSUB_Table)
1845 return;
1847 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1849 for (i = 0; i < cChars; i++)
1851 if (pwcChars[i] >= phags_pa_START && pwcChars[i] <= phags_pa_END)
1853 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1854 WCHAR lchar = neighbour_char(i,dirL,pwcChars,cChars);
1855 BOOL jrchar = (rchar != phags_pa_CANDRABINDU && rchar >= phags_pa_START && rchar <= phags_pa_END);
1856 BOOL jlchar = (lchar != phags_pa_CANDRABINDU && lchar >= phags_pa_START && lchar <= phags_pa_END);
1858 if (jrchar && jlchar)
1859 context_shape[i] = Xm;
1860 else if (jrchar)
1861 context_shape[i] = Xr;
1862 else if (jlchar)
1863 context_shape[i] = Xl;
1864 else
1865 context_shape[i] = Xn;
1867 else
1868 context_shape[i] = -1;
1871 /* Contextual Shaping */
1872 i = 0;
1873 while(i < *pcGlyphs)
1875 if (context_shape[i] >= 0)
1877 INT nextIndex;
1878 INT prevCount = *pcGlyphs;
1879 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1880 if (nextIndex > GSUB_E_NOGLYPH)
1882 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1883 i = nextIndex;
1885 else
1886 i++;
1888 else
1889 i++;
1892 HeapFree(GetProcessHeap(),0,context_shape);
1895 static void ReplaceInsertChars(HDC hdc, INT cWalk, INT* pcChars, WCHAR *pwOutChars, const WCHAR *replacements)
1897 int i;
1899 /* Replace */
1900 pwOutChars[cWalk] = replacements[0];
1901 cWalk=cWalk+1;
1903 /* Insert */
1904 for (i = 1; replacements[i] != 0x0000 && i < 3; i++)
1906 int j;
1907 for (j = *pcChars; j > cWalk; j--)
1908 pwOutChars[j] = pwOutChars[j-1];
1909 *pcChars= *pcChars+1;
1910 pwOutChars[cWalk] = replacements[i];
1911 cWalk = cWalk+1;
1915 static void DecomposeVowels(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const VowelComponents vowels[], WORD* pwLogClust, INT cChars)
1917 int i;
1918 int cWalk;
1920 for (cWalk = 0; cWalk < *pcChars; cWalk++)
1922 for (i = 0; vowels[i].base != 0x0; i++)
1924 if (pwOutChars[cWalk] == vowels[i].base)
1926 int o = 0;
1927 ReplaceInsertChars(hdc, cWalk, pcChars, pwOutChars, vowels[i].parts);
1928 if (vowels[i].parts[1]) { cWalk++; o++; }
1929 if (vowels[i].parts[2]) { cWalk++; o++; }
1930 UpdateClusters(cWalk, o, 1, cChars, pwLogClust);
1931 break;
1937 static void ComposeConsonants(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const ConsonantComponents consonants[], WORD* pwLogClust)
1939 int i;
1940 int offset = 0;
1941 int cWalk;
1943 for (cWalk = 0; cWalk < *pcChars; cWalk++)
1945 for (i = 0; consonants[i].output!= 0x0; i++)
1947 int j;
1948 for (j = 0; j + cWalk < *pcChars && consonants[i].parts[j]!=0x0; j++)
1949 if (pwOutChars[cWalk+j] != consonants[i].parts[j])
1950 break;
1952 if (consonants[i].parts[j]==0x0) /* matched all */
1954 int k;
1955 j--;
1956 pwOutChars[cWalk] = consonants[i].output;
1957 for(k = cWalk+1; k < *pcChars - j; k++)
1958 pwOutChars[k] = pwOutChars[k+j];
1959 *pcChars = *pcChars - j;
1960 for (k = j ; k > 0; k--)
1961 pwLogClust[cWalk + k + offset] = pwLogClust[cWalk + offset];
1962 offset += j;
1963 for (k = cWalk + j + offset; k < *pcChars + offset; k++)
1964 pwLogClust[k]--;
1965 break;
1968 cWalk++;
1972 static void Reorder_Ra_follows_base(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1974 if (s->ralf >= 0)
1976 int j;
1977 WORD Ra = pwChar[s->start];
1978 WORD H = pwChar[s->start+1];
1980 TRACE("Doing reorder of Ra to %i\n",s->base);
1981 for (j = s->start; j < s->base-1; j++)
1982 pwChar[j] = pwChar[j+2];
1983 pwChar[s->base-1] = Ra;
1984 pwChar[s->base] = H;
1986 s->ralf = s->base-1;
1987 s->base -= 2;
1991 static void Reorder_Ra_follows_matra(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
1993 if (s->ralf >= 0)
1995 int j,loc;
1996 int stop = (s->blwf >=0)? s->blwf+1 : s->base;
1997 WORD Ra = pwChar[s->start];
1998 WORD H = pwChar[s->start+1];
1999 for (loc = s->end; loc > stop; loc--)
2000 if (lexical(pwChar[loc]) == lex_Matra_post || lexical(pwChar[loc]) == lex_Matra_below)
2001 break;
2003 TRACE("Doing reorder of Ra to %i\n",loc);
2004 for (j = s->start; j < loc-1; j++)
2005 pwChar[j] = pwChar[j+2];
2006 pwChar[loc-1] = Ra;
2007 pwChar[loc] = H;
2009 s->ralf = loc-1;
2010 s->base -= 2;
2011 if (s->blwf >= 0) s->blwf -= 2;
2012 if (s->pref >= 0) s->pref -= 2;
2016 static void Reorder_Ra_follows_syllable(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2018 if (s->ralf >= 0)
2020 int j;
2021 WORD Ra = pwChar[s->start];
2022 WORD H = pwChar[s->start+1];
2024 TRACE("Doing reorder of Ra to %i\n",s->end-1);
2025 for (j = s->start; j < s->end-1; j++)
2026 pwChar[j] = pwChar[j+2];
2027 pwChar[s->end-1] = Ra;
2028 pwChar[s->end] = H;
2030 s->ralf = s->end-1;
2031 s->base -= 2;
2032 if (s->blwf >= 0) s->blwf -= 2;
2033 if (s->pref >= 0) s->pref -= 2;
2037 static void Reorder_Matra_precede_base(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2039 int i;
2041 /* reorder Matras */
2042 if (s->end > s->base)
2044 for (i = 1; i <= s->end-s->base; i++)
2046 if (lexical(pwChar[s->base+i]) == lex_Matra_pre)
2048 int j;
2049 WCHAR c = pwChar[s->base+i];
2050 TRACE("Doing reorder of %x %x\n",c,pwChar[s->base]);
2051 for (j = s->base+i; j > s->base; j--)
2052 pwChar[j] = pwChar[j-1];
2053 pwChar[s->base] = c;
2055 if (s->ralf >= s->base) s->ralf++;
2056 if (s->blwf >= s->base) s->blwf++;
2057 if (s->pref >= s->base) s->pref++;
2058 s->base ++;
2064 static void Reorder_Matra_precede_syllable(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2066 int i;
2068 /* reorder Matras */
2069 if (s->end > s->base)
2071 for (i = 1; i <= s->end-s->base; i++)
2073 if (lexical(pwChar[s->base+i]) == lex_Matra_pre)
2075 int j;
2076 WCHAR c = pwChar[s->base+i];
2077 TRACE("Doing reorder of %x to %i\n",c,s->start);
2078 for (j = s->base+i; j > s->start; j--)
2079 pwChar[j] = pwChar[j-1];
2080 pwChar[s->start] = c;
2082 if (s->ralf >= 0) s->ralf++;
2083 if (s->blwf >= 0) s->blwf++;
2084 if (s->pref >= 0) s->pref++;
2085 s->base ++;
2091 static void SecondReorder_Blwf_follows_matra(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2093 if (s->blwf >= 0 && g->blwf > g->base)
2095 int j,loc;
2096 int g_offset;
2097 for (loc = s->end; loc > s->blwf; loc--)
2098 if (lexical(pwChar[loc]) == lex_Matra_below || lexical(pwChar[loc]) == lex_Matra_above || lexical(pwChar[loc]) == lex_Matra_post)
2099 break;
2101 g_offset = (loc - s->blwf) - 1;
2103 if (loc != s->blwf)
2105 WORD blwf = glyphs[g->blwf];
2106 TRACE("Doing reorder of Below-base to %i (glyph offset %i)\n",loc,g_offset);
2107 /* do not care about the pwChar array anymore, just the glyphs */
2108 for (j = 0; j < g_offset; j++)
2109 glyphs[g->blwf + j] = glyphs[g->blwf + j + 1];
2110 glyphs[g->blwf + g_offset] = blwf;
2115 static void SecondReorder_Matra_precede_base(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2117 int i;
2119 /* reorder previously moved Matras to correct position*/
2120 for (i = s->start; i < s->base; i++)
2122 if (lexical(pwChar[i]) == lex_Matra_pre)
2124 int j;
2125 int g_start = g->start + i - s->start;
2126 if (g_start < g->base -1 )
2128 WCHAR og = glyphs[g_start];
2129 TRACE("Doing reorder of matra from %i to %i\n",g_start,g->base);
2130 for (j = g_start; j < g->base-1; j++)
2131 glyphs[j] = glyphs[j+1];
2132 glyphs[g->base-1] = og;
2138 static void SecondReorder_Pref_precede_base(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2140 if (s->pref >= 0 && g->pref > g->base)
2142 int j;
2143 WCHAR og = glyphs[g->pref];
2144 TRACE("Doing reorder of pref from %i to %i\n",g->pref,g->base);
2145 for (j = g->pref; j > g->base; j--)
2146 glyphs[j] = glyphs[j-1];
2147 glyphs[g->base] = og;
2151 static void Reorder_Like_Sinhala(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2153 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2154 if (s->start == s->base && s->base == s->end) return;
2155 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2157 Reorder_Ra_follows_base(pwChar, s, lexical);
2158 Reorder_Matra_precede_base(pwChar, s, lexical);
2161 static void Reorder_Like_Devanagari(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2163 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2164 if (s->start == s->base && s->base == s->end) return;
2165 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2167 Reorder_Ra_follows_matra(pwChar, s, lexical);
2168 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2171 static void Reorder_Like_Bengali(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2173 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2174 if (s->start == s->base && s->base == s->end) return;
2175 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2177 Reorder_Ra_follows_base(pwChar, s, lexical);
2178 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2181 static void Reorder_Like_Kannada(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2183 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2184 if (s->start == s->base && s->base == s->end) return;
2185 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2187 Reorder_Ra_follows_syllable(pwChar, s, lexical);
2188 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2191 static void SecondReorder_Like_Telugu(LPWSTR pwChar, IndicSyllable *s, WORD* pwGlyphs, IndicSyllable *g, lexical_function lexical)
2193 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2194 TRACE("Glyphs (%i..%i..%i)\n",g->start,g->base,g->end);
2195 if (s->start == s->base && s->base == s->end) return;
2196 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2198 SecondReorder_Blwf_follows_matra(pwChar, s, pwGlyphs, g, lexical);
2201 static void SecondReorder_Like_Tamil(LPWSTR pwChar, IndicSyllable *s, WORD* pwGlyphs, IndicSyllable *g, lexical_function lexical)
2203 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2204 TRACE("Glyphs (%i..%i..%i)\n",g->start,g->base,g->end);
2205 if (s->start == s->base && s->base == s->end) return;
2206 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2208 SecondReorder_Matra_precede_base(pwChar, s, pwGlyphs, g, lexical);
2209 SecondReorder_Pref_precede_base(pwChar, s, pwGlyphs, g, lexical);
2213 static inline void shift_syllable_glyph_indexs(IndicSyllable *glyph_index, INT index, INT shift)
2215 if (shift == 0)
2216 return;
2218 if (glyph_index->start > index)
2219 glyph_index->start += shift;
2220 if (glyph_index->base > index)
2221 glyph_index->base+= shift;
2222 if (glyph_index->end > index)
2223 glyph_index->end+= shift;
2224 if (glyph_index->ralf > index)
2225 glyph_index->ralf+= shift;
2226 if (glyph_index->blwf > index)
2227 glyph_index->blwf+= shift;
2228 if (glyph_index->pref > index)
2229 glyph_index->pref+= shift;
2232 static void Apply_Indic_BasicForm(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwChars, INT cChars, IndicSyllable *syllable, WORD *pwOutGlyphs, INT* pcGlyphs, WORD *pwLogClust, lexical_function lexical, IndicSyllable *glyph_index, const GSUB_Feature *feature )
2234 int index = glyph_index->start;
2236 if (!feature)
2237 return;
2239 while(index <= glyph_index->end)
2241 INT nextIndex;
2242 INT prevCount = *pcGlyphs;
2243 nextIndex = GSUB_apply_feature_all_lookups(psc->GSUB_Table, feature, pwOutGlyphs, index, 1, pcGlyphs);
2244 if (nextIndex > GSUB_E_NOGLYPH)
2246 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2247 shift_syllable_glyph_indexs(glyph_index,index,*pcGlyphs - prevCount);
2248 index = nextIndex;
2250 else
2251 index++;
2255 static inline INT find_consonant_halant(WCHAR* pwChars, INT index, INT end, lexical_function lexical)
2257 int i = 0;
2258 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)))))
2259 i++;
2260 if (index + i <= end-1)
2261 return index + i;
2262 else
2263 return -1;
2266 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)
2268 INT index, nextIndex;
2269 INT count,g_offset;
2271 count = syllable->base - syllable->start;
2273 g_offset = 0;
2274 index = find_consonant_halant(&pwChars[syllable->start], 0, count, lexical);
2275 while (index >= 0 && index + g_offset < (glyph_index->base - glyph_index->start))
2277 INT prevCount = *pcGlyphs;
2278 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, index+glyph_index->start+g_offset, 1, pcGlyphs, feature);
2279 if (nextIndex > GSUB_E_NOGLYPH)
2281 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2282 shift_syllable_glyph_indexs(glyph_index, index + glyph_index->start + g_offset, (*pcGlyphs - prevCount));
2283 g_offset += (*pcGlyphs - prevCount);
2286 index+=2;
2287 index = find_consonant_halant(&pwChars[syllable->start], index, count, lexical);
2291 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)
2293 INT nextIndex;
2294 INT prevCount = *pcGlyphs;
2296 if (syllable->ralf >= 0)
2298 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, glyph_index->ralf, 1, pcGlyphs, "rphf");
2299 if (nextIndex > GSUB_E_NOGLYPH)
2301 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2302 shift_syllable_glyph_indexs(glyph_index,glyph_index->ralf,*pcGlyphs - prevCount);
2307 static inline INT find_halant_consonant(WCHAR* pwChars, INT index, INT end, lexical_function lexical)
2309 int i = 0;
2310 while (index + i < end-1 && !(lexical(pwChars[index+i]) == lex_Halant &&
2311 ((index + i < end-2 && lexical(pwChars[index+i]) == lex_Nukta && is_consonant(lexical(pwChars[index+i+1]))) ||
2312 is_consonant(lexical(pwChars[index+i+1])))))
2313 i++;
2314 if (index + i <= end-1)
2315 return index+i;
2316 else
2317 return -1;
2320 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)
2322 INT index, nextIndex;
2323 INT count, g_offset=0;
2324 INT ralf = syllable->ralf;
2326 count = syllable->end - syllable->base;
2328 index = find_halant_consonant(&pwChars[syllable->base], 0, count, lexical);
2330 while (index >= 0)
2332 INT prevCount = *pcGlyphs;
2333 if (ralf >=0 && ralf < index)
2335 g_offset--;
2336 ralf = -1;
2339 if (!modern)
2341 WORD g = pwOutGlyphs[index+glyph_index->base+g_offset];
2342 pwOutGlyphs[index+glyph_index->base+g_offset] = pwOutGlyphs[index+glyph_index->base+g_offset+1];
2343 pwOutGlyphs[index+glyph_index->base+g_offset+1] = g;
2346 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, index+glyph_index->base+g_offset, 1, pcGlyphs, feat);
2347 if (nextIndex > GSUB_E_NOGLYPH)
2349 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2350 shift_syllable_glyph_indexs(glyph_index,index+glyph_index->start+g_offset, (*pcGlyphs - prevCount));
2351 g_offset += (*pcGlyphs - prevCount);
2353 else if (!modern)
2355 WORD g = pwOutGlyphs[index+glyph_index->base+g_offset];
2356 pwOutGlyphs[index+glyph_index->base+g_offset] = pwOutGlyphs[index+glyph_index->base+g_offset+1];
2357 pwOutGlyphs[index+glyph_index->base+g_offset+1] = g;
2360 index+=2;
2361 index = find_halant_consonant(&pwChars[syllable->base], index, count, lexical);
2365 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)
2367 int c;
2368 int overall_shift = 0;
2369 const GSUB_Feature *locl = (modern)?load_GSUB_feature(hdc, psa, psc, "locl"):NULL;
2370 const GSUB_Feature *nukt = load_GSUB_feature(hdc, psa, psc, "nukt");
2371 const GSUB_Feature *akhn = load_GSUB_feature(hdc, psa, psc, "akhn");
2372 const GSUB_Feature *rkrf = (modern)?load_GSUB_feature(hdc, psa, psc, "rkrf"):NULL;
2373 const GSUB_Feature *pstf = load_GSUB_feature(hdc, psa, psc, "pstf");
2374 const GSUB_Feature *vatu = (!rkrf)?load_GSUB_feature(hdc, psa, psc, "vatu"):NULL;
2375 const GSUB_Feature *cjct = (modern)?load_GSUB_feature(hdc, psa, psc, "cjct"):NULL;
2376 BOOL rphf = (load_GSUB_feature(hdc, psa, psc, "rphf") != NULL);
2377 BOOL pref = (load_GSUB_feature(hdc, psa, psc, "pref") != NULL);
2378 BOOL blwf = (load_GSUB_feature(hdc, psa, psc, "blwf") != NULL);
2379 BOOL half = (load_GSUB_feature(hdc, psa, psc, "half") != NULL);
2380 IndicSyllable glyph_indexs;
2382 for (c = 0; c < syllable_count; c++)
2384 int old_end;
2385 memcpy(&glyph_indexs, &syllables[c], sizeof(IndicSyllable));
2386 shift_syllable_glyph_indexs(&glyph_indexs, -1, overall_shift);
2387 old_end = glyph_indexs.end;
2389 if (locl)
2391 TRACE("applying feature locl\n");
2392 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, locl);
2394 if (nukt)
2396 TRACE("applying feature nukt\n");
2397 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, nukt);
2399 if (akhn)
2401 TRACE("applying feature akhn\n");
2402 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, akhn);
2405 if (rphf)
2406 Apply_Indic_Rphf(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs);
2407 if (rkrf)
2409 TRACE("applying feature rkrf\n");
2410 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, rkrf);
2412 if (pref)
2413 Apply_Indic_PostBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, modern, "pref");
2414 if (blwf)
2416 if (!modern)
2417 Apply_Indic_PreBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, "blwf");
2419 Apply_Indic_PostBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, modern, "blwf");
2422 if (half)
2423 Apply_Indic_PreBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, "half");
2424 if (pstf)
2426 TRACE("applying feature pstf\n");
2427 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, pstf);
2429 if (vatu)
2431 TRACE("applying feature vatu\n");
2432 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, vatu);
2434 if (cjct)
2436 TRACE("applying feature cjct\n");
2437 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, cjct);
2440 if (second_reorder)
2441 second_reorder(pwChars, &syllables[c], pwOutGlyphs, &glyph_indexs, lexical);
2443 overall_shift += glyph_indexs.end - old_end;
2447 static inline int unicode_lex(WCHAR c)
2449 int type;
2451 if (!c) return lex_Generic;
2452 if (c == 0x200D) return lex_ZWJ;
2453 if (c == 0x200C) return lex_ZWNJ;
2454 if (c == 0x00A0) return lex_NBSP;
2456 type = get_table_entry( indic_syllabic_table, c );
2458 if ((type & 0x00ff) != 0x0007) type = type & 0x00ff;
2460 switch( type )
2462 case 0x0d07: /* Unknown */
2463 case 0x0e07: /* Unknwon */
2464 default: return lex_Generic;
2465 case 0x0001:
2466 case 0x0002:
2467 case 0x0011:
2468 case 0x0012:
2469 case 0x0013:
2470 case 0x0014: return lex_Modifier;
2471 case 0x0003:
2472 case 0x0009:
2473 case 0x000a:
2474 case 0x000b:
2475 case 0x000d:
2476 case 0x000e:
2477 case 0x000f:
2478 case 0x0010: return lex_Consonant;
2479 case 0x0004: return lex_Nukta;
2480 case 0x0005: return lex_Halant;
2481 case 0x0006:
2482 case 0x0008: return lex_Vowel;
2483 case 0x0007:
2484 case 0x0107: return lex_Matra_post;
2485 case 0x0207:
2486 case 0x0307: return lex_Matra_pre;
2487 case 0x0807:
2488 case 0x0907:
2489 case 0x0a07:
2490 case 0x0b07:
2491 case 0x0c07:
2492 case 0x0407: return lex_Composed_Vowel;
2493 case 0x0507: return lex_Matra_above;
2494 case 0x0607: return lex_Matra_below;
2495 case 0x000c: return lex_Ra;
2499 static int sinhala_lex(WCHAR c)
2501 switch (c)
2503 case 0x0DDA:
2504 case 0x0DDD:
2505 case 0x0DDC:
2506 case 0x0DDE: return lex_Matra_post;
2507 default:
2508 return unicode_lex(c);
2512 static const VowelComponents Sinhala_vowels[] = {
2513 {0x0DDA, {0x0DD9,0x0DDA,0x0}},
2514 {0x0DDC, {0x0DD9,0x0DDC,0x0}},
2515 {0x0DDD, {0x0DD9,0x0DDD,0x0}},
2516 {0x0DDE, {0x0DD9,0x0DDE,0x0}},
2517 {0x0000, {0x0000,0x0000,0x0}}};
2519 static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2521 int cCount = cChars;
2522 int i;
2523 WCHAR *input;
2524 IndicSyllable *syllables = NULL;
2525 int syllable_count = 0;
2527 if (*pcGlyphs != cChars)
2529 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2530 return;
2533 input = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR) * (cChars * 3));
2535 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2537 /* Step 1: Decompose multi part vowels */
2538 DecomposeVowels(hdc, input, &cCount, Sinhala_vowels, pwLogClust, cChars);
2540 TRACE("New double vowel expanded string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2542 /* Step 2: Reorder within Syllables */
2543 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, sinhala_lex, Reorder_Like_Sinhala, TRUE);
2544 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2546 /* Step 3: Strip dangling joiners */
2547 for (i = 0; i < cCount; i++)
2549 if ((input[i] == 0x200D || input[i] == 0x200C) &&
2550 (i == 0 || input[i-1] == 0x0020 || i == cCount-1 || input[i+1] == 0x0020))
2551 input[i] = 0x0020;
2554 /* Step 4: Base Form application to syllables */
2555 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2556 *pcGlyphs = cCount;
2557 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, sinhala_lex, NULL, TRUE);
2559 HeapFree(GetProcessHeap(),0,input);
2560 HeapFree(GetProcessHeap(),0,syllables);
2563 static int devanagari_lex(WCHAR c)
2565 switch (c)
2567 case 0x0930: return lex_Ra;
2568 default:
2569 return unicode_lex(c);
2573 static const ConsonantComponents Devanagari_consonants[] ={
2574 {{0x0928, 0x093C, 0x00000}, 0x0929},
2575 {{0x0930, 0x093C, 0x00000}, 0x0931},
2576 {{0x0933, 0x093C, 0x00000}, 0x0934},
2577 {{0x0915, 0x093C, 0x00000}, 0x0958},
2578 {{0x0916, 0x093C, 0x00000}, 0x0959},
2579 {{0x0917, 0x093C, 0x00000}, 0x095A},
2580 {{0x091C, 0x093C, 0x00000}, 0x095B},
2581 {{0x0921, 0x093C, 0x00000}, 0x095C},
2582 {{0x0922, 0x093C, 0x00000}, 0x095D},
2583 {{0x092B, 0x093C, 0x00000}, 0x095E},
2584 {{0x092F, 0x093C, 0x00000}, 0x095F}};
2586 static void ContextualShape_Devanagari(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2588 int cCount = cChars;
2589 WCHAR *input;
2590 IndicSyllable *syllables = NULL;
2591 int syllable_count = 0;
2592 BOOL modern = get_GSUB_Indic2(psa, psc);
2594 if (*pcGlyphs != cChars)
2596 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2597 return;
2600 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2601 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2603 /* Step 1: Compose Consonant and Nukta */
2604 ComposeConsonants(hdc, input, &cCount, Devanagari_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, devanagari_lex, Reorder_Like_Devanagari, 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, devanagari_lex, NULL, modern);
2616 HeapFree(GetProcessHeap(),0,input);
2617 HeapFree(GetProcessHeap(),0,syllables);
2620 static int bengali_lex(WCHAR c)
2622 switch (c)
2624 case 0x09B0: return lex_Ra;
2625 default:
2626 return unicode_lex(c);
2630 static const VowelComponents Bengali_vowels[] = {
2631 {0x09CB, {0x09C7,0x09BE,0x0000}},
2632 {0x09CC, {0x09C7,0x09D7,0x0000}},
2633 {0x0000, {0x0000,0x0000,0x0000}}};
2635 static const ConsonantComponents Bengali_consonants[] = {
2636 {{0x09A4,0x09CD,0x200D}, 0x09CE},
2637 {{0x09A1,0x09BC,0x0000}, 0x09DC},
2638 {{0x09A2,0x09BC,0x0000}, 0x09DD},
2639 {{0x09AF,0x09BC,0x0000}, 0x09DF},
2640 {{0x0000,0x0000,0x0000}, 0x0000}};
2642 static void ContextualShape_Bengali(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2644 int cCount = cChars;
2645 WCHAR *input;
2646 IndicSyllable *syllables = NULL;
2647 int syllable_count = 0;
2648 BOOL modern = get_GSUB_Indic2(psa, psc);
2650 if (*pcGlyphs != cChars)
2652 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2653 return;
2656 input = HeapAlloc(GetProcessHeap(), 0, (cChars * 2) * sizeof(WCHAR));
2657 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2659 /* Step 1: Decompose Vowels and Compose Consonents */
2660 DecomposeVowels(hdc, input, &cCount, Bengali_vowels, pwLogClust, cChars);
2661 ComposeConsonants(hdc, input, &cCount, Bengali_consonants, pwLogClust);
2662 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2664 /* Step 2: Reorder within Syllables */
2665 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, bengali_lex, Reorder_Like_Bengali, modern);
2666 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2667 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2668 *pcGlyphs = cCount;
2670 /* Step 3: Initial form is only applied to the beginning of words */
2671 for (cCount = cCount - 1 ; cCount >= 0; cCount --)
2673 if (cCount == 0 || input[cCount] == 0x0020) /* space */
2675 int index = cCount;
2676 int gCount = 1;
2677 if (index > 0) index++;
2679 apply_GSUB_feature_to_glyph(hdc, psa, psc, &pwOutGlyphs[index], 0, 1, &gCount, "init");
2683 /* Step 4: Base Form application to syllables */
2684 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, bengali_lex, NULL, modern);
2686 HeapFree(GetProcessHeap(),0,input);
2687 HeapFree(GetProcessHeap(),0,syllables);
2690 static int gurmukhi_lex(WCHAR c)
2692 if (c == 0x0A71)
2693 return lex_Modifier;
2694 else
2695 return unicode_lex(c);
2698 static const ConsonantComponents Gurmukhi_consonants[] = {
2699 {{0x0A32,0x0A3C,0x0000}, 0x0A33},
2700 {{0x0A16,0x0A3C,0x0000}, 0x0A59},
2701 {{0x0A17,0x0A3C,0x0000}, 0x0A5A},
2702 {{0x0A1C,0x0A3C,0x0000}, 0x0A5B},
2703 {{0x0A2B,0x0A3C,0x0000}, 0x0A5E},
2704 {{0x0000,0x0000,0x0000}, 0x0000}};
2706 static void ContextualShape_Gurmukhi(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2708 int cCount = cChars;
2709 WCHAR *input;
2710 IndicSyllable *syllables = NULL;
2711 int syllable_count = 0;
2712 BOOL modern = get_GSUB_Indic2(psa, psc);
2714 if (*pcGlyphs != cChars)
2716 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2717 return;
2720 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2721 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2723 /* Step 1: Compose Consonents */
2724 ComposeConsonants(hdc, input, &cCount, Gurmukhi_consonants, pwLogClust);
2725 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2727 /* Step 2: Reorder within Syllables */
2728 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, gurmukhi_lex, Reorder_Like_Bengali, modern);
2729 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2730 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2731 *pcGlyphs = cCount;
2733 /* Step 3: Base Form application to syllables */
2734 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, gurmukhi_lex, NULL, modern);
2736 HeapFree(GetProcessHeap(),0,input);
2737 HeapFree(GetProcessHeap(),0,syllables);
2740 static int gujarati_lex(WCHAR c)
2742 switch (c)
2744 case 0x0AB0: return lex_Ra;
2745 default:
2746 return unicode_lex(c);
2750 static void ContextualShape_Gujarati(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2752 int cCount = cChars;
2753 WCHAR *input;
2754 IndicSyllable *syllables = NULL;
2755 int syllable_count = 0;
2756 BOOL modern = get_GSUB_Indic2(psa, psc);
2758 if (*pcGlyphs != cChars)
2760 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2761 return;
2764 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2765 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2767 /* Step 1: Reorder within Syllables */
2768 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, gujarati_lex, Reorder_Like_Devanagari, modern);
2769 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2770 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2771 *pcGlyphs = cCount;
2773 /* Step 2: Base Form application to syllables */
2774 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, gujarati_lex, NULL, modern);
2776 HeapFree(GetProcessHeap(),0,input);
2777 HeapFree(GetProcessHeap(),0,syllables);
2780 static int oriya_lex(WCHAR c)
2782 switch (c)
2784 case 0x0B30: return lex_Ra;
2785 default:
2786 return unicode_lex(c);
2790 static const VowelComponents Oriya_vowels[] = {
2791 {0x0B48, {0x0B47,0x0B56,0x0000}},
2792 {0x0B4B, {0x0B47,0x0B3E,0x0000}},
2793 {0x0B4C, {0x0B47,0x0B57,0x0000}},
2794 {0x0000, {0x0000,0x0000,0x0000}}};
2796 static const ConsonantComponents Oriya_consonants[] = {
2797 {{0x0B21,0x0B3C,0x0000}, 0x0B5C},
2798 {{0x0B22,0x0B3C,0x0000}, 0x0B5D},
2799 {{0x0000,0x0000,0x0000}, 0x0000}};
2801 static void ContextualShape_Oriya(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2803 int cCount = cChars;
2804 WCHAR *input;
2805 IndicSyllable *syllables = NULL;
2806 int syllable_count = 0;
2807 BOOL modern = get_GSUB_Indic2(psa, psc);
2809 if (*pcGlyphs != cChars)
2811 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2812 return;
2815 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2816 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2818 /* Step 1: Decompose Vowels and Compose Consonents */
2819 DecomposeVowels(hdc, input, &cCount, Oriya_vowels, pwLogClust, cChars);
2820 ComposeConsonants(hdc, input, &cCount, Oriya_consonants, pwLogClust);
2821 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2823 /* Step 2: Reorder within Syllables */
2824 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, oriya_lex, Reorder_Like_Bengali, modern);
2825 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2826 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2827 *pcGlyphs = cCount;
2829 /* Step 3: Base Form application to syllables */
2830 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, oriya_lex, NULL, modern);
2832 HeapFree(GetProcessHeap(),0,input);
2833 HeapFree(GetProcessHeap(),0,syllables);
2836 static int tamil_lex(WCHAR c)
2838 return unicode_lex(c);
2841 static const VowelComponents Tamil_vowels[] = {
2842 {0x0BCA, {0x0BC6,0x0BBE,0x0000}},
2843 {0x0BCB, {0x0BC7,0x0BBE,0x0000}},
2844 {0x0BCB, {0x0BC6,0x0BD7,0x0000}},
2845 {0x0000, {0x0000,0x0000,0x0000}}};
2847 static const ConsonantComponents Tamil_consonants[] = {
2848 {{0x0B92,0x0BD7,0x0000}, 0x0B94},
2849 {{0x0000,0x0000,0x0000}, 0x0000}};
2851 static void ContextualShape_Tamil(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2853 int cCount = cChars;
2854 WCHAR *input;
2855 IndicSyllable *syllables = NULL;
2856 int syllable_count = 0;
2857 BOOL modern = get_GSUB_Indic2(psa, psc);
2859 if (*pcGlyphs != cChars)
2861 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2862 return;
2865 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2866 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2868 /* Step 1: Decompose Vowels and Compose Consonents */
2869 DecomposeVowels(hdc, input, &cCount, Tamil_vowels, pwLogClust, cChars);
2870 ComposeConsonants(hdc, input, &cCount, Tamil_consonants, pwLogClust);
2871 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2873 /* Step 2: Reorder within Syllables */
2874 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, tamil_lex, Reorder_Like_Bengali, modern);
2875 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2876 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2877 *pcGlyphs = cCount;
2879 /* Step 3: Base Form application to syllables */
2880 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, tamil_lex, SecondReorder_Like_Tamil, modern);
2882 HeapFree(GetProcessHeap(),0,input);
2883 HeapFree(GetProcessHeap(),0,syllables);
2886 static int telugu_lex(WCHAR c)
2888 switch (c)
2890 case 0x0C43:
2891 case 0x0C44: return lex_Modifier;
2892 default:
2893 return unicode_lex(c);
2897 static const VowelComponents Telugu_vowels[] = {
2898 {0x0C48, {0x0C46,0x0C56,0x0000}},
2899 {0x0000, {0x0000,0x0000,0x0000}}};
2901 static void ContextualShape_Telugu(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2903 int cCount = cChars;
2904 WCHAR *input;
2905 IndicSyllable *syllables = NULL;
2906 int syllable_count = 0;
2907 BOOL modern = get_GSUB_Indic2(psa, psc);
2909 if (*pcGlyphs != cChars)
2911 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2912 return;
2915 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2916 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2918 /* Step 1: Decompose Vowels */
2919 DecomposeVowels(hdc, input, &cCount, Telugu_vowels, pwLogClust, cChars);
2920 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2922 /* Step 2: Reorder within Syllables */
2923 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, telugu_lex, Reorder_Like_Bengali, modern);
2924 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2925 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2926 *pcGlyphs = cCount;
2928 /* Step 3: Base Form application to syllables */
2929 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, telugu_lex, SecondReorder_Like_Telugu, modern);
2931 HeapFree(GetProcessHeap(),0,input);
2932 HeapFree(GetProcessHeap(),0,syllables);
2935 static int kannada_lex(WCHAR c)
2937 switch (c)
2939 case 0x0CB0: return lex_Ra;
2940 default:
2941 return unicode_lex(c);
2945 static const VowelComponents Kannada_vowels[] = {
2946 {0x0CC0, {0x0CBF,0x0CD5,0x0000}},
2947 {0x0CC7, {0x0CC6,0x0CD5,0x0000}},
2948 {0x0CC8, {0x0CC6,0x0CD6,0x0000}},
2949 {0x0CCA, {0x0CC6,0x0CC2,0x0000}},
2950 {0x0CCB, {0x0CC6,0x0CC2,0x0CD5}},
2951 {0x0000, {0x0000,0x0000,0x0000}}};
2953 static void ContextualShape_Kannada(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2955 int cCount = cChars;
2956 WCHAR *input;
2957 IndicSyllable *syllables = NULL;
2958 int syllable_count = 0;
2959 BOOL modern = get_GSUB_Indic2(psa, psc);
2961 if (*pcGlyphs != cChars)
2963 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2964 return;
2967 input = HeapAlloc(GetProcessHeap(), 0, (cChars*3) * sizeof(WCHAR));
2968 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2970 /* Step 1: Decompose Vowels */
2971 DecomposeVowels(hdc, input, &cCount, Kannada_vowels, pwLogClust, cChars);
2972 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2974 /* Step 2: Reorder within Syllables */
2975 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, kannada_lex, Reorder_Like_Kannada, modern);
2976 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2977 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2978 *pcGlyphs = cCount;
2980 /* Step 3: Base Form application to syllables */
2981 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, kannada_lex, SecondReorder_Like_Telugu, modern);
2983 HeapFree(GetProcessHeap(),0,input);
2984 HeapFree(GetProcessHeap(),0,syllables);
2987 static int malayalam_lex(WCHAR c)
2989 return unicode_lex(c);
2992 static const VowelComponents Malayalam_vowels[] = {
2993 {0x0D4A, {0x0D46,0x0D3E,0x0000}},
2994 {0x0D4B, {0x0D47,0x0D3E,0x0000}},
2995 {0x0D4C, {0x0D46,0x0D57,0x0000}},
2996 {0x0000, {0x0000,0x0000,0x0000}}};
2998 static void ContextualShape_Malayalam(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3000 int cCount = cChars;
3001 WCHAR *input;
3002 IndicSyllable *syllables = NULL;
3003 int syllable_count = 0;
3004 BOOL modern = get_GSUB_Indic2(psa, psc);
3006 if (*pcGlyphs != cChars)
3008 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3009 return;
3012 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
3013 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
3015 /* Step 1: Decompose Vowels */
3016 DecomposeVowels(hdc, input, &cCount, Malayalam_vowels, pwLogClust, cChars);
3017 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
3019 /* Step 2: Reorder within Syllables */
3020 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, malayalam_lex, Reorder_Like_Devanagari, modern);
3021 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
3022 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
3023 *pcGlyphs = cCount;
3025 /* Step 3: Base Form application to syllables */
3026 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, malayalam_lex, SecondReorder_Like_Tamil, modern);
3028 HeapFree(GetProcessHeap(),0,input);
3029 HeapFree(GetProcessHeap(),0,syllables);
3032 static int khmer_lex(WCHAR c)
3034 return unicode_lex(c);
3037 static void ContextualShape_Khmer(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3039 int cCount = cChars;
3040 WCHAR *input;
3041 IndicSyllable *syllables = NULL;
3042 int syllable_count = 0;
3044 if (*pcGlyphs != cChars)
3046 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3047 return;
3050 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
3051 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
3053 /* Step 1: Reorder within Syllables */
3054 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, khmer_lex, Reorder_Like_Devanagari, FALSE);
3055 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
3056 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
3057 *pcGlyphs = cCount;
3059 /* Step 2: Base Form application to syllables */
3060 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, khmer_lex, NULL, FALSE);
3062 HeapFree(GetProcessHeap(),0,input);
3063 HeapFree(GetProcessHeap(),0,syllables);
3066 static inline BOOL mongolian_wordbreak(WCHAR chr)
3068 return ((chr == 0x0020) || (chr == 0x200C) || (chr == 0x202F) || (chr == 0x180E) || (chr == 0x1800) || (chr == 0x1802) || (chr == 0x1803) || (chr == 0x1805) || (chr == 0x1808) || (chr == 0x1809) || (chr == 0x1807));
3071 static void ContextualShape_Mongolian(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3073 INT *context_shape;
3074 INT dirL;
3075 int i;
3077 if (*pcGlyphs != cChars)
3079 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3080 return;
3083 if (!psa->fLogicalOrder && psa->fRTL)
3084 dirL = -1;
3085 else
3086 dirL = 1;
3088 if (!psc->GSUB_Table)
3089 psc->GSUB_Table = load_gsub_table(hdc);
3091 if (!psc->GSUB_Table)
3092 return;
3094 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
3096 for (i = 0; i < cChars; i++)
3098 if (i == 0 || mongolian_wordbreak(pwcChars[i-1]))
3100 if ((i == cChars-1) || mongolian_wordbreak(pwcChars[i+1]))
3101 context_shape[i] = Xn;
3102 else
3103 context_shape[i] = Xl;
3105 else
3107 if ((i == cChars-1) || mongolian_wordbreak(pwcChars[i+1]))
3108 context_shape[i] = Xr;
3109 else
3110 context_shape[i] = Xm;
3114 /* Contextual Shaping */
3115 i = 0;
3116 while(i < *pcGlyphs)
3118 INT nextIndex;
3119 INT prevCount = *pcGlyphs;
3120 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
3121 if (nextIndex > GSUB_E_NOGLYPH)
3123 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
3124 i = nextIndex;
3126 else
3127 i++;
3130 HeapFree(GetProcessHeap(),0,context_shape);
3133 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)
3135 int i,k;
3137 for (i = 0; i < cGlyphs; i++)
3139 int char_index[20];
3140 int char_count = 0;
3142 for (k = 0; k < cChars; k++)
3144 if (pwLogClust[k] == i)
3146 char_index[char_count] = k;
3147 char_count++;
3151 if (char_count == 0)
3152 continue;
3154 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3156 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3157 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3159 else
3160 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3163 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3164 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3167 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 )
3169 int i,k;
3170 int initGlyph, finaGlyph;
3171 INT dirR, dirL;
3172 BYTE *spaces;
3174 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
3175 memset(spaces,0,cGlyphs);
3177 if (!psa->fLogicalOrder && psa->fRTL)
3179 initGlyph = cGlyphs-1;
3180 finaGlyph = 0;
3181 dirR = 1;
3182 dirL = -1;
3184 else
3186 initGlyph = 0;
3187 finaGlyph = cGlyphs-1;
3188 dirR = -1;
3189 dirL = 1;
3192 for (i = 0; i < cGlyphs; i++)
3194 for (k = 0; k < cChars; k++)
3195 if (pwLogClust[k] == i)
3197 if (pwcChars[k] == 0x0020)
3198 spaces[i] = 1;
3202 for (i = 0; i < cGlyphs; i++)
3204 int char_index[20];
3205 int char_count = 0;
3206 BOOL isInit, isFinal;
3208 for (k = 0; k < cChars; k++)
3210 if (pwLogClust[k] == i)
3212 char_index[char_count] = k;
3213 char_count++;
3217 isInit = (i == initGlyph || (i+dirR > 0 && i+dirR < cGlyphs && spaces[i+dirR]));
3218 isFinal = (i == finaGlyph || (i+dirL > 0 && i+dirL < cGlyphs && spaces[i+dirL]));
3220 if (char_count == 0)
3221 continue;
3223 if (char_count == 1)
3225 if (pwcChars[char_index[0]] == 0x0020) /* space */
3227 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BLANK;
3228 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3230 else if (pwcChars[char_index[0]] == 0x0640) /* kashida */
3231 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
3232 else if (pwcChars[char_index[0]] == 0x0633) /* SEEN */
3234 if (!isInit && !isFinal)
3235 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN_M;
3236 else if (isInit)
3237 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN;
3238 else
3239 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3241 else if (!isInit)
3243 if (pwcChars[char_index[0]] == 0x0628 ) /* BA */
3244 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BA;
3245 else if (pwcChars[char_index[0]] == 0x0631 ) /* RA */
3246 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_RA;
3247 else if (pwcChars[char_index[0]] == 0x0647 ) /* HA */
3248 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_HA;
3249 else if ((pwcChars[char_index[0]] == 0x0627 || pwcChars[char_index[0]] == 0x0625 || pwcChars[char_index[0]] == 0x0623 || pwcChars[char_index[0]] == 0x0622) ) /* alef-like */
3250 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_ALEF;
3251 else
3252 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3254 else if (!isInit && !isFinal)
3255 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3256 else
3257 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3259 else if (char_count == 2)
3261 if ((pwcChars[char_index[0]] == 0x0628 && pwcChars[char_index[1]]== 0x0631) || (pwcChars[char_index[0]] == 0x0631 && pwcChars[char_index[1]]== 0x0628)) /* BA+RA */
3262 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BARA;
3263 else if (!isInit)
3264 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3265 else
3266 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3268 else if (!isInit && !isFinal)
3269 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3270 else
3271 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3274 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3275 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3276 HeapFree(GetProcessHeap(),0,spaces);
3279 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 )
3281 int i,k;
3282 int finaGlyph;
3283 INT dirL;
3284 BYTE *spaces;
3286 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
3287 memset(spaces,0,cGlyphs);
3289 if (!psa->fLogicalOrder && psa->fRTL)
3291 finaGlyph = 0;
3292 dirL = -1;
3294 else
3296 finaGlyph = cGlyphs-1;
3297 dirL = 1;
3300 for (i = 0; i < cGlyphs; i++)
3302 for (k = 0; k < cChars; k++)
3303 if (pwLogClust[k] == i)
3305 if (pwcChars[k] == 0x0020)
3306 spaces[i] = 1;
3310 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3312 for (i = 0; i < cGlyphs; i++)
3314 int char_index[20];
3315 int char_count = 0;
3317 for (k = 0; k < cChars; k++)
3319 if (pwLogClust[k] == i)
3321 char_index[char_count] = k;
3322 char_count++;
3326 if (char_count == 0)
3327 continue;
3329 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3331 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3332 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3334 else if (i == finaGlyph)
3335 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3336 else
3337 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3339 /* handle Thai SARA AM (U+0E33) differently than GDEF */
3340 if (char_count == 1 && pwcChars[char_index[0]] == 0x0e33)
3341 pGlyphProp[i].sva.fClusterStart = 0;
3344 HeapFree(GetProcessHeap(),0,spaces);
3345 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3347 /* Do not allow justification between marks and their base */
3348 for (i = 0; i < cGlyphs; i++)
3350 if (!pGlyphProp[i].sva.fClusterStart)
3351 pGlyphProp[i-dirL].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3355 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)
3357 int i,k;
3359 for (i = 0; i < cGlyphs; i++)
3361 int char_index[20];
3362 int char_count = 0;
3364 for (k = 0; k < cChars; k++)
3366 if (pwLogClust[k] == i)
3368 char_index[char_count] = k;
3369 char_count++;
3373 if (char_count == 0)
3374 continue;
3376 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3378 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3379 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3381 else
3382 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3384 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3385 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3388 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)
3390 int i,k;
3392 for (i = 0; i < cGlyphs; i++)
3394 int char_index[20];
3395 int char_count = 0;
3397 for (k = 0; k < cChars; k++)
3399 if (pwLogClust[k] == i)
3401 char_index[char_count] = k;
3402 char_count++;
3406 if (char_count == 0)
3407 continue;
3409 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3411 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3412 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3414 else
3415 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3417 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3418 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3420 /* Tibeten script does not set sva.fDiacritic or sva.fZeroWidth */
3421 for (i = 0; i < cGlyphs; i++)
3423 if (!pGlyphProp[i].sva.fClusterStart)
3425 pGlyphProp[i].sva.fDiacritic = 0;
3426 pGlyphProp[i].sva.fZeroWidth = 0;
3431 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)
3433 int i,k;
3435 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3436 for (i = 0; i < cGlyphs; i++)
3438 int char_index[20];
3439 int char_count = 0;
3441 for (k = 0; k < cChars; k++)
3443 if (pwLogClust[k] == i)
3445 char_index[char_count] = k;
3446 char_count++;
3450 if (override_gsub)
3452 /* Most indic scripts do not set fDiacritic or fZeroWidth */
3453 pGlyphProp[i].sva.fDiacritic = FALSE;
3454 pGlyphProp[i].sva.fZeroWidth = FALSE;
3457 if (char_count == 0)
3458 continue;
3460 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3462 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3463 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3465 else
3466 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3468 pGlyphProp[i].sva.fClusterStart = 0;
3469 for (k = 0; k < char_count && !pGlyphProp[i].sva.fClusterStart; k++)
3470 switch (lexical(pwcChars[char_index[k]]))
3472 case lex_Matra_pre:
3473 case lex_Matra_post:
3474 case lex_Matra_above:
3475 case lex_Matra_below:
3476 case lex_Modifier:
3477 case lex_Halant:
3478 break;
3479 case lex_ZWJ:
3480 case lex_ZWNJ:
3481 /* check for dangling joiners */
3482 if (pwcChars[char_index[k]-1] == 0x0020 || pwcChars[char_index[k]+1] == 0x0020)
3483 pGlyphProp[i].sva.fClusterStart = 1;
3484 else
3485 k = char_count;
3486 break;
3487 default:
3488 pGlyphProp[i].sva.fClusterStart = 1;
3489 break;
3493 if (use_syllables)
3495 IndicSyllable *syllables = NULL;
3496 int syllable_count = 0;
3497 BOOL modern = get_GSUB_Indic2(psa, psc);
3499 Indic_ParseSyllables( hdc, psa, psc, pwcChars, cChars, &syllables, &syllable_count, lexical, modern);
3501 for (i = 0; i < syllable_count; i++)
3503 int j;
3504 WORD g = pwLogClust[syllables[i].start];
3505 for (j = syllables[i].start+1; j <= syllables[i].end; j++)
3507 if (pwLogClust[j] != g)
3509 pGlyphProp[pwLogClust[j]].sva.fClusterStart = 0;
3510 pwLogClust[j] = g;
3515 HeapFree(GetProcessHeap(), 0, syllables);
3518 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3521 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 )
3523 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, sinhala_lex, FALSE, FALSE);
3526 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 )
3528 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, devanagari_lex, TRUE, TRUE);
3531 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 )
3533 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, bengali_lex, TRUE, TRUE);
3536 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 )
3538 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, gurmukhi_lex, TRUE, TRUE);
3541 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 )
3543 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, gujarati_lex, TRUE, TRUE);
3546 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 )
3548 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, oriya_lex, TRUE, TRUE);
3551 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 )
3553 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, tamil_lex, TRUE, TRUE);
3556 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 )
3558 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, telugu_lex, TRUE, TRUE);
3561 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 )
3563 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, kannada_lex, TRUE, TRUE);
3566 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 )
3568 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, malayalam_lex, TRUE, TRUE);
3571 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 )
3573 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, khmer_lex, TRUE, TRUE);
3576 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)
3578 if (ShapingData[psa->eScript].charGlyphPropProc)
3579 ShapingData[psa->eScript].charGlyphPropProc(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
3580 else
3581 ShapeCharGlyphProp_Default(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
3584 void SHAPE_ContextualShaping(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3586 if (!psc->GSUB_Table)
3587 psc->GSUB_Table = load_gsub_table(hdc);
3589 if (ShapingData[psa->eScript].contextProc)
3590 ShapingData[psa->eScript].contextProc(hdc, psc, psa, pwcChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3593 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)
3595 int i;
3596 INT dirL;
3598 if (!rpRangeProperties)
3599 return;
3601 if (!psc->GSUB_Table)
3602 psc->GSUB_Table = load_gsub_table(hdc);
3604 if (!psc->GSUB_Table)
3605 return;
3607 if (!psa->fLogicalOrder && psa->fRTL)
3608 dirL = -1;
3609 else
3610 dirL = 1;
3612 for (i = 0; i < rpRangeProperties->cotfRecords; i++)
3614 if (rpRangeProperties->potfRecords[i].lParameter > 0)
3615 apply_GSUB_feature(hdc, psa, psc, pwOutGlyphs, dirL, pcGlyphs, cChars, (const char*)&rpRangeProperties->potfRecords[i].tagFeature, pwLogClust);
3619 void SHAPE_ApplyDefaultOpentypeFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, INT cChars, WORD *pwLogClust)
3621 const TEXTRANGE_PROPERTIES *rpRangeProperties;
3622 rpRangeProperties = &ShapingData[psa->eScript].defaultTextRange;
3624 SHAPE_ApplyOpenTypeFeatures(hdc, psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, rpRangeProperties, pwLogClust);
3627 HRESULT SHAPE_CheckFontForRequiredFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa)
3629 const GSUB_Feature *feature;
3630 int i;
3632 if (!ShapingData[psa->eScript].requiredFeatures)
3633 return S_OK;
3635 if (!psc->GSUB_Table)
3636 psc->GSUB_Table = load_gsub_table(hdc);
3638 /* we need to have at least one of the required features */
3639 i = 0;
3640 while (ShapingData[psa->eScript].requiredFeatures[i])
3642 feature = load_GSUB_feature(hdc, psa, psc, ShapingData[psa->eScript].requiredFeatures[i]);
3643 if (feature)
3644 return S_OK;
3645 i++;
3648 return USP_E_SCRIPT_NOT_IN_FONT;