usp10: Cache FontScriptTags information.
[wine.git] / dlls / usp10 / shape.c
blobfcf765ecdec0889eeec225cc8d4c42fbb428314b
1 /*
2 * Implementation of Shaping for the Uniscribe Script Processor (usp10.dll)
4 * Copyright 2010 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <stdlib.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winnls.h"
29 #include "usp10.h"
30 #include "winternl.h"
32 #include "usp10_internal.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
38 #define FIRST_ARABIC_CHAR 0x0600
39 #define LAST_ARABIC_CHAR 0x06ff
41 typedef VOID (*ContextualShapingProc)(HDC, ScriptCache*, SCRIPT_ANALYSIS*,
42 WCHAR*, INT, WORD*, INT*, INT, WORD*);
44 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
45 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
46 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
47 static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
48 static void ContextualShape_Devanagari(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
49 static void ContextualShape_Bengali(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
50 static void ContextualShape_Gurmukhi(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
51 static void ContextualShape_Gujarati(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
52 static void ContextualShape_Oriya(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
53 static void ContextualShape_Tamil(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
54 static void ContextualShape_Telugu(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
55 static void ContextualShape_Kannada(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
56 static void ContextualShape_Malayalam(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
57 static void ContextualShape_Khmer(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
58 static void ContextualShape_Mongolian(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
60 typedef VOID (*ShapeCharGlyphPropProc)( HDC , ScriptCache*, SCRIPT_ANALYSIS*, const WCHAR*, const INT, const WORD*, const INT, WORD*, SCRIPT_CHARPROP*, SCRIPT_GLYPHPROP*);
62 static void ShapeCharGlyphProp_Default( HDC hdc, ScriptCache* psc, SCRIPT_ANALYSIS* psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD* pwLogClust, SCRIPT_CHARPROP* pCharProp, SCRIPT_GLYPHPROP* pGlyphProp);
63 static void ShapeCharGlyphProp_Arabic( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP* pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
64 static void ShapeCharGlyphProp_Thai( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
65 static void ShapeCharGlyphProp_None( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
66 static void ShapeCharGlyphProp_Tibet( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
67 static void ShapeCharGlyphProp_Sinhala( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
68 static void ShapeCharGlyphProp_Devanagari( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
69 static void ShapeCharGlyphProp_Bengali( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
70 static void ShapeCharGlyphProp_Gurmukhi( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
71 static void ShapeCharGlyphProp_Gujarati( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
72 static void ShapeCharGlyphProp_Oriya( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
73 static void ShapeCharGlyphProp_Tamil( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
74 static void ShapeCharGlyphProp_Telugu( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
75 static void ShapeCharGlyphProp_Kannada( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
76 static void ShapeCharGlyphProp_Malayalam( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
77 static void ShapeCharGlyphProp_Khmer( HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp );
79 extern const unsigned short indic_syllabic_table[];
80 extern const unsigned short wine_shaping_table[];
81 extern const unsigned short wine_shaping_forms[LAST_ARABIC_CHAR - FIRST_ARABIC_CHAR + 1][4];
83 enum joining_types {
84 jtU,
85 jtT,
86 jtR,
87 jtL,
88 jtD,
89 jtC
92 enum joined_forms {
93 Xn=0,
94 Xr,
95 Xl,
96 Xm,
97 /* Syriac Alaph */
98 Afj,
99 Afn,
103 #ifdef WORDS_BIGENDIAN
104 #define GET_BE_WORD(x) (x)
105 #define GET_BE_DWORD(x) (x)
106 #else
107 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
108 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
109 #endif
111 /* These are all structures needed for the GSUB table */
112 #define GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B')
113 #define GSUB_E_NOFEATURE -2
114 #define GSUB_E_NOGLYPH -1
116 typedef struct {
117 DWORD version;
118 WORD ScriptList;
119 WORD FeatureList;
120 WORD LookupList;
121 } GSUB_Header;
123 typedef struct {
124 CHAR ScriptTag[4];
125 WORD Script;
126 } GSUB_ScriptRecord;
128 typedef struct {
129 WORD ScriptCount;
130 GSUB_ScriptRecord ScriptRecord[1];
131 } GSUB_ScriptList;
133 typedef struct {
134 CHAR LangSysTag[4];
135 WORD LangSys;
136 } GSUB_LangSysRecord;
138 typedef struct {
139 WORD DefaultLangSys;
140 WORD LangSysCount;
141 GSUB_LangSysRecord LangSysRecord[1];
142 } GSUB_Script;
144 typedef struct {
145 WORD LookupOrder; /* Reserved */
146 WORD ReqFeatureIndex;
147 WORD FeatureCount;
148 WORD FeatureIndex[1];
149 } GSUB_LangSys;
151 typedef struct {
152 CHAR FeatureTag[4];
153 WORD Feature;
154 } GSUB_FeatureRecord;
156 typedef struct {
157 WORD FeatureCount;
158 GSUB_FeatureRecord FeatureRecord[1];
159 } GSUB_FeatureList;
161 typedef struct {
162 WORD FeatureParams; /* Reserved */
163 WORD LookupCount;
164 WORD LookupListIndex[1];
165 } GSUB_Feature;
167 typedef struct {
168 WORD LookupCount;
169 WORD Lookup[1];
170 } GSUB_LookupList;
172 typedef struct {
173 WORD LookupType;
174 WORD LookupFlag;
175 WORD SubTableCount;
176 WORD SubTable[1];
177 } GSUB_LookupTable;
179 typedef struct {
180 WORD CoverageFormat;
181 WORD GlyphCount;
182 WORD GlyphArray[1];
183 } GSUB_CoverageFormat1;
185 typedef struct {
186 WORD Start;
187 WORD End;
188 WORD StartCoverageIndex;
189 } GSUB_RangeRecord;
191 typedef struct {
192 WORD CoverageFormat;
193 WORD RangeCount;
194 GSUB_RangeRecord RangeRecord[1];
195 } GSUB_CoverageFormat2;
197 typedef struct {
198 WORD SubstFormat; /* = 1 */
199 WORD Coverage;
200 WORD DeltaGlyphID;
201 } GSUB_SingleSubstFormat1;
203 typedef struct {
204 WORD SubstFormat; /* = 2 */
205 WORD Coverage;
206 WORD GlyphCount;
207 WORD Substitute[1];
208 }GSUB_SingleSubstFormat2;
210 typedef struct {
211 WORD SubstFormat; /* = 1 */
212 WORD Coverage;
213 WORD SequenceCount;
214 WORD Sequence[1];
215 }GSUB_MultipleSubstFormat1;
217 typedef struct {
218 WORD GlyphCount;
219 WORD Substitute[1];
220 }GSUB_Sequence;
222 typedef struct {
223 WORD SubstFormat; /* = 1 */
224 WORD Coverage;
225 WORD LigSetCount;
226 WORD LigatureSet[1];
227 }GSUB_LigatureSubstFormat1;
229 typedef struct {
230 WORD LigatureCount;
231 WORD Ligature[1];
232 }GSUB_LigatureSet;
234 typedef struct{
235 WORD LigGlyph;
236 WORD CompCount;
237 WORD Component[1];
238 }GSUB_Ligature;
240 typedef struct{
241 WORD SequenceIndex;
242 WORD LookupListIndex;
244 }GSUB_SubstLookupRecord;
246 typedef struct{
247 WORD SubstFormat; /* = 1 */
248 WORD Coverage;
249 WORD ChainSubRuleSetCount;
250 WORD ChainSubRuleSet[1];
251 }GSUB_ChainContextSubstFormat1;
253 typedef struct {
254 WORD SubstFormat; /* = 3 */
255 WORD BacktrackGlyphCount;
256 WORD Coverage[1];
257 }GSUB_ChainContextSubstFormat3_1;
259 typedef struct{
260 WORD InputGlyphCount;
261 WORD Coverage[1];
262 }GSUB_ChainContextSubstFormat3_2;
264 typedef struct{
265 WORD LookaheadGlyphCount;
266 WORD Coverage[1];
267 }GSUB_ChainContextSubstFormat3_3;
269 typedef struct{
270 WORD SubstCount;
271 GSUB_SubstLookupRecord SubstLookupRecord[1];
272 }GSUB_ChainContextSubstFormat3_4;
274 typedef struct {
275 WORD SubstFormat; /* = 1 */
276 WORD Coverage;
277 WORD AlternateSetCount;
278 WORD AlternateSet[1];
279 } GSUB_AlternateSubstFormat1;
281 typedef struct{
282 WORD GlyphCount;
283 WORD Alternate[1];
284 } GSUB_AlternateSet;
286 /* These are all structures needed for the GDEF table */
287 #define GDEF_TAG MS_MAKE_TAG('G', 'D', 'E', 'F')
289 enum {BaseGlyph=1, LigatureGlyph, MarkGlyph, ComponentGlyph};
291 typedef struct {
292 DWORD Version;
293 WORD GlyphClassDef;
294 WORD AttachList;
295 WORD LigCaretList;
296 WORD MarkAttachClassDef;
297 } GDEF_Header;
299 typedef struct {
300 WORD ClassFormat;
301 WORD StartGlyph;
302 WORD GlyphCount;
303 WORD ClassValueArray[1];
304 } GDEF_ClassDefFormat1;
306 typedef struct {
307 WORD Start;
308 WORD End;
309 WORD Class;
310 } GDEF_ClassRangeRecord;
312 typedef struct {
313 WORD ClassFormat;
314 WORD ClassRangeCount;
315 GDEF_ClassRangeRecord ClassRangeRecord[1];
316 } GDEF_ClassDefFormat2;
319 /* These are all structures needed for the cmap format 12 table */
320 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p')
322 typedef struct {
323 WORD platformID;
324 WORD encodingID;
325 DWORD offset;
326 } CMAP_EncodingRecord;
328 typedef struct {
329 WORD version;
330 WORD numTables;
331 CMAP_EncodingRecord tables[1];
332 } CMAP_Header;
334 typedef struct {
335 DWORD startCharCode;
336 DWORD endCharCode;
337 DWORD startGlyphID;
338 } CMAP_SegmentedCoverage_group;
340 typedef struct {
341 WORD format;
342 WORD reserved;
343 DWORD length;
344 DWORD language;
345 DWORD nGroups;
346 CMAP_SegmentedCoverage_group groups[1];
347 } CMAP_SegmentedCoverage;
349 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
351 typedef struct tagVowelComponents
353 WCHAR base;
354 WCHAR parts[3];
355 } VowelComponents;
357 typedef struct tagConsonantComponents
359 WCHAR parts[3];
360 WCHAR output;
361 } ConsonantComponents;
363 typedef void (*second_reorder_function)(LPWSTR pwChar, IndicSyllable *syllable,WORD* pwGlyphs, IndicSyllable* glyph_index, lexical_function lex);
365 /* the orders of joined_forms and contextual_features need to line up */
366 static const char* contextual_features[] =
368 "isol",
369 "fina",
370 "init",
371 "medi",
372 /* Syriac Alaph */
373 "med2",
374 "fin2",
375 "fin3"
378 static OPENTYPE_FEATURE_RECORD standard_features[] =
380 { MS_MAKE_TAG('c','c','m','p'), 1},
381 { MS_MAKE_TAG('l','o','c','l'), 1},
384 static OPENTYPE_FEATURE_RECORD latin_features[] =
386 { MS_MAKE_TAG('l','i','g','a'), 1},
387 { MS_MAKE_TAG('c','l','i','g'), 1},
390 static OPENTYPE_FEATURE_RECORD arabic_features[] =
392 { MS_MAKE_TAG('r','l','i','g'), 1},
393 { MS_MAKE_TAG('c','a','l','t'), 1},
394 { MS_MAKE_TAG('l','i','g','a'), 1},
395 { MS_MAKE_TAG('d','l','i','g'), 1},
396 { MS_MAKE_TAG('c','s','w','h'), 1},
397 { MS_MAKE_TAG('m','s','e','t'), 1},
400 static const char* required_arabic_features[] =
402 "fina",
403 "init",
404 "medi",
405 "rlig",
406 NULL
409 static OPENTYPE_FEATURE_RECORD hebrew_features[] =
411 { MS_MAKE_TAG('d','l','i','g'), 0},
414 static OPENTYPE_FEATURE_RECORD syriac_features[] =
416 { MS_MAKE_TAG('r','l','i','g'), 1},
417 { MS_MAKE_TAG('c','a','l','t'), 1},
418 { MS_MAKE_TAG('l','i','g','a'), 1},
419 { MS_MAKE_TAG('d','l','i','g'), 1},
422 static const char* required_syriac_features[] =
424 "fina",
425 "fin2",
426 "fin3",
427 "init",
428 "medi",
429 "med2",
430 "rlig",
431 NULL
434 static OPENTYPE_FEATURE_RECORD sinhala_features[] =
436 /* Presentation forms */
437 { MS_MAKE_TAG('b','l','w','s'), 1},
438 { MS_MAKE_TAG('a','b','v','s'), 1},
439 { MS_MAKE_TAG('p','s','t','s'), 1},
442 static OPENTYPE_FEATURE_RECORD tibetan_features[] =
444 { MS_MAKE_TAG('a','b','v','s'), 1},
445 { MS_MAKE_TAG('b','l','w','s'), 1},
448 static OPENTYPE_FEATURE_RECORD phags_features[] =
450 { MS_MAKE_TAG('a','b','v','s'), 1},
451 { MS_MAKE_TAG('b','l','w','s'), 1},
452 { MS_MAKE_TAG('c','a','l','t'), 1},
455 static OPENTYPE_FEATURE_RECORD thai_features[] =
457 { MS_MAKE_TAG('c','c','m','p'), 1},
460 static const char* required_lao_features[] =
462 "ccmp",
463 NULL
466 static const char* required_devanagari_features[] =
468 "nukt",
469 "akhn",
470 "rphf",
471 "blwf",
472 "half",
473 "vatu",
474 "pres",
475 "abvs",
476 "blws",
477 "psts",
478 "haln",
479 NULL
482 static OPENTYPE_FEATURE_RECORD devanagari_features[] =
484 { MS_MAKE_TAG('p','r','e','s'), 1},
485 { MS_MAKE_TAG('a','b','v','s'), 1},
486 { MS_MAKE_TAG('b','l','w','s'), 1},
487 { MS_MAKE_TAG('p','s','t','s'), 1},
488 { MS_MAKE_TAG('h','a','l','n'), 1},
489 { MS_MAKE_TAG('c','a','l','t'), 1},
492 static OPENTYPE_FEATURE_RECORD myanmar_features[] =
494 { MS_MAKE_TAG('l','i','g','a'), 1},
495 { MS_MAKE_TAG('c','l','i','g'), 1},
498 static const char* required_bengali_features[] =
500 "nukt",
501 "akhn",
502 "rphf",
503 "blwf",
504 "half",
505 "vatu",
506 "pstf",
507 "init",
508 "abvs",
509 "blws",
510 "psts",
511 "haln",
512 NULL
515 static const char* required_gurmukhi_features[] =
517 "nukt",
518 "akhn",
519 "rphf",
520 "blwf",
521 "half",
522 "pstf",
523 "vatu",
524 "cjct",
525 "pres",
526 "abvs",
527 "blws",
528 "psts",
529 "haln",
530 "calt",
531 NULL
534 static const char* required_oriya_features[] =
536 "nukt",
537 "akhn",
538 "rphf",
539 "blwf",
540 "pstf",
541 "cjct",
542 "pres",
543 "abvs",
544 "blws",
545 "psts",
546 "haln",
547 "calt",
548 NULL
551 static const char* required_tamil_features[] =
553 "nukt",
554 "akhn",
555 "rphf",
556 "pref",
557 "half",
558 "pres",
559 "abvs",
560 "blws",
561 "psts",
562 "haln",
563 "calt",
564 NULL
567 static const char* required_telugu_features[] =
569 "nukt",
570 "akhn",
571 "rphf",
572 "pref",
573 "half",
574 "pstf",
575 "cjct",
576 "pres",
577 "abvs",
578 "blws",
579 "psts",
580 "haln",
581 "calt",
582 NULL
585 static OPENTYPE_FEATURE_RECORD khmer_features[] =
587 { MS_MAKE_TAG('p','r','e','s'), 1},
588 { MS_MAKE_TAG('b','l','w','s'), 1},
589 { MS_MAKE_TAG('a','b','v','s'), 1},
590 { MS_MAKE_TAG('p','s','t','s'), 1},
591 { MS_MAKE_TAG('c','l','i','g'), 1},
594 static const char* required_khmer_features[] =
596 "pref",
597 "blwf",
598 "abvf",
599 "pstf",
600 "pres",
601 "blws",
602 "abvs",
603 "psts",
604 "clig",
605 NULL
608 static OPENTYPE_FEATURE_RECORD no_features[] =
609 { };
611 static OPENTYPE_FEATURE_RECORD ethiopic_features[] =
613 { MS_MAKE_TAG('c','c','m','p'), 1},
614 { MS_MAKE_TAG('l','o','c','l'), 1},
615 { MS_MAKE_TAG('c','a','l','t'), 1},
616 { MS_MAKE_TAG('l','i','g','a'), 1},
619 static OPENTYPE_FEATURE_RECORD mongolian_features[] =
621 { MS_MAKE_TAG('c','c','m','p'), 1},
622 { MS_MAKE_TAG('l','o','c','l'), 1},
623 { MS_MAKE_TAG('c','a','l','t'), 1},
624 { MS_MAKE_TAG('r','l','i','g'), 1},
627 typedef struct ScriptShapeDataTag {
628 TEXTRANGE_PROPERTIES defaultTextRange;
629 const char** requiredFeatures;
630 CHAR otTag[5];
631 CHAR newOtTag[5];
632 ContextualShapingProc contextProc;
633 ShapeCharGlyphPropProc charGlyphPropProc;
634 } ScriptShapeData;
636 /* in order of scripts */
637 static const ScriptShapeData ShapingData[] =
639 {{ standard_features, 2}, NULL, "", "", NULL, NULL},
640 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
641 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
642 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
643 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
644 {{ latin_features, 2}, NULL, "latn", "", NULL, NULL},
645 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
646 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
647 {{ hebrew_features, 1}, NULL, "hebr", "", NULL, NULL},
648 {{ syriac_features, 4}, required_syriac_features, "syrc", "", ContextualShape_Syriac, ShapeCharGlyphProp_None},
649 {{ arabic_features, 6}, required_arabic_features, "arab", "", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
650 {{ NULL, 0}, NULL, "thaa", "", NULL, ShapeCharGlyphProp_None},
651 {{ standard_features, 2}, NULL, "grek", "", NULL, NULL},
652 {{ standard_features, 2}, NULL, "cyrl", "", NULL, NULL},
653 {{ standard_features, 2}, NULL, "armn", "", NULL, NULL},
654 {{ standard_features, 2}, NULL, "geor", "", NULL, NULL},
655 {{ sinhala_features, 3}, NULL, "sinh", "", ContextualShape_Sinhala, ShapeCharGlyphProp_Sinhala},
656 {{ tibetan_features, 2}, NULL, "tibt", "", NULL, ShapeCharGlyphProp_Tibet},
657 {{ tibetan_features, 2}, NULL, "tibt", "", NULL, ShapeCharGlyphProp_Tibet},
658 {{ phags_features, 3}, NULL, "phag", "", ContextualShape_Phags_pa, ShapeCharGlyphProp_Thai},
659 {{ thai_features, 1}, NULL, "thai", "", NULL, ShapeCharGlyphProp_Thai},
660 {{ thai_features, 1}, NULL, "thai", "", NULL, ShapeCharGlyphProp_Thai},
661 {{ thai_features, 1}, required_lao_features, "lao", "", NULL, ShapeCharGlyphProp_Thai},
662 {{ thai_features, 1}, required_lao_features, "lao", "", NULL, ShapeCharGlyphProp_Thai},
663 {{ devanagari_features, 6}, required_devanagari_features, "deva", "dev2", ContextualShape_Devanagari, ShapeCharGlyphProp_Devanagari},
664 {{ devanagari_features, 6}, required_devanagari_features, "deva", "dev2", ContextualShape_Devanagari, ShapeCharGlyphProp_Devanagari},
665 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
666 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
667 {{ devanagari_features, 6}, required_bengali_features, "beng", "bng2", ContextualShape_Bengali, ShapeCharGlyphProp_Bengali},
668 {{ devanagari_features, 6}, required_gurmukhi_features, "guru", "gur2", ContextualShape_Gurmukhi, ShapeCharGlyphProp_Gurmukhi},
669 {{ devanagari_features, 6}, required_gurmukhi_features, "guru", "gur2", ContextualShape_Gurmukhi, ShapeCharGlyphProp_Gurmukhi},
670 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
671 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
672 {{ devanagari_features, 6}, required_devanagari_features, "gujr", "gjr2", ContextualShape_Gujarati, ShapeCharGlyphProp_Gujarati},
673 {{ devanagari_features, 6}, required_oriya_features, "orya", "ory2", ContextualShape_Oriya, ShapeCharGlyphProp_Oriya},
674 {{ devanagari_features, 6}, required_oriya_features, "orya", "ory2", ContextualShape_Oriya, ShapeCharGlyphProp_Oriya},
675 {{ devanagari_features, 6}, required_tamil_features, "taml", "tam2", ContextualShape_Tamil, ShapeCharGlyphProp_Tamil},
676 {{ devanagari_features, 6}, required_tamil_features, "taml", "tam2", ContextualShape_Tamil, ShapeCharGlyphProp_Tamil},
677 {{ devanagari_features, 6}, required_telugu_features, "telu", "tel2", ContextualShape_Telugu, ShapeCharGlyphProp_Telugu},
678 {{ devanagari_features, 6}, required_telugu_features, "telu", "tel2", ContextualShape_Telugu, ShapeCharGlyphProp_Telugu},
679 {{ devanagari_features, 6}, required_telugu_features, "knda", "knd2", ContextualShape_Kannada, ShapeCharGlyphProp_Kannada},
680 {{ devanagari_features, 6}, required_telugu_features, "knda", "knd2", ContextualShape_Kannada, ShapeCharGlyphProp_Kannada},
681 {{ devanagari_features, 6}, required_telugu_features, "mlym", "mlm2", ContextualShape_Malayalam, ShapeCharGlyphProp_Malayalam},
682 {{ devanagari_features, 6}, required_telugu_features, "mlym", "mlm2", ContextualShape_Malayalam, ShapeCharGlyphProp_Malayalam},
683 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
684 {{ latin_features, 2}, NULL, "latn" , "", NULL, NULL},
685 {{ standard_features, 2}, NULL, "" , "", NULL, NULL},
686 {{ myanmar_features, 2}, NULL, "mymr", "", NULL, NULL},
687 {{ myanmar_features, 2}, NULL, "mymr", "", NULL, NULL},
688 {{ standard_features, 2}, NULL, "tale", "", NULL, NULL},
689 {{ standard_features, 2}, NULL, "talu", "", NULL, NULL},
690 {{ standard_features, 2}, NULL, "talu", "", NULL, NULL},
691 {{ khmer_features, 5}, required_khmer_features, "khmr", "", ContextualShape_Khmer, ShapeCharGlyphProp_Khmer},
692 {{ khmer_features, 5}, required_khmer_features, "khmr", "", ContextualShape_Khmer, ShapeCharGlyphProp_Khmer},
693 {{ no_features, 0}, NULL, "hani", "", NULL, NULL},
694 {{ no_features, 0}, NULL, "hani", "", NULL, NULL},
695 {{ no_features, 0}, NULL, "bopo", "", NULL, NULL},
696 {{ no_features, 0}, NULL, "kana", "", NULL, NULL},
697 {{ no_features, 0}, NULL, "hang", "", NULL, NULL},
698 {{ no_features, 0}, NULL, "yi ", "", NULL, NULL},
699 {{ ethiopic_features, 4}, NULL, "ethi", "", NULL, NULL},
700 {{ ethiopic_features, 4}, NULL, "ethi", "", NULL, NULL},
701 {{ mongolian_features, 4}, NULL, "mong", "", ContextualShape_Mongolian, NULL},
702 {{ mongolian_features, 4}, NULL, "mong", "", ContextualShape_Mongolian, NULL},
703 {{ no_features, 0}, NULL, "tfng", "", NULL, NULL},
704 {{ no_features, 0}, NULL, "nko ", "", NULL, NULL},
705 {{ no_features, 0}, NULL, "vai ", "", NULL, NULL},
706 {{ no_features, 0}, NULL, "vai ", "", NULL, NULL},
707 {{ no_features, 0}, NULL, "cher", "", NULL, NULL},
708 {{ no_features, 0}, NULL, "cans", "", NULL, NULL},
709 {{ no_features, 0}, NULL, "ogam", "", NULL, NULL},
710 {{ no_features, 0}, NULL, "runr", "", NULL, NULL},
711 {{ no_features, 0}, NULL, "brai", "", NULL, NULL},
712 {{ no_features, 0}, NULL, "", "", NULL, NULL},
713 {{ no_features, 0}, NULL, "", "", NULL, NULL},
714 {{ no_features, 0}, NULL, "dsrt", "", NULL, NULL},
715 {{ no_features, 0}, NULL, "osma", "", NULL, NULL},
716 {{ no_features, 0}, NULL, "osma", "", NULL, NULL},
717 {{ no_features, 0}, NULL, "math", "", NULL, NULL},
718 {{ hebrew_features, 1}, NULL, "hebr", "", NULL, NULL},
719 {{ latin_features, 2}, NULL, "latn" , "", NULL, NULL},
720 {{ thai_features, 1}, NULL, "thai", "", NULL, ShapeCharGlyphProp_Thai},
723 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
725 const GSUB_CoverageFormat1* cf1;
727 cf1 = table;
729 if (GET_BE_WORD(cf1->CoverageFormat) == 1)
731 int count = GET_BE_WORD(cf1->GlyphCount);
732 int i;
733 TRACE("Coverage Format 1, %i glyphs\n",count);
734 for (i = 0; i < count; i++)
735 if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
736 return i;
737 return -1;
739 else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
741 const GSUB_CoverageFormat2* cf2;
742 int i;
743 int count;
744 cf2 = (const GSUB_CoverageFormat2*)cf1;
746 count = GET_BE_WORD(cf2->RangeCount);
747 TRACE("Coverage Format 2, %i ranges\n",count);
748 for (i = 0; i < count; i++)
750 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
751 return -1;
752 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
753 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
755 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
756 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
759 return -1;
761 else
762 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
764 return -1;
767 static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
769 const GSUB_ScriptList *script;
770 const GSUB_Script *deflt = NULL;
771 int i;
772 script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
774 TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
775 for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
777 const GSUB_Script *scr;
778 int offset;
780 offset = GET_BE_WORD(script->ScriptRecord[i].Script);
781 scr = (const GSUB_Script*)((const BYTE*)script + offset);
783 if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
784 return scr;
785 if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
786 deflt = scr;
788 return deflt;
791 static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
793 int i;
794 int offset;
795 const GSUB_LangSys *Lang;
797 TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
799 for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
801 offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
802 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
804 if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
805 return Lang;
807 offset = GET_BE_WORD(script->DefaultLangSys);
808 if (offset)
810 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
811 return Lang;
813 return NULL;
816 static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
818 int i;
819 const GSUB_FeatureList *feature;
820 feature = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
822 TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
823 for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
825 int index = GET_BE_WORD(lang->FeatureIndex[i]);
826 if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
828 const GSUB_Feature *feat;
829 feat = (const GSUB_Feature*)((const BYTE*)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
830 return feat;
833 return NULL;
836 static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
838 int j;
839 TRACE("Single Substitution Subtable\n");
841 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
843 int offset;
844 const GSUB_SingleSubstFormat1 *ssf1;
845 offset = GET_BE_WORD(look->SubTable[j]);
846 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
847 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
849 int offset = GET_BE_WORD(ssf1->Coverage);
850 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
851 if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
853 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
854 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
855 TRACE(" 0x%x\n",glyphs[glyph_index]);
856 return glyph_index + write_dir;
859 else
861 const GSUB_SingleSubstFormat2 *ssf2;
862 INT index;
863 INT offset;
865 ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
866 offset = GET_BE_WORD(ssf1->Coverage);
867 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
868 index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
869 TRACE(" Coverage index %i\n",index);
870 if (index != -1)
872 if (glyphs[glyph_index] == GET_BE_WORD(ssf2->Substitute[index]))
873 return GSUB_E_NOGLYPH;
875 TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]);
876 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
877 TRACE("0x%x\n",glyphs[glyph_index]);
878 return glyph_index + write_dir;
882 return GSUB_E_NOGLYPH;
885 static INT GSUB_apply_MultipleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
887 int j;
888 TRACE("Multiple Substitution Subtable\n");
890 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
892 int offset, index;
893 const GSUB_MultipleSubstFormat1 *msf1;
894 offset = GET_BE_WORD(look->SubTable[j]);
895 msf1 = (const GSUB_MultipleSubstFormat1*)((const BYTE*)look+offset);
897 offset = GET_BE_WORD(msf1->Coverage);
898 index = GSUB_is_glyph_covered((const BYTE*)msf1+offset, glyphs[glyph_index]);
899 if (index != -1)
901 const GSUB_Sequence *seq;
902 int sub_count;
903 int j;
904 offset = GET_BE_WORD(msf1->Sequence[index]);
905 seq = (const GSUB_Sequence*)((const BYTE*)msf1+offset);
906 sub_count = GET_BE_WORD(seq->GlyphCount);
907 TRACE(" Glyph 0x%x (+%i)->",glyphs[glyph_index],(sub_count-1));
909 for (j = (*glyph_count)+(sub_count-1); j > glyph_index; j--)
910 glyphs[j] =glyphs[j-(sub_count-1)];
912 for (j = 0; j < sub_count; j++)
913 if (write_dir < 0)
914 glyphs[glyph_index + (sub_count-1) - j] = GET_BE_WORD(seq->Substitute[j]);
915 else
916 glyphs[glyph_index + j] = GET_BE_WORD(seq->Substitute[j]);
918 *glyph_count = *glyph_count + (sub_count - 1);
920 if (TRACE_ON(uniscribe))
922 for (j = 0; j < sub_count; j++)
923 TRACE(" 0x%x",glyphs[glyph_index+j]);
924 TRACE("\n");
927 return glyph_index + (sub_count * write_dir);
930 return GSUB_E_NOGLYPH;
933 static INT GSUB_apply_AlternateSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
935 int j;
936 TRACE("Alternate Substitution Subtable\n");
938 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
940 int offset;
941 const GSUB_AlternateSubstFormat1 *asf1;
942 INT index;
944 offset = GET_BE_WORD(look->SubTable[j]);
945 asf1 = (const GSUB_AlternateSubstFormat1*)((const BYTE*)look+offset);
946 offset = GET_BE_WORD(asf1->Coverage);
948 index = GSUB_is_glyph_covered((const BYTE*)asf1+offset, glyphs[glyph_index]);
949 if (index != -1)
951 const GSUB_AlternateSet *as;
952 offset = GET_BE_WORD(asf1->AlternateSet[index]);
953 as = (const GSUB_AlternateSet*)((const BYTE*)asf1+offset);
954 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as->GlyphCount));
955 if (glyphs[glyph_index] == GET_BE_WORD(as->Alternate[0]))
956 return GSUB_E_NOGLYPH;
958 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
959 glyphs[glyph_index] = GET_BE_WORD(as->Alternate[0]);
960 TRACE(" 0x%x\n",glyphs[glyph_index]);
961 return glyph_index + write_dir;
964 return GSUB_E_NOGLYPH;
967 static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
969 int j;
971 TRACE("Ligature Substitution Subtable\n");
972 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
974 const GSUB_LigatureSubstFormat1 *lsf1;
975 int offset,index;
977 offset = GET_BE_WORD(look->SubTable[j]);
978 lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
979 offset = GET_BE_WORD(lsf1->Coverage);
980 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
981 TRACE(" Coverage index %i\n",index);
982 if (index != -1)
984 const GSUB_LigatureSet *ls;
985 int k, count;
987 offset = GET_BE_WORD(lsf1->LigatureSet[index]);
988 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
989 count = GET_BE_WORD(ls->LigatureCount);
990 TRACE(" LigatureSet has %i members\n",count);
991 for (k = 0; k < count; k++)
993 const GSUB_Ligature *lig;
994 int CompCount,l,CompIndex;
996 offset = GET_BE_WORD(ls->Ligature[k]);
997 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
998 CompCount = GET_BE_WORD(lig->CompCount) - 1;
999 CompIndex = glyph_index+write_dir;
1000 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
1002 int CompGlyph;
1003 CompGlyph = GET_BE_WORD(lig->Component[l]);
1004 if (CompGlyph != glyphs[CompIndex])
1005 break;
1006 CompIndex += write_dir;
1008 if (l == CompCount)
1010 int replaceIdx = glyph_index;
1011 if (write_dir < 0)
1012 replaceIdx = glyph_index - CompCount;
1014 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
1015 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
1016 TRACE("0x%x\n",glyphs[replaceIdx]);
1017 if (CompCount > 0)
1019 int j;
1020 for (j = replaceIdx + 1; j < *glyph_count; j++)
1021 glyphs[j] =glyphs[j+CompCount];
1022 *glyph_count = *glyph_count - CompCount;
1024 return replaceIdx + write_dir;
1029 return GSUB_E_NOGLYPH;
1032 static INT GSUB_apply_ChainContextSubst(const GSUB_LookupList* lookup, const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1034 int j;
1035 BOOL done = FALSE;
1037 TRACE("Chaining Contextual Substitution Subtable\n");
1038 for (j = 0; j < GET_BE_WORD(look->SubTableCount) && !done; j++)
1040 const GSUB_ChainContextSubstFormat1 *ccsf1;
1041 int offset;
1042 int dirLookahead = write_dir;
1043 int dirBacktrack = -1 * write_dir;
1045 offset = GET_BE_WORD(look->SubTable[j]);
1046 ccsf1 = (const GSUB_ChainContextSubstFormat1*)((const BYTE*)look+offset);
1047 if (GET_BE_WORD(ccsf1->SubstFormat) == 1)
1049 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
1050 continue;
1052 else if (GET_BE_WORD(ccsf1->SubstFormat) == 2)
1054 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
1055 continue;
1057 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
1059 int k;
1060 int indexGlyphs;
1061 const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
1062 const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
1063 const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
1064 const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
1065 int newIndex = glyph_index;
1067 ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
1069 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
1071 for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
1073 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
1074 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
1075 break;
1077 if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
1078 continue;
1079 TRACE("Matched Backtrack\n");
1081 ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)(((LPBYTE)ccsf1)+sizeof(GSUB_ChainContextSubstFormat3_1) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)-1)));
1083 indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
1084 for (k = 0; k < indexGlyphs; k++)
1086 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
1087 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
1088 break;
1090 if (k != indexGlyphs)
1091 continue;
1092 TRACE("Matched IndexGlyphs\n");
1094 ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)(((LPBYTE)ccsf3_2)+sizeof(GSUB_ChainContextSubstFormat3_2) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_2->InputGlyphCount)-1)));
1096 for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
1098 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
1099 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
1100 break;
1102 if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
1103 continue;
1104 TRACE("Matched LookAhead\n");
1106 ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)(((LPBYTE)ccsf3_3)+sizeof(GSUB_ChainContextSubstFormat3_3) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)-1)));
1108 if (GET_BE_WORD(ccsf3_4->SubstCount))
1110 for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
1112 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
1113 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
1115 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
1116 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
1117 if (newIndex == -1)
1119 ERR("Chain failed to generate a glyph\n");
1120 continue;
1123 return newIndex;
1125 else return GSUB_E_NOGLYPH;
1128 return -1;
1131 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
1133 int offset;
1134 const GSUB_LookupTable *look;
1136 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
1137 look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
1138 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
1139 switch(GET_BE_WORD(look->LookupType))
1141 case 1:
1142 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1143 case 2:
1144 return GSUB_apply_MultipleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1145 case 3:
1146 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1147 case 4:
1148 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
1149 case 6:
1150 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
1151 default:
1152 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
1154 return GSUB_E_NOGLYPH;
1157 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)
1159 int i;
1160 int out_index = GSUB_E_NOGLYPH;
1161 const GSUB_LookupList *lookup;
1163 lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1165 TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
1166 for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
1168 out_index = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[i]), glyphs, glyph_index, write_dir, glyph_count);
1169 if (out_index != GSUB_E_NOGLYPH)
1170 break;
1172 if (out_index == GSUB_E_NOGLYPH)
1173 TRACE("lookups found no glyphs\n");
1174 else
1176 int out2;
1177 out2 = GSUB_apply_feature_all_lookups(header, feature, glyphs, glyph_index, write_dir, glyph_count);
1178 if (out2!=GSUB_E_NOGLYPH)
1179 out_index = out2;
1181 return out_index;
1184 static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, BOOL tryNew)
1186 UINT charset;
1188 if (psc->userScript != 0)
1190 if (tryNew && ShapingData[psa->eScript].newOtTag[0] != 0 && strncmp((char*)&psc->userScript,ShapingData[psa->eScript].otTag,4)==0)
1191 return ShapingData[psa->eScript].newOtTag;
1192 else
1193 return (char*)&psc->userScript;
1196 if (tryNew && ShapingData[psa->eScript].newOtTag[0] != 0)
1197 return ShapingData[psa->eScript].newOtTag;
1199 if (ShapingData[psa->eScript].otTag[0] != 0)
1200 return ShapingData[psa->eScript].otTag;
1203 * fall back to the font charset
1205 charset = GetTextCharsetInfo(hdc, NULL, 0x0);
1206 switch (charset)
1208 case ANSI_CHARSET: return "latn";
1209 case BALTIC_CHARSET: return "latn"; /* ?? */
1210 case CHINESEBIG5_CHARSET: return "hani";
1211 case EASTEUROPE_CHARSET: return "latn"; /* ?? */
1212 case GB2312_CHARSET: return "hani";
1213 case GREEK_CHARSET: return "grek";
1214 case HANGUL_CHARSET: return "hang";
1215 case RUSSIAN_CHARSET: return "cyrl";
1216 case SHIFTJIS_CHARSET: return "kana";
1217 case TURKISH_CHARSET: return "latn"; /* ?? */
1218 case VIETNAMESE_CHARSET: return "latn";
1219 case JOHAB_CHARSET: return "latn"; /* ?? */
1220 case ARABIC_CHARSET: return "arab";
1221 case HEBREW_CHARSET: return "hebr";
1222 case THAI_CHARSET: return "thai";
1223 default: return "latn";
1227 static LPCVOID load_GSUB_feature(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, const char* feat)
1229 const GSUB_Feature *feature;
1230 const char* script;
1231 int i;
1233 script = get_opentype_script(hdc,psa,psc,FALSE);
1235 for (i = 0; i < psc->feature_count; i++)
1237 if (strncmp(psc->features[i].tag,feat,4)==0 && strncmp(psc->features[i].script,script,4)==0)
1238 return psc->features[i].feature;
1241 feature = NULL;
1243 if (psc->GSUB_Table)
1245 const GSUB_Script *script;
1246 const GSUB_LangSys *language;
1247 int attempt = 2;
1251 script = GSUB_get_script_table(psc->GSUB_Table, get_opentype_script(hdc,psa,psc,(attempt==2)));
1252 attempt--;
1253 if (script)
1255 if (psc->userLang != 0)
1256 language = GSUB_get_lang_table(script,(char*)&psc->userLang);
1257 else
1258 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
1259 if (language)
1260 feature = GSUB_get_feature(psc->GSUB_Table, language, feat);
1262 } while(attempt && !feature);
1264 /* try in the default (latin) table */
1265 if (!feature)
1267 script = GSUB_get_script_table(psc->GSUB_Table, "latn");
1268 if (script)
1270 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
1271 if (language)
1272 feature = GSUB_get_feature(psc->GSUB_Table, language, feat);
1277 TRACE("Feature %s located at %p\n",debugstr_an(feat,4),feature);
1279 psc->feature_count++;
1281 if (psc->features)
1282 psc->features = HeapReAlloc(GetProcessHeap(), 0, psc->features, psc->feature_count * sizeof(LoadedFeature));
1283 else
1284 psc->features = HeapAlloc(GetProcessHeap(), 0, psc->feature_count * sizeof(LoadedFeature));
1286 lstrcpynA(psc->features[psc->feature_count - 1].tag, feat, 5);
1287 lstrcpynA(psc->features[psc->feature_count - 1].script, script, 5);
1288 psc->features[psc->feature_count - 1].feature = feature;
1289 return feature;
1292 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)
1294 const GSUB_Feature *feature;
1296 feature = load_GSUB_feature(hdc, psa, psc, feat);
1297 if (!feature)
1298 return GSUB_E_NOFEATURE;
1300 TRACE("applying feature %s\n",feat);
1301 return GSUB_apply_feature_all_lookups(psc->GSUB_Table, feature, glyphs, index, write_dir, glyph_count);
1304 static VOID *load_gsub_table(HDC hdc)
1306 VOID* GSUB_Table = NULL;
1307 int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
1308 if (length != GDI_ERROR)
1310 GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
1311 GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
1312 TRACE("Loaded GSUB table of %i bytes\n",length);
1314 return GSUB_Table;
1317 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)
1319 WORD *glyphs;
1320 INT glyph_count = count;
1321 INT rc;
1323 glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD)*(count*2));
1324 GetGlyphIndicesW(hdc, chars, count, glyphs, 0);
1325 rc = apply_GSUB_feature_to_glyph(hdc, psa, psc, glyphs, 0, write_dir, &glyph_count, feature);
1326 if (rc > GSUB_E_NOGLYPH)
1327 rc = count - glyph_count;
1328 else
1329 rc = 0;
1331 HeapFree(GetProcessHeap(),0,glyphs);
1332 return rc;
1335 static WORD GDEF_get_glyph_class(const GDEF_Header *header, WORD glyph)
1337 int offset;
1338 WORD class = 0;
1339 const GDEF_ClassDefFormat1 *cf1;
1341 if (!header)
1342 return 0;
1344 offset = GET_BE_WORD(header->GlyphClassDef);
1345 if (!offset)
1346 return 0;
1348 cf1 = (GDEF_ClassDefFormat1*)(((BYTE*)header)+offset);
1349 if (GET_BE_WORD(cf1->ClassFormat) == 1)
1351 if (glyph >= GET_BE_WORD(cf1->StartGlyph))
1353 int index = glyph - GET_BE_WORD(cf1->StartGlyph);
1354 if (index < GET_BE_WORD(cf1->GlyphCount))
1355 class = GET_BE_WORD(cf1->ClassValueArray[index]);
1358 else if (GET_BE_WORD(cf1->ClassFormat) == 2)
1360 const GDEF_ClassDefFormat2 *cf2 = (GDEF_ClassDefFormat2*)cf1;
1361 int i, top;
1362 top = GET_BE_WORD(cf2->ClassRangeCount);
1363 for (i = 0; i < top; i++)
1365 if (glyph >= GET_BE_WORD(cf2->ClassRangeRecord[i].Start) &&
1366 glyph <= GET_BE_WORD(cf2->ClassRangeRecord[i].End))
1368 class = GET_BE_WORD(cf2->ClassRangeRecord[i].Class);
1369 break;
1373 else
1374 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1->ClassFormat));
1376 return class;
1379 static VOID *load_gdef_table(HDC hdc)
1381 VOID* GDEF_Table = NULL;
1382 int length = GetFontData(hdc, GDEF_TAG , 0, NULL, 0);
1383 if (length != GDI_ERROR)
1385 GDEF_Table = HeapAlloc(GetProcessHeap(),0,length);
1386 GetFontData(hdc, GDEF_TAG , 0, GDEF_Table, length);
1387 TRACE("Loaded GDEF table of %i bytes\n",length);
1389 return GDEF_Table;
1392 static void GDEF_UpdateGlyphProps(HDC hdc, ScriptCache *psc, const WORD *pwGlyphs, const WORD cGlyphs, WORD* pwLogClust, const WORD cChars, SCRIPT_GLYPHPROP *pGlyphProp)
1394 int i;
1396 if (!psc->GDEF_Table)
1397 psc->GDEF_Table = load_gdef_table(hdc);
1399 for (i = 0; i < cGlyphs; i++)
1401 WORD class;
1402 int char_count = 0;
1403 int k;
1405 for (k = 0; k < cChars; k++)
1406 if (pwLogClust[k] == i)
1407 char_count++;
1409 class = GDEF_get_glyph_class(psc->GDEF_Table, pwGlyphs[i]);
1411 switch (class)
1413 case 0:
1414 case BaseGlyph:
1415 pGlyphProp[i].sva.fClusterStart = 1;
1416 pGlyphProp[i].sva.fDiacritic = 0;
1417 pGlyphProp[i].sva.fZeroWidth = 0;
1418 break;
1419 case LigatureGlyph:
1420 pGlyphProp[i].sva.fClusterStart = 1;
1421 pGlyphProp[i].sva.fDiacritic = 0;
1422 pGlyphProp[i].sva.fZeroWidth = 0;
1423 break;
1424 case MarkGlyph:
1425 pGlyphProp[i].sva.fClusterStart = 0;
1426 pGlyphProp[i].sva.fDiacritic = 1;
1427 pGlyphProp[i].sva.fZeroWidth = 1;
1428 break;
1429 case ComponentGlyph:
1430 pGlyphProp[i].sva.fClusterStart = 0;
1431 pGlyphProp[i].sva.fDiacritic = 0;
1432 pGlyphProp[i].sva.fZeroWidth = 0;
1433 break;
1434 default:
1435 ERR("Unknown glyph class %i\n",class);
1436 pGlyphProp[i].sva.fClusterStart = 1;
1437 pGlyphProp[i].sva.fDiacritic = 0;
1438 pGlyphProp[i].sva.fZeroWidth = 0;
1441 if (char_count == 0)
1442 pGlyphProp[i].sva.fClusterStart = 0;
1446 static void UpdateClustersFromGlyphProp(const int cGlyphs, const int cChars, WORD* pwLogClust, SCRIPT_GLYPHPROP *pGlyphProp)
1448 int i;
1450 for (i = 0; i < cGlyphs; i++)
1452 if (!pGlyphProp[i].sva.fClusterStart)
1454 int j;
1455 for (j = 0; j < cChars; j++)
1457 if (pwLogClust[j] == i)
1459 int k = j;
1460 while (!pGlyphProp[pwLogClust[k]].sva.fClusterStart && k >= 0 && k <cChars)
1461 k-=1;
1462 if (pGlyphProp[pwLogClust[k]].sva.fClusterStart)
1463 pwLogClust[j] = pwLogClust[k];
1470 static void UpdateClusters(int nextIndex, int changeCount, int write_dir, int chars, WORD* pwLogClust )
1472 if (changeCount == 0)
1473 return;
1474 else
1476 int i;
1477 int target_glyph = nextIndex - write_dir;
1478 int seeking_glyph;
1479 int target_index = -1;
1480 int replacing_glyph = -1;
1481 int changed = 0;
1482 int top_logclust = 0;
1484 if (changeCount > 0)
1486 if (write_dir > 0)
1487 target_glyph = nextIndex - changeCount;
1488 else
1489 target_glyph = nextIndex + (changeCount + 1);
1492 seeking_glyph = target_glyph;
1493 for (i = 0; i < chars; i++)
1494 if (pwLogClust[i] > top_logclust)
1495 top_logclust = pwLogClust[i];
1497 do {
1498 if (write_dir > 0)
1499 for (i = 0; i < chars; i++)
1501 if (pwLogClust[i] == seeking_glyph)
1503 target_index = i;
1504 break;
1507 else
1508 for (i = chars - 1; i >= 0; i--)
1510 if (pwLogClust[i] == seeking_glyph)
1512 target_index = i;
1513 break;
1516 if (target_index == -1)
1517 seeking_glyph ++;
1519 while (target_index == -1 && seeking_glyph <= top_logclust);
1521 if (target_index == -1)
1523 ERR("Unable to find target glyph\n");
1524 return;
1527 if (changeCount < 0)
1529 /* merge glyphs */
1530 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1532 if (pwLogClust[i] == target_glyph)
1533 continue;
1534 if(pwLogClust[i] == replacing_glyph)
1535 pwLogClust[i] = target_glyph;
1536 else
1538 changed--;
1539 if (changed >= changeCount)
1541 replacing_glyph = pwLogClust[i];
1542 pwLogClust[i] = target_glyph;
1544 else
1545 break;
1549 /* renumber trailing indexes*/
1550 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1552 if (pwLogClust[i] != target_glyph)
1553 pwLogClust[i] += changeCount;
1556 else
1558 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1559 pwLogClust[i] += changeCount;
1564 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 )
1566 if (psc->GSUB_Table)
1568 const GSUB_Feature *feature;
1569 const GSUB_LookupList *lookup;
1570 const GSUB_Header *header = psc->GSUB_Table;
1571 int lookup_index, lookup_count;
1573 feature = load_GSUB_feature(hdc, psa, psc, feat);
1574 if (!feature)
1575 return GSUB_E_NOFEATURE;
1577 TRACE("applying feature %s\n",debugstr_an(feat,4));
1578 lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
1579 lookup_count = GET_BE_WORD(feature->LookupCount);
1580 TRACE("%i lookups\n", lookup_count);
1581 for (lookup_index = 0; lookup_index < lookup_count; lookup_index++)
1583 int i;
1585 if (write_dir > 0)
1586 i = 0;
1587 else
1588 i = *pcGlyphs-1;
1589 TRACE("applying lookup (%i/%i)\n",lookup_index,lookup_count);
1590 while(i < *pcGlyphs && i >= 0)
1592 INT nextIndex;
1593 INT prevCount = *pcGlyphs;
1595 nextIndex = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[lookup_index]), pwOutGlyphs, i, write_dir, pcGlyphs);
1596 if (*pcGlyphs != prevCount)
1598 UpdateClusters(nextIndex, *pcGlyphs - prevCount, write_dir, cChars, pwLogClust);
1599 i = nextIndex;
1601 else
1602 i+=write_dir;
1605 return *pcGlyphs;
1607 return GSUB_E_NOFEATURE;
1610 static inline BOOL get_GSUB_Indic2(SCRIPT_ANALYSIS *psa, ScriptCache *psc)
1612 return(GSUB_get_script_table(psc->GSUB_Table,ShapingData[psa->eScript].newOtTag)!=NULL);
1615 static WCHAR neighbour_char(int i, int delta, const WCHAR* chars, INT cchLen)
1617 if (i + delta < 0)
1618 return 0;
1619 if ( i+ delta >= cchLen)
1620 return 0;
1622 i += delta;
1624 return chars[i];
1627 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
1629 if (i + delta < 0)
1631 if (psa->fLinkBefore)
1632 return jtR;
1633 else
1634 return jtU;
1636 if ( i+ delta >= cchLen)
1638 if (psa->fLinkAfter)
1639 return jtL;
1640 else
1641 return jtU;
1644 i += delta;
1646 if (context_type[i] == jtT)
1647 return neighbour_joining_type(i,delta,context_type,cchLen,psa);
1648 else
1649 return context_type[i];
1652 static inline BOOL right_join_causing(CHAR joining_type)
1654 return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
1657 static inline BOOL left_join_causing(CHAR joining_type)
1659 return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
1662 static inline BOOL word_break_causing(WCHAR chr)
1664 /* we are working within a string of characters already guareented to
1665 be within one script, Syriac, so we do not worry about any character
1666 other than the space character outside of that range */
1667 return (chr == 0 || chr == 0x20 );
1671 * ContextualShape_Arabic
1673 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1675 CHAR *context_type;
1676 INT *context_shape;
1677 INT dirR, dirL;
1678 int i;
1680 if (*pcGlyphs != cChars)
1682 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1683 return;
1686 if (!psa->fLogicalOrder && psa->fRTL)
1688 dirR = 1;
1689 dirL = -1;
1691 else
1693 dirR = -1;
1694 dirL = 1;
1697 if (!psc->GSUB_Table)
1698 psc->GSUB_Table = load_gsub_table(hdc);
1700 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1701 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1703 for (i = 0; i < cChars; i++)
1704 context_type[i] = get_table_entry( wine_shaping_table, pwcChars[i] );
1706 for (i = 0; i < cChars; i++)
1708 if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1709 context_shape[i] = Xr;
1710 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1711 context_shape[i] = Xl;
1712 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)))
1713 context_shape[i] = Xm;
1714 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1715 context_shape[i] = Xr;
1716 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1717 context_shape[i] = Xl;
1718 else
1719 context_shape[i] = Xn;
1722 /* Contextual Shaping */
1723 i = 0;
1724 while(i < *pcGlyphs)
1726 BOOL shaped = FALSE;
1728 if (psc->GSUB_Table)
1730 INT nextIndex;
1731 INT prevCount = *pcGlyphs;
1732 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1733 if (nextIndex > GSUB_E_NOGLYPH)
1735 i = nextIndex;
1736 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1738 shaped = (nextIndex > GSUB_E_NOGLYPH);
1741 if (!shaped)
1743 if (context_shape[i] == Xn)
1745 WORD newGlyph = pwOutGlyphs[i];
1746 if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
1748 /* fall back to presentation form B */
1749 WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
1750 if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
1751 pwOutGlyphs[i] = newGlyph;
1754 i++;
1758 HeapFree(GetProcessHeap(),0,context_shape);
1759 HeapFree(GetProcessHeap(),0,context_type);
1763 * ContextualShape_Syriac
1766 #define ALAPH 0x710
1767 #define DALATH 0x715
1768 #define RISH 0x72A
1770 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1772 CHAR *context_type;
1773 INT *context_shape;
1774 INT dirR, dirL;
1775 int i;
1777 if (*pcGlyphs != cChars)
1779 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1780 return;
1783 if (!psa->fLogicalOrder && psa->fRTL)
1785 dirR = 1;
1786 dirL = -1;
1788 else
1790 dirR = -1;
1791 dirL = 1;
1794 if (!psc->GSUB_Table)
1795 psc->GSUB_Table = load_gsub_table(hdc);
1797 if (!psc->GSUB_Table)
1798 return;
1800 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1801 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1803 for (i = 0; i < cChars; i++)
1804 context_type[i] = get_table_entry( wine_shaping_table, pwcChars[i] );
1806 for (i = 0; i < cChars; i++)
1808 if (pwcChars[i] == ALAPH)
1810 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1812 if (left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1813 context_shape[i] = Afj;
1814 else if ( rchar != DALATH && rchar != RISH &&
1815 !left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) &&
1816 word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1817 context_shape[i] = Afn;
1818 else if ( (rchar == DALATH || rchar == RISH) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1819 context_shape[i] = Afx;
1820 else
1821 context_shape[i] = Xn;
1823 else if (context_type[i] == jtR &&
1824 right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1825 context_shape[i] = Xr;
1826 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1827 context_shape[i] = Xl;
1828 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)))
1829 context_shape[i] = Xm;
1830 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1831 context_shape[i] = Xr;
1832 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1833 context_shape[i] = Xl;
1834 else
1835 context_shape[i] = Xn;
1838 /* Contextual Shaping */
1839 i = 0;
1840 while(i < *pcGlyphs)
1842 INT nextIndex;
1843 INT prevCount = *pcGlyphs;
1844 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1845 if (nextIndex > GSUB_E_NOGLYPH)
1847 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1848 i = nextIndex;
1850 else
1851 i++;
1854 HeapFree(GetProcessHeap(),0,context_shape);
1855 HeapFree(GetProcessHeap(),0,context_type);
1859 * ContextualShape_Phags_pa
1862 #define phags_pa_CANDRABINDU 0xA873
1863 #define phags_pa_START 0xA840
1864 #define phags_pa_END 0xA87F
1866 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1868 INT *context_shape;
1869 INT dirR, dirL;
1870 int i;
1872 if (*pcGlyphs != cChars)
1874 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1875 return;
1878 if (!psa->fLogicalOrder && psa->fRTL)
1880 dirR = 1;
1881 dirL = -1;
1883 else
1885 dirR = -1;
1886 dirL = 1;
1889 if (!psc->GSUB_Table)
1890 psc->GSUB_Table = load_gsub_table(hdc);
1892 if (!psc->GSUB_Table)
1893 return;
1895 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1897 for (i = 0; i < cChars; i++)
1899 if (pwcChars[i] >= phags_pa_START && pwcChars[i] <= phags_pa_END)
1901 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1902 WCHAR lchar = neighbour_char(i,dirL,pwcChars,cChars);
1903 BOOL jrchar = (rchar != phags_pa_CANDRABINDU && rchar >= phags_pa_START && rchar <= phags_pa_END);
1904 BOOL jlchar = (lchar != phags_pa_CANDRABINDU && lchar >= phags_pa_START && lchar <= phags_pa_END);
1906 if (jrchar && jlchar)
1907 context_shape[i] = Xm;
1908 else if (jrchar)
1909 context_shape[i] = Xr;
1910 else if (jlchar)
1911 context_shape[i] = Xl;
1912 else
1913 context_shape[i] = Xn;
1915 else
1916 context_shape[i] = -1;
1919 /* Contextual Shaping */
1920 i = 0;
1921 while(i < *pcGlyphs)
1923 if (context_shape[i] >= 0)
1925 INT nextIndex;
1926 INT prevCount = *pcGlyphs;
1927 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1928 if (nextIndex > GSUB_E_NOGLYPH)
1930 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1931 i = nextIndex;
1933 else
1934 i++;
1936 else
1937 i++;
1940 HeapFree(GetProcessHeap(),0,context_shape);
1943 static void ReplaceInsertChars(HDC hdc, INT cWalk, INT* pcChars, WCHAR *pwOutChars, const WCHAR *replacements)
1945 int i;
1947 /* Replace */
1948 pwOutChars[cWalk] = replacements[0];
1949 cWalk=cWalk+1;
1951 /* Insert */
1952 for (i = 1; replacements[i] != 0x0000 && i < 3; i++)
1954 int j;
1955 for (j = *pcChars; j > cWalk; j--)
1956 pwOutChars[j] = pwOutChars[j-1];
1957 *pcChars= *pcChars+1;
1958 pwOutChars[cWalk] = replacements[i];
1959 cWalk = cWalk+1;
1963 static void DecomposeVowels(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const VowelComponents vowels[], WORD* pwLogClust, INT cChars)
1965 int i;
1966 int cWalk;
1968 for (cWalk = 0; cWalk < *pcChars; cWalk++)
1970 for (i = 0; vowels[i].base != 0x0; i++)
1972 if (pwOutChars[cWalk] == vowels[i].base)
1974 int o = 0;
1975 ReplaceInsertChars(hdc, cWalk, pcChars, pwOutChars, vowels[i].parts);
1976 if (vowels[i].parts[1]) { cWalk++; o++; }
1977 if (vowels[i].parts[2]) { cWalk++; o++; }
1978 UpdateClusters(cWalk, o, 1, cChars, pwLogClust);
1979 break;
1985 static void ComposeConsonants(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const ConsonantComponents consonants[], WORD* pwLogClust)
1987 int i;
1988 int offset = 0;
1989 int cWalk;
1991 for (cWalk = 0; cWalk < *pcChars; cWalk++)
1993 for (i = 0; consonants[i].output!= 0x0; i++)
1995 int j;
1996 for (j = 0; j + cWalk < *pcChars && consonants[i].parts[j]!=0x0; j++)
1997 if (pwOutChars[cWalk+j] != consonants[i].parts[j])
1998 break;
2000 if (consonants[i].parts[j]==0x0) /* matched all */
2002 int k;
2003 j--;
2004 pwOutChars[cWalk] = consonants[i].output;
2005 for(k = cWalk+1; k < *pcChars - j; k++)
2006 pwOutChars[k] = pwOutChars[k+j];
2007 *pcChars = *pcChars - j;
2008 for (k = j ; k > 0; k--)
2009 pwLogClust[cWalk + k + offset] = pwLogClust[cWalk + offset];
2010 offset += j;
2011 for (k = cWalk + j + offset; k < *pcChars + offset; k++)
2012 pwLogClust[k]--;
2013 break;
2016 cWalk++;
2020 static void Reorder_Ra_follows_base(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2022 if (s->ralf >= 0)
2024 int j;
2025 WORD Ra = pwChar[s->start];
2026 WORD H = pwChar[s->start+1];
2028 TRACE("Doing reorder of Ra to %i\n",s->base);
2029 for (j = s->start; j < s->base-1; j++)
2030 pwChar[j] = pwChar[j+2];
2031 pwChar[s->base-1] = Ra;
2032 pwChar[s->base] = H;
2034 s->ralf = s->base-1;
2035 s->base -= 2;
2039 static void Reorder_Ra_follows_matra(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2041 if (s->ralf >= 0)
2043 int j,loc;
2044 int stop = (s->blwf >=0)? s->blwf+1 : s->base;
2045 WORD Ra = pwChar[s->start];
2046 WORD H = pwChar[s->start+1];
2047 for (loc = s->end; loc > stop; loc--)
2048 if (lexical(pwChar[loc]) == lex_Matra_post || lexical(pwChar[loc]) == lex_Matra_below)
2049 break;
2051 TRACE("Doing reorder of Ra to %i\n",loc);
2052 for (j = s->start; j < loc-1; j++)
2053 pwChar[j] = pwChar[j+2];
2054 pwChar[loc-1] = Ra;
2055 pwChar[loc] = H;
2057 s->ralf = loc-1;
2058 s->base -= 2;
2059 if (s->blwf >= 0) s->blwf -= 2;
2060 if (s->pref >= 0) s->pref -= 2;
2064 static void Reorder_Ra_follows_syllable(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2066 if (s->ralf >= 0)
2068 int j;
2069 WORD Ra = pwChar[s->start];
2070 WORD H = pwChar[s->start+1];
2072 TRACE("Doing reorder of Ra to %i\n",s->end-1);
2073 for (j = s->start; j < s->end-1; j++)
2074 pwChar[j] = pwChar[j+2];
2075 pwChar[s->end-1] = Ra;
2076 pwChar[s->end] = H;
2078 s->ralf = s->end-1;
2079 s->base -= 2;
2080 if (s->blwf >= 0) s->blwf -= 2;
2081 if (s->pref >= 0) s->pref -= 2;
2085 static void Reorder_Matra_precede_base(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2087 int i;
2089 /* reorder Matras */
2090 if (s->end > s->base)
2092 for (i = 1; i <= s->end-s->base; i++)
2094 if (lexical(pwChar[s->base+i]) == lex_Matra_pre)
2096 int j;
2097 WCHAR c = pwChar[s->base+i];
2098 TRACE("Doing reorder of %x %x\n",c,pwChar[s->base]);
2099 for (j = s->base+i; j > s->base; j--)
2100 pwChar[j] = pwChar[j-1];
2101 pwChar[s->base] = c;
2103 if (s->ralf >= s->base) s->ralf++;
2104 if (s->blwf >= s->base) s->blwf++;
2105 if (s->pref >= s->base) s->pref++;
2106 s->base ++;
2112 static void Reorder_Matra_precede_syllable(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2114 int i;
2116 /* reorder Matras */
2117 if (s->end > s->base)
2119 for (i = 1; i <= s->end-s->base; i++)
2121 if (lexical(pwChar[s->base+i]) == lex_Matra_pre)
2123 int j;
2124 WCHAR c = pwChar[s->base+i];
2125 TRACE("Doing reorder of %x to %i\n",c,s->start);
2126 for (j = s->base+i; j > s->start; j--)
2127 pwChar[j] = pwChar[j-1];
2128 pwChar[s->start] = c;
2130 if (s->ralf >= 0) s->ralf++;
2131 if (s->blwf >= 0) s->blwf++;
2132 if (s->pref >= 0) s->pref++;
2133 s->base ++;
2139 static void SecondReorder_Blwf_follows_matra(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2141 if (s->blwf >= 0 && g->blwf > g->base)
2143 int j,loc;
2144 int g_offset;
2145 for (loc = s->end; loc > s->blwf; loc--)
2146 if (lexical(pwChar[loc]) == lex_Matra_below || lexical(pwChar[loc]) == lex_Matra_above || lexical(pwChar[loc]) == lex_Matra_post)
2147 break;
2149 g_offset = (loc - s->blwf) - 1;
2151 if (loc != s->blwf)
2153 WORD blwf = glyphs[g->blwf];
2154 TRACE("Doing reorder of Below-base to %i (glyph offset %i)\n",loc,g_offset);
2155 /* do not care about the pwChar array anymore, just the glyphs */
2156 for (j = 0; j < g_offset; j++)
2157 glyphs[g->blwf + j] = glyphs[g->blwf + j + 1];
2158 glyphs[g->blwf + g_offset] = blwf;
2163 static void SecondReorder_Matra_precede_base(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2165 int i;
2167 /* reorder previously moved Matras to correct position*/
2168 for (i = s->start; i < s->base; i++)
2170 if (lexical(pwChar[i]) == lex_Matra_pre)
2172 int j;
2173 int g_start = g->start + i - s->start;
2174 if (g_start < g->base -1 )
2176 WCHAR og = glyphs[g_start];
2177 TRACE("Doing reorder of matra from %i to %i\n",g_start,g->base);
2178 for (j = g_start; j < g->base-1; j++)
2179 glyphs[j] = glyphs[j+1];
2180 glyphs[g->base-1] = og;
2186 static void SecondReorder_Pref_precede_base(LPWSTR pwChar, IndicSyllable *s, WORD *glyphs, IndicSyllable *g, lexical_function lexical)
2188 if (s->pref >= 0 && g->pref > g->base)
2190 int j;
2191 WCHAR og = glyphs[g->pref];
2192 TRACE("Doing reorder of pref from %i to %i\n",g->pref,g->base);
2193 for (j = g->pref; j > g->base; j--)
2194 glyphs[j] = glyphs[j-1];
2195 glyphs[g->base] = og;
2199 static void Reorder_Like_Sinhala(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2201 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2202 if (s->start == s->base && s->base == s->end) return;
2203 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2205 Reorder_Ra_follows_base(pwChar, s, lexical);
2206 Reorder_Matra_precede_base(pwChar, s, lexical);
2209 static void Reorder_Like_Devanagari(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2211 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2212 if (s->start == s->base && s->base == s->end) return;
2213 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2215 Reorder_Ra_follows_matra(pwChar, s, lexical);
2216 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2219 static void Reorder_Like_Bengali(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2221 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2222 if (s->start == s->base && s->base == s->end) return;
2223 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2225 Reorder_Ra_follows_base(pwChar, s, lexical);
2226 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2229 static void Reorder_Like_Kannada(LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
2231 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2232 if (s->start == s->base && s->base == s->end) return;
2233 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2235 Reorder_Ra_follows_syllable(pwChar, s, lexical);
2236 Reorder_Matra_precede_syllable(pwChar, s, lexical);
2239 static void SecondReorder_Like_Telugu(LPWSTR pwChar, IndicSyllable *s, WORD* pwGlyphs, IndicSyllable *g, lexical_function lexical)
2241 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2242 TRACE("Glyphs (%i..%i..%i)\n",g->start,g->base,g->end);
2243 if (s->start == s->base && s->base == s->end) return;
2244 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2246 SecondReorder_Blwf_follows_matra(pwChar, s, pwGlyphs, g, lexical);
2249 static void SecondReorder_Like_Tamil(LPWSTR pwChar, IndicSyllable *s, WORD* pwGlyphs, IndicSyllable *g, lexical_function lexical)
2251 TRACE("Syllable (%i..%i..%i)\n",s->start,s->base,s->end);
2252 TRACE("Glyphs (%i..%i..%i)\n",g->start,g->base,g->end);
2253 if (s->start == s->base && s->base == s->end) return;
2254 if (lexical(pwChar[s->base]) == lex_Vowel) return;
2256 SecondReorder_Matra_precede_base(pwChar, s, pwGlyphs, g, lexical);
2257 SecondReorder_Pref_precede_base(pwChar, s, pwGlyphs, g, lexical);
2261 static inline void shift_syllable_glyph_indexs(IndicSyllable *glyph_index, INT index, INT shift)
2263 if (shift == 0)
2264 return;
2266 if (glyph_index->start > index)
2267 glyph_index->start += shift;
2268 if (glyph_index->base > index)
2269 glyph_index->base+= shift;
2270 if (glyph_index->end > index)
2271 glyph_index->end+= shift;
2272 if (glyph_index->ralf > index)
2273 glyph_index->ralf+= shift;
2274 if (glyph_index->blwf > index)
2275 glyph_index->blwf+= shift;
2276 if (glyph_index->pref > index)
2277 glyph_index->pref+= shift;
2280 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 )
2282 int index = glyph_index->start;
2284 if (!feature)
2285 return;
2287 while(index <= glyph_index->end)
2289 INT nextIndex;
2290 INT prevCount = *pcGlyphs;
2291 nextIndex = GSUB_apply_feature_all_lookups(psc->GSUB_Table, feature, pwOutGlyphs, index, 1, pcGlyphs);
2292 if (nextIndex > GSUB_E_NOGLYPH)
2294 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2295 shift_syllable_glyph_indexs(glyph_index,index,*pcGlyphs - prevCount);
2296 index = nextIndex;
2298 else
2299 index++;
2303 static inline INT find_consonant_halant(WCHAR* pwChars, INT index, INT end, lexical_function lexical)
2305 int i = 0;
2306 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)))))
2307 i++;
2308 if (index + i <= end-1)
2309 return index + i;
2310 else
2311 return -1;
2314 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)
2316 INT index, nextIndex;
2317 INT count,g_offset;
2319 count = syllable->base - syllable->start;
2321 g_offset = 0;
2322 index = find_consonant_halant(&pwChars[syllable->start], 0, count, lexical);
2323 while (index >= 0 && index + g_offset < (glyph_index->base - glyph_index->start))
2325 INT prevCount = *pcGlyphs;
2326 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, index+glyph_index->start+g_offset, 1, pcGlyphs, feature);
2327 if (nextIndex > GSUB_E_NOGLYPH)
2329 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2330 shift_syllable_glyph_indexs(glyph_index, index + glyph_index->start + g_offset, (*pcGlyphs - prevCount));
2331 g_offset += (*pcGlyphs - prevCount);
2334 index+=2;
2335 index = find_consonant_halant(&pwChars[syllable->start], index, count, lexical);
2339 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)
2341 INT nextIndex;
2342 INT prevCount = *pcGlyphs;
2344 if (syllable->ralf >= 0)
2346 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, glyph_index->ralf, 1, pcGlyphs, "rphf");
2347 if (nextIndex > GSUB_E_NOGLYPH)
2349 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2350 shift_syllable_glyph_indexs(glyph_index,glyph_index->ralf,*pcGlyphs - prevCount);
2355 static inline INT find_halant_consonant(WCHAR* pwChars, INT index, INT end, lexical_function lexical)
2357 int i = 0;
2358 while (index + i < end-1 && !(lexical(pwChars[index+i]) == lex_Halant &&
2359 ((index + i < end-2 && lexical(pwChars[index+i]) == lex_Nukta && is_consonant(lexical(pwChars[index+i+1]))) ||
2360 is_consonant(lexical(pwChars[index+i+1])))))
2361 i++;
2362 if (index + i <= end-1)
2363 return index+i;
2364 else
2365 return -1;
2368 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)
2370 INT index, nextIndex;
2371 INT count, g_offset=0;
2372 INT ralf = syllable->ralf;
2374 count = syllable->end - syllable->base;
2376 index = find_halant_consonant(&pwChars[syllable->base], 0, count, lexical);
2378 while (index >= 0)
2380 INT prevCount = *pcGlyphs;
2381 if (ralf >=0 && ralf < index)
2383 g_offset--;
2384 ralf = -1;
2387 if (!modern)
2389 WORD g = pwOutGlyphs[index+glyph_index->base+g_offset];
2390 pwOutGlyphs[index+glyph_index->base+g_offset] = pwOutGlyphs[index+glyph_index->base+g_offset+1];
2391 pwOutGlyphs[index+glyph_index->base+g_offset+1] = g;
2394 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, index+glyph_index->base+g_offset, 1, pcGlyphs, feat);
2395 if (nextIndex > GSUB_E_NOGLYPH)
2397 UpdateClusters(nextIndex, *pcGlyphs - prevCount, 1, cChars, pwLogClust);
2398 shift_syllable_glyph_indexs(glyph_index,index+glyph_index->start+g_offset, (*pcGlyphs - prevCount));
2399 g_offset += (*pcGlyphs - prevCount);
2401 else if (!modern)
2403 WORD g = pwOutGlyphs[index+glyph_index->base+g_offset];
2404 pwOutGlyphs[index+glyph_index->base+g_offset] = pwOutGlyphs[index+glyph_index->base+g_offset+1];
2405 pwOutGlyphs[index+glyph_index->base+g_offset+1] = g;
2408 index+=2;
2409 index = find_halant_consonant(&pwChars[syllable->base], index, count, lexical);
2413 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)
2415 int c;
2416 int overall_shift = 0;
2417 const GSUB_Feature *locl = (modern)?load_GSUB_feature(hdc, psa, psc, "locl"):NULL;
2418 const GSUB_Feature *nukt = load_GSUB_feature(hdc, psa, psc, "nukt");
2419 const GSUB_Feature *akhn = load_GSUB_feature(hdc, psa, psc, "akhn");
2420 const GSUB_Feature *rkrf = (modern)?load_GSUB_feature(hdc, psa, psc, "rkrf"):NULL;
2421 const GSUB_Feature *pstf = load_GSUB_feature(hdc, psa, psc, "pstf");
2422 const GSUB_Feature *vatu = (!rkrf)?load_GSUB_feature(hdc, psa, psc, "vatu"):NULL;
2423 const GSUB_Feature *cjct = (modern)?load_GSUB_feature(hdc, psa, psc, "cjct"):NULL;
2424 BOOL rphf = (load_GSUB_feature(hdc, psa, psc, "rphf") != NULL);
2425 BOOL pref = (load_GSUB_feature(hdc, psa, psc, "pref") != NULL);
2426 BOOL blwf = (load_GSUB_feature(hdc, psa, psc, "blwf") != NULL);
2427 BOOL half = (load_GSUB_feature(hdc, psa, psc, "half") != NULL);
2428 IndicSyllable glyph_indexs;
2430 for (c = 0; c < syllable_count; c++)
2432 int old_end;
2433 memcpy(&glyph_indexs, &syllables[c], sizeof(IndicSyllable));
2434 shift_syllable_glyph_indexs(&glyph_indexs, -1, overall_shift);
2435 old_end = glyph_indexs.end;
2437 if (locl)
2439 TRACE("applying feature locl\n");
2440 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, locl);
2442 if (nukt)
2444 TRACE("applying feature nukt\n");
2445 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, nukt);
2447 if (akhn)
2449 TRACE("applying feature akhn\n");
2450 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, akhn);
2453 if (rphf)
2454 Apply_Indic_Rphf(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs);
2455 if (rkrf)
2457 TRACE("applying feature rkrf\n");
2458 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, rkrf);
2460 if (pref)
2461 Apply_Indic_PostBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, modern, "pref");
2462 if (blwf)
2464 if (!modern)
2465 Apply_Indic_PreBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, "blwf");
2467 Apply_Indic_PostBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, modern, "blwf");
2470 if (half)
2471 Apply_Indic_PreBase(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, "half");
2472 if (pstf)
2474 TRACE("applying feature pstf\n");
2475 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, pstf);
2477 if (vatu)
2479 TRACE("applying feature vatu\n");
2480 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, vatu);
2482 if (cjct)
2484 TRACE("applying feature cjct\n");
2485 Apply_Indic_BasicForm(hdc, psc, psa, pwChars, cChars, &syllables[c], pwOutGlyphs, pcGlyphs, pwLogClust, lexical, &glyph_indexs, cjct);
2488 if (second_reorder)
2489 second_reorder(pwChars, &syllables[c], pwOutGlyphs, &glyph_indexs, lexical);
2491 overall_shift += glyph_indexs.end - old_end;
2495 static inline int unicode_lex(WCHAR c)
2497 int type;
2499 if (!c) return lex_Generic;
2500 if (c == 0x200D) return lex_ZWJ;
2501 if (c == 0x200C) return lex_ZWNJ;
2502 if (c == 0x00A0) return lex_NBSP;
2504 type = get_table_entry( indic_syllabic_table, c );
2506 if ((type & 0x00ff) != 0x0007) type = type & 0x00ff;
2508 switch( type )
2510 case 0x0d07: /* Unknown */
2511 case 0x0e07: /* Unknwon */
2512 default: return lex_Generic;
2513 case 0x0001:
2514 case 0x0002:
2515 case 0x0011:
2516 case 0x0012:
2517 case 0x0013:
2518 case 0x0014: return lex_Modifier;
2519 case 0x0003:
2520 case 0x0009:
2521 case 0x000a:
2522 case 0x000b:
2523 case 0x000d:
2524 case 0x000e:
2525 case 0x000f:
2526 case 0x0010: return lex_Consonant;
2527 case 0x0004: return lex_Nukta;
2528 case 0x0005: return lex_Halant;
2529 case 0x0006:
2530 case 0x0008: return lex_Vowel;
2531 case 0x0007:
2532 case 0x0107: return lex_Matra_post;
2533 case 0x0207:
2534 case 0x0307: return lex_Matra_pre;
2535 case 0x0807:
2536 case 0x0907:
2537 case 0x0a07:
2538 case 0x0b07:
2539 case 0x0c07:
2540 case 0x0407: return lex_Composed_Vowel;
2541 case 0x0507: return lex_Matra_above;
2542 case 0x0607: return lex_Matra_below;
2543 case 0x000c: return lex_Ra;
2547 static int sinhala_lex(WCHAR c)
2549 switch (c)
2551 case 0x0DDA:
2552 case 0x0DDD:
2553 case 0x0DDC:
2554 case 0x0DDE: return lex_Matra_post;
2555 default:
2556 return unicode_lex(c);
2560 static const VowelComponents Sinhala_vowels[] = {
2561 {0x0DDA, {0x0DD9,0x0DDA,0x0}},
2562 {0x0DDC, {0x0DD9,0x0DDC,0x0}},
2563 {0x0DDD, {0x0DD9,0x0DDD,0x0}},
2564 {0x0DDE, {0x0DD9,0x0DDE,0x0}},
2565 {0x0000, {0x0000,0x0000,0x0}}};
2567 static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2569 int cCount = cChars;
2570 int i;
2571 WCHAR *input;
2572 IndicSyllable *syllables = NULL;
2573 int syllable_count = 0;
2575 if (*pcGlyphs != cChars)
2577 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2578 return;
2581 input = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR) * (cChars * 3));
2583 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2585 /* Step 1: Decompose multi part vowels */
2586 DecomposeVowels(hdc, input, &cCount, Sinhala_vowels, pwLogClust, cChars);
2588 TRACE("New double vowel expanded string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2590 /* Step 2: Reorder within Syllables */
2591 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, sinhala_lex, Reorder_Like_Sinhala, TRUE);
2592 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2594 /* Step 3: Strip dangling joiners */
2595 for (i = 0; i < cCount; i++)
2597 if ((input[i] == 0x200D || input[i] == 0x200C) &&
2598 (i == 0 || input[i-1] == 0x0020 || i == cCount-1 || input[i+1] == 0x0020))
2599 input[i] = 0x0020;
2602 /* Step 4: Base Form application to syllables */
2603 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2604 *pcGlyphs = cCount;
2605 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, sinhala_lex, NULL, TRUE);
2607 HeapFree(GetProcessHeap(),0,input);
2608 HeapFree(GetProcessHeap(),0,syllables);
2611 static int devanagari_lex(WCHAR c)
2613 switch (c)
2615 case 0x0930: return lex_Ra;
2616 default:
2617 return unicode_lex(c);
2621 static const ConsonantComponents Devanagari_consonants[] ={
2622 {{0x0928, 0x093C, 0x00000}, 0x0929},
2623 {{0x0930, 0x093C, 0x00000}, 0x0931},
2624 {{0x0933, 0x093C, 0x00000}, 0x0934},
2625 {{0x0915, 0x093C, 0x00000}, 0x0958},
2626 {{0x0916, 0x093C, 0x00000}, 0x0959},
2627 {{0x0917, 0x093C, 0x00000}, 0x095A},
2628 {{0x091C, 0x093C, 0x00000}, 0x095B},
2629 {{0x0921, 0x093C, 0x00000}, 0x095C},
2630 {{0x0922, 0x093C, 0x00000}, 0x095D},
2631 {{0x092B, 0x093C, 0x00000}, 0x095E},
2632 {{0x092F, 0x093C, 0x00000}, 0x095F}};
2634 static void ContextualShape_Devanagari(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2636 int cCount = cChars;
2637 WCHAR *input;
2638 IndicSyllable *syllables = NULL;
2639 int syllable_count = 0;
2640 BOOL modern = get_GSUB_Indic2(psa, psc);
2642 if (*pcGlyphs != cChars)
2644 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2645 return;
2648 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2649 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2651 /* Step 1: Compose Consonant and Nukta */
2652 ComposeConsonants(hdc, input, &cCount, Devanagari_consonants, pwLogClust);
2653 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2655 /* Step 2: Reorder within Syllables */
2656 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, devanagari_lex, Reorder_Like_Devanagari, modern);
2657 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2658 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2659 *pcGlyphs = cCount;
2661 /* Step 3: Base Form application to syllables */
2662 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, devanagari_lex, NULL, modern);
2664 HeapFree(GetProcessHeap(),0,input);
2665 HeapFree(GetProcessHeap(),0,syllables);
2668 static int bengali_lex(WCHAR c)
2670 switch (c)
2672 case 0x09B0: return lex_Ra;
2673 default:
2674 return unicode_lex(c);
2678 static const VowelComponents Bengali_vowels[] = {
2679 {0x09CB, {0x09C7,0x09BE,0x0000}},
2680 {0x09CC, {0x09C7,0x09D7,0x0000}},
2681 {0x0000, {0x0000,0x0000,0x0000}}};
2683 static const ConsonantComponents Bengali_consonants[] = {
2684 {{0x09A4,0x09CD,0x200D}, 0x09CE},
2685 {{0x09A1,0x09BC,0x0000}, 0x09DC},
2686 {{0x09A2,0x09BC,0x0000}, 0x09DD},
2687 {{0x09AF,0x09BC,0x0000}, 0x09DF},
2688 {{0x0000,0x0000,0x0000}, 0x0000}};
2690 static void ContextualShape_Bengali(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2692 int cCount = cChars;
2693 WCHAR *input;
2694 IndicSyllable *syllables = NULL;
2695 int syllable_count = 0;
2696 BOOL modern = get_GSUB_Indic2(psa, psc);
2698 if (*pcGlyphs != cChars)
2700 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2701 return;
2704 input = HeapAlloc(GetProcessHeap(), 0, (cChars * 2) * sizeof(WCHAR));
2705 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2707 /* Step 1: Decompose Vowels and Compose Consonents */
2708 DecomposeVowels(hdc, input, &cCount, Bengali_vowels, pwLogClust, cChars);
2709 ComposeConsonants(hdc, input, &cCount, Bengali_consonants, pwLogClust);
2710 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2712 /* Step 2: Reorder within Syllables */
2713 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, bengali_lex, Reorder_Like_Bengali, modern);
2714 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2715 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2716 *pcGlyphs = cCount;
2718 /* Step 3: Initial form is only applied to the beginning of words */
2719 for (cCount = cCount - 1 ; cCount >= 0; cCount --)
2721 if (cCount == 0 || input[cCount] == 0x0020) /* space */
2723 int index = cCount;
2724 int gCount = 1;
2725 if (index > 0) index++;
2727 apply_GSUB_feature_to_glyph(hdc, psa, psc, &pwOutGlyphs[index], 0, 1, &gCount, "init");
2731 /* Step 4: Base Form application to syllables */
2732 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, bengali_lex, NULL, modern);
2734 HeapFree(GetProcessHeap(),0,input);
2735 HeapFree(GetProcessHeap(),0,syllables);
2738 static int gurmukhi_lex(WCHAR c)
2740 if (c == 0x0A71)
2741 return lex_Modifier;
2742 else
2743 return unicode_lex(c);
2746 static const ConsonantComponents Gurmukhi_consonants[] = {
2747 {{0x0A32,0x0A3C,0x0000}, 0x0A33},
2748 {{0x0A16,0x0A3C,0x0000}, 0x0A59},
2749 {{0x0A17,0x0A3C,0x0000}, 0x0A5A},
2750 {{0x0A1C,0x0A3C,0x0000}, 0x0A5B},
2751 {{0x0A2B,0x0A3C,0x0000}, 0x0A5E},
2752 {{0x0000,0x0000,0x0000}, 0x0000}};
2754 static void ContextualShape_Gurmukhi(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2756 int cCount = cChars;
2757 WCHAR *input;
2758 IndicSyllable *syllables = NULL;
2759 int syllable_count = 0;
2760 BOOL modern = get_GSUB_Indic2(psa, psc);
2762 if (*pcGlyphs != cChars)
2764 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2765 return;
2768 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2769 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2771 /* Step 1: Compose Consonents */
2772 ComposeConsonants(hdc, input, &cCount, Gurmukhi_consonants, pwLogClust);
2773 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2775 /* Step 2: Reorder within Syllables */
2776 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, gurmukhi_lex, Reorder_Like_Bengali, modern);
2777 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2778 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2779 *pcGlyphs = cCount;
2781 /* Step 3: Base Form application to syllables */
2782 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, gurmukhi_lex, NULL, modern);
2784 HeapFree(GetProcessHeap(),0,input);
2785 HeapFree(GetProcessHeap(),0,syllables);
2788 static int gujarati_lex(WCHAR c)
2790 switch (c)
2792 case 0x0AB0: return lex_Ra;
2793 default:
2794 return unicode_lex(c);
2798 static void ContextualShape_Gujarati(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2800 int cCount = cChars;
2801 WCHAR *input;
2802 IndicSyllable *syllables = NULL;
2803 int syllable_count = 0;
2804 BOOL modern = get_GSUB_Indic2(psa, psc);
2806 if (*pcGlyphs != cChars)
2808 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2809 return;
2812 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
2813 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2815 /* Step 1: Reorder within Syllables */
2816 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, gujarati_lex, Reorder_Like_Devanagari, modern);
2817 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2818 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2819 *pcGlyphs = cCount;
2821 /* Step 2: Base Form application to syllables */
2822 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, gujarati_lex, NULL, modern);
2824 HeapFree(GetProcessHeap(),0,input);
2825 HeapFree(GetProcessHeap(),0,syllables);
2828 static int oriya_lex(WCHAR c)
2830 switch (c)
2832 case 0x0B30: return lex_Ra;
2833 default:
2834 return unicode_lex(c);
2838 static const VowelComponents Oriya_vowels[] = {
2839 {0x0B48, {0x0B47,0x0B56,0x0000}},
2840 {0x0B4B, {0x0B47,0x0B3E,0x0000}},
2841 {0x0B4C, {0x0B47,0x0B57,0x0000}},
2842 {0x0000, {0x0000,0x0000,0x0000}}};
2844 static const ConsonantComponents Oriya_consonants[] = {
2845 {{0x0B21,0x0B3C,0x0000}, 0x0B5C},
2846 {{0x0B22,0x0B3C,0x0000}, 0x0B5D},
2847 {{0x0000,0x0000,0x0000}, 0x0000}};
2849 static void ContextualShape_Oriya(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2851 int cCount = cChars;
2852 WCHAR *input;
2853 IndicSyllable *syllables = NULL;
2854 int syllable_count = 0;
2855 BOOL modern = get_GSUB_Indic2(psa, psc);
2857 if (*pcGlyphs != cChars)
2859 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2860 return;
2863 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2864 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2866 /* Step 1: Decompose Vowels and Compose Consonents */
2867 DecomposeVowels(hdc, input, &cCount, Oriya_vowels, pwLogClust, cChars);
2868 ComposeConsonants(hdc, input, &cCount, Oriya_consonants, pwLogClust);
2869 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2871 /* Step 2: Reorder within Syllables */
2872 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, oriya_lex, Reorder_Like_Bengali, modern);
2873 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2874 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2875 *pcGlyphs = cCount;
2877 /* Step 3: Base Form application to syllables */
2878 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, oriya_lex, NULL, modern);
2880 HeapFree(GetProcessHeap(),0,input);
2881 HeapFree(GetProcessHeap(),0,syllables);
2884 static int tamil_lex(WCHAR c)
2886 return unicode_lex(c);
2889 static const VowelComponents Tamil_vowels[] = {
2890 {0x0BCA, {0x0BC6,0x0BBE,0x0000}},
2891 {0x0BCB, {0x0BC7,0x0BBE,0x0000}},
2892 {0x0BCB, {0x0BC6,0x0BD7,0x0000}},
2893 {0x0000, {0x0000,0x0000,0x0000}}};
2895 static const ConsonantComponents Tamil_consonants[] = {
2896 {{0x0B92,0x0BD7,0x0000}, 0x0B94},
2897 {{0x0000,0x0000,0x0000}, 0x0000}};
2899 static void ContextualShape_Tamil(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2901 int cCount = cChars;
2902 WCHAR *input;
2903 IndicSyllable *syllables = NULL;
2904 int syllable_count = 0;
2905 BOOL modern = get_GSUB_Indic2(psa, psc);
2907 if (*pcGlyphs != cChars)
2909 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2910 return;
2913 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2914 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2916 /* Step 1: Decompose Vowels and Compose Consonents */
2917 DecomposeVowels(hdc, input, &cCount, Tamil_vowels, pwLogClust, cChars);
2918 ComposeConsonants(hdc, input, &cCount, Tamil_consonants, pwLogClust);
2919 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2921 /* Step 2: Reorder within Syllables */
2922 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, tamil_lex, Reorder_Like_Bengali, modern);
2923 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2924 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2925 *pcGlyphs = cCount;
2927 /* Step 3: Base Form application to syllables */
2928 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, tamil_lex, SecondReorder_Like_Tamil, modern);
2930 HeapFree(GetProcessHeap(),0,input);
2931 HeapFree(GetProcessHeap(),0,syllables);
2934 static int telugu_lex(WCHAR c)
2936 switch (c)
2938 case 0x0C43:
2939 case 0x0C44: return lex_Modifier;
2940 default:
2941 return unicode_lex(c);
2945 static const VowelComponents Telugu_vowels[] = {
2946 {0x0C48, {0x0C46,0x0C56,0x0000}},
2947 {0x0000, {0x0000,0x0000,0x0000}}};
2949 static void ContextualShape_Telugu(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
2951 int cCount = cChars;
2952 WCHAR *input;
2953 IndicSyllable *syllables = NULL;
2954 int syllable_count = 0;
2955 BOOL modern = get_GSUB_Indic2(psa, psc);
2957 if (*pcGlyphs != cChars)
2959 ERR("Number of Glyphs and Chars need to match at the beginning\n");
2960 return;
2963 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
2964 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
2966 /* Step 1: Decompose Vowels */
2967 DecomposeVowels(hdc, input, &cCount, Telugu_vowels, pwLogClust, cChars);
2968 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
2970 /* Step 2: Reorder within Syllables */
2971 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, telugu_lex, Reorder_Like_Bengali, modern);
2972 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
2973 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
2974 *pcGlyphs = cCount;
2976 /* Step 3: Base Form application to syllables */
2977 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, telugu_lex, SecondReorder_Like_Telugu, modern);
2979 HeapFree(GetProcessHeap(),0,input);
2980 HeapFree(GetProcessHeap(),0,syllables);
2983 static int kannada_lex(WCHAR c)
2985 switch (c)
2987 case 0x0CB0: return lex_Ra;
2988 default:
2989 return unicode_lex(c);
2993 static const VowelComponents Kannada_vowels[] = {
2994 {0x0CC0, {0x0CBF,0x0CD5,0x0000}},
2995 {0x0CC7, {0x0CC6,0x0CD5,0x0000}},
2996 {0x0CC8, {0x0CC6,0x0CD6,0x0000}},
2997 {0x0CCA, {0x0CC6,0x0CC2,0x0000}},
2998 {0x0CCB, {0x0CC6,0x0CC2,0x0CD5}},
2999 {0x0000, {0x0000,0x0000,0x0000}}};
3001 static void ContextualShape_Kannada(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3003 int cCount = cChars;
3004 WCHAR *input;
3005 IndicSyllable *syllables = NULL;
3006 int syllable_count = 0;
3007 BOOL modern = get_GSUB_Indic2(psa, psc);
3009 if (*pcGlyphs != cChars)
3011 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3012 return;
3015 input = HeapAlloc(GetProcessHeap(), 0, (cChars*3) * sizeof(WCHAR));
3016 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
3018 /* Step 1: Decompose Vowels */
3019 DecomposeVowels(hdc, input, &cCount, Kannada_vowels, pwLogClust, cChars);
3020 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
3022 /* Step 2: Reorder within Syllables */
3023 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, kannada_lex, Reorder_Like_Kannada, modern);
3024 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
3025 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
3026 *pcGlyphs = cCount;
3028 /* Step 3: Base Form application to syllables */
3029 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, kannada_lex, SecondReorder_Like_Telugu, modern);
3031 HeapFree(GetProcessHeap(),0,input);
3032 HeapFree(GetProcessHeap(),0,syllables);
3035 static int malayalam_lex(WCHAR c)
3037 return unicode_lex(c);
3040 static const VowelComponents Malayalam_vowels[] = {
3041 {0x0D4A, {0x0D46,0x0D3E,0x0000}},
3042 {0x0D4B, {0x0D47,0x0D3E,0x0000}},
3043 {0x0D4C, {0x0D46,0x0D57,0x0000}},
3044 {0x0000, {0x0000,0x0000,0x0000}}};
3046 static void ContextualShape_Malayalam(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3048 int cCount = cChars;
3049 WCHAR *input;
3050 IndicSyllable *syllables = NULL;
3051 int syllable_count = 0;
3052 BOOL modern = get_GSUB_Indic2(psa, psc);
3054 if (*pcGlyphs != cChars)
3056 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3057 return;
3060 input = HeapAlloc(GetProcessHeap(), 0, (cChars*2) * sizeof(WCHAR));
3061 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
3063 /* Step 1: Decompose Vowels */
3064 DecomposeVowels(hdc, input, &cCount, Malayalam_vowels, pwLogClust, cChars);
3065 TRACE("New composed string %s (%i)\n",debugstr_wn(input,cCount),cCount);
3067 /* Step 2: Reorder within Syllables */
3068 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, malayalam_lex, Reorder_Like_Devanagari, modern);
3069 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
3070 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
3071 *pcGlyphs = cCount;
3073 /* Step 3: Base Form application to syllables */
3074 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, malayalam_lex, SecondReorder_Like_Tamil, modern);
3076 HeapFree(GetProcessHeap(),0,input);
3077 HeapFree(GetProcessHeap(),0,syllables);
3080 static int khmer_lex(WCHAR c)
3082 return unicode_lex(c);
3085 static void ContextualShape_Khmer(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3087 int cCount = cChars;
3088 WCHAR *input;
3089 IndicSyllable *syllables = NULL;
3090 int syllable_count = 0;
3092 if (*pcGlyphs != cChars)
3094 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3095 return;
3098 input = HeapAlloc(GetProcessHeap(), 0, cChars * sizeof(WCHAR));
3099 memcpy(input, pwcChars, cChars * sizeof(WCHAR));
3101 /* Step 1: Reorder within Syllables */
3102 Indic_ReorderCharacters( hdc, psa, psc, input, cCount, &syllables, &syllable_count, khmer_lex, Reorder_Like_Devanagari, FALSE);
3103 TRACE("reordered string %s\n",debugstr_wn(input,cCount));
3104 GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
3105 *pcGlyphs = cCount;
3107 /* Step 2: Base Form application to syllables */
3108 ShapeIndicSyllables(hdc, psc, psa, input, cChars, syllables, syllable_count, pwOutGlyphs, pcGlyphs, pwLogClust, khmer_lex, NULL, FALSE);
3110 HeapFree(GetProcessHeap(),0,input);
3111 HeapFree(GetProcessHeap(),0,syllables);
3114 static inline BOOL mongolian_wordbreak(WCHAR chr)
3116 return ((chr == 0x0020) || (chr == 0x200C) || (chr == 0x202F) || (chr == 0x180E) || (chr == 0x1800) || (chr == 0x1802) || (chr == 0x1803) || (chr == 0x1805) || (chr == 0x1808) || (chr == 0x1809) || (chr == 0x1807));
3119 static void ContextualShape_Mongolian(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3121 INT *context_shape;
3122 INT dirL;
3123 int i;
3125 if (*pcGlyphs != cChars)
3127 ERR("Number of Glyphs and Chars need to match at the beginning\n");
3128 return;
3131 if (!psa->fLogicalOrder && psa->fRTL)
3132 dirL = -1;
3133 else
3134 dirL = 1;
3136 if (!psc->GSUB_Table)
3137 psc->GSUB_Table = load_gsub_table(hdc);
3139 if (!psc->GSUB_Table)
3140 return;
3142 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
3144 for (i = 0; i < cChars; i++)
3146 if (i == 0 || mongolian_wordbreak(pwcChars[i-1]))
3148 if ((i == cChars-1) || mongolian_wordbreak(pwcChars[i+1]))
3149 context_shape[i] = Xn;
3150 else
3151 context_shape[i] = Xl;
3153 else
3155 if ((i == cChars-1) || mongolian_wordbreak(pwcChars[i+1]))
3156 context_shape[i] = Xr;
3157 else
3158 context_shape[i] = Xm;
3162 /* Contextual Shaping */
3163 i = 0;
3164 while(i < *pcGlyphs)
3166 INT nextIndex;
3167 INT prevCount = *pcGlyphs;
3168 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
3169 if (nextIndex > GSUB_E_NOGLYPH)
3171 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
3172 i = nextIndex;
3174 else
3175 i++;
3178 HeapFree(GetProcessHeap(),0,context_shape);
3181 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)
3183 int i,k;
3185 for (i = 0; i < cGlyphs; i++)
3187 int char_index[20];
3188 int char_count = 0;
3190 for (k = 0; k < cChars; k++)
3192 if (pwLogClust[k] == i)
3194 char_index[char_count] = k;
3195 char_count++;
3199 if (char_count == 0)
3200 continue;
3202 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3204 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3205 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3207 else
3208 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3211 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3212 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3215 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 )
3217 int i,k;
3218 int initGlyph, finaGlyph;
3219 INT dirR, dirL;
3220 BYTE *spaces;
3222 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
3223 memset(spaces,0,cGlyphs);
3225 if (!psa->fLogicalOrder && psa->fRTL)
3227 initGlyph = cGlyphs-1;
3228 finaGlyph = 0;
3229 dirR = 1;
3230 dirL = -1;
3232 else
3234 initGlyph = 0;
3235 finaGlyph = cGlyphs-1;
3236 dirR = -1;
3237 dirL = 1;
3240 for (i = 0; i < cGlyphs; i++)
3242 for (k = 0; k < cChars; k++)
3243 if (pwLogClust[k] == i)
3245 if (pwcChars[k] == 0x0020)
3246 spaces[i] = 1;
3250 for (i = 0; i < cGlyphs; i++)
3252 int char_index[20];
3253 int char_count = 0;
3254 BOOL isInit, isFinal;
3256 for (k = 0; k < cChars; k++)
3258 if (pwLogClust[k] == i)
3260 char_index[char_count] = k;
3261 char_count++;
3265 isInit = (i == initGlyph || (i+dirR > 0 && i+dirR < cGlyphs && spaces[i+dirR]));
3266 isFinal = (i == finaGlyph || (i+dirL > 0 && i+dirL < cGlyphs && spaces[i+dirL]));
3268 if (char_count == 0)
3269 continue;
3271 if (char_count == 1)
3273 if (pwcChars[char_index[0]] == 0x0020) /* space */
3275 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BLANK;
3276 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3278 else if (pwcChars[char_index[0]] == 0x0640) /* kashida */
3279 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
3280 else if (pwcChars[char_index[0]] == 0x0633) /* SEEN */
3282 if (!isInit && !isFinal)
3283 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN_M;
3284 else if (isInit)
3285 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN;
3286 else
3287 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3289 else if (!isInit)
3291 if (pwcChars[char_index[0]] == 0x0628 ) /* BA */
3292 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BA;
3293 else if (pwcChars[char_index[0]] == 0x0631 ) /* RA */
3294 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_RA;
3295 else if (pwcChars[char_index[0]] == 0x0647 ) /* HA */
3296 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_HA;
3297 else if ((pwcChars[char_index[0]] == 0x0627 || pwcChars[char_index[0]] == 0x0625 || pwcChars[char_index[0]] == 0x0623 || pwcChars[char_index[0]] == 0x0622) ) /* alef-like */
3298 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_ALEF;
3299 else
3300 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3302 else if (!isInit && !isFinal)
3303 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3304 else
3305 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3307 else if (char_count == 2)
3309 if ((pwcChars[char_index[0]] == 0x0628 && pwcChars[char_index[1]]== 0x0631) || (pwcChars[char_index[0]] == 0x0631 && pwcChars[char_index[1]]== 0x0628)) /* BA+RA */
3310 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BARA;
3311 else if (!isInit)
3312 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3313 else
3314 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3316 else if (!isInit && !isFinal)
3317 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
3318 else
3319 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3322 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3323 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3324 HeapFree(GetProcessHeap(),0,spaces);
3327 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 )
3329 int i,k;
3330 int finaGlyph;
3331 INT dirL;
3332 BYTE *spaces;
3334 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
3335 memset(spaces,0,cGlyphs);
3337 if (!psa->fLogicalOrder && psa->fRTL)
3339 finaGlyph = 0;
3340 dirL = -1;
3342 else
3344 finaGlyph = cGlyphs-1;
3345 dirL = 1;
3348 for (i = 0; i < cGlyphs; i++)
3350 for (k = 0; k < cChars; k++)
3351 if (pwLogClust[k] == i)
3353 if (pwcChars[k] == 0x0020)
3354 spaces[i] = 1;
3358 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3360 for (i = 0; i < cGlyphs; i++)
3362 int char_index[20];
3363 int char_count = 0;
3365 for (k = 0; k < cChars; k++)
3367 if (pwLogClust[k] == i)
3369 char_index[char_count] = k;
3370 char_count++;
3374 if (char_count == 0)
3375 continue;
3377 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3379 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3380 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3382 else if (i == finaGlyph)
3383 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3384 else
3385 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3387 /* handle Thai SARA AM (U+0E33) differently than GDEF */
3388 if (char_count == 1 && pwcChars[char_index[0]] == 0x0e33)
3389 pGlyphProp[i].sva.fClusterStart = 0;
3392 HeapFree(GetProcessHeap(),0,spaces);
3393 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3395 /* Do not allow justification between marks and their base */
3396 for (i = 0; i < cGlyphs; i++)
3398 if (!pGlyphProp[i].sva.fClusterStart)
3399 pGlyphProp[i-dirL].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3403 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)
3405 int i,k;
3407 for (i = 0; i < cGlyphs; i++)
3409 int char_index[20];
3410 int char_count = 0;
3412 for (k = 0; k < cChars; k++)
3414 if (pwLogClust[k] == i)
3416 char_index[char_count] = k;
3417 char_count++;
3421 if (char_count == 0)
3422 continue;
3424 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3426 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
3427 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3429 else
3430 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3432 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3433 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3436 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)
3438 int i,k;
3440 for (i = 0; i < cGlyphs; i++)
3442 int char_index[20];
3443 int char_count = 0;
3445 for (k = 0; k < cChars; k++)
3447 if (pwLogClust[k] == i)
3449 char_index[char_count] = k;
3450 char_count++;
3454 if (char_count == 0)
3455 continue;
3457 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3459 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3460 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3462 else
3463 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3465 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3466 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3468 /* Tibeten script does not set sva.fDiacritic or sva.fZeroWidth */
3469 for (i = 0; i < cGlyphs; i++)
3471 if (!pGlyphProp[i].sva.fClusterStart)
3473 pGlyphProp[i].sva.fDiacritic = 0;
3474 pGlyphProp[i].sva.fZeroWidth = 0;
3479 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)
3481 int i,k;
3483 GDEF_UpdateGlyphProps(hdc, psc, pwGlyphs, cGlyphs, pwLogClust, cChars, pGlyphProp);
3484 for (i = 0; i < cGlyphs; i++)
3486 int char_index[20];
3487 int char_count = 0;
3489 for (k = 0; k < cChars; k++)
3491 if (pwLogClust[k] == i)
3493 char_index[char_count] = k;
3494 char_count++;
3498 if (override_gsub)
3500 /* Most indic scripts do not set fDiacritic or fZeroWidth */
3501 pGlyphProp[i].sva.fDiacritic = FALSE;
3502 pGlyphProp[i].sva.fZeroWidth = FALSE;
3505 if (char_count == 0)
3506 continue;
3508 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
3510 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
3511 pCharProp[char_index[0]].fCanGlyphAlone = 1;
3513 else
3514 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
3516 pGlyphProp[i].sva.fClusterStart = 0;
3517 for (k = 0; k < char_count && !pGlyphProp[i].sva.fClusterStart; k++)
3518 switch (lexical(pwcChars[char_index[k]]))
3520 case lex_Matra_pre:
3521 case lex_Matra_post:
3522 case lex_Matra_above:
3523 case lex_Matra_below:
3524 case lex_Modifier:
3525 case lex_Halant:
3526 break;
3527 case lex_ZWJ:
3528 case lex_ZWNJ:
3529 /* check for dangling joiners */
3530 if (pwcChars[char_index[k]-1] == 0x0020 || pwcChars[char_index[k]+1] == 0x0020)
3531 pGlyphProp[i].sva.fClusterStart = 1;
3532 else
3533 k = char_count;
3534 break;
3535 default:
3536 pGlyphProp[i].sva.fClusterStart = 1;
3537 break;
3541 if (use_syllables)
3543 IndicSyllable *syllables = NULL;
3544 int syllable_count = 0;
3545 BOOL modern = get_GSUB_Indic2(psa, psc);
3547 Indic_ParseSyllables( hdc, psa, psc, pwcChars, cChars, &syllables, &syllable_count, lexical, modern);
3549 for (i = 0; i < syllable_count; i++)
3551 int j;
3552 WORD g = pwLogClust[syllables[i].start];
3553 for (j = syllables[i].start+1; j <= syllables[i].end; j++)
3555 if (pwLogClust[j] != g)
3557 pGlyphProp[pwLogClust[j]].sva.fClusterStart = 0;
3558 pwLogClust[j] = g;
3563 HeapFree(GetProcessHeap(), 0, syllables);
3566 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
3569 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 )
3571 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, sinhala_lex, FALSE, FALSE);
3574 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 )
3576 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, devanagari_lex, TRUE, TRUE);
3579 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 )
3581 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, bengali_lex, TRUE, TRUE);
3584 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 )
3586 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, gurmukhi_lex, TRUE, TRUE);
3589 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 )
3591 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, gujarati_lex, TRUE, TRUE);
3594 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 )
3596 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, oriya_lex, TRUE, TRUE);
3599 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 )
3601 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, tamil_lex, TRUE, TRUE);
3604 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 )
3606 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, telugu_lex, TRUE, TRUE);
3609 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 )
3611 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, kannada_lex, TRUE, TRUE);
3614 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 )
3616 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, malayalam_lex, TRUE, TRUE);
3619 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 )
3621 ShapeCharGlyphProp_BaseIndic(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp, khmer_lex, TRUE, TRUE);
3624 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)
3626 if (ShapingData[psa->eScript].charGlyphPropProc)
3627 ShapingData[psa->eScript].charGlyphPropProc(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
3628 else
3629 ShapeCharGlyphProp_Default(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
3632 void SHAPE_ContextualShaping(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
3634 if (!psc->GSUB_Table)
3635 psc->GSUB_Table = load_gsub_table(hdc);
3637 if (ShapingData[psa->eScript].contextProc)
3638 ShapingData[psa->eScript].contextProc(hdc, psc, psa, pwcChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3641 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)
3643 int i;
3644 INT dirL;
3646 if (!rpRangeProperties)
3647 return;
3649 if (!psc->GSUB_Table)
3650 psc->GSUB_Table = load_gsub_table(hdc);
3652 if (!psc->GSUB_Table)
3653 return;
3655 if (!psa->fLogicalOrder && psa->fRTL)
3656 dirL = -1;
3657 else
3658 dirL = 1;
3660 for (i = 0; i < rpRangeProperties->cotfRecords; i++)
3662 if (rpRangeProperties->potfRecords[i].lParameter > 0)
3663 apply_GSUB_feature(hdc, psa, psc, pwOutGlyphs, dirL, pcGlyphs, cChars, (const char*)&rpRangeProperties->potfRecords[i].tagFeature, pwLogClust);
3667 void SHAPE_ApplyDefaultOpentypeFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, INT cChars, WORD *pwLogClust)
3669 const TEXTRANGE_PROPERTIES *rpRangeProperties;
3670 rpRangeProperties = &ShapingData[psa->eScript].defaultTextRange;
3672 SHAPE_ApplyOpenTypeFeatures(hdc, psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, rpRangeProperties, pwLogClust);
3675 HRESULT SHAPE_CheckFontForRequiredFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa)
3677 const GSUB_Feature *feature;
3678 int i;
3680 if (!ShapingData[psa->eScript].requiredFeatures)
3681 return S_OK;
3683 if (!psc->GSUB_Table)
3684 psc->GSUB_Table = load_gsub_table(hdc);
3686 /* we need to have at least one of the required features */
3687 i = 0;
3688 while (ShapingData[psa->eScript].requiredFeatures[i])
3690 feature = load_GSUB_feature(hdc, psa, psc, ShapingData[psa->eScript].requiredFeatures[i]);
3691 if (feature)
3692 return S_OK;
3693 i++;
3696 return USP_E_SCRIPT_NOT_IN_FONT;
3699 static VOID *load_CMAP_format12_table(HDC hdc, ScriptCache *psc)
3701 CMAP_Header *CMAP_Table = NULL;
3702 int length;
3703 int i;
3705 if (!psc->CMAP_Table)
3707 length = GetFontData(hdc, CMAP_TAG , 0, NULL, 0);
3708 if (length != GDI_ERROR)
3710 psc->CMAP_Table = HeapAlloc(GetProcessHeap(),0,length);
3711 GetFontData(hdc, CMAP_TAG , 0, psc->CMAP_Table, length);
3712 TRACE("Loaded cmap table of %i bytes\n",length);
3714 else
3715 return NULL;
3718 CMAP_Table = psc->CMAP_Table;
3720 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++)
3722 if ( (GET_BE_WORD(CMAP_Table->tables[i].platformID) == 3) &&
3723 (GET_BE_WORD(CMAP_Table->tables[i].encodingID) == 10) )
3725 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
3726 if (GET_BE_WORD(format->format) == 12)
3727 return format;
3730 return NULL;
3733 static int compare_group(const void *a, const void* b)
3735 const DWORD *chr = a;
3736 const CMAP_SegmentedCoverage_group *group = b;
3738 if (*chr < GET_BE_DWORD(group->startCharCode))
3739 return -1;
3740 if (*chr > GET_BE_DWORD(group->endCharCode))
3741 return 1;
3742 return 0;
3745 DWORD CMAP_GetGlyphIndex(HDC hdc, ScriptCache *psc, DWORD utf32c, LPWORD pgi, DWORD flags)
3747 /* BMP: use gdi32 for ease */
3748 if (utf32c < 0x10000)
3750 WCHAR ch = utf32c;
3751 return GetGlyphIndicesW(hdc,&ch, 1, pgi, flags);
3754 if (!psc->CMAP_format12_Table)
3755 psc->CMAP_format12_Table = load_CMAP_format12_table(hdc, psc);
3757 if (flags & GGI_MARK_NONEXISTING_GLYPHS)
3758 *pgi = 0xffff;
3759 else
3760 *pgi = 0;
3762 if (psc->CMAP_format12_Table)
3764 CMAP_SegmentedCoverage *format = NULL;
3765 CMAP_SegmentedCoverage_group *group = NULL;
3767 format = (CMAP_SegmentedCoverage *)psc->CMAP_format12_Table;
3769 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups),
3770 sizeof(CMAP_SegmentedCoverage_group), compare_group);
3772 if (group)
3774 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode);
3775 *pgi = GET_BE_DWORD(group->startGlyphID) + offset;
3776 return 0;
3779 return 0;
3782 static void GSUB_initialize_script_cache(ScriptCache *psc)
3784 int i;
3786 if (!psc->script_count)
3788 const GSUB_ScriptList *script;
3789 const GSUB_Header* header = (const GSUB_Header*)psc->GSUB_Table;
3790 script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
3791 psc->script_count = GET_BE_WORD(script->ScriptCount);
3792 TRACE("initializing %i scripts in this font\n",psc->script_count);
3793 psc->scripts = HeapAlloc(GetProcessHeap(),0,sizeof(LoadedScript) * psc->script_count);
3794 for (i = 0; i < psc->script_count; i++)
3796 int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
3797 psc->scripts[i].tag = MS_MAKE_TAG(script->ScriptRecord[i].ScriptTag[0], script->ScriptRecord[i].ScriptTag[1], script->ScriptRecord[i].ScriptTag[2], script->ScriptRecord[i].ScriptTag[3]);
3798 psc->scripts[i].table = ((const BYTE*)script + offset);
3803 static HRESULT GSUB_GetFontScriptTags(ScriptCache *psc, OPENTYPE_TAG searchingFor, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags, LPCVOID* script_table)
3805 int i;
3806 HRESULT rc = S_OK;
3808 GSUB_initialize_script_cache(psc);
3809 *pcTags = psc->script_count;
3811 if (!searchingFor && cMaxTags < *pcTags)
3812 rc = E_OUTOFMEMORY;
3813 else if (searchingFor)
3814 rc = USP_E_SCRIPT_NOT_IN_FONT;
3816 for (i = 0; i < psc->script_count; i++)
3818 if (i < cMaxTags)
3819 pScriptTags[i] = psc->scripts[i].tag;
3821 if (searchingFor)
3823 if (searchingFor == psc->scripts[i].tag)
3825 pScriptTags[0] = psc->scripts[i].tag;
3826 *pcTags = 1;
3827 if (script_table)
3828 *script_table = psc->scripts[i].table;
3829 rc = S_OK;
3830 break;
3834 return rc;
3837 HRESULT SHAPE_GetFontScriptTags( HDC hdc, ScriptCache *psc,
3838 SCRIPT_ANALYSIS *psa, int cMaxTags,
3839 OPENTYPE_TAG *pScriptTags, int *pcTags)
3841 HRESULT hr;
3842 OPENTYPE_TAG searching = 0x00000000;
3844 if (!psc->GSUB_Table)
3845 psc->GSUB_Table = load_gsub_table(hdc);
3847 if (psa)
3849 if (ShapingData[psa->eScript].otTag[0] != 0)
3850 searching = MS_MAKE_TAG(ShapingData[psa->eScript].otTag[0], ShapingData[psa->eScript].otTag[1], ShapingData[psa->eScript].otTag[2], ShapingData[psa->eScript].otTag[3]);
3853 hr = GSUB_GetFontScriptTags(psc, searching, cMaxTags, pScriptTags, pcTags, NULL);
3854 if (FAILED(hr))
3855 *pcTags = 0;
3856 return hr;