usp10: Handle Ligature Substitution Subtable from GSUB.

This also involves a restructuring of the handling of glyph lookups
from GSUB since Ligature Substitution can result in many glyphs being
replaced with 1 glyph, and future looksup may result in one glyph
being replaces with many glyphs.
This commit is contained in:
Aric Stewart 2010-05-21 15:12:03 -05:00 committed by Alexandre Julliard
parent ec62c33d79
commit 6c73258725
3 changed files with 197 additions and 68 deletions

View File

@ -165,6 +165,24 @@ typedef struct {
WORD Substitute[1]; WORD Substitute[1];
}GSUB_SingleSubstFormat2; }GSUB_SingleSubstFormat2;
typedef struct {
WORD SubstFormat; /* = 1 */
WORD Coverage;
WORD LigSetCount;
WORD LigatureSet[1];
}GSUB_LigatureSubstFormat1;
typedef struct {
WORD LigatureCount;
WORD Ligature[1];
}GSUB_LigatureSet;
typedef struct{
WORD LigGlyph;
WORD CompCount;
WORD Component[1];
}GSUB_Ligature;
/* the orders of joined_forms and contextual_features need to line up */ /* the orders of joined_forms and contextual_features need to line up */
static const char* contextual_features[] = static const char* contextual_features[] =
{ {
@ -287,28 +305,14 @@ static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GS
return NULL; return NULL;
} }
static UINT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, UINT glyph) static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
{ {
int i;
int offset;
const GSUB_LookupList *lookup;
lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
{
const GSUB_LookupTable *look;
offset = GET_BE_WORD(lookup->Lookup[GET_BE_WORD(feature->LookupListIndex[i])]);
look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
if (GET_BE_WORD(look->LookupType) != 1)
FIXME("We only handle SubType 1 (%i)\n",GET_BE_WORD(look->LookupType));
else
{
int j; int j;
TRACE("Single Substitution Subtable\n");
for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
{ {
int offset;
const GSUB_SingleSubstFormat1 *ssf1; const GSUB_SingleSubstFormat1 *ssf1;
offset = GET_BE_WORD(look->SubTable[j]); offset = GET_BE_WORD(look->SubTable[j]);
ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset); ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
@ -316,11 +320,12 @@ static UINT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* f
{ {
int offset = GET_BE_WORD(ssf1->Coverage); int offset = GET_BE_WORD(ssf1->Coverage);
TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID)); TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyph) != -1) if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
{ {
TRACE(" Glyph 0x%x ->",glyph); TRACE(" Glyph 0x%x ->",glyphs[glyph_index]);
glyph += GET_BE_WORD(ssf1->DeltaGlyphID); glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
TRACE(" 0x%x\n",glyph); TRACE(" 0x%x\n",glyphs[glyph_index]);
return glyph_index + 1;
} }
} }
else else
@ -332,19 +337,123 @@ static UINT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* f
ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1; ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
offset = GET_BE_WORD(ssf1->Coverage); offset = GET_BE_WORD(ssf1->Coverage);
TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount)); TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyph); index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
TRACE(" Coverage index %i\n",index); TRACE(" Coverage index %i\n",index);
if (index != -1) if (index != -1)
{ {
TRACE(" Glyph is 0x%x ->",glyph); TRACE(" Glyph is 0x%x ->",glyphs[glyph_index]);
glyph = GET_BE_WORD(ssf2->Substitute[index]); glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
TRACE("0x%x\n",glyph); TRACE("0x%x\n",glyphs[glyph_index]);
return glyph_index + 1;
}
}
}
return -1;
}
static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
{
int j;
TRACE("Ligature Substitution Subtable\n");
for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
{
const GSUB_LigatureSubstFormat1 *lsf1;
int offset,index;
offset = GET_BE_WORD(look->SubTable[j]);
lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
offset = GET_BE_WORD(lsf1->Coverage);
index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
TRACE(" Coverage index %i\n",index);
if (index != -1)
{
const GSUB_LigatureSet *ls;
int k, count;
offset = GET_BE_WORD(lsf1->LigatureSet[index]);
ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
count = GET_BE_WORD(ls->LigatureCount);
TRACE(" LigatureSet has %i members\n",count);
for (k = 0; k < count; k++)
{
const GSUB_Ligature *lig;
int CompCount,l,CompIndex;
offset = GET_BE_WORD(ls->Ligature[k]);
lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
CompCount = GET_BE_WORD(lig->CompCount) - 1;
CompIndex = glyph_index+write_dir;
for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
{
int CompGlyph;
CompGlyph = GET_BE_WORD(lig->Component[l]);
if (CompGlyph != glyphs[CompIndex])
break;
CompIndex += write_dir;
}
if (l == CompCount)
{
int replaceIdx = glyph_index;
if (write_dir < 0)
replaceIdx = glyph_index - CompCount;
TRACE(" Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
TRACE("0x%x\n",glyphs[replaceIdx]);
if (CompCount > 0)
{
int j;
for (j = replaceIdx + 1; j < *glyph_count; j++)
glyphs[j] =glyphs[j+CompCount];
*glyph_count = *glyph_count - CompCount;
}
return replaceIdx + 1;
} }
} }
} }
} }
return -1;
}
static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
{
int offset;
const GSUB_LookupTable *look;
offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
switch(GET_BE_WORD(look->LookupType))
{
case 1:
return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
case 4:
return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
default:
FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
} }
return glyph; return -1;
}
static INT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
{
int i;
int out_index = -1;
const GSUB_LookupList *lookup;
lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
{
out_index = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[i]), glyphs, glyph_index, write_dir, glyph_count);
if (out_index != -1)
break;
}
if (out_index == -1)
TRACE("lookups found no glyphs\n");
return out_index;
} }
static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa) static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa)
@ -391,7 +500,7 @@ static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa)
} }
} }
static WORD get_GSUB_feature_glyph(HDC hdc, SCRIPT_ANALYSIS *psa, void* GSUB_Table, UINT glyph, const char* feat) 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)
{ {
const GSUB_Header *header; const GSUB_Header *header;
const GSUB_Script *script; const GSUB_Script *script;
@ -399,7 +508,7 @@ static WORD get_GSUB_feature_glyph(HDC hdc, SCRIPT_ANALYSIS *psa, void* GSUB_Tab
const GSUB_Feature *feature; const GSUB_Feature *feature;
if (!GSUB_Table) if (!GSUB_Table)
return glyph; return -1;
header = GSUB_Table; header = GSUB_Table;
@ -407,21 +516,22 @@ static WORD get_GSUB_feature_glyph(HDC hdc, SCRIPT_ANALYSIS *psa, void* GSUB_Tab
if (!script) if (!script)
{ {
TRACE("Script not found\n"); TRACE("Script not found\n");
return glyph; return -1;
} }
language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */ language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
if (!language) if (!language)
{ {
TRACE("Language not found\n"); TRACE("Language not found\n");
return glyph; return -1;
} }
feature = GSUB_get_feature(header, language, feat); feature = GSUB_get_feature(header, language, feat);
if (!feature) if (!feature)
{ {
TRACE("%s feature not found\n",feat); TRACE("%s feature not found\n",feat);
return glyph; return -1;
} }
return GSUB_apply_feature(header, feature, glyph); TRACE("applying feature %s\n",feat);
return GSUB_apply_feature(header, feature, glyphs, index, write_dir, glyph_count);
} }
static VOID *load_gsub_table(HDC hdc) static VOID *load_gsub_table(HDC hdc)
@ -474,7 +584,7 @@ static inline BOOL left_join_causing(CHAR joining_type)
/* SHAPE_ShapeArabicGlyphs /* SHAPE_ShapeArabicGlyphs
*/ */
void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT cMaxGlyphs) void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs)
{ {
CHAR *context_type; CHAR *context_type;
INT *context_shape; INT *context_shape;
@ -484,6 +594,13 @@ void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WC
if (psa->eScript != Script_Arabic) if (psa->eScript != Script_Arabic)
return; return;
if (*pcGlyphs != cChars)
{
ERR("Number of Glyphs and Chars need to match at the beginning\n");
return;
}
if (!psa->fLogicalOrder && psa->fRTL) if (!psa->fLogicalOrder && psa->fRTL)
{ {
dirR = 1; dirR = 1;
@ -520,21 +637,33 @@ void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WC
context_shape[i] = Xn; context_shape[i] = Xn;
} }
for (i = 0; i < cChars; i++) /* Contextual Shaping */
i = 0;
while(i < *pcGlyphs)
{ {
WORD newGlyph = pwOutGlyphs[i]; BOOL shaped = FALSE;
if (psc->GSUB_Table) if (psc->GSUB_Table)
newGlyph = get_GSUB_feature_glyph(hdc, psa, psc->GSUB_Table, pwOutGlyphs[i], contextual_features[context_shape[i]]); {
if (newGlyph == pwOutGlyphs[i] && pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR) INT nextIndex;
nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc->GSUB_Table, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
if (nextIndex != -1)
i = nextIndex;
shaped = (nextIndex != -1);
}
if (!shaped)
{
WORD newGlyph = pwOutGlyphs[i];
if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
{ {
/* fall back to presentation form B */ /* fall back to presentation form B */
WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]]; WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000) if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
pwOutGlyphs[i] = newGlyph; pwOutGlyphs[i] = newGlyph;
} }
else if (newGlyph != pwOutGlyphs[i]) i++;
pwOutGlyphs[i] = newGlyph; }
} }
HeapFree(GetProcessHeap(),0,context_shape); HeapFree(GetProcessHeap(),0,context_shape);

View File

@ -1397,7 +1397,7 @@ HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
} }
rChars[i] = chInput; rChars[i] = chInput;
} }
SHAPE_ShapeArabicGlyphs(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, cMaxGlyphs); SHAPE_ShapeArabicGlyphs(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs);
heap_free(rChars); heap_free(rChars);
} }
else else

View File

@ -47,4 +47,4 @@ BOOL BIDI_DetermineLevels( LPCWSTR lpString, INT uCount, const SCRIPT_STATE *s,
INT BIDI_ReorderV2lLevel(int level, int *pIndexs, const BYTE* plevel, int cch, BOOL fReverse); INT BIDI_ReorderV2lLevel(int level, int *pIndexs, const BYTE* plevel, int cch, BOOL fReverse);
INT BIDI_ReorderL2vLevel(int level, int *pIndexs, const BYTE* plevel, int cch, BOOL fReverse); INT BIDI_ReorderL2vLevel(int level, int *pIndexs, const BYTE* plevel, int cch, BOOL fReverse);
void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT cMaxGlyphs); void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs);