diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index 0769dada355..4b666b8ca46 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -220,6 +220,95 @@ static const DWRITE_UNICODE_RANGE cjk_ranges[] = { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */ }; +struct text_source_context +{ + IDWriteTextAnalysisSource *source; + + UINT32 position; + UINT32 length; + UINT32 consumed; + + UINT32 chunk_length; + const WCHAR *text; + UINT32 cursor; + HRESULT status; + BOOL end; + + UINT32 ch; +}; + +static void text_source_read_more(struct text_source_context *context) +{ + if ((context->chunk_length - context->cursor) > 1) return; + + context->position += context->cursor; + context->cursor = 0; + if (FAILED(context->status = IDWriteTextAnalysisSource_GetTextAtPosition(context->source, context->position, + &context->text, &context->chunk_length))) + { + context->end = TRUE; + } +} + +static void text_source_get_u32_char(struct text_source_context *context) +{ + const WCHAR *text; + UINT32 available; + + /* Make sure to have full pair of surrogates */ + text_source_read_more(context); + + available = context->chunk_length - context->cursor; + text = context->text + context->cursor; + + if (available > 1 && IS_HIGH_SURROGATE(*text) && IS_LOW_SURROGATE(*(text + 1))) + { + context->cursor += 2; + context->consumed += 2; + context->ch = 0x10000 + ((*text - 0xd800) << 10) + *(text + 1) - 0xdc00; + return; + } + + context->cursor++; + context->consumed++; + context->ch = *text; +} + +static HRESULT text_source_context_init(struct text_source_context *context, IDWriteTextAnalysisSource *source, + UINT32 position, UINT32 length) +{ + memset(context, 0, sizeof(*context)); + context->source = source; + context->position = position; + context->length = length; + + return IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &context->text, &context->chunk_length); +} + +static BOOL text_source_get_next_u32_char(struct text_source_context *context) +{ + if (context->consumed == context->length) + { + context->ch = 0; + context->end = TRUE; + } + else if (context->cursor < context->chunk_length) + { + text_source_get_u32_char(context); + } + else + { + text_source_read_more(context); + /* Normal end-of-text condition. */ + if (!context->text || !context->chunk_length) + context->end = TRUE; + else + text_source_get_u32_char(context); + } + + return context->end; +} + struct fallback_mapping { DWRITE_UNICODE_RANGE *ranges; @@ -352,6 +441,38 @@ static void release_fallback_data(struct fallback_data *data) fallback_locale_list_destroy(&data->locales); } +static const struct fallback_mapping * find_fallback_mapping(const struct fallback_data *fallback, + const struct fallback_locale *locale, UINT32 ch) +{ + const struct fallback_mapping *mapping; + size_t i, j, r; + + for (i = 0; i < locale->ranges.count; i += 2) + { + size_t start = locale->ranges.data[i], end = locale->ranges.data[i + 1]; + for (j = start; j <= end; ++j) + { + mapping = &fallback->mappings[j]; + for (r = 0; r < mapping->ranges_count; ++r) + { + const DWRITE_UNICODE_RANGE *range = &mapping->ranges[r]; + if (range->first <= ch && range->last >= ch) return mapping; + } + } + } + + mapping = NULL; + + /* Mapping wasn't found for specific locale, try with neutral one. This will only recurse once. */ + if (*locale->name) + { + locale = font_fallback_get_locale(&fallback->locales, L""); + mapping = find_fallback_mapping(fallback, locale, ch); + } + + return mapping; +} + struct dwrite_numbersubstitution { IDWriteNumberSubstitution IDWriteNumberSubstitution_iface; @@ -2114,7 +2235,7 @@ static int __cdecl compare_mapping_range(const void *a, const void *b) return 0; } -static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch) +static const struct fallback_mapping *find_fallback_mapping_system(struct dwrite_fontfallback *fallback, UINT32 ch) { UINT32 i; @@ -2129,6 +2250,89 @@ static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfa return NULL; } +static UINT32 fallback_font_get_supported_length(IDWriteFont3 *font, IDWriteTextAnalysisSource *source, UINT32 position, + UINT32 length) +{ + struct text_source_context context; + UINT32 mapped = 0; + + text_source_context_init(&context, source, position, length); + while (!text_source_get_next_u32_char(&context)) + { + if (!IDWriteFont3_HasCharacter(font, context.ch)) break; + mapped++; + } + + return mapped; +} + +static HRESULT fallback_map_characters(const struct dwrite_fontfallback *fallback, IDWriteTextAnalysisSource *source, + UINT32 position, UINT32 text_length, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, + DWRITE_FONT_STRETCH stretch, IDWriteFont **ret_font, UINT32 *ret_length, float *scale) +{ + const struct fallback_mapping *mapping = NULL; + struct text_source_context context; + const WCHAR *locale_name = NULL; + struct fallback_locale *locale; + UINT32 i, length = 0, mapped; + IDWriteFont3 *font; + HRESULT hr; + + /* We will try to map as much of given input as GetLocaleName() says. It's assumed that returned length covers + whole span of characters set with that locale, so callback is only used once. */ + if (FAILED(hr = IDWriteTextAnalysisSource_GetLocaleName(source, position, &length, &locale_name))) + return hr; + + length = length ? min(length, text_length) : text_length; + if (!locale_name) locale_name = L""; + + /* Lookup locale entry once, if specific locale is missing neutral one will be returned. */ + locale = font_fallback_get_locale(&fallback->data.locales, locale_name); + + if (FAILED(hr = text_source_context_init(&context, source, position, length))) return hr; + + /* Find a mapping for given locale. */ + text_source_get_next_u32_char(&context); + if (!(mapping = find_fallback_mapping(&fallback->data, locale, context.ch))) + { + *ret_font = NULL; + *ret_length = 0; + + return S_OK; + } + + while (!text_source_get_next_u32_char(&context)) + { + if (find_fallback_mapping(&fallback->data, locale, context.ch) != mapping) break; + } + + /* Go through families in the mapping, use first family that supports some of the input. */ + for (i = 0; i < mapping->families_count; ++i) + { + if (SUCCEEDED(create_matching_font(mapping->collection ? mapping->collection : fallback->systemcollection, + mapping->families[i], weight, style, stretch, &IID_IDWriteFont3, (void **)&font))) + { + if (!(mapped = fallback_font_get_supported_length(font, source, position, length))) + { + IDWriteFont3_Release(font); + continue; + } + + *ret_font = (IDWriteFont *)font; + *ret_length = mapped; + *scale = mapping->scale; + + return S_OK; + } + } + + /* Mapping was found, but either font couldn't be created or there's no font that supports given input. */ + *ret_font = NULL; + *ret_length = length; + + return S_OK; +} + HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, REFIID riid, void **obj) { @@ -2163,7 +2367,7 @@ HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *nam return hr; } -static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length) +static HRESULT fallback_map_characters_system(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length) { HRESULT hr = S_OK; UINT32 i; @@ -2199,7 +2403,7 @@ static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, *mapped_font = NULL; - mapping = find_fallback_mapping(fallback, text[0]); + mapping = find_fallback_mapping_system(fallback, text[0]); if (!mapping) { WARN("No mapping range for %#x.\n", text[0]); return E_FAIL; @@ -2220,7 +2424,7 @@ static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, return E_FAIL; } - hr = fallback_map_characters(*mapped_font, text, length, mapped_length); + hr = fallback_map_characters_system(*mapped_font, text, length, mapped_length); if (FAILED(hr)) WARN("Mapping with fallback family %s failed, hr %#lx.\n", debugstr_w(mapping->families[i]), hr); @@ -2267,7 +2471,7 @@ static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback1 *iface, ID if (FAILED(hr)) goto done; - hr = fallback_map_characters(*ret_font, text, length, mapped_length); + hr = fallback_map_characters_system(*ret_font, text, length, mapped_length); if (FAILED(hr)) goto done; } @@ -2372,14 +2576,42 @@ static ULONG WINAPI customfontfallback_Release(IDWriteFontFallback1 *iface) } static HRESULT WINAPI customfontfallback_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source, - UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily, - DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length, - IDWriteFont **ret_font, FLOAT *scale) + UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily, + DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length, + IDWriteFont **ret_font, float *scale) { - FIXME("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length, + struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface); + IDWriteFont3 *font; + + TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length, basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale); - return E_NOTIMPL; + *mapped_length = 0; + *ret_font = NULL; + *scale = 1.0f; + + if (!length) + return S_OK; + + if (!basecollection) + basecollection = fallback->systemcollection; + + if (basefamily && *basefamily) + { + if (SUCCEEDED(create_matching_font(basecollection, basefamily, weight, style, stretch, + &IID_IDWriteFont, (void **)&font))) + { + if ((*mapped_length = fallback_font_get_supported_length(font, source, position, length))) + { + *ret_font = (IDWriteFont *)font; + *scale = 1.0f; + return S_OK; + } + IDWriteFont3_Release(font); + } + } + + return fallback_map_characters(fallback, source, position, length, weight, style, stretch, ret_font, mapped_length, scale); } static HRESULT WINAPI customfontfallback1_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source, diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 31bccfca3be..bcc5303ab61 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -4937,12 +4937,11 @@ static void test_FontFallbackBuilder(void) font = (void*)0xdeadbeef; hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); -todo_wine { ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(mappedlength == 1, "got %u\n", mappedlength); - ok(scale == 1.0f, "got %f\n", scale); - ok(font == NULL, "got %p\n", font); -} + ok(mappedlength == 1, "Unexpected length %u.\n", mappedlength); + ok(scale == 1.0f, "Unexpected scale %f.\n", scale); + ok(!font, "Unexpected font instance %p.\n", font); + IDWriteFontFallback_Release(fallback); /* remap with custom collection */ @@ -4959,12 +4958,10 @@ todo_wine { font = NULL; hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); -todo_wine { ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(mappedlength == 1, "got %u\n", mappedlength); - ok(scale == 5.0f, "got %f\n", scale); - ok(font != NULL, "got %p\n", font); -} + ok(mappedlength == 1, "Unexpected length %u.\n", mappedlength); + ok(scale == 5.0f, "Unexpected scale %f.\n", scale); + ok(font != NULL, "Expected font instance %p.\n", font); if (font) IDWriteFont_Release(font); @@ -4984,12 +4981,10 @@ todo_wine { font = NULL; hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); -todo_wine { ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(mappedlength == 1, "got %u\n", mappedlength); - ok(scale == 5.0f, "got %f\n", scale); - ok(font != NULL, "got %p\n", font); -} + ok(mappedlength == 1, "Unexpected length %u.\n", mappedlength); + ok(scale == 5.0f, "Unexpected scale %f.\n", scale); + ok(font != NULL, "Expected font instance %p.\n", font); if (font) IDWriteFont_Release(font); @@ -5010,12 +5005,10 @@ todo_wine { font = NULL; hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); -todo_wine { ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(mappedlength == 1, "got %u\n", mappedlength); - ok(scale == 5.0f, "got %f\n", scale); - ok(font != NULL, "got %p\n", font); -} + ok(mappedlength == 1, "Unexpected length %u.\n", mappedlength); + ok(scale == 5.0f, "Unexpected scale %f.\n", scale); + ok(font != NULL, "Expected font instance %p.\n", font); if (font) IDWriteFont_Release(font);