From 3d1d9a1defef2cca14458e1622525e7a965e048c Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Thu, 31 Jan 2019 11:35:27 +0300 Subject: [PATCH] dwrite: Resolve script and language to apply positional features. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/dwrite/analyzer.c | 6 ++ dlls/dwrite/dwrite_private.h | 27 ++++++ dlls/dwrite/opentype.c | 181 +++++++++++++++++++++++++++-------- dlls/dwrite/shape.c | 81 +++++++++++++++- 4 files changed, 255 insertions(+), 40 deletions(-) diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index 13d3d3216a9..13c1941d75a 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -1313,6 +1313,9 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 context.text = text; context.length = text_len; context.is_rtl = is_rtl; + context.u.pos.glyph_props = glyph_props; + context.glyph_count = glyph_count; + context.advances = advances; context.language_tag = get_opentype_language(locale); hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features); @@ -1371,6 +1374,9 @@ static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWrite context.text = text; context.length = text_len; context.is_rtl = is_rtl; + context.u.pos.glyph_props = glyph_props; + context.glyph_count = glyph_count; + context.advances = advances; context.language_tag = get_opentype_language(locale); hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features); diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 2299a23cf1e..d992abda2a0 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -331,6 +331,14 @@ struct scriptshaping_cache const struct shaping_font_ops *font; void *context; UINT16 upem; + + struct + { + struct dwrite_fonttable table; + unsigned int script_list; + unsigned int feature_list; + unsigned int lookup_list; + } gpos; }; struct scriptshaping_context @@ -341,6 +349,17 @@ struct scriptshaping_context const WCHAR *text; unsigned int length; BOOL is_rtl; + + union + { + struct + { + const DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props; + } pos; + } u; + + unsigned int glyph_count; + float *advances; }; struct shaping_font_ops @@ -361,6 +380,14 @@ struct shaping_features unsigned int count; }; +extern void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache) DECLSPEC_HIDDEN; +extern DWORD opentype_layout_find_script(const struct scriptshaping_cache *cache, DWORD kind, DWORD tag, + unsigned int *script_index) DECLSPEC_HIDDEN; +extern DWORD opentype_layout_find_language(const struct scriptshaping_cache *cache, DWORD kind, DWORD tag, + unsigned int script_index, unsigned int *language_index) DECLSPEC_HIDDEN; +extern void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index, + unsigned int language_index, const struct shaping_features *features) DECLSPEC_HIDDEN; + struct scriptshaping_ops { HRESULT (*contextual_shaping)(struct scriptshaping_context *context, UINT16 *clustermap, UINT16 *glyph_indices, UINT32* actual_glyph_count); diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index f92febd2e03..7d3db16049f 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -390,33 +390,38 @@ typedef struct { WORD FeatureIndex[1]; } OT_LangSys; -typedef struct { - CHAR LangSysTag[4]; - WORD LangSys; -} OT_LangSysRecord; +struct ot_langsys_record +{ + CHAR tag[4]; + WORD langsys; +}; -typedef struct { - WORD DefaultLangSys; - WORD LangSysCount; - OT_LangSysRecord LangSysRecord[1]; +struct ot_script +{ + WORD default_langsys; + WORD langsys_count; + struct ot_langsys_record langsys[1]; } OT_Script; -typedef struct { - CHAR ScriptTag[4]; - WORD Script; -} OT_ScriptRecord; +struct ot_script_record +{ + CHAR tag[4]; + WORD script; +}; -typedef struct { - WORD ScriptCount; - OT_ScriptRecord ScriptRecord[1]; -} OT_ScriptList; +struct ot_script_list +{ + WORD script_count; + struct ot_script_record scripts[1]; +}; -typedef struct { +struct gpos_gsub_header +{ DWORD version; - WORD ScriptList; - WORD FeatureList; - WORD LookupList; -} GPOS_GSUB_Header; + WORD script_list; + WORD feature_list; + WORD lookup_list; +}; enum OPENTYPE_PLATFORM_ID { @@ -911,6 +916,12 @@ static DWORD table_read_be_dword(const struct dwrite_fonttable *table, unsigned return ptr ? GET_BE_DWORD(*ptr) : 0; } +static DWORD table_read_dword(const struct dwrite_fonttable *table, unsigned int offset) +{ + const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr)); + return ptr ? *ptr : 0; +} + BOOL is_face_type_supported(DWRITE_FONT_FACE_TYPE type) { return (type == DWRITE_FONT_FACE_TYPE_CFF) || @@ -1828,36 +1839,36 @@ HRESULT opentype_get_font_facename(struct file_stream_desc *stream_desc, WCHAR * return hr; } -static inline const OT_Script *opentype_get_script(const OT_ScriptList *scriptlist, UINT32 scripttag) +static inline const struct ot_script *opentype_get_script(const struct ot_script_list *scriptlist, UINT32 scripttag) { UINT16 j; - for (j = 0; j < GET_BE_WORD(scriptlist->ScriptCount); j++) { - const char *tag = scriptlist->ScriptRecord[j].ScriptTag; + for (j = 0; j < GET_BE_WORD(scriptlist->script_count); j++) { + const char *tag = scriptlist->scripts[j].tag; if (scripttag == DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3])) - return (OT_Script*)((BYTE*)scriptlist + GET_BE_WORD(scriptlist->ScriptRecord[j].Script)); + return (struct ot_script*)((BYTE*)scriptlist + GET_BE_WORD(scriptlist->scripts[j].script)); } return NULL; } -static inline const OT_LangSys *opentype_get_langsys(const OT_Script *script, UINT32 languagetag) +static inline const OT_LangSys *opentype_get_langsys(const struct ot_script *script, UINT32 languagetag) { UINT16 j; - for (j = 0; j < GET_BE_WORD(script->LangSysCount); j++) { - const char *tag = script->LangSysRecord[j].LangSysTag; + for (j = 0; j < GET_BE_WORD(script->langsys_count); j++) { + const char *tag = script->langsys[j].tag; if (languagetag == DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3])) - return (OT_LangSys*)((BYTE*)script + GET_BE_WORD(script->LangSysRecord[j].LangSys)); + return (OT_LangSys*)((BYTE*)script + GET_BE_WORD(script->langsys[j].langsys)); } return NULL; } -static void opentype_add_font_features(const GPOS_GSUB_Header *header, const OT_LangSys *langsys, +static void opentype_add_font_features(const struct gpos_gsub_header *header, const OT_LangSys *langsys, UINT32 max_tagcount, UINT32 *count, DWRITE_FONT_FEATURE_TAG *tags) { - const OT_FeatureList *features = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList)); + const OT_FeatureList *features = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->feature_list)); UINT16 j; for (j = 0; j < GET_BE_WORD(langsys->FeatureCount); j++) { @@ -1880,9 +1891,9 @@ HRESULT opentype_get_typographic_features(IDWriteFontFace *fontface, UINT32 scri *count = 0; for (i = 0; i < ARRAY_SIZE(tables); i++) { - const OT_ScriptList *scriptlist; - const GPOS_GSUB_Header *header; - const OT_Script *script; + const struct ot_script_list *scriptlist; + const struct gpos_gsub_header *header; + const struct ot_script *script; const void *ptr; void *context; UINT32 size; @@ -1896,8 +1907,8 @@ HRESULT opentype_get_typographic_features(IDWriteFontFace *fontface, UINT32 scri if (!exists) continue; - header = (const GPOS_GSUB_Header*)ptr; - scriptlist = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList)); + header = (const struct gpos_gsub_header *)ptr; + scriptlist = (const struct ot_script_list *)((const BYTE*)header + GET_BE_WORD(header->script_list)); script = opentype_get_script(scriptlist, scripttag); if (script) { @@ -2150,10 +2161,10 @@ void opentype_colr_next_glyph(const struct dwrite_fonttable *colr, struct dwrite BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) { + const struct gpos_gsub_header *header; 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; @@ -2165,8 +2176,8 @@ BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) 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)); + featurelist = (OT_FeatureList*)((BYTE*)header + GET_BE_WORD(header->feature_list)); + lookup_list = (const OT_LookupList*)((BYTE*)header + GET_BE_WORD(header->lookup_list)); for (i = 0; i < GET_BE_WORD(featurelist->FeatureCount); i++) { if (*(UINT32*)featurelist->FeatureRecord[i].FeatureTag == DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING) { @@ -2396,3 +2407,95 @@ DWRITE_CONTAINER_TYPE opentype_analyze_container_type(void const *data, UINT32 d return DWRITE_CONTAINER_TYPE_UNKNOWN; } } + +void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache) +{ + cache->font->grab_font_table(cache->context, MS_GPOS_TAG, &cache->gpos.table.data, &cache->gpos.table.size, + &cache->gpos.table.context); + + if (cache->gpos.table.data) + { + cache->gpos.script_list = table_read_be_word(&cache->gpos.table, + FIELD_OFFSET(struct gpos_gsub_header, script_list)); + cache->gpos.feature_list = table_read_be_word(&cache->gpos.table, + FIELD_OFFSET(struct gpos_gsub_header, feature_list)); + cache->gpos.lookup_list = table_read_be_word(&cache->gpos.table, + FIELD_OFFSET(struct gpos_gsub_header, lookup_list)); + } +} + +DWORD opentype_layout_find_script(const struct scriptshaping_cache *cache, DWORD kind, DWORD script, + unsigned int *script_index) +{ + WORD script_count; + unsigned int i; + + *script_index = ~0u; + + if (kind != MS_GPOS_TAG) + return 0; + + script_count = table_read_be_word(&cache->gpos.table, cache->gpos.script_list); + if (!script_count) + return 0; + + for (i = 0; i < script_count; i++) + { + DWORD tag = table_read_dword(&cache->gpos.table, cache->gpos.script_list + + FIELD_OFFSET(struct ot_script_list, scripts) + i * sizeof(struct ot_script_record)); + if (!tag) + continue; + + if (tag == script) + { + *script_index = i; + return script; + } + } + + return 0; +} + +DWORD opentype_layout_find_language(const struct scriptshaping_cache *cache, DWORD kind, DWORD language, + unsigned int script_index, unsigned int *language_index) +{ + WORD table_offset, lang_count; + unsigned int i; + + *language_index = ~0u; + + if (kind != MS_GPOS_TAG) + return 0; + + table_offset = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + + FIELD_OFFSET(struct ot_script_list, scripts) + script_index * sizeof(struct ot_script_record) + + FIELD_OFFSET(struct ot_script_record, script)); + if (!table_offset) + return 0; + + lang_count = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset + + FIELD_OFFSET(struct ot_script, langsys_count)); + for (i = 0; i < lang_count; i++) + { + DWORD tag = table_read_dword(&cache->gpos.table, cache->gpos.script_list + table_offset + + FIELD_OFFSET(struct ot_script, langsys) + i * sizeof(struct ot_langsys_record)); + + if (tag == language) + { + *language_index = i; + return language; + } + } + + /* Try 'defaultLangSys' if it's set. */ + if (table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset)) + return ~0u; + + return 0; +} + +void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, + unsigned int script_index, unsigned int language_index, const struct shaping_features *features) +{ + /* FIXME: stub */ +} diff --git a/dlls/dwrite/shape.c b/dlls/dwrite/shape.c index 69780450e97..831c7754fc6 100644 --- a/dlls/dwrite/shape.c +++ b/dlls/dwrite/shape.c @@ -23,6 +23,12 @@ #include "dwrite_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(dwrite); + +#define MS_GPOS_TAG DWRITE_MAKE_OPENTYPE_TAG('G','P','O','S') + struct scriptshaping_cache *create_scriptshaping_cache(void *context, const struct shaping_font_ops *font_ops) { struct scriptshaping_cache *cache; @@ -34,6 +40,7 @@ struct scriptshaping_cache *create_scriptshaping_cache(void *context, const stru cache->font = font_ops; cache->context = context; + opentype_layout_scriptshaping_cache_init(cache); cache->upem = cache->font->get_font_upem(cache->context); return cache; @@ -43,6 +50,8 @@ void release_scriptshaping_cache(struct scriptshaping_cache *cache) { if (!cache) return; + + cache->font->release_font_table(cache->context, cache->gpos.table.context); heap_free(cache); } @@ -180,10 +189,80 @@ const struct scriptshaping_ops default_shaping_ops = default_set_text_glyphs_props }; +static DWORD shape_select_script(const struct scriptshaping_cache *cache, DWORD kind, const DWORD *scripts, + unsigned int *script_index) +{ + static const DWORD fallback_scripts[] = + { + DWRITE_MAKE_OPENTYPE_TAG('D','F','L','T'), + DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t'), + DWRITE_MAKE_OPENTYPE_TAG('l','a','t','n'), + 0, + }; + DWORD script; + + /* Passed scripts in ascending priority. */ + while (*scripts) + { + if ((script = opentype_layout_find_script(cache, kind, *scripts, script_index))) + return script; + + scripts++; + } + + /* 'DFLT' -> 'dflt' -> 'latn' */ + scripts = fallback_scripts; + while (*scripts) + { + if ((script = opentype_layout_find_script(cache, kind, *scripts, script_index))) + return script; + scripts++; + } + + return 0; +} + +static DWORD shape_select_language(const struct scriptshaping_cache *cache, DWORD kind, unsigned int script_index, + DWORD language, unsigned int *language_index) +{ + /* Specified language -> 'dflt'. */ + if ((language = opentype_layout_find_language(cache, kind, language, script_index, language_index))) + return language; + + if ((language = opentype_layout_find_language(cache, kind, DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t'), + script_index, language_index))) + return language; + + return 0; +} + HRESULT shape_get_positions(struct scriptshaping_context *context, const DWORD *scripts, const struct shaping_features *features) { - /* FIXME: stub */ + struct scriptshaping_cache *cache = context->cache; + unsigned int script_index, language_index; + unsigned int i; + DWORD script; + + /* Resolve script tag to actually supported script. */ + if (cache->gpos.table.data) + { + if ((script = shape_select_script(cache, MS_GPOS_TAG, scripts, &script_index))) + { + DWORD language = context->language_tag; + + if ((language = shape_select_language(cache, MS_GPOS_TAG, script_index, language, &language_index))) + { + TRACE("script %s, language %s.\n", debugstr_tag(script), + language != ~0u ? debugstr_tag(language) : "deflangsys"); + opentype_layout_apply_gpos_features(context, script_index, language_index, features); + } + } + } + + for (i = 0; i < context->glyph_count; ++i) + if (context->u.pos.glyph_props[i].isZeroWidthSpace) + context->advances[i] = 0.0f; return S_OK; }