Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/analyzer.c | 134 ++++++++++++++++------------------- dlls/dwrite/dwrite_private.h | 36 ++++++++-- dlls/dwrite/font.c | 49 ++++++++++++- dlls/dwrite/shape.c | 48 ++++++++----- 4 files changed, 172 insertions(+), 95 deletions(-)
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index 7ee3675d2e..13d3d3216a 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -1157,16 +1157,14 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface, UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices, DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count) { - const struct dwritescript_properties *scriptprops; DWRITE_NUMBER_SUBSTITUTION_METHOD method; struct scriptshaping_context context; - struct scriptshaping_cache *cache = NULL; + struct dwrite_fontface *font_obj; WCHAR digits[NATIVE_DIGITS_LEN]; BOOL update_cluster; WCHAR *string; UINT32 i, g; HRESULT hr = S_OK; - UINT16 script;
TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length), length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution, @@ -1175,8 +1173,6 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
- script = analysis->script > Script_LastId ? Script_Unknown : analysis->script; - if (max_glyph_count < length) return E_NOT_SUFFICIENT_BUFFER;
@@ -1253,33 +1249,19 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface, } *actual_glyph_count = g;
- hr = create_scriptshaping_cache(fontface, &cache); - if (FAILED(hr)) - goto done; + font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
- context.cache = cache; + context.cache = fontface_get_shaping_cache(font_obj); context.text = text; context.length = length; context.is_rtl = is_rtl; - context.max_glyph_count = max_glyph_count; context.language_tag = get_opentype_language(locale);
- scriptprops = &dwritescripts_properties[script]; - if (scriptprops->ops && scriptprops->ops->contextual_shaping) { - hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count); - if (FAILED(hr)) - goto done; - } - /* FIXME: apply default features */
- if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props) - hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props); - else - hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props); + hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
done: - release_scriptshaping_cache(cache); heap_free(string);
return hr; @@ -1288,14 +1270,14 @@ done: static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface, WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props, UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props, - UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl, + UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, BOOL is_sideways, BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features, - UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets) + UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets) { - DWRITE_FONT_METRICS metrics; - IDWriteFontFace1 *fontface1; - HRESULT hr; - UINT32 i; + const struct dwritescript_properties *scriptprops; + struct dwrite_fontface *font_obj; + unsigned int i, script; + HRESULT hr = S_OK;
TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len), clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways, @@ -1307,46 +1289,51 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 if (glyph_count == 0) return S_OK;
- hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1); - if (FAILED(hr)) { - WARN("failed to get IDWriteFontFace1.\n"); - return hr; - } + font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
- IDWriteFontFace_GetMetrics(fontface, &metrics); - for (i = 0; i < glyph_count; i++) { + for (i = 0; i < glyph_count; ++i) + { if (glyph_props[i].isZeroWidthSpace) advances[i] = 0.0f; - else { - INT32 a; - - hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways); - if (FAILED(hr)) - a = 0; - advances[i] = get_scaled_advance_width(a, emSize, &metrics); - } + else + advances[i] = fontface_get_scaled_design_advance(font_obj, DWRITE_MEASURING_MODE_NATURAL, emSize, 1.0f, + NULL, glyphs[i], is_sideways); offsets[i].advanceOffset = 0.0f; offsets[i].ascenderOffset = 0.0f; }
- /* FIXME: actually apply features */ + script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
- IDWriteFontFace1_Release(fontface1); - return S_OK; + scriptprops = &dwritescripts_properties[script]; + if (scriptprops->ops && scriptprops->ops->gpos_features) + { + struct scriptshaping_context context; + + context.cache = fontface_get_shaping_cache(font_obj); + context.text = text; + context.length = text_len; + context.is_rtl = is_rtl; + context.language_tag = get_opentype_language(locale); + + hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features); + } + + return hr; }
static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface, WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props, UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props, - UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip, + UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, float ppdip, DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features, - UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets) + UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets) { - DWRITE_FONT_METRICS metrics; - IDWriteFontFace1 *fontface1; - HRESULT hr; - UINT32 i; + const struct dwritescript_properties *scriptprops; + DWRITE_MEASURING_MODE measuring_mode; + struct dwrite_fontface *font_obj; + unsigned int i, script; + HRESULT hr = S_OK;
TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len), clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip, @@ -1358,35 +1345,38 @@ static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWrite if (glyph_count == 0) return S_OK;
- hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1); - if (FAILED(hr)) { - WARN("failed to get IDWriteFontFace1.\n"); - return hr; - } + font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
- hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics); - if (FAILED(hr)) { - IDWriteFontFace1_Release(fontface1); - WARN("failed to get compat metrics, 0x%08x\n", hr); - return hr; - } - for (i = 0; i < glyph_count; i++) { - INT32 a; + measuring_mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
- hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip, - transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a); - if (FAILED(hr)) + for (i = 0; i < glyph_count; ++i) + { + if (glyph_props[i].isZeroWidthSpace) advances[i] = 0.0f; else - advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip; + advances[i] = fontface_get_scaled_design_advance(font_obj, measuring_mode, emSize, ppdip, + transform, glyphs[i], is_sideways); offsets[i].advanceOffset = 0.0f; offsets[i].ascenderOffset = 0.0f; }
- /* FIXME: actually apply features */ + script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
- IDWriteFontFace1_Release(fontface1); - return S_OK; + scriptprops = &dwritescripts_properties[script]; + if (scriptprops->ops && scriptprops->ops->gpos_features) + { + struct scriptshaping_context context; + + context.cache = fontface_get_shaping_cache(font_obj); + context.text = text; + context.length = text_len; + context.is_rtl = is_rtl; + context.language_tag = get_opentype_language(locale); + + hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features); + } + + return hr; }
static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end) diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 3595503f1e..2299a23cf1 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -192,6 +192,12 @@ extern void factory_lock(IDWriteFactory5*) DECLSPEC_HIDDEN; extern void factory_unlock(IDWriteFactory5*) DECLSPEC_HIDDEN; extern HRESULT create_inmemory_fileloader(IDWriteFontFileLoader**) DECLSPEC_HIDDEN;
+struct dwrite_fontface; + +extern float fontface_get_scaled_design_advance(struct dwrite_fontface *fontface, DWRITE_MEASURING_MODE measuring_mode, + float emsize, float ppdip, const DWRITE_MATRIX *transform, UINT16 glyph, BOOL is_sideways) DECLSPEC_HIDDEN; +extern struct dwrite_fontface *unsafe_impl_from_IDWriteFontFace(IDWriteFontFace *iface) DECLSPEC_HIDDEN; + /* Opentype font table functions */ struct dwrite_font_props { DWRITE_FONT_STYLE style; @@ -320,7 +326,12 @@ enum SCRIPT_JUSTIFY SCRIPT_JUSTIFY_ARABIC_SEEN_M };
-struct scriptshaping_cache; +struct scriptshaping_cache +{ + const struct shaping_font_ops *font; + void *context; + UINT16 upem; +};
struct scriptshaping_context { @@ -328,21 +339,38 @@ struct scriptshaping_context UINT32 language_tag;
const WCHAR *text; - UINT32 length; + unsigned int length; BOOL is_rtl; +};
- UINT32 max_glyph_count; +struct shaping_font_ops +{ + void (*grab_font_table)(void *context, UINT32 table, const BYTE **data, UINT32 *size, void **data_context); + void (*release_font_table)(void *context, void *data_context); + UINT16 (*get_font_upem)(void *context); };
-extern HRESULT create_scriptshaping_cache(IDWriteFontFace*,struct scriptshaping_cache**) DECLSPEC_HIDDEN; +extern struct scriptshaping_cache *create_scriptshaping_cache(void *context, + const struct shaping_font_ops *font_ops) DECLSPEC_HIDDEN; extern void release_scriptshaping_cache(struct scriptshaping_cache*) DECLSPEC_HIDDEN; +extern struct scriptshaping_cache *fontface_get_shaping_cache(struct dwrite_fontface *fontface) DECLSPEC_HIDDEN; + +struct shaping_features +{ + const DWORD *tags; + unsigned int count; +};
struct scriptshaping_ops { HRESULT (*contextual_shaping)(struct scriptshaping_context *context, UINT16 *clustermap, UINT16 *glyph_indices, UINT32* actual_glyph_count); HRESULT (*set_text_glyphs_props)(struct scriptshaping_context *context, UINT16 *clustermap, UINT16 *glyph_indices, UINT32 glyphcount, DWRITE_SHAPING_TEXT_PROPERTIES *text_props, DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props); + const struct shaping_features *gpos_features; };
extern const struct scriptshaping_ops default_shaping_ops DECLSPEC_HIDDEN; extern const struct scriptshaping_ops latn_shaping_ops DECLSPEC_HIDDEN; + +extern HRESULT shape_get_positions(struct scriptshaping_context *context, const DWORD *scripts, + const struct shaping_features *features) DECLSPEC_HIDDEN; diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index d1e4fb9725..f50133d18c 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -240,6 +240,8 @@ struct dwrite_fontface { FONTSIGNATURE fontsig; UINT32 glyph_image_formats;
+ struct scriptshaping_cache *shaping_cache; + LOGFONTW lf; };
@@ -263,6 +265,47 @@ struct dwrite_fontfacereference { IDWriteFactory5 *factory; };
+static void dwrite_grab_font_table(void *context, UINT32 table, const BYTE **data, UINT32 *size, void **data_context) +{ + struct dwrite_fontface *fontface = context; + BOOL exists = FALSE; + + if (FAILED(IDWriteFontFace4_TryGetFontTable(&fontface->IDWriteFontFace4_iface, table, (const void **)data, + size, data_context, &exists)) || !exists) + { + *data = NULL; + *size = 0; + *data_context = NULL; + } +} + +static void dwrite_release_font_table(void *context, void *data_context) +{ + struct dwrite_fontface *fontface = context; + IDWriteFontFace4_ReleaseFontTable(&fontface->IDWriteFontFace4_iface, data_context); +} + +static UINT16 dwrite_get_font_upem(void *context) +{ + struct dwrite_fontface *fontface = context; + return fontface->metrics.designUnitsPerEm; +} + +static const struct shaping_font_ops dwrite_font_ops = +{ + dwrite_grab_font_table, + dwrite_release_font_table, + dwrite_get_font_upem, +}; + +struct scriptshaping_cache *fontface_get_shaping_cache(struct dwrite_fontface *fontface) +{ + if (fontface->shaping_cache) + return fontface->shaping_cache; + + return fontface->shaping_cache = create_scriptshaping_cache(fontface, &dwrite_font_ops); +} + static inline struct dwrite_fontface *impl_from_IDWriteFontFace4(IDWriteFontFace4 *iface) { return CONTAINING_RECORD(iface, struct dwrite_fontface, IDWriteFontFace4_iface); @@ -493,7 +536,7 @@ static ULONG WINAPI dwritefontface_Release(IDWriteFontFace4 *iface) factory_unlock(This->factory); heap_free(This->cached); } - + release_scriptshaping_cache(This->shaping_cache); if (This->cmap.context) IDWriteFontFace4_ReleaseFontTable(iface, This->cmap.context); if (This->vdmx.context) @@ -1775,7 +1818,7 @@ static struct dwrite_font *unsafe_impl_from_IDWriteFont(IDWriteFont *iface) return CONTAINING_RECORD(iface, struct dwrite_font, IDWriteFont3_iface); }
-static struct dwrite_fontface *unsafe_impl_from_IDWriteFontFace(IDWriteFontFace *iface) +struct dwrite_fontface *unsafe_impl_from_IDWriteFontFace(IDWriteFontFace *iface) { if (!iface) return NULL; @@ -5363,7 +5406,7 @@ static inline void transform_point(D2D_POINT_2F *point, const DWRITE_MATRIX *m) *point = ret; }
-static float fontface_get_scaled_design_advance(struct dwrite_fontface *fontface, DWRITE_MEASURING_MODE measuring_mode, +float fontface_get_scaled_design_advance(struct dwrite_fontface *fontface, DWRITE_MEASURING_MODE measuring_mode, float emsize, float ppdip, const DWRITE_MATRIX *transform, UINT16 glyph, BOOL is_sideways) { unsigned int upem = fontface->metrics.designUnitsPerEm; diff --git a/dlls/dwrite/shape.c b/dlls/dwrite/shape.c index a6039323ea..69780450e9 100644 --- a/dlls/dwrite/shape.c +++ b/dlls/dwrite/shape.c @@ -23,32 +23,26 @@
#include "dwrite_private.h"
-struct scriptshaping_cache +struct scriptshaping_cache *create_scriptshaping_cache(void *context, const struct shaping_font_ops *font_ops) { - IDWriteFontFace *fontface; -}; - -HRESULT create_scriptshaping_cache(IDWriteFontFace *fontface, struct scriptshaping_cache **cache) -{ - struct scriptshaping_cache *ret; + struct scriptshaping_cache *cache;
- ret = heap_alloc(sizeof(*ret)); - if (!ret) - return E_OUTOFMEMORY; + cache = heap_alloc_zero(sizeof(*cache)); + if (!cache) + return NULL;
- ret->fontface = fontface; - IDWriteFontFace_AddRef(fontface); + cache->font = font_ops; + cache->context = context;
- *cache = ret; + cache->upem = cache->font->get_font_upem(cache->context);
- return S_OK; + return cache; }
void release_scriptshaping_cache(struct scriptshaping_cache *cache) { if (!cache) return; - IDWriteFontFace_Release(cache->fontface); heap_free(cache); }
@@ -160,10 +154,24 @@ static HRESULT latn_set_text_glyphs_props(struct scriptshaping_context *context, return hr; }
+static const DWORD std_gpos_tags[] = +{ + DWRITE_FONT_FEATURE_TAG_KERNING, + DWRITE_FONT_FEATURE_TAG_MARK_POSITIONING, + DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING, +}; + +static const struct shaping_features std_gpos_features = +{ + std_gpos_tags, + ARRAY_SIZE(std_gpos_tags), +}; + const struct scriptshaping_ops latn_shaping_ops = { NULL, - latn_set_text_glyphs_props + latn_set_text_glyphs_props, + &std_gpos_features, };
const struct scriptshaping_ops default_shaping_ops = @@ -171,3 +179,11 @@ const struct scriptshaping_ops default_shaping_ops = NULL, default_set_text_glyphs_props }; + +HRESULT shape_get_positions(struct scriptshaping_context *context, const DWORD *scripts, + const struct shaping_features *features) +{ + /* FIXME: stub */ + + return S_OK; +}
Signed-off-by: Nikolay Sivov nsivov@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; }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/dwrite_private.h | 27 +++++ dlls/dwrite/opentype.c | 201 +++++++++++++++++++++++++++-------- 2 files changed, 183 insertions(+), 45 deletions(-)
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index d992abda2a..b7d5d4583c 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -76,6 +76,33 @@ static inline const char *debugstr_matrix(const DWRITE_MATRIX *m) m->dx, m->dy); }
+static inline BOOL dwrite_array_reserve(void **elements, size_t *capacity, size_t count, size_t size) +{ + size_t new_capacity, max_capacity; + void *new_elements; + + if (count <= *capacity) + return TRUE; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return FALSE; + + new_capacity = max(4, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = max_capacity; + + if (!(new_elements = heap_realloc(*elements, new_capacity * size))) + return FALSE; + + *elements = new_elements; + *capacity = new_capacity; + + return TRUE; +} + static inline const char *debugstr_tag(DWORD tag) { return debugstr_an((char *)&tag, 4); diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 7d3db16049..02825c2d78 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -312,8 +312,6 @@ struct gasp_header struct gasp_range ranges[1]; };
-#include "poppack.h" - enum OS2_FSSELECTION { OS2_FSSELECTION_ITALIC = 1 << 0, OS2_FSSELECTION_UNDERSCORE = 1 << 1, @@ -373,22 +371,25 @@ struct vdmx_group struct vdmx_vtable entries[1]; };
-typedef struct { - CHAR FeatureTag[4]; - WORD Feature; -} OT_FeatureRecord; +struct ot_feature_record +{ + DWORD tag; + WORD offset; +};
-typedef struct { - WORD FeatureCount; - OT_FeatureRecord FeatureRecord[1]; -} OT_FeatureList; +struct ot_feature_list +{ + WORD feature_count; + struct ot_feature_record features[1]; +};
-typedef struct { - WORD LookupOrder; /* Reserved */ - WORD ReqFeatureIndex; - WORD FeatureCount; - WORD FeatureIndex[1]; -} OT_LangSys; +struct ot_langsys +{ + WORD lookup_order; /* Reserved */ + WORD required_feature_index; + WORD feature_count; + WORD feature_index[1]; +};
struct ot_langsys_record { @@ -432,11 +433,12 @@ enum OPENTYPE_PLATFORM_ID OPENTYPE_PLATFORM_CUSTOM };
-typedef struct { - WORD FeatureParams; - WORD LookupCount; - WORD LookupListIndex[1]; -} OT_Feature; +struct ot_feature +{ + WORD feature_params; + WORD lookup_count; + WORD lookuplist_index[1]; +};
typedef struct { WORD LookupCount; @@ -469,10 +471,12 @@ typedef struct { DWORD ExtensionOffset; } GSUB_ExtensionPosFormat1;
-enum OPENTYPE_GPOS_LOOKUPS +#include "poppack.h" + +enum gsub_lookup_type { - OPENTYPE_GPOS_SINGLE_SUBST = 1, - OPENTYPE_GPOS_EXTENSION_SUBST = 7 + GSUB_LOOKUP_SINGLE_SUBST = 1, + GSUB_LOOKUP_EXTENSION_SUBST = 7, };
enum TT_NAME_WINDOWS_ENCODING_ID @@ -1852,31 +1856,30 @@ static inline const struct ot_script *opentype_get_script(const struct ot_script return NULL; }
-static inline const OT_LangSys *opentype_get_langsys(const struct ot_script *script, UINT32 languagetag) +static inline const struct ot_langsys *opentype_get_langsys(const struct ot_script *script, UINT32 languagetag) { 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 (OT_LangSys*)((BYTE*)script + GET_BE_WORD(script->langsys[j].langsys)); + return (struct ot_langsys *)((BYTE*)script + GET_BE_WORD(script->langsys[j].langsys)); }
return NULL; }
-static void opentype_add_font_features(const struct gpos_gsub_header *header, const OT_LangSys *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) { - const OT_FeatureList *features = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->feature_list)); + 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->FeatureCount); j++) { - const OT_FeatureRecord *feature = &features->FeatureRecord[langsys->FeatureIndex[j]]; - const char *tag = feature->FeatureTag; + 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] = DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]); + tags[*count] = GET_BE_DWORD(feature->tag);
(*count)++; } @@ -1912,7 +1915,7 @@ HRESULT opentype_get_typographic_features(IDWriteFontFace *fontface, UINT32 scri
script = opentype_get_script(scriptlist, scripttag); if (script) { - const OT_LangSys *langsys = opentype_get_langsys(script, languagetag); + const struct ot_langsys *langsys = opentype_get_langsys(script, languagetag); if (langsys) opentype_add_font_features(header, langsys, max_tagcount, count, tags); } @@ -2162,27 +2165,27 @@ 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 struct ot_feature_list *featurelist; const OT_LookupList *lookup_list; BOOL exists = FALSE, ret = FALSE; + unsigned int i, j; const void *data; void *context; UINT32 size; HRESULT hr; - UINT16 i;
hr = IDWriteFontFace4_TryGetFontTable(fontface, MS_GSUB_TAG, &data, &size, &context, &exists); if (FAILED(hr) || !exists) return FALSE;
header = data; - featurelist = (OT_FeatureList*)((BYTE*)header + GET_BE_WORD(header->feature_list)); + featurelist = (struct ot_feature_list *)((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) { - const OT_Feature *feature = (const OT_Feature*)((BYTE*)featurelist + GET_BE_WORD(featurelist->FeatureRecord[i].Feature)); - UINT16 lookup_count = GET_BE_WORD(feature->LookupCount), i, index, count, type; + for (i = 0; i < GET_BE_WORD(featurelist->feature_count); i++) { + if (featurelist->features[i].tag == DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING) { + const struct ot_feature *feature = (const struct ot_feature*)((BYTE*)featurelist + GET_BE_WORD(featurelist->features[i].offset)); + UINT16 lookup_count = GET_BE_WORD(feature->lookup_count), index, count, type; const GSUB_SingleSubstFormat2 *subst2; const OT_LookupTable *lookup_table; UINT32 offset; @@ -2190,13 +2193,13 @@ BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) if (lookup_count == 0) continue;
- for (i = 0; i < lookup_count; i++) { + for (j = 0; j < lookup_count; ++j) { /* check if lookup is empty */ - index = GET_BE_WORD(feature->LookupListIndex[i]); + index = GET_BE_WORD(feature->lookuplist_index[j]); lookup_table = (const OT_LookupTable*)((BYTE*)lookup_list + GET_BE_WORD(lookup_list->Lookup[index]));
type = GET_BE_WORD(lookup_table->LookupType); - if (type != OPENTYPE_GPOS_SINGLE_SUBST && type != OPENTYPE_GPOS_EXTENSION_SUBST) + if (type != GSUB_LOOKUP_SINGLE_SUBST && type != GSUB_LOOKUP_EXTENSION_SUBST) continue;
count = GET_BE_WORD(lookup_table->SubTableCount); @@ -2204,7 +2207,7 @@ BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) continue;
offset = GET_BE_WORD(lookup_table->SubTable[0]); - if (type == OPENTYPE_GPOS_EXTENSION_SUBST) { + if (type == GSUB_LOOKUP_EXTENSION_SUBST) { const GSUB_ExtensionPosFormat1 *ext = (const GSUB_ExtensionPosFormat1 *)((const BYTE *)lookup_table + offset); if (GET_BE_WORD(ext->SubstFormat) == 1) offset += GET_BE_DWORD(ext->ExtensionOffset); @@ -2494,8 +2497,116 @@ DWORD opentype_layout_find_language(const struct scriptshaping_cache *cache, DWO return 0; }
+static void opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, int lookup_index) +{ + /* FIXME: stub */ +} + +struct lookups +{ + int *indexes; + size_t capacity; + size_t count; +}; + +static int lookups_sorting_compare(const void *left, const void *right) +{ + return *(int *)left - *(int *)right; +}; + 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 */ + WORD table_offset, langsys_offset, script_feature_count, total_feature_count, total_lookup_count; + struct scriptshaping_cache *cache = context->cache; + const struct ot_feature_list *feature_list; + struct lookups lookups = { 0 }; + unsigned int i, j, l; + + /* ScriptTable offset. */ + 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; + + if (language_index == ~0u) + langsys_offset = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset); + else + langsys_offset = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset + + FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) + + FIELD_OFFSET(struct ot_langsys_record, langsys)); + + script_feature_count = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset + + langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count)); + if (!script_feature_count) + return; + + total_feature_count = table_read_be_word(&cache->gpos.table, cache->gpos.feature_list); + if (!total_feature_count) + return; + + total_lookup_count = table_read_be_word(&cache->gpos.table, cache->gpos.lookup_list); + if (!total_lookup_count) + return; + + feature_list = table_read_ensure(&cache->gpos.table, cache->gpos.feature_list, + FIELD_OFFSET(struct ot_feature_list, features[total_feature_count])); + if (!feature_list) + return; + + /* Collect lookups for all given features. */ + for (i = 0; i < features->count; ++i) + { + for (j = 0; j < script_feature_count; ++j) + { + WORD feature_index = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset + + langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_index[j])); + if (feature_index >= total_feature_count) + continue; + + if (feature_list->features[feature_index].tag == features->tags[i]) + { + WORD feature_offset = GET_BE_WORD(feature_list->features[feature_index].offset); + WORD lookup_count; + + lookup_count = table_read_be_word(&cache->gpos.table, cache->gpos.feature_list + feature_offset + + FIELD_OFFSET(struct ot_feature, lookup_count)); + if (!lookup_count) + continue; + + if (!dwrite_array_reserve((void **)&lookups.indexes, &lookups.capacity, lookups.count + lookup_count, + sizeof(*lookups.indexes))) + { + heap_free(lookups.indexes); + return; + } + + for (l = 0; l < lookup_count; ++l) + { + WORD lookup_index = table_read_be_word(&cache->gpos.table, cache->gpos.feature_list + + feature_offset + FIELD_OFFSET(struct ot_feature, lookuplist_index[l])); + + if (lookup_index >= total_lookup_count) + continue; + + lookups.indexes[lookups.count++] = lookup_index; + } + } + } + } + + /* Sort lookups. */ + qsort(lookups.indexes, lookups.count, sizeof(*lookups.indexes), lookups_sorting_compare); + + for (l = 0; l < lookups.count; ++l) + { + /* Skip duplicates. */ + if (l && lookups.indexes[l] == lookups.indexes[l - 1]) + continue; + + opentype_layout_apply_gpos_lookup(context, lookups.indexes[l]); + } + + heap_free(lookups.indexes); }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/opentype.c | 227 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 209 insertions(+), 18 deletions(-)
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 02825c2d78..b360bbb009 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -424,6 +424,19 @@ struct gpos_gsub_header WORD lookup_list; };
+enum gpos_lookup_type +{ + GPOS_LOOKUP_SINGLE_ADJUSTMENT = 1, + GPOS_LOOKUP_PAIR_ADJUSTMENT = 2, + GPOS_LOOKUP_CURSIVE_ATTACHMENT = 3, + GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT = 4, + GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT = 5, + GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT = 6, + GPOS_LOOKUP_CONTEXTUAL_POSITION = 7, + GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION = 8, + GPOS_LOOKUP_EXTENSION_POSITION = 9, +}; + enum OPENTYPE_PLATFORM_ID { OPENTYPE_PLATFORM_UNICODE = 0, @@ -433,6 +446,13 @@ enum OPENTYPE_PLATFORM_ID OPENTYPE_PLATFORM_CUSTOM };
+struct ot_gpos_extensionpos_format1 +{ + WORD format; + WORD lookup_type; + DWORD extension_offset; +}; + struct ot_feature { WORD feature_params; @@ -440,17 +460,19 @@ struct ot_feature WORD lookuplist_index[1]; };
-typedef struct { - WORD LookupCount; - WORD Lookup[1]; -} OT_LookupList; +struct ot_lookup_list +{ + WORD lookup_count; + WORD lookup[1]; +};
-typedef struct { - WORD LookupType; - WORD LookupFlag; - WORD SubTableCount; - WORD SubTable[1]; -} OT_LookupTable; +struct ot_lookup_table +{ + WORD lookup_type; + WORD flags; + WORD subtable_count; + WORD subtable[1]; +};
typedef struct { WORD SubstFormat; @@ -2166,7 +2188,7 @@ BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) { const struct gpos_gsub_header *header; const struct ot_feature_list *featurelist; - const OT_LookupList *lookup_list; + const struct ot_lookup_list *lookup_list; BOOL exists = FALSE, ret = FALSE; unsigned int i, j; const void *data; @@ -2180,14 +2202,14 @@ BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface)
header = data; featurelist = (struct ot_feature_list *)((BYTE*)header + GET_BE_WORD(header->feature_list)); - lookup_list = (const OT_LookupList*)((BYTE*)header + GET_BE_WORD(header->lookup_list)); + lookup_list = (const struct ot_lookup_list *)((BYTE*)header + GET_BE_WORD(header->lookup_list));
for (i = 0; i < GET_BE_WORD(featurelist->feature_count); i++) { if (featurelist->features[i].tag == DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING) { const struct ot_feature *feature = (const struct ot_feature*)((BYTE*)featurelist + GET_BE_WORD(featurelist->features[i].offset)); UINT16 lookup_count = GET_BE_WORD(feature->lookup_count), index, count, type; const GSUB_SingleSubstFormat2 *subst2; - const OT_LookupTable *lookup_table; + const struct ot_lookup_table *lookup_table; UINT32 offset;
if (lookup_count == 0) @@ -2196,17 +2218,17 @@ BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) for (j = 0; j < lookup_count; ++j) { /* check if lookup is empty */ index = GET_BE_WORD(feature->lookuplist_index[j]); - lookup_table = (const OT_LookupTable*)((BYTE*)lookup_list + GET_BE_WORD(lookup_list->Lookup[index])); + lookup_table = (const struct ot_lookup_table *)((BYTE*)lookup_list + GET_BE_WORD(lookup_list->lookup[index]));
- type = GET_BE_WORD(lookup_table->LookupType); + type = GET_BE_WORD(lookup_table->lookup_type); if (type != GSUB_LOOKUP_SINGLE_SUBST && type != GSUB_LOOKUP_EXTENSION_SUBST) continue;
- count = GET_BE_WORD(lookup_table->SubTableCount); + count = GET_BE_WORD(lookup_table->subtable_count); if (count == 0) continue;
- offset = GET_BE_WORD(lookup_table->SubTable[0]); + offset = GET_BE_WORD(lookup_table->subtable[0]); if (type == GSUB_LOOKUP_EXTENSION_SUBST) { const GSUB_ExtensionPosFormat1 *ext = (const GSUB_ExtensionPosFormat1 *)((const BYTE *)lookup_table + offset); if (GET_BE_WORD(ext->SubstFormat) == 1) @@ -2497,9 +2519,178 @@ DWORD opentype_layout_find_language(const struct scriptshaping_cache *cache, DWO return 0; }
+struct lookup +{ + unsigned int offset; + unsigned int subtable_count; + unsigned int flags; +}; + +struct glyph_iterator +{ + const struct scriptshaping_context *context; + unsigned int flags; + unsigned int pos; + unsigned int len; +}; + +static void glyph_iterator_init(const struct scriptshaping_context *context, unsigned int flags, unsigned int pos, + unsigned int len, struct glyph_iterator *iter) +{ + iter->context = context; + iter->flags = flags; + iter->pos = pos; + iter->len = len; +} + +static BOOL glyph_iterator_match(const struct glyph_iterator *iter) +{ + /* FIXME: implement class matching */ + return TRUE; +} + +static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context, + struct glyph_iterator *iter, const struct lookup *lookup) +{ + return FALSE; +} + +static BOOL opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context *context, + struct glyph_iterator *iter, const struct lookup *lookup) +{ + return FALSE; +} + +static BOOL opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context *context, + struct glyph_iterator *iter, const struct lookup *lookup) +{ + return FALSE; +} + +static BOOL opentype_layout_apply_gpos_mark_to_base_attachment(const struct scriptshaping_context *context, + struct glyph_iterator *iter, const struct lookup *lookup) +{ + return FALSE; +} + +static BOOL opentype_layout_apply_gpos_mark_to_lig_attachment(const struct scriptshaping_context *context, + struct glyph_iterator *iter, const struct lookup *lookup) +{ + return FALSE; +} + +static BOOL opentype_layout_apply_gpos_mark_to_mark_attachment(const struct scriptshaping_context *context, + struct glyph_iterator *iter, const struct lookup *lookup) +{ + return FALSE; +} + +static BOOL opentype_layout_apply_gpos_contextual_positioning(const struct scriptshaping_context *context, + struct glyph_iterator *iter, const struct lookup *lookup) +{ + return FALSE; +} + +static BOOL opentype_layout_apply_gpos_chaining_contextual_positioning(const struct scriptshaping_context *context, + struct glyph_iterator *iter, const struct lookup *lookup) +{ + return FALSE; +} + static void opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, int lookup_index) { - /* FIXME: stub */ + struct scriptshaping_cache *cache = context->cache; + const struct ot_lookup_table *lookup_table; + struct glyph_iterator iter; + struct lookup lookup; + WORD lookup_type; + + lookup.offset = table_read_be_word(&cache->gpos.table, cache->gpos.lookup_list + + FIELD_OFFSET(struct ot_lookup_list, lookup[lookup_index])); + if (!lookup.offset) + return; + + lookup.offset += cache->gpos.lookup_list; + + if (!(lookup_table = table_read_ensure(&cache->gpos.table, lookup.offset, sizeof(*lookup_table)))) + return; + + lookup.subtable_count = GET_BE_WORD(lookup_table->subtable_count); + if (!lookup.subtable_count) + return; + + lookup_type = GET_BE_WORD(lookup_table->lookup_type); + if (lookup_type == GPOS_LOOKUP_EXTENSION_POSITION) + { + const struct ot_gpos_extensionpos_format1 *extension = table_read_ensure(&cache->gpos.table, + lookup.offset + GET_BE_WORD(lookup_table->subtable[0]), sizeof(*extension)); + WORD format; + + if (!extension) + return; + + format = GET_BE_WORD(extension->format); + if (format != 1) + { + WARN("Unexpected extension table format %u.\n", format); + return; + } + + lookup_type = GET_BE_WORD(extension->lookup_type); + } + lookup.flags = GET_BE_WORD(lookup_table->flags); + + glyph_iterator_init(context, lookup.flags, 0, context->glyph_count, &iter); + + while (iter.pos < context->glyph_count) + { + BOOL ret; + + if (!glyph_iterator_match(&iter)) + { + ++iter.pos; + continue; + } + + switch (lookup_type) + { + case GPOS_LOOKUP_SINGLE_ADJUSTMENT: + ret = opentype_layout_apply_gpos_single_adjustment(context, &iter, &lookup); + break; + case GPOS_LOOKUP_PAIR_ADJUSTMENT: + ret = opentype_layout_apply_gpos_pair_adjustment(context, &iter, &lookup); + break; + case GPOS_LOOKUP_CURSIVE_ATTACHMENT: + ret = opentype_layout_apply_gpos_cursive_attachment(context, &iter, &lookup); + break; + case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT: + ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, &iter, &lookup); + break; + case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT: + ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, &iter, &lookup); + break; + case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT: + ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, &iter, &lookup); + break; + case GPOS_LOOKUP_CONTEXTUAL_POSITION: + ret = opentype_layout_apply_gpos_contextual_positioning(context, &iter, &lookup); + break; + case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION: + ret = opentype_layout_apply_gpos_chaining_contextual_positioning(context, &iter, &lookup); + break; + case GPOS_LOOKUP_EXTENSION_POSITION: + WARN("Recursive extension lookup.\n"); + ret = FALSE; + break; + default: + WARN("Unknown lookup type %u.\n", lookup_type); + return; + } + + /* Some lookups update position after making changes. */ + if (!ret) + ++iter.pos; + } }
struct lookups
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/analyzer.c | 2 + dlls/dwrite/dwrite_private.h | 7 ++ dlls/dwrite/opentype.c | 127 ++++++++++++++++++++++++++++++++++- dlls/dwrite/shape.c | 1 + 4 files changed, 136 insertions(+), 1 deletion(-)
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index 13c1941d75..7f15c91279 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -1313,6 +1313,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 context.text = text; context.length = text_len; context.is_rtl = is_rtl; + context.u.pos.glyphs = glyphs; context.u.pos.glyph_props = glyph_props; context.glyph_count = glyph_count; context.advances = advances; @@ -1374,6 +1375,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWrite context.text = text; context.length = text_len; context.is_rtl = is_rtl; + context.u.pos.glyphs = glyphs; context.u.pos.glyph_props = glyph_props; context.glyph_count = glyph_count; context.advances = advances; diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index b7d5d4583c..5a6cd4b16b 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -366,6 +366,12 @@ struct scriptshaping_cache unsigned int feature_list; unsigned int lookup_list; } gpos; + + struct + { + struct dwrite_fonttable table; + unsigned int classdef; + } gdef; };
struct scriptshaping_context @@ -381,6 +387,7 @@ struct scriptshaping_context { struct { + const UINT16 *glyphs; const DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props; } pos; } u; diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index b360bbb009..8cc74638b0 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -32,6 +32,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dwrite); #define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2') #define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t') #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f') +#define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F') #define MS_GPOS_TAG DWRITE_MAKE_OPENTYPE_TAG('G','P','O','S') #define MS_GSUB_TAG DWRITE_MAKE_OPENTYPE_TAG('G','S','U','B') #define MS_NAME_TAG DWRITE_MAKE_OPENTYPE_TAG('n','a','m','e') @@ -416,6 +417,47 @@ struct ot_script_list struct ot_script_record scripts[1]; };
+enum ot_gdef_class +{ + GDEF_CLASS_UNCLASSIFIED = 0, + GDEF_CLASS_BASE = 1, + GDEF_CLASS_LIGATURE = 2, + GDEF_CLASS_MARK = 3, + GDEF_CLASS_COMPONENT = 4, + GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT, +}; + +struct gdef_header +{ + DWORD version; + WORD classdef; + WORD attach_list; + WORD ligcaret_list; + WORD markattach_classdef; +}; + +struct ot_gdef_classdef_format1 +{ + WORD format; + WORD start_glyph; + WORD glyph_count; + WORD classes[1]; +}; + +struct ot_gdef_class_range +{ + WORD start_glyph; + WORD end_glyph; + WORD glyph_class; +}; + +struct ot_gdef_classdef_format2 +{ + WORD format; + WORD range_count; + struct ot_gdef_class_range ranges[1]; +}; + struct gpos_gsub_header { DWORD version; @@ -424,6 +466,16 @@ struct gpos_gsub_header WORD lookup_list; };
+enum gsub_gpos_lookup_flags +{ + LOOKUP_FLAG_RTL = 0x1, + LOOKUP_FLAG_IGNORE_BASE = 0x2, + LOOKUP_FLAG_IGNORE_LIGATURES = 0x4, + LOOKUP_FLAG_IGNORE_MARKS = 0x8, + + LOOKUP_FLAG_IGNORE_MASK = 0xe, +}; + enum gpos_lookup_type { GPOS_LOOKUP_SINGLE_ADJUSTMENT = 1, @@ -2447,6 +2499,12 @@ void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache) cache->gpos.lookup_list = table_read_be_word(&cache->gpos.table, FIELD_OFFSET(struct gpos_gsub_header, lookup_list)); } + + cache->font->grab_font_table(cache->context, MS_GDEF_TAG, &cache->gdef.table.data, &cache->gdef.table.size, + &cache->gdef.table.context); + + if (cache->gdef.table.data) + cache->gdef.classdef = table_read_be_word(&cache->gdef.table, FIELD_OFFSET(struct gdef_header, classdef)); }
DWORD opentype_layout_find_script(const struct scriptshaping_cache *cache, DWORD kind, DWORD script, @@ -2519,6 +2577,64 @@ DWORD opentype_layout_find_language(const struct scriptshaping_cache *cache, DWO return 0; }
+static int gdef_class_compare_format2(const void *g, const void *r) +{ + const struct ot_gdef_class_range *range = r; + UINT16 glyph = *(UINT16 *)g; + + if (glyph < GET_BE_WORD(range->start_glyph)) + return -1; + else if (glyph > GET_BE_WORD(range->end_glyph)) + return 1; + else + return 0; +} + +static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable *table, + unsigned int offset, UINT16 glyph) +{ + WORD format = table_read_be_word(table, offset), count; + unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED; + + if (format == 1) + { + const struct ot_gdef_classdef_format1 *format1; + + count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count)); + format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count])); + if (format1) + { + WORD start_glyph = GET_BE_WORD(format1->start_glyph); + if (glyph >= start_glyph && (glyph - start_glyph) < count) + { + glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]); + if (glyph_class > GDEF_CLASS_MAX) + glyph_class = GDEF_CLASS_UNCLASSIFIED; + } + } + } + else if (format == 2) + { + const struct ot_gdef_classdef_format2 *format2; + + count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count)); + format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count])); + if (format2) + { + const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count, + sizeof(struct ot_gdef_class_range), gdef_class_compare_format2); + glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ? + GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED; + if (glyph_class > GDEF_CLASS_MAX) + glyph_class = GDEF_CLASS_UNCLASSIFIED; + } + } + else + WARN("Unknown GDEF format %u.\n", format); + + return glyph_class; +} + struct lookup { unsigned int offset; @@ -2545,7 +2661,16 @@ static void glyph_iterator_init(const struct scriptshaping_context *context, uns
static BOOL glyph_iterator_match(const struct glyph_iterator *iter) { - /* FIXME: implement class matching */ + struct scriptshaping_cache *cache = iter->context->cache; + + if (cache->gdef.classdef) + { + unsigned int glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.classdef, + iter->context->u.pos.glyphs[iter->pos]); + if ((1 << glyph_class) & iter->flags & LOOKUP_FLAG_IGNORE_MASK) + return FALSE; + } + return TRUE; }
diff --git a/dlls/dwrite/shape.c b/dlls/dwrite/shape.c index 831c7754fc..d7d1261f35 100644 --- a/dlls/dwrite/shape.c +++ b/dlls/dwrite/shape.c @@ -51,6 +51,7 @@ void release_scriptshaping_cache(struct scriptshaping_cache *cache) if (!cache) return;
+ cache->font->release_font_table(cache->context, cache->gdef.table.context); cache->font->release_font_table(cache->context, cache->gpos.table.context); heap_free(cache); }