mirror of
https://github.com/reactos/reactos.git
synced 2025-08-07 05:43:08 +00:00
[GDI32][LPK] BiDi support for ExtTextOut and GetCharacterPlacement (#534)
Introduce BiDi (bi-directional text) support for ExtTextOut and GetCharacterPlacement, using Wine's GDI BIDI_Reorder function.
Solves the main issue with CORE-7003.
To be compatible with Win2k3+, introduce the "Language Pack" (LPK) dll.
- All the bidi code is removed from gdi32 and replaced by calls to LPK.
Gdi32 uses dynamic linking to lpk.dll. In case of linking failure no bidi processing will be available.
- Implemented LpkGetCharacterPlacement.
- Implement LpkExtTextOut.
- Add a demo test program to show how the apis should function.
- Added all the remaining code, added special case for lpDx calculation if also GCP_GLYPHSHAPE flag was called.
Applications that call GCP that use GCP_GLYPHSHAPE flags should also use the GCP_REORDER flag.
(As written in dd144860
(v=vs.85).aspx )
- Add ETO_RTLREADING flag handling.
Imported the ETO_RTLREADING flag handling from wine, which changes the string part order (runs).
A RRR1LLLRRR2 string without will show as RRR1LLLRRR2 without it, with it RRR2LLLRRR1.
This commit is contained in:
parent
3ca1ac639c
commit
a4a59ad413
19 changed files with 1434 additions and 86 deletions
|
@ -1,2 +1,3 @@
|
|||
add_subdirectory(biditext)
|
||||
add_subdirectory(paintdesktop)
|
||||
add_subdirectory(sysicon)
|
||||
|
|
BIN
modules/rostests/win32/user32/biditext/Application.ico
Normal file
BIN
modules/rostests/win32/user32/biditext/Application.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
30
modules/rostests/win32/user32/biditext/Application.manifest
Normal file
30
modules/rostests/win32/user32/biditext/Application.manifest
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Supports Windows Vista / Server 2008 -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Supports Windows 7 / Server 2008 R2 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Supports Windows 8 / Server 2012 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Supports Windows 8.1 / Server 2012 R2 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Supports Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
6
modules/rostests/win32/user32/biditext/CMakeLists.txt
Normal file
6
modules/rostests/win32/user32/biditext/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
add_rc_deps(biditext.rc ${CMAKE_CURRENT_SOURCE_DIR}/Application.ico)
|
||||
add_executable(biditext biditext.c biditext.rc)
|
||||
set_module_type(biditext win32gui UNICODE)
|
||||
add_importlibs(biditext lpk gdi32 user32 shell32 comctl32 msvcrt kernel32 ntdll)
|
||||
add_rostests_file(TARGET biditext SUBDIR suppl)
|
273
modules/rostests/win32/user32/biditext/biditext.c
Normal file
273
modules/rostests/win32/user32/biditext/biditext.c
Normal file
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Tests
|
||||
* FILE: rostests/win32/user32/biditext/biditext.c
|
||||
* PURPOSE: Demonstrates how ExtTextOut and GetCharacterPlacement
|
||||
* handle bidirectional text strings via certain selection
|
||||
* of flags provided to them.
|
||||
*
|
||||
* PROGRAMMER: Program skeleton: https://github.com/TransmissionZero/MinGW-Win32-Application
|
||||
* Test code by Baruch Rutman
|
||||
*/
|
||||
|
||||
#include "biditext.h"
|
||||
|
||||
/* Prototypes */
|
||||
DWORD WINAPI LpkGetCharacterPlacement(HDC hdc, LPCWSTR lpString, INT uCount, INT nMaxExtent,
|
||||
GCP_RESULTSW *lpResults, DWORD dwFlags, DWORD dwUnused);
|
||||
|
||||
BOOL WINAPI LpkExtTextOut(HDC hdc, int x, int y,
|
||||
UINT fuOptions, const RECT *lprc, LPCWSTR lpString,
|
||||
UINT uCount , const INT *lpDx, INT unknown);
|
||||
|
||||
/* Global instance handle */
|
||||
HINSTANCE g_hInstance = NULL;
|
||||
|
||||
/* Our application entry point */
|
||||
int WINAPI
|
||||
wWinMain(HINSTANCE hInstance,
|
||||
HINSTANCE hPrevInstance,
|
||||
LPTSTR lpszCmdLine,
|
||||
int nCmdShow)
|
||||
{
|
||||
INITCOMMONCONTROLSEX icc;
|
||||
HWND hWnd;
|
||||
HACCEL hAccelerators;
|
||||
MSG msg;
|
||||
|
||||
/* Assign global HINSTANCE */
|
||||
g_hInstance = hInstance;
|
||||
|
||||
/* Initialise common controls */
|
||||
icc.dwSize = sizeof(icc);
|
||||
icc.dwICC = ICC_WIN95_CLASSES;
|
||||
InitCommonControlsEx(&icc);
|
||||
|
||||
/* Register our main window class, or error */
|
||||
if (!RegisterMainWindowClass())
|
||||
{
|
||||
MessageBox(NULL, TEXT("Error registering main window class."), TEXT("Error"), MB_ICONERROR | MB_OK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create our main window, or error */
|
||||
if (!(hWnd = CreateMainWindow()))
|
||||
{
|
||||
MessageBox(NULL, TEXT("Error creating main window."), TEXT("Error"), MB_ICONERROR | MB_OK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load accelerators */
|
||||
hAccelerators = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR));
|
||||
|
||||
/* Show main window and force a paint */
|
||||
ShowWindow(hWnd, nCmdShow);
|
||||
UpdateWindow(hWnd);
|
||||
|
||||
/* Main message loop */
|
||||
while (GetMessage(&msg, NULL, 0, 0) > 0)
|
||||
{
|
||||
if (!TranslateAccelerator(hWnd, hAccelerators, &msg))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
return (int)msg.wParam;
|
||||
}
|
||||
|
||||
/* Dialog procedure for our "about" dialog */
|
||||
INT_PTR CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_COMMAND:
|
||||
{
|
||||
WORD id = LOWORD(wParam);
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case IDOK:
|
||||
case IDCANCEL:
|
||||
{
|
||||
EndDialog(hwndDlg, (INT_PTR)id);
|
||||
return (INT_PTR)TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
return (INT_PTR)TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
/* Show our "about" dialog */
|
||||
void ShowAboutDialog(HWND owner)
|
||||
{
|
||||
DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ABOUTDIALOG), owner, &AboutDialogProc);
|
||||
}
|
||||
|
||||
/* Main window class and title */
|
||||
static LPCTSTR MainWndClass = TEXT("BiDi Test");
|
||||
|
||||
/* Window procedure for our main window */
|
||||
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case WM_COMMAND:
|
||||
{
|
||||
WORD id = LOWORD(wParam);
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case ID_HELP_ABOUT:
|
||||
{
|
||||
ShowAboutDialog(hWnd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case ID_FILE_EXIT:
|
||||
{
|
||||
DestroyWindow(hWnd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
/* Prevent our window from being sized too small */
|
||||
MINMAXINFO *minMax = (MINMAXINFO*)lParam;
|
||||
minMax->ptMinTrackSize.x = 220;
|
||||
minMax->ptMinTrackSize.y = 110;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Item from system menu has been invoked */
|
||||
case WM_SYSCOMMAND:
|
||||
{
|
||||
WORD id = LOWORD(wParam);
|
||||
|
||||
switch (id)
|
||||
{
|
||||
/* Show "about" dialog on about system menu item */
|
||||
case ID_HELP_ABOUT:
|
||||
{
|
||||
ShowAboutDialog(hWnd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
|
||||
HDC hdc = BeginPaint(hWnd, &ps);
|
||||
|
||||
LPWSTR szString = L"אבגדהABCDוזחטי";
|
||||
int Len = (int)wcslen(szString);
|
||||
|
||||
WCHAR Glyphs[100] = { 0 };
|
||||
WCHAR OutString[100] = { 0 };
|
||||
GCP_RESULTSW Results = { 0 };
|
||||
Results.lStructSize = sizeof(Results);
|
||||
Results.lpOutString = OutString;
|
||||
Results.lpGlyphs = Glyphs;
|
||||
Results.nGlyphs = 100;
|
||||
|
||||
TextOutW(hdc, 10, 10, L"Proper (string being used):", 27);
|
||||
TextOutW(hdc, 200, 10, szString, 14);
|
||||
TextOutW(hdc, 10, 30, L"Reversed (example):", 19);
|
||||
TextOutW(hdc, 200, 30, L"הדגבאABCDיטחזו", 14);
|
||||
|
||||
TextOutW(hdc, 10, 50, L"String with NULL LpkETO call (not reversed):", 44);
|
||||
LpkExtTextOut(hdc, 10, 70, 0, NULL, szString, Len, NULL, 0);
|
||||
|
||||
TextOutW(hdc, 10, 90, L"String with ETO_IGNORELANGUAGE LpkETO call (not reversed):", 58);
|
||||
LpkExtTextOut(hdc, 10, 110, ETO_IGNORELANGUAGE, NULL, szString, Len, NULL, 0);
|
||||
|
||||
TextOutW(hdc, 10, 130, L"String with GCP_REORDER and ETO_GLYPH_INDEX LpkGCP call (not reversed):", 71);
|
||||
LpkGetCharacterPlacement(hdc, szString, Len, 0, &Results, GCP_REORDER, 0);
|
||||
LpkExtTextOut(hdc, 10, 150, ETO_GLYPH_INDEX, NULL, Glyphs, Results.nGlyphs, NULL, 0);
|
||||
TextOutW(hdc, 10, 170, L"String with GCP_REORDER and ETO_IGNORELANGUAGE LpkGCP call (not reversed, lpOutString):", 87);
|
||||
ExtTextOutW(hdc, 10, 190, ETO_IGNORELANGUAGE, NULL, OutString, Results.nGlyphs, NULL);
|
||||
|
||||
TextOutW(hdc, 10, 210, L"String without GCP_REORDER and ETO_GLYPH_INDEX LpkGCP call (reversed):", 70);
|
||||
LpkGetCharacterPlacement(hdc, szString, Len, 0, &Results, 0, 0);
|
||||
LpkExtTextOut(hdc, 10, 230, ETO_GLYPH_INDEX, NULL, Glyphs, Results.nGlyphs, NULL, 0);
|
||||
TextOutW(hdc, 10, 250, L"String without GCP_REORDER and ETO_IGNORELANGUAGE LpkGCP call (reversed, lpOutString):", 86);
|
||||
ExtTextOutW(hdc, 10, 270, ETO_IGNORELANGUAGE, NULL, OutString, Len, NULL);
|
||||
|
||||
TextOutW(hdc, 10, 290, L"String with ETO_IGNORELANGUAGE ETO call (reversed, not Lpk direct call!):", 73);
|
||||
ExtTextOutW(hdc, 10, 310, ETO_IGNORELANGUAGE, NULL, szString, Len, NULL);
|
||||
|
||||
TextOutW(hdc, 10, 330, L"String with ETO_RTLREADING LpkETO call (slight order change)", 60);
|
||||
LpkExtTextOut(hdc, 10, 350, ETO_RTLREADING, NULL, szString, Len, NULL, 0);
|
||||
|
||||
TextOutW(hdc, 10, 370, L"String with ETO_RTLREADING ETO call (slight order change)", 57);
|
||||
ExtTextOutW(hdc, 10, 390, ETO_RTLREADING, NULL, szString, Len, NULL);
|
||||
|
||||
EndPaint(hWnd, &ps);
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
{
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
/* Register a class for our main window */
|
||||
BOOL RegisterMainWindowClass()
|
||||
{
|
||||
WNDCLASSEX wc;
|
||||
|
||||
/* Class for our main window */
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = &MainWndProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = g_hInstance;
|
||||
wc.hIcon = (HICON)LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE |
|
||||
LR_DEFAULTCOLOR | LR_SHARED);
|
||||
wc.hCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
|
||||
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
|
||||
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAINMENU);
|
||||
wc.lpszClassName = MainWndClass;
|
||||
wc.hIconSm = (HICON)LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
|
||||
|
||||
return (RegisterClassEx(&wc)) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/* Create an instance of our main window */
|
||||
HWND CreateMainWindow()
|
||||
{
|
||||
/* Create instance of main window */
|
||||
HWND hWnd = CreateWindowEx(0, MainWndClass, MainWndClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
|
||||
NULL, NULL, g_hInstance, NULL);
|
||||
|
||||
if (hWnd)
|
||||
{
|
||||
/* Add "about" to the system menu */
|
||||
HMENU hSysMenu = GetSystemMenu(hWnd, FALSE);
|
||||
InsertMenu(hSysMenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
||||
InsertMenu(hSysMenu, 6, MF_BYPOSITION, ID_HELP_ABOUT, TEXT("About"));
|
||||
}
|
||||
|
||||
return hWnd;
|
||||
}
|
34
modules/rostests/win32/user32/biditext/biditext.h
Normal file
34
modules/rostests/win32/user32/biditext/biditext.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
|
||||
/* Global instance handle */
|
||||
extern HINSTANCE g_hInstance;
|
||||
|
||||
/* Window procedure for our main window */
|
||||
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
/* Register a class for our main window */
|
||||
BOOL RegisterMainWindowClass(void);
|
||||
|
||||
/* Create an instance of our main window */
|
||||
HWND CreateMainWindow(void);
|
||||
|
||||
/* Dialog procedure for our "about" dialog */
|
||||
INT_PTR CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
/* Show our "about" dialog */
|
||||
void ShowAboutDialog(HWND owner);
|
||||
|
||||
|
||||
#define IDI_APPICON 101
|
||||
#define IDR_MAINMENU 102
|
||||
#define IDR_ACCELERATOR 103
|
||||
#define IDD_ABOUTDIALOG 104
|
||||
#define ID_FILE_EXIT 40001
|
||||
#define ID_HELP_ABOUT 40002
|
||||
|
||||
#ifndef IDC_STATIC
|
||||
#define IDC_STATIC -1
|
||||
#endif
|
69
modules/rostests/win32/user32/biditext/biditext.rc
Normal file
69
modules/rostests/win32/user32/biditext/biditext.rc
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include "biditext.h"
|
||||
|
||||
/* Our main menu */
|
||||
IDR_MAINMENU MENU
|
||||
BEGIN
|
||||
POPUP "&File"
|
||||
BEGIN
|
||||
MENUITEM "E&xit", ID_FILE_EXIT
|
||||
END
|
||||
POPUP "&Help"
|
||||
BEGIN
|
||||
MENUITEM "&About", ID_HELP_ABOUT
|
||||
END
|
||||
END
|
||||
|
||||
/* Application manifest */
|
||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "Application.manifest"
|
||||
|
||||
/* Executable version information */
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,0
|
||||
PRODUCTVERSION 1,0,0,0
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG | VS_FF_PRERELEASE
|
||||
#else
|
||||
FILEFLAGS 0
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "080904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Transmission Zero"
|
||||
VALUE "FileDescription", "Win32 Example Application"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
VALUE "InternalName", "Win32App"
|
||||
VALUE "LegalCopyright", "©2017 Transmission Zero"
|
||||
VALUE "OriginalFilename", "Win32App.exe"
|
||||
VALUE "ProductName", "Win32 Example Application"
|
||||
VALUE "ProductVersion", "1.0.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x809, 1200
|
||||
END
|
||||
END
|
||||
|
||||
/* Our "about" dialog */
|
||||
IDD_ABOUTDIALOG DIALOGEX 0, 0, 147, 67
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "About"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
ICON IDI_APPICON,IDC_STATIC,7,7,20,20
|
||||
LTEXT "Win32 Example Application",IDC_STATIC,34,7,89,8
|
||||
LTEXT "©2017 Transmission Zero",IDC_STATIC,34,17,86,8
|
||||
DEFPUSHBUTTON "OK",IDOK,90,46,50,14,WS_GROUP
|
||||
END
|
||||
|
||||
/* Our accelerators */
|
||||
IDR_ACCELERATOR ACCELERATORS
|
||||
BEGIN
|
||||
"A", ID_HELP_ABOUT, VIRTKEY, ALT, NOINVERT
|
||||
END
|
Loading…
Add table
Add a link
Reference in a new issue