From e7324209e05a95df8b3856ee4de3ff7d5d0d401d Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 1 Jun 2020 12:23:35 +0300 Subject: [PATCH] dwrite: Implement contextual subsitution (GSUB lookup 5). Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/dwrite/opentype.c | 165 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 158 insertions(+), 7 deletions(-) diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index c724327c507..f1b9fb4e474 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -610,7 +610,7 @@ struct ot_gsub_lig UINT16 components[1]; }; -struct ot_gsub_chaincontext_subst_format1 +struct ot_gsub_context_subst_format1 { UINT16 format; UINT16 coverage; @@ -5205,6 +5205,157 @@ static BOOL opentype_layout_apply_chain_rule_set(const struct match_context *mc, return FALSE; } +static BOOL opentype_layout_apply_gsub_context_lookup(unsigned int input_count, const UINT16 *input, unsigned int lookup_count, + const UINT16 *lookup_records, const struct match_context *mc) +{ + unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH]; + unsigned int match_length = 0; + + return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) && + opentype_layout_context_gsub_apply_lookup(mc->context, input_count, match_positions, lookup_count, + lookup_records, match_length); +} + +static BOOL opentype_layout_apply_rule_set(const struct match_context *mc, unsigned int offset) +{ + unsigned int input_count, lookup_count; + const struct dwrite_fonttable *table = &mc->context->table->table; + const UINT16 *input, *lookup_records; + const struct ot_gsub_ruleset *ruleset; + unsigned int i, count; + + count = table_read_be_word(table, offset); + ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets)); + + for (i = 0; i < count; ++i) + { + unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]); + + if (!(input_count = table_read_be_word(table, rule_offset))) + continue; + rule_offset += 2; + + if (!(lookup_count = table_read_be_word(table, rule_offset))) + continue; + rule_offset += 2; + + if (!(input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input)))) + continue; + rule_offset += (input_count - 1) * sizeof(*input); + + if (!(lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records)))) + continue; + + /* First applicable rule is used. */ + if (opentype_layout_apply_gsub_context_lookup(input_count, input, lookup_count, lookup_records, mc)) + return TRUE; + } + + return FALSE; +} + +static BOOL opentype_layout_apply_gsub_context_substitution(struct scriptshaping_context *context, const struct lookup *lookup, + unsigned int subtable_offset) +{ + struct match_context mc = { .context = context, .mask = lookup->mask }; + const struct dwrite_fonttable *table = &context->table->table; + unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset; + UINT16 glyph, format, coverage; + BOOL ret = FALSE; + + glyph = context->u.subst.glyphs[context->cur]; + + format = table_read_be_word(table, subtable_offset); + + if (format == 1) + { + coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_context_subst_format1, coverage)); + + coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph); + if (coverage_index == GLYPH_NOT_COVERED) + return FALSE; + + count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_context_subst_format1, + ruleset_count)); + if (coverage_index >= count) + return FALSE; + + offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_context_subst_format1, + rulesets[coverage_index])); + offset += subtable_offset; + + mc.match_func = opentype_match_glyph_func; + + ret = opentype_layout_apply_rule_set(&mc, offset); + } + else if (format == 2) + { + unsigned int input_classdef, rule_set_idx; + + offset = subtable_offset + 2 /* format */; + + coverage = table_read_be_word(table, offset); + offset += 2; + + coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph); + if (coverage_index == GLYPH_NOT_COVERED) + return FALSE; + + input_classdef = table_read_be_word(table, offset) + subtable_offset; + offset += 2; + + count = table_read_be_word(table, offset); + offset+= 2; + + rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph); + if (rule_set_idx >= count) + return FALSE; + + offset = table_read_be_word(table, offset + rule_set_idx * 2); + offset += subtable_offset; + + mc.input_offset = input_classdef; + mc.match_func = opentype_match_class_func; + + ret = opentype_layout_apply_rule_set(&mc, offset); + } + else if (format == 3) + { + unsigned int input_count, lookup_count; + const UINT16 *input, *lookup_records; + + offset = subtable_offset + 2 /* format */; + + input_count = table_read_be_word(table, offset); + offset += 2; + + if (!input_count) + return FALSE; + + lookup_count = table_read_be_word(table, offset); + offset += 2; + + if (!(input = table_read_ensure(table, offset, sizeof(*input) * input_count))) + return FALSE; + offset += sizeof(*input) * input_count; + + coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph); + if (coverage_index == GLYPH_NOT_COVERED) + return FALSE; + + lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records)); + + mc.input_offset = subtable_offset; + mc.match_func = opentype_match_coverage_func; + + ret = opentype_layout_apply_gsub_context_lookup(input_count, input + 1, lookup_count, lookup_records, &mc); + } + else + WARN("Unknown contextual substitution format %u.\n", format); + + return ret; +} + static BOOL opentype_layout_apply_gsub_chain_context_substitution(struct scriptshaping_context *context, const struct lookup *lookup, unsigned int subtable_offset) { @@ -5220,19 +5371,17 @@ static BOOL opentype_layout_apply_gsub_chain_context_substitution(struct scripts if (format == 1) { - coverage = table_read_be_word(table, subtable_offset + - FIELD_OFFSET(struct ot_gsub_chaincontext_subst_format1, coverage)); + coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_context_subst_format1, coverage)); coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph); if (coverage_index == GLYPH_NOT_COVERED) return FALSE; - count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_chaincontext_subst_format1, - ruleset_count)); + count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_context_subst_format1, ruleset_count)); if (coverage_index >= count) return FALSE; - offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_chaincontext_subst_format1, + offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_context_subst_format1, rulesets[coverage_index])); offset += subtable_offset; @@ -5357,10 +5506,12 @@ static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *cont case GSUB_LOOKUP_LIGATURE_SUBST: ret = opentype_layout_apply_gsub_lig_substitution(context, lookup, subtable_offset); break; + case GSUB_LOOKUP_CONTEXTUAL_SUBST: + ret = opentype_layout_apply_gsub_context_substitution(context, lookup, subtable_offset); + break; case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST: ret = opentype_layout_apply_gsub_chain_context_substitution(context, lookup, subtable_offset); break; - case GSUB_LOOKUP_CONTEXTUAL_SUBST: case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST: WARN("Unimplemented lookup %d.\n", lookup->type); break;