[PATCH 2/5] dwrite: Resolve script and language to apply positional features.

Nikolay Sivov nsivov at codeweavers.com
Thu Jan 31 02:35:27 CST 2019


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 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 13d3d3216a..13c1941d75 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 2299a23cf1..d992abda2a 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 f92febd2e0..7d3db16049 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 69780450e9..831c7754fc 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;
 }
-- 
2.20.1




More information about the wine-devel mailing list