 win32ss/gdi/ntgdi/freetype.c       | 71 ++++++++++++++++++++++++++++++++++++++
 win32ss/include/callback.h         | 15 +++++++-
 win32ss/user/user32/misc/dllmain.c | 24 +++++++++++++
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/win32ss/gdi/ntgdi/freetype.c b/win32ss/gdi/ntgdi/freetype.c
index 6417012381..b6572c8ce0 100644
--- a/win32ss/gdi/ntgdi/freetype.c
+++ b/win32ss/gdi/ntgdi/freetype.c
@@ -5117,6 +5117,9 @@ GreExtTextOutW(
     int thickness;
     BOOL bResult;
 
+    LPWSTR ReorderedString = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR) * Count, GDITAG_TEXT);
+    //BOOL bIsReordered = FALSE;
+
     /* Check if String is valid */
     if ((Count > 0xFFFF) || (Count > 0 && String == NULL))
     {
@@ -5124,6 +5127,74 @@ GreExtTextOutW(
         return FALSE;
     }
 
+    if (!(fuOptions & (ETO_IGNORELANGUAGE | ETO_GLYPH_INDEX)))
+    {
+        NTSTATUS Status;
+        ULONG ArgumentLength, ResultLength;
+        PVOID Argument, ResultPointer;
+        PLPK_CALLBACK_ARGUMENTS Common;
+
+        ResultPointer = NULL;
+        ResultLength = ArgumentLength = sizeof(LPK_CALLBACK_ARGUMENTS);
+
+        Argument = IntCbAllocateMemory(ArgumentLength);
+
+        if (Argument == NULL || ReorderedString == NULL)
+            goto done;
+
+        Common = (PLPK_CALLBACK_ARGUMENTS) Argument;
+
+        Common->hdc         = hDC;
+        Common->lpString    = String;
+        Common->lpOutString = ReorderedString;
+        Common->uCount      = Count;
+        Common->uCountOut   = Count;
+        Common->bGlyphs     = FALSE;
+
+        UserLeaveCo();
+
+        Status = KeUserModeCallback(USER32_CALLBACK_LPK,
+                                    Argument,
+                                    ArgumentLength,
+                                    &ResultPointer,
+                                    &ResultLength);
+        
+        if (NT_SUCCESS(Status))
+        {
+            _SEH2_TRY
+            {
+                RtlMoveMemory(Argument, ResultPointer, ArgumentLength);
+            }
+            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+            {
+                DPRINT("Failed to copy result from user mode!\n");
+                Status = _SEH2_GetExceptionCode();
+            }
+
+            _SEH2_END;
+        }
+
+        UserEnterCo();
+
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT("Failed LPK usermode callback!\n");
+            goto done;
+        }
+
+        if (Common->bGlyphs)
+            fuOptions |= ETO_GLYPH_INDEX;
+
+        if (Common->lpOutString)
+        {
+            RtlCopyMemory(ReorderedString, Common->lpOutString, sizeof(WCHAR) * Common->uCountOut);
+            //bIsReordered = TRUE;
+        }
+
+        done:
+        IntCbFreeMemory(Argument);
+    }
+
     /* NOTE: This function locks the screen DC, so it must never be called
        with a DC already locked */
     Render = IntIsFontRenderingEnabled();
diff --git a/win32ss/include/callback.h b/win32ss/include/callback.h
index bc8caecb67..472010fb3d 100644
--- a/win32ss/include/callback.h
+++ b/win32ss/include/callback.h
@@ -17,7 +17,8 @@
 #define USER32_CALLBACK_DDEPOST               (13)
 #define USER32_CALLBACK_DDEGET                (14)
 #define USER32_CALLBACK_SETOBM                (15)
-#define USER32_CALLBACK_MAXIMUM               (15)
+#define USER32_CALLBACK_LPK                   (16)
+#define USER32_CALLBACK_MAXIMUM               (16)
 
 typedef struct _WINDOWPROC_CALLBACK_ARGUMENTS
 {
@@ -139,6 +140,16 @@ typedef struct _SETOBM_CALLBACK_ARGUMENTS
     struct tagOEMBITMAPINFO oembmi[93];   
 } SETOBM_CALLBACK_ARGUMENTS, *PSETOBM_CALLBACK_ARGUMENTS;
 
+typedef struct _LPK_CALLBACK_ARGUMENTS
+{
+    HDC hdc;
+    LPCWSTR lpString;
+    UINT uCount;
+    LPWSTR lpOutString;
+    UINT uCountOut;
+    BOOL bGlyphs;
+} LPK_CALLBACK_ARGUMENTS, *PLPK_CALLBACK_ARGUMENTS;
+
 NTSTATUS WINAPI
 User32CallCopyImageFromKernel(PVOID Arguments, ULONG ArgumentLength);
 NTSTATUS WINAPI
@@ -171,4 +182,6 @@ NTSTATUS WINAPI
 User32CallDDEGetFromKernel(PVOID Arguments, ULONG ArgumentLength);
 NTSTATUS WINAPI
 User32CallOBMFromKernel(PVOID Arguments, ULONG ArgumentLength);
+NTSTATUS WINAPI
+User32CallLPKFromKernel(PVOID Arguments, ULONG ArgumentLength);
 #endif /* __INCLUDE_USER32_CALLBACK_H */
diff --git a/win32ss/user/user32/misc/dllmain.c b/win32ss/user/user32/misc/dllmain.c
index 5bf68551f7..d6f70b9268 100644
--- a/win32ss/user/user32/misc/dllmain.c
+++ b/win32ss/user/user32/misc/dllmain.c
@@ -205,6 +205,7 @@ PVOID apfnDispatch[USER32_CALLBACK_MAXIMUM + 1] =
     User32CallDDEPostFromKernel,
     User32CallDDEGetFromKernel,
     User32CallOBMFromKernel,
+    User32CallLPKFromKernel,
 };
 
 
@@ -641,3 +642,26 @@ User32CallOBMFromKernel(PVOID Arguments, ULONG ArgumentLength)
 
   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
 }
+
+NTSTATUS WINAPI User32CallLPKFromKernel(PVOID Arguments, ULONG ArgumentLength)
+{
+    PLPK_CALLBACK_ARGUMENTS Common = Arguments;
+    LPWSTR lpGlyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * Common->uCount);
+    GCP_RESULTSW gcpResults;
+    gcpResults.lStructSize = sizeof(gcpResults);
+    gcpResults.lpGlyphs = lpGlyphs;
+    gcpResults.lpOutString = Common->lpOutString;
+    gcpResults.nGlyphs = Common->uCountOut;
+
+    GetCharacterPlacementW(Common->hdc, Common->lpString, Common->uCount, 0, &gcpResults, GCP_REORDER); //Reorder here
+
+    if (lpGlyphs)
+    {
+        Common->bGlyphs = TRUE;
+        wcscpy(Common->lpOutString, lpGlyphs);
+    }
+
+    HeapFree(GetProcessHeap(), 0, lpGlyphs);
+
+    return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
+}
