mirror of
https://github.com/reactos/wine.git
synced 2024-11-25 12:49:45 +00:00
gdi32: Add loading and processing of GSUB table vert/vrt2 functions for proper tategaki (vertical writing).
This commit is contained in:
parent
1137f4bf31
commit
36ac341b55
@ -336,6 +336,7 @@ struct tagGdiFont {
|
||||
DWORD ntmFlags;
|
||||
FONTSIGNATURE fs;
|
||||
GdiFont *base_font;
|
||||
VOID *GSUB_Table;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -501,6 +502,105 @@ static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font,
|
||||
* cga40woa.fon=cga40850.fon
|
||||
*/
|
||||
|
||||
/* These are all structures needed for the GSUB table */
|
||||
|
||||
#define GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B')
|
||||
#define TATEGAKI_LOWER_BOUND 0x02F1
|
||||
|
||||
typedef struct {
|
||||
DWORD version;
|
||||
WORD ScriptList;
|
||||
WORD FeatureList;
|
||||
WORD LookupList;
|
||||
} GSUB_Header;
|
||||
|
||||
typedef struct {
|
||||
CHAR ScriptTag[4];
|
||||
WORD Script;
|
||||
} GSUB_ScriptRecord;
|
||||
|
||||
typedef struct {
|
||||
WORD ScriptCount;
|
||||
GSUB_ScriptRecord ScriptRecord[1];
|
||||
} GSUB_ScriptList;
|
||||
|
||||
typedef struct {
|
||||
CHAR LangSysTag[4];
|
||||
WORD LangSys;
|
||||
} GSUB_LangSysRecord;
|
||||
|
||||
typedef struct {
|
||||
WORD DefaultLangSys;
|
||||
WORD LangSysCount;
|
||||
GSUB_LangSysRecord LangSysRecord[1];
|
||||
} GSUB_Script;
|
||||
|
||||
typedef struct {
|
||||
WORD LookupOrder; /* Reserved */
|
||||
WORD ReqFeatureIndex;
|
||||
WORD FeatureCount;
|
||||
WORD FeatureIndex[1];
|
||||
} GSUB_LangSys;
|
||||
|
||||
typedef struct {
|
||||
CHAR FeatureTag[4];
|
||||
WORD Feature;
|
||||
} GSUB_FeatureRecord;
|
||||
|
||||
typedef struct {
|
||||
WORD FeatureCount;
|
||||
GSUB_FeatureRecord FeatureRecord[1];
|
||||
} GSUB_FeatureList;
|
||||
|
||||
typedef struct {
|
||||
WORD FeatureParams; /* Reserved */
|
||||
WORD LookupCount;
|
||||
WORD LookupListIndex[1];
|
||||
} GSUB_Feature;
|
||||
|
||||
typedef struct {
|
||||
WORD LookupCount;
|
||||
WORD Lookup[1];
|
||||
} GSUB_LookupList;
|
||||
|
||||
typedef struct {
|
||||
WORD LookupType;
|
||||
WORD LookupFlag;
|
||||
WORD SubTableCount;
|
||||
WORD SubTable[1];
|
||||
} GSUB_LookupTable;
|
||||
|
||||
typedef struct {
|
||||
WORD CoverageFormat;
|
||||
WORD GlyphCount;
|
||||
WORD GlyphArray[1];
|
||||
} GSUB_CoverageFormat1;
|
||||
|
||||
typedef struct {
|
||||
WORD Start;
|
||||
WORD End;
|
||||
WORD StartCoverageIndex;
|
||||
} GSUB_RangeRecord;
|
||||
|
||||
typedef struct {
|
||||
WORD CoverageFormat;
|
||||
WORD RangeCount;
|
||||
GSUB_RangeRecord RangeRecord[1];
|
||||
} GSUB_CoverageFormat2;
|
||||
|
||||
typedef struct {
|
||||
WORD SubstFormat; /* = 1 */
|
||||
WORD Coverage;
|
||||
WORD DeltaGlyphID;
|
||||
} GSUB_SingleSubstFormat1;
|
||||
|
||||
typedef struct {
|
||||
WORD SubstFormat; /* = 2 */
|
||||
WORD Coverage;
|
||||
WORD GlyphCount;
|
||||
WORD Substitute[1];
|
||||
}GSUB_SingleSubstFormat2;
|
||||
|
||||
#ifdef HAVE_CARBON_CARBON_H
|
||||
static char *find_cache_dir(void)
|
||||
{
|
||||
@ -2628,6 +2728,7 @@ static void free_font(GdiFont *font)
|
||||
for (i = 0; i < font->gmsize; i++)
|
||||
HeapFree(GetProcessHeap(),0,font->gm[i]);
|
||||
HeapFree(GetProcessHeap(), 0, font->gm);
|
||||
HeapFree(GetProcessHeap(), 0, font->GSUB_Table);
|
||||
HeapFree(GetProcessHeap(), 0, font);
|
||||
}
|
||||
|
||||
@ -3321,6 +3422,17 @@ found:
|
||||
ret->strikeout = lf.lfStrikeOut ? 0xff : 0;
|
||||
create_child_font_list(ret);
|
||||
|
||||
if (lf.lfFaceName[0]=='@') /* We need to try to load the GSUB table */
|
||||
{
|
||||
int length = WineEngGetFontData (ret, GSUB_TAG , 0, NULL, 0);
|
||||
if (length != GDI_ERROR)
|
||||
{
|
||||
ret->GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
|
||||
WineEngGetFontData(ret, GSUB_TAG , 0, ret->GSUB_Table, length);
|
||||
TRACE("Loaded GSUB table of %i bytes\n",length);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
|
||||
|
||||
list_add_head(&gdi_font_list, &ret->entry);
|
||||
@ -3679,8 +3791,249 @@ static BOOL codepage_sets_default_used(UINT codepage)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* GSUB Table handling functions
|
||||
*/
|
||||
|
||||
static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
|
||||
{
|
||||
const GSUB_CoverageFormat1* cf1;
|
||||
|
||||
cf1 = (GSUB_CoverageFormat1*)table;
|
||||
|
||||
if (GET_BE_WORD(cf1->CoverageFormat) == 1)
|
||||
{
|
||||
int count = GET_BE_WORD(cf1->GlyphCount);
|
||||
int i;
|
||||
TRACE("Coverage Format 1, %i glyphs\n",count);
|
||||
for (i = 0; i < count; i++)
|
||||
if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
|
||||
{
|
||||
const GSUB_CoverageFormat2* cf2;
|
||||
int i;
|
||||
int count;
|
||||
cf2 = (GSUB_CoverageFormat2*)cf1;
|
||||
|
||||
count = GET_BE_WORD(cf2->RangeCount);
|
||||
TRACE("Coverage Format 2, %i ranges\n",count);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
|
||||
return -1;
|
||||
if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
|
||||
(glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
|
||||
{
|
||||
return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
|
||||
glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
|
||||
{
|
||||
const GSUB_ScriptList *script;
|
||||
const GSUB_Script *deflt = NULL;
|
||||
int i;
|
||||
script = (GSUB_ScriptList*)((LPBYTE)header + GET_BE_WORD(header->ScriptList));
|
||||
|
||||
TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
|
||||
for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
|
||||
{
|
||||
const GSUB_Script *scr;
|
||||
int offset;
|
||||
|
||||
offset = GET_BE_WORD(script->ScriptRecord[i].Script);
|
||||
scr = (GSUB_Script*)((LPBYTE)script + offset);
|
||||
|
||||
if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
|
||||
return scr;
|
||||
if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
|
||||
deflt = scr;
|
||||
}
|
||||
return deflt;
|
||||
}
|
||||
|
||||
static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
|
||||
{
|
||||
int i;
|
||||
int offset;
|
||||
const GSUB_LangSys *Lang;
|
||||
|
||||
TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
|
||||
|
||||
for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
|
||||
{
|
||||
offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
|
||||
Lang = (GSUB_LangSys*)((LPBYTE)script + offset);
|
||||
|
||||
if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
|
||||
return Lang;
|
||||
}
|
||||
offset = GET_BE_WORD(script->DefaultLangSys);
|
||||
if (offset)
|
||||
{
|
||||
Lang = (GSUB_LangSys*)((LPBYTE)script + offset);
|
||||
return Lang;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
|
||||
{
|
||||
int i;
|
||||
const GSUB_FeatureList *feature;
|
||||
feature = (GSUB_FeatureList*)((LPBYTE)header + GET_BE_WORD(header->FeatureList));
|
||||
|
||||
TRACE("%i features \n",GET_BE_WORD(lang->FeatureCount));
|
||||
for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
|
||||
{
|
||||
int index = GET_BE_WORD(lang->FeatureIndex[i]);
|
||||
if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
|
||||
{
|
||||
const GSUB_Feature *feat;
|
||||
feat = (GSUB_Feature*)((LPBYTE)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
|
||||
return feat;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static FT_UInt GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, UINT glyph)
|
||||
{
|
||||
int i;
|
||||
int offset;
|
||||
const GSUB_LookupList *lookup;
|
||||
lookup = (GSUB_LookupList*)((LPBYTE)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 = (GSUB_LookupTable*)((LPBYTE)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\n");
|
||||
else
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
|
||||
{
|
||||
const GSUB_SingleSubstFormat1 *ssf1;
|
||||
offset = GET_BE_WORD(look->SubTable[j]);
|
||||
ssf1 = (GSUB_SingleSubstFormat1*)((LPBYTE)look+offset);
|
||||
if (GET_BE_WORD(ssf1->SubstFormat) == 1)
|
||||
{
|
||||
int offset = GET_BE_WORD(ssf1->Coverage);
|
||||
TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
|
||||
if (GSUB_is_glyph_covered((LPBYTE)ssf1+offset, glyph) != -1)
|
||||
{
|
||||
TRACE(" Glyph 0x%x ->",glyph);
|
||||
glyph += GET_BE_WORD(ssf1->DeltaGlyphID);
|
||||
TRACE(" 0x%x\n",glyph);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const GSUB_SingleSubstFormat2 *ssf2;
|
||||
INT index;
|
||||
INT offset;
|
||||
|
||||
ssf2 = (GSUB_SingleSubstFormat2 *)ssf1;
|
||||
offset = GET_BE_WORD(ssf1->Coverage);
|
||||
TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
|
||||
index = GSUB_is_glyph_covered((LPBYTE)ssf2+offset, glyph);
|
||||
TRACE(" Coverage index %i\n",index);
|
||||
if (index != -1)
|
||||
{
|
||||
TRACE(" Glyph is 0x%x ->",glyph);
|
||||
glyph = GET_BE_WORD(ssf2->Substitute[index]);
|
||||
TRACE("0x%x\n",glyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return glyph;
|
||||
}
|
||||
|
||||
static const char* get_opentype_script(const GdiFont *font)
|
||||
{
|
||||
/*
|
||||
* I am not sure if this is the correct way to generate our script tag
|
||||
*/
|
||||
|
||||
switch (font->charset)
|
||||
{
|
||||
case ANSI_CHARSET: return "latn";
|
||||
case BALTIC_CHARSET: return "latn"; /* ?? */
|
||||
case CHINESEBIG5_CHARSET: return "hani";
|
||||
case EASTEUROPE_CHARSET: return "latn"; /* ?? */
|
||||
case GB2312_CHARSET: return "hani";
|
||||
case GREEK_CHARSET: return "grek";
|
||||
case HANGUL_CHARSET: return "hang";
|
||||
case RUSSIAN_CHARSET: return "cyrl";
|
||||
case SHIFTJIS_CHARSET: return "kana";
|
||||
case TURKISH_CHARSET: return "latn"; /* ?? */
|
||||
case VIETNAMESE_CHARSET: return "latn";
|
||||
case JOHAB_CHARSET: return "latn"; /* ?? */
|
||||
case ARABIC_CHARSET: return "arab";
|
||||
case HEBREW_CHARSET: return "hebr";
|
||||
case THAI_CHARSET: return "thai";
|
||||
default: return "latn";
|
||||
}
|
||||
}
|
||||
|
||||
static FT_UInt get_GSUB_vert_glyph(const GdiFont *font, UINT glyph)
|
||||
{
|
||||
const GSUB_Header *header;
|
||||
const GSUB_Script *script;
|
||||
const GSUB_LangSys *language;
|
||||
const GSUB_Feature *feature;
|
||||
|
||||
if (!font->GSUB_Table)
|
||||
return glyph;
|
||||
|
||||
header = font->GSUB_Table;
|
||||
|
||||
script = GSUB_get_script_table(header, get_opentype_script(font));
|
||||
if (!script)
|
||||
{
|
||||
TRACE("Script not found\n");
|
||||
return glyph;
|
||||
}
|
||||
language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
|
||||
if (!language)
|
||||
{
|
||||
TRACE("Language not found\n");
|
||||
return glyph;
|
||||
}
|
||||
feature = GSUB_get_feature(header, language, "vrt2");
|
||||
if (!feature)
|
||||
feature = GSUB_get_feature(header, language, "vert");
|
||||
if (!feature)
|
||||
{
|
||||
TRACE("vrt2/vert feature not found\n");
|
||||
return glyph;
|
||||
}
|
||||
return GSUB_apply_feature(header, feature, glyph);
|
||||
}
|
||||
|
||||
static FT_UInt get_glyph_index(const GdiFont *font, UINT glyph)
|
||||
{
|
||||
FT_UInt glyphId;
|
||||
|
||||
if(font->ft_face->charmap->encoding == FT_ENCODING_NONE) {
|
||||
WCHAR wc = (WCHAR)glyph;
|
||||
BOOL default_used;
|
||||
@ -3696,12 +4049,13 @@ static FT_UInt get_glyph_index(const GdiFont *font, UINT glyph)
|
||||
else
|
||||
ret = pFT_Get_Char_Index(font->ft_face, (unsigned char)buf);
|
||||
TRACE("%04x (%02x) -> ret %d def_used %d\n", glyph, buf, ret, default_used);
|
||||
return ret;
|
||||
return get_GSUB_vert_glyph(font,ret);
|
||||
}
|
||||
|
||||
if(font->charset == SYMBOL_CHARSET && glyph < 0x100)
|
||||
glyph = glyph + 0xf000;
|
||||
return pFT_Get_Char_Index(font->ft_face, glyph);
|
||||
glyphId = pFT_Get_Char_Index(font->ft_face, glyph);
|
||||
return get_GSUB_vert_glyph(font,glyphId);
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
@ -3759,6 +4113,8 @@ DWORD WineEngGetGlyphOutline(GdiFont *incoming_font, UINT glyph, UINT format,
|
||||
float widthRatio = 1.0;
|
||||
FT_Matrix transMat = identityMat;
|
||||
BOOL needsTransform = FALSE;
|
||||
BOOL tategaki = (font->GSUB_Table != NULL);
|
||||
UINT original_index;
|
||||
|
||||
|
||||
TRACE("%p, %04x, %08x, %p, %08x, %p, %p\n", font, glyph, format, lpgm,
|
||||
@ -3767,27 +4123,33 @@ DWORD WineEngGetGlyphOutline(GdiFont *incoming_font, UINT glyph, UINT format,
|
||||
EnterCriticalSection( &freetype_cs );
|
||||
|
||||
if(format & GGO_GLYPH_INDEX) {
|
||||
glyph_index = glyph;
|
||||
glyph_index = get_GSUB_vert_glyph(incoming_font,glyph);
|
||||
original_index = glyph;
|
||||
format &= ~GGO_GLYPH_INDEX;
|
||||
} else {
|
||||
get_glyph_index_linked(incoming_font, glyph, &font, &glyph_index);
|
||||
ft_face = font->ft_face;
|
||||
original_index = glyph_index;
|
||||
}
|
||||
|
||||
if(glyph_index >= font->gmsize * GM_BLOCK_SIZE) {
|
||||
font->gmsize = (glyph_index / GM_BLOCK_SIZE + 1);
|
||||
/* tategaki never appears to happen to lower glyph index */
|
||||
if (glyph_index < TATEGAKI_LOWER_BOUND )
|
||||
tategaki = FALSE;
|
||||
|
||||
if(original_index >= font->gmsize * GM_BLOCK_SIZE) {
|
||||
font->gmsize = (original_index / GM_BLOCK_SIZE + 1);
|
||||
font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
|
||||
font->gmsize * sizeof(GM*));
|
||||
} else {
|
||||
if(format == GGO_METRICS && font->gm[glyph_index / GM_BLOCK_SIZE] != NULL && FONT_GM(font,glyph_index)->init ) {
|
||||
*lpgm = FONT_GM(font,glyph_index)->gm;
|
||||
if(format == GGO_METRICS && font->gm[original_index / GM_BLOCK_SIZE] != NULL && FONT_GM(font,original_index)->init ) {
|
||||
*lpgm = FONT_GM(font,original_index)->gm;
|
||||
LeaveCriticalSection( &freetype_cs );
|
||||
return 1; /* FIXME */
|
||||
}
|
||||
}
|
||||
|
||||
if (!font->gm[glyph_index / GM_BLOCK_SIZE])
|
||||
font->gm[glyph_index / GM_BLOCK_SIZE] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(GM) * GM_BLOCK_SIZE);
|
||||
if (!font->gm[original_index / GM_BLOCK_SIZE])
|
||||
font->gm[original_index / GM_BLOCK_SIZE] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(GM) * GM_BLOCK_SIZE);
|
||||
|
||||
if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP && format != WINE_GGO_GRAY16_BITMAP) || lpmat)
|
||||
load_flags |= FT_LOAD_NO_BITMAP;
|
||||
@ -3841,7 +4203,7 @@ DWORD WineEngGetGlyphOutline(GdiFont *incoming_font, UINT glyph, UINT format,
|
||||
}
|
||||
|
||||
/* Rotation transform */
|
||||
if(font->orientation) {
|
||||
if(font->orientation && !tategaki) {
|
||||
FT_Matrix rotationMat;
|
||||
FT_Vector vecAngle;
|
||||
angle = FT_FixedFromFloat((float)font->orientation / 10.0);
|
||||
@ -3913,11 +4275,11 @@ DWORD WineEngGetGlyphOutline(GdiFont *incoming_font, UINT glyph, UINT format,
|
||||
|
||||
if(format == GGO_METRICS || format == GGO_BITMAP || format == WINE_GGO_GRAY16_BITMAP)
|
||||
{
|
||||
FONT_GM(font,glyph_index)->gm = *lpgm;
|
||||
FONT_GM(font,glyph_index)->adv = adv;
|
||||
FONT_GM(font,glyph_index)->lsb = lsb;
|
||||
FONT_GM(font,glyph_index)->bbx = bbx;
|
||||
FONT_GM(font,glyph_index)->init = TRUE;
|
||||
FONT_GM(font,original_index)->gm = *lpgm;
|
||||
FONT_GM(font,original_index)->adv = adv;
|
||||
FONT_GM(font,original_index)->lsb = lsb;
|
||||
FONT_GM(font,original_index)->bbx = bbx;
|
||||
FONT_GM(font,original_index)->init = TRUE;
|
||||
}
|
||||
|
||||
if(format == GGO_METRICS)
|
||||
|
Loading…
Reference in New Issue
Block a user