Commit b60796bd authored by Nikolay Sivov's avatar Nikolay Sivov Committed by Alexandre Julliard

dwrite: Sort feature tags returned from GetTypographicFeatures().

parent 59c0dbb6
......@@ -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,
......
......@@ -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;
......@@ -2454,87 +2454,69 @@ HRESULT opentype_get_font_facename(struct file_stream_desc *stream_desc, WCHAR *
return hr;
}
static inline const struct ot_script *opentype_get_script(const struct ot_script_list *scriptlist, UINT32 scripttag)
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;
unsigned int table_offset, langsys_offset;
const struct ot_langsys *langsys = NULL;
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 (struct ot_script*)((BYTE*)scriptlist + GET_BE_WORD(scriptlist->scripts[j].script));
}
*feature_count = 0;
return NULL;
}
static inline const struct ot_langsys *opentype_get_langsys(const struct ot_script *script, UINT32 languagetag)
{
UINT16 j;
if (!table->table.data || script_index == ~0u)
return NULL;
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));
}
/* 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;
return NULL;
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;
}
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)
void opentype_get_typographic_features(struct ot_gsubgpos_table *table, unsigned int script_index,
unsigned int language_index, struct tag_array *t)
{
const struct ot_feature_list *features = (const struct ot_feature_list *)((const BYTE*)header + GET_BE_WORD(header->feature_list));
UINT16 j;
for (j = 0; j < GET_BE_WORD(langsys->feature_count); j++) {
const struct ot_feature_record *feature = &features->features[langsys->feature_index[j]];
if (*count < max_tagcount)
tags[*count] = GET_BE_DWORD(feature->tag);
unsigned int i, total_feature_count, script_feature_count;
const struct ot_feature_list *feature_list;
const struct ot_langsys *langsys = NULL;
(*count)++;
}
}
langsys = opentype_get_langsys(table, script_index, language_index, &script_feature_count);
HRESULT opentype_get_typographic_features(IDWriteFontFace *fontface, UINT32 scripttag, UINT32 languagetag, UINT32 max_tagcount,
UINT32 *count, DWRITE_FONT_FEATURE_TAG *tags)
{
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;
total_feature_count = table_read_be_word(&table->table, table->feature_list);
if (!total_feature_count)
return;
exists = FALSE;
hr = IDWriteFontFace_TryGetFontTable(fontface, tables[i], &ptr, &size, &context, &exists);
if (FAILED(hr))
return hr;
feature_list = table_read_ensure(&table->table, table->feature_list,
FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
if (!feature_list)
return;
if (!exists)
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;
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) {
const struct ot_langsys *langsys = opentype_get_langsys(script, languagetag);
if (langsys)
opentype_add_font_features(header, langsys, max_tagcount, count, tags);
}
if (!dwrite_array_reserve((void **)&t->tags, &t->capacity, t->count + 1, sizeof(*t->tags)))
return;
IDWriteFontFace_ReleaseFontTable(fontface, context);
t->tags[t->count++] = feature_list->features[feature_index].tag;
}
return *count > max_tagcount ? E_NOT_SUFFICIENT_BUFFER : S_OK;
}
static unsigned int find_vdmx_group(const struct vdmx_header *hdr)
......
......@@ -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;
}
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment