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