dwrite: Implement masking with positional lookup flags.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
stable
Nikolay Sivov 2019-01-31 11:35:30 +03:00 committed by Alexandre Julliard
parent b2d6fbe100
commit cb39c9853c
4 changed files with 136 additions and 1 deletions

View File

@ -1313,6 +1313,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
context.text = text;
context.length = text_len;
context.is_rtl = is_rtl;
context.u.pos.glyphs = glyphs;
context.u.pos.glyph_props = glyph_props;
context.glyph_count = glyph_count;
context.advances = advances;
@ -1374,6 +1375,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWrite
context.text = text;
context.length = text_len;
context.is_rtl = is_rtl;
context.u.pos.glyphs = glyphs;
context.u.pos.glyph_props = glyph_props;
context.glyph_count = glyph_count;
context.advances = advances;

View File

@ -366,6 +366,12 @@ struct scriptshaping_cache
unsigned int feature_list;
unsigned int lookup_list;
} gpos;
struct
{
struct dwrite_fonttable table;
unsigned int classdef;
} gdef;
};
struct scriptshaping_context
@ -381,6 +387,7 @@ struct scriptshaping_context
{
struct
{
const UINT16 *glyphs;
const DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
} pos;
} u;

View File

@ -32,6 +32,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
#define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2')
#define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t')
#define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
#define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
#define MS_GPOS_TAG DWRITE_MAKE_OPENTYPE_TAG('G','P','O','S')
#define MS_GSUB_TAG DWRITE_MAKE_OPENTYPE_TAG('G','S','U','B')
#define MS_NAME_TAG DWRITE_MAKE_OPENTYPE_TAG('n','a','m','e')
@ -416,6 +417,47 @@ struct ot_script_list
struct ot_script_record scripts[1];
};
enum ot_gdef_class
{
GDEF_CLASS_UNCLASSIFIED = 0,
GDEF_CLASS_BASE = 1,
GDEF_CLASS_LIGATURE = 2,
GDEF_CLASS_MARK = 3,
GDEF_CLASS_COMPONENT = 4,
GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
};
struct gdef_header
{
DWORD version;
WORD classdef;
WORD attach_list;
WORD ligcaret_list;
WORD markattach_classdef;
};
struct ot_gdef_classdef_format1
{
WORD format;
WORD start_glyph;
WORD glyph_count;
WORD classes[1];
};
struct ot_gdef_class_range
{
WORD start_glyph;
WORD end_glyph;
WORD glyph_class;
};
struct ot_gdef_classdef_format2
{
WORD format;
WORD range_count;
struct ot_gdef_class_range ranges[1];
};
struct gpos_gsub_header
{
DWORD version;
@ -424,6 +466,16 @@ struct gpos_gsub_header
WORD lookup_list;
};
enum gsub_gpos_lookup_flags
{
LOOKUP_FLAG_RTL = 0x1,
LOOKUP_FLAG_IGNORE_BASE = 0x2,
LOOKUP_FLAG_IGNORE_LIGATURES = 0x4,
LOOKUP_FLAG_IGNORE_MARKS = 0x8,
LOOKUP_FLAG_IGNORE_MASK = 0xe,
};
enum gpos_lookup_type
{
GPOS_LOOKUP_SINGLE_ADJUSTMENT = 1,
@ -2447,6 +2499,12 @@ void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache)
cache->gpos.lookup_list = table_read_be_word(&cache->gpos.table,
FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
}
cache->font->grab_font_table(cache->context, MS_GDEF_TAG, &cache->gdef.table.data, &cache->gdef.table.size,
&cache->gdef.table.context);
if (cache->gdef.table.data)
cache->gdef.classdef = table_read_be_word(&cache->gdef.table, FIELD_OFFSET(struct gdef_header, classdef));
}
DWORD opentype_layout_find_script(const struct scriptshaping_cache *cache, DWORD kind, DWORD script,
@ -2519,6 +2577,64 @@ DWORD opentype_layout_find_language(const struct scriptshaping_cache *cache, DWO
return 0;
}
static int gdef_class_compare_format2(const void *g, const void *r)
{
const struct ot_gdef_class_range *range = r;
UINT16 glyph = *(UINT16 *)g;
if (glyph < GET_BE_WORD(range->start_glyph))
return -1;
else if (glyph > GET_BE_WORD(range->end_glyph))
return 1;
else
return 0;
}
static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable *table,
unsigned int offset, UINT16 glyph)
{
WORD format = table_read_be_word(table, offset), count;
unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED;
if (format == 1)
{
const struct ot_gdef_classdef_format1 *format1;
count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
if (format1)
{
WORD start_glyph = GET_BE_WORD(format1->start_glyph);
if (glyph >= start_glyph && (glyph - start_glyph) < count)
{
glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
if (glyph_class > GDEF_CLASS_MAX)
glyph_class = GDEF_CLASS_UNCLASSIFIED;
}
}
}
else if (format == 2)
{
const struct ot_gdef_classdef_format2 *format2;
count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
if (format2)
{
const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
if (glyph_class > GDEF_CLASS_MAX)
glyph_class = GDEF_CLASS_UNCLASSIFIED;
}
}
else
WARN("Unknown GDEF format %u.\n", format);
return glyph_class;
}
struct lookup
{
unsigned int offset;
@ -2545,7 +2661,16 @@ static void glyph_iterator_init(const struct scriptshaping_context *context, uns
static BOOL glyph_iterator_match(const struct glyph_iterator *iter)
{
/* FIXME: implement class matching */
struct scriptshaping_cache *cache = iter->context->cache;
if (cache->gdef.classdef)
{
unsigned int glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.classdef,
iter->context->u.pos.glyphs[iter->pos]);
if ((1 << glyph_class) & iter->flags & LOOKUP_FLAG_IGNORE_MASK)
return FALSE;
}
return TRUE;
}

View File

@ -51,6 +51,7 @@ void release_scriptshaping_cache(struct scriptshaping_cache *cache)
if (!cache)
return;
cache->font->release_font_table(cache->context, cache->gdef.table.context);
cache->font->release_font_table(cache->context, cache->gpos.table.context);
heap_free(cache);
}