https://git.reactos.org/?p=reactos.git;a=commitdiff;h=a4a59ad4137fbf959d2cc…
commit a4a59ad4137fbf959d2cc7972ef4a5e33f6a80aa
Author: Baruch Rutman <peterooch(a)gmail.com>
AuthorDate: Wed May 30 15:41:22 2018 +0300
Commit: Hermès BÉLUSCA - MAÏTO <hermes.belusca-maito(a)reactos.org>
CommitDate: Wed May 30 14:41:22 2018 +0200
[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
https://msdn.microsoft.com/en-us/library/windows/desktop/dd144860(v=vs.85).… )
- 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.
---
dll/win32/lpk/CMakeLists.txt | 10 +-
dll/win32/lpk/bidi.c | 687 +++++++++++++++++++++
dll/win32/lpk/dllmain.c | 47 --
dll/win32/lpk/lpk.c | 181 ++++++
dll/win32/lpk/lpk.spec | 4 +-
dll/win32/lpk/ros_lpk.h | 34 +-
dll/win32/lpk/stub.c | 18 -
media/doc/README.WINE | 3 +
modules/rostests/win32/user32/CMakeLists.txt | 1 +
.../rostests/win32/user32/biditext/Application.ico | Bin 0 -> 23558 bytes
.../win32/user32/biditext/Application.manifest | 30 +
.../rostests/win32/user32/biditext/CMakeLists.txt | 6 +
modules/rostests/win32/user32/biditext/biditext.c | 273 ++++++++
modules/rostests/win32/user32/biditext/biditext.h | 34 +
modules/rostests/win32/user32/biditext/biditext.rc | 69 +++
win32ss/gdi/gdi32/include/gdi32p.h | 37 ++
win32ss/gdi/gdi32/objects/font.c | 27 +-
win32ss/gdi/gdi32/objects/text.c | 6 +
win32ss/gdi/gdi32/objects/utils.c | 53 ++
19 files changed, 1434 insertions(+), 86 deletions(-)
diff --git a/dll/win32/lpk/CMakeLists.txt b/dll/win32/lpk/CMakeLists.txt
index 4bd68b3486..858e468bc4 100644
--- a/dll/win32/lpk/CMakeLists.txt
+++ b/dll/win32/lpk/CMakeLists.txt
@@ -1,10 +1,11 @@
add_definitions(-DLANGPACK)
include_directories(include)
-spec2def(lpk.dll lpk.spec)
+spec2def(lpk.dll lpk.spec ADD_IMPORTLIB)
list(APPEND SOURCE
- dllmain.c
+ bidi.c
+ lpk.c
stub.c
ros_lpk.h)
@@ -14,6 +15,9 @@ add_library(lpk SHARED
${CMAKE_CURRENT_BINARY_DIR}/lpk.def)
set_module_type(lpk win32dll UNICODE)
-add_importlibs(lpk user32 usp10 msvcrt kernel32 ntdll)
+
+target_link_libraries(lpk wine)
+
+add_importlibs(lpk usp10 user32 gdi32 msvcrt kernel32 ntdll)
add_pch(lpk ros_lpk.h SOURCE)
add_cd_file(TARGET lpk DESTINATION reactos/system32 FOR all)
diff --git a/dll/win32/lpk/bidi.c b/dll/win32/lpk/bidi.c
new file mode 100644
index 0000000000..a380e1f2ed
--- /dev/null
+++ b/dll/win32/lpk/bidi.c
@@ -0,0 +1,687 @@
+/*
+ * GDI BiDirectional handling
+ *
+ * Copyright 2003 Shachar Shemesh
+ * Copyright 2007 Maarten Lankhorst
+ * Copyright 2010 CodeWeavers, Aric Stewart
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Code derived from the modified reference implementation
+ * that was found in revision 17 of
http://unicode.org/reports/tr9/
+ * "Unicode Standard Annex #9: THE BIDIRECTIONAL ALGORITHM"
+ *
+ * -- Copyright (C) 1999-2005, ASMUS, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the
+ * "Data Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software,
+ * and to permit persons to whom the Data Files or Software are furnished
+ * to do so, provided that (a) the above copyright notice(s) and this
+ * permission notice appear with all copies of the Data Files or Software,
+ * (b) both the above copyright notice(s) and this permission notice appear
+ * in associated documentation, and (c) there is clear notice in each
+ * modified Data File or in the Software as well as in the documentation
+ * associated with the Data File(s) or Software that the data or software
+ * has been modified.
+ */
+
+#include "ros_lpk.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+//#include "config.h"
+//#include "gdi_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(bidi);
+
+/* HELPER FUNCTIONS AND DECLARATIONS */
+
+#define odd(x) ((x) & 1)
+
+/*------------------------------------------------------------------------
+ Bidirectional Character Types
+
+ as defined by the Unicode Bidirectional Algorithm Table 3-7.
+
+ Note:
+
+ The list of bidirectional character types here is not grouped the
+ same way as the table 3-7, since the numberic values for the types
+ are chosen to keep the state and action tables compact.
+------------------------------------------------------------------------*/
+enum directions
+{
+ /* input types */
+ /* ON MUST be zero, code relies on ON = N = 0 */
+ ON = 0, /* Other Neutral */
+ L, /* Left Letter */
+ R, /* Right Letter */
+ AN, /* Arabic Number */
+ EN, /* European Number */
+ AL, /* Arabic Letter (Right-to-left) */
+ NSM, /* Non-spacing Mark */
+ CS, /* Common Separator */
+ ES, /* European Separator */
+ ET, /* European Terminator (post/prefix e.g. $ and %) */
+
+ /* resolved types */
+ BN, /* Boundary neutral (type of RLE etc after explicit levels) */
+
+ /* input types, */
+ S, /* Segment Separator (TAB) // used only in L1 */
+ WS, /* White space // used only in L1 */
+ B, /* Paragraph Separator (aka as PS) */
+
+ /* types for explicit controls */
+ RLO, /* these are used only in X1-X9 */
+ RLE,
+ LRO,
+ LRE,
+ PDF,
+
+ /* resolved types, also resolved directions */
+ N = ON, /* alias, where ON, WS and S are treated the same */
+};
+
+/* HELPER FUNCTIONS */
+
+/* Convert the libwine information to the direction enum */
+static void classify(LPCWSTR lpString, WORD *chartype, DWORD uCount)
+{
+ static const enum directions dir_map[16] =
+ {
+ L, /* unassigned defaults to L */
+ L,
+ R,
+ EN,
+ ES,
+ ET,
+ AN,
+ CS,
+ B,
+ S,
+ WS,
+ ON,
+ AL,
+ NSM,
+ BN,
+ PDF /* also LRE, LRO, RLE, RLO */
+ };
+
+ unsigned i;
+
+ for (i = 0; i < uCount; ++i)
+ {
+ chartype[i] = dir_map[get_char_typeW(lpString[i]) >> 12];
+ if (chartype[i] == PDF)
+ {
+ switch (lpString[i])
+ {
+ case 0x202A: chartype[i] = LRE; break;
+ case 0x202B: chartype[i] = RLE; break;
+ case 0x202C: chartype[i] = PDF; break;
+ case 0x202D: chartype[i] = LRO; break;
+ case 0x202E: chartype[i] = RLO; break;
+ }
+ }
+ }
+}
+
+/* Set a run of cval values at locations all prior to, but not including */
+/* iStart, to the new value nval. */
+static void SetDeferredRun(BYTE *pval, int cval, int iStart, int nval)
+{
+ int i = iStart - 1;
+ for (; i >= iStart - cval; i--)
+ {
+ pval[i] = nval;
+ }
+}
+
+/* THE PARAGRAPH LEVEL */
+
+/*------------------------------------------------------------------------
+ Function: resolveParagraphs
+
+ Resolves the input strings into blocks over which the algorithm
+ is then applied.
+
+ Implements Rule P1 of the Unicode Bidi Algorithm
+
+ Input: Text string
+ Character count
+
+ Output: revised character count
+
+ Note: This is a very simplistic function. In effect it restricts
+ the action of the algorithm to the first paragraph in the input
+ where a paragraph ends at the end of the first block separator
+ or at the end of the input text.
+
+------------------------------------------------------------------------*/
+
+static int resolveParagraphs(WORD *types, int cch)
+{
+ /* skip characters not of type B */
+ int ich = 0;
+ for(; ich < cch && types[ich] != B; ich++);
+ /* stop after first B, make it a BN for use in the next steps */
+ if (ich < cch && types[ich] == B)
+ types[ich++] = BN;
+ return ich;
+}
+
+/* REORDER */
+/*------------------------------------------------------------------------
+ Function: resolveLines
+
+ Breaks a paragraph into lines
+
+ Input: Array of line break flags
+ Character count
+ In/Out: Array of characters
+
+ Returns the count of characters on the first line
+
+ Note: This function only breaks lines at hard line breaks. Other
+ line breaks can be passed in. If pbrk[n] is TRUE, then a break
+ occurs after the character in pszInput[n]. Breaks before the first
+ character are not allowed.
+------------------------------------------------------------------------*/
+static int resolveLines(LPCWSTR pszInput, const BOOL * pbrk, int cch)
+{
+ /* skip characters not of type LS */
+ int ich = 0;
+ for(; ich < cch; ich++)
+ {
+ if (pszInput[ich] == (WCHAR)'\n' || (pbrk && pbrk[ich]))
+ {
+ ich++;
+ break;
+ }
+ }
+
+ return ich;
+}
+
+/*------------------------------------------------------------------------
+ Function: resolveWhiteSpace
+
+ Resolves levels for WS and S
+ Implements rule L1 of the Unicode bidi Algorithm.
+
+ Input: Base embedding level
+ Character count
+ Array of direction classes (for one line of text)
+
+ In/Out: Array of embedding levels (for one line of text)
+
+ Note: this should be applied a line at a time. The default driver
+ code supplied in this file assumes a single line of text; for
+ a real implementation, cch and the initial pointer values
+ would have to be adjusted.
+------------------------------------------------------------------------*/
+static void resolveWhitespace(int baselevel, const WORD *pcls, BYTE *plevel, int cch)
+{
+ int cchrun = 0;
+ BYTE oldlevel = baselevel;
+
+ int ich = 0;
+ for (; ich < cch; ich++)
+ {
+ switch(pcls[ich])
+ {
+ default:
+ cchrun = 0; /* any other character breaks the run */
+ break;
+ case WS:
+ cchrun++;
+ break;
+
+ case RLE:
+ case LRE:
+ case LRO:
+ case RLO:
+ case PDF:
+ case BN:
+ plevel[ich] = oldlevel;
+ cchrun++;
+ break;
+
+ case S:
+ case B:
+ /* reset levels for WS before eot */
+ SetDeferredRun(plevel, cchrun, ich, baselevel);
+ cchrun = 0;
+ plevel[ich] = baselevel;
+ break;
+ }
+ oldlevel = plevel[ich];
+ }
+ /* reset level before eot */
+ SetDeferredRun(plevel, cchrun, ich, baselevel);
+}
+
+/*------------------------------------------------------------------------
+ Function: BidiLines
+
+ Implements the Line-by-Line phases of the Unicode Bidi Algorithm
+
+ Input: Count of characters
+ Array of character directions
+
+ Inp/Out: Input text
+ Array of levels
+
+------------------------------------------------------------------------*/
+static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD *
pclsLine,
+ BYTE * plevelLine, int cchPara, const BOOL * pbrk)
+{
+ int cchLine = 0;
+ int done = 0;
+ int *run;
+
+ run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int));
+ if (!run)
+ {
+ WARN("Out of memory\n");
+ return;
+ }
+
+ do
+ {
+ /* break lines at LS */
+ cchLine = resolveLines(pszLine, pbrk, cchPara);
+
+ /* resolve whitespace */
+ resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine);
+
+ if (pszOutLine)
+ {
+ int i;
+ /* reorder each line in place */
+ ScriptLayout(cchLine, plevelLine, NULL, run);
+ for (i = 0; i < cchLine; i++)
+ pszOutLine[done+run[i]] = pszLine[i];
+ }
+
+ pszLine += cchLine;
+ plevelLine += cchLine;
+ pbrk += pbrk ? cchLine : 0;
+ pclsLine += cchLine;
+ cchPara -= cchLine;
+ done += cchLine;
+
+ } while (cchPara);
+
+ HeapFree(GetProcessHeap(), 0, run);
+}
+
+/*************************************************************
+ * BIDI_Reorder
+ *
+ * Returns TRUE if reordering was required and done.
+ */
+BOOL BIDI_Reorder(
+ HDC hDC, /*[in] Display DC */
+ LPCWSTR lpString, /* [in] The string for which information is to be
returned */
+ INT uCount, /* [in] Number of WCHARs in string. */
+ DWORD dwFlags, /* [in] GetCharacterPlacement compatible flags specifying
how to process the string */
+ DWORD dwWineGCP_Flags, /* [in] Wine internal flags - Force
paragraph direction */
+ LPWSTR lpOutString, /* [out] Reordered string */
+ INT uCountOut, /* [in] Size of output buffer */
+ UINT *lpOrder, /* [out] Logical -> Visual order map */
+ WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display
*/
+ INT *cGlyphs /* [out] number of glyphs generated */
+ )
+{
+ WORD *chartype;
+ BYTE *levels;
+ INT i, done;
+ unsigned glyph_i;
+ BOOL is_complex;
+
+ int maxItems;
+ int nItems;
+ SCRIPT_CONTROL Control;
+ SCRIPT_STATE State;
+ SCRIPT_ITEM *pItems;
+ HRESULT res;
+ SCRIPT_CACHE psc = NULL;
+ WORD *run_glyphs = NULL;
+ WORD *pwLogClust = NULL;
+ SCRIPT_VISATTR *psva = NULL;
+ DWORD cMaxGlyphs = 0;
+ BOOL doGlyphs = TRUE;
+
+ TRACE("%s, %d, 0x%08x lpOutString=%p, lpOrder=%p\n",
+ debugstr_wn(lpString, uCount), uCount, dwFlags,
+ lpOutString, lpOrder);
+
+ memset(&Control, 0, sizeof(Control));
+ memset(&State, 0, sizeof(State));
+ if (lpGlyphs)
+ *lpGlyphs = NULL;
+
+ if (!(dwFlags & GCP_REORDER))
+ {
+ FIXME("Asked to reorder without reorder flag set\n");
+ return FALSE;
+ }
+
+ if (lpOutString && uCountOut < uCount)
+ {
+ FIXME("lpOutString too small\n");
+ return FALSE;
+ }
+
+ chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
+ if (!chartype)
+ {
+ WARN("Out of memory\n");
+ return FALSE;
+ }
+
+ if (lpOutString)
+ memcpy(lpOutString, lpString, uCount * sizeof(WCHAR));
+
+ is_complex = FALSE;
+ for (i = 0; i < uCount && !is_complex; i++)
+ {
+ if ((lpString[i] >= 0x900 && lpString[i] <= 0xfff) ||
+ (lpString[i] >= 0x1cd0 && lpString[i] <= 0x1cff) ||
+ (lpString[i] >= 0xa840 && lpString[i] <= 0xa8ff))
+ is_complex = TRUE;
+ }
+
+ /* Verify reordering will be required */
+ if ((WINE_GCPW_FORCE_RTL == (dwWineGCP_Flags&WINE_GCPW_DIR_MASK)) ||
+ ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL))
+ State.uBidiLevel = 1;
+ else if (!is_complex)
+ {
+ done = 1;
+ classify(lpString, chartype, uCount);
+ for (i = 0; i < uCount; i++)
+ switch (chartype[i])
+ {
+ case R:
+ case AL:
+ case RLE:
+ case RLO:
+ done = 0;
+ break;
+ }
+ if (done)
+ {
+ HeapFree(GetProcessHeap(), 0, chartype);
+ if (lpOrder)
+ {
+ for (i = 0; i < uCount; i++)
+ lpOrder[i] = i;
+ }
+ return TRUE;
+ }
+ }
+
+ levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
+ if (!levels)
+ {
+ WARN("Out of memory\n");
+ HeapFree(GetProcessHeap(), 0, chartype);
+ return FALSE;
+ }
+
+ maxItems = 5;
+ pItems = HeapAlloc(GetProcessHeap(),0, maxItems * sizeof(SCRIPT_ITEM));
+ if (!pItems)
+ {
+ WARN("Out of memory\n");
+ HeapFree(GetProcessHeap(), 0, chartype);
+ HeapFree(GetProcessHeap(), 0, levels);
+ return FALSE;
+ }
+
+ if (lpGlyphs)
+ {
+ cMaxGlyphs = 1.5 * uCount + 16;
+ run_glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * cMaxGlyphs);
+ if (!run_glyphs)
+ {
+ WARN("Out of memory\n");
+ HeapFree(GetProcessHeap(), 0, chartype);
+ HeapFree(GetProcessHeap(), 0, levels);
+ HeapFree(GetProcessHeap(), 0, pItems);
+ return FALSE;
+ }
+ pwLogClust = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * uCount);
+ if (!pwLogClust)
+ {
+ WARN("Out of memory\n");
+ HeapFree(GetProcessHeap(), 0, chartype);
+ HeapFree(GetProcessHeap(), 0, levels);
+ HeapFree(GetProcessHeap(), 0, pItems);
+ HeapFree(GetProcessHeap(), 0, run_glyphs);
+ return FALSE;
+ }
+ psva = HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR) * uCount);
+ if (!psva)
+ {
+ WARN("Out of memory\n");
+ HeapFree(GetProcessHeap(), 0, chartype);
+ HeapFree(GetProcessHeap(), 0, levels);
+ HeapFree(GetProcessHeap(), 0, pItems);
+ HeapFree(GetProcessHeap(), 0, run_glyphs);
+ HeapFree(GetProcessHeap(), 0, pwLogClust);
+ return FALSE;
+ }
+ }
+
+ done = 0;
+ glyph_i = 0;
+ while (done < uCount)
+ {
+ INT j;
+ classify(lpString + done, chartype, uCount - done);
+ /* limit text to first block */
+ i = resolveParagraphs(chartype, uCount - done);
+ for (j = 0; j < i; ++j)
+ switch(chartype[j])
+ {
+ case B:
+ case S:
+ case WS:
+ case ON: chartype[j] = N;
+ default: continue;
+ }
+
+ if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL)
+ State.uBidiLevel = 1;
+ else if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_LTR)
+ State.uBidiLevel = 0;
+
+ if (dwWineGCP_Flags & WINE_GCPW_LOOSE_MASK)
+ {
+ for (j = 0; j < i; ++j)
+ if (chartype[j] == L)
+ {
+ State.uBidiLevel = 0;
+ break;
+ }
+ else if (chartype[j] == R || chartype[j] == AL)
+ {
+ State.uBidiLevel = 1;
+ break;
+ }
+ }
+
+ res = ScriptItemize(lpString + done, i, maxItems, &Control, &State,
pItems, &nItems);
+ while (res == E_OUTOFMEMORY)
+ {
+ maxItems = maxItems * 2;
+ pItems = HeapReAlloc(GetProcessHeap(), 0, pItems, sizeof(SCRIPT_ITEM) *
maxItems);
+ if (!pItems)
+ {
+ WARN("Out of memory\n");
+ HeapFree(GetProcessHeap(), 0, chartype);
+ HeapFree(GetProcessHeap(), 0, levels);
+ HeapFree(GetProcessHeap(), 0, run_glyphs);
+ HeapFree(GetProcessHeap(), 0, pwLogClust);
+ HeapFree(GetProcessHeap(), 0, psva);
+ return FALSE;
+ }
+ res = ScriptItemize(lpString + done, i, maxItems, &Control, &State,
pItems, &nItems);
+ }
+
+ if (lpOutString || lpOrder)
+ for (j = 0; j < nItems; j++)
+ {
+ int k;
+ for (k = pItems[j].iCharPos; k < pItems[j+1].iCharPos; k++)
+ levels[k] = pItems[j].a.s.uBidiLevel;
+ }
+
+ if (lpOutString)
+ {
+ /* assign directional types again, but for WS, S this time */
+ classify(lpString + done, chartype, i);
+
+ BidiLines(State.uBidiLevel, lpOutString + done, lpString + done,
+ chartype, levels, i, 0);
+ }
+
+ if (lpOrder)
+ {
+ int k, lastgood;
+ for (j = lastgood = 0; j < i; ++j)
+ if (levels[j] != levels[lastgood])
+ {
+ --j;
+ if (odd(levels[lastgood]))
+ for (k = j; k >= lastgood; --k)
+ lpOrder[done + k] = done + j - k;
+ else
+ for (k = lastgood; k <= j; ++k)
+ lpOrder[done + k] = done + k;
+ lastgood = ++j;
+ }
+ if (odd(levels[lastgood]))
+ for (k = j - 1; k >= lastgood; --k)
+ lpOrder[done + k] = done + j - 1 - k;
+ else
+ for (k = lastgood; k < j; ++k)
+ lpOrder[done + k] = done + k;
+ }
+
+ if (lpGlyphs && doGlyphs)
+ {
+ BYTE *runOrder;
+ int *visOrder;
+ SCRIPT_ITEM *curItem;
+
+ runOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*runOrder));
+ visOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*visOrder));
+ if (!runOrder || !visOrder)
+ {
+ WARN("Out of memory\n");
+ HeapFree(GetProcessHeap(), 0, runOrder);
+ HeapFree(GetProcessHeap(), 0, visOrder);
+ HeapFree(GetProcessHeap(), 0, chartype);
+ HeapFree(GetProcessHeap(), 0, levels);
+ HeapFree(GetProcessHeap(), 0, pItems);
+ HeapFree(GetProcessHeap(), 0, psva);
+ HeapFree(GetProcessHeap(), 0, pwLogClust);
+ return FALSE;
+ }
+
+ for (j = 0; j < nItems; j++)
+ runOrder[j] = pItems[j].a.s.uBidiLevel;
+
+ ScriptLayout(nItems, runOrder, visOrder, NULL);
+
+ for (j = 0; j < nItems; j++)
+ {
+ int k;
+ int cChars,cOutGlyphs;
+ curItem = &pItems[visOrder[j]];
+
+ cChars = pItems[visOrder[j]+1].iCharPos - curItem->iCharPos;
+
+ res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos,
cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
+ while (res == E_OUTOFMEMORY)
+ {
+ cMaxGlyphs *= 2;
+ run_glyphs = HeapReAlloc(GetProcessHeap(), 0, run_glyphs,
sizeof(WORD) * cMaxGlyphs);
+ if (!run_glyphs)
+ {
+ WARN("Out of memory\n");
+ HeapFree(GetProcessHeap(), 0, runOrder);
+ HeapFree(GetProcessHeap(), 0, visOrder);
+ HeapFree(GetProcessHeap(), 0, chartype);
+ HeapFree(GetProcessHeap(), 0, levels);
+ HeapFree(GetProcessHeap(), 0, pItems);
+ HeapFree(GetProcessHeap(), 0, psva);
+ HeapFree(GetProcessHeap(), 0, pwLogClust);
+ HeapFree(GetProcessHeap(), 0, *lpGlyphs);
+ ScriptFreeCache(&psc);
+ *lpGlyphs = NULL;
+ return FALSE;
+ }
+ res = ScriptShape(hDC, &psc, lpString + done +
curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust,
psva, &cOutGlyphs);
+ }
+ if (res)
+ {
+ if (res == USP_E_SCRIPT_NOT_IN_FONT)
+ TRACE("Unable to shape with currently selected
font\n");
+ else
+ FIXME("Unable to shape string (%x)\n",res);
+ j = nItems;
+ doGlyphs = FALSE;
+ HeapFree(GetProcessHeap(), 0, *lpGlyphs);
+ *lpGlyphs = NULL;
+ }
+ else
+ {
+ if (*lpGlyphs)
+ *lpGlyphs = HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs,
sizeof(WORD) * (glyph_i + cOutGlyphs));
+ else
+ *lpGlyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(WORD) *
(glyph_i + cOutGlyphs));
+ for (k = 0; k < cOutGlyphs; k++)
+ (*lpGlyphs)[glyph_i+k] = run_glyphs[k];
+ glyph_i += cOutGlyphs;
+ }
+ }
+ HeapFree(GetProcessHeap(), 0, runOrder);
+ HeapFree(GetProcessHeap(), 0, visOrder);
+ }
+
+ done += i;
+ }
+ if (cGlyphs)
+ *cGlyphs = glyph_i;
+
+ HeapFree(GetProcessHeap(), 0, chartype);
+ HeapFree(GetProcessHeap(), 0, levels);
+ HeapFree(GetProcessHeap(), 0, pItems);
+ HeapFree(GetProcessHeap(), 0, run_glyphs);
+ HeapFree(GetProcessHeap(), 0, pwLogClust);
+ HeapFree(GetProcessHeap(), 0, psva);
+ ScriptFreeCache(&psc);
+ return TRUE;
+}
diff --git a/dll/win32/lpk/dllmain.c b/dll/win32/lpk/dllmain.c
deleted file mode 100644
index f54beb950b..0000000000
--- a/dll/win32/lpk/dllmain.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS
- * PURPOSE: LPK Library
- * PROGRAMMER: Magnus Olsen (greatlrd)
- *
- */
-
-#include "ros_lpk.h"
-
-LPK_LPEDITCONTROL_LIST LpkEditControl = {EditCreate, EditIchToXY, EditMouseToIch,
EditCchInWidth,
- EditGetLineWidth, EditDrawText, EditHScroll,
EditMoveSelection,
- EditVerifyText, EditNextWord, EditSetMenu,
EditProcessMenu,
- EditCreateCaret, EditAdjustCaret};
-
-BOOL
-WINAPI
-DllMain (
- HANDLE hDll,
- DWORD dwReason,
- LPVOID lpReserved)
-{
-
- return LpkDllInitialize(hDll,dwReason,lpReserved);
-}
-
-BOOL
-WINAPI
-LpkDllInitialize (
- HANDLE hDll,
- DWORD dwReason,
- LPVOID lpReserved)
-{
- switch(dwReason)
- {
- case DLL_PROCESS_ATTACH:
- DisableThreadLibraryCalls(hDll);
- /* Tell usp10 it is activated usp10 */
- //LpkPresent();
- break;
-
- default:
- break;
- }
-
- return TRUE;
-}
diff --git a/dll/win32/lpk/lpk.c b/dll/win32/lpk/lpk.c
new file mode 100644
index 0000000000..fde181cc52
--- /dev/null
+++ b/dll/win32/lpk/lpk.c
@@ -0,0 +1,181 @@
+/*
+ * PROJECT: ReactOS LPK
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: Language Pack DLL.
+ * PROGRAMMERS: Magnus Olsen (greatlrd)
+ * Baruch Rutman (peterooch at gmail dot com)
+ */
+
+#include "ros_lpk.h"
+
+LPK_LPEDITCONTROL_LIST LpkEditControl = {EditCreate, EditIchToXY, EditMouseToIch,
EditCchInWidth,
+ EditGetLineWidth, EditDrawText, EditHScroll,
EditMoveSelection,
+ EditVerifyText, EditNextWord, EditSetMenu,
EditProcessMenu,
+ EditCreateCaret, EditAdjustCaret};
+
+BOOL
+WINAPI
+DllMain(
+ HANDLE hDll,
+ DWORD dwReason,
+ LPVOID lpReserved)
+{
+
+ return LpkDllInitialize(hDll,dwReason,lpReserved);
+}
+
+BOOL
+WINAPI
+LpkDllInitialize(
+ HANDLE hDll,
+ DWORD dwReason,
+ LPVOID lpReserved)
+{
+ switch(dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hDll);
+ /* Tell usp10 it is activated usp10 */
+ //LpkPresent();
+ break;
+
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+LpkExtTextOut(
+ HDC hdc,
+ int x,
+ int y,
+ UINT fuOptions,
+ const RECT *lprc,
+ LPCWSTR lpString,
+ UINT uCount,
+ const INT *lpDx,
+ INT unknown)
+{
+ LPWORD glyphs = NULL;
+ INT cGlyphs;
+
+ UNREFERENCED_PARAMETER(unknown);
+
+ if (!(fuOptions & ETO_IGNORELANGUAGE))
+ fuOptions |= ETO_IGNORELANGUAGE;
+
+ /* Check text direction */
+ if ((GetLayout(hdc) & LAYOUT_RTL) || (GetTextAlign(hdc) & TA_RTLREADING))
+ {
+ if (!(fuOptions & ETO_RTLREADING))
+ fuOptions |= ETO_RTLREADING;
+ }
+
+ /* Check if the string requires complex script processing and not a "glyph
indices" array */
+ if (ScriptIsComplex(lpString, uCount, SIC_COMPLEX) == S_OK && !(fuOptions
& ETO_GLYPH_INDEX))
+ {
+ BIDI_Reorder(hdc, lpString, uCount, GCP_REORDER,
+ (fuOptions & ETO_RTLREADING) ? WINE_GCPW_FORCE_RTL :
WINE_GCPW_FORCE_LTR,
+ NULL, uCount, NULL, &glyphs, &cGlyphs);
+
+ fuOptions |= ETO_GLYPH_INDEX;
+
+ if (uCount > cGlyphs)
+ cGlyphs = uCount;
+
+ return ExtTextOutW(hdc, x, y, fuOptions, lprc, (LPWSTR)glyphs, cGlyphs, lpDx);
+ }
+
+ return ExtTextOutW(hdc, x, y, fuOptions, lprc, lpString, uCount, lpDx);
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+LpkGetCharacterPlacement(
+ HDC hdc,
+ LPCWSTR lpString,
+ INT uCount,
+ INT nMaxExtent,
+ LPGCP_RESULTSW lpResults,
+ DWORD dwFlags,
+ DWORD dwUnused)
+{
+ LPWORD lpGlyphs = NULL;
+ SIZE size;
+ DWORD ret = 0;
+ UINT nSet, i;
+ INT cGlyphs;
+
+ UNREFERENCED_PARAMETER(dwUnused);
+
+ /* Sanity check (most likely a direct call) */
+ if (!(dwFlags & GCP_REORDER))
+ return GetCharacterPlacementW(hdc, lpString, uCount, nMaxExtent, lpResults,
dwFlags);
+
+ nSet = (UINT)uCount;
+ if (nSet > lpResults->nGlyphs)
+ nSet = lpResults->nGlyphs;
+
+ BIDI_Reorder(hdc, lpString, uCount, dwFlags, WINE_GCPW_FORCE_LTR,
lpResults->lpOutString,
+ nSet, lpResults->lpOrder, &lpGlyphs, &cGlyphs);
+
+ lpResults->nGlyphs = (UINT)cGlyphs;
+
+ if (lpResults->lpGlyphs)
+ wcscpy(lpResults->lpGlyphs, lpGlyphs);
+
+ if (lpResults->lpDx && !(dwFlags & GCP_GLYPHSHAPE))
+ {
+ int c;
+ for (i = 0; i < nSet; i++)
+ {
+ if (GetCharWidth32W(hdc, lpResults->lpOutString[i],
lpResults->lpOutString[i], &c))
+ lpResults->lpDx[i] = c;
+ }
+ }
+
+ /* If glyph shaping was requested */
+ else if (lpResults->lpDx && (dwFlags & GCP_GLYPHSHAPE))
+ {
+ int c;
+
+ if (lpResults->lpGlyphs)
+ {
+ for (i = 0; i < lpResults->nGlyphs; i++)
+ {
+ if (GetCharWidth32W(hdc, lpGlyphs[i], lpGlyphs[i], &c))
+ lpResults->lpDx[i] = c;
+ }
+ }
+ }
+
+ /* FIXME: Currently not bidi compliant! */
+ if (lpResults->lpCaretPos)
+ {
+ int pos = 0;
+
+ lpResults->lpCaretPos[0] = 0;
+ for (i = 1; i < nSet; i++)
+ {
+ if (GetTextExtentPoint32W(hdc, &(lpString[i - 1]), 1, &size))
+ lpResults->lpCaretPos[i] = (pos += size.cx);
+ }
+ }
+
+ if (GetTextExtentPoint32W(hdc, lpString, uCount, &size))
+ ret = MAKELONG(size.cx, size.cy);
+
+ HeapFree(GetProcessHeap(), 0, lpGlyphs);
+
+ return ret;
+}
diff --git a/dll/win32/lpk/lpk.spec b/dll/win32/lpk/lpk.spec
index 01ed057bf6..910d3dc65d 100644
--- a/dll/win32/lpk/lpk.spec
+++ b/dll/win32/lpk/lpk.spec
@@ -3,8 +3,8 @@
@ stdcall LpkDllInitialize(ptr long ptr)
@ stdcall LpkDrawTextEx(long long long long long long long long long long)
@ extern LpkEditControl
-@ stdcall LpkExtTextOut(long long long long long long long long long)
-@ stdcall LpkGetCharacterPlacement(long long long long long long long)
+@ stdcall LpkExtTextOut(long long long long ptr wstr long ptr long)
+@ stdcall LpkGetCharacterPlacement(long wstr long long ptr long long)
@ stdcall LpkGetTextExtentExPoint(long long long long long long long long long)
@ stdcall LpkPSMTextOut(long long long long long long)
@ stdcall LpkUseGDIWidthCache(long long long long long)
diff --git a/dll/win32/lpk/ros_lpk.h b/dll/win32/lpk/ros_lpk.h
index 5c5a4c88a3..d82a4498bc 100644
--- a/dll/win32/lpk/ros_lpk.h
+++ b/dll/win32/lpk/ros_lpk.h
@@ -14,6 +14,9 @@
#define WIN32_NO_STATUS
#include <windef.h>
#include <winbase.h>
+#include <wingdi.h>
+#include <winnls.h>
+#include <usp10.h>
/* FIXME USP10 api that does not have prototype in any include file */
VOID WINAPI LpkPresent(VOID);
@@ -61,11 +64,38 @@ DWORD WINAPI LpkInitialize(DWORD x1);
DWORD WINAPI LpkTabbedTextOut(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5,DWORD x6,DWORD
x7,DWORD x8,DWORD x9,DWORD x10,DWORD x11,DWORD x12);
BOOL WINAPI LpkDllInitialize (HANDLE hDll, DWORD dwReason, LPVOID lpReserved);
DWORD WINAPI LpkDrawTextEx(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5,DWORD x6,DWORD
x7,DWORD x8,DWORD x9, DWORD x10);
-DWORD WINAPI LpkExtTextOut(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5,DWORD x6,DWORD
x7,DWORD x8,DWORD x9);
-DWORD WINAPI LpkGetCharacterPlacement(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5,DWORD
x6, DWORD x7);
DWORD WINAPI LpkGetTextExtentExPoint(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5,DWORD
x6,DWORD x7,DWORD x8,DWORD x9);
DWORD WINAPI LpkPSMTextOut(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5,DWORD x6);
DWORD WINAPI LpkUseGDIWidthCache(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5);
DWORD WINAPI ftsWordBreak(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5);
+/* Implemented */
+
+BOOL WINAPI LpkExtTextOut(HDC hdc, int x, int y, UINT fuOptions, const RECT *lprc,
+ LPCWSTR lpString, UINT uCount , const INT *lpDx, INT unknown);
+
+DWORD WINAPI LpkGetCharacterPlacement(HDC hdc, LPCWSTR lpString, INT uCount, INT
nMaxExtent,
+ GCP_RESULTSW *lpResults, DWORD dwFlags, DWORD
dwUnused);
+/* bidi.c */
+
+#define WINE_GCPW_FORCE_LTR 0
+#define WINE_GCPW_FORCE_RTL 1
+#define WINE_GCPW_LOOSE_LTR 2
+#define WINE_GCPW_LOOSE_RTL 3
+#define WINE_GCPW_DIR_MASK 3
+#define WINE_GCPW_LOOSE_MASK 2
+
+BOOL BIDI_Reorder(
+ _In_ HDC hDC, /* [in] Display DC */
+ _In_ LPCWSTR lpString, /* [in] The string for which information is to be
returned */
+ _In_ INT uCount, /* [in] Number of WCHARs in string. */
+ _In_ DWORD dwFlags, /* [in] GetCharacterPlacement compatible flags
specifying how to process the string */
+ _In_ DWORD dwWineGCP_Flags, /* [in] Wine internal flags - Force paragraph direction
*/
+ _Out_ LPWSTR lpOutString, /* [out] Reordered string */
+ _In_ INT uCountOut, /* [in] Size of output buffer */
+ _Out_ UINT *lpOrder, /* [out] Logical -> Visual order map */
+ _Out_ WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display
*/
+ _Out_ INT *cGlyphs /* [out] number of glyphs generated */
+ );
+
#endif /* _LPK_H */
diff --git a/dll/win32/lpk/stub.c b/dll/win32/lpk/stub.c
index 37072f05b7..51d8eda0e4 100644
--- a/dll/win32/lpk/stub.c
+++ b/dll/win32/lpk/stub.c
@@ -39,24 +39,6 @@ DWORD WINAPI LpkDrawTextEx(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD
x5,DWORD x6
return 0;
}
-/*
- * @unimplemented
- */
-DWORD WINAPI LpkExtTextOut(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5,DWORD x6,DWORD
x7,DWORD x8,DWORD x9)
-{
- UNIMPLEMENTED
- return 0;
-}
-
-/*
- * @unimplemented
- */
-DWORD WINAPI LpkGetCharacterPlacement(DWORD x1,DWORD x2,DWORD x3,DWORD x4,DWORD x5,DWORD
x6, DWORD x7)
-{
- UNIMPLEMENTED
- return 0;
-}
-
/*
* @unimplemented
*/
diff --git a/media/doc/README.WINE b/media/doc/README.WINE
index 632e0ed069..b75500483f 100644
--- a/media/doc/README.WINE
+++ b/media/doc/README.WINE
@@ -286,6 +286,9 @@ kernel32 -
reactos/dll/win32/kernel32/winnls/string/nls.c # Synced in r52754
reactos/dll/win32/kernel32/winnls/string/sortkey.c # Synced to WineStaging-3.3
+lpk -
+ reactos/dll/win32/lpk/bidi.c # Synced to WineStaging-3.7
+
msvcrt -
reactos/sdk/lib/crt/conio/cputs.c # Synced to WineStaging-1.9.16
reactos/sdk/lib/crt/except/cpp.c # Synced at 20080528
diff --git a/modules/rostests/win32/user32/CMakeLists.txt
b/modules/rostests/win32/user32/CMakeLists.txt
index b61af83ece..8298a919d3 100644
--- a/modules/rostests/win32/user32/CMakeLists.txt
+++ b/modules/rostests/win32/user32/CMakeLists.txt
@@ -1,2 +1,3 @@
+add_subdirectory(biditext)
add_subdirectory(paintdesktop)
add_subdirectory(sysicon)
diff --git a/modules/rostests/win32/user32/biditext/Application.ico
b/modules/rostests/win32/user32/biditext/Application.ico
new file mode 100644
index 0000000000..1073447c20
Binary files /dev/null and b/modules/rostests/win32/user32/biditext/Application.ico
differ
diff --git a/modules/rostests/win32/user32/biditext/Application.manifest
b/modules/rostests/win32/user32/biditext/Application.manifest
new file mode 100644
index 0000000000..76090d4133
--- /dev/null
+++ b/modules/rostests/win32/user32/biditext/Application.manifest
@@ -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>
diff --git a/modules/rostests/win32/user32/biditext/CMakeLists.txt
b/modules/rostests/win32/user32/biditext/CMakeLists.txt
new file mode 100644
index 0000000000..042ab63b43
--- /dev/null
+++ b/modules/rostests/win32/user32/biditext/CMakeLists.txt
@@ -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)
diff --git a/modules/rostests/win32/user32/biditext/biditext.c
b/modules/rostests/win32/user32/biditext/biditext.c
new file mode 100644
index 0000000000..cc714c40f7
--- /dev/null
+++ b/modules/rostests/win32/user32/biditext/biditext.c
@@ -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;
+}
diff --git a/modules/rostests/win32/user32/biditext/biditext.h
b/modules/rostests/win32/user32/biditext/biditext.h
new file mode 100644
index 0000000000..433223776d
--- /dev/null
+++ b/modules/rostests/win32/user32/biditext/biditext.h
@@ -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
diff --git a/modules/rostests/win32/user32/biditext/biditext.rc
b/modules/rostests/win32/user32/biditext/biditext.rc
new file mode 100644
index 0000000000..cc2a2632f8
--- /dev/null
+++ b/modules/rostests/win32/user32/biditext/biditext.rc
@@ -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
diff --git a/win32ss/gdi/gdi32/include/gdi32p.h b/win32ss/gdi/gdi32/include/gdi32p.h
index dddbfd74b3..1b7c51856d 100644
--- a/win32ss/gdi/gdi32/include/gdi32p.h
+++ b/win32ss/gdi/gdi32/include/gdi32p.h
@@ -27,6 +27,34 @@ typedef INT
HANDLE hPageQuery
);
+typedef BOOL
+(WINAPI* LPKETO)(
+ HDC hdc,
+ int x,
+ int y,
+ UINT fuOptions,
+ const RECT *lprc,
+ LPCWSTR lpString,
+ UINT uCount,
+ const INT *lpDx,
+ INT unknown
+);
+
+typedef DWORD
+(WINAPI* LPKGCP)(
+ HDC hdc,
+ LPCWSTR lpString,
+ INT uCount,
+ INT nMaxExtent,
+ LPGCP_RESULTSW lpResults,
+ DWORD dwFlags,
+ DWORD dwUnused
+);
+
+extern HINSTANCE hLpk;
+extern LPKETO LpkExtTextOut;
+extern LPKGCP LpkGetCharacterPlacement;
+
/* DEFINES *******************************************************************/
#define HANDLE_LIST_INC 20
@@ -36,6 +64,9 @@ typedef INT
#define SAPCALLBACKDELAY 244
+#define LPK_ETO 1
+#define LPK_GCP 2
+
/* MACRO ********************************************************************/
#define ROP_USES_SOURCE(Rop) (((Rop) << 2 ^ Rop) & 0xCC0000)
@@ -282,6 +313,12 @@ EnumLogFontExW2A(
LPENUMLOGFONTEXA fontA,
CONST ENUMLOGFONTEXW *fontW );
+BOOL
+WINAPI
+LoadLPK(
+ INT LpkFunctionID
+);
+
BOOL
WINAPI
GetETM(HDC hdc,
diff --git a/win32ss/gdi/gdi32/objects/font.c b/win32ss/gdi/gdi32/objects/font.c
index 56998ab84b..0763ff7df2 100644
--- a/win32ss/gdi/gdi32/objects/font.c
+++ b/win32ss/gdi/gdi32/objects/font.c
@@ -448,35 +448,34 @@ GetCharacterPlacementW(
UINT i, nSet;
DPRINT("GetCharacterPlacementW\n");
- if(dwFlags&(~GCP_REORDER)) DPRINT("flags 0x%08lx ignored\n", dwFlags);
- if(lpResults->lpClass) DPRINT("classes not implemented\n");
+ if (dwFlags&(~GCP_REORDER)) DPRINT("flags 0x%08lx ignored\n",
dwFlags);
+ if (lpResults->lpClass) DPRINT("classes not implemented\n");
if (lpResults->lpCaretPos && (dwFlags & GCP_REORDER))
DPRINT("Caret positions for complex scripts not implemented\n");
nSet = (UINT)uCount;
- if(nSet > lpResults->nGlyphs)
+ if (nSet > lpResults->nGlyphs)
nSet = lpResults->nGlyphs;
/* return number of initialized fields */
lpResults->nGlyphs = nSet;
- /*if((dwFlags&GCP_REORDER)==0 || !BidiAvail)
- {*/
+ if (dwFlags & GCP_REORDER)
+ {
+ if (LoadLPK(LPK_GCP))
+ return LpkGetCharacterPlacement(hdc, lpString, uCount, nMaxExtent, lpResults,
dwFlags, 0);
+ }
+
/* Treat the case where no special handling was requested in a fastpath way */
- /* copy will do if the GCP_REORDER flag is not set */
- if(lpResults->lpOutString)
+ /* copy will do if the GCP_REORDER flag is not set */
+ if (lpResults->lpOutString)
lstrcpynW( lpResults->lpOutString, lpString, nSet );
- if(lpResults->lpOrder)
+ if (lpResults->lpOrder)
{
- for(i = 0; i < nSet; i++)
+ for (i = 0; i < nSet; i++)
lpResults->lpOrder[i] = i;
}
- /*} else
- {
- BIDI_Reorder( lpString, uCount, dwFlags, WINE_GCPW_FORCE_LTR,
lpResults->lpOutString,
- nSet, lpResults->lpOrder );
- }*/
/* FIXME: Will use the placement chars */
if (lpResults->lpDx)
diff --git a/win32ss/gdi/gdi32/objects/text.c b/win32ss/gdi/gdi32/objects/text.c
index 31ee3ca2ff..1242020639 100644
--- a/win32ss/gdi/gdi32/objects/text.c
+++ b/win32ss/gdi/gdi32/objects/text.c
@@ -500,6 +500,12 @@ ExtTextOutW(
cwc,
lpDx);
+ if (!(fuOptions & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)))
+ {
+ if (LoadLPK(LPK_ETO))
+ return LpkExtTextOut(hdc, x, y, fuOptions, lprc, lpString, cwc , lpDx, 0);
+ }
+
return NtGdiExtTextOutW(hdc,
x,
y,
diff --git a/win32ss/gdi/gdi32/objects/utils.c b/win32ss/gdi/gdi32/objects/utils.c
index 5a0bca1e93..432c6e1482 100644
--- a/win32ss/gdi/gdi32/objects/utils.c
+++ b/win32ss/gdi/gdi32/objects/utils.c
@@ -1,5 +1,10 @@
#include <precomp.h>
+/* LoadLPK global variables */
+HINSTANCE hLpk = NULL;
+LPKETO LpkExtTextOut = NULL;
+LPKGCP LpkGetCharacterPlacement = NULL;
+
/**
* @name CalculateColorTableSize
*
@@ -408,3 +413,51 @@ EnumLogFontExW2A( LPENUMLOGFONTEXA fontA, CONST ENUMLOGFONTEXW *fontW
)
fontA->elfScript[LF_FACESIZE-1] = '\0';
}
+/*
+* LPK.DLL loader function
+*
+* Returns TRUE if a valid parameter was passed and loading was successful,
+* retruns FALSE otherwise.
+*/
+BOOL WINAPI LoadLPK(INT LpkFunctionID)
+{
+ if(!hLpk) // Check if the DLL is already loaded
+ hLpk = LoadLibraryW(L"lpk.dll");
+
+ if (hLpk)
+ {
+ switch (LpkFunctionID)
+ {
+ case LPK_ETO:
+ if (!LpkExtTextOut) // Check if the function is already loaded
+ LpkExtTextOut = (LPKETO) GetProcAddress(hLpk,
"LpkExtTextOut");
+
+ if (!LpkExtTextOut)
+ {
+ FreeLibrary(hLpk);
+ return FALSE;
+ }
+
+ return TRUE;
+
+ case LPK_GCP:
+ if (!LpkGetCharacterPlacement) // Check if the function is already
loaded
+ LpkGetCharacterPlacement = (LPKGCP) GetProcAddress(hLpk,
"LpkGetCharacterPlacement");
+
+ if (!LpkGetCharacterPlacement)
+ {
+ FreeLibrary(hLpk);
+ return FALSE;
+ }
+
+ return TRUE;
+
+ default:
+ FreeLibrary(hLpk);
+ return FALSE;
+ }
+ }
+
+ else
+ return FALSE;
+}