diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 4c657324838..583f2eb0554 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -98,8 +98,16 @@ struct layout_range { IDWriteFontCollection *collection; }; +struct layout_run { + struct list entry; + DWRITE_GLYPH_RUN_DESCRIPTION descr; + DWRITE_SCRIPT_ANALYSIS sa; +}; + struct dwrite_textlayout { IDWriteTextLayout2 IDWriteTextLayout2_iface; + IDWriteTextAnalysisSink IDWriteTextAnalysisSink_iface; + IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface; LONG ref; WCHAR *str; @@ -108,6 +116,8 @@ struct dwrite_textlayout { FLOAT maxwidth; FLOAT maxheight; struct list ranges; + struct list runs; + BOOL recompute; }; struct dwrite_textformat { @@ -145,6 +155,16 @@ static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout2(IDWriteText return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout2_iface); } +static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink(IDWriteTextAnalysisSink *iface) +{ + return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink_iface); +} + +static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource(IDWriteTextAnalysisSource *iface) +{ + return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource_iface); +} + static inline struct dwrite_textformat *impl_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface) { return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat1_iface); @@ -165,6 +185,70 @@ static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypog return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface); } +static struct layout_run *alloc_layout_run(void) +{ + struct layout_run *ret; + + ret = heap_alloc(sizeof(*ret)); + if (!ret) return NULL; + + ret->descr.localeName = NULL; + ret->descr.string = NULL; + ret->descr.stringLength = 0; + ret->descr.clusterMap = NULL; + ret->descr.textPosition = 0; + + return ret; +} + +static void free_layout_runs(struct dwrite_textlayout *layout) +{ + struct layout_run *cur, *cur2; + LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->runs, struct layout_run, entry) { + list_remove(&cur->entry); + heap_free(cur); + } +} + +static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) +{ + IDWriteTextAnalyzer *analyzer; + struct layout_range *cur; + HRESULT hr = S_OK; + + free_layout_runs(layout); + + hr = get_textanalyzer(&analyzer); + if (FAILED(hr)) + return hr; + + LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, entry) { + /* inline objects override actual text in a range */ + if (cur->object) + continue; + + hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &layout->IDWriteTextAnalysisSource_iface, + cur->range.startPosition, cur->range.length, &layout->IDWriteTextAnalysisSink_iface); + if (FAILED(hr)) + break; + } + + IDWriteTextAnalyzer_Release(analyzer); + return hr; +} + +static HRESULT layout_compute(struct dwrite_textlayout *layout) +{ + HRESULT hr; + + if (!layout->recompute) + return S_OK; + + hr = layout_compute_runs(layout); + layout->recompute = FALSE; + return hr; +} + /* To be used in IDWriteTextLayout methods to validate and fix passed range */ static inline BOOL validate_text_range(struct dwrite_textlayout *layout, DWRITE_TEXT_RANGE *r) { @@ -499,6 +583,7 @@ done: if (changed) { struct list *next, *i; + layout->recompute = TRUE; i = list_head(ranges); while ((next = list_next(ranges, i))) { struct layout_range *next_range = LIST_ENTRY(next, struct layout_range, entry); @@ -557,6 +642,7 @@ static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout2 *iface) if (!ref) { free_layout_ranges_list(This); + free_layout_runs(This); release_format_data(&This->format); heap_free(This->str); heap_free(This); @@ -1187,10 +1273,17 @@ static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout2 *if } static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout2 *iface, - DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32* act_count) + DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count) { struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface); - FIXME("(%p)->(%p %u %p): stub\n", This, metrics, max_count, act_count); + HRESULT hr; + + FIXME("(%p)->(%p %u %p): stub\n", This, metrics, max_count, count); + + hr = layout_compute(This); + if (FAILED(hr)) + return hr; + return E_NOTIMPL; } @@ -1405,6 +1498,159 @@ static const IDWriteTextLayout2Vtbl dwritetextlayoutvtbl = { dwritetextlayout2_GetFontFallback }; +static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink *iface, + REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown)) { + *obj = iface; + IDWriteTextAnalysisSink_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink *iface) +{ + return 2; +} + +static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink *iface) +{ + return 1; +} + +static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface, + UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa) +{ + struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink(iface); + struct layout_run *run; + + TRACE("%u %u script=%d\n", position, length, sa->script); + + run = alloc_layout_run(); + if (!run) + return E_OUTOFMEMORY; + + run->descr.string = &layout->str[position]; + run->descr.stringLength = length; + run->descr.textPosition = position; + run->sa = *sa; + list_add_head(&layout->runs, &run->entry); + return S_OK; +} + +static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface, + UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink *iface, UINT32 position, + UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface, + UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution) +{ + return E_NOTIMPL; +} + +static const IDWriteTextAnalysisSinkVtbl dwritetextlayoutsinkvtbl = { + dwritetextlayout_sink_QueryInterface, + dwritetextlayout_sink_AddRef, + dwritetextlayout_sink_Release, + dwritetextlayout_sink_SetScriptAnalysis, + dwritetextlayout_sink_SetLineBreakpoints, + dwritetextlayout_sink_SetBidiLevel, + dwritetextlayout_sink_SetNumberSubstitution +}; + +static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource *iface, + REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IDWriteTextAnalysisSource_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource *iface) +{ + return 2; +} + +static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource *iface) +{ + return 1; +} + +static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource *iface, + UINT32 position, WCHAR const** text, UINT32* text_len) +{ + struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource(iface); + + TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len); + + if (position < layout->len) { + *text = &layout->str[position]; + *text_len = layout->len - position; + } + else { + *text = NULL; + *text_len = 0; + } + + return S_OK; +} + +static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource *iface, + UINT32 position, WCHAR const** text, UINT32* text_len) +{ + FIXME("%u %p %p: stub\n", position, text, text_len); + return E_NOTIMPL; +} + +static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource *iface) +{ + struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource(iface); + return IDWriteTextLayout2_GetReadingDirection(&layout->IDWriteTextLayout2_iface); +} + +static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource *iface, + UINT32 position, UINT32* text_len, WCHAR const** locale) +{ + FIXME("%u %p %p: stub\n", position, text_len, locale); + return E_NOTIMPL; +} + +static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource *iface, + UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution) +{ + FIXME("%u %p %p: stub\n", position, text_len, substitution); + return E_NOTIMPL; +} + +static const IDWriteTextAnalysisSourceVtbl dwritetextlayoutsourcevtbl = { + dwritetextlayout_source_QueryInterface, + dwritetextlayout_source_AddRef, + dwritetextlayout_source_Release, + dwritetextlayout_source_GetTextAtPosition, + dwritetextlayout_source_GetTextBeforePosition, + dwritetextlayout_source_GetParagraphReadingDirection, + dwritetextlayout_source_GetLocaleName, + dwritetextlayout_source_GetNumberSubstitution +}; + static void layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format) { struct dwrite_textformat *f; @@ -1467,13 +1713,17 @@ HRESULT create_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat *forma if (!This) return E_OUTOFMEMORY; This->IDWriteTextLayout2_iface.lpVtbl = &dwritetextlayoutvtbl; + This->IDWriteTextAnalysisSink_iface.lpVtbl = &dwritetextlayoutsinkvtbl; + This->IDWriteTextAnalysisSource_iface.lpVtbl = &dwritetextlayoutsourcevtbl; This->ref = 1; This->str = heap_strdupnW(str, len); This->len = len; This->maxwidth = maxwidth; This->maxheight = maxheight; + This->recompute = TRUE; layout_format_from_textformat(This, format); + list_init(&This->runs); list_init(&This->ranges); range = alloc_layout_range(This, &r); if (!range) { diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index bf872f061c1..de3e544aa35 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -881,6 +881,49 @@ static void test_typography(void) IDWriteTypography_Release(typography); } +static void test_GetClusterMetrics(void) +{ + static const WCHAR strW[] = {'a','b','c','d',0}; + IDWriteInlineObject *trimm; + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + DWRITE_TEXT_RANGE range; + UINT32 count; + HRESULT hr; + + 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, 4, format, 1000.0, 1000.0, &layout); + ok(hr == S_OK, "got 0x%08x\n", hr); + + count = 0; + hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count); +todo_wine { + ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); + ok(count == 4, "got %u\n", count); +} + hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &trimm); + ok(hr == S_OK, "got 0x%08x\n", hr); + + range.startPosition = 0; + range.length = 2; + hr = IDWriteTextLayout_SetInlineObject(layout, trimm, range); + ok(hr == S_OK, "got 0x%08x\n", hr); + + /* inline object takes a separate cluster, replaced codepoints number doesn't matter */ + count = 0; + hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count); +todo_wine { + ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); + ok(count == 3, "got %u\n", count); +} + IDWriteInlineObject_Release(trimm); + IDWriteTextLayout_Release(layout); + IDWriteTextFormat_Release(format); +} + START_TEST(layout) { HRESULT hr; @@ -905,6 +948,7 @@ START_TEST(layout) test_SetInlineObject(); test_draw_sequence(); test_typography(); + test_GetClusterMetrics(); IDWriteFactory_Release(factory); }