From b4b33e13d2dc72cd70dfe6b7c39f55cddca4861e Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 8 Jun 2020 16:29:45 +0300 Subject: [PATCH] dwrite: Implement CheckTypographicFeature(). Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/dwrite/analyzer.c | 29 +++- dlls/dwrite/dwrite_private.h | 5 + dlls/dwrite/opentype.c | 249 +++++++++++++++++++++++++++++++++++ dlls/dwrite/shape.c | 25 ++++ 4 files changed, 304 insertions(+), 4 deletions(-) diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index 909d6e91e3e..af4325248a7 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -1787,12 +1787,33 @@ static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnal }; static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface, - IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature, + IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *glyphs, UINT8 *feature_applies) { - FIXME("(%p %u %s %s %u %p %p): stub\n", face, sa.script, debugstr_w(locale), debugstr_tag(feature), glyph_count, - glyphs, feature_applies); - return E_NOTIMPL; + struct scriptshaping_context context = { 0 }; + const struct dwritescript_properties *props; + struct dwrite_fontface *font_obj; + HRESULT hr; + + TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), debugstr_tag(feature), + glyph_count, glyphs, feature_applies); + + if (sa.script > Script_LastId) + return E_INVALIDARG; + + font_obj = unsafe_impl_from_IDWriteFontFace(fontface); + + context.cache = fontface_get_shaping_cache(font_obj); + context.language_tag = get_opentype_language(locale); + context.glyph_infos = heap_calloc(glyph_count, sizeof(*context.glyph_infos)); + + props = &dwritescripts_properties[sa.script]; + + hr = shape_check_typographic_feature(&context, props->scripttags, feature, glyph_count, glyphs, feature_applies); + + heap_free(context.glyph_infos); + + return hr; } static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = { diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 246dab69327..966750c12b4 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -591,8 +591,13 @@ extern void opentype_layout_apply_gsub_features(struct scriptshaping_context *co unsigned int language_index, const struct shaping_features *features) 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; +extern BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index, + unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count, + const UINT16 *glyphs, UINT8 *feature_applies) DECLSPEC_HIDDEN; extern HRESULT shape_get_glyphs(struct scriptshaping_context *context, const unsigned int *scripts) DECLSPEC_HIDDEN; extern HRESULT shape_get_positions(struct scriptshaping_context *context, const unsigned int *scripts) DECLSPEC_HIDDEN; extern HRESULT shape_get_typographic_features(struct scriptshaping_context *context, const unsigned int *scripts, unsigned int max_tagcount, unsigned int *actual_tagcount, unsigned int *tags) DECLSPEC_HIDDEN; +extern HRESULT shape_check_typographic_feature(struct scriptshaping_context *context, const unsigned int *scripts, + unsigned int tag, unsigned int glyph_count, const UINT16 *glyphs, UINT8 *feature_applies) DECLSPEC_HIDDEN; diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index cb4b706fba3..aacf3661c18 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -5732,3 +5732,252 @@ void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, heap_free(lookups.lookups); } + +static BOOL opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph, + unsigned int subtable_offset, unsigned int coverage, unsigned int format) +{ + const struct dwrite_fonttable *table = &context->table->table; + const UINT16 *offsets; + unsigned int count; + + if (format == 1 || format == 2) + { + if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED) + return TRUE; + } + else if (format == 3) + { + count = table_read_be_word(table, subtable_offset + 2); + if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6, count * sizeof(*offsets)))) + return FALSE; + + if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED) + return TRUE; + } + + return FALSE; +} + +static BOOL opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph, + unsigned int subtable_offset, unsigned int coverage, unsigned int format) +{ + const struct dwrite_fonttable *table = &context->table->table; + unsigned int count, backtrack_count; + const UINT16 *offsets; + + if (format == 1 || format == 2) + { + if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED) + return TRUE; + } + else if (format == 3) + { + backtrack_count = table_read_be_word(table, subtable_offset + 2); + + count = table_read_be_word(table, subtable_offset + 4 + backtrack_count * sizeof(*offsets)); + + if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6 + backtrack_count * sizeof(*offsets), + count * sizeof(*offsets)))) + return FALSE; + + if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED) + return TRUE; + } + + return FALSE; +} + +static BOOL opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph, + const struct lookup *lookup) +{ + const struct dwrite_fonttable *gsub = &context->table->table; + static const unsigned short gsub_formats[] = + { + 0, /* Unused */ + 1, /* SingleSubst */ + 1, /* MultipleSubst */ + 1, /* AlternateSubst */ + 1, /* LigatureSubst */ + 3, /* ContextSubst */ + 3, /* ChainContextSubst */ + 0, /* Extension, unused */ + 1, /* ReverseChainSubst */ + }; + unsigned int i, coverage, lookup_type, format; + + for (i = 0; i < lookup->subtable_count; ++i) + { + unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i); + + if (lookup->type == GSUB_LOOKUP_EXTENSION_SUBST) + { + lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset); + if (!lookup_type) + continue; + } + else + lookup_type = lookup->type; + + format = table_read_be_word(gsub, subtable_offset); + + if (!format || format > ARRAY_SIZE(gsub_formats) || format > gsub_formats[lookup_type]) + break; + + coverage = table_read_be_word(gsub, subtable_offset + 2); + + switch (lookup_type) + { + case GSUB_LOOKUP_SINGLE_SUBST: + case GSUB_LOOKUP_MULTIPLE_SUBST: + case GSUB_LOOKUP_ALTERNATE_SUBST: + case GSUB_LOOKUP_LIGATURE_SUBST: + case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST: + + if (opentype_layout_is_glyph_covered(gsub, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED) + return TRUE; + + break; + + case GSUB_LOOKUP_CONTEXTUAL_SUBST: + + if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format)) + return TRUE; + + break; + + case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST: + + if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format)) + return TRUE; + + break; + + default: + WARN("Unknown lookup type %u.\n", lookup_type); + } + } + + return FALSE; +} + +static BOOL opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph, + const struct lookup *lookup) +{ + const struct dwrite_fonttable *gpos = &context->table->table; + static const unsigned short gpos_formats[] = + { + 0, /* Unused */ + 2, /* SinglePos */ + 2, /* PairPos */ + 1, /* CursivePos */ + 1, /* MarkBasePos */ + 1, /* MarkLigPos */ + 1, /* MarkMarkPos */ + 3, /* ContextPos */ + 3, /* ChainContextPos */ + 0, /* Extension, unused */ + }; + unsigned int i, coverage, lookup_type, format; + + for (i = 0; i < lookup->subtable_count; ++i) + { + unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i); + + if (lookup->type == GPOS_LOOKUP_EXTENSION_POSITION) + { + lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset); + if (!lookup_type) + continue; + } + else + lookup_type = lookup->type; + + format = table_read_be_word(gpos, subtable_offset); + + if (!format || format > ARRAY_SIZE(gpos_formats) || format > gpos_formats[lookup_type]) + break; + + coverage = table_read_be_word(gpos, subtable_offset + 2); + + switch (lookup_type) + { + case GPOS_LOOKUP_SINGLE_ADJUSTMENT: + case GPOS_LOOKUP_PAIR_ADJUSTMENT: + case GPOS_LOOKUP_CURSIVE_ATTACHMENT: + case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT: + case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT: + case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT: + + if (opentype_layout_is_glyph_covered(gpos, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED) + return TRUE; + + break; + + case GPOS_LOOKUP_CONTEXTUAL_POSITION: + + if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format)) + return TRUE; + + break; + + case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION: + + if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format)) + return TRUE; + + break; + + default: + WARN("Unknown lookup type %u.\n", lookup_type); + } + } + + return FALSE; +} + +typedef BOOL (*p_lookup_is_glyph_covered_func)(struct scriptshaping_context *context, UINT16 glyph, const struct lookup *lookup); + +BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index, + unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count, + const UINT16 *glyphs, UINT8 *feature_applies) +{ + p_lookup_is_glyph_covered_func func_is_covered; + struct shaping_features features = { 0 }; + struct lookups lookups = { 0 }; + BOOL ret = FALSE, is_covered; + unsigned int i, j, applies; + + features.features = feature; + features.count = 1; + + for (i = 0; i < context->glyph_count; ++i) + opentype_set_glyph_props(context, i); + + opentype_layout_collect_lookups(context, script_index, language_index, &features, context->table, &lookups); + + func_is_covered = context->table == &context->cache->gsub ? opentype_layout_gsub_lookup_is_glyph_covered : + opentype_layout_gpos_lookup_is_glyph_covered; + + for (i = 0; i < lookups.count; ++i) + { + struct lookup *lookup = &lookups.lookups[i]; + + applies = 0; + for (j = 0; j < context->glyph_count; ++j) + { + if (lookup_is_glyph_match(context, j, lookup->flags)) + { + if ((is_covered = func_is_covered(context, glyphs[i], lookup))) + ++applies; + feature_applies[j] |= is_covered; + } + } + + if ((ret = (applies == context->glyph_count))) + break; + } + + heap_free(lookups.lookups); + + return ret; +} diff --git a/dlls/dwrite/shape.c b/dlls/dwrite/shape.c index 4968cd995d4..82eb5308da6 100644 --- a/dlls/dwrite/shape.c +++ b/dlls/dwrite/shape.c @@ -350,3 +350,28 @@ HRESULT shape_get_typographic_features(struct scriptshaping_context *context, co return t.count <= max_tagcount ? S_OK : E_NOT_SUFFICIENT_BUFFER; } + +HRESULT shape_check_typographic_feature(struct scriptshaping_context *context, const unsigned int *scripts, + unsigned int tag, unsigned int glyph_count, const UINT16 *glyphs, UINT8 *feature_applies) +{ + static const unsigned int tables[] = { MS_GSUB_TAG, MS_GPOS_TAG }; + struct shaping_feature feature = { .tag = tag }; + unsigned int script_index, language_index; + unsigned int i; + + memset(feature_applies, 0, glyph_count * sizeof(*feature_applies)); + + for (i = 0; i < ARRAY_SIZE(tables); ++i) + { + shape_get_script_lang_index(context, scripts, tables[i], &script_index, &language_index); + context->table = tables[i] == MS_GSUB_TAG ? &context->cache->gsub : &context->cache->gpos; + /* Skip second table if feature applies to all. */ + if (opentype_layout_check_feature(context, script_index, language_index, &feature, glyph_count, + glyphs, feature_applies)) + { + break; + } + } + + return S_OK; +}