diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 3309f3f00c3..dfbeffe9711 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -229,6 +229,7 @@ extern UINT32 opentype_get_cpal_palettecount(const void*) DECLSPEC_HIDDEN; extern UINT32 opentype_get_cpal_paletteentrycount(const void*) DECLSPEC_HIDDEN; extern HRESULT opentype_get_cpal_entries(const void*,UINT32,UINT32,UINT32,DWRITE_COLOR_F*) DECLSPEC_HIDDEN; extern HRESULT opentype_get_font_signature(struct file_stream_desc*,FONTSIGNATURE*) DECLSPEC_HIDDEN; +extern BOOL opentype_has_vertical_variants(IDWriteFontFace3*) DECLSPEC_HIDDEN; struct dwrite_colorglyph { USHORT layer; /* [0, num_layers) index indicating current layer */ diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index c99c5c3f901..d635c156711 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -205,6 +205,13 @@ struct dwrite_colorglyphenum { #define GLYPH_BLOCK_MASK (GLYPH_BLOCK_SIZE - 1) #define GLYPH_MAX 65536 +enum fontface_flags { + FONTFACE_IS_SYMBOL = 1 << 0, + FONTFACE_IS_MONOSPACED = 1 << 1, + FONTFACE_HAS_KERN_PAIRS = 1 << 2, + FONTFACE_HAS_VERTICAL_VARIANTS = 1 << 3 +}; + struct dwrite_fontface { IDWriteFontFace3 IDWriteFontFace3_iface; LONG ref; @@ -219,9 +226,7 @@ struct dwrite_fontface { DWRITE_FONT_METRICS1 metrics; DWRITE_CARET_METRICS caret; INT charmap; - BOOL is_symbol; - BOOL has_kerning_pairs : 1; - BOOL is_monospaced : 1; + UINT16 flags; struct dwrite_fonttable cmap; struct dwrite_fonttable vdmx; @@ -545,7 +550,7 @@ static BOOL WINAPI dwritefontface_IsSymbolFont(IDWriteFontFace3 *iface) { struct dwrite_fontface *This = impl_from_IDWriteFontFace3(iface); TRACE("(%p)\n", This); - return This->is_symbol; + return !!(This->flags & FONTFACE_IS_SYMBOL); } static void WINAPI dwritefontface_GetMetrics(IDWriteFontFace3 *iface, DWRITE_FONT_METRICS *metrics) @@ -859,7 +864,7 @@ static BOOL WINAPI dwritefontface1_IsMonospacedFont(IDWriteFontFace3 *iface) { struct dwrite_fontface *This = impl_from_IDWriteFontFace3(iface); TRACE("(%p)\n", This); - return This->is_monospaced; + return !!(This->flags & FONTFACE_IS_MONOSPACED); } static HRESULT WINAPI dwritefontface1_GetDesignGlyphAdvances(IDWriteFontFace3 *iface, @@ -929,7 +934,7 @@ static HRESULT WINAPI dwritefontface1_GetKerningPairAdjustments(IDWriteFontFace3 return E_INVALIDARG; } - if (!This->has_kerning_pairs) { + if (This->flags & FONTFACE_HAS_KERN_PAIRS) { memset(adjustments, 0, count*sizeof(INT32)); return S_OK; } @@ -945,7 +950,7 @@ static BOOL WINAPI dwritefontface1_HasKerningPairs(IDWriteFontFace3 *iface) { struct dwrite_fontface *This = impl_from_IDWriteFontFace3(iface); TRACE("(%p)\n", This); - return This->has_kerning_pairs; + return !!(This->flags & FONTFACE_HAS_KERN_PAIRS); } static HRESULT WINAPI dwritefontface1_GetRecommendedRenderingMode(IDWriteFontFace3 *iface, @@ -968,8 +973,8 @@ static HRESULT WINAPI dwritefontface1_GetVerticalGlyphVariants(IDWriteFontFace3 static BOOL WINAPI dwritefontface1_HasVerticalGlyphVariants(IDWriteFontFace3 *iface) { struct dwrite_fontface *This = impl_from_IDWriteFontFace3(iface); - FIXME("(%p): stub\n", This); - return FALSE; + TRACE("(%p)\n", This); + return !!(This->flags & FONTFACE_HAS_VERTICAL_VARIANTS); } static BOOL WINAPI dwritefontface2_IsColorFont(IDWriteFontFace3 *iface) @@ -4130,6 +4135,7 @@ HRESULT create_fontface(const struct fontface_desc *desc, IDWriteFontFace3 **ret struct file_stream_desc stream_desc; struct dwrite_fontface *fontface; HRESULT hr = S_OK; + BOOL is_symbol; int i; *ret = NULL; @@ -4188,9 +4194,17 @@ HRESULT create_fontface(const struct fontface_desc *desc, IDWriteFontFace3 **ret fontface->caret.slopeRun = fontface->caret.slopeRise / 3; } } - fontface->charmap = freetype_get_charmap_index(&fontface->IDWriteFontFace3_iface, &fontface->is_symbol); - fontface->has_kerning_pairs = freetype_has_kerning_pairs(&fontface->IDWriteFontFace3_iface); - fontface->is_monospaced = freetype_is_monospaced(&fontface->IDWriteFontFace3_iface); + + fontface->flags = 0; + fontface->charmap = freetype_get_charmap_index(&fontface->IDWriteFontFace3_iface, &is_symbol); + if (is_symbol) + fontface->flags |= FONTFACE_IS_SYMBOL; + if (freetype_has_kerning_pairs(&fontface->IDWriteFontFace3_iface)) + fontface->flags |= FONTFACE_HAS_KERN_PAIRS; + if (freetype_is_monospaced(&fontface->IDWriteFontFace3_iface)) + fontface->flags |= FONTFACE_IS_MONOSPACED; + if (opentype_has_vertical_variants(&fontface->IDWriteFontFace3_iface)) + fontface->flags |= FONTFACE_HAS_VERTICAL_VARIANTS; /* Font properties are reused from font object when 'normal' face creation path is used: collection -> family -> matching font -> fontface. diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 51a8dd81d56..19dc6dd13bf 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -334,6 +334,49 @@ enum OPENTYPE_PLATFORM_ID OPENTYPE_PLATFORM_CUSTOM }; +typedef struct { + WORD FeatureParams; + WORD LookupCount; + WORD LookupListIndex[1]; +} OT_Feature; + +typedef struct { + WORD LookupCount; + WORD Lookup[1]; +} OT_LookupList; + +typedef struct { + WORD LookupType; + WORD LookupFlag; + WORD SubTableCount; + WORD SubTable[1]; +} OT_LookupTable; + +typedef struct { + WORD SubstFormat; + WORD Coverage; + WORD DeltaGlyphID; +} GSUB_SingleSubstFormat1; + +typedef struct { + WORD SubstFormat; + WORD Coverage; + WORD GlyphCount; + WORD Substitute[1]; +} GSUB_SingleSubstFormat2; + +typedef struct { + WORD SubstFormat; + WORD ExtensionLookupType; + DWORD ExtensionOffset; +} GSUB_ExtensionPosFormat1; + +enum OPENTYPE_GPOS_LOOKUPS +{ + OPENTYPE_GPOS_SINGLE_SUBST = 1, + OPENTYPE_GPOS_EXTENSION_SUBST = 7 +}; + enum TT_NAME_WINDOWS_ENCODING_ID { TT_NAME_WINDOWS_ENCODING_SYMBOL = 0, @@ -1887,3 +1930,76 @@ HRESULT opentype_get_font_signature(struct file_stream_desc *stream_desc, FONTSI return hr; } + +BOOL opentype_has_vertical_variants(IDWriteFontFace3 *fontface) +{ + const OT_FeatureList *featurelist; + const OT_LookupList *lookup_list; + BOOL exists = FALSE, ret = FALSE; + const GPOS_GSUB_Header *header; + const void *data; + void *context; + UINT32 size; + HRESULT hr; + UINT16 i; + + hr = IDWriteFontFace3_TryGetFontTable(fontface, MS_GSUB_TAG, &data, &size, &context, &exists); + if (FAILED(hr) || !exists) + return FALSE; + + header = data; + featurelist = (OT_FeatureList*)((BYTE*)header + GET_BE_WORD(header->FeatureList)); + lookup_list = (const OT_LookupList*)((BYTE*)header + GET_BE_WORD(header->LookupList)); + + for (i = 0; i < GET_BE_WORD(featurelist->FeatureCount); i++) { + if (*(UINT32*)featurelist->FeatureRecord[i].FeatureTag == DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING) { + const OT_Feature *feature = (const OT_Feature*)((BYTE*)featurelist + GET_BE_WORD(featurelist->FeatureRecord[i].Feature)); + UINT16 lookup_count = GET_BE_WORD(feature->LookupCount), index, count, type; + const GSUB_SingleSubstFormat2 *subst2; + const OT_LookupTable *lookup_table; + UINT32 offset; + + if (lookup_count == 0) + continue; + + /* check if lookup is empty */ + index = GET_BE_WORD(feature->LookupListIndex[0]); + lookup_table = (const OT_LookupTable*)((BYTE*)lookup_list + GET_BE_WORD(lookup_list->Lookup[index])); + + type = GET_BE_WORD(lookup_table->LookupType); + if (type != OPENTYPE_GPOS_SINGLE_SUBST && type != OPENTYPE_GPOS_EXTENSION_SUBST) + continue; + + count = GET_BE_WORD(lookup_table->SubTableCount); + if (count == 0) + continue; + + offset = GET_BE_WORD(lookup_table->SubTable[0]); + if (type == OPENTYPE_GPOS_EXTENSION_SUBST) { + const GSUB_ExtensionPosFormat1 *ext = (const GSUB_ExtensionPosFormat1 *)((const BYTE *)lookup_table + offset); + if (GET_BE_WORD(ext->SubstFormat) == 1) + offset += GET_BE_DWORD(ext->ExtensionOffset); + else + FIXME("Unhandled Extension Substitution Format %u\n", GET_BE_WORD(ext->SubstFormat)); + } + + subst2 = (const GSUB_SingleSubstFormat2*)((BYTE*)lookup_table + offset); + index = GET_BE_WORD(subst2->SubstFormat); + if (index == 1) + FIXME("Validate Single Substitution Format 1\n"); + else if (index == 2) { + /* SimSun-ExtB has 0 glyph count for this substitution */ + if (GET_BE_WORD(subst2->GlyphCount) > 0) { + ret = TRUE; + break; + } + } + else + WARN("Unknown Single Substitution Format, %u\n", index); + } + } + + IDWriteFontFace3_ReleaseFontTable(fontface, context); + + return ret; +} diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index e211712991d..4fe494ab95a 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -40,6 +40,7 @@ #define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d') #define MS_HHEA_TAG DWRITE_MAKE_OPENTYPE_TAG('h','h','e','a') #define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t') +#define MS_GSUB_TAG DWRITE_MAKE_OPENTYPE_TAG('G','S','U','B') #ifdef WORDS_BIGENDIAN #define GET_BE_WORD(x) (x) @@ -220,6 +221,60 @@ typedef struct { USHORT numberOfHMetrics; } TT_HHEA; +typedef struct { + DWORD version; + WORD ScriptList; + WORD FeatureList; + WORD LookupList; +} GSUB_Header; + +typedef struct { + CHAR FeatureTag[4]; + WORD Feature; +} OT_FeatureRecord; + +typedef struct { + WORD FeatureCount; + OT_FeatureRecord FeatureRecord[1]; +} OT_FeatureList; + +typedef struct { + WORD FeatureParams; + WORD LookupCount; + WORD LookupListIndex[1]; +} OT_Feature; + +typedef struct { + WORD LookupCount; + WORD Lookup[1]; +} OT_LookupList; + +typedef struct { + WORD LookupType; + WORD LookupFlag; + WORD SubTableCount; + WORD SubTable[1]; +} OT_LookupTable; + +typedef struct { + WORD SubstFormat; + WORD Coverage; + WORD DeltaGlyphID; +} GSUB_SingleSubstFormat1; + +typedef struct { + WORD SubstFormat; + WORD Coverage; + WORD GlyphCount; + WORD Substitute[1]; +} GSUB_SingleSubstFormat2; + +typedef struct { + WORD SubstFormat; + WORD ExtensionLookupType; + DWORD ExtensionOffset; +} GSUB_ExtensionPosFormat1; + #include "poppack.h" static IDWriteFactory *create_factory(void) @@ -6300,6 +6355,153 @@ static void test_font_properties(void) IDWriteFactory_Release(factory); } +static BOOL has_vertical_glyph_variants(IDWriteFontFace1 *fontface) +{ + const OT_FeatureList *featurelist; + const OT_LookupList *lookup_list; + BOOL exists = FALSE, ret = FALSE; + const GSUB_Header *header; + const void *data; + void *context; + UINT32 size; + HRESULT hr; + UINT16 i; + + hr = IDWriteFontFace1_TryGetFontTable(fontface, MS_GSUB_TAG, &data, &size, &context, &exists); + ok(hr == S_OK, "got 0x%08x\n", hr); + + if (!exists) + return FALSE; + + header = data; + featurelist = (OT_FeatureList*)((BYTE*)header + GET_BE_WORD(header->FeatureList)); + lookup_list = (const OT_LookupList*)((BYTE*)header + GET_BE_WORD(header->LookupList)); + + for (i = 0; i < GET_BE_WORD(featurelist->FeatureCount); i++) { + if (*(UINT32*)featurelist->FeatureRecord[i].FeatureTag == DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING) { + const OT_Feature *feature = (const OT_Feature*)((BYTE*)featurelist + GET_BE_WORD(featurelist->FeatureRecord[i].Feature)); + UINT16 lookup_count = GET_BE_WORD(feature->LookupCount), index, count, type; + const GSUB_SingleSubstFormat2 *subst2; + const OT_LookupTable *lookup_table; + UINT32 offset; + + if (lookup_count == 0) + continue; + + ok(lookup_count == 1, "got lookup count %u\n", lookup_count); + + /* check if lookup is empty */ + index = GET_BE_WORD(feature->LookupListIndex[0]); + lookup_table = (const OT_LookupTable*)((BYTE*)lookup_list + GET_BE_WORD(lookup_list->Lookup[index])); + + type = GET_BE_WORD(lookup_table->LookupType); + ok(type == 1 || type == 7, "got unexpected lookup type %u\n", type); + + + count = GET_BE_WORD(lookup_table->SubTableCount); + if (count == 0) + continue; + + ok(count > 0, "got unexpected subtable count %u\n", count); + + offset = GET_BE_WORD(lookup_table->SubTable[0]); + if (type == 7) { + const GSUB_ExtensionPosFormat1 *ext = (const GSUB_ExtensionPosFormat1 *)((const BYTE *)lookup_table + offset); + if (GET_BE_WORD(ext->SubstFormat) == 1) + offset += GET_BE_DWORD(ext->ExtensionOffset); + else + ok(0, "Unhandled Extension Substitution Format %u\n", GET_BE_WORD(ext->SubstFormat)); + } + + subst2 = (const GSUB_SingleSubstFormat2*)((BYTE*)lookup_table + offset); + index = GET_BE_WORD(subst2->SubstFormat); + if (index == 1) + ok(0, "validate Single Substitution Format 1\n"); + else if (index == 2) { + /* SimSun-ExtB has 0 glyph count for this substitution */ + if (GET_BE_WORD(subst2->GlyphCount) > 0) { + ret = TRUE; + break; + } + } + else + ok(0, "unknown Single Substitution Format, %u\n", index); + } + } + + IDWriteFontFace1_ReleaseFontTable(fontface, context); + + return ret; +} + +static void test_HasVerticalGlyphVariants(void) +{ + IDWriteFontCollection *syscollection; + IDWriteFontFace1 *fontface1; + IDWriteFontFace *fontface; + IDWriteFactory *factory; + UINT32 count, i; + HRESULT hr; + + factory = create_factory(); + fontface = create_fontface(factory); + + hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1); + IDWriteFontFace_Release(fontface); + if (hr != S_OK) { + win_skip("HasVerticalGlyphVariants() is not supported.\n"); + IDWriteFactory_Release(factory); + return; + } + IDWriteFontFace1_Release(fontface1); + + hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE); + ok(hr == S_OK, "got 0x%08x\n", hr); + count = IDWriteFontCollection_GetFontFamilyCount(syscollection); + + for (i = 0; i < count; i++) { + IDWriteLocalizedStrings *names; + BOOL expected_vert, has_vert; + IDWriteFontFamily *family; + IDWriteFont *font; + WCHAR nameW[256]; + + hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteFontFamily_GetFirstMatchingFont(family, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, + DWRITE_FONT_STYLE_NORMAL, &font); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteFont_CreateFontFace(font, &fontface); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteFontFamily_GetFamilyNames(family, &names); + ok(hr == S_OK, "got 0x%08x\n", hr); + + get_enus_string(names, nameW, sizeof(nameW)/sizeof(nameW[0])); + + expected_vert = has_vertical_glyph_variants(fontface1); + has_vert = IDWriteFontFace1_HasVerticalGlyphVariants(fontface1); + + ok(expected_vert == has_vert, "%s: expected vertical feature %d, got %d\n", + wine_dbgstr_w(nameW), expected_vert, has_vert); + + IDWriteLocalizedStrings_Release(names); + IDWriteFont_Release(font); + + IDWriteFontFace1_Release(fontface1); + IDWriteFontFace_Release(fontface); + IDWriteFontFamily_Release(family); + } + + IDWriteFontCollection_Release(syscollection); + IDWriteFactory_Release(factory); +} + START_TEST(font) { IDWriteFactory *factory; @@ -6357,6 +6559,7 @@ START_TEST(font) test_CreateFontFaceReference(); test_GetFontSignature(); test_font_properties(); + test_HasVerticalGlyphVariants(); IDWriteFactory_Release(factory); }