Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/dwrite_private.h | 4 +++ dlls/dwrite/opentype.c | 59 ++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 33 deletions(-)
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 38863b85bd6..7c8a5c9d896 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -513,8 +513,12 @@ struct scriptshaping_cache
struct shaping_glyph_info { + /* Combined features mask. */ unsigned int mask; + /* Derived from glyph class, supplied by GDEF. */ unsigned int props; + /* Only relevant for isClusterStart glyphs. Indicates text position for this cluster. */ + unsigned int start_text_idx; };
struct shaping_glyph_properties diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 72cc6f4c93c..e58ff34cb34 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -4949,30 +4949,6 @@ static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *con 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) { @@ -5053,20 +5029,13 @@ static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_co { context->u.subst.glyph_props[idx + i].isClusterStart = 0; context->u.buffer.glyph_props[idx + i].components = 0; + context->glyph_infos[idx + i].start_text_idx = 0; } opentype_set_subst_glyph_props(context, idx + i); /* 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; } @@ -5889,6 +5858,7 @@ static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, c if (!g || !opentype_is_diacritic(codepoint)) { context->u.buffer.glyph_props[g].isClusterStart = 1; + context->glyph_infos[g].start_text_idx = i; cluster_start_idx = g; }
@@ -5932,7 +5902,7 @@ void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int language_index, const struct shaping_features *features) { struct lookups lookups = { 0 }; - unsigned int i; + unsigned int i, j, start_idx; BOOL ret;
context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL; @@ -5981,6 +5951,29 @@ void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, } }
+ /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding + text span to start_idx. */ + start_idx = 0; + for (i = 1; i < context->glyph_count; ++i) + { + if (context->u.buffer.glyph_props[i].isClusterStart) + { + unsigned int start_text, end_text; + + start_text = context->glyph_infos[start_idx].start_text_idx; + end_text = context->glyph_infos[i].start_text_idx; + + for (j = start_text; j < end_text; ++j) + context->u.buffer.clustermap[j] = start_idx; + + start_idx = i; + } + } + + /* Fill the tail. */ + for (j = context->glyph_infos[start_idx].start_text_idx; j < context->length; ++j) + context->u.buffer.clustermap[j] = start_idx; + heap_free(lookups.lookups); }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/analyzer.c | 22 +++++++++++----------- dlls/dwrite/dwrite_private.h | 3 +++ dlls/dwrite/opentype.c | 6 +++++- 3 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index af4325248a7..f3eca12f96b 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -1167,9 +1167,6 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
get_number_substitutes(substitution, is_rtl, digits);
- /* FIXME: have the shaping engine set this */ - memset(text_props, 0, length * sizeof(*text_props)); - font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
context.cache = fontface_get_shaping_cache(font_obj); @@ -1179,6 +1176,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface, context.is_sideways = is_sideways; context.u.subst.glyphs = heap_calloc(max_glyph_count, sizeof(*glyphs)); context.u.subst.glyph_props = heap_calloc(max_glyph_count, sizeof(*glyph_props)); + context.u.subst.text_props = text_props; context.u.subst.clustermap = clustermap; context.u.subst.max_glyph_count = max_glyph_count; context.u.subst.capacity = max_glyph_count; @@ -1208,7 +1206,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface, }
static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface, - WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props, + WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props, UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props, UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, BOOL is_sideways, BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features, @@ -1220,8 +1218,8 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 unsigned int i, script; HRESULT hr;
- TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len), - clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways, + TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n", debugstr_wn(text, text_len), + clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_lengths, feature_ranges, advances, offsets);
@@ -1253,6 +1251,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 context.is_sideways = is_sideways; context.u.pos.glyphs = glyphs; context.u.pos.glyph_props = glyph_props; + context.u.pos.text_props = text_props; context.u.pos.clustermap = clustermap; context.glyph_count = glyph_count; context.emsize = emSize; @@ -1274,7 +1273,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 }
static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface, - WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props, + WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props, UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props, UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, float ppdip, DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl, @@ -1288,10 +1287,10 @@ static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWrite unsigned int i, script; HRESULT hr;
- TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len), - clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip, - transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), - features, feature_range_lengths, feature_ranges, advances, offsets); + TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n", + debugstr_wn(text, text_len), clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface, + emSize, ppdip, transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script), + debugstr_w(locale), features, feature_range_lengths, feature_ranges, advances, offsets);
analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
@@ -1323,6 +1322,7 @@ static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWrite context.is_sideways = is_sideways; context.u.pos.glyphs = glyphs; context.u.pos.glyph_props = glyph_props; + context.u.pos.text_props = text_props; context.u.pos.clustermap = clustermap; context.glyph_count = glyph_count; context.emsize = emSize * ppdip; diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 7c8a5c9d896..4cb35a44e49 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -552,6 +552,7 @@ struct scriptshaping_context { const UINT16 *glyphs; const DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props; + DWRITE_SHAPING_TEXT_PROPERTIES *text_props; const UINT16 *clustermap; p_apply_context_lookup apply_context_lookup; } pos; @@ -559,6 +560,7 @@ struct scriptshaping_context { UINT16 *glyphs; DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props; + DWRITE_SHAPING_TEXT_PROPERTIES *text_props; UINT16 *clustermap; p_apply_context_lookup apply_context_lookup; unsigned int max_glyph_count; @@ -569,6 +571,7 @@ struct scriptshaping_context { UINT16 *glyphs; struct shaping_glyph_properties *glyph_props; + DWRITE_SHAPING_TEXT_PROPERTIES *text_props; UINT16 *clustermap; p_apply_context_lookup apply_context_lookup; } buffer; diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index e58ff34cb34..5def6042bee 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -5823,6 +5823,7 @@ static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, c BOOL bmp;
memset(context->u.subst.glyph_props, 0, context->u.subst.max_glyph_count * sizeof(*context->u.subst.glyph_props)); + memset(context->u.buffer.text_props, 0, context->length * sizeof(*context->u.buffer.text_props));
for (i = 0; i < context->length; ++i) { @@ -5869,9 +5870,12 @@ static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, c
/* Set initial cluster map here, it's used for setting user features masks. */ clustermap[i] = cluster_start_idx; - if (!bmp) + if (bmp) + context->u.buffer.text_props[i].canBreakShapingAfter = 1; + else { clustermap[i + 1] = cluster_start_idx; + context->u.buffer.text_props[i + 1].canBreakShapingAfter = 1; ++i; } }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/opentype.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 5def6042bee..63b35155b31 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -3746,7 +3746,7 @@ static BOOL opentype_match_class_func(UINT16 glyph, UINT16 glyph_data, const str static BOOL opentype_match_coverage_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data) { const struct match_context *mc = data->mc; - return opentype_layout_is_glyph_covered(&mc->context->table->table, data->subtable_offset + GET_BE_WORD(glyph_data), glyph) + return opentype_layout_is_glyph_covered(&mc->context->table->table, data->subtable_offset + glyph_data, glyph) != GLYPH_NOT_COVERED; }
@@ -3808,8 +3808,10 @@ static enum iterator_match glyph_iterator_may_match(const struct glyph_iterator if (!(iter->mask & iter->context->glyph_infos[iter->pos].mask)) return ITER_NO;
+ /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that + in all matching functions. */ if (iter->match_func) - return !!iter->match_func(iter->context->u.buffer.glyphs[iter->pos], *iter->glyph_data, iter->match_data); + return !!iter->match_func(iter->context->u.buffer.glyphs[iter->pos], GET_BE_WORD(*iter->glyph_data), iter->match_data);
return ITER_MAYBE; }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/dwrite_private.h | 2 +- dlls/dwrite/opentype.c | 134 +++++++++++++++++++++++++---------- 2 files changed, 97 insertions(+), 39 deletions(-)
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 4cb35a44e49..661e01123c9 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -529,7 +529,7 @@ struct shaping_glyph_properties UINT16 isZeroWidthSpace : 1; UINT16 reserved : 1; UINT16 components : 4; - UINT16 reserved2 : 4; + UINT16 lig_component : 4; };
struct scriptshaping_context; diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 63b35155b31..a6e2663ab16 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -5104,11 +5104,71 @@ static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_con return TRUE; }
-static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset) +static BOOL opentype_layout_context_match_input(const struct match_context *mc, unsigned int count, const UINT16 *input, + unsigned int *end_offset, unsigned int *match_positions) +{ + struct match_data match_data = { .mc = mc, .subtable_offset = mc->input_offset }; + struct scriptshaping_context *context = mc->context; + struct glyph_iterator iter; + unsigned int i; + + if (count > GLYPH_CONTEXT_MAX_LENGTH) + return FALSE; + + match_positions[0] = context->cur; + + glyph_iterator_init(context, mc->lookup->flags, context->cur, count - 1, &iter); + iter.mask = mc->lookup->mask; + iter.match_func = mc->match_func; + iter.match_data = &match_data; + iter.glyph_data = input; + + for (i = 1; i < count; ++i) + { + if (!glyph_iterator_next(&iter)) + return FALSE; + + match_positions[i] = iter.pos; + } + + *end_offset = iter.pos - context->cur + 1; + + return TRUE; +} + +static void opentype_layout_unsafe_to_break(struct scriptshaping_context *context, unsigned int idx) +{ + if (context->u.buffer.glyph_props[idx].isClusterStart) + context->u.buffer.text_props[context->glyph_infos[idx].start_text_idx].canBreakShapingAfter = 0; +} + +static void opentype_layout_delete_glyph(struct scriptshaping_context *context, unsigned int idx) { + unsigned int shift_len; + + shift_len = context->glyph_count - context->cur - 1; + + if (shift_len) + { + memmove(&context->u.buffer.glyphs[idx], &context->u.buffer.glyphs[idx + 1], + shift_len * sizeof(*context->u.buffer.glyphs)); + memmove(&context->u.buffer.glyph_props[idx], &context->u.buffer.glyph_props[idx + 1], + shift_len * sizeof(*context->u.buffer.glyph_props)); + memmove(&context->glyph_infos[idx], &context->glyph_infos[idx + 1], shift_len * sizeof(*context->glyph_infos)); + } + + context->glyph_count--; +} + +static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset, + const struct lookup *lookup) +{ + struct match_context mc = { .context = context, .lookup = lookup, .match_func = opentype_match_glyph_func }; const struct dwrite_fonttable *gsub = &context->table->table; + unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH]; + unsigned int i, j, comp_count, match_length = 0; const struct ot_gsub_lig *lig; - unsigned int comp_count; + UINT16 lig_glyph;
comp_count = table_read_be_word(gsub, offset + FIELD_OFFSET(struct ot_gsub_lig, comp_count));
@@ -5119,16 +5179,46 @@ static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context if (!lig) return FALSE;
+ lig_glyph = GET_BE_WORD(lig->lig_glyph); + if (comp_count == 1) { - opentype_layout_replace_glyph(context, GET_BE_WORD(lig->lig_glyph)); + opentype_layout_replace_glyph(context, lig_glyph); context->cur++; return TRUE; }
- /* TODO: actually apply ligature */ + if (!opentype_layout_context_match_input(&mc, comp_count, lig->components, &match_length, match_positions)) + return FALSE;
- return FALSE; + opentype_layout_replace_glyph(context, lig_glyph); + context->u.buffer.glyph_props[context->cur].components = comp_count; + + /* Positioning against a ligature implies keeping track of ligature component + glyph should be attached to. Update per-glyph property for interleaving glyphs, + 0 means attaching to last component, n - attaching to n-th glyph before last. */ + for (i = 1; i < comp_count; ++i) + { + j = match_positions[i - 1] + 1; + while (j < match_positions[i]) + { + context->u.buffer.glyph_props[j++].lig_component = comp_count - i; + } + opentype_layout_unsafe_to_break(context, i); + context->u.buffer.glyph_props[i].isClusterStart = 0; + context->glyph_infos[i].start_text_idx = 0; + } + + /* Delete ligated glyphs, backwards to preserve index. */ + for (i = 1; i < comp_count; ++i) + { + opentype_layout_delete_glyph(context, match_positions[comp_count - i]); + } + + /* Skip whole matched sequence, accounting for deleted glyphs. */ + context->cur += match_length - (comp_count - 1); + + return TRUE; }
static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context *context, const struct lookup *lookup, @@ -5173,7 +5263,7 @@ static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_con /* 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]))) + if (opentype_layout_apply_ligature(context, offset + GET_BE_WORD(lig_set->offsets[i]), lookup)) return TRUE; } } @@ -5183,38 +5273,6 @@ static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_con return FALSE; }
-static BOOL opentype_layout_context_match_input(const struct match_context *mc, unsigned int count, const UINT16 *input, - unsigned int *end_offset, unsigned int *match_positions) -{ - struct match_data match_data = { .mc = mc, .subtable_offset = mc->input_offset }; - struct scriptshaping_context *context = mc->context; - struct glyph_iterator iter; - unsigned int i; - - if (count > GLYPH_CONTEXT_MAX_LENGTH) - return FALSE; - - match_positions[0] = context->cur; - - glyph_iterator_init(context, mc->lookup->flags, context->cur, count - 1, &iter); - iter.mask = mc->lookup->mask; - iter.match_func = mc->match_func; - iter.match_data = &match_data; - iter.glyph_data = input; - - for (i = 1; i < count; ++i) - { - if (!glyph_iterator_next(&iter)) - return FALSE; - - match_positions[i] = iter.pos; - } - - *end_offset = iter.pos - context->cur + 1; - - return TRUE; -} - static BOOL opentype_layout_context_match_backtrack(const struct match_context *mc, unsigned int count, const UINT16 *backtrack, unsigned int *match_start) {
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/font.c | 18 +++++++++++++++--- dlls/dwrite/tests/font.c | 25 ++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index baee8d1e491..19fe911420f 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -3002,11 +3002,23 @@ static HRESULT WINAPI dwritefontcollection1_GetFontFamily(IDWriteFontCollection3 }
static HRESULT WINAPI dwritefontcollection2_GetFontFamily(IDWriteFontCollection3 *iface, - UINT32 index, IDWriteFontFamily2 **family) + UINT32 index, IDWriteFontFamily2 **ret) { - FIXME("%p, %u, %p.\n", iface, index, family); + struct dwrite_fontcollection *collection = impl_from_IDWriteFontCollection3(iface); + struct dwrite_fontfamily *family; + HRESULT hr;
- return E_NOTIMPL; + TRACE("%p, %u, %p.\n", iface, index, ret); + + *ret = NULL; + + if (index >= collection->count) + return E_FAIL; + + if (SUCCEEDED(hr = create_fontfamily(collection, index, &family))) + *ret = &family->IDWriteFontFamily2_iface; + + return hr; }
static HRESULT WINAPI dwritefontcollection2_GetMatchingFonts(IDWriteFontCollection3 *iface, diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index 07baf112eed..e1024d0263b 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -1521,14 +1521,15 @@ static void test_GetFontFamily(void) { IDWriteFontCollection *collection, *collection2; IDWriteFontCollection *syscoll; + IDWriteFontCollection2 *coll2; IDWriteFontFamily *family, *family2; IDWriteFontFamily1 *family1; IDWriteGdiInterop *interop; IDWriteFont *font, *font2; IDWriteFactory *factory; LOGFONTW logfont; + ULONG ref, count; HRESULT hr; - ULONG ref;
factory = create_factory();
@@ -1656,6 +1657,28 @@ if (0) /* crashes on native */ else win_skip("IDWriteFontFamily1 is not supported.\n");
+ /* IDWriteFontCollection2::GetFontFamily() */ + if (SUCCEEDED(IDWriteFontCollection_QueryInterface(syscoll, &IID_IDWriteFontCollection2, (void **)&coll2))) + { + IDWriteFontFamily2 *family2; + + count = IDWriteFontCollection2_GetFontFamilyCount(coll2); + ok(!!count, "Unexpected family count.\n"); + + family2 = (void *)0xdeadbeef; + hr = IDWriteFontCollection2_GetFontFamily(coll2, count, &family2); + ok(hr == E_FAIL, "Unexpected hr %#x.\n", hr); + ok(!family2, "Unexpected pointer.\n"); + + hr = IDWriteFontCollection2_GetFontFamily(coll2, 0, &family2); + ok(hr == S_OK, "Failed to get family, hr %#x.\n", hr); + IDWriteFontFamily2_Release(family2); + + IDWriteFontCollection2_Release(coll2); + } + else + win_skip("IDWriteFontCollection2 is not supported.\n"); + IDWriteFontCollection_Release(syscoll); IDWriteFontCollection_Release(collection2); IDWriteFontCollection_Release(collection);