diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index da08b393824..03001666118 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -3229,23 +3229,12 @@ static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout2 *ifa return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER; } -/* Only to be used with DetermineMinWidth() to find the longest cluster sequence that we don't want to try - too hard to break. */ -static inline BOOL is_terminal_cluster(struct dwrite_textlayout *layout, UINT32 index) -{ - if (layout->clustermetrics[index].isWhitespace || layout->clustermetrics[index].isNewline || - (index == layout->cluster_count - 1)) - return TRUE; - /* check next one */ - return (index < layout->cluster_count - 1) && layout->clustermetrics[index+1].isWhitespace; -} - static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout2 *iface, FLOAT* min_width) { struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface); + UINT32 start; FLOAT width; HRESULT hr; - UINT32 i; TRACE("(%p)->(%p)\n", This, min_width); @@ -3260,20 +3249,30 @@ static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout2 *ifa if (FAILED(hr)) return hr; - for (i = 0; i < This->cluster_count;) { - if (is_terminal_cluster(This, i)) { - width = This->clustermetrics[i].width; - i++; - } - else { - width = 0.0f; - while (!is_terminal_cluster(This, i)) { - width += This->clustermetrics[i].width; - i++; - } - /* count last one too */ - width += This->clustermetrics[i].width; - } + /* Find widest word without emergency breaking between clusters, trailing whitespaces + preceding breaking point do not contribute to word width. */ + for (start = 0; start < This->cluster_count;) { + UINT32 end = start, j, next; + + /* Last cluster always could be wrapped after. */ + while (!This->clustermetrics[end].canWrapLineAfter) + end++; + /* make is so current cluster range that we can wrap after is [start,end) */ + end++; + + next = end; + + /* Ignore trailing whitespace clusters, in case of single space range will + be reduced to empty range, or [start,start+1). */ + while ((end - 1) >= start && This->clustermetrics[end-1].isWhitespace) + end--; + + /* check if cluster range exceeds last minimal width */ + width = 0.0f; + for (j = start; j < end; j++) + width += This->clustermetrics[j].width; + + start = next; if (width > This->minwidth) This->minwidth = width; diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index c04e73aa227..7d83c1a03a3 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -2347,10 +2347,21 @@ if (hr == S_OK) { static void test_DetermineMinWidth(void) { + struct minwidth_test { + const WCHAR text[10]; /* text to create a layout for */ + const WCHAR mintext[10]; /* text that represents sequence of minimal width */ + } minwidth_tests[] = { + { {' ','a','b',' ',0}, {'a','b',0} }, + { {'a','\n',' ',' ',0}, {'a',0} }, + { {'a','\n',' ',' ','b',0}, {'b',0} }, + { {'a','b','c','\n',' ',' ','b',0}, {'a','b','c',0} }, + }; static const WCHAR strW[] = {'a','b','c','d',0}; + DWRITE_CLUSTER_METRICS metrics[10]; IDWriteTextFormat *format; IDWriteTextLayout *layout; IDWriteFactory *factory; + UINT32 count, i, j; FLOAT minwidth; HRESULT hr; @@ -2365,13 +2376,34 @@ static void test_DetermineMinWidth(void) hr = IDWriteTextLayout_DetermineMinWidth(layout, NULL); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); - - minwidth = 0.0; - hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); - ok(hr == S_OK, "got 0x%08x\n", hr); - ok(minwidth > 0.0, "got %.2f\n", minwidth); - IDWriteTextLayout_Release(layout); + + for (i = 0; i < sizeof(minwidth_tests)/sizeof(minwidth_tests[0]); i++) { + FLOAT width = 0.0f; + + /* measure expected width */ + hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].mintext, lstrlenW(minwidth_tests[i].mintext), format, 1000.0f, 1000.0f, &layout); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count); + ok(hr == S_OK, "got 0x%08x\n", hr); + + for (j = 0; j < count; j++) + width += metrics[j].width; + + IDWriteTextLayout_Release(layout); + + hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].text, lstrlenW(minwidth_tests[i].text), format, 1000.0f, 1000.0f, &layout); + ok(hr == S_OK, "got 0x%08x\n", hr); + + minwidth = 0.0f; + hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(minwidth == width, "test %u: expected width %f, got %f\n", i, width, minwidth); + + IDWriteTextLayout_Release(layout); + } + IDWriteTextFormat_Release(format); IDWriteFactory_Release(factory); }