[PATCH 1/5] dwrite: Sort feature tags returned from GetTypographicFeatures().

Nikolay Sivov nsivov at codeweavers.com
Mon Jun 8 08:29:43 CDT 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/dwrite/analyzer.c       |  24 +++-----
 dlls/dwrite/dwrite_private.h |  28 +++++++---
 dlls/dwrite/opentype.c       | 103 +++++++++++++++++------------------
 dlls/dwrite/shape.c          |  47 ++++++++++++++++
 dlls/dwrite/tests/analyzer.c |  50 +++++------------
 5 files changed, 137 insertions(+), 115 deletions(-)

diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c
index a8a3e7c359b..10c59f1bdca 100644
--- a/dlls/dwrite/analyzer.c
+++ b/dlls/dwrite/analyzer.c
@@ -1767,29 +1767,23 @@ static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnal
     IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
     UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
 {
+    struct scriptshaping_context context = { 0 };
     const struct dwritescript_properties *props;
-    const DWORD *scripts;
-    HRESULT hr = S_OK;
-    UINT32 language;
+    struct dwrite_fontface *font_obj;
 
-    TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
-        tags);
+    TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), max_tagcount,
+            actual_tagcount, tags);
 
     if (sa.script > Script_LastId)
         return E_INVALIDARG;
 
-    language = get_opentype_language(locale);
-    props = &dwritescripts_properties[sa.script];
-    *actual_tagcount = 0;
+    font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
 
-    scripts = props->scripttags;
-    while (*scripts && !*actual_tagcount)
-    {
-        hr = opentype_get_typographic_features(fontface, *scripts, language, max_tagcount, actual_tagcount, tags);
-        scripts++;
-    }
+    context.cache = fontface_get_shaping_cache(font_obj);
+    context.language_tag = get_opentype_language(locale);
+    props = &dwritescripts_properties[sa.script];
 
-    return hr;
+    return shape_get_typographic_features(&context, props->scripttags, max_tagcount, actual_tagcount, tags);
 };
 
 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h
index f8b7e0d139d..246dab69327 100644
--- a/dlls/dwrite/dwrite_private.h
+++ b/dlls/dwrite/dwrite_private.h
@@ -330,6 +330,21 @@ struct file_stream_desc {
 extern const void* get_fontface_table(IDWriteFontFace5 *fontface, UINT32 tag,
         struct dwrite_fonttable *table) DECLSPEC_HIDDEN;
 
+struct tag_array
+{
+    unsigned int *tags;
+    size_t capacity;
+    size_t count;
+};
+
+struct ot_gsubgpos_table
+{
+    struct dwrite_fonttable table;
+    unsigned int script_list;
+    unsigned int feature_list;
+    unsigned int lookup_list;
+};
+
 extern HRESULT opentype_analyze_font(IDWriteFontFileStream*,BOOL*,DWRITE_FONT_FILE_TYPE*,DWRITE_FONT_FACE_TYPE*,UINT32*) DECLSPEC_HIDDEN;
 extern HRESULT opentype_try_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag, const void **data,
         void **context, UINT32 *size, BOOL *exists) DECLSPEC_HIDDEN;
@@ -343,7 +358,8 @@ extern HRESULT opentype_get_font_info_strings(const struct file_stream_desc *str
         DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **strings) DECLSPEC_HIDDEN;
 extern HRESULT opentype_get_font_familyname(struct file_stream_desc*,IDWriteLocalizedStrings**) DECLSPEC_HIDDEN;
 extern HRESULT opentype_get_font_facename(struct file_stream_desc*,WCHAR*,IDWriteLocalizedStrings**) DECLSPEC_HIDDEN;
-extern HRESULT opentype_get_typographic_features(IDWriteFontFace*,UINT32,UINT32,UINT32,UINT32*,DWRITE_FONT_FEATURE_TAG*) DECLSPEC_HIDDEN;
+extern void opentype_get_typographic_features(struct ot_gsubgpos_table *table, unsigned int script_index,
+        unsigned int language_index, struct tag_array *tags) DECLSPEC_HIDDEN;
 extern BOOL opentype_get_vdmx_size(const struct dwrite_fonttable *table, INT ppem, UINT16 *ascent,
         UINT16 *descent) DECLSPEC_HIDDEN;
 extern unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable *table) DECLSPEC_HIDDEN;
@@ -438,14 +454,6 @@ enum SCRIPT_JUSTIFY
     SCRIPT_JUSTIFY_ARABIC_SEEN_M
 };
 
-struct ot_gsubgpos_table
-{
-    struct dwrite_fonttable table;
-    unsigned int script_list;
-    unsigned int feature_list;
-    unsigned int lookup_list;
-};
-
 struct scriptshaping_cache
 {
     const struct shaping_font_ops *font;
@@ -586,3 +594,5 @@ extern void opentype_layout_apply_gpos_features(struct scriptshaping_context *co
 
 extern HRESULT shape_get_glyphs(struct scriptshaping_context *context, const unsigned int *scripts) DECLSPEC_HIDDEN;
 extern HRESULT shape_get_positions(struct scriptshaping_context *context, const unsigned int *scripts) DECLSPEC_HIDDEN;
+extern HRESULT shape_get_typographic_features(struct scriptshaping_context *context, const unsigned int *scripts,
+        unsigned int max_tagcount, unsigned int *actual_tagcount, unsigned int *tags) DECLSPEC_HIDDEN;
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c
index 2106fe2cf78..a07d12c0f8c 100644
--- a/dlls/dwrite/opentype.c
+++ b/dlls/dwrite/opentype.c
@@ -2467,74 +2467,69 @@ static inline const struct ot_script *opentype_get_script(const struct ot_script
     return NULL;
 }
 
-static inline const struct ot_langsys *opentype_get_langsys(const struct ot_script *script, UINT32 languagetag)
+static const struct ot_langsys *opentype_get_langsys(const struct ot_gsubgpos_table *table, unsigned int script_index,
+        unsigned int language_index, unsigned int *feature_count)
 {
-    UINT16 j;
-
-    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 (struct ot_langsys *)((BYTE*)script + GET_BE_WORD(script->langsys[j].langsys));
-    }
-
-    return NULL;
-}
+    unsigned int table_offset, langsys_offset;
+    const struct ot_langsys *langsys = NULL;
 
-static void opentype_add_font_features(const struct gpos_gsub_header *header, const struct ot_langsys *langsys,
-    UINT32 max_tagcount, UINT32 *count, DWRITE_FONT_FEATURE_TAG *tags)
-{
-    const struct ot_feature_list *features = (const struct ot_feature_list *)((const BYTE*)header + GET_BE_WORD(header->feature_list));
-    UINT16 j;
+    *feature_count = 0;
 
-    for (j = 0; j < GET_BE_WORD(langsys->feature_count); j++) {
-        const struct ot_feature_record *feature = &features->features[langsys->feature_index[j]];
+    if (!table->table.data || script_index == ~0u)
+        return NULL;
 
-        if (*count < max_tagcount)
-            tags[*count] = GET_BE_DWORD(feature->tag);
+    /* ScriptTable offset. */
+    table_offset = table_read_be_word(&table->table, table->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 NULL;
 
-        (*count)++;
-    }
+    if (language_index == ~0u)
+        langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
+    else
+        langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
+                FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
+                FIELD_OFFSET(struct ot_langsys_record, langsys));
+    langsys_offset += table->script_list + table_offset;
+
+    *feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
+    if (*feature_count)
+        langsys = table_read_ensure(&table->table, langsys_offset, FIELD_OFFSET(struct ot_langsys, feature_index[*feature_count]));
+    if (!langsys)
+        *feature_count = 0;
+
+    return langsys;
 }
 
-HRESULT opentype_get_typographic_features(IDWriteFontFace *fontface, UINT32 scripttag, UINT32 languagetag, UINT32 max_tagcount,
-    UINT32 *count, DWRITE_FONT_FEATURE_TAG *tags)
+void opentype_get_typographic_features(struct ot_gsubgpos_table *table, unsigned int script_index,
+        unsigned int language_index, struct tag_array *t)
 {
-    UINT32 tables[2] = { MS_GSUB_TAG, MS_GPOS_TAG };
-    HRESULT hr;
-    UINT8 i;
-
-    *count = 0;
-    for (i = 0; i < ARRAY_SIZE(tables); i++) {
-        const struct ot_script_list *scriptlist;
-        const struct gpos_gsub_header *header;
-        const struct ot_script *script;
-        const void *ptr;
-        void *context;
-        UINT32 size;
-        BOOL exists;
+    unsigned int i, total_feature_count, script_feature_count;
+    const struct ot_feature_list *feature_list;
+    const struct ot_langsys *langsys = NULL;
 
-        exists = FALSE;
-        hr = IDWriteFontFace_TryGetFontTable(fontface, tables[i], &ptr, &size, &context, &exists);
-        if (FAILED(hr))
-            return hr;
+    langsys = opentype_get_langsys(table, script_index, language_index, &script_feature_count);
 
-        if (!exists)
-            continue;
+    total_feature_count = table_read_be_word(&table->table, table->feature_list);
+    if (!total_feature_count)
+        return;
 
-        header = (const struct gpos_gsub_header *)ptr;
-        scriptlist = (const struct ot_script_list *)((const BYTE*)header + GET_BE_WORD(header->script_list));
+    feature_list = table_read_ensure(&table->table, table->feature_list,
+            FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
+    if (!feature_list)
+        return;
 
-        script = opentype_get_script(scriptlist, scripttag);
-        if (script) {
-            const struct ot_langsys *langsys = opentype_get_langsys(script, languagetag);
-            if (langsys)
-                opentype_add_font_features(header, langsys, max_tagcount, count, tags);
-        }
+    for (i = 0; i < script_feature_count; ++i)
+    {
+        unsigned int feature_index = GET_BE_WORD(langsys->feature_index[i]);
+        if (feature_index >= total_feature_count)
+            continue;
 
-        IDWriteFontFace_ReleaseFontTable(fontface, context);
-    }
+        if (!dwrite_array_reserve((void **)&t->tags, &t->capacity, t->count + 1, sizeof(*t->tags)))
+            return;
 
-    return *count > max_tagcount ? E_NOT_SUFFICIENT_BUFFER : S_OK;
+        t->tags[t->count++] = feature_list->features[feature_index].tag;
+    }
 }
 
 static unsigned int find_vdmx_group(const struct vdmx_header *hdr)
diff --git a/dlls/dwrite/shape.c b/dlls/dwrite/shape.c
index 32aabac5f68..4968cd995d4 100644
--- a/dlls/dwrite/shape.c
+++ b/dlls/dwrite/shape.c
@@ -22,11 +22,18 @@
 #define COBJMACROS
 
 #include "dwrite_private.h"
+#include "winternl.h"
 
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
 
+#ifdef WORDS_BIGENDIAN
+#define GET_BE_DWORD(x) (x)
+#else
+#define GET_BE_DWORD(x) RtlUlongByteSwap(x)
+#endif
+
 struct scriptshaping_cache *create_scriptshaping_cache(void *context, const struct shaping_font_ops *font_ops)
 {
     struct scriptshaping_cache *cache;
@@ -303,3 +310,43 @@ HRESULT shape_get_glyphs(struct scriptshaping_context *context, const unsigned i
 
     return (context->glyph_count <= context->u.subst.max_glyph_count) ? S_OK : E_NOT_SUFFICIENT_BUFFER;
 }
+
+static int tag_array_sorting_compare(const void *a, const void *b)
+{
+    unsigned int left = GET_BE_DWORD(*(unsigned int *)a), right = GET_BE_DWORD(*(unsigned int *)b);
+    return left != right ? (left < right ? -1 : 1) : 0;
+};
+
+HRESULT shape_get_typographic_features(struct scriptshaping_context *context, const unsigned int *scripts,
+        unsigned int max_tagcount, unsigned int *actual_tagcount, unsigned int *tags)
+{
+    unsigned int i, j, script_index, language_index;
+    struct tag_array t = { 0 };
+
+    /* Collect from both tables, sort and remove duplicates. */
+
+    shape_get_script_lang_index(context, scripts, MS_GSUB_TAG, &script_index, &language_index);
+    opentype_get_typographic_features(&context->cache->gsub, script_index, language_index, &t);
+
+    shape_get_script_lang_index(context, scripts, MS_GPOS_TAG, &script_index, &language_index);
+    opentype_get_typographic_features(&context->cache->gpos, script_index, language_index, &t);
+
+    /* Sort and remove duplicates. */
+    qsort(t.tags, t.count, sizeof(*t.tags), tag_array_sorting_compare);
+
+    for (i = 1, j = 0; i < t.count; ++i)
+    {
+        if (t.tags[i] != t.tags[j])
+            t.tags[++j] = t.tags[i];
+    }
+    t.count = j + 1;
+
+    if (t.count <= max_tagcount)
+        memcpy(tags, t.tags, t.count * sizeof(*t.tags));
+
+    *actual_tagcount = t.count;
+
+    heap_free(t.tags);
+
+    return t.count <= max_tagcount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
+}
diff --git a/dlls/dwrite/tests/analyzer.c b/dlls/dwrite/tests/analyzer.c
index 70becb55ee5..6dd251270c7 100644
--- a/dlls/dwrite/tests/analyzer.c
+++ b/dlls/dwrite/tests/analyzer.c
@@ -1795,15 +1795,6 @@ if (0) {
     IDWriteTextAnalyzer_Release(analyzer);
 }
 
-static BOOL has_feature(const DWRITE_FONT_FEATURE_TAG *tags, UINT32 count, DWRITE_FONT_FEATURE_TAG feature)
-{
-    UINT32 i;
-
-    for (i = 0; i < count; i++)
-        if (tags[i] == feature) return TRUE;
-    return FALSE;
-}
-
 static void test_GetTypographicFeatures(void)
 {
     static const WCHAR arabicW[] = {0x064a,0x064f,0x0633,0};
@@ -1814,7 +1805,6 @@ static void test_GetTypographicFeatures(void)
     DWRITE_SCRIPT_ANALYSIS sa;
     UINT32 count;
     HRESULT hr;
-    BOOL ret;
 
     hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
     ok(hr == S_OK, "got 0x%08x\n", hr);
@@ -1831,45 +1821,31 @@ static void test_GetTypographicFeatures(void)
     get_script_analysis(L"abc", &sa);
     count = 0;
     hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, 0, &count, NULL);
-todo_wine {
-    ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
-    ok(count > 0, "got %u\n", count);
-}
+    ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#x.\n", hr);
+    ok(!!count, "Unexpected count %u.\n", count);
+
     /* invalid locale name is ignored */
     get_script_analysis(L"abc", &sa);
     count = 0;
     hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, L"cadabra", 0, &count, NULL);
-todo_wine {
-    ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
-    ok(count > 0, "got %u\n", count);
-}
-    /* both GSUB and GPOS features are reported */
+    ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#x.\n", hr);
+    ok(!!count, "Unexpected count %u.\n", count);
+
+    /* Make some calls for different scripts. */
+
     get_script_analysis(arabicW, &sa);
     memset(tags, 0, sizeof(tags));
     count = 0;
     hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
-    ok(hr == S_OK, "got 0x%08x\n", hr);
-todo_wine {
-    ok(count > 0, "got %u\n", count);
-    ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
-    ok(ret, "expected 'calt' feature\n");
-    ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
-    ok(ret, "expected 'mkmk' feature\n");
-}
+    ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+    ok(!!count, "Unexpected count %u.\n", count);
+
     get_script_analysis(L"abc", &sa);
     memset(tags, 0, sizeof(tags));
     count = 0;
     hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
-    ok(hr == S_OK, "got 0x%08x\n", hr);
-todo_wine {
-    ok(count > 0, "got %u\n", count);
-    ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_GLYPH_COMPOSITION_DECOMPOSITION);
-    ok(ret, "expected 'ccmp' feature\n");
-    ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
-    ok(ret, "expected 'mkmk' feature\n");
-}
-    ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
-    ok(!ret, "unexpected 'calt' feature\n");
+    ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+    ok(!!count, "Unexpected count %u.\n", count);
 
     IDWriteFontFace_Release(fontface);
     IDWriteTextAnalyzer2_Release(analyzer2);
-- 
2.26.2




More information about the wine-devel mailing list