[PATCH 2/5] dwrite: Resolve script and language to apply positional features.
Nikolay Sivov
nsivov at codeweavers.com
Thu Jan 31 02:35:27 CST 2019
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
dlls/dwrite/analyzer.c | 6 ++
dlls/dwrite/dwrite_private.h | 27 ++++++
dlls/dwrite/opentype.c | 181 +++++++++++++++++++++++++++--------
dlls/dwrite/shape.c | 81 +++++++++++++++-
4 files changed, 255 insertions(+), 40 deletions(-)
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c
index 13d3d3216a..13c1941d75 100644
--- a/dlls/dwrite/analyzer.c
+++ b/dlls/dwrite/analyzer.c
@@ -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);
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h
index 2299a23cf1..d992abda2a 100644
--- a/dlls/dwrite/dwrite_private.h
+++ b/dlls/dwrite/dwrite_private.h
@@ -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);
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c
index f92febd2e0..7d3db16049 100644
--- a/dlls/dwrite/opentype.c
+++ b/dlls/dwrite/opentype.c
@@ -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 */
+}
diff --git a/dlls/dwrite/shape.c b/dlls/dwrite/shape.c
index 69780450e9..831c7754fc 100644
--- a/dlls/dwrite/shape.c
+++ b/dlls/dwrite/shape.c
@@ -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;
}
--
2.20.1
More information about the wine-devel
mailing list