usp10: Apply the GSUB 'rlig' feature for Required ligature substitution.
[wine.git] / dlls / usp10 / shape.c
blobd7e9811717d6eae11fa10f53a65c2ec91b912d70
1 /*
2 * Implementation of Shaping for the Uniscribe Script Processor (usp10.dll)
4 * Copyright 2010 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "usp10.h"
29 #include "winternl.h"
31 #include "usp10_internal.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
37 #define FIRST_ARABIC_CHAR 0x0600
38 #define LAST_ARABIC_CHAR 0x06ff
40 extern const unsigned short wine_shaping_table[];
41 extern const unsigned short wine_shaping_forms[LAST_ARABIC_CHAR - FIRST_ARABIC_CHAR + 1][4];
43 enum joining_types {
44 jtU,
45 jtT,
46 jtR,
47 jtL,
48 jtD,
49 jtC
52 enum joined_forms {
53 Xn=0,
54 Xr,
55 Xl,
59 #ifdef WORDS_BIGENDIAN
60 #define GET_BE_WORD(x) (x)
61 #else
62 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
63 #endif
65 /* These are all structures needed for the GSUB table */
66 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
67 ( ( (ULONG)_x4 << 24 ) | \
68 ( (ULONG)_x3 << 16 ) | \
69 ( (ULONG)_x2 << 8 ) | \
70 (ULONG)_x1 )
72 #define GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B')
73 #define GSUB_E_NOFEATURE -2
74 #define GSUB_E_NOGLYPH -1
76 typedef struct {
77 DWORD version;
78 WORD ScriptList;
79 WORD FeatureList;
80 WORD LookupList;
81 } GSUB_Header;
83 typedef struct {
84 CHAR ScriptTag[4];
85 WORD Script;
86 } GSUB_ScriptRecord;
88 typedef struct {
89 WORD ScriptCount;
90 GSUB_ScriptRecord ScriptRecord[1];
91 } GSUB_ScriptList;
93 typedef struct {
94 CHAR LangSysTag[4];
95 WORD LangSys;
96 } GSUB_LangSysRecord;
98 typedef struct {
99 WORD DefaultLangSys;
100 WORD LangSysCount;
101 GSUB_LangSysRecord LangSysRecord[1];
102 } GSUB_Script;
104 typedef struct {
105 WORD LookupOrder; /* Reserved */
106 WORD ReqFeatureIndex;
107 WORD FeatureCount;
108 WORD FeatureIndex[1];
109 } GSUB_LangSys;
111 typedef struct {
112 CHAR FeatureTag[4];
113 WORD Feature;
114 } GSUB_FeatureRecord;
116 typedef struct {
117 WORD FeatureCount;
118 GSUB_FeatureRecord FeatureRecord[1];
119 } GSUB_FeatureList;
121 typedef struct {
122 WORD FeatureParams; /* Reserved */
123 WORD LookupCount;
124 WORD LookupListIndex[1];
125 } GSUB_Feature;
127 typedef struct {
128 WORD LookupCount;
129 WORD Lookup[1];
130 } GSUB_LookupList;
132 typedef struct {
133 WORD LookupType;
134 WORD LookupFlag;
135 WORD SubTableCount;
136 WORD SubTable[1];
137 } GSUB_LookupTable;
139 typedef struct {
140 WORD CoverageFormat;
141 WORD GlyphCount;
142 WORD GlyphArray[1];
143 } GSUB_CoverageFormat1;
145 typedef struct {
146 WORD Start;
147 WORD End;
148 WORD StartCoverageIndex;
149 } GSUB_RangeRecord;
151 typedef struct {
152 WORD CoverageFormat;
153 WORD RangeCount;
154 GSUB_RangeRecord RangeRecord[1];
155 } GSUB_CoverageFormat2;
157 typedef struct {
158 WORD SubstFormat; /* = 1 */
159 WORD Coverage;
160 WORD DeltaGlyphID;
161 } GSUB_SingleSubstFormat1;
163 typedef struct {
164 WORD SubstFormat; /* = 2 */
165 WORD Coverage;
166 WORD GlyphCount;
167 WORD Substitute[1];
168 }GSUB_SingleSubstFormat2;
170 typedef struct {
171 WORD SubstFormat; /* = 1 */
172 WORD Coverage;
173 WORD LigSetCount;
174 WORD LigatureSet[1];
175 }GSUB_LigatureSubstFormat1;
177 typedef struct {
178 WORD LigatureCount;
179 WORD Ligature[1];
180 }GSUB_LigatureSet;
182 typedef struct{
183 WORD LigGlyph;
184 WORD CompCount;
185 WORD Component[1];
186 }GSUB_Ligature;
188 /* the orders of joined_forms and contextual_features need to line up */
189 static const char* contextual_features[] =
191 "isol",
192 "fina",
193 "init",
194 "medi"
197 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
199 const GSUB_CoverageFormat1* cf1;
201 cf1 = table;
203 if (GET_BE_WORD(cf1->CoverageFormat) == 1)
205 int count = GET_BE_WORD(cf1->GlyphCount);
206 int i;
207 TRACE("Coverage Format 1, %i glyphs\n",count);
208 for (i = 0; i < count; i++)
209 if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
210 return i;
211 return -1;
213 else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
215 const GSUB_CoverageFormat2* cf2;
216 int i;
217 int count;
218 cf2 = (const GSUB_CoverageFormat2*)cf1;
220 count = GET_BE_WORD(cf2->RangeCount);
221 TRACE("Coverage Format 2, %i ranges\n",count);
222 for (i = 0; i < count; i++)
224 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
225 return -1;
226 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
227 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
229 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
230 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
233 return -1;
235 else
236 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
238 return -1;
241 static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
243 const GSUB_ScriptList *script;
244 const GSUB_Script *deflt = NULL;
245 int i;
246 script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
248 TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
249 for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
251 const GSUB_Script *scr;
252 int offset;
254 offset = GET_BE_WORD(script->ScriptRecord[i].Script);
255 scr = (const GSUB_Script*)((const BYTE*)script + offset);
257 if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
258 return scr;
259 if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
260 deflt = scr;
262 return deflt;
265 static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
267 int i;
268 int offset;
269 const GSUB_LangSys *Lang;
271 TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
273 for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
275 offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
276 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
278 if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
279 return Lang;
281 offset = GET_BE_WORD(script->DefaultLangSys);
282 if (offset)
284 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
285 return Lang;
287 return NULL;
290 static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
292 int i;
293 const GSUB_FeatureList *feature;
294 feature = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
296 TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
297 for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
299 int index = GET_BE_WORD(lang->FeatureIndex[i]);
300 if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
302 const GSUB_Feature *feat;
303 feat = (const GSUB_Feature*)((const BYTE*)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
304 return feat;
307 return NULL;
310 static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
312 int j;
313 TRACE("Single Substitution Subtable\n");
315 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
317 int offset;
318 const GSUB_SingleSubstFormat1 *ssf1;
319 offset = GET_BE_WORD(look->SubTable[j]);
320 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
321 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
323 int offset = GET_BE_WORD(ssf1->Coverage);
324 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
325 if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
327 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
328 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
329 TRACE(" 0x%x\n",glyphs[glyph_index]);
330 return glyph_index + 1;
333 else
335 const GSUB_SingleSubstFormat2 *ssf2;
336 INT index;
337 INT offset;
339 ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
340 offset = GET_BE_WORD(ssf1->Coverage);
341 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
342 index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
343 TRACE(" Coverage index %i\n",index);
344 if (index != -1)
346 TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]);
347 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
348 TRACE("0x%x\n",glyphs[glyph_index]);
349 return glyph_index + 1;
353 return GSUB_E_NOGLYPH;
356 static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
358 int j;
360 TRACE("Ligature Substitution Subtable\n");
361 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
363 const GSUB_LigatureSubstFormat1 *lsf1;
364 int offset,index;
366 offset = GET_BE_WORD(look->SubTable[j]);
367 lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
368 offset = GET_BE_WORD(lsf1->Coverage);
369 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
370 TRACE(" Coverage index %i\n",index);
371 if (index != -1)
373 const GSUB_LigatureSet *ls;
374 int k, count;
376 offset = GET_BE_WORD(lsf1->LigatureSet[index]);
377 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
378 count = GET_BE_WORD(ls->LigatureCount);
379 TRACE(" LigatureSet has %i members\n",count);
380 for (k = 0; k < count; k++)
382 const GSUB_Ligature *lig;
383 int CompCount,l,CompIndex;
385 offset = GET_BE_WORD(ls->Ligature[k]);
386 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
387 CompCount = GET_BE_WORD(lig->CompCount) - 1;
388 CompIndex = glyph_index+write_dir;
389 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
391 int CompGlyph;
392 CompGlyph = GET_BE_WORD(lig->Component[l]);
393 if (CompGlyph != glyphs[CompIndex])
394 break;
395 CompIndex += write_dir;
397 if (l == CompCount)
399 int replaceIdx = glyph_index;
400 if (write_dir < 0)
401 replaceIdx = glyph_index - CompCount;
403 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
404 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
405 TRACE("0x%x\n",glyphs[replaceIdx]);
406 if (CompCount > 0)
408 int j;
409 for (j = replaceIdx + 1; j < *glyph_count; j++)
410 glyphs[j] =glyphs[j+CompCount];
411 *glyph_count = *glyph_count - CompCount;
413 return replaceIdx + 1;
418 return GSUB_E_NOGLYPH;
421 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
423 int offset;
424 const GSUB_LookupTable *look;
426 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
427 look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
428 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
429 switch(GET_BE_WORD(look->LookupType))
431 case 1:
432 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
433 case 4:
434 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
435 default:
436 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
438 return GSUB_E_NOGLYPH;
441 static INT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
443 int i;
444 int out_index = GSUB_E_NOGLYPH;
445 const GSUB_LookupList *lookup;
447 lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
449 TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
450 for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
452 out_index = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[i]), glyphs, glyph_index, write_dir, glyph_count);
453 if (out_index != GSUB_E_NOGLYPH)
454 break;
456 if (out_index == GSUB_E_NOGLYPH)
457 TRACE("lookups found no glyphs\n");
458 return out_index;
461 static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa)
463 UINT charset;
465 switch (psa->eScript)
467 case Script_Arabic:
468 return "arab";
469 case Script_Syriac:
470 return "syrc";
471 case Script_Hebrew:
472 return "hebr";
473 case Script_Latin:
474 case Script_Numeric:
475 case Script_CR:
476 case Script_LF:
477 return "latn";
481 * fall back to the font charset
483 charset = GetTextCharsetInfo(hdc, NULL, 0x0);
484 switch (charset)
486 case ANSI_CHARSET: return "latn";
487 case BALTIC_CHARSET: return "latn"; /* ?? */
488 case CHINESEBIG5_CHARSET: return "hani";
489 case EASTEUROPE_CHARSET: return "latn"; /* ?? */
490 case GB2312_CHARSET: return "hani";
491 case GREEK_CHARSET: return "grek";
492 case HANGUL_CHARSET: return "hang";
493 case RUSSIAN_CHARSET: return "cyrl";
494 case SHIFTJIS_CHARSET: return "kana";
495 case TURKISH_CHARSET: return "latn"; /* ?? */
496 case VIETNAMESE_CHARSET: return "latn";
497 case JOHAB_CHARSET: return "latn"; /* ?? */
498 case ARABIC_CHARSET: return "arab";
499 case HEBREW_CHARSET: return "hebr";
500 case THAI_CHARSET: return "thai";
501 default: return "latn";
505 static INT apply_GSUB_feature_to_glyph(HDC hdc, SCRIPT_ANALYSIS *psa, void* GSUB_Table, WORD *glyphs, INT index, INT write_dir, INT* glyph_count, const char* feat)
507 const GSUB_Header *header;
508 const GSUB_Script *script;
509 const GSUB_LangSys *language;
510 const GSUB_Feature *feature;
512 if (!GSUB_Table)
513 return GSUB_E_NOFEATURE;
515 header = GSUB_Table;
517 script = GSUB_get_script_table(header, get_opentype_script(hdc,psa));
518 if (!script)
520 TRACE("Script not found\n");
521 return GSUB_E_NOFEATURE;
523 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
524 if (!language)
526 TRACE("Language not found\n");
527 return GSUB_E_NOFEATURE;
529 feature = GSUB_get_feature(header, language, feat);
530 if (!feature)
532 TRACE("%s feature not found\n",feat);
533 return GSUB_E_NOFEATURE;
535 TRACE("applying feature %s\n",feat);
536 return GSUB_apply_feature(header, feature, glyphs, index, write_dir, glyph_count);
539 static VOID *load_gsub_table(HDC hdc)
541 VOID* GSUB_Table = NULL;
542 int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
543 if (length != GDI_ERROR)
545 GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
546 GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
547 TRACE("Loaded GSUB table of %i bytes\n",length);
549 return GSUB_Table;
552 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
554 if (i + delta < 0)
556 if (psa->fLinkBefore)
557 return jtR;
558 else
559 return jtU;
561 if ( i+ delta >= cchLen)
563 if (psa->fLinkAfter)
564 return jtL;
565 else
566 return jtU;
569 i += delta;
571 if (context_type[i] == jtT)
572 return neighbour_joining_type(i,delta,context_type,cchLen,psa);
573 else
574 return context_type[i];
577 static inline BOOL right_join_causing(CHAR joining_type)
579 return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
582 static inline BOOL left_join_causing(CHAR joining_type)
584 return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
587 /* SHAPE_ShapeArabicGlyphs
589 void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs)
591 CHAR *context_type;
592 INT *context_shape;
593 INT dirR, dirL;
594 int i;
596 if (psa->eScript != Script_Arabic)
597 return;
599 if (*pcGlyphs != cChars)
601 ERR("Number of Glyphs and Chars need to match at the beginning\n");
602 return;
606 if (!psa->fLogicalOrder && psa->fRTL)
608 dirR = 1;
609 dirL = -1;
611 else
613 dirR = -1;
614 dirL = 1;
617 if (!psc->GSUB_Table)
618 psc->GSUB_Table = load_gsub_table(hdc);
620 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
621 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
623 for (i = 0; i < cChars; i++)
624 context_type[i] = wine_shaping_table[wine_shaping_table[pwcChars[i] >> 8] + (pwcChars[i] & 0xff)];
626 for (i = 0; i < cChars; i++)
628 if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
629 context_shape[i] = Xr;
630 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
631 context_shape[i] = Xl;
632 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)))
633 context_shape[i] = Xm;
634 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
635 context_shape[i] = Xr;
636 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
637 context_shape[i] = Xl;
638 else
639 context_shape[i] = Xn;
642 /* Contextual Shaping */
643 i = 0;
644 while(i < *pcGlyphs)
646 BOOL shaped = FALSE;
648 if (psc->GSUB_Table)
650 INT nextIndex;
651 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc->GSUB_Table, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
652 if (nextIndex > GSUB_E_NOGLYPH)
653 i = nextIndex;
654 shaped = (nextIndex > GSUB_E_NOGLYPH);
657 if (!shaped)
659 WORD newGlyph = pwOutGlyphs[i];
660 if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
662 /* fall back to presentation form B */
663 WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
664 if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
665 pwOutGlyphs[i] = newGlyph;
667 i++;
671 /* Required ligature substitution */
672 if (psc->GSUB_Table)
674 i = 0;
675 while(i < *pcGlyphs)
677 INT nextIndex;
678 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc->GSUB_Table, pwOutGlyphs, i, dirL, pcGlyphs, "rlig");
679 if (nextIndex > GSUB_E_NOGLYPH)
680 i = nextIndex;
681 else if (nextIndex == GSUB_E_NOFEATURE)
682 break;
683 else
684 i++;
688 HeapFree(GetProcessHeap(),0,context_shape);
689 HeapFree(GetProcessHeap(),0,context_type);