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:
parent
d1c7348997
commit
3d1d9a1def
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue