diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 054aae74604..28889a74a26 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -1,7 +1,7 @@ /* * Text format and layout * - * Copyright 2012, 2014 Nikolay Sivov for CodeWeavers + * Copyright 2012, 2014-2015 Nikolay Sivov for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -121,6 +121,12 @@ struct layout_run { DWRITE_GLYPH_OFFSET *offsets; }; +enum layout_recompute_mask { + RECOMPUTE_NOMINAL_RUNS = 1 << 0, + RECOMPUTE_MINIMAL_WIDTH = 1 << 1, + RECOMPUTE_EVERYTHING = 0xffff +}; + struct dwrite_textlayout { IDWriteTextLayout2 IDWriteTextLayout2_iface; IDWriteTextFormat1 IDWriteTextFormat1_iface; @@ -135,13 +141,14 @@ struct dwrite_textlayout { FLOAT maxheight; struct list ranges; struct list runs; - BOOL recompute; + USHORT recompute; DWRITE_LINE_BREAKPOINT *nominal_breakpoints; DWRITE_LINE_BREAKPOINT *actual_breakpoints; DWRITE_CLUSTER_METRICS *clusters; UINT32 clusters_count; + FLOAT minwidth; /* gdi-compatible layout specifics */ BOOL gdicompatible; @@ -586,7 +593,7 @@ static HRESULT layout_compute(struct dwrite_textlayout *layout) { HRESULT hr; - if (!layout->recompute) + if (!(layout->recompute & RECOMPUTE_NOMINAL_RUNS)) return S_OK; /* nominal breakpoints are evaluated only once, because string never changes */ @@ -618,7 +625,7 @@ static HRESULT layout_compute(struct dwrite_textlayout *layout) } } - layout->recompute = FALSE; + layout->recompute &= ~RECOMPUTE_NOMINAL_RUNS; return hr; } @@ -998,7 +1005,7 @@ done: if (changed) { struct list *next, *i; - layout->recompute = TRUE; + layout->recompute = RECOMPUTE_EVERYTHING; i = list_head(ranges); while ((next = list_next(ranges, i))) { struct layout_range *next_range = LIST_ENTRY(next, struct layout_range, entry); @@ -1743,11 +1750,60 @@ static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout2 *ifa return max_count >= This->clusters_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->clusters[index].isWhitespace || layout->clusters[index].isNewline || + (index == layout->clusters_count - 1)) + return TRUE; + /* check next one */ + return (index < layout->clusters_count - 1) && layout->clusters[index+1].isWhitespace; +} + static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout2 *iface, FLOAT* min_width) { struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface); - FIXME("(%p)->(%p): stub\n", This, min_width); - return E_NOTIMPL; + FLOAT width; + HRESULT hr; + UINT32 i; + + TRACE("(%p)->(%p)\n", This, min_width); + + if (!min_width) + return E_INVALIDARG; + + if (!(This->recompute & RECOMPUTE_MINIMAL_WIDTH)) + goto width_done; + + *min_width = 0.0; + hr = layout_compute(This); + if (FAILED(hr)) + return hr; + + for (i = 0; i < This->clusters_count;) { + if (is_terminal_cluster(This, i)) { + width = This->clusters[i].width; + i++; + } + else { + width = 0.0; + while (!is_terminal_cluster(This, i)) { + width += This->clusters[i].width; + i++; + } + /* count last one too */ + width += This->clusters[i].width; + } + + if (width > This->minwidth) + This->minwidth = width; + } + This->recompute &= ~RECOMPUTE_MINIMAL_WIDTH; + +width_done: + *min_width = This->minwidth; + return S_OK; } static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout2 *iface, @@ -2579,11 +2635,12 @@ static HRESULT init_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat * layout->len = len; layout->maxwidth = maxwidth; layout->maxheight = maxheight; - layout->recompute = TRUE; + layout->recompute = RECOMPUTE_EVERYTHING; layout->nominal_breakpoints = NULL; layout->actual_breakpoints = NULL; layout->clusters_count = 0; layout->clusters = NULL; + layout->minwidth = 0.0; list_init(&layout->runs); list_init(&layout->ranges); memset(&layout->format, 0, sizeof(layout->format)); diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index bbfc7bc8863..f36c01e6c8f 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -1,7 +1,7 @@ /* * Text layout/format tests * - * Copyright 2012, 2014 Nikolay Sivov for CodeWeavers + * Copyright 2012, 2014-2015 Nikolay Sivov for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1297,6 +1297,36 @@ if (hr == S_OK) { IDWriteFactory_Release(factory); } +static void test_DetermineMinWidth(void) +{ + static const WCHAR strW[] = {'a','b','c','d',0}; + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + IDWriteFactory *factory; + FLOAT minwidth; + HRESULT hr; + + factory = create_factory(); + + hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteFactory_CreateTextLayout(factory, strW, lstrlenW(strW), format, 1000.0, 1000.0, &layout); + ok(hr == S_OK, "got 0x%08x\n", hr); + + 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); + IDWriteTextFormat_Release(format); +} + START_TEST(layout) { IDWriteFactory *factory; @@ -1323,6 +1353,7 @@ START_TEST(layout) test_SetPairKerning(); test_SetVerticalGlyphOrientation(); test_fallback(); + test_DetermineMinWidth(); IDWriteFactory_Release(factory); }