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
31 #include "usp10_internal.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
37 #define FIRST_ARABIC_CHAR 0x0600
38 #define LAST_ARABIC_CHAR 0x06ff
40 typedef VOID (*ContextualShapingProc)(HDC, ScriptCache*, SCRIPT_ANALYSIS*,
41 WCHAR*, INT, WORD*, INT*, INT, WORD*);
43 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
44 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
45 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
48 typedef VOID (*ShapeCharGlyphPropProc)( HDC , ScriptCache*, SCRIPT_ANALYSIS*, const WCHAR*, const INT, const WORD*, const INT, WORD*, SCRIPT_CHARPROP*, SCRIPT_GLYPHPROP*);
50 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);
51 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 );
52 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 );
53 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 );
55 extern const unsigned short wine_shaping_table[];
56 extern const unsigned short wine_shaping_forms[LAST_ARABIC_CHAR - FIRST_ARABIC_CHAR + 1][4];
78 #ifdef WORDS_BIGENDIAN
79 #define GET_BE_WORD(x) (x)
81 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
84 /* These are all structures needed for the GSUB table */
85 #define GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B')
86 #define GSUB_E_NOFEATURE -2
87 #define GSUB_E_NOGLYPH -1
103 GSUB_ScriptRecord ScriptRecord[1];
109 } GSUB_LangSysRecord;
114 GSUB_LangSysRecord LangSysRecord[1];
118 WORD LookupOrder; /* Reserved */
119 WORD ReqFeatureIndex;
121 WORD FeatureIndex[1];
127 } GSUB_FeatureRecord;
131 GSUB_FeatureRecord FeatureRecord[1];
135 WORD FeatureParams; /* Reserved */
137 WORD LookupListIndex[1];
156 } GSUB_CoverageFormat1;
161 WORD StartCoverageIndex;
167 GSUB_RangeRecord RangeRecord[1];
168 } GSUB_CoverageFormat2;
171 WORD SubstFormat; /* = 1 */
174 } GSUB_SingleSubstFormat1;
177 WORD SubstFormat; /* = 2 */
181 }GSUB_SingleSubstFormat2;
184 WORD SubstFormat; /* = 1 */
188 }GSUB_LigatureSubstFormat1;
203 WORD LookupListIndex;
205 }GSUB_SubstLookupRecord;
208 WORD SubstFormat; /* = 1 */
210 WORD ChainSubRuleSetCount;
211 WORD ChainSubRuleSet[1];
212 }GSUB_ChainContextSubstFormat1;
215 WORD SubstFormat; /* = 3 */
216 WORD BacktrackGlyphCount;
218 }GSUB_ChainContextSubstFormat3_1;
221 WORD InputGlyphCount;
223 }GSUB_ChainContextSubstFormat3_2;
226 WORD LookaheadGlyphCount;
228 }GSUB_ChainContextSubstFormat3_3;
232 GSUB_SubstLookupRecord SubstLookupRecord[1];
233 }GSUB_ChainContextSubstFormat3_4;
236 WORD SubstFormat; /* = 1 */
238 WORD AlternateSetCount;
239 WORD AlternateSet[1];
240 } GSUB_AlternateSubstFormat1;
247 /* These are all structures needed for the GDEF table */
248 #define GDEF_TAG MS_MAKE_TAG('G', 'D', 'E', 'F')
250 enum {BaseGlyph=1, LigatureGlyph, MarkGlyph, ComponentGlyph};
257 WORD MarkAttachClassDef;
264 WORD ClassValueArray[1];
265 } GDEF_ClassDefFormat1;
271 } GDEF_ClassRangeRecord;
275 WORD ClassRangeCount;
276 GDEF_ClassRangeRecord ClassRangeRecord[1];
277 } GDEF_ClassDefFormat2;
279 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
281 /* the orders of joined_forms and contextual_features need to line up */
282 static const char* contextual_features[] =
294 static OPENTYPE_FEATURE_RECORD standard_features[] =
296 { MS_MAKE_TAG('l','i','g','a'), 1},
297 { MS_MAKE_TAG('c','l','i','g'), 1},
300 static OPENTYPE_FEATURE_RECORD arabic_features[] =
302 { MS_MAKE_TAG('r','l','i','g'), 1},
303 { MS_MAKE_TAG('c','a','l','t'), 1},
304 { MS_MAKE_TAG('l','i','g','a'), 1},
305 { MS_MAKE_TAG('d','l','i','g'), 1},
306 { MS_MAKE_TAG('c','s','w','h'), 1},
307 { MS_MAKE_TAG('m','s','e','t'), 1},
310 static const char* required_arabic_features[] =
319 static OPENTYPE_FEATURE_RECORD hebrew_features[] =
321 { MS_MAKE_TAG('d','l','i','g'), 1},
324 static OPENTYPE_FEATURE_RECORD syriac_features[] =
326 { MS_MAKE_TAG('r','l','i','g'), 1},
327 { MS_MAKE_TAG('c','a','l','t'), 1},
328 { MS_MAKE_TAG('l','i','g','a'), 1},
329 { MS_MAKE_TAG('d','l','i','g'), 1},
332 static const char* required_syriac_features[] =
344 static OPENTYPE_FEATURE_RECORD sinhala_features[] =
347 { MS_MAKE_TAG('a','k','h','n'), 1},
348 { MS_MAKE_TAG('r','p','h','f'), 1},
349 { MS_MAKE_TAG('v','a','t','u'), 1},
350 { MS_MAKE_TAG('p','s','t','f'), 1},
351 /* Presentation forms */
352 { MS_MAKE_TAG('b','l','w','s'), 1},
353 { MS_MAKE_TAG('a','b','v','s'), 1},
354 { MS_MAKE_TAG('p','s','t','s'), 1},
357 static OPENTYPE_FEATURE_RECORD tibetan_features[] =
359 { MS_MAKE_TAG('a','b','v','s'), 1},
360 { MS_MAKE_TAG('b','l','w','s'), 1},
363 static OPENTYPE_FEATURE_RECORD thai_features[] =
365 { MS_MAKE_TAG('c','c','m','p'), 1},
368 static const char* required_lao_features[] =
374 typedef struct ScriptShapeDataTag {
375 TEXTRANGE_PROPERTIES defaultTextRange;
376 const char** requiredFeatures;
378 ContextualShapingProc contextProc;
379 ShapeCharGlyphPropProc charGlyphPropProc;
382 /* in order of scripts */
383 static const ScriptShapeData ShapingData[] =
385 {{ standard_features, 2}, NULL, "", NULL, NULL},
386 {{ standard_features, 2}, NULL, "latn", NULL, NULL},
387 {{ standard_features, 2}, NULL, "latn", NULL, NULL},
388 {{ standard_features, 2}, NULL, "latn", NULL, NULL},
389 {{ standard_features, 2}, NULL, "" , NULL, NULL},
390 {{ standard_features, 2}, NULL, "latn", NULL, NULL},
391 {{ arabic_features, 6}, required_arabic_features, "arab", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
392 {{ arabic_features, 6}, required_arabic_features, "arab", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
393 {{ hebrew_features, 1}, NULL, "hebr", NULL, NULL},
394 {{ syriac_features, 4}, required_syriac_features, "syrc", ContextualShape_Syriac, ShapeCharGlyphProp_None},
395 {{ arabic_features, 6}, required_arabic_features, "arab", ContextualShape_Arabic, ShapeCharGlyphProp_Arabic},
396 {{ NULL, 0}, NULL, "thaa", NULL, ShapeCharGlyphProp_None},
397 {{ standard_features, 2}, NULL, "grek", NULL, NULL},
398 {{ standard_features, 2}, NULL, "cyrl", NULL, NULL},
399 {{ standard_features, 2}, NULL, "armn", NULL, NULL},
400 {{ standard_features, 2}, NULL, "geor", NULL, NULL},
401 {{ sinhala_features, 7}, NULL, "sinh", NULL, NULL},
402 {{ tibetan_features, 2}, NULL, "tibt", NULL, ShapeCharGlyphProp_None},
403 {{ tibetan_features, 2}, NULL, "tibt", NULL, ShapeCharGlyphProp_None},
404 {{ tibetan_features, 2}, NULL, "phag", ContextualShape_Phags_pa, ShapeCharGlyphProp_Thai},
405 {{ thai_features, 1}, NULL, "thai", NULL, ShapeCharGlyphProp_Thai},
406 {{ thai_features, 1}, NULL, "thai", NULL, ShapeCharGlyphProp_Thai},
407 {{ thai_features, 1}, required_lao_features, "lao", NULL, ShapeCharGlyphProp_Thai},
408 {{ thai_features, 1}, required_lao_features, "lao", NULL, ShapeCharGlyphProp_Thai},
411 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
413 const GSUB_CoverageFormat1* cf1;
417 if (GET_BE_WORD(cf1->CoverageFormat) == 1)
419 int count = GET_BE_WORD(cf1->GlyphCount);
421 TRACE("Coverage Format 1, %i glyphs\n",count);
422 for (i = 0; i < count; i++)
423 if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
427 else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
429 const GSUB_CoverageFormat2* cf2;
432 cf2 = (const GSUB_CoverageFormat2*)cf1;
434 count = GET_BE_WORD(cf2->RangeCount);
435 TRACE("Coverage Format 2, %i ranges\n",count);
436 for (i = 0; i < count; i++)
438 if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
440 if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
441 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
443 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
444 glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
450 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
455 static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
457 const GSUB_ScriptList *script;
458 const GSUB_Script *deflt = NULL;
460 script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
462 TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
463 for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
465 const GSUB_Script *scr;
468 offset = GET_BE_WORD(script->ScriptRecord[i].Script);
469 scr = (const GSUB_Script*)((const BYTE*)script + offset);
471 if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
473 if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
479 static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
483 const GSUB_LangSys *Lang;
485 TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
487 for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
489 offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
490 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
492 if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
495 offset = GET_BE_WORD(script->DefaultLangSys);
498 Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
504 static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
507 const GSUB_FeatureList *feature;
508 feature = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
510 TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
511 for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
513 int index = GET_BE_WORD(lang->FeatureIndex[i]);
514 if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
516 const GSUB_Feature *feat;
517 feat = (const GSUB_Feature*)((const BYTE*)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
524 static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
527 TRACE("Single Substitution Subtable\n");
529 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
532 const GSUB_SingleSubstFormat1 *ssf1;
533 offset = GET_BE_WORD(look->SubTable[j]);
534 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
535 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
537 int offset = GET_BE_WORD(ssf1->Coverage);
538 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
539 if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
541 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
542 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
543 TRACE(" 0x%x\n",glyphs[glyph_index]);
544 return glyph_index + 1;
549 const GSUB_SingleSubstFormat2 *ssf2;
553 ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
554 offset = GET_BE_WORD(ssf1->Coverage);
555 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
556 index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
557 TRACE(" Coverage index %i\n",index);
560 if (glyphs[glyph_index] == GET_BE_WORD(ssf2->Substitute[index]))
561 return GSUB_E_NOGLYPH;
563 TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]);
564 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
565 TRACE("0x%x\n",glyphs[glyph_index]);
566 return glyph_index + 1;
570 return GSUB_E_NOGLYPH;
573 static INT GSUB_apply_AlternateSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
576 TRACE("Alternate Substitution Subtable\n");
578 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
581 const GSUB_AlternateSubstFormat1 *asf1;
584 offset = GET_BE_WORD(look->SubTable[j]);
585 asf1 = (const GSUB_AlternateSubstFormat1*)((const BYTE*)look+offset);
586 offset = GET_BE_WORD(asf1->Coverage);
588 index = GSUB_is_glyph_covered((const BYTE*)asf1+offset, glyphs[glyph_index]);
591 const GSUB_AlternateSet *as;
592 offset = GET_BE_WORD(asf1->AlternateSet[index]);
593 as = (const GSUB_AlternateSet*)((const BYTE*)asf1+offset);
594 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as->GlyphCount));
595 if (glyphs[glyph_index] == GET_BE_WORD(as->Alternate[0]))
596 return GSUB_E_NOGLYPH;
598 TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
599 glyphs[glyph_index] = GET_BE_WORD(as->Alternate[0]);
600 TRACE(" 0x%x\n",glyphs[glyph_index]);
601 return glyph_index + 1;
604 return GSUB_E_NOGLYPH;
607 static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
611 TRACE("Ligature Substitution Subtable\n");
612 for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
614 const GSUB_LigatureSubstFormat1 *lsf1;
617 offset = GET_BE_WORD(look->SubTable[j]);
618 lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
619 offset = GET_BE_WORD(lsf1->Coverage);
620 index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
621 TRACE(" Coverage index %i\n",index);
624 const GSUB_LigatureSet *ls;
627 offset = GET_BE_WORD(lsf1->LigatureSet[index]);
628 ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
629 count = GET_BE_WORD(ls->LigatureCount);
630 TRACE(" LigatureSet has %i members\n",count);
631 for (k = 0; k < count; k++)
633 const GSUB_Ligature *lig;
634 int CompCount,l,CompIndex;
636 offset = GET_BE_WORD(ls->Ligature[k]);
637 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
638 CompCount = GET_BE_WORD(lig->CompCount) - 1;
639 CompIndex = glyph_index+write_dir;
640 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
643 CompGlyph = GET_BE_WORD(lig->Component[l]);
644 if (CompGlyph != glyphs[CompIndex])
646 CompIndex += write_dir;
650 int replaceIdx = glyph_index;
652 replaceIdx = glyph_index - CompCount;
654 TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
655 glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
656 TRACE("0x%x\n",glyphs[replaceIdx]);
660 for (j = replaceIdx + 1; j < *glyph_count; j++)
661 glyphs[j] =glyphs[j+CompCount];
662 *glyph_count = *glyph_count - CompCount;
664 return replaceIdx + 1;
669 return GSUB_E_NOGLYPH;
672 static INT GSUB_apply_ChainContextSubst(const GSUB_LookupList* lookup, const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
677 TRACE("Chaining Contextual Substitution Subtable\n");
678 for (j = 0; j < GET_BE_WORD(look->SubTableCount) && !done; j++)
680 const GSUB_ChainContextSubstFormat1 *ccsf1;
682 int dirLookahead = write_dir;
683 int dirBacktrack = -1 * write_dir;
685 offset = GET_BE_WORD(look->SubTable[j]);
686 ccsf1 = (const GSUB_ChainContextSubstFormat1*)((const BYTE*)look+offset);
687 if (GET_BE_WORD(ccsf1->SubstFormat) == 1)
689 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
692 else if (GET_BE_WORD(ccsf1->SubstFormat) == 2)
694 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
697 else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
701 const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
702 const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
703 const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
704 const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
705 int newIndex = glyph_index;
707 ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
709 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
711 for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
713 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
714 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
717 if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
719 TRACE("Matched Backtrack\n");
721 ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)(((LPBYTE)ccsf1)+sizeof(GSUB_ChainContextSubstFormat3_1) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)-1)));
723 indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
724 for (k = 0; k < indexGlyphs; k++)
726 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
727 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
730 if (k != indexGlyphs)
732 TRACE("Matched IndexGlyphs\n");
734 ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)(((LPBYTE)ccsf3_2)+sizeof(GSUB_ChainContextSubstFormat3_2) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_2->InputGlyphCount)-1)));
736 for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
738 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
739 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k))]) == -1)
742 if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
744 TRACE("Matched LookAhead\n");
746 ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)(((LPBYTE)ccsf3_3)+sizeof(GSUB_ChainContextSubstFormat3_3) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)-1)));
748 for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
750 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
751 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
753 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
754 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
757 ERR("Chain failed to generate a glyph\n");
767 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
770 const GSUB_LookupTable *look;
772 offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
773 look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
774 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
775 switch(GET_BE_WORD(look->LookupType))
778 return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
780 return GSUB_apply_AlternateSubst(look, glyphs, glyph_index, write_dir, glyph_count);
782 return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
784 return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
786 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
788 return GSUB_E_NOGLYPH;
791 static INT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
794 int out_index = GSUB_E_NOGLYPH;
795 const GSUB_LookupList *lookup;
797 lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
799 TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
800 for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
802 out_index = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[i]), glyphs, glyph_index, write_dir, glyph_count);
803 if (out_index != GSUB_E_NOGLYPH)
806 if (out_index == GSUB_E_NOGLYPH)
807 TRACE("lookups found no glyphs\n");
811 out2 = GSUB_apply_feature(header, feature, glyphs, glyph_index, write_dir, glyph_count);
812 if (out2!=GSUB_E_NOGLYPH)
818 static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc)
822 if (psc->userScript != 0)
823 return (char*)&psc->userScript;
825 if (ShapingData[psa->eScript].otTag[0] != 0)
826 return ShapingData[psa->eScript].otTag;
829 * fall back to the font charset
831 charset = GetTextCharsetInfo(hdc, NULL, 0x0);
834 case ANSI_CHARSET: return "latn";
835 case BALTIC_CHARSET: return "latn"; /* ?? */
836 case CHINESEBIG5_CHARSET: return "hani";
837 case EASTEUROPE_CHARSET: return "latn"; /* ?? */
838 case GB2312_CHARSET: return "hani";
839 case GREEK_CHARSET: return "grek";
840 case HANGUL_CHARSET: return "hang";
841 case RUSSIAN_CHARSET: return "cyrl";
842 case SHIFTJIS_CHARSET: return "kana";
843 case TURKISH_CHARSET: return "latn"; /* ?? */
844 case VIETNAMESE_CHARSET: return "latn";
845 case JOHAB_CHARSET: return "latn"; /* ?? */
846 case ARABIC_CHARSET: return "arab";
847 case HEBREW_CHARSET: return "hebr";
848 case THAI_CHARSET: return "thai";
849 default: return "latn";
853 static LPCVOID load_GSUB_feature(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache *psc, const char* feat)
855 const GSUB_Feature *feature;
858 for (i = 0; i < psc->feature_count; i++)
859 if (strncmp(psc->features[i].tag,feat,4)==0)
860 return psc->features[i].feature;
866 const GSUB_Script *script;
867 const GSUB_LangSys *language;
869 script = GSUB_get_script_table(psc->GSUB_Table, get_opentype_script(hdc,psa,psc));
872 if (psc->userLang != 0)
873 language = GSUB_get_lang_table(script,(char*)&psc->userLang);
875 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
877 feature = GSUB_get_feature(psc->GSUB_Table, language, feat);
880 /* try in the default (latin) table */
883 script = GSUB_get_script_table(psc->GSUB_Table, "latn");
886 language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
888 feature = GSUB_get_feature(psc->GSUB_Table, language, feat);
893 TRACE("Feature %s located at %p\n",debugstr_an(feat,4),feature);
895 psc->feature_count++;
898 psc->features = HeapReAlloc(GetProcessHeap(), 0, psc->features, psc->feature_count * sizeof(LoadedFeature));
900 psc->features = HeapAlloc(GetProcessHeap(), 0, psc->feature_count * sizeof(LoadedFeature));
902 lstrcpynA(psc->features[psc->feature_count - 1].tag, feat, 5);
903 psc->features[psc->feature_count - 1].feature = feature;
907 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)
909 const GSUB_Feature *feature;
911 feature = load_GSUB_feature(hdc, psa, psc, feat);
913 return GSUB_E_NOFEATURE;
915 TRACE("applying feature %s\n",feat);
916 return GSUB_apply_feature(psc->GSUB_Table, feature, glyphs, index, write_dir, glyph_count);
919 static VOID *load_gsub_table(HDC hdc)
921 VOID* GSUB_Table = NULL;
922 int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
923 if (length != GDI_ERROR)
925 GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
926 GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
927 TRACE("Loaded GSUB table of %i bytes\n",length);
932 static WORD GDEF_get_glyph_class(const GDEF_Header *header, WORD glyph)
936 const GDEF_ClassDefFormat1 *cf1;
941 offset = GET_BE_WORD(header->GlyphClassDef);
945 cf1 = (GDEF_ClassDefFormat1*)(((BYTE*)header)+offset);
946 if (GET_BE_WORD(cf1->ClassFormat) == 1)
948 if (glyph >= GET_BE_WORD(cf1->StartGlyph))
950 int index = glyph - GET_BE_WORD(cf1->StartGlyph);
951 if (index < GET_BE_WORD(cf1->GlyphCount))
952 class = GET_BE_WORD(cf1->ClassValueArray[index]);
955 else if (GET_BE_WORD(cf1->ClassFormat) == 2)
957 const GDEF_ClassDefFormat2 *cf2 = (GDEF_ClassDefFormat2*)cf1;
959 top = GET_BE_WORD(cf2->ClassRangeCount);
960 for (i = 0; i < top; i++)
962 if (glyph >= GET_BE_WORD(cf2->ClassRangeRecord[i].Start) &&
963 glyph <= GET_BE_WORD(cf2->ClassRangeRecord[i].End))
965 class = GET_BE_WORD(cf2->ClassRangeRecord[i].Class);
971 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1->ClassFormat));
976 static VOID *load_gdef_table(HDC hdc)
978 VOID* GDEF_Table = NULL;
979 int length = GetFontData(hdc, GDEF_TAG , 0, NULL, 0);
980 if (length != GDI_ERROR)
982 GDEF_Table = HeapAlloc(GetProcessHeap(),0,length);
983 GetFontData(hdc, GDEF_TAG , 0, GDEF_Table, length);
984 TRACE("Loaded GDEF table of %i bytes\n",length);
989 static void GDEF_UpdateGlyphProps(HDC hdc, const WORD *pwGlyphs, const WORD cGlyphs, WORD* pwLogClust, SCRIPT_GLYPHPROP *pGlyphProp)
991 VOID* header = load_gdef_table(hdc);
994 for (i = 0; i < cGlyphs; i++)
998 class = GDEF_get_glyph_class(header, pwGlyphs[i]);
1004 pGlyphProp[i].sva.fClusterStart = 1;
1005 pGlyphProp[i].sva.fDiacritic = 0;
1006 pGlyphProp[i].sva.fZeroWidth = 0;
1009 pGlyphProp[i].sva.fClusterStart = 1;
1010 pGlyphProp[i].sva.fDiacritic = 0;
1011 pGlyphProp[i].sva.fZeroWidth = 0;
1014 pGlyphProp[i].sva.fClusterStart = 0;
1015 pGlyphProp[i].sva.fDiacritic = 1;
1016 pGlyphProp[i].sva.fZeroWidth = 1;
1018 case ComponentGlyph:
1019 pGlyphProp[i].sva.fClusterStart = 0;
1020 pGlyphProp[i].sva.fDiacritic = 0;
1021 pGlyphProp[i].sva.fZeroWidth = 0;
1024 ERR("Unknown glyph class %i\n",class);
1025 pGlyphProp[i].sva.fClusterStart = 1;
1026 pGlyphProp[i].sva.fDiacritic = 0;
1027 pGlyphProp[i].sva.fZeroWidth = 0;
1032 static void UpdateClustersFromGlyphProp(const int cGlyphs, const int cChars, WORD* pwLogClust, SCRIPT_GLYPHPROP *pGlyphProp)
1036 for (i = 0; i < cGlyphs; i++)
1038 if (!pGlyphProp[i].sva.fClusterStart)
1041 for (j = 0; j < cChars; j++)
1043 if (pwLogClust[j] == i)
1046 while (!pGlyphProp[pwLogClust[k]].sva.fClusterStart && k >= 0 && k <cChars)
1048 if (pGlyphProp[pwLogClust[k]].sva.fClusterStart)
1049 pwLogClust[j] = pwLogClust[k];
1056 static void UpdateClusters(int nextIndex, int changeCount, int write_dir, int chars, WORD* pwLogClust )
1058 if (changeCount == 0)
1063 int target_glyph = nextIndex - 1;
1064 int target_index = -1;
1065 int replacing_glyph = -1;
1069 for (i = 0; i < chars; i++)
1071 if (pwLogClust[i] == target_glyph)
1078 for (i = chars - 1; i >= 0; i--)
1080 if (pwLogClust[i] == target_glyph)
1086 if (target_index == -1)
1088 ERR("Unable to find target glyph\n");
1092 if (changeCount < 0)
1095 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1097 if (pwLogClust[i] == target_glyph)
1099 if(pwLogClust[i] == replacing_glyph)
1100 pwLogClust[i] = target_glyph;
1104 if (changed >= changeCount)
1106 replacing_glyph = pwLogClust[i];
1107 pwLogClust[i] = target_glyph;
1115 /* renumber trailing indexes*/
1116 for(i = target_index; i < chars && i >= 0; i+=write_dir)
1118 if (pwLogClust[i] != target_glyph)
1119 pwLogClust[i] += changeCount;
1124 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 )
1128 if (psc->GSUB_Table)
1130 const GSUB_Feature *feature;
1132 feature = load_GSUB_feature(hdc, psa, psc, feat);
1134 return GSUB_E_NOFEATURE;
1137 TRACE("applying feature %s\n",debugstr_an(feat,4));
1138 while(i < *pcGlyphs)
1141 INT prevCount = *pcGlyphs;
1142 nextIndex = GSUB_apply_feature(psc->GSUB_Table, feature, pwOutGlyphs, i, write_dir, pcGlyphs);
1143 if (nextIndex > GSUB_E_NOGLYPH)
1145 UpdateClusters(nextIndex, *pcGlyphs - prevCount, write_dir, cChars, pwLogClust);
1153 return GSUB_E_NOFEATURE;
1156 static WCHAR neighbour_char(int i, int delta, const WCHAR* chars, INT cchLen)
1160 if ( i+ delta >= cchLen)
1168 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
1172 if (psa->fLinkBefore)
1177 if ( i+ delta >= cchLen)
1179 if (psa->fLinkAfter)
1187 if (context_type[i] == jtT)
1188 return neighbour_joining_type(i,delta,context_type,cchLen,psa);
1190 return context_type[i];
1193 static inline BOOL right_join_causing(CHAR joining_type)
1195 return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
1198 static inline BOOL left_join_causing(CHAR joining_type)
1200 return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
1203 static inline BOOL word_break_causing(WCHAR chr)
1205 /* we are working within a string of characters already guareented to
1206 be within one script, Syriac, so we do not worry about any characers
1207 other than the space character outside of that range */
1208 return (chr == 0 || chr == 0x20 );
1212 * ContextualShape_Arabic
1214 static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1221 if (*pcGlyphs != cChars)
1223 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1227 if (!psa->fLogicalOrder && psa->fRTL)
1238 if (!psc->GSUB_Table)
1239 psc->GSUB_Table = load_gsub_table(hdc);
1241 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1242 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1244 for (i = 0; i < cChars; i++)
1245 context_type[i] = wine_shaping_table[wine_shaping_table[pwcChars[i] >> 8] + (pwcChars[i] & 0xff)];
1247 for (i = 0; i < cChars; i++)
1249 if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1250 context_shape[i] = Xr;
1251 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1252 context_shape[i] = Xl;
1253 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)))
1254 context_shape[i] = Xm;
1255 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1256 context_shape[i] = Xr;
1257 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1258 context_shape[i] = Xl;
1260 context_shape[i] = Xn;
1263 /* Contextual Shaping */
1265 while(i < *pcGlyphs)
1267 BOOL shaped = FALSE;
1269 if (psc->GSUB_Table)
1272 INT prevCount = *pcGlyphs;
1273 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1274 if (nextIndex > GSUB_E_NOGLYPH)
1277 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1279 shaped = (nextIndex > GSUB_E_NOGLYPH);
1284 WORD newGlyph = pwOutGlyphs[i];
1285 if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
1287 /* fall back to presentation form B */
1288 WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
1289 if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
1290 pwOutGlyphs[i] = newGlyph;
1296 HeapFree(GetProcessHeap(),0,context_shape);
1297 HeapFree(GetProcessHeap(),0,context_type);
1301 * ContextualShape_Syriac
1305 #define DALATH 0x715
1308 static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1315 if (*pcGlyphs != cChars)
1317 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1321 if (!psa->fLogicalOrder && psa->fRTL)
1332 if (!psc->GSUB_Table)
1333 psc->GSUB_Table = load_gsub_table(hdc);
1335 if (!psc->GSUB_Table)
1338 context_type = HeapAlloc(GetProcessHeap(),0,cChars);
1339 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1341 for (i = 0; i < cChars; i++)
1342 context_type[i] = wine_shaping_table[wine_shaping_table[pwcChars[i] >> 8] + (pwcChars[i] & 0xff)];
1344 for (i = 0; i < cChars; i++)
1346 if (pwcChars[i] == ALAPH)
1348 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1350 if (left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1351 context_shape[i] = Afj;
1352 else if ( rchar != DALATH && rchar != RISH &&
1353 !left_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)) &&
1354 word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1355 context_shape[i] = Afn;
1356 else if ( (rchar == DALATH || rchar == RISH) && word_break_causing(neighbour_char(i,dirL,pwcChars,cChars)))
1357 context_shape[i] = Afx;
1359 context_shape[i] = Xn;
1361 else if (context_type[i] == jtR &&
1362 right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1363 context_shape[i] = Xr;
1364 else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1365 context_shape[i] = Xl;
1366 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)))
1367 context_shape[i] = Xm;
1368 else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
1369 context_shape[i] = Xr;
1370 else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
1371 context_shape[i] = Xl;
1373 context_shape[i] = Xn;
1376 /* Contextual Shaping */
1378 while(i < *pcGlyphs)
1381 INT prevCount = *pcGlyphs;
1382 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1383 if (nextIndex > GSUB_E_NOGLYPH)
1385 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1392 HeapFree(GetProcessHeap(),0,context_shape);
1393 HeapFree(GetProcessHeap(),0,context_type);
1397 * ContextualShape_Phags_pa
1400 #define phags_pa_CANDRABINDU 0xA873
1401 #define phags_pa_START 0xA840
1402 #define phags_pa_END 0xA87F
1404 static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1410 if (*pcGlyphs != cChars)
1412 ERR("Number of Glyphs and Chars need to match at the beginning\n");
1416 if (!psa->fLogicalOrder && psa->fRTL)
1427 if (!psc->GSUB_Table)
1428 psc->GSUB_Table = load_gsub_table(hdc);
1430 if (!psc->GSUB_Table)
1433 context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
1435 for (i = 0; i < cChars; i++)
1437 if (pwcChars[i] >= phags_pa_START && pwcChars[i] <= phags_pa_END)
1439 WCHAR rchar = neighbour_char(i,dirR,pwcChars,cChars);
1440 WCHAR lchar = neighbour_char(i,dirL,pwcChars,cChars);
1441 BOOL jrchar = (rchar != phags_pa_CANDRABINDU && rchar >= phags_pa_START && rchar <= phags_pa_END);
1442 BOOL jlchar = (lchar != phags_pa_CANDRABINDU && lchar >= phags_pa_START && lchar <= phags_pa_END);
1444 if (jrchar && jlchar)
1445 context_shape[i] = Xm;
1447 context_shape[i] = Xr;
1449 context_shape[i] = Xl;
1451 context_shape[i] = Xn;
1454 context_shape[i] = -1;
1457 /* Contextual Shaping */
1459 while(i < *pcGlyphs)
1461 if (context_shape[i] >= 0)
1464 INT prevCount = *pcGlyphs;
1465 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
1466 if (nextIndex > GSUB_E_NOGLYPH)
1468 UpdateClusters(nextIndex, *pcGlyphs - prevCount, dirL, cChars, pwLogClust);
1478 HeapFree(GetProcessHeap(),0,context_shape);
1481 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)
1485 for (i = 0; i < cGlyphs; i++)
1490 for (k = 0; k < cChars; k++)
1492 if (pwLogClust[k] == i)
1494 char_index[char_count] = k;
1499 if (char_count == 0)
1501 FIXME("No chars in this glyph? Must be an error\n");
1505 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
1507 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_BLANK;
1508 pCharProp[char_index[0]].fCanGlyphAlone = 1;
1511 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
1514 GDEF_UpdateGlyphProps(hdc, pwGlyphs, cGlyphs, pwLogClust, pGlyphProp);
1515 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
1518 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 )
1521 int initGlyph, finaGlyph;
1525 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
1526 memset(spaces,0,cGlyphs);
1528 if (!psa->fLogicalOrder && psa->fRTL)
1530 initGlyph = cGlyphs-1;
1538 finaGlyph = cGlyphs-1;
1543 for (i = 0; i < cGlyphs; i++)
1545 for (k = 0; k < cChars; k++)
1546 if (pwLogClust[k] == i)
1548 if (pwcChars[k] == 0x0020)
1553 for (i = 0; i < cGlyphs; i++)
1557 BOOL isInit, isFinal;
1559 for (k = 0; k < cChars; k++)
1561 if (pwLogClust[k] == i)
1563 char_index[char_count] = k;
1568 isInit = (i == initGlyph || (i+dirR > 0 && i+dirR < cGlyphs && spaces[i+dirR]));
1569 isFinal = (i == finaGlyph || (i+dirL > 0 && i+dirL < cGlyphs && spaces[i+dirL]));
1571 if (char_count == 0)
1573 FIXME("No chars in this glyph? Must be an error\n");
1577 if (char_count == 1)
1579 if (pwcChars[char_index[0]] == 0x0020) /* space */
1581 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BLANK;
1582 pCharProp[char_index[0]].fCanGlyphAlone = 1;
1584 else if (pwcChars[char_index[0]] == 0x0640) /* kashida */
1585 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
1586 else if (pwcChars[char_index[0]] == 0x0633) /* SEEN */
1588 if (!isInit && !isFinal)
1589 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN_M;
1591 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_SEEN;
1593 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
1597 if (pwcChars[char_index[0]] == 0x0628 ) /* BA */
1598 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BA;
1599 else if (pwcChars[char_index[0]] == 0x0631 ) /* RA */
1600 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_RA;
1601 else if (pwcChars[char_index[0]] == 0x0647 ) /* HA */
1602 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_HA;
1603 else if ((pwcChars[char_index[0]] == 0x0627 || pwcChars[char_index[0]] == 0x0625 || pwcChars[char_index[0]] == 0x0623 || pwcChars[char_index[0]] == 0x0622) ) /* alef-like */
1604 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_ALEF;
1606 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
1608 else if (!isInit && !isFinal)
1609 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
1611 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
1613 else if (char_count == 2)
1615 if ((pwcChars[char_index[0]] == 0x0628 && pwcChars[char_index[1]]== 0x0631) || (pwcChars[char_index[0]] == 0x0631 && pwcChars[char_index[1]]== 0x0628)) /* BA+RA */
1616 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_BARA;
1618 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
1620 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
1622 else if (!isInit && !isFinal)
1623 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_ARABIC_NORMAL;
1625 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
1628 GDEF_UpdateGlyphProps(hdc, pwGlyphs, cGlyphs, pwLogClust, pGlyphProp);
1629 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
1630 HeapFree(GetProcessHeap(),0,spaces);
1633 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 )
1640 spaces = HeapAlloc(GetProcessHeap(),0,cGlyphs);
1641 memset(spaces,0,cGlyphs);
1643 if (!psa->fLogicalOrder && psa->fRTL)
1650 finaGlyph = cGlyphs-1;
1654 for (i = 0; i < cGlyphs; i++)
1656 for (k = 0; k < cChars; k++)
1657 if (pwLogClust[k] == i)
1659 if (pwcChars[k] == 0x0020)
1664 for (i = 0; i < cGlyphs; i++)
1669 for (k = 0; k < cChars; k++)
1671 if (pwLogClust[k] == i)
1673 char_index[char_count] = k;
1678 if (char_count == 0)
1680 FIXME("No chars in this glyph? Must be an error\n");
1684 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
1686 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
1687 pCharProp[char_index[0]].fCanGlyphAlone = 1;
1689 else if (i == finaGlyph)
1690 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
1692 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
1695 HeapFree(GetProcessHeap(),0,spaces);
1696 GDEF_UpdateGlyphProps(hdc, pwGlyphs, cGlyphs, pwLogClust, pGlyphProp);
1697 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
1699 /* Do not allow justification between marks and their base */
1700 for (i = 0; i < cGlyphs; i++)
1702 if (!pGlyphProp[i].sva.fClusterStart)
1703 pGlyphProp[i-dirL].sva.uJustification = SCRIPT_JUSTIFY_NONE;
1707 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)
1711 for (i = 0; i < cGlyphs; i++)
1716 for (k = 0; k < cChars; k++)
1718 if (pwLogClust[k] == i)
1720 char_index[char_count] = k;
1725 if (char_count == 0)
1727 FIXME("No chars in this glyph? Must be an error\n");
1731 if (char_count ==1 && pwcChars[char_index[0]] == 0x0020) /* space */
1733 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_CHARACTER;
1734 pCharProp[char_index[0]].fCanGlyphAlone = 1;
1737 pGlyphProp[i].sva.uJustification = SCRIPT_JUSTIFY_NONE;
1739 GDEF_UpdateGlyphProps(hdc, pwGlyphs, cGlyphs, pwLogClust, pGlyphProp);
1740 UpdateClustersFromGlyphProp(cGlyphs, cChars, pwLogClust, pGlyphProp);
1743 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)
1745 if (ShapingData[psa->eScript].charGlyphPropProc)
1746 ShapingData[psa->eScript].charGlyphPropProc(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
1748 ShapeCharGlyphProp_Default(hdc, psc, psa, pwcChars, cChars, pwGlyphs, cGlyphs, pwLogClust, pCharProp, pGlyphProp);
1751 void SHAPE_ContextualShaping(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
1753 if (ShapingData[psa->eScript].contextProc)
1754 ShapingData[psa->eScript].contextProc(hdc, psc, psa, pwcChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
1757 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)
1762 if (!rpRangeProperties)
1765 if (!psc->GSUB_Table)
1766 psc->GSUB_Table = load_gsub_table(hdc);
1768 if (!psc->GSUB_Table)
1771 if (!psa->fLogicalOrder && psa->fRTL)
1776 for (i = 0; i < rpRangeProperties->cotfRecords; i++)
1778 if (rpRangeProperties->potfRecords[i].lParameter > 0)
1779 apply_GSUB_feature(hdc, psa, psc, pwOutGlyphs, dirL, pcGlyphs, cChars, (const char*)&rpRangeProperties->potfRecords[i].tagFeature, pwLogClust);
1783 void SHAPE_ApplyDefaultOpentypeFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, INT cChars, WORD *pwLogClust)
1785 const TEXTRANGE_PROPERTIES *rpRangeProperties;
1786 rpRangeProperties = &ShapingData[psa->eScript].defaultTextRange;
1788 SHAPE_ApplyOpenTypeFeatures(hdc, psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, rpRangeProperties, pwLogClust);
1791 HRESULT SHAPE_CheckFontForRequiredFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa)
1793 const GSUB_Feature *feature;
1796 if (!ShapingData[psa->eScript].requiredFeatures)
1799 if (!psc->GSUB_Table)
1800 psc->GSUB_Table = load_gsub_table(hdc);
1802 /* we need to have at least one of the required features */
1804 while (ShapingData[psa->eScript].requiredFeatures[i])
1806 feature = load_GSUB_feature(hdc, psa, psc, ShapingData[psa->eScript].requiredFeatures[i]);
1812 return USP_E_SCRIPT_NOT_IN_FONT;