[PATCH 2/5] dwrite: Use GPOS handlers for subtables, not whole lookups.

Nikolay Sivov nsivov at codeweavers.com
Thu May 28 06:54:26 CDT 2020


Signed-off-by: Nikolay Sivov <nsivov at 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)
         {
-- 
2.26.2




More information about the wine-devel mailing list