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:
parent
5eb742687d
commit
a6be957919
|
@ -1252,6 +1252,10 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
|
||||||
context.length = length;
|
context.length = length;
|
||||||
context.is_rtl = is_rtl;
|
context.is_rtl = is_rtl;
|
||||||
context.is_sideways = is_sideways;
|
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);
|
context.language_tag = get_opentype_language(locale);
|
||||||
|
|
||||||
script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
|
script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
|
||||||
|
|
|
@ -478,6 +478,12 @@ struct scriptshaping_context
|
||||||
const UINT16 *glyphs;
|
const UINT16 *glyphs;
|
||||||
const DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
|
const DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
|
||||||
} pos;
|
} pos;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
UINT16 *glyphs;
|
||||||
|
DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
|
||||||
|
unsigned int max_glyph_count;
|
||||||
|
} subst;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
unsigned int glyph_count;
|
unsigned int glyph_count;
|
||||||
|
|
|
@ -507,6 +507,18 @@ enum gpos_lookup_type
|
||||||
GPOS_LOOKUP_EXTENSION_POSITION = 9,
|
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
|
enum gpos_value_format
|
||||||
{
|
{
|
||||||
GPOS_VALUE_X_PLACEMENT = 0x1,
|
GPOS_VALUE_X_PLACEMENT = 0x1,
|
||||||
|
@ -528,13 +540,28 @@ enum OPENTYPE_PLATFORM_ID
|
||||||
OPENTYPE_PLATFORM_CUSTOM
|
OPENTYPE_PLATFORM_CUSTOM
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ot_gpos_extensionpos_format1
|
struct ot_gsubgpos_extensionpos_format1
|
||||||
{
|
{
|
||||||
WORD format;
|
UINT16 format;
|
||||||
WORD lookup_type;
|
UINT16 lookup_type;
|
||||||
DWORD extension_offset;
|
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
|
struct ot_feature
|
||||||
{
|
{
|
||||||
WORD feature_params;
|
WORD feature_params;
|
||||||
|
@ -740,12 +767,6 @@ typedef struct {
|
||||||
|
|
||||||
#include "poppack.h"
|
#include "poppack.h"
|
||||||
|
|
||||||
enum gsub_lookup_type
|
|
||||||
{
|
|
||||||
GSUB_LOOKUP_SINGLE_SUBST = 1,
|
|
||||||
GSUB_LOOKUP_EXTENSION_SUBST = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TT_NAME_WINDOWS_ENCODING_ID
|
enum TT_NAME_WINDOWS_ENCODING_ID
|
||||||
{
|
{
|
||||||
TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
|
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]));
|
FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
|
||||||
if (lookup_type == GPOS_LOOKUP_EXTENSION_POSITION)
|
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));
|
lookup_offset + subtable_offset, sizeof(*format1));
|
||||||
subtable_offset += GET_BE_DWORD(format1->extension_offset);
|
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);
|
lookup_type = GET_BE_WORD(lookup_table->lookup_type);
|
||||||
if (lookup_type == GPOS_LOOKUP_EXTENSION_POSITION)
|
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));
|
lookup.offset + GET_BE_WORD(lookup_table->subtable[0]), sizeof(*extension));
|
||||||
WORD format;
|
WORD format;
|
||||||
|
|
||||||
|
@ -4157,9 +4194,115 @@ void opentype_layout_apply_gpos_features(struct scriptshaping_context *context,
|
||||||
heap_free(lookups.indexes);
|
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,
|
static void opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, unsigned int first_glyph,
|
||||||
unsigned int glyph_count, int lookup_index)
|
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,
|
HRESULT opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int script_index,
|
||||||
|
|
Loading…
Reference in New Issue