diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4638c9b..a778716 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -32,6 +32,9 @@ target_link_libraries("draw-simple" "windrawlib") add_executable("draw-string" WIN32 "draw-string.c") target_link_libraries("draw-string" "windrawlib") +add_executable("draw-text" WIN32 "draw-text.c") +target_link_libraries("draw-text" "windrawlib") + add_executable("draw-styled" WIN32 "draw-styled.c") target_link_libraries("draw-styled" "windrawlib") diff --git a/examples/draw-cached.c b/examples/draw-cached.c index 0b360ab..5afebfc 100644 --- a/examples/draw-cached.c +++ b/examples/draw-cached.c @@ -6,6 +6,17 @@ static HWND hwndMain = NULL; + +/* The cached canvas (if not NULL). + * + * Note that if the caching is enabled, it remembers all its painted contents + * across all the WM_PAINT messages so as long as we do not need to paint it + * with different contents, we may just call wdBeginPaint() + wdEndPaint() to + * make it blit into the window. + * + * If only some sub-region needs to be painted differently, then only that + * part of it needs to be repainted. + */ static WD_HCANVAS hCachedCanvas = NULL; @@ -21,32 +32,44 @@ MainWinPaintToCanvas(WD_HCANVAS hCanvas, BOOL* pCanCache) int i; wdBeginPaint(hCanvas); - wdClear(hCanvas, WD_RGB(255,255,255)); - hBrush = wdCreateSolidBrush(hCanvas, 0); - - for(i = 0; i < 3; i++) { - float x = 10.0f + i * 20.0f; - float y = 10.0f + i * 20.0f; - wdSetSolidBrushColor(hBrush, fillColors[i]); - wdFillRect(hCanvas, hBrush, x, y, x + 100.0f, y + 100.0f); + /* This very simple example never changes what it paints; i.e. if we + * already cached the completely painted canvas, we may skip the paint + * code altogether. + * + * In real applications, we would have to paint only parts of the canvas + * which needs to be painted differently since the last time; e.g. to + * reflect a change in the application's state. + */ + if(hCanvas != hCachedCanvas) { + wdClear(hCanvas, WD_RGB(255,255,255)); + hBrush = wdCreateSolidBrush(hCanvas, 0); + + for(i = 0; i < 3; i++) { + float x = 10.0f + i * 20.0f; + float y = 10.0f + i * 20.0f; + + wdSetSolidBrushColor(hBrush, fillColors[i]); + wdFillRect(hCanvas, hBrush, x, y, x + 100.0f, y + 100.0f); + + wdSetSolidBrushColor(hBrush, drawColors[i]); + wdDrawRect(hCanvas, hBrush, x, y, x + 100.0f, y + 100.0f, 3.0f); + } - wdSetSolidBrushColor(hBrush, drawColors[i]); - wdDrawRect(hCanvas, hBrush, x, y, x + 100.0f, y + 100.0f, 3.0f); - } + for(i = 0; i < 3; i++) { + float x = 250.0f + i * 20.0f; + float y = 60.0f + i * 20.0f; - for(i = 0; i < 3; i++) { - float x = 250.0f + i * 20.0f; - float y = 60.0f + i * 20.0f; + wdSetSolidBrushColor(hBrush, fillColors[i]); + wdFillCircle(hCanvas, hBrush, x, y, 55.0f); - wdSetSolidBrushColor(hBrush, fillColors[i]); - wdFillCircle(hCanvas, hBrush, x, y, 55.0f); + wdSetSolidBrushColor(hBrush, drawColors[i]); + wdDrawCircle(hCanvas, hBrush, x, y, 55.0f, 3.0f); + } - wdSetSolidBrushColor(hBrush, drawColors[i]); - wdDrawCircle(hCanvas, hBrush, x, y, 55.0f, 3.0f); + wdDestroyBrush(hBrush); } - wdDestroyBrush(hBrush); *pCanCache = wdEndPaint(hCanvas); } @@ -68,6 +91,7 @@ MainWinPaint(void) MainWinPaintToCanvas(hCanvas, &bCanCache); + /* Cache the completely painted canvas if we are allowed to. */ if(bCanCache) { hCachedCanvas = hCanvas; } else { @@ -75,6 +99,7 @@ MainWinPaint(void) hCachedCanvas = NULL; } } + EndPaint(hwndMain, &ps); } /* Main window procedure */ @@ -87,14 +112,28 @@ MainWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; case WM_SIZE: - /* If we are caching the canvas for WM_PAINT, we need to resize it. */ if(wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) { - if(hCachedCanvas != NULL) - wdResizeCanvas(hCachedCanvas, LOWORD(lParam), HIWORD(lParam)); + if(hCachedCanvas != NULL) { + /* We could call wdResizeCanvas() here. But that would + * loose the painted stuff on the canvas anyway, and save + * us "only" the reallocation of the canvas. + * + * For the sake of simplicity, we just destroy it all. */ + wdDestroyCanvas(hCachedCanvas); + hCachedCanvas = NULL; + } + InvalidateRect(hwndMain, NULL, FALSE); } return 0; + case WM_LBUTTONDOWN: + InvalidateRect(hwnd, NULL, FALSE); + return 0; + case WM_DISPLAYCHANGE: + /* Some relevant graphics settings has been changed and we cannot + * use the cached canvas as it can use incompatible pixel format, + * so discard it. */ if(hCachedCanvas != NULL) { wdDestroyCanvas(hCachedCanvas); hCachedCanvas = NULL; @@ -130,7 +169,7 @@ _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nC /* Create main window */ hwndMain = CreateWindow( _T("main_window"), _T("LibWinDraw Example"), - WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 550, 350, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 150, 350, NULL, NULL, hInstance, NULL ); SendMessage(hwndMain, WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), diff --git a/examples/draw-text.c b/examples/draw-text.c new file mode 100644 index 0000000..6cd25ca --- /dev/null +++ b/examples/draw-text.c @@ -0,0 +1,180 @@ + +#include +#include + +#include + + +static HWND hwndMain = NULL; + + + +static WCHAR pszTitle[] = L"Lorem Ipsum"; +static WCHAR pszText[] = + L"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam dui " + L"sem, fermentum vitae, sagittis id, malesuada in, quam. Morbi imperdiet, " + L"mauris ac auctor dictum, nisl ligula egestas nulla, et sollicitudin " + L"sem purus in lacus. Fusce aliquam vestibulum ipsum. Excepteur sint " + L"occaecat cupidatat non proident, sunt in culpa qui officia deserunt " + L"mollit anim id est laborum. Morbi imperdiet, mauris ac auctor dictum, " + L"nisl ligula egestas nulla, et sollicitudin sem purus in lacus. " + L"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " + L"officia deserunt mollit anim id est laborum. Et harum quidem rerum " + L"facilis est et expedita distinctio. Ut enim ad minim veniam, quis " + L"nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo " + L"consequat. Donec vitae arcu. Vivamus ac leo pretium faucibus.\n" + L"\n" + L"In dapibus augue non sapien. Aenean id metus id velit ullamcorper " + L"pulvinar. Sed convallis magna eu sem. Maecenas sollicitudin. Nulla " + L"turpis magna, cursus sit amet, suscipit a, interdum id, felis. Nulla " + L"non lectus sed nisl molestie malesuada. Aliquam ante. Integer lacinia. " + L"Nullam sapien sem, ornare ac, nonummy non, lobortis a enim. Integer " + L"lacinia. Mauris metus. Fusce suscipit libero eget elit.\n" + L"\n" + L"Nam quis nulla. Fusce nibh. Nulla pulvinar eleifend sem. Curabitur " + L"sagittis hendrerit ante. Cras pede libero, dapibus nec, pretium sit " + L"amet, tempor quis. Nulla non lectus sed nisl molestie malesuada. " + L"Maecenas sollicitudin. Duis condimentum augue id magna semper rutrum. " + L"Proin mattis lacinia justo. Ut enim ad minima veniam, quis nostrum " + L"exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid " + L"ex ea commodi consequatur? Integer in sapien. Nemo enim ipsam " + L"voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia " + L"consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. " + L"Fusce aliquam vestibulum ipsum. In enim a arcu imperdiet malesuada. " + L"Maecenas ipsum velit, consectetuer eu lobortis ut, dictum at dui.\n" + L"\n" + L"Duis viverra diam non justo. Class aptent taciti sociosqu ad litora " + L"torquent per conubia nostra, per inceptos hymenaeos. Mauris metus. " + L"Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil " + L"impedit quo minus plus id quod maxime placeat facere possimus, omnis " + L"voluptas assumenda est, omnis dolor repellendus. Class aptent taciti " + L"sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos."; + +static void +PaintToCanvas(WD_HCANVAS hCanvas) +{ + RECT client; + WD_HBRUSH hBrush; + WD_HFONT hFont; + WD_RECT rect; + WD_HTEXT hTitle; + WD_HTEXT hText; + WD_FONTMETRICS fontMetrics; + WD_TEXTMETRICS titleMetrics; + + GetClientRect(hwndMain, &client); + rect.x0 = 10.0f; + rect.x1 = client.right - 10.0f; + + wdBeginPaint(hCanvas); + wdClear(hCanvas, WD_RGB(255,255,255)); + hBrush = wdCreateSolidBrush(hCanvas, WD_RGB(0,0,0)); + hFont = wdCreateFontWithGdiHandle(GetStockObject(DEFAULT_GUI_FONT)); + wdFontMetrics(hFont, &fontMetrics); + + /* Create and paint title. */ + rect.y0 = 10.0f; + rect.y1 = client.bottom - 10.0f; + hTitle = wdCreateText(hFont, &rect, pszTitle, -1, 0); + wdSetTextFontSize(hTitle, 0, wcslen(pszTitle), 1.66f * fontMetrics.fEmHeight); + wdSetTextFontSize(hTitle, 0, 1, 1.85f * fontMetrics.fEmHeight); + wdSetTextFontSize(hTitle, 6, 1, 1.85f * fontMetrics.fEmHeight); + wdTextMetrics(hTitle, &titleMetrics); + wdDrawText(hCanvas, hTitle, hBrush, rect.x0, rect.y0, 0); + + /* Create body text, customize some its parts, and paint it. */ + rect.y0 = 20.0f + titleMetrics.fHeight; + rect.y1 = client.bottom - 10.0f; + hText = wdCreateText(hFont, &rect, pszText, -1, 0); + wdSetTextFontWeight(hText, 103, 12, FW_BOLD); + wdSetTextFontStyle(hText, 932, 68, WD_TEXTSTYLE_ITALIC); + wdSetTextStrikethrough(hText, 2159, 5, TRUE); + wdSetTextUnderline(hText, 2165, 4, TRUE); + wdDrawText(hCanvas, hText, hBrush, rect.x0, rect.y0, 0); + + wdDestroyText(hTitle); + wdDestroyText(hText); + wdDestroyBrush(hBrush); + wdDestroyFont(hFont); + wdEndPaint(hCanvas); + } + +/* Main window procedure */ +static LRESULT CALLBACK +MainWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_PAINT: + { + PAINTSTRUCT ps; + WD_HCANVAS hCanvas; + + BeginPaint(hwndMain, &ps); + hCanvas = wdCreateCanvasWithPaintStruct(hwndMain, &ps, 0); + PaintToCanvas(hCanvas); + wdDestroyCanvas(hCanvas); + EndPaint(hwndMain, &ps); + return 0; + } + + case WM_PRINTCLIENT: + { + HDC dc = (HDC) wParam; + WD_HCANVAS hCanvas; + + hCanvas = wdCreateCanvasWithHDC(dc, NULL, 0); + PaintToCanvas(hCanvas); + wdDestroyCanvas(hCanvas); + return 0; + } + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + + +int APIENTRY +_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) +{ + WNDCLASS wc = { 0 }; + MSG msg; + + wdInitialize(WD_INIT_TEXTAPI); + + /* Register main window class */ + wc.lpfnWndProc = MainWinProc; + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wc.lpszClassName = _T("main_window"); + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + /* Create main window */ + hwndMain = CreateWindow( + _T("main_window"), _T("LibWinDraw Example: Draw Text"), + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 650, + NULL, NULL, hInstance, NULL + ); + SendMessage(hwndMain, WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), + MAKELPARAM(TRUE, 0)); + ShowWindow(hwndMain, nCmdShow); + + /* Message loop */ + while(GetMessage(&msg, NULL, 0, 0)) { + if(IsDialogMessage(hwndMain, &msg)) + continue; + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + wdTerminate(WD_INIT_TEXTAPI); + + /* Return exit code of WM_QUIT */ + return (int)msg.wParam; +} diff --git a/include/wdl.h b/include/wdl.h index f795ce7..2a98daf 100644 --- a/include/wdl.h +++ b/include/wdl.h @@ -1,6 +1,6 @@ /* * WinDrawLib - * Copyright (c) 2015-2018 Martin Mitas + * Copyright (c) 2015-2019 Martin Mitas * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -145,6 +145,7 @@ void wdPreInitialize(void (*fnLock)(void), void (*fnUnlock)(void), DWORD dwFlags #define WD_INIT_COREAPI 0x0000 #define WD_INIT_IMAGEAPI 0x0001 #define WD_INIT_STRINGAPI 0x0002 +#define WD_INIT_TEXTAPI 0x0004 BOOL wdInitialize(DWORD dwFlags); void wdTerminate(DWORD dwFlags); @@ -169,6 +170,7 @@ typedef struct WD_FONT_tag *WD_HFONT; typedef struct WD_IMAGE_tag *WD_HIMAGE; typedef struct WD_CACHEDIMAGE_tag* WD_HCACHEDIMAGE; typedef struct WD_PATH_tag *WD_HPATH; +typedef struct WD_HTEXT_tag *WD_HTEXT; /*************************** @@ -209,12 +211,18 @@ void wdDestroyCanvas(WD_HCANVAS hCanvas); * - The canvas has been created with wdCreateCanvasWithPaintStruct() and * is used strictly for handling WM_PAINT. * - wdEndPaint() returns TRUE. + * + * The cached canvas retains all the contents; so on the next WM_PAINT, + * the application can repaint only those arts of the canvas which need + * to present something new/different. */ void wdBeginPaint(WD_HCANVAS hCanvas); BOOL wdEndPaint(WD_HCANVAS hCanvas); /* This is supposed to be called to resize cached canvas (see above), if it * needs to be resized, typically as a response to WM_SIZE message. + * + * (Note however, that the painted contents of the canvas is lost.) */ BOOL wdResizeCanvas(WD_HCANVAS hCanvas, UINT uWidth, UINT uHeight); @@ -584,8 +592,13 @@ void wdBitBltHICON(WD_HCANVAS hCanvas, HICON hIcon, /* Functions for basic string output. Note the functions operate strictly with * Unicode strings. * + * This API allows to output simple string, using one font, one brush and one + * style. If you need to render more complex text (e.g. a paragraph, which + * uses different styles or fonts or brushes for various parts of the text) + * use rather the advanced text output API below. + * * All these functions are usable only if the library has been initialized with - * the flag WD_INIT_DRAWSTRINGAPI. + * the flag WD_INIT_STRINGAPI. */ /* Flags specifying alignment and various rendering options. @@ -596,6 +609,7 @@ void wdBitBltHICON(WD_HCANVAS hCanvas, HICON hIcon, #define WD_STR_LEFTALIGN 0x0000 #define WD_STR_CENTERALIGN 0x0001 #define WD_STR_RIGHTALIGN 0x0002 +#define WD_STR_JUSTIFY 0x0003 /* Falls back to left align if not supported */ #define WD_STR_TOPALIGN 0x0000 #define WD_STR_MIDDLEALIGN 0x0004 #define WD_STR_BOTTOMALIGN 0x0008 @@ -605,7 +619,7 @@ void wdBitBltHICON(WD_HCANVAS hCanvas, HICON hIcon, #define WD_STR_WORDELLIPSIS 0x0080 #define WD_STR_PATHELLIPSIS 0x0100 -#define WD_STR_ALIGNMASK (WD_STR_LEFTALIGN | WD_STR_CENTERALIGN | WD_STR_RIGHTALIGN) +#define WD_STR_ALIGNMASK (WD_STR_LEFTALIGN | WD_STR_CENTERALIGN | WD_STR_RIGHTALIGN | WD_STR_JUSTIFY) #define WD_STR_VALIGNMASK (WD_STR_TOPALIGN | WD_STR_MIDDLEALIGN | WD_STR_BOTTOMALIGN) #define WD_STR_ELLIPSISMASK (WD_STR_ENDELLIPSIS | WD_STR_WORDELLIPSIS | WD_STR_PATHELLIPSIS) @@ -621,6 +635,104 @@ void wdMeasureString(WD_HCANVAS hCanvas, WD_HFONT hFont, const WD_RECT* pRect, float wdStringWidth(WD_HCANVAS hCanvas, WD_HFONT hFont, const WCHAR* pszText); float wdStringHeight(WD_HFONT hFont, const WCHAR* pszText); + +/****************************** + *** Advanced Text Output *** + ******************************/ + +/* Functions for advanced string output. + * Note the functions operate strictly with Unicode strings. + * + * This API is more complex and more difficult to use then wdDrawString(), but + * it offers richer functionality. For example it allows to render paragraph of + * text, whose parts use different fonts, effects, or brushes. + * + * Consider for example paragraph of a text of normal text which includes some + * hyper-links and/or some emphasis of text using a bold or italic typeface + * and/or talks some about some programming language and the described + * identifier should be rendered in a monospaced font. + * + * All these functions are usable only if the library has been initialized with + * the flag WD_INIT_TEXTAPI. + * + * WARNING: This module is currently available only for D2D back-end. If not + * available, wdInitialize(WD_INIT_TEXTAPI) shall fail. + */ + +/* Flags specifying alignment and various rendering options. + * + * Most of the flags are for wdCreateText(). + * Only WD_TEXT_NOCLIP is for wdDrawText(). + */ +#define WD_TEXT_LEFTALIGN WD_STR_LEFTALIGN +#define WD_TEXT_CENTERALIGN WD_STR_CENTERALIGN +#define WD_TEXT_RIGHTALIGN WD_STR_RIGHTALIGN +#define WD_TEXT_JUSTIFY WD_STR_JUSTIFY /* Falls back to left align if not supported */ +#define WD_TEXT_TOPALIGN WD_STR_TOPALIGN +#define WD_TEXT_MIDDLEALIGN WD_STR_MIDDLEALIGN +#define WD_TEXT_BOTTOMALIGN WD_STR_BOTTOMALIGN +#define WD_TEXT_NOCLIP WD_STR_NOCLIP +#define WD_TEXT_NOWRAP WD_STR_NOWRAP +#define WD_TEXT_ENDELLIPSIS WD_STR_ENDELLIPSIS +#define WD_TEXT_WORDELLIPSIS WD_STR_WORDELLIPSIS +#define WD_TEXT_PATHELLIPSIS WD_STR_PATHELLIPSIS + +#define WD_TEXT_ALIGNMASK WD_STR_ALIGNMASK +#define WD_TEXT_VALIGNMASK WD_STR_VALIGNMASK +#define WD_TEXT_ELLIPSISMASK WD_STR_ELLIPSISMASK + + +WD_HTEXT wdCreateText(WD_HFONT hFont, const WD_RECT* pRect, + const WCHAR* pszText, int iTextLength, DWORD dwFlags); +void wdDestroyText(WD_HTEXT hText); + +/* Get minimal width required for reasonable output. If lower width is requested, + * there might be some line breaks inside words or similar unpleasant effects. + */ +float wdMinimalTextWidth(WD_HTEXT hText); + +/* Customizers of the text layout. + * + * Typically these apply some effects, other font or other text attribute + * to the specified range of the whole text. + */ + +#define WD_TEXTSTYLE_NORMAL 0 +#define WD_TEXTSTYLE_OBLIQUE 1 +#define WD_TEXTSTYLE_ITALIC 2 + +void wdSetTextMaxWidth(WD_HTEXT hText, float fWidth); +void wdSetTextMaxHeight(WD_HTEXT hText, float fHeight); +BOOL wdSetTextFontFamily(WD_HTEXT hText, UINT uPos, UINT uLen, const WCHAR* pszFamilyName); +void wdSetTextFontSize(WD_HTEXT hText, UINT uPos, UINT uLen, float fSize); +void wdSetTextFontStyle(WD_HTEXT hText, UINT uPos, UINT uLen, int iStyle); +void wdSetTextFontWeight(WD_HTEXT hText, UINT uPos, UINT uLen, LONG lfWeight); +void wdSetTextStrikethrough(WD_HTEXT hText, UINT uPos, UINT uLen, BOOL bStrikethrough); +void wdSetTextUnderline(WD_HTEXT hText, UINT uPos, UINT uLen, BOOL bUnderline); + +/* Getters inspecting the text layout. + */ +typedef struct WD_TEXTMETRICS_tag WD_TEXTMETRICS; +struct WD_TEXTMETRICS_tag { + float fLeft; + float fTop; + float fWidth; /* Computed/required width. */ + float fWidthWithTrailingWhitespace; + float fHeight; /* Computed/required height. */ + float fInitialWidth; /* Width of the rect passed into wdCreateText() */ + float fInitialHeight; /* Height of the rect passed into wdCreateText() */ + UINT32 uMaxBidiDepth; + UINT32 uLineCount; +}; + +void wdTextMetrics(WD_HTEXT hText, WD_TEXTMETRICS* pMetrics); + +/* Draw the text. + */ +void wdDrawText(WD_HCANVAS hCanvas, WD_HTEXT hText, WD_HBRUSH hBrush, + float x, float y, DWORD dwFlags); + + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c6f4c64..9dd0187 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,6 +29,7 @@ add_library(windrawlib STATIC path.c string.c strokestyle.c + text.c ) add_definitions(-DUNICODE -D_UNICODE) diff --git a/src/backend-dwrite.c b/src/backend-dwrite.c index a9899dc..1549ea6 100644 --- a/src/backend-dwrite.c +++ b/src/backend-dwrite.c @@ -221,20 +221,24 @@ dwrite_create_text_layout(dummy_IDWriteTextFormat* tf, const WD_RECT* rect, return NULL; } - if(flags & WD_STR_RIGHTALIGN) - tla = dummy_DWRITE_TEXT_ALIGNMENT_TRAILING; - else if(flags & WD_STR_CENTERALIGN) - tla = dummy_DWRITE_TEXT_ALIGNMENT_CENTER; - else + switch(flags & WD_STR_ALIGNMASK) { + case WD_STR_LEFTALIGN: tla = dummy_DWRITE_TEXT_ALIGNMENT_LEADING; break; + case WD_STR_CENTERALIGN: tla = dummy_DWRITE_TEXT_ALIGNMENT_CENTER; break; + case WD_STR_RIGHTALIGN: tla = dummy_DWRITE_TEXT_ALIGNMENT_TRAILING; break; + case WD_STR_JUSTIFY: tla = dummy_DWRITE_TEXT_ALIGNMENT_JUSTIFY; break; + } + hr = dummy_IDWriteTextLayout_SetTextAlignment(layout, tla); + if(FAILED(hr) && tla == dummy_DWRITE_TEXT_ALIGNMENT_JUSTIFY) { + /* Older DWrite versions (Windows 7) do not support DWRITE_TEXT_ALIGNMENT_JUSTIFY. */ tla = dummy_DWRITE_TEXT_ALIGNMENT_LEADING; - dummy_IDWriteTextLayout_SetTextAlignment(layout, tla); - - if(flags & WD_STR_BOTTOMALIGN) - tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_FAR; - else if(flags & WD_STR_MIDDLEALIGN) - tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_CENTER; - else - tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_NEAR; + dummy_IDWriteTextLayout_SetTextAlignment(layout, tla); + } + + switch(flags & WD_STR_VALIGNMASK) { + case WD_STR_MIDDLEALIGN: tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_CENTER; break; + case WD_STR_BOTTOMALIGN: tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_FAR; break; + default: tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_NEAR; break; + } dummy_IDWriteTextLayout_SetParagraphAlignment(layout, tla); if(flags & WD_STR_NOWRAP) diff --git a/src/backend-gdix.c b/src/backend-gdix.c index ddda8ec..8adeccc 100644 --- a/src/backend-gdix.c +++ b/src/backend-gdix.c @@ -376,20 +376,19 @@ gdix_canvas_apply_string_flags(gdix_canvas_t* c, DWORD flags) int sff; int trim; - if(flags & WD_STR_RIGHTALIGN) - sfa = dummy_StringAlignmentFar; - else if(flags & WD_STR_CENTERALIGN) - sfa = dummy_StringAlignmentCenter; - else - sfa = dummy_StringAlignmentNear; + /* Note WD_STR_JUSTIFY is not supported here. */ + switch(flags & WD_STR_ALIGNMASK) { + case WD_STR_CENTERALIGN: sfa = dummy_StringAlignmentCenter; break; + case WD_STR_RIGHTALIGN: sfa = dummy_StringAlignmentFar; break; + default: sfa = dummy_StringAlignmentNear; break; + } gdix_vtable->fn_SetStringFormatAlign(c->string_format, sfa); - if(flags & WD_STR_BOTTOMALIGN) - sfa = dummy_StringAlignmentFar; - else if(flags & WD_STR_MIDDLEALIGN) - sfa = dummy_StringAlignmentCenter; - else - sfa = dummy_StringAlignmentNear; + switch(flags & WD_STR_VALIGNMASK) { + case WD_STR_MIDDLEALIGN: sfa = dummy_StringAlignmentCenter; break; + case WD_STR_BOTTOMALIGN: sfa = dummy_StringAlignmentFar; break; + default: sfa = dummy_StringAlignmentNear; break; + } gdix_vtable->fn_SetStringFormatLineAlign(c->string_format, sfa); sff = 0; diff --git a/src/dummy/dwrite.h b/src/dummy/dwrite.h index 2008592..98a775f 100644 --- a/src/dummy/dwrite.h +++ b/src/dummy/dwrite.h @@ -101,7 +101,8 @@ typedef enum dummy_DWRITE_TEXT_ALIGNMENT_tag dummy_DWRITE_TEXT_ALIGNMENT; enum dummy_DWRITE_TEXT_ALIGNMENT_tag { dummy_DWRITE_TEXT_ALIGNMENT_LEADING = 0, dummy_DWRITE_TEXT_ALIGNMENT_TRAILING, - dummy_DWRITE_TEXT_ALIGNMENT_CENTER + dummy_DWRITE_TEXT_ALIGNMENT_CENTER, + dummy_DWRITE_TEXT_ALIGNMENT_JUSTIFY }; typedef enum dummy_DWRITE_PARAGRAPH_ALIGNMENT_tag dummy_DWRITE_PARAGRAPH_ALIGNMENT; @@ -157,6 +158,12 @@ struct dummy_DWRITE_TEXT_METRICS_tag { UINT32 lineCount; }; +typedef struct dummy_DWRITE_TEXT_RANGE_tag dummy_DWRITE_TEXT_RANGE; +struct dummy_DWRITE_TEXT_RANGE_tag { + UINT32 startPosition; + UINT32 length; +}; + /****************************** *** Forward declarations *** @@ -508,21 +515,21 @@ struct dummy_IDWriteTextLayoutVtbl_tag { STDMETHOD(dummy_GetLocaleName)(void); /* IDWriteTextLayout methods */ - STDMETHOD(dummy_SetMaxWidth)(void); - STDMETHOD(dummy_SetMaxHeight)(void); + STDMETHOD(SetMaxWidth)(dummy_IDWriteTextLayout*, FLOAT); + STDMETHOD(SetMaxHeight)(dummy_IDWriteTextLayout*, FLOAT); STDMETHOD(dummy_SetFontCollection)(void); - STDMETHOD(dummy_SetFontFamilyName)(void); - STDMETHOD(dummy_SetFontWeight)(void); - STDMETHOD(dummy_SetFontStyle)(void); + STDMETHOD(SetFontFamilyName)(dummy_IDWriteTextLayout*, const WCHAR*, dummy_DWRITE_TEXT_RANGE); + STDMETHOD(SetFontWeight)(dummy_IDWriteTextLayout*, dummy_DWRITE_FONT_WEIGHT, dummy_DWRITE_TEXT_RANGE); + STDMETHOD(SetFontStyle)(dummy_IDWriteTextLayout*, dummy_DWRITE_FONT_STYLE, dummy_DWRITE_TEXT_RANGE); STDMETHOD(dummy_SetFontStretch)(void); - STDMETHOD(dummy_SetFontSize)(void); - STDMETHOD(dummy_SetUnderline)(void); - STDMETHOD(dummy_SetStrikethrough)(void); + STDMETHOD(SetFontSize)(dummy_IDWriteTextLayout*, FLOAT, dummy_DWRITE_TEXT_RANGE); + STDMETHOD(SetUnderline)(dummy_IDWriteTextLayout*, BOOL, dummy_DWRITE_TEXT_RANGE); + STDMETHOD(SetStrikethrough)(dummy_IDWriteTextLayout*, BOOL, dummy_DWRITE_TEXT_RANGE); STDMETHOD(dummy_SetDrawingEffect)(void); STDMETHOD(dummy_SetInlineObject)(void); STDMETHOD(dummy_SetTypography)(void); STDMETHOD(dummy_SetLocaleName)(void); - STDMETHOD(dummy_GetMaxWidth)(void); + STDMETHOD_(FLOAT, GetMaxWidth)(dummy_IDWriteTextLayout*); STDMETHOD(dummy_GetMaxHeight)(void); STDMETHOD(dummy_GetFontCollection2)(void); STDMETHOD(dummy_GetFontFamilyNameLength2)(void); @@ -543,7 +550,7 @@ struct dummy_IDWriteTextLayoutVtbl_tag { STDMETHOD(GetMetrics)(dummy_IDWriteTextLayout*, dummy_DWRITE_TEXT_METRICS*); STDMETHOD(dummy_GetOverhangMetrics)(void); STDMETHOD(dummy_GetClusterMetrics)(void); - STDMETHOD(dummy_DetermineMinWidth)(void); + STDMETHOD(DetermineMinWidth)(dummy_IDWriteTextLayout*, FLOAT*); STDMETHOD(dummy_HitTestPoint)(void); STDMETHOD(dummy_HitTestTextPosition)(void); STDMETHOD(dummy_HitTestTextRange)(void); @@ -561,7 +568,17 @@ struct dummy_IDWriteTextLayout_tag { #define dummy_IDWriteTextLayout_SetWordWrapping(self,a) (self)->vtbl->SetWordWrapping(self,a) #define dummy_IDWriteTextLayout_SetReadingDirection(self,a) (self)->vtbl->SetReadingDirection(self,a) #define dummy_IDWriteTextLayout_SetTrimming(self,a,b) (self)->vtbl->SetTrimming(self,a,b) +#define dummy_IDWriteTextLayout_SetMaxWidth(self,a) (self)->vtbl->SetMaxWidth(self,a) +#define dummy_IDWriteTextLayout_SetMaxHeight(self,a) (self)->vtbl->SetMaxHeight(self,a) +#define dummy_IDWriteTextLayout_SetFontFamilyName(self,a,b) (self)->vtbl->SetFontFamilyName(self,a,b) +#define dummy_IDWriteTextLayout_SetFontWeight(self,a,b) (self)->vtbl->SetFontWeight(self,a,b) +#define dummy_IDWriteTextLayout_SetFontStyle(self,a,b) (self)->vtbl->SetFontStyle(self,a,b) +#define dummy_IDWriteTextLayout_SetFontSize(self,a,b) (self)->vtbl->SetFontSize(self,a,b) +#define dummy_IDWriteTextLayout_SetStrikethrough(self,a,b) (self)->vtbl->SetStrikethrough(self,a,b) +#define dummy_IDWriteTextLayout_SetUnderline(self,a,b) (self)->vtbl->SetUnderline(self,a,b) +#define dummy_IDWriteTextLayout_GetMaxWidth(self) (self)->vtbl->GetMaxWidth(self) #define dummy_IDWriteTextLayout_GetMetrics(self,a) (self)->vtbl->GetMetrics(self,a) +#define dummy_IDWriteTextLayout_DetermineMinWidth(self,a) (self)->vtbl->DetermineMinWidth(self,a) #endif /* DUMMY_DWRITE_H */ diff --git a/src/init.c b/src/init.c index 3dc355c..1e87547 100644 --- a/src/init.c +++ b/src/init.c @@ -1,6 +1,6 @@ /* * WinDrawLib - * Copyright (c) 2016 Martin Mitas + * Copyright (c) 2016-2019 Martin Mitas * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -45,7 +45,7 @@ wdPreInitialize(void (*fnLock)(void), void (*fnUnlock)(void), DWORD dwFlags) static int -wd_init_core_api(void) +core_init(void) { if(!(wd_preinit_flags & WD_DISABLE_D2D)) { if(d2d_init() == 0) @@ -61,7 +61,7 @@ wd_init_core_api(void) } static void -wd_fini_core_api(void) +core_fini(void) { if(d2d_enabled()) d2d_fini(); @@ -69,156 +69,126 @@ wd_fini_core_api(void) gdix_fini(); } -static int -wd_init_image_api(void) -{ - if(d2d_enabled()) { - return wic_init(); - } else { - /* noop */ - return 0; - } -} +static UINT core_counter = 0; +static UINT wic_counter = 0; +static UINT dwrite_counter = 0; -static void -wd_fini_image_api(void) -{ - if(d2d_enabled()) { - wic_fini(); - } else { - /* noop */ - } -} +#define WD_MOD_WIC 0x0001 +#define WD_MOD_DWRITE 0x0002 -static int -wd_init_string_api(void) -{ - if(d2d_enabled()) { - return dwrite_init(); - } else { - /* noop */ - return 0; - } -} +static void wdTerminateImpl(DWORD dwModMask); -static void -wd_fini_string_api(void) +BOOL +wdInitialize(DWORD dwFlags) { - if(d2d_enabled()) { - dwrite_fini(); - } else { - /* noop */ + int err = -1; + DWORD undo = 0; + + wd_lock(); + + /* Core module */ + if(core_counter == 0) { + if(core_init() != 0) + goto out; } -} + core_counter++; + /* WIC module */ + if(d2d_enabled() && (dwFlags & WD_INIT_IMAGEAPI)) { + if(wic_counter == 0) { + if(wic_init() != 0) + goto out; + } -static const struct { - int (*fn_init)(void); - void (*fn_fini)(void); -} wd_modules[] = { - { wd_init_core_api, wd_fini_core_api }, - { wd_init_image_api, wd_fini_image_api }, - { wd_init_string_api, wd_fini_string_api } -}; + wic_counter++; + undo |= WD_MOD_WIC; + } -#define WD_MOD_COUNT (sizeof(wd_modules) / sizeof(wd_modules[0])) + /* DWrite module */ + if(d2d_enabled() && (dwFlags & (WD_INIT_STRINGAPI | WD_INIT_TEXTAPI))) { + if(dwrite_counter == 0) { + if(dwrite_init() != 0) + goto out; + } -#define WD_MOD_COREAPI 0 -#define WD_MOD_IMAGEAPI 1 -#define WD_MOD_STRINGAPI 2 + dwrite_counter++; + undo |= WD_MOD_DWRITE; + } -static UINT wd_init_counter[WD_MOD_COUNT] = { 0 }; + if(gdix_enabled() && (dwFlags & WD_INIT_TEXTAPI)) { + WD_TRACE("wdInitialize: WD_INIT_TEXTAPI for GDI+ back-end is not (yet?) implemented."); + goto out; + } + /* Success. */ + err = 0; -BOOL -wdInitialize(DWORD dwFlags) -{ - BOOL want_init[WD_MOD_COUNT]; - int i; +out: + /* In case of an error, undo what we have done. */ + if(err != 0 && undo != 0) + wdTerminateImpl(undo); - want_init[WD_MOD_COREAPI] = TRUE; - want_init[WD_MOD_IMAGEAPI] = (dwFlags & WD_INIT_IMAGEAPI); - want_init[WD_MOD_STRINGAPI] = (dwFlags & WD_INIT_STRINGAPI); + wd_unlock(); - wd_lock(); + return (err == 0); +} + +static void +wdTerminateImpl(DWORD dwModMask) +{ + /* Handle the optional modules. */ + if((dwModMask & WD_MOD_WIC) && --wic_counter == 0) + wic_fini(); - for(i = 0; i < WD_MOD_COUNT; i++) { - if(!want_init[i]) - continue; + if((dwModMask & WD_MOD_DWRITE) && --dwrite_counter == 0) + dwrite_fini(); - wd_init_counter[i]++; - if(wd_init_counter[i] > 0) { - if(wd_modules[i].fn_init() != 0) - goto fail; + /* Handle the core module. + * + * Note that if the core module counter drops to zero, we make sure no + * optional module survives. This should only happen when that caller + * forgot to pass some flags into wdTerminate() so it does not match + * those passed previously into wdInitialize(). + */ + if(--core_counter == 0) { + if(wic_counter > 0) { + WD_TRACE("wdTerminate: Forcefully terminating WIC module."); + wic_fini(); + wic_counter = 0; } - } - - wd_unlock(); - return TRUE; - -fail: - /* Undo initializations from successful iterations. */ - while(--i >= 0) { - if(want_init[i]) { - wd_init_counter[i]--; - if(wd_init_counter[i] == 0) - wd_modules[i].fn_fini(); + if(dwrite_counter > 0) { + WD_TRACE("wdTerminate: Forcefully terminating DWrite module."); + dwrite_fini(); + dwrite_counter = 0; } - } - wd_unlock(); - return FALSE; + core_fini(); + } } void wdTerminate(DWORD dwFlags) { - BOOL want_fini[WD_MOD_COUNT]; - int i; + DWORD dwModMask = 0; - want_fini[WD_MOD_COREAPI] = TRUE; - want_fini[WD_MOD_IMAGEAPI] = (dwFlags & WD_INIT_IMAGEAPI); - want_fini[WD_MOD_STRINGAPI] = (dwFlags & WD_INIT_STRINGAPI); + if(d2d_enabled() && (dwFlags & WD_INIT_IMAGEAPI)) + dwModMask |= WD_MOD_WIC; + if(d2d_enabled() && (dwFlags & (WD_INIT_STRINGAPI | WD_INIT_TEXTAPI))) + dwModMask |= WD_MOD_DWRITE; wd_lock(); - - for(i = WD_MOD_COUNT-1; i >= 0; i--) { - if(!want_fini[i]) - continue; - - wd_init_counter[i]--; - if(wd_init_counter[i] == 0) - wd_modules[i].fn_fini(); - } - - /* If core module counter has dropped to zero, caller likely forgot to - * terminate some optional module (i.e. mismatching flags for wdTerminate() - * somewhere. So lets kill all those modules forcefully now anyway even - * though well behaving applications should never do that... - */ - if(wd_init_counter[WD_MOD_COREAPI] == 0) { - for(i = WD_MOD_COUNT-1; i >= 0; i--) { - if(wd_init_counter[i] > 0) { - WD_TRACE("wdTerminate: Forcefully terminating module %d.", i); - wd_modules[i].fn_fini(); - wd_init_counter[i] = 0; - } - } - } - + wdTerminateImpl(dwModMask); wd_unlock(); } int wdBackend(void) { - if(d2d_enabled()) { + if(d2d_enabled()) return WD_BACKEND_D2D; - } - if(gdix_enabled()) { + if(gdix_enabled()) return WD_BACKEND_GDIPLUS; - } - return -1; + return -1; } diff --git a/src/text.c b/src/text.c new file mode 100644 index 0000000..3412772 --- /dev/null +++ b/src/text.c @@ -0,0 +1,215 @@ +/* + * WinDrawLib + * Copyright (c) 2019 Martin Mitas + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "misc.h" +#include "backend-d2d.h" +#include "backend-dwrite.h" +#include "backend-gdix.h" +#include "lock.h" + + +WD_HTEXT +wdCreateText(WD_HFONT hFont, const WD_RECT* pRect, + const WCHAR* pszText, int iTextLength, DWORD dwFlags) +{ + const WD_RECT dummyRect = { 0.0f, 0.0f, 0.0f, 0.0f }; + + if(pRect->x0 > pRect->x1 || pRect->y0 > pRect->y1) + pRect = &dummyRect; + + if(d2d_enabled()) { + dwrite_font_t* font = (dwrite_font_t*) hFont; + dummy_IDWriteTextLayout* layout; + + layout = dwrite_create_text_layout(font->tf, pRect, pszText, iTextLength, dwFlags); + + return (WD_HTEXT) layout; + } else { + WD_TRACE("wdCreateText: Not currently implemented for GDI+ back-end."); + return NULL; + } +} + +void +wdDestroyText(WD_HTEXT hText) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_IDWriteTextLayout_Release(layout); + } +} + +float +wdMinimalTextWidth(WD_HTEXT hText) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + float min_width; + + dummy_IDWriteTextLayout_DetermineMinWidth(layout, &min_width); + return min_width; + } else { + return 0; + } +} + +void +wdSetTextMaxWidth(WD_HTEXT hText, float fWidth) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_IDWriteTextLayout_SetMaxWidth(layout, fWidth); + } +} + +void +wdSetTextMaxHeight(WD_HTEXT hText, float fHeight) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_IDWriteTextLayout_SetMaxHeight(layout, fHeight); + } +} + +BOOL +wdSetTextFontFamily(WD_HTEXT hText, UINT uPos, UINT uLen, const WCHAR* pszFamilyName) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + HRESULT hr; + + hr = dummy_IDWriteTextLayout_SetFontFamilyName(layout, pszFamilyName, range); + if(FAILED(hr)) { + WD_TRACE_HR("wdSetTextFontFamily: IDWriteTextLayout::SetFontFamilyName() failed."); + return FALSE; + } + + return TRUE; + } else { + return FALSE; + } +} + +void +wdSetTextFontSize(WD_HTEXT hText, UINT uPos, UINT uLen, float fSize) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + + dummy_IDWriteTextLayout_SetFontSize(layout, fSize, range); + } +} + +void +wdSetTextFontStyle(WD_HTEXT hText, UINT uPos, UINT uLen, int iStyle) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + dummy_DWRITE_FONT_STYLE style; + + switch(iStyle) { + case WD_TEXTSTYLE_NORMAL: style = dummy_DWRITE_FONT_STYLE_NORMAL; break; + case WD_TEXTSTYLE_OBLIQUE: style = dummy_DWRITE_FONT_STYLE_OBLIQUE; break; + case WD_TEXTSTYLE_ITALIC: /* Pass through. */ + default: style = dummy_DWRITE_FONT_STYLE_ITALIC; break; + } + + dummy_IDWriteTextLayout_SetFontStyle(layout, style, range); + } +} + +void +wdSetTextFontWeight(WD_HTEXT hText, UINT uPos, UINT uLen, LONG lfWeight) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + + dummy_IDWriteTextLayout_SetFontWeight(layout, lfWeight, range); + } +} + +void +wdSetTextStrikethrough(WD_HTEXT hText, UINT uPos, UINT uLen, BOOL bStrikethrough) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + + dummy_IDWriteTextLayout_SetStrikethrough(layout, bStrikethrough, range); + } +} + +void +wdSetTextUnderline(WD_HTEXT hText, UINT uPos, UINT uLen, BOOL bUnderline) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + + dummy_IDWriteTextLayout_SetUnderline(layout, bUnderline, range); + } +} + +void +wdTextMetrics(WD_HTEXT hText, WD_TEXTMETRICS* pMetrics) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + + /* We have designed WD_TEXTMETRICS to be binary compatible with + * DWRITE_TEXT_METRICS. */ + dummy_IDWriteTextLayout_GetMetrics(layout, (dummy_DWRITE_TEXT_METRICS*) pMetrics); + } +} + +void +wdDrawText(WD_HCANVAS hCanvas, WD_HTEXT hText, WD_HBRUSH hBrush, + float x, float y, DWORD dwFlags) +{ + if(d2d_enabled()) { + dummy_D2D1_POINT_2F origin = { x, y }; + d2d_canvas_t* c = (d2d_canvas_t*) hCanvas; + dummy_ID2D1Brush* b = (dummy_ID2D1Brush*) hBrush; + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_D2D1_MATRIX_3X2_F old_matrix; + + if(c->flags & D2D_CANVASFLAG_RTL) { + d2d_disable_rtl_transform(c, &old_matrix); + origin.x = (float)c->width - dummy_IDWriteTextLayout_GetMaxWidth(layout); + + dummy_IDWriteTextLayout_SetReadingDirection(layout, + dummy_DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + } + + dummy_ID2D1RenderTarget_DrawTextLayout(c->target, origin, layout, b, + (dwFlags & WD_STR_NOCLIP) ? 0 : dummy_D2D1_DRAW_TEXT_OPTIONS_CLIP); + + if(c->flags & D2D_CANVASFLAG_RTL) { + dummy_ID2D1RenderTarget_SetTransform(c->target, &old_matrix); + } + } +}