dwrite: Implement MapCharacters() for custom fallback.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
This commit is contained in:
Nikolay Sivov 2022-07-18 17:48:13 +03:00 committed by Alexandre Julliard
parent d800cd7437
commit d37eb0f144
2 changed files with 255 additions and 30 deletions

View file

@ -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,

View file

@ -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);