dwrite: Implement single substitution lookup (GSUB lookup 1).

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2020-05-04 11:29:34 +03:00 committed by Alexandre Julliard
parent 5eb742687d
commit a6be957919
3 changed files with 164 additions and 11 deletions

View File

@ -1252,6 +1252,10 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
context.length = length;
context.is_rtl = is_rtl;
context.is_sideways = is_sideways;
context.u.subst.glyphs = glyphs;
context.u.subst.glyph_props = glyph_props;
context.u.subst.max_glyph_count = max_glyph_count;
context.glyph_count = g;
context.language_tag = get_opentype_language(locale);
script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;

View File

@ -478,6 +478,12 @@ struct scriptshaping_context
const UINT16 *glyphs;
const DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
} pos;
struct
{
UINT16 *glyphs;
DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
unsigned int max_glyph_count;
} subst;
} u;
unsigned int glyph_count;

View File

@ -507,6 +507,18 @@ enum gpos_lookup_type
GPOS_LOOKUP_EXTENSION_POSITION = 9,
};
enum gsub_lookup_type
{
GSUB_LOOKUP_SINGLE_SUBST = 1,
GSUB_LOOKUP_MULTIPLE_SUBST = 2,
GSUB_LOOKUP_ALTERNATE_SUBST = 3,
GSUB_LOOKUP_LIGATURE_SUBST = 4,
GSUB_LOOKUP_CONTEXTUAL_SUBST = 5,
GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST = 6,
GSUB_LOOKUP_EXTENSION_SUBST = 7,
GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST = 8,
};
enum gpos_value_format
{
GPOS_VALUE_X_PLACEMENT = 0x1,
@ -528,13 +540,28 @@ enum OPENTYPE_PLATFORM_ID
OPENTYPE_PLATFORM_CUSTOM
};
struct ot_gpos_extensionpos_format1
struct ot_gsubgpos_extensionpos_format1
{
WORD format;
WORD lookup_type;
UINT16 format;
UINT16 lookup_type;
DWORD extension_offset;
};
struct ot_gsub_singlesubst_format1
{
UINT16 format;
UINT16 coverage;
short delta;
};
struct ot_gsub_singlesubst_format2
{
UINT16 format;
UINT16 coverage;
UINT16 count;
UINT16 substitutes[1];
};
struct ot_feature
{
WORD feature_params;
@ -740,12 +767,6 @@ typedef struct {
#include "poppack.h"
enum gsub_lookup_type
{
GSUB_LOOKUP_SINGLE_SUBST = 1,
GSUB_LOOKUP_EXTENSION_SUBST = 7,
};
enum TT_NAME_WINDOWS_ENCODING_ID
{
TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
@ -3286,7 +3307,23 @@ static unsigned int opentype_layout_get_gpos_subtable(const struct scriptshaping
FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
if (lookup_type == GPOS_LOOKUP_EXTENSION_POSITION)
{
const struct ot_gpos_extensionpos_format1 *format1 = table_read_ensure(&cache->gpos.table,
const struct ot_gsubgpos_extensionpos_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_cache *cache,
unsigned int lookup_offset, unsigned int subtable)
{
UINT16 lookup_type = table_read_be_word(&cache->gsub.table, lookup_offset);
unsigned int subtable_offset = table_read_be_word(&cache->gsub.table, lookup_offset +
FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
if (lookup_type == GSUB_LOOKUP_EXTENSION_SUBST)
{
const struct ot_gsubgpos_extensionpos_format1 *format1 = table_read_ensure(&cache->gsub.table,
lookup_offset + subtable_offset, sizeof(*format1));
subtable_offset += GET_BE_DWORD(format1->extension_offset);
}
@ -3971,7 +4008,7 @@ static void opentype_layout_apply_gpos_lookup(struct scriptshaping_context *cont
lookup_type = GET_BE_WORD(lookup_table->lookup_type);
if (lookup_type == GPOS_LOOKUP_EXTENSION_POSITION)
{
const struct ot_gpos_extensionpos_format1 *extension = table_read_ensure(&cache->gpos.table,
const struct ot_gsubgpos_extensionpos_format1 *extension = table_read_ensure(&cache->gpos.table,
lookup.offset + GET_BE_WORD(lookup_table->subtable[0]), sizeof(*extension));
WORD format;
@ -4157,9 +4194,115 @@ void opentype_layout_apply_gpos_features(struct scriptshaping_context *context,
heap_free(lookups.indexes);
}
static BOOL opentype_layout_apply_gsub_single_substitution(struct glyph_iterator *iter, const struct lookup *lookup)
{
struct scriptshaping_cache *cache = iter->context->cache;
UINT16 format, coverage;
unsigned int i;
for (i = 0; i < lookup->subtable_count; ++i)
{
unsigned int subtable_offset = opentype_layout_get_gsub_subtable(cache, lookup->offset, i);
UINT16 glyph = iter->context->u.subst.glyphs[iter->pos];
unsigned int coverage_index;
format = table_read_be_word(&cache->gsub.table, subtable_offset);
coverage = table_read_be_word(&cache->gsub.table, subtable_offset +
FIELD_OFFSET(struct ot_gsub_singlesubst_format1, coverage));
if (format == 1)
{
const struct ot_gsub_singlesubst_format1 *format1 = table_read_ensure(&cache->gsub.table, subtable_offset,
sizeof(*format1));
coverage_index = opentype_layout_is_glyph_covered(&cache->gsub.table, subtable_offset + coverage, glyph);
if (coverage_index == GLYPH_NOT_COVERED)
continue;
iter->context->u.subst.glyphs[iter->pos] = glyph + GET_BE_WORD(format1->delta);
break;
}
else if (format == 2)
{
UINT16 count = table_read_be_word(&cache->gsub.table, subtable_offset +
FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count));
const struct ot_gsub_singlesubst_format2 *format2 = table_read_ensure(&cache->gsub.table, subtable_offset,
FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count) + count * sizeof(UINT16));
coverage_index = opentype_layout_is_glyph_covered(&cache->gsub.table, subtable_offset + coverage, glyph);
if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= count)
continue;
iter->context->u.subst.glyphs[iter->pos] = GET_BE_WORD(format2->substitutes[coverage_index]);
break;
}
else
WARN("Unknown single substitution format %u.\n", format);
}
return FALSE;
}
static void opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, unsigned int first_glyph,
unsigned int glyph_count, int lookup_index)
{
struct ot_gsubgpos_table *table = &context->cache->gsub;
const struct ot_lookup_table *lookup_table;
struct glyph_iterator iter;
struct lookup lookup;
WORD lookup_type;
lookup.offset = table_read_be_word(&table->table, table->lookup_list + FIELD_OFFSET(struct ot_lookup_list, lookup[lookup_index]));
if (!lookup.offset)
return;
lookup.offset += table->lookup_list;
if (!(lookup_table = table_read_ensure(&table->table, lookup.offset, sizeof(*lookup_table))))
return;
lookup.subtable_count = GET_BE_WORD(lookup_table->subtable_count);
if (!lookup.subtable_count)
return;
lookup_type = GET_BE_WORD(lookup_table->lookup_type);
lookup.flags = GET_BE_WORD(lookup_table->flags);
glyph_iterator_init(context, lookup.flags, first_glyph, glyph_count, &iter);
while (iter.pos < first_glyph + iter.len)
{
BOOL ret;
if (!glyph_iterator_match(&iter))
{
++iter.pos;
continue;
}
switch (lookup_type)
{
case GSUB_LOOKUP_SINGLE_SUBST:
ret = opentype_layout_apply_gsub_single_substitution(&iter, &lookup);
break;
case GSUB_LOOKUP_MULTIPLE_SUBST:
case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
case GSUB_LOOKUP_ALTERNATE_SUBST:
case GSUB_LOOKUP_LIGATURE_SUBST:
case GSUB_LOOKUP_CONTEXTUAL_SUBST:
case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
WARN("Unimplemented lookup %d.\n", lookup_type);
break;
default:
WARN("Unknown lookup type %u.\n", lookup_type);
return;
}
/* Some lookups update position after making changes. */
if (!ret)
++iter.pos;
}
}
HRESULT opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int script_index,