Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/analyzer.c | 6 +-- dlls/dwrite/dwrite_private.h | 10 ---- dlls/dwrite/shape.c | 99 ------------------------------------ 3 files changed, 2 insertions(+), 113 deletions(-)
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index 556191a13d7..56950a5baa3 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -34,11 +34,11 @@ extern const unsigned short wine_scripts_table[] DECLSPEC_HIDDEN; /* Number of characters needed for LOCALE_SNATIVEDIGITS */ #define NATIVE_DIGITS_LEN 11
-struct dwritescript_properties { +struct dwritescript_properties +{ DWRITE_SCRIPT_PROPERTIES props; UINT32 scripttags[3]; /* Maximum 2 script tags, 0-terminated. */ BOOL is_complex; - const struct scriptshaping_ops *ops; };
#define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d) @@ -1194,8 +1194,6 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface, *actual_glyph_count = context.glyph_count; memcpy(glyphs, context.u.subst.glyphs, context.glyph_count * sizeof(*glyphs)); memcpy(glyph_props, context.u.subst.glyph_props, context.glyph_count * sizeof(*glyph_props)); - hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyphs, *actual_glyph_count, - text_props, glyph_props); }
heap_free(context.u.subst.glyph_props); diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index cf3e0f7c123..2ed53658cd2 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -571,15 +571,5 @@ extern void opentype_layout_apply_gsub_features(struct scriptshaping_context *co 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); - 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 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; diff --git a/dlls/dwrite/shape.c b/dlls/dwrite/shape.c index b7b40d1211d..32aabac5f68 100644 --- a/dlls/dwrite/shape.c +++ b/dlls/dwrite/shape.c @@ -55,105 +55,6 @@ void release_scriptshaping_cache(struct scriptshaping_cache *cache) heap_free(cache); }
-static void shape_update_clusters_from_glyphprop(UINT32 glyphcount, UINT32 text_len, UINT16 *clustermap, DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props) -{ - UINT32 i; - - for (i = 0; i < glyphcount; i++) { - if (!glyph_props[i].isClusterStart) { - UINT32 j; - - for (j = 0; j < text_len; j++) { - if (clustermap[j] == i) { - int k = j; - while (k >= 0 && k < text_len && !glyph_props[clustermap[k]].isClusterStart) - k--; - - if (k >= 0 && k < text_len && glyph_props[clustermap[k]].isClusterStart) - clustermap[j] = clustermap[k]; - } - } - } - } -} - -static int compare_clustersearch(const void *a, const void* b) -{ - UINT16 target = *(UINT16*)a; - UINT16 index = *(UINT16*)b; - int ret = 0; - - if (target > index) - ret = 1; - else if (target < index) - ret = -1; - - return ret; -} - -/* Maps given glyph position in glyph indices array to text index this glyph represents. - Lowest possible index is returned. - - clustermap [I] Text index to index in glyph indices array map - len [I] Clustermap size - target [I] Index in glyph indices array to map - */ -static INT32 map_glyph_to_text_pos(const UINT16 *clustermap, UINT32 len, UINT16 target) -{ - UINT16 *ptr; - INT32 k; - - ptr = bsearch(&target, clustermap, len, sizeof(UINT16), compare_clustersearch); - if (!ptr) - return -1; - - /* get to the beginning */ - for (k = (ptr - clustermap) - 1; k >= 0 && clustermap[k] == target; k--) - ; - k++; - - return k; -} - -static HRESULT default_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) -{ - UINT32 i; - - for (i = 0; i < glyphcount; i++) { - UINT32 char_index[20]; - UINT32 char_count = 0; - INT32 k; - - k = map_glyph_to_text_pos(clustermap, context->length, i); - if (k >= 0) { - for (; k < context->length && clustermap[k] == i; k++) - char_index[char_count++] = k; - } - - if (char_count == 0) - continue; - - if (char_count == 1 && isspaceW(context->text[char_index[0]])) { - glyph_props[i].justification = SCRIPT_JUSTIFY_BLANK; - text_props[char_index[0]].isShapedAlone = context->text[char_index[0]] == ' '; - } - else - glyph_props[i].justification = SCRIPT_JUSTIFY_CHARACTER; - } - - /* FIXME: update properties using GDEF table */ - shape_update_clusters_from_glyphprop(glyphcount, context->length, clustermap, glyph_props); - - return S_OK; -} - -const struct scriptshaping_ops default_shaping_ops = -{ - NULL, - default_set_text_glyphs_props -}; - static unsigned int shape_select_script(const struct scriptshaping_cache *cache, DWORD kind, const DWORD *scripts, unsigned int *script_index) {
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/opentype.c | 885 +++++++++++++++++++---------------------- 1 file changed, 410 insertions(+), 475 deletions(-)
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 18bffc3fa2e..88861863034 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -3402,23 +3402,7 @@ static void opentype_layout_apply_gpos_value(struct scriptshaping_context *conte } }
-static unsigned int opentype_layout_get_gpos_subtable(const struct scriptshaping_cache *cache, - unsigned int lookup_offset, unsigned int subtable) -{ - WORD lookup_type = table_read_be_word(&cache->gpos.table, lookup_offset); - unsigned int subtable_offset = table_read_be_word(&cache->gpos.table, lookup_offset + - FIELD_OFFSET(struct ot_lookup_table, subtable[subtable])); - if (lookup_type == GPOS_LOOKUP_EXTENSION_POSITION) - { - const struct ot_gsubgpos_extension_format1 *format1 = table_read_ensure(&cache->gpos.table, - lookup_offset + subtable_offset, sizeof(*format1)); - subtable_offset += GET_BE_DWORD(format1->extension_offset); - } - - return lookup_offset + subtable_offset; -} - -static unsigned int opentype_layout_get_gsub_subtable(const struct scriptshaping_context *context, +static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context *context, unsigned int lookup_offset, unsigned int subtable) { unsigned int subtable_offset = table_read_be_word(&context->table->table, lookup_offset + @@ -3499,60 +3483,69 @@ static BOOL glyph_iterator_prev(struct glyph_iterator *iter) return FALSE; }
+struct lookup +{ + unsigned short index; + unsigned short type; + unsigned short flags; + unsigned short subtable_count; + + unsigned int mask; + unsigned int offset; +}; + static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context, - struct glyph_iterator *iter, const struct ot_lookup *lookup) + const struct lookup *lookup, unsigned int subtable_offset) { struct scriptshaping_cache *cache = context->cache; - WORD format, value_format, value_len, coverage; - unsigned int i; + UINT16 format, value_format, value_len, coverage, glyph;
- for (i = 0; i < lookup->subtable_count; ++i) - { - unsigned int subtable_offset = opentype_layout_get_gpos_subtable(cache, lookup->offset, i); - unsigned int coverage_index; + unsigned int coverage_index;
- format = table_read_be_word(&cache->gpos.table, subtable_offset); + format = table_read_be_word(&cache->gpos.table, subtable_offset);
- coverage = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage)); - value_format = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format)); - value_len = dwrite_popcount(value_format); + coverage = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage)); + value_format = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format)); + value_len = dwrite_popcount(value_format);
- if (format == 1) - { - const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(&cache->gpos.table, subtable_offset, - FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len])); + glyph = context->u.pos.glyphs[context->cur];
- coverage_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage, - context->u.pos.glyphs[iter->pos]); - if (coverage_index == GLYPH_NOT_COVERED) - continue; + if (format == 1) + { + const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(&cache->gpos.table, subtable_offset, + FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len]));
- opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, iter->pos); - break; - } - else if (format == 2) - { - WORD value_count = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count)); - const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(&cache->gpos.table, subtable_offset, - FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD)); - - coverage_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage, - context->u.pos.glyphs[iter->pos]); - if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count) - continue; + coverage_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage, glyph); + if (coverage_index == GLYPH_NOT_COVERED) + return FALSE;
- opentype_layout_apply_gpos_value(context, subtable_offset, value_format, - &format2->values[coverage_index * value_len], iter->pos); - break; - } - else - WARN("Unknown single adjustment format %u.\n", format); + opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, context->cur); } + else if (format == 2) + { + WORD value_count = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count)); + const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(&cache->gpos.table, subtable_offset, + FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD));
- return FALSE; + coverage_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage, glyph); + if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count) + return FALSE; + + opentype_layout_apply_gpos_value(context, subtable_offset, value_format, &format2->values[coverage_index * value_len], + context->cur); + } + else + { + WARN("Unknown single adjustment format %u.\n", format); + return FALSE; + } + + context->cur++; + + return TRUE; }
static int gpos_pair_adjustment_compare_format1(const void *g, const void *r) @@ -3563,153 +3556,144 @@ static int gpos_pair_adjustment_compare_format1(const void *g, const void *r) }
static BOOL opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context *context, - struct glyph_iterator *iter, const struct ot_lookup *lookup) + const struct lookup *lookup, unsigned int subtable_offset) { struct scriptshaping_cache *cache = context->cache; - unsigned int i, first_glyph, second_glyph; + unsigned int first_glyph, second_glyph; struct glyph_iterator iter_pair; WORD format, coverage;
- glyph_iterator_init(context, iter->flags, iter->pos, 1, &iter_pair); + WORD value_format1, value_format2, value_len1, value_len2; + unsigned int coverage_index; + + glyph_iterator_init(context, lookup->flags, context->cur, 1, &iter_pair); if (!glyph_iterator_next(&iter_pair)) return FALSE;
if (context->is_rtl) { first_glyph = iter_pair.pos; - second_glyph = iter->pos; + second_glyph = context->cur; } else { - first_glyph = iter->pos; + first_glyph = context->cur; second_glyph = iter_pair.pos; }
- for (i = 0; i < lookup->subtable_count; ++i) - { - unsigned int subtable_offset = opentype_layout_get_gpos_subtable(cache, lookup->offset, i); - WORD value_format1, value_format2, value_len1, value_len2; - unsigned int coverage_index; - - format = table_read_be_word(&cache->gpos.table, subtable_offset); - - coverage = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_pairpos_format1, coverage)); - if (!coverage) - continue; + format = table_read_be_word(&cache->gpos.table, subtable_offset);
- coverage_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage, - context->u.pos.glyphs[first_glyph]); - if (coverage_index == GLYPH_NOT_COVERED) - continue; + coverage = table_read_be_word(&cache->gpos.table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1, coverage)); + if (!coverage) + return FALSE;
- if (format == 1) - { - const struct ot_gpos_pairpos_format1 *format1; - WORD pairset_count = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairset_count)); - unsigned int pairvalue_len, pairset_offset; - const struct ot_gpos_pairset *pairset; - const WORD *pairvalue; - WORD pairvalue_count; - - if (!pairset_count || coverage_index >= pairset_count) - continue; + coverage_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage, context->u.pos.glyphs[first_glyph]); + if (coverage_index == GLYPH_NOT_COVERED) + return FALSE;
- format1 = table_read_ensure(&cache->gpos.table, subtable_offset, - FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairsets[pairset_count])); - if (!format1) - continue; + if (format == 1) + { + const struct ot_gpos_pairpos_format1 *format1; + WORD pairset_count = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairset_count)); + unsigned int pairvalue_len, pairset_offset; + const struct ot_gpos_pairset *pairset; + const WORD *pairvalue; + WORD pairvalue_count; + + if (!pairset_count || coverage_index >= pairset_count) + return FALSE;
- /* Ordered paired values. */ - pairvalue_count = table_read_be_word(&cache->gpos.table, subtable_offset + - GET_BE_WORD(format1->pairsets[coverage_index])); - if (!pairvalue_count) - continue; + format1 = table_read_ensure(&cache->gpos.table, subtable_offset, + FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairsets[pairset_count])); + if (!format1) + return FALSE;
- /* Structure length is variable, but does not change across the subtable. */ - value_format1 = GET_BE_WORD(format1->value_format1) & 0xff; - value_format2 = GET_BE_WORD(format1->value_format2) & 0xff; + /* Ordered paired values. */ + pairvalue_count = table_read_be_word(&cache->gpos.table, subtable_offset + + GET_BE_WORD(format1->pairsets[coverage_index])); + if (!pairvalue_count) + return FALSE;
- value_len1 = dwrite_popcount(value_format1); - value_len2 = dwrite_popcount(value_format2); - pairvalue_len = FIELD_OFFSET(struct ot_gpos_pairvalue, data) + value_len1 * sizeof(WORD) + - value_len2 * sizeof(WORD); + /* Structure length is variable, but does not change across the subtable. */ + value_format1 = GET_BE_WORD(format1->value_format1) & 0xff; + value_format2 = GET_BE_WORD(format1->value_format2) & 0xff;
- pairset_offset = subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]); - pairset = table_read_ensure(&cache->gpos.table, subtable_offset + pairset_offset, - pairvalue_len * pairvalue_count); - if (!pairset) - continue; + value_len1 = dwrite_popcount(value_format1); + value_len2 = dwrite_popcount(value_format2); + pairvalue_len = FIELD_OFFSET(struct ot_gpos_pairvalue, data) + value_len1 * sizeof(WORD) + + value_len2 * sizeof(WORD);
- pairvalue = bsearch(&context->u.pos.glyphs[second_glyph], pairset->pairvalues, pairvalue_count, - pairvalue_len, gpos_pair_adjustment_compare_format1); - if (!pairvalue) - continue; + pairset_offset = subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]); + pairset = table_read_ensure(&cache->gpos.table, subtable_offset + pairset_offset, + pairvalue_len * pairvalue_count); + if (!pairset) + return FALSE;
- pairvalue += 1; /* Skip SecondGlyph. */ - opentype_layout_apply_gpos_value(context, pairset_offset, value_format1, pairvalue, first_glyph); - opentype_layout_apply_gpos_value(context, pairset_offset, value_format2, pairvalue + value_len1, - second_glyph); + pairvalue = bsearch(&context->u.pos.glyphs[second_glyph], pairset->pairvalues, pairvalue_count, + pairvalue_len, gpos_pair_adjustment_compare_format1); + if (!pairvalue) + return FALSE;
- iter->pos = iter_pair.pos; - if (value_len2) - iter->pos++; + pairvalue += 1; /* Skip SecondGlyph. */ + opentype_layout_apply_gpos_value(context, pairset_offset, value_format1, pairvalue, first_glyph); + opentype_layout_apply_gpos_value(context, pairset_offset, value_format2, pairvalue + value_len1, + second_glyph);
- return TRUE; - } - else if (format == 2) - { - const struct ot_gpos_pairpos_format2 *format2; - WORD class1_count, class2_count; - unsigned int class1, class2; - - value_format1 = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format1)) & 0xff; - value_format2 = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format2)) & 0xff; - - class1_count = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_pairpos_format2, class1_count)); - class2_count = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_pairpos_format2, class2_count)); - - value_len1 = dwrite_popcount(value_format1); - value_len2 = dwrite_popcount(value_format2); - - format2 = table_read_ensure(&cache->gpos.table, subtable_offset, - FIELD_OFFSET(struct ot_gpos_pairpos_format2, - values[class1_count * class2_count * (value_len1 + value_len2)])); - if (!format2) - continue; + context->cur = iter_pair.pos; + if (value_len2) + context->cur++; + } + else if (format == 2) + { + const struct ot_gpos_pairpos_format2 *format2; + WORD class1_count, class2_count; + unsigned int class1, class2; + const WCHAR *values; + + value_format1 = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format1)) & 0xff; + value_format2 = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format2)) & 0xff; + + class1_count = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class1_count)); + class2_count = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class2_count)); + + value_len1 = dwrite_popcount(value_format1); + value_len2 = dwrite_popcount(value_format2); + + format2 = table_read_ensure(&cache->gpos.table, subtable_offset, + FIELD_OFFSET(struct ot_gpos_pairpos_format2, + values[class1_count * class2_count * (value_len1 + value_len2)])); + if (!format2) + return FALSE;
- class1 = opentype_layout_get_glyph_class(&cache->gpos.table, subtable_offset + GET_BE_WORD(format2->class_def1), - context->u.pos.glyphs[first_glyph]); - class2 = opentype_layout_get_glyph_class(&cache->gpos.table, subtable_offset + GET_BE_WORD(format2->class_def2), - context->u.pos.glyphs[second_glyph]); + class1 = opentype_layout_get_glyph_class(&cache->gpos.table, subtable_offset + GET_BE_WORD(format2->class_def1), + context->u.pos.glyphs[first_glyph]); + class2 = opentype_layout_get_glyph_class(&cache->gpos.table, subtable_offset + GET_BE_WORD(format2->class_def2), + context->u.pos.glyphs[second_glyph]);
- if (class1 < class1_count && class2 < class2_count) - { - const WCHAR *values = &format2->values[(class1 * class2_count + class2) * (value_len1 + value_len2)]; - opentype_layout_apply_gpos_value(context, subtable_offset, value_format1, values, first_glyph); - opentype_layout_apply_gpos_value(context, subtable_offset, value_format2, values + value_len1, - second_glyph); + if (!(class1 < class1_count && class2 < class2_count)) + return FALSE;
- iter->pos = iter_pair.pos; - if (value_len2) - iter->pos++; + values = &format2->values[(class1 * class2_count + class2) * (value_len1 + value_len2)]; + opentype_layout_apply_gpos_value(context, subtable_offset, value_format1, values, first_glyph); + opentype_layout_apply_gpos_value(context, subtable_offset, value_format2, values + value_len1, + second_glyph);
- return TRUE; - } - } - else - { - WARN("Unknown pair adjustment format %u.\n", format); - continue; - } + context->cur = iter_pair.pos; + if (value_len2) + context->cur++; + } + else + { + WARN("Unknown pair adjustment format %u.\n", format); + return FALSE; }
- return FALSE; + return TRUE; }
static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context *context, unsigned int anchor_offset, @@ -3770,421 +3754,373 @@ static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context * }
static BOOL opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context *context, - struct glyph_iterator *iter, const struct ot_lookup *lookup) + const struct lookup *lookup, unsigned int subtable_offset) { struct scriptshaping_cache *cache = context->cache; - unsigned int i; + WORD format = table_read_be_word(&cache->gpos.table, subtable_offset); + UINT16 glyph = context->u.pos.glyphs[context->cur];
- for (i = 0; i < lookup->subtable_count; ++i) + if (format == 1) { - unsigned int subtable_offset = opentype_layout_get_gpos_subtable(cache, lookup->offset, i); - WORD format; - - format = table_read_be_word(&cache->gpos.table, subtable_offset); - - if (format == 1) - { - WORD coverage_offset = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_cursive_format1, coverage)); - unsigned int glyph_index, entry_count, entry_anchor, exit_anchor; - float entry_x, entry_y, exit_x, exit_y, delta; - struct glyph_iterator prev_iter; - - if (!coverage_offset) - continue; + WORD coverage_offset = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_cursive_format1, coverage)); + unsigned int glyph_index, entry_count, entry_anchor, exit_anchor; + float entry_x, entry_y, exit_x, exit_y, delta; + struct glyph_iterator prev_iter;
- entry_count = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_cursive_format1, count)); - - glyph_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage_offset, - context->u.pos.glyphs[iter->pos]); - if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count) - continue; + if (!coverage_offset) + return FALSE;
- entry_anchor = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2])); - if (!entry_anchor) - continue; + entry_count = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_cursive_format1, count));
- glyph_iterator_init(context, iter->flags, iter->pos, 1, &prev_iter); - if (!glyph_iterator_prev(&prev_iter)) - continue; + glyph_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage_offset, glyph); + if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count) + return FALSE;
- glyph_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage_offset, - context->u.pos.glyphs[prev_iter.pos]); - if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count) - continue; + entry_anchor = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2])); + if (!entry_anchor) + return FALSE;
- exit_anchor = table_read_be_word(&cache->gpos.table, subtable_offset + - FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2 + 1])); - if (!exit_anchor) - continue; + glyph_iterator_init(context, lookup->flags, context->cur, 1, &prev_iter); + if (!glyph_iterator_prev(&prev_iter)) + return FALSE;
- opentype_layout_gpos_get_anchor(context, subtable_offset + exit_anchor, prev_iter.pos, &exit_x, &exit_y); - opentype_layout_gpos_get_anchor(context, subtable_offset + entry_anchor, iter->pos, &entry_x, &entry_y); + glyph_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage_offset, + context->u.pos.glyphs[prev_iter.pos]); + if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count) + return FALSE;
- if (context->is_rtl) - { - delta = exit_x + context->offsets[prev_iter.pos].advanceOffset; - context->advances[prev_iter.pos] -= delta; - context->advances[iter->pos] = entry_x + context->offsets[iter->pos].advanceOffset; - context->offsets[prev_iter.pos].advanceOffset -= delta; - } - else - { - delta = entry_x + context->offsets[iter->pos].advanceOffset; - context->advances[prev_iter.pos] = exit_x + context->offsets[prev_iter.pos].advanceOffset; - context->advances[iter->pos] -= delta; - context->offsets[iter->pos].advanceOffset -= delta; - } + exit_anchor = table_read_be_word(&cache->gpos.table, subtable_offset + + FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2 + 1])); + if (!exit_anchor) + return FALSE;
- if (lookup->flags & LOOKUP_FLAG_RTL) - context->offsets[prev_iter.pos].ascenderOffset = entry_y - exit_y; - else - context->offsets[iter->pos].ascenderOffset = exit_y - entry_y; + opentype_layout_gpos_get_anchor(context, subtable_offset + exit_anchor, prev_iter.pos, &exit_x, &exit_y); + opentype_layout_gpos_get_anchor(context, subtable_offset + entry_anchor, context->cur, &entry_x, &entry_y);
- break; + if (context->is_rtl) + { + delta = exit_x + context->offsets[prev_iter.pos].advanceOffset; + context->advances[prev_iter.pos] -= delta; + context->advances[context->cur] = entry_x + context->offsets[context->cur].advanceOffset; + context->offsets[prev_iter.pos].advanceOffset -= delta; + } + else + { + delta = entry_x + context->offsets[context->cur].advanceOffset; + context->advances[prev_iter.pos] = exit_x + context->offsets[prev_iter.pos].advanceOffset; + context->advances[context->cur] -= delta; + context->offsets[context->cur].advanceOffset -= delta; } + + if (lookup->flags & LOOKUP_FLAG_RTL) + context->offsets[prev_iter.pos].ascenderOffset = entry_y - exit_y; else - WARN("Unknown cursive attachment format %u.\n", format); + context->offsets[context->cur].ascenderOffset = exit_y - entry_y;
+ context->cur++; + } + else + { + WARN("Unknown cursive attachment format %u.\n", format); + return FALSE; }
- return FALSE; + return TRUE; }
static BOOL opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context *context, - struct glyph_iterator *iter, const struct ot_lookup *lookup) + const struct lookup *lookup, unsigned int subtable_offset) { struct scriptshaping_cache *cache = context->cache; - unsigned int i; WORD format;
- for (i = 0; i < lookup->subtable_count; ++i) - { - unsigned int subtable_offset = opentype_layout_get_gpos_subtable(cache, lookup->offset, i); - - format = table_read_be_word(&cache->gpos.table, subtable_offset); + format = table_read_be_word(&cache->gpos.table, subtable_offset);
- if (format == 1) - { - const struct ot_gpos_mark_to_base_format1 *format1 = table_read_ensure(&cache->gpos.table, subtable_offset, - sizeof(*format1)); - unsigned int mark_class_count, count, mark_array_offset, base_array_offset; - const struct ot_gpos_mark_array *mark_array; - const struct ot_gpos_base_array *base_array; - float mark_x, mark_y, base_x, base_y; - unsigned int base_index, mark_index; - struct glyph_iterator base_iter; - unsigned int base_anchor; - - if (!format1) - continue; + if (format == 1) + { + const struct ot_gpos_mark_to_base_format1 *format1 = table_read_ensure(&cache->gpos.table, subtable_offset, + sizeof(*format1)); + unsigned int mark_class_count, count, mark_array_offset, base_array_offset; + const struct ot_gpos_mark_array *mark_array; + const struct ot_gpos_base_array *base_array; + float mark_x, mark_y, base_x, base_y; + unsigned int base_index, mark_index; + struct glyph_iterator base_iter; + unsigned int base_anchor; + + if (!format1) + return FALSE;
- mark_array_offset = subtable_offset + GET_BE_WORD(format1->mark_array); - if (!(count = table_read_be_word(&cache->gpos.table, mark_array_offset))) - continue; + mark_array_offset = subtable_offset + GET_BE_WORD(format1->mark_array); + if (!(count = table_read_be_word(&cache->gpos.table, mark_array_offset))) + return FALSE;
- mark_array = table_read_ensure(&cache->gpos.table, mark_array_offset, - FIELD_OFFSET(struct ot_gpos_mark_array, records[count])); - if (!mark_array) - continue; + mark_array = table_read_ensure(&cache->gpos.table, mark_array_offset, + FIELD_OFFSET(struct ot_gpos_mark_array, records[count])); + if (!mark_array) + return FALSE;
- base_array_offset = subtable_offset + GET_BE_WORD(format1->base_array); - if (!(count = table_read_be_word(&cache->gpos.table, base_array_offset))) - continue; + base_array_offset = subtable_offset + GET_BE_WORD(format1->base_array); + if (!(count = table_read_be_word(&cache->gpos.table, base_array_offset))) + return FALSE;
- base_array = table_read_ensure(&cache->gpos.table, base_array_offset, - FIELD_OFFSET(struct ot_gpos_base_array, offsets[count * GET_BE_WORD(format1->mark_class_count)])); - if (!base_array) - continue; + base_array = table_read_ensure(&cache->gpos.table, base_array_offset, + FIELD_OFFSET(struct ot_gpos_base_array, offsets[count * GET_BE_WORD(format1->mark_class_count)])); + if (!base_array) + return FALSE;
- mark_class_count = GET_BE_WORD(format1->mark_class_count); + mark_class_count = GET_BE_WORD(format1->mark_class_count);
- mark_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->mark_coverage), - context->u.pos.glyphs[iter->pos]); + mark_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->mark_coverage), + context->u.pos.glyphs[context->cur]);
- if (mark_index == GLYPH_NOT_COVERED || mark_index >= GET_BE_WORD(mark_array->count)) - continue; + if (mark_index == GLYPH_NOT_COVERED || mark_index >= GET_BE_WORD(mark_array->count)) + return FALSE;
- /* Look back for first base glyph. */ - glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, iter->pos, 1, &base_iter); - if (!glyph_iterator_prev(&base_iter)) - continue; + /* Look back for first base glyph. */ + glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &base_iter); + if (!glyph_iterator_prev(&base_iter)) + return FALSE;
- base_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->base_coverage), - context->u.pos.glyphs[base_iter.pos]); - if (base_index == GLYPH_NOT_COVERED || base_index >= GET_BE_WORD(base_array->count)) - continue; + base_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->base_coverage), + context->u.pos.glyphs[base_iter.pos]); + if (base_index == GLYPH_NOT_COVERED || base_index >= GET_BE_WORD(base_array->count)) + return FALSE;
- base_anchor = GET_BE_WORD(base_array->offsets[base_index * mark_class_count + - GET_BE_WORD(mark_array->records[mark_index].mark_class)]); + base_anchor = GET_BE_WORD(base_array->offsets[base_index * mark_class_count + + GET_BE_WORD(mark_array->records[mark_index].mark_class)]);
- opentype_layout_gpos_get_anchor(context, mark_array_offset + - GET_BE_WORD(mark_array->records[mark_index].mark_anchor), iter->pos, &mark_x, &mark_y); - opentype_layout_gpos_get_anchor(context, base_array_offset + base_anchor, base_iter.pos, &base_x, &base_y); + opentype_layout_gpos_get_anchor(context, mark_array_offset + + GET_BE_WORD(mark_array->records[mark_index].mark_anchor), context->cur, &mark_x, &mark_y); + opentype_layout_gpos_get_anchor(context, base_array_offset + base_anchor, base_iter.pos, &base_x, &base_y);
- context->offsets[iter->pos].advanceOffset = (context->is_rtl ? -1.0f : 1.0f) * (base_x - mark_x); - context->offsets[iter->pos].ascenderOffset = base_y - mark_y; + context->offsets[context->cur].advanceOffset = (context->is_rtl ? -1.0f : 1.0f) * (base_x - mark_x); + context->offsets[context->cur].ascenderOffset = base_y - mark_y;
- break; - } - else - WARN("Unknown mark-to-base format %u.\n", format); + context->cur++; + } + else + { + WARN("Unknown mark-to-base format %u.\n", format); + return FALSE; }
- return FALSE; + return TRUE; }
static BOOL opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context *context, - struct glyph_iterator *iter, const struct ot_lookup *lookup) + const struct lookup *lookup, unsigned int subtable_offset) { struct scriptshaping_cache *cache = context->cache; - unsigned int i; WORD format;
- for (i = 0; i < lookup->subtable_count; ++i) - { - unsigned int subtable_offset = opentype_layout_get_gpos_subtable(cache, lookup->offset, i); + format = table_read_be_word(&cache->gpos.table, subtable_offset);
- format = table_read_be_word(&cache->gpos.table, subtable_offset); - - if (format == 1) - { - const struct ot_gpos_mark_to_lig_format1 *format1 = table_read_ensure(&cache->gpos.table, - subtable_offset, sizeof(*format1)); - unsigned int mark_index, lig_index; - struct glyph_iterator lig_iter; + if (format == 1) + { + const struct ot_gpos_mark_to_lig_format1 *format1 = table_read_ensure(&cache->gpos.table, + subtable_offset, sizeof(*format1)); + unsigned int mark_index, lig_index; + struct glyph_iterator lig_iter;
- if (!format1) - continue; + if (!format1) + return FALSE;
- mark_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->mark_coverage), - context->u.pos.glyphs[iter->pos]); - if (mark_index == GLYPH_NOT_COVERED) - continue; + mark_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->mark_coverage), + context->u.pos.glyphs[context->cur]); + if (mark_index == GLYPH_NOT_COVERED) + return FALSE;
- glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, iter->pos, 1, &lig_iter); - if (!glyph_iterator_prev(&lig_iter)) - continue; + glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &lig_iter); + if (!glyph_iterator_prev(&lig_iter)) + return FALSE;
- lig_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->lig_coverage), - context->u.pos.glyphs[lig_iter.pos]); - if (lig_index == GLYPH_NOT_COVERED) - continue; + lig_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->lig_coverage), + context->u.pos.glyphs[lig_iter.pos]); + if (lig_index == GLYPH_NOT_COVERED) + return FALSE;
- FIXME("Unimplemented.\n"); - } - else - WARN("Unknown mark-to-ligature format %u.\n", format); + FIXME("Unimplemented.\n"); } + else + WARN("Unknown mark-to-ligature format %u.\n", format);
return FALSE; }
static BOOL opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context *context, - struct glyph_iterator *iter, const struct ot_lookup *lookup) + const struct lookup *lookup, unsigned int subtable_offset) { struct scriptshaping_cache *cache = context->cache; - unsigned int i; WORD format;
- for (i = 0; i < lookup->subtable_count; ++i) - { - unsigned int subtable_offset = opentype_layout_get_gpos_subtable(cache, lookup->offset, i); - - format = table_read_be_word(&cache->gpos.table, subtable_offset); + format = table_read_be_word(&cache->gpos.table, subtable_offset);
- if (format == 1) - { - const struct ot_gpos_mark_to_mark_format1 *format1 = table_read_ensure(&cache->gpos.table, - subtable_offset, sizeof(*format1)); - unsigned int count, mark1_array_offset, mark2_array_offset, mark_class_count; - unsigned int mark1_index, mark2_index, mark2_anchor; - const struct ot_gpos_mark_array *mark1_array; - const struct ot_gpos_base_array *mark2_array; - float mark1_x, mark1_y, mark2_x, mark2_y; - struct glyph_iterator mark_iter; - - if (!format1) - continue; + if (format == 1) + { + const struct ot_gpos_mark_to_mark_format1 *format1 = table_read_ensure(&cache->gpos.table, + subtable_offset, sizeof(*format1)); + unsigned int count, mark1_array_offset, mark2_array_offset, mark_class_count; + unsigned int mark1_index, mark2_index, mark2_anchor; + const struct ot_gpos_mark_array *mark1_array; + const struct ot_gpos_base_array *mark2_array; + float mark1_x, mark1_y, mark2_x, mark2_y; + struct glyph_iterator mark_iter; + + if (!format1) + return FALSE;
- mark1_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->mark1_coverage), - context->u.pos.glyphs[iter->pos]); + mark1_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->mark1_coverage), + context->u.pos.glyphs[context->cur]);
- mark1_array_offset = subtable_offset + GET_BE_WORD(format1->mark1_array); - if (!(count = table_read_be_word(&cache->gpos.table, mark1_array_offset))) - continue; + mark1_array_offset = subtable_offset + GET_BE_WORD(format1->mark1_array); + if (!(count = table_read_be_word(&cache->gpos.table, mark1_array_offset))) + return FALSE;
- mark1_array = table_read_ensure(&cache->gpos.table, mark1_array_offset, - FIELD_OFFSET(struct ot_gpos_mark_array, records[count])); - if (!mark1_array) - continue; + mark1_array = table_read_ensure(&cache->gpos.table, mark1_array_offset, + FIELD_OFFSET(struct ot_gpos_mark_array, records[count])); + if (!mark1_array) + return FALSE;
- if (mark1_index == GLYPH_NOT_COVERED || mark1_index >= count) - continue; + if (mark1_index == GLYPH_NOT_COVERED || mark1_index >= count) + return FALSE;
- glyph_iterator_init(context, lookup->flags & ~LOOKUP_FLAG_IGNORE_MASK, iter->pos, 1, &mark_iter); - if (!glyph_iterator_prev(&mark_iter)) - continue; + glyph_iterator_init(context, lookup->flags & ~LOOKUP_FLAG_IGNORE_MASK, context->cur, 1, &mark_iter); + if (!glyph_iterator_prev(&mark_iter)) + return FALSE;
- if (!context->u.pos.glyph_props[mark_iter.pos].isDiacritic) - continue; + if (!context->u.pos.glyph_props[mark_iter.pos].isDiacritic) + return FALSE;
- mark2_array_offset = subtable_offset + GET_BE_WORD(format1->mark2_array); - if (!(count = table_read_be_word(&cache->gpos.table, mark2_array_offset))) - continue; + mark2_array_offset = subtable_offset + GET_BE_WORD(format1->mark2_array); + if (!(count = table_read_be_word(&cache->gpos.table, mark2_array_offset))) + return FALSE;
- mark_class_count = GET_BE_WORD(format1->mark_class_count); + mark_class_count = GET_BE_WORD(format1->mark_class_count);
- mark2_array = table_read_ensure(&cache->gpos.table, mark2_array_offset, - FIELD_OFFSET(struct ot_gpos_base_array, offsets[count * mark_class_count])); - if (!mark2_array) - continue; + mark2_array = table_read_ensure(&cache->gpos.table, mark2_array_offset, + FIELD_OFFSET(struct ot_gpos_base_array, offsets[count * mark_class_count])); + if (!mark2_array) + return FALSE;
- mark2_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->mark2_coverage), - context->u.pos.glyphs[mark_iter.pos]); + mark2_index = opentype_layout_is_glyph_covered(context, subtable_offset + GET_BE_WORD(format1->mark2_coverage), + context->u.pos.glyphs[mark_iter.pos]);
- if (mark2_index == GLYPH_NOT_COVERED || mark2_index >= count) - continue; + if (mark2_index == GLYPH_NOT_COVERED || mark2_index >= count) + return FALSE;
- mark2_anchor = GET_BE_WORD(mark2_array->offsets[mark2_index * mark_class_count + - GET_BE_WORD(mark1_array->records[mark1_index].mark_class)]); - opentype_layout_gpos_get_anchor(context, mark1_array_offset + - GET_BE_WORD(mark1_array->records[mark1_index].mark_anchor), iter->pos, &mark1_x, &mark1_y); - opentype_layout_gpos_get_anchor(context, mark2_array_offset + mark2_anchor, mark_iter.pos, - &mark2_x, &mark2_y); + mark2_anchor = GET_BE_WORD(mark2_array->offsets[mark2_index * mark_class_count + + GET_BE_WORD(mark1_array->records[mark1_index].mark_class)]); + opentype_layout_gpos_get_anchor(context, mark1_array_offset + + GET_BE_WORD(mark1_array->records[mark1_index].mark_anchor), context->cur, &mark1_x, &mark1_y); + opentype_layout_gpos_get_anchor(context, mark2_array_offset + mark2_anchor, mark_iter.pos, + &mark2_x, &mark2_y);
- context->offsets[iter->pos].advanceOffset = mark2_x - mark1_x; - context->offsets[iter->pos].ascenderOffset = mark2_y - mark1_y; + context->offsets[context->cur].advanceOffset = mark2_x - mark1_x; + context->offsets[context->cur].ascenderOffset = mark2_y - mark1_y;
- break; - } - else - WARN("Unknown mark-to-mark format %u.\n", format); + context->cur++; + } + else + { + WARN("Unknown mark-to-mark format %u.\n", format); + return FALSE; }
- return FALSE; + return TRUE; }
static BOOL opentype_layout_apply_gpos_contextual_positioning(const struct scriptshaping_context *context, - struct glyph_iterator *iter, const struct ot_lookup *lookup) + const struct lookup *lookup, unsigned int subtable_offset) { return FALSE; }
static BOOL opentype_layout_apply_gpos_chaining_contextual_positioning(const struct scriptshaping_context *context, - struct glyph_iterator *iter, const struct ot_lookup *lookup) + const struct lookup *lookup, unsigned int subtable_offset) { return FALSE; }
-static void opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, int lookup_index) +static unsigned int opentype_layout_adjust_extension_subtable(struct scriptshaping_context *context, + unsigned int *subtable_offset) { - struct scriptshaping_cache *cache = context->cache; - const struct ot_lookup_table *lookup_table; - struct glyph_iterator iter; - struct ot_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; + const struct ot_gsubgpos_extension_format1 *format1;
- lookup.subtable_count = GET_BE_WORD(lookup_table->subtable_count); - if (!lookup.subtable_count) - return; + if (!(format1 = table_read_ensure(&context->table->table, *subtable_offset, sizeof(*format1)))) + return 0;
- lookup_type = GET_BE_WORD(lookup_table->lookup_type); - if (lookup_type == GPOS_LOOKUP_EXTENSION_POSITION) + if (GET_BE_WORD(format1->format) != 1) { - const struct ot_gsubgpos_extension_format1 *extension = table_read_ensure(&cache->gpos.table, - lookup.offset + GET_BE_WORD(lookup_table->subtable[0]), sizeof(*extension)); - WORD format; - - if (!extension) - return; + WARN("Unexpected extension table format %#x.\n", format1->format); + return 0; + }
- format = GET_BE_WORD(extension->format); - if (format != 1) - { - WARN("Unexpected extension table format %u.\n", format); - return; - } + *subtable_offset = *subtable_offset + GET_BE_DWORD(format1->extension_offset);
- lookup_type = GET_BE_WORD(extension->lookup_type); - } - lookup.flags = GET_BE_WORD(lookup_table->flags); + return GET_BE_WORD(format1->lookup_type); +}
- glyph_iterator_init(context, lookup.flags, 0, context->glyph_count, &iter); +static BOOL opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, const struct lookup *lookup) +{ + unsigned int i, lookup_type; + BOOL ret = FALSE;
- while (iter.pos < context->glyph_count) + for (i = 0; i < lookup->subtable_count; ++i) { - BOOL ret; + unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
- if (!glyph_iterator_match(&iter)) + if (lookup->type == GPOS_LOOKUP_EXTENSION_POSITION) { - ++iter.pos; - continue; + lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset); + if (!lookup_type) + continue; } + else + lookup_type = lookup->type;
switch (lookup_type) { case GPOS_LOOKUP_SINGLE_ADJUSTMENT: - ret = opentype_layout_apply_gpos_single_adjustment(context, &iter, &lookup); + ret = opentype_layout_apply_gpos_single_adjustment(context, lookup, subtable_offset); break; case GPOS_LOOKUP_PAIR_ADJUSTMENT: - ret = opentype_layout_apply_gpos_pair_adjustment(context, &iter, &lookup); + ret = opentype_layout_apply_gpos_pair_adjustment(context, lookup, subtable_offset); break; case GPOS_LOOKUP_CURSIVE_ATTACHMENT: - ret = opentype_layout_apply_gpos_cursive_attachment(context, &iter, &lookup); + ret = opentype_layout_apply_gpos_cursive_attachment(context, lookup, subtable_offset); break; case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT: - ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, &iter, &lookup); + ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, lookup, subtable_offset); break; case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT: - ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, &iter, &lookup); + ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, lookup, subtable_offset); break; case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT: - ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, &iter, &lookup); + ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, lookup, subtable_offset); break; case GPOS_LOOKUP_CONTEXTUAL_POSITION: - ret = opentype_layout_apply_gpos_contextual_positioning(context, &iter, &lookup); + ret = opentype_layout_apply_gpos_contextual_positioning(context, lookup, subtable_offset); break; case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION: - ret = opentype_layout_apply_gpos_chaining_contextual_positioning(context, &iter, &lookup); + ret = opentype_layout_apply_gpos_chaining_contextual_positioning(context, lookup, subtable_offset); 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; + if (ret) + break; } -} - -struct lookup -{ - unsigned short index; - unsigned short type; - unsigned short flags; - unsigned short subtable_count;
- unsigned int mask; - unsigned int offset; -}; + return ret; +}
struct lookups { @@ -4489,6 +4425,7 @@ void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, { struct lookups lookups = { 0 }; unsigned int i; + BOOL ret;
opentype_layout_collect_lookups(context, script_index, language_index, features, &context->cache->gpos, &lookups);
@@ -4497,7 +4434,24 @@ void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, opentype_layout_set_glyph_masks(context, features);
for (i = 0; i < lookups.count; ++i) - opentype_layout_apply_gpos_lookup(context, lookups.lookups[i].index); + { + const struct lookup *lookup = &lookups.lookups[i]; + + context->cur = 0; + while (context->cur < context->glyph_count) + { + ret = FALSE; + + if ((context->glyph_infos[context->cur].mask & lookup->mask) && + lookup_is_glyph_match(context->glyph_infos[context->cur].props, lookup->flags)) + { + ret = opentype_layout_apply_gpos_lookup(context, lookup); + } + + if (!ret) + context->cur++; + } + }
heap_free(lookups.lookups); } @@ -4845,25 +4799,6 @@ static BOOL opentype_layout_apply_gsub_chain_context_substitution(struct scripts return ret; }
-static unsigned int opentype_layout_adjust_extension_subtable(struct scriptshaping_context *context, - unsigned int *subtable_offset) -{ - const struct ot_gsubgpos_extension_format1 *format1; - - if (!(format1 = table_read_ensure(&context->table->table, *subtable_offset, sizeof(*format1)))) - return 0; - - if (GET_BE_WORD(format1->format) != 1) - { - WARN("Unexpected extension table format %#x.\n", format1->format); - return 0; - } - - *subtable_offset = *subtable_offset + GET_BE_DWORD(format1->extension_offset); - - return GET_BE_WORD(format1->lookup_type); -} - static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, const struct lookup *lookup) { unsigned int i, lookup_type; @@ -4871,7 +4806,7 @@ static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *cont
for (i = 0; i < lookup->subtable_count; ++i) { - unsigned int subtable_offset = opentype_layout_get_gsub_subtable(context, lookup->offset, i); + unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
if (lookup->type == GSUB_LOOKUP_EXTENSION_SUBST) {
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/opentype.c | 173 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 1 deletion(-)
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 88861863034..e31057710b4 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -569,6 +569,14 @@ struct ot_gsub_singlesubst_format2 UINT16 substitutes[1]; };
+struct ot_gsub_multsubst_format1 +{ + UINT16 format; + UINT16 coverage; + UINT16 seq_count; + UINT16 seq[1]; +}; + struct ot_gsub_altsubst_format1 { UINT16 format; @@ -4506,6 +4514,167 @@ static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_ return ret; }
+static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *context, unsigned int count) +{ + DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props; + struct shaping_glyph_info *glyph_infos; + unsigned int new_capacity; + UINT16 *glyphs; + BOOL ret; + + if (context->u.subst.capacity >= count) + return TRUE; + + new_capacity = context->u.subst.capacity * 2; + + if ((glyphs = heap_realloc(context->u.subst.glyphs, new_capacity * sizeof(*glyphs)))) + context->u.subst.glyphs = glyphs; + if ((glyph_props = heap_realloc(context->u.subst.glyph_props, new_capacity * sizeof(*glyph_props)))) + context->u.subst.glyph_props = glyph_props; + if ((glyph_infos = heap_realloc(context->glyph_infos, new_capacity * sizeof(*glyph_infos)))) + context->glyph_infos = glyph_infos; + + if ((ret = (glyphs && glyph_props && glyph_infos))) + context->u.subst.capacity = new_capacity; + + return ret; +} + +static unsigned int opentype_layout_get_next_char_index(const struct scriptshaping_context *context, unsigned int idx) +{ + unsigned int start = 0, end = context->length - 1, mid, ret = ~0u; + + for (;;) + { + mid = (start + end) / 2; + + if (context->u.buffer.clustermap[mid] <= idx) + { + if (mid == end) break; + start = mid + 1; + } + else + { + ret = mid; + if (mid == start) break; + end = mid - 1; + } + } + + return ret; +} + +static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context *context, const struct lookup *lookup, + unsigned int subtable_offset) +{ + const struct dwrite_fonttable *gsub = &context->table->table; + UINT16 format, coverage, orig_glyph, glyph, seq_count, glyph_count; + const struct ot_gsub_multsubst_format1 *format1; + unsigned int i, idx, coverage_index; + const UINT16 *glyphs; + + idx = context->cur; + orig_glyph = glyph = context->u.subst.glyphs[idx]; + + format = table_read_be_word(gsub, subtable_offset); + + coverage = table_read_be_word(gsub, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, coverage)); + + if (format == 1) + { + coverage_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage, glyph); + if (coverage_index == GLYPH_NOT_COVERED) + return FALSE; + + seq_count = table_read_be_word(gsub, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, seq_count)); + + format1 = table_read_ensure(gsub, subtable_offset, FIELD_OFFSET(struct ot_gsub_multsubst_format1, seq[seq_count])); + + if (!seq_count || seq_count <= coverage_index || !format1) + return FALSE; + + glyph_count = table_read_be_word(gsub, subtable_offset + GET_BE_WORD(format1->seq[coverage_index])); + + glyphs = table_read_ensure(gsub, subtable_offset + GET_BE_WORD(format1->seq[coverage_index]) + 2, + glyph_count * sizeof(*glyphs)); + if (!glyphs) + return FALSE; + + if (glyph_count == 1) + { + /* Equivalent of single substitution. */ + glyph = GET_BE_WORD(glyphs[0]); + + if (glyph != orig_glyph) + { + context->u.subst.glyphs[idx] = glyph; + opentype_set_subst_glyph_props(context, idx, glyph); + } + + context->cur++; + } + else if (glyph_count == 0) + { + context->cur++; + } + else + { + unsigned int shift_len, src_idx, dest_idx, mask; + + /* Current glyph is also replaced. */ + glyph_count--; + + if (!(opentype_layout_gsub_ensure_buffer(context, context->glyph_count + glyph_count))) + return FALSE; + + shift_len = context->cur + 1 < context->glyph_count ? context->glyph_count - context->cur - 1 : 0; + + if (shift_len) + { + src_idx = context->cur + 1; + dest_idx = src_idx + glyph_count; + + memmove(&context->u.subst.glyphs[dest_idx], &context->u.subst.glyphs[src_idx], + shift_len * sizeof(*context->u.subst.glyphs)); + memmove(&context->u.subst.glyph_props[dest_idx], &context->u.subst.glyph_props[src_idx], + shift_len * sizeof(*context->u.subst.glyph_props)); + memmove(&context->glyph_infos[dest_idx], &context->glyph_infos[src_idx], + shift_len * sizeof(*context->glyph_infos)); + } + + mask = context->glyph_infos[context->cur].mask; + for (i = 0, idx = context->cur; i <= glyph_count; ++i) + { + glyph = GET_BE_WORD(glyphs[i]); + context->u.subst.glyphs[idx + i] = glyph; + if (i) + context->u.subst.glyph_props[idx + i].isClusterStart = 0; + opentype_set_subst_glyph_props(context, idx + i, glyph); + /* Inherit feature mask from original matched glyph. */ + context->glyph_infos[idx + i].mask = mask; + } + + /* Update following clusters. */ + idx = opentype_layout_get_next_char_index(context, context->cur); + if (idx != ~0u) + { + for (i = idx; i < context->length; ++i) + context->u.subst.clustermap[i] += glyph_count; + } + + context->cur += glyph_count + 1; + context->glyph_count += glyph_count; + } + } + else + { + WARN("Unknown multiple substitution format %u.\n", format); + return FALSE; + } + + return TRUE; +} + static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context *context, const struct lookup *lookup, unsigned int subtable_offset) { @@ -4822,13 +4991,15 @@ static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *cont case GSUB_LOOKUP_SINGLE_SUBST: ret = opentype_layout_apply_gsub_single_substitution(context, lookup, subtable_offset); break; + case GSUB_LOOKUP_MULTIPLE_SUBST: + ret = opentype_layout_apply_gsub_mult_substitution(context, lookup, subtable_offset); + break; case GSUB_LOOKUP_ALTERNATE_SUBST: ret = opentype_layout_apply_gsub_alt_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_MULTIPLE_SUBST: case GSUB_LOOKUP_LIGATURE_SUBST: case GSUB_LOOKUP_CONTEXTUAL_SUBST: case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/dwrite_private.h | 13 ++++++++++++- dlls/dwrite/opentype.c | 10 +++++++--- 2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 2ed53658cd2..94705b0d636 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -468,6 +468,17 @@ struct shaping_glyph_info unsigned int props; };
+struct shaping_glyph_properties +{ + UINT16 justification : 4; + UINT16 isClusterStart : 1; + UINT16 isDiacritic : 1; + UINT16 isZeroWidthSpace : 1; + UINT16 reserved : 1; + UINT16 components : 4; + UINT16 reserved2 : 4; +}; + struct scriptshaping_context { struct scriptshaping_cache *cache; @@ -498,7 +509,7 @@ struct scriptshaping_context struct { UINT16 *glyphs; - DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props; + struct shaping_glyph_properties *glyph_props; UINT16 *clustermap; } buffer; } u; diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index e31057710b4..92061cbf5ca 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -4648,7 +4648,10 @@ static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_co glyph = GET_BE_WORD(glyphs[i]); context->u.subst.glyphs[idx + i] = glyph; if (i) + { context->u.subst.glyph_props[idx + i].isClusterStart = 0; + context->u.buffer.glyph_props[idx + i].components = 0; + } opentype_set_subst_glyph_props(context, idx + i, glyph); /* Inherit feature mask from original matched glyph. */ context->glyph_infos[idx + i].mask = mask; @@ -5081,11 +5084,12 @@ static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, c codepoint = context->u.subst.digits[codepoint - '0'];
context->u.subst.glyphs[g] = font->get_glyph(context->cache->context, codepoint); - context->u.subst.glyph_props[g].justification = SCRIPT_JUSTIFY_CHARACTER; - context->u.subst.glyph_props[g].isClusterStart = 1; + context->u.buffer.glyph_props[g].justification = SCRIPT_JUSTIFY_CHARACTER; + context->u.buffer.glyph_props[g].isClusterStart = 1; opentype_set_subst_glyph_props(context, g, context->u.subst.glyphs[g]); if (opentype_is_default_ignorable(codepoint)) - context->u.subst.glyph_props[g].isZeroWidthSpace = 1; + context->u.buffer.glyph_props[g].isZeroWidthSpace = 1; + context->u.buffer.glyph_props[g].components = 1; context->glyph_count++;
clustermap[i] = i;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/opentype.c | 154 +++++++++++++++++++++++++++++++++-------- 1 file changed, 126 insertions(+), 28 deletions(-)
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 92061cbf5ca..c6cfdb6133b 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -585,6 +585,27 @@ struct ot_gsub_altsubst_format1 UINT16 sets[1]; };
+struct ot_gsub_ligsubst_format1 +{ + UINT16 format; + UINT16 coverage; + UINT16 lig_set_count; + UINT16 lig_sets[1]; +}; + +struct ot_gsub_ligset +{ + UINT16 count; + UINT16 offsets[1]; +}; + +struct ot_gsub_lig +{ + UINT16 lig_glyph; + UINT16 comp_count; + UINT16 components[1]; +}; + struct ot_gsub_chaincontext_subst_format1 { UINT16 format; @@ -4464,13 +4485,23 @@ void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, heap_free(lookups.lookups); }
+static void opentype_layout_replace_glyph(struct scriptshaping_context *context, UINT16 glyph) +{ + UINT16 orig_glyph = context->u.subst.glyphs[context->cur]; + if (glyph != orig_glyph) + { + context->u.subst.glyphs[context->cur] = glyph; + opentype_set_subst_glyph_props(context, context->cur, glyph); + } + context->cur++; +} + static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context *context, const struct lookup *lookup, unsigned int subtable_offset) { const struct dwrite_fonttable *gsub = &context->table->table; UINT16 format, coverage, orig_glyph, glyph; unsigned int idx, coverage_index; - BOOL ret;
idx = context->cur; orig_glyph = glyph = context->u.subst.glyphs[idx]; @@ -4502,16 +4533,14 @@ static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_ glyph = GET_BE_WORD(format2->substitutes[coverage_index]); } else - WARN("Unknown single substitution format %u.\n", format); - - if ((ret = (glyph != orig_glyph))) { - context->u.subst.glyphs[idx] = glyph; - opentype_set_subst_glyph_props(context, idx, glyph); - context->cur++; + WARN("Unknown single substitution format %u.\n", format); + return FALSE; }
- return ret; + opentype_layout_replace_glyph(context, glyph); + + return TRUE; }
static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *context, unsigned int count) @@ -4568,13 +4597,13 @@ static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_co unsigned int subtable_offset) { const struct dwrite_fonttable *gsub = &context->table->table; - UINT16 format, coverage, orig_glyph, glyph, seq_count, glyph_count; + UINT16 format, coverage, glyph, seq_count, glyph_count; const struct ot_gsub_multsubst_format1 *format1; unsigned int i, idx, coverage_index; const UINT16 *glyphs;
idx = context->cur; - orig_glyph = glyph = context->u.subst.glyphs[idx]; + glyph = context->u.subst.glyphs[idx];
format = table_read_be_word(gsub, subtable_offset);
@@ -4603,15 +4632,7 @@ static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_co if (glyph_count == 1) { /* Equivalent of single substitution. */ - glyph = GET_BE_WORD(glyphs[0]); - - if (glyph != orig_glyph) - { - context->u.subst.glyphs[idx] = glyph; - opentype_set_subst_glyph_props(context, idx, glyph); - } - - context->cur++; + opentype_layout_replace_glyph(context, GET_BE_WORD(glyphs[0])); } else if (glyph_count == 0) { @@ -4682,12 +4703,11 @@ static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_con unsigned int subtable_offset) { const struct dwrite_fonttable *gsub = &context->table->table; - UINT16 format, coverage, orig_glyph, glyph; unsigned int idx, coverage_index, offset; - BOOL ret; + UINT16 format, coverage, glyph;
idx = context->cur; - orig_glyph = glyph = context->u.subst.glyphs[idx]; + glyph = context->u.subst.glyphs[idx];
format = table_read_be_word(gsub, subtable_offset);
@@ -4721,16 +4741,92 @@ static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_con glyph = table_read_be_word(gsub, subtable_offset + offset + sizeof(count) + (alt_index - 1) * sizeof(glyph)); } else + { WARN("Unexpected alternate substitution format %d.\n", format); + return FALSE; + } + + opentype_layout_replace_glyph(context, glyph); + + return TRUE; +} + +static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset) +{ + const struct dwrite_fonttable *gsub = &context->table->table; + const struct ot_gsub_lig *lig; + unsigned int comp_count; + + comp_count = table_read_be_word(gsub, offset + FIELD_OFFSET(struct ot_gsub_lig, comp_count)); + + if (!comp_count) + return FALSE; + + lig = table_read_ensure(gsub, offset, FIELD_OFFSET(struct ot_gsub_lig, components[comp_count-1])); + if (!lig) + return FALSE;
- if ((ret = (glyph != orig_glyph))) + if (comp_count == 1) { - context->u.subst.glyphs[idx] = glyph; - opentype_set_subst_glyph_props(context, idx, glyph); - context->cur++; + opentype_layout_replace_glyph(context, GET_BE_WORD(lig->lig_glyph)); + return TRUE; }
- return ret; + /* TODO: actually apply ligature */ + + return FALSE; +} + +static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context *context, const struct lookup *lookup, + unsigned int subtable_offset) +{ + const struct dwrite_fonttable *gsub = &context->table->table; + unsigned int coverage_index, offset, lig_set_offset, lig_set_count; + UINT16 format, coverage, glyph; + + glyph = context->u.subst.glyphs[context->cur]; + + format = table_read_be_word(gsub, subtable_offset); + + if (format == 1) + { + const struct ot_gsub_ligsubst_format1 *format1 = table_read_ensure(gsub, subtable_offset, sizeof(*format1)); + const struct ot_gsub_ligset *lig_set; + unsigned int i, lig_count; + + coverage = table_read_be_word(gsub, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, coverage)); + + coverage_index = opentype_layout_is_glyph_covered(context, subtable_offset + coverage, glyph); + if (coverage_index == GLYPH_NOT_COVERED) + return FALSE; + + lig_set_count = table_read_be_word(gsub, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_set_count)); + if (coverage_index >= lig_set_count || !table_read_ensure(gsub, subtable_offset, + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_sets[lig_set_count]))) + { + return FALSE; + } + + lig_set_offset = table_read_be_word(gsub, subtable_offset + + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_sets[coverage_index])); + offset = subtable_offset + lig_set_offset; + + lig_count = table_read_be_word(gsub, offset); + lig_set = table_read_ensure(gsub, offset, FIELD_OFFSET(struct ot_gsub_ligset, offsets[lig_count])); + if (!lig_count || !lig_set) + return FALSE; + + /* First applicable ligature is used. */ + for (i = 0; i < lig_count; ++i) + { + if (opentype_layout_apply_ligature(context, offset + GET_BE_WORD(lig_set->offsets[i]))) + return TRUE; + } + } + else + WARN("Unexpected ligature substitution format %d.\n", format); + + return FALSE; }
#define CHAIN_CONTEXT_MAX_LENGTH 64 @@ -5000,10 +5096,12 @@ static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *cont case GSUB_LOOKUP_ALTERNATE_SUBST: ret = opentype_layout_apply_gsub_alt_substitution(context, lookup, subtable_offset); break; + case GSUB_LOOKUP_LIGATURE_SUBST: + ret = opentype_layout_apply_gsub_lig_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_LIGATURE_SUBST: case GSUB_LOOKUP_CONTEXTUAL_SUBST: case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST: WARN("Unimplemented lookup %d.\n", lookup->type);