From 3fbc00b593b242902d027147563cf88eb1f23fa8 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 2 Oct 2017 07:50:58 +0300 Subject: [PATCH] dwrite: Consider inline objects overhang metrics for overall layout overhang metrics. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/dwrite/layout.c | 36 +++++++++++- dlls/dwrite/tests/layout.c | 117 +++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 7975651299f..eda90ec7840 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -3673,10 +3673,37 @@ static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout d2d_rect_offset(bbox, run->origin.x + run->align_dx, run->origin.y); } +static void layout_get_inlineobj_bbox(struct dwrite_textlayout *layout, struct layout_effective_inline *run, + D2D1_RECT_F *bbox) +{ + DWRITE_OVERHANG_METRICS overhang_metrics = { 0 }; + DWRITE_INLINE_OBJECT_METRICS metrics = { 0 }; + HRESULT hr; + + if (FAILED(hr = IDWriteInlineObject_GetMetrics(run->object, &metrics))) { + WARN("Failed to get inline object metrics, hr %#x.\n", hr); + memset(bbox, 0, sizeof(*bbox)); + return; + } + + bbox->left = run->origin.x + run->align_dx; + bbox->right = bbox->left + metrics.width; + bbox->top = run->origin.y; + bbox->bottom = bbox->top + metrics.height; + + IDWriteInlineObject_GetOverhangMetrics(run->object, &overhang_metrics); + + bbox->left -= overhang_metrics.left; + bbox->right += overhang_metrics.right; + bbox->top -= overhang_metrics.top; + bbox->bottom += overhang_metrics.bottom; +} + static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout3 *iface, DWRITE_OVERHANG_METRICS *overhangs) { struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); + struct layout_effective_inline *inline_run; struct layout_effective_run *run; D2D1_RECT_F bbox = { 0 }; HRESULT hr; @@ -3701,9 +3728,14 @@ static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout3 *if d2d_rect_union(&bbox, &run_bbox); } - /* FIXME: iterate over inline objects too */ + LIST_FOR_EACH_ENTRY(inline_run, &This->inlineobjects, struct layout_effective_inline, entry) { + D2D1_RECT_F object_bbox; - /* deltas from text content metrics */ + layout_get_inlineobj_bbox(This, inline_run, &object_bbox); + d2d_rect_union(&bbox, &object_bbox); + } + + /* Deltas from layout box. */ This->overhangs.left = -bbox.left; This->overhangs.top = -bbox.top; This->overhangs.right = bbox.right - This->metrics.layoutWidth; diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 3b0bf981253..d689771ed79 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -782,6 +782,51 @@ static IDWriteInlineObject testinlineobj = { &testinlineobjvtbl }; static IDWriteInlineObject testinlineobj2 = { &testinlineobjvtbl }; static IDWriteInlineObject testinlineobj3 = { &testinlineobjvtbl2 }; +struct test_inline_obj +{ + IDWriteInlineObject IDWriteInlineObject_iface; + DWRITE_INLINE_OBJECT_METRICS metrics; + DWRITE_OVERHANG_METRICS overhangs; +}; + +static inline struct test_inline_obj *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface) +{ + return CONTAINING_RECORD(iface, struct test_inline_obj, IDWriteInlineObject_iface); +} + +static HRESULT WINAPI testinlineobj3_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *metrics) +{ + struct test_inline_obj *obj = impl_from_IDWriteInlineObject(iface); + *metrics = obj->metrics; + return S_OK; +} + +static HRESULT WINAPI testinlineobj3_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs) +{ + struct test_inline_obj *obj = impl_from_IDWriteInlineObject(iface); + *overhangs = obj->overhangs; + /* Return value is ignored. */ + return E_NOTIMPL; +} + +static const IDWriteInlineObjectVtbl testinlineobjvtbl3 = { + testinlineobj_QI, + testinlineobj_AddRef, + testinlineobj_Release, + testinlineobj_Draw, + testinlineobj3_GetMetrics, + testinlineobj3_GetOverhangMetrics, + testinlineobj_GetBreakConditions, +}; + +static void test_inline_obj_init(struct test_inline_obj *obj, const DWRITE_INLINE_OBJECT_METRICS *metrics, + const DWRITE_OVERHANG_METRICS *overhangs) +{ + obj->IDWriteInlineObject_iface.lpVtbl = &testinlineobjvtbl3; + obj->metrics = *metrics; + obj->overhangs = *overhangs; +} + struct test_effect { IUnknown IUnknown_iface; @@ -5398,6 +5443,77 @@ static void test_line_spacing(void) IDWriteFactory_Release(factory); } +static void test_GetOverhangMetrics(void) +{ + static const struct overhangs_test + { + FLOAT uniform_baseline; + DWRITE_INLINE_OBJECT_METRICS metrics; + DWRITE_OVERHANG_METRICS overhang_metrics; + DWRITE_OVERHANG_METRICS expected; + } overhangs_tests[] = { + { 16.0f, { 10.0f, 50.0f, 20.0f }, { 1.0f, 2.0f, 3.0f, 4.0f }, { 1.0f, 6.0f, 3.0f, 0.0f } }, + { 15.0f, { 10.0f, 50.0f, 20.0f }, { 1.0f, 2.0f, 3.0f, 4.0f }, { 1.0f, 7.0f, 3.0f, -1.0f } }, + { 16.0f, { 10.0f, 50.0f, 20.0f }, { -1.0f, 0.0f, -3.0f, 4.0f }, { -1.0f, 4.0f, -3.0f, 0.0f } }, + { 15.0f, { 10.0f, 50.0f, 20.0f }, { -1.0f, 10.0f, 3.0f, -4.0f }, { -1.0f, 15.0f, 3.0f, -9.0f } }, + }; + static const WCHAR strW[] = {'A',0}; + IDWriteFactory *factory; + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + HRESULT hr; + UINT32 i; + + factory = create_factory(); + + hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, 100.0f, enusW, &format); + ok(hr == S_OK, "Failed to create text format, hr %#x.\n", hr); + + hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 1000.0f, 1000.0f, &layout); + ok(hr == S_OK, "Failed to create text layout, hr %x.\n", hr); + + for (i = 0; i < sizeof(overhangs_tests)/sizeof(overhangs_tests[0]); i++) { + const struct overhangs_test *test = &overhangs_tests[i]; + DWRITE_OVERHANG_METRICS overhang_metrics; + DWRITE_TEXT_RANGE range = { 0, 1 }; + DWRITE_TEXT_METRICS metrics; + struct test_inline_obj obj; + + test_inline_obj_init(&obj, &test->metrics, &test->overhang_metrics); + + hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_UNIFORM, test->metrics.height * 2.0f, + test->uniform_baseline); + ok(hr == S_OK, "Failed to set line spacing, hr %#x.\n", hr); + + hr = IDWriteTextLayout_SetInlineObject(layout, NULL, range); + ok(hr == S_OK, "Failed to reset inline object, hr %#x.\n", hr); + + hr = IDWriteTextLayout_SetInlineObject(layout, &obj.IDWriteInlineObject_iface, range); + ok(hr == S_OK, "Failed to set inline object, hr %#x.\n", hr); + + hr = IDWriteTextLayout_GetMetrics(layout, &metrics); + ok(hr == S_OK, "Failed to get layout metrics, hr %#x.\n", hr); + + ok(metrics.width == test->metrics.width, "%u: unexpected formatted width.\n", i); + ok(metrics.height == test->metrics.height * 2.0f, "%u: unexpected formatted height.\n", i); + + hr = IDWriteTextLayout_SetMaxWidth(layout, metrics.width); + hr = IDWriteTextLayout_SetMaxHeight(layout, test->metrics.height); + + hr = IDWriteTextLayout_GetOverhangMetrics(layout, &overhang_metrics); + ok(hr == S_OK, "Failed to get overhang metrics, hr %#x.\n", hr); + + ok(!memcmp(&overhang_metrics, &test->expected, sizeof(overhang_metrics)), + "%u: unexpected overhang metrics (%f, %f, %f, %f).\n", i, overhang_metrics.left, overhang_metrics.top, + overhang_metrics.right, overhang_metrics.bottom); + } + + IDWriteTextLayout_Release(layout); + IDWriteTextFormat_Release(format); + IDWriteFactory_Release(factory); +} + START_TEST(layout) { IDWriteFactory *factory; @@ -5447,6 +5563,7 @@ START_TEST(layout) test_SetUnderline(); test_InvalidateLayout(); test_line_spacing(); + test_GetOverhangMetrics(); IDWriteFactory_Release(factory); }