Signed-off-by: Nikolay Sivov <nsivov(a)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