dwrite: Resolve script and language to apply positional features.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2019-01-31 11:35:27 +03:00 committed by Alexandre Julliard
parent d1c7348997
commit 3d1d9a1def
4 changed files with 255 additions and 40 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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 */
}

View File

@ -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;
}