[WIN32K:NTGDI]
[reactos.git] / reactos / win32ss / gdi / ntgdi / freetype.c
index 14e41d5..18e682e 100644 (file)
@@ -48,6 +48,7 @@ extern const MATRIX gmxWorldToPageDefault;
 #define gmxWorldToDeviceDefault gmxWorldToPageDefault
 
 FT_Library  library;
+static const WORD gusEnglishUS = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
 
 /* special font names */
 static const UNICODE_STRING MarlettW = RTL_CONSTANT_STRING(L"Marlett");
@@ -58,39 +59,6 @@ static const UNICODE_STRING FixedSysW = RTL_CONSTANT_STRING(L"FixedSys");
 static UNICODE_STRING FontRegPath =
     RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
 
-static PSHARED_FACE
-SharedFace_Create(FT_Face Face)
-{
-    PSHARED_FACE Ptr;
-    Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_FACE), TAG_FONT);
-    if (Ptr)
-    {
-        Ptr->Face = Face;
-        Ptr->RefCount = 1;
-    }
-    return Ptr;
-}
-
-static void
-SharedFace_AddRef(PSHARED_FACE Ptr)
-{
-    ++Ptr->RefCount;
-}
-
-static void
-SharedFace_Release(PSHARED_FACE Ptr)
-{
-    if (Ptr->RefCount <= 0)
-        return;
-
-    --Ptr->RefCount;
-    if (Ptr->RefCount == 0)
-    {
-        FT_Done_Face(Ptr->Face);
-        ExFreePoolWithTag(Ptr, TAG_FONT);
-    }
-}
-
 
 /* The FreeType library is not thread safe, so we have
    to serialize access to it */
@@ -106,12 +74,18 @@ static BOOL RenderingEnabled = TRUE;
 #define IntUnLockGlobalFonts \
   ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FontListLock)
 
+#define ASSERT_GLOBALFONTS_LOCK_HELD() \
+  ASSERT(FontListLock->Owner == KeGetCurrentThread())
+
 #define IntLockFreeType \
   ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FreeTypeLock)
 
 #define IntUnLockFreeType \
   ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FreeTypeLock)
 
+#define ASSERT_FREETYPE_LOCK_HELD() \
+  ASSERT(FreeTypeLock->Owner == KeGetCurrentThread())
+
 #define MAX_FONT_CACHE 256
 
 static LIST_ENTRY FontCacheListHead;
@@ -189,6 +163,129 @@ static const CHARSETINFO FontTci[MAXTCIINDEX] =
 /* list head */
 static RTL_STATIC_LIST_HEAD(FontSubstListHead);
 
+static void
+SharedMem_AddRef(PSHARED_MEM Ptr)
+{
+    ASSERT_FREETYPE_LOCK_HELD();
+
+    ++Ptr->RefCount;
+}
+
+static PSHARED_FACE
+SharedFace_Create(FT_Face Face, PSHARED_MEM Memory)
+{
+    PSHARED_FACE Ptr;
+    Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_FACE), TAG_FONT);
+    if (Ptr)
+    {
+        Ptr->Face = Face;
+        Ptr->RefCount = 1;
+        Ptr->Memory = Memory;
+        SharedMem_AddRef(Memory);
+        DPRINT("Creating SharedFace for %s\n", Face->family_name);
+    }
+    return Ptr;
+}
+
+static PSHARED_MEM
+SharedMem_Create(PBYTE Buffer, ULONG BufferSize, BOOL IsMapping)
+{
+    PSHARED_MEM Ptr;
+    Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_MEM), TAG_FONT);
+    if (Ptr)
+    {
+        Ptr->Buffer = Buffer;
+        Ptr->BufferSize = BufferSize;
+        Ptr->RefCount = 1;
+        Ptr->IsMapping = IsMapping;
+        DPRINT("Creating SharedMem for %p (%i, %p)\n", Buffer, IsMapping, Ptr);
+    }
+    return Ptr;
+}
+
+static void
+SharedFace_AddRef(PSHARED_FACE Ptr)
+{
+    ASSERT_FREETYPE_LOCK_HELD();
+
+    ++Ptr->RefCount;
+}
+
+static void
+RemoveCachedEntry(PFONT_CACHE_ENTRY Entry)
+{
+    ASSERT_FREETYPE_LOCK_HELD();
+
+    FT_Done_Glyph((FT_Glyph)Entry->BitmapGlyph);
+    RemoveEntryList(&Entry->ListEntry);
+    ExFreePoolWithTag(Entry, TAG_FONT);
+    FontCacheNumEntries--;
+    ASSERT(FontCacheNumEntries <= MAX_FONT_CACHE);
+}
+
+static void
+RemoveCacheEntries(FT_Face Face)
+{
+    PLIST_ENTRY CurrentEntry;
+    PFONT_CACHE_ENTRY FontEntry;
+
+    ASSERT_FREETYPE_LOCK_HELD();
+
+    CurrentEntry = FontCacheListHead.Flink;
+    while (CurrentEntry != &FontCacheListHead)
+    {
+        FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
+        CurrentEntry = CurrentEntry->Flink;
+
+        if (FontEntry->Face == Face)
+        {
+            RemoveCachedEntry(FontEntry);
+        }
+    }
+}
+
+static void SharedMem_Release(PSHARED_MEM Ptr)
+{
+    ASSERT_FREETYPE_LOCK_HELD();
+    ASSERT(Ptr->RefCount > 0);
+
+    if (Ptr->RefCount <= 0)
+        return;
+
+    --Ptr->RefCount;
+    if (Ptr->RefCount == 0)
+    {
+        DPRINT("Releasing SharedMem for %p (%i, %p)\n", Ptr->Buffer, Ptr->IsMapping, Ptr);
+        if (Ptr->IsMapping)
+            MmUnmapViewInSystemSpace(Ptr->Buffer);
+        else
+            ExFreePoolWithTag(Ptr->Buffer, TAG_FONT);
+        ExFreePoolWithTag(Ptr, TAG_FONT);
+    }
+}
+
+static void
+SharedFace_Release(PSHARED_FACE Ptr)
+{
+    IntLockFreeType;
+    ASSERT(Ptr->RefCount > 0);
+
+    if (Ptr->RefCount <= 0)
+        return;
+
+    --Ptr->RefCount;
+    if (Ptr->RefCount == 0)
+    {
+        DPRINT("Releasing SharedFace for %s\n", Ptr->Face->family_name);
+        RemoveCacheEntries(Ptr->Face);
+        FT_Done_Face(Ptr->Face);
+        SharedMem_Release(Ptr->Memory);
+        ExFreePoolWithTag(Ptr, TAG_FONT);
+    }
+    IntUnLockFreeType;
+}
+
+
 /*
  * IntLoadFontSubstList --- loads the list of font substitutes
  */
@@ -675,10 +772,8 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
     FT_Face             Face;
     ANSI_STRING         AnsiFaceName;
     FT_WinFNT_HeaderRec WinFNT;
-    INT                 FontCount = 0, CharSetCount = 0;
+    INT                 FaceCount = 0, CharSetCount = 0;
     PUNICODE_STRING     pFileName       = pLoadFont->pFileName;
-    PVOID               Buffer          = pLoadFont->Buffer;
-    ULONG               BufferSize      = pLoadFont->BufferSize;
     DWORD               Characteristics = pLoadFont->Characteristics;
     PUNICODE_STRING     pValueName = &pLoadFont->RegValueName;
     TT_OS2 *            pOS2;
@@ -693,30 +788,37 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
         IntLockFreeType;
         Error = FT_New_Memory_Face(
                     library,
-                    Buffer,
-                    BufferSize,
+                    pLoadFont->Memory->Buffer,
+                    pLoadFont->Memory->BufferSize,
                     ((FontIndex != -1) ? FontIndex : 0),
                     &Face);
+
+        if (!Error)
+            SharedFace = SharedFace_Create(Face, pLoadFont->Memory);
+
         IntUnLockFreeType;
 
         if (FT_IS_SFNT(Face))
             pLoadFont->IsTrueType = TRUE;
 
-        if (!Error)
-            SharedFace = SharedFace_Create(Face);
         if (Error || SharedFace == NULL)
         {
+            if (SharedFace)
+                SharedFace_Release(SharedFace);
+
             if (Error == FT_Err_Unknown_File_Format)
                 DPRINT1("Unknown font file format\n");
             else
-                DPRINT1("Error reading font file (error code: %d)\n", Error);
+                DPRINT1("Error reading font (error code: %d)\n", Error);
             return 0;   /* failure */
         }
     }
     else
     {
         Face = SharedFace->Face;
+        IntLockFreeType;
         SharedFace_AddRef(SharedFace);
+        IntUnLockFreeType;
     }
 
     /* allocate a FONT_ENTRY */
@@ -870,7 +972,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
         FontGDI->CharSet = SYMBOL_CHARSET;
     }
 
-    ++FontCount;
+    ++FaceCount;
     DPRINT("Font loaded: %s (%s)\n", Face->family_name, Face->style_name);
     DPRINT("Num glyphs: %d\n", Face->num_glyphs);
     DPRINT("CharSet: %d\n", FontGDI->CharSet);
@@ -905,7 +1007,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
                 FT_Long i;
                 for (i = 1; i < TrueType->ttc_header.count; ++i)
                 {
-                    FontCount += IntGdiLoadFontsFromMemory(pLoadFont, NULL, i, -1);
+                    FaceCount += IntGdiLoadFontsFromMemory(pLoadFont, NULL, i, -1);
                 }
             }
         }
@@ -941,11 +1043,12 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
 
         for (i = 1; i < CharSetCount; ++i)
         {
-            FontCount += IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, i);
+            /* Do not count charsets towards 'faces' loaded */
+            IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, i);
         }
     }
 
-    return FontCount;   /* number of loaded fonts */
+    return FaceCount;   /* number of loaded faces */
 }
 
 /*
@@ -1006,8 +1109,7 @@ IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
     }
 
     LoadFont.pFileName          = FileName;
-    LoadFont.Buffer             = Buffer;
-    LoadFont.BufferSize         = ViewSize;
+    LoadFont.Memory             = SharedMem_Create(Buffer, ViewSize, TRUE);
     LoadFont.Characteristics    = Characteristics;
     RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
     LoadFont.IsTrueType         = FALSE;
@@ -1016,6 +1118,11 @@ IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
 
     ObDereferenceObject(SectionObject);
 
+    /* Release our copy */
+    IntLockFreeType;
+    SharedMem_Release(LoadFont.Memory);
+    IntUnLockFreeType;
+
     if (FontCount > 0)
     {
         if (LoadFont.IsTrueType)
@@ -1067,30 +1174,34 @@ IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded)
 {
     GDI_LOAD_FONT LoadFont;
     FONT_ENTRY_COLL_MEM* EntryCollection;
-    INT FontCount;
+    INT FaceCount;
     HANDLE Ret = 0;
 
-    /* We leak this buffer for now, same as all fonts do with their buffer! */
-    LoadFont.Buffer = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_FONT);
-    if (!LoadFont.Buffer)
+    PVOID BufferCopy = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_FONT);
+
+    if (!BufferCopy)
     {
         *pNumAdded = 0;
         return NULL;
     }
-    memcpy(LoadFont.Buffer, Buffer, dwSize);
+    memcpy(BufferCopy, Buffer, dwSize);
 
     LoadFont.pFileName = NULL;
-    LoadFont.BufferSize = dwSize;
+    LoadFont.Memory = SharedMem_Create(BufferCopy, dwSize, FALSE);
     LoadFont.Characteristics = FR_PRIVATE | FR_NOT_ENUM;
     RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
     LoadFont.IsTrueType = FALSE;
     LoadFont.PrivateEntry = NULL;
-    FontCount = IntGdiLoadFontsFromMemory(&LoadFont, NULL, -1, -1);
+    FaceCount = IntGdiLoadFontsFromMemory(&LoadFont, NULL, -1, -1);
 
     RtlFreeUnicodeString(&LoadFont.RegValueName);
 
-    *pNumAdded = FontCount;
-    if (FontCount > 0)
+    /* Release our copy */
+    IntLockFreeType;
+    SharedMem_Release(LoadFont.Memory);
+    IntUnLockFreeType;
+
+    if (FaceCount > 0)
     {
         EntryCollection = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_COLL_MEM), TAG_FONT);
         if (EntryCollection)
@@ -1104,12 +1215,27 @@ IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded)
             Ret = (HANDLE)EntryCollection->Handle;
         }
     }
+    *pNumAdded = FaceCount;
 
     return Ret;
 }
 
 // FIXME: Add RemoveFontResource
 
+static VOID FASTCALL
+CleanupFontEntry(PFONT_ENTRY FontEntry)
+{
+    PFONTGDI FontGDI = FontEntry->Font;
+    PSHARED_FACE SharedFace = FontGDI->SharedFace;
+
+    if (FontGDI->Filename)
+        ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
+
+    EngFreeMem(FontGDI);
+    SharedFace_Release(SharedFace);
+    ExFreePoolWithTag(FontEntry, TAG_FONT);
+}
+
 VOID FASTCALL
 IntGdiCleanupMemEntry(PFONT_ENTRY_MEM Head)
 {
@@ -1121,14 +1247,31 @@ IntGdiCleanupMemEntry(PFONT_ENTRY_MEM Head)
         Entry = RemoveHeadList(&Head->ListEntry);
         FontEntry = CONTAINING_RECORD(Entry, FONT_ENTRY_MEM, ListEntry);
 
-        // Delete FontEntry->Entry (FONT_ENTRY*)
+        CleanupFontEntry(FontEntry->Entry);
         ExFreePoolWithTag(FontEntry, TAG_FONT);
     }
 
-    // Delete Head->Entry (FONT_ENTRY*)
+    CleanupFontEntry(Head->Entry);
     ExFreePoolWithTag(Head, TAG_FONT);
 }
 
+static VOID FASTCALL
+UnlinkFontMemCollection(PFONT_ENTRY_COLL_MEM Collection)
+{
+    PFONT_ENTRY_MEM FontMemEntry = Collection->Entry;
+    PLIST_ENTRY ListEntry;
+    RemoveEntryList(&Collection->ListEntry);
+
+    do {
+        /* Also unlink the FONT_ENTRY stuff from the PrivateFontListHead */
+        RemoveEntryList(&FontMemEntry->Entry->ListEntry);
+
+        ListEntry = FontMemEntry->ListEntry.Flink;
+        FontMemEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY_MEM, ListEntry);
+
+    } while (FontMemEntry != Collection->Entry);
+}
+
 BOOL FASTCALL
 IntGdiRemoveFontMemResource(HANDLE hMMFont)
 {
@@ -1146,7 +1289,7 @@ IntGdiRemoveFontMemResource(HANDLE hMMFont)
         if (CurrentEntry->Handle == (UINT)hMMFont)
         {
             EntryCollection = CurrentEntry;
-            RemoveEntryList(Entry);
+            UnlinkFontMemCollection(CurrentEntry);
             break;
         }
 
@@ -1164,6 +1307,52 @@ IntGdiRemoveFontMemResource(HANDLE hMMFont)
 }
 
 
+VOID FASTCALL
+IntGdiCleanupPrivateFontsForProcess(VOID)
+{
+    PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
+    PLIST_ENTRY Entry;
+    PFONT_ENTRY_COLL_MEM EntryCollection;
+
+    DPRINT("IntGdiCleanupPrivateFontsForProcess()\n");
+    do {
+        Entry = NULL;
+        EntryCollection = NULL;
+
+        IntLockProcessPrivateFonts(Win32Process);
+        if (!IsListEmpty(&Win32Process->PrivateMemFontListHead))
+        {
+            Entry = Win32Process->PrivateMemFontListHead.Flink;
+            EntryCollection = CONTAINING_RECORD(Entry, FONT_ENTRY_COLL_MEM, ListEntry);
+            UnlinkFontMemCollection(EntryCollection);
+        }
+        IntUnLockProcessPrivateFonts(Win32Process);
+
+        if (EntryCollection)
+        {
+            IntGdiCleanupMemEntry(EntryCollection->Entry);
+            ExFreePoolWithTag(EntryCollection, TAG_FONT);
+        }
+        else
+        {
+            /* No Mem fonts anymore, see if we have any other private fonts left */
+            Entry = NULL;
+            IntLockProcessPrivateFonts(Win32Process);
+            if (!IsListEmpty(&Win32Process->PrivateFontListHead))
+            {
+                Entry = RemoveHeadList(&Win32Process->PrivateFontListHead);
+            }
+            IntUnLockProcessPrivateFonts(Win32Process);
+
+            if (Entry)
+            {
+                CleanupFontEntry(CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry));
+            }
+        }
+
+    } while (Entry);
+}
+
 BOOL FASTCALL
 IntIsFontRenderingEnabled(VOID)
 {
@@ -1549,6 +1738,10 @@ FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI,
     FillTMEx(TM, FontGDI, pOS2, pHori, pFNT, FALSE);
 }
 
+static NTSTATUS
+IntGetFontLocalizedName(PUNICODE_STRING pNameW, FT_Face Face,
+                        FT_UShort NameID, FT_UShort LangID);
+
 /*************************************************************
  * IntGetOutlineTextMetrics
  *
@@ -1563,57 +1756,40 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI,
     TT_HoriHeader *pHori;
     TT_Postscript *pPost;
     FT_Fixed XScale, YScale;
-    ANSI_STRING FamilyNameA, StyleNameA;
-    UNICODE_STRING FamilyNameW, StyleNameW, Regular;
     FT_WinFNT_HeaderRec Win;
     FT_Error Error;
     char *Cp;
-    NTSTATUS status;
     FT_Face Face = FontGDI->SharedFace->Face;
+    UNICODE_STRING FamilyNameW, FaceNameW, StyleNameW, FullNameW;
 
-    Needed = sizeof(OUTLINETEXTMETRICW);
+    /* family name */
+    RtlInitUnicodeString(&FamilyNameW, NULL);
+    IntGetFontLocalizedName(&FamilyNameW, Face, TT_NAME_ID_FONT_FAMILY, gusLanguageID);
 
-    RtlInitAnsiString(&FamilyNameA, Face->family_name);
-    status = RtlAnsiStringToUnicodeString(&FamilyNameW, &FamilyNameA, TRUE);
-    if (!NT_SUCCESS(status))
-    {
-        return 0;
-    }
+    /* face name */
+    RtlInitUnicodeString(&FaceNameW, NULL);
+    IntGetFontLocalizedName(&FaceNameW, Face, TT_NAME_ID_FULL_NAME, gusLanguageID);
 
-    RtlInitAnsiString(&StyleNameA, Face->style_name);
-    status = RtlAnsiStringToUnicodeString(&StyleNameW, &StyleNameA, TRUE);
-    if (!NT_SUCCESS(status))
-    {
-        RtlFreeUnicodeString(&FamilyNameW);
-        return 0;
-    }
+    /* style name */
+    RtlInitUnicodeString(&StyleNameW, NULL);
+    IntGetFontLocalizedName(&StyleNameW, Face, TT_NAME_ID_FONT_SUBFAMILY, gusLanguageID);
 
-    /* These names should be read from the TT name table */
+    /* unique name (full name) */
+    RtlInitUnicodeString(&FullNameW, NULL);
+    IntGetFontLocalizedName(&FullNameW, Face, TT_NAME_ID_UNIQUE_ID, gusLanguageID);
 
-    /* Length of otmpFamilyName */
+    Needed = sizeof(OUTLINETEXTMETRICW);
     Needed += FamilyNameW.Length + sizeof(WCHAR);
-
-    RtlInitUnicodeString(&Regular, L"Regular");
-    /* Length of otmpFaceName */
-    if (RtlEqualUnicodeString(&StyleNameW, &Regular, TRUE))
-    {
-        Needed += FamilyNameW.Length + sizeof(WCHAR); /* Just the family name */
-    }
-    else
-    {
-        Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1); /* family + " " + style */
-    }
-
-    /* Length of otmpStyleName */
+    Needed += FaceNameW.Length + sizeof(WCHAR);
     Needed += StyleNameW.Length + sizeof(WCHAR);
-
-    /* Length of otmpFullName */
-    Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1);
+    Needed += FullNameW.Length + sizeof(WCHAR);
 
     if (Size < Needed)
     {
         RtlFreeUnicodeString(&FamilyNameW);
+        RtlFreeUnicodeString(&FaceNameW);
         RtlFreeUnicodeString(&StyleNameW);
+        RtlFreeUnicodeString(&FullNameW);
         return Needed;
     }
 
@@ -1626,8 +1802,10 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI,
     {
         IntUnLockFreeType;
         DPRINT1("Can't find OS/2 table - not TT font?\n");
-        RtlFreeUnicodeString(&StyleNameW);
         RtlFreeUnicodeString(&FamilyNameW);
+        RtlFreeUnicodeString(&FaceNameW);
+        RtlFreeUnicodeString(&StyleNameW);
+        RtlFreeUnicodeString(&FullNameW);
         return 0;
     }
 
@@ -1636,8 +1814,10 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI,
     {
         IntUnLockFreeType;
         DPRINT1("Can't find HHEA table - not TT font?\n");
-        RtlFreeUnicodeString(&StyleNameW);
         RtlFreeUnicodeString(&FamilyNameW);
+        RtlFreeUnicodeString(&FaceNameW);
+        RtlFreeUnicodeString(&StyleNameW);
+        RtlFreeUnicodeString(&FullNameW);
         return 0;
     }
 
@@ -1693,33 +1873,34 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI,
 
     IntUnLockFreeType;
 
-    /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
     Cp = (char*) Otm + sizeof(OUTLINETEXTMETRICW);
+
+    /* family name */
     Otm->otmpFamilyName = (LPSTR)(Cp - (char*) Otm);
     wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
     Cp += FamilyNameW.Length + sizeof(WCHAR);
+
+    /* face name */
+    Otm->otmpFaceName = (LPSTR)(Cp - (char*) Otm);
+    wcscpy((WCHAR*) Cp, FaceNameW.Buffer);
+    Cp += FaceNameW.Length + sizeof(WCHAR);
+
+    /* style name */
     Otm->otmpStyleName = (LPSTR)(Cp - (char*) Otm);
     wcscpy((WCHAR*) Cp, StyleNameW.Buffer);
     Cp += StyleNameW.Length + sizeof(WCHAR);
-    Otm->otmpFaceName = (LPSTR)(Cp - (char*) Otm);
-    wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
-    if (!RtlEqualUnicodeString(&StyleNameW, &Regular, TRUE))
-    {
-        wcscat((WCHAR*) Cp, L" ");
-        wcscat((WCHAR*) Cp, StyleNameW.Buffer);
-        Cp += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1);
-    }
-    else
-    {
-        Cp += FamilyNameW.Length + sizeof(WCHAR);
-    }
+
+    /* unique name (full name) */
     Otm->otmpFullName = (LPSTR)(Cp - (char*) Otm);
-    wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
-    wcscat((WCHAR*) Cp, L" ");
-    wcscat((WCHAR*) Cp, StyleNameW.Buffer);
+    wcscpy((WCHAR*) Cp, FullNameW.Buffer);
+    Cp += FullNameW.Length + sizeof(WCHAR);
+
+    ASSERT(Cp - (char*)Otm == Needed);
 
-    RtlFreeUnicodeString(&StyleNameW);
     RtlFreeUnicodeString(&FamilyNameW);
+    RtlFreeUnicodeString(&FaceNameW);
+    RtlFreeUnicodeString(&StyleNameW);
+    RtlFreeUnicodeString(&FullNameW);
 
     return Needed;
 }
@@ -1737,7 +1918,7 @@ FindFaceNameInList(PUNICODE_STRING FaceName, PLIST_ENTRY Head)
     Entry = Head->Flink;
     while (Entry != Head)
     {
-        CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
+        CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
 
         FontGDI = CurrentEntry->Font;
         ASSERT(FontGDI);
@@ -1774,7 +1955,8 @@ FindFaceNameInLists(PUNICODE_STRING FaceName)
     PPROCESSINFO Win32Process;
     PFONTGDI Font;
 
-    /* Search the process local list */
+    /* Search the process local list.
+       We do not have to search the 'Mem' list, since those fonts are linked in the PrivateFontListHead */
     Win32Process = PsGetCurrentProcessWin32Process();
     IntLockProcessPrivateFonts(Win32Process);
     Font = FindFaceNameInList(FaceName, &Win32Process->PrivateFontListHead);
@@ -1852,30 +2034,40 @@ SwapEndian(LPVOID pvData, DWORD Size)
 }
 
 static NTSTATUS
-IntGetFontLocalizedName(PUNICODE_STRING pLocalNameW, FT_Face Face)
+IntGetFontLocalizedName(PUNICODE_STRING pNameW, FT_Face Face,
+                        FT_UShort NameID, FT_UShort LangID)
 {
     FT_SfntName Name;
     INT i, Count;
-    WCHAR Buf[LF_FACESIZE];
+    WCHAR Buf[LF_FULLFACESIZE];
+    FT_Error Error;
     NTSTATUS Status = STATUS_NOT_FOUND;
+    ANSI_STRING AnsiName;
 
-    RtlInitUnicodeString(pLocalNameW, NULL);
+    RtlFreeUnicodeString(pNameW);
 
     Count = FT_Get_Sfnt_Name_Count(Face);
     for (i = 0; i < Count; ++i)
     {
-        FT_Get_Sfnt_Name(Face, i, &Name);
+        Error = FT_Get_Sfnt_Name(Face, i, &Name);
+        if (Error)
+            continue;
+
         if (Name.platform_id != TT_PLATFORM_MICROSOFT ||
             Name.encoding_id != TT_MS_ID_UNICODE_CS)
         {
             continue;   /* not Microsoft Unicode name */
         }
 
-        if (Name.name_id != TT_NAME_ID_FONT_FAMILY ||
-            Name.string == NULL || Name.string_len == 0 ||
+        if (Name.name_id != NameID || Name.language_id != LangID)
+        {
+            continue;   /* mismatched */
+        }
+
+        if (Name.string == NULL || Name.string_len == 0 ||
             (Name.string[0] == 0 && Name.string[1] == 0))
         {
-            continue;   /* not family name */
+            continue;   /* invalid string */
         }
 
         if (sizeof(Buf) < Name.string_len + sizeof(UNICODE_NULL))
@@ -1889,19 +2081,37 @@ IntGetFontLocalizedName(PUNICODE_STRING pLocalNameW, FT_Face Face)
 
         /* Convert UTF-16 big endian to little endian */
         SwapEndian(Buf, Name.string_len);
-#if 0
-        DPRINT("IntGetFontLocalizedName: %S (%d)\n", Buf, Name.string_len);
-#endif
 
-        Status = RtlCreateUnicodeString(pLocalNameW, Buf);
+        RtlCreateUnicodeString(pNameW, Buf);
+        Status = STATUS_SUCCESS;
         break;
     }
 
+    if (Status == STATUS_NOT_FOUND)
+    {
+        if (LangID != gusEnglishUS)
+        {
+            /* Retry with English US */
+            Status = IntGetFontLocalizedName(pNameW, Face, NameID, gusEnglishUS);
+        }
+        else if (NameID == TT_NAME_ID_FONT_SUBFAMILY)
+        {
+            RtlInitAnsiString(&AnsiName, Face->style_name);
+            Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
+        }
+        else
+        {
+            RtlInitAnsiString(&AnsiName, Face->family_name);
+            Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
+        }
+    }
+
     return Status;
 }
 
 static void FASTCALL
-FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI)
+FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName,
+                   LPCWSTR FullName, PFONTGDI FontGDI)
 {
     ANSI_STRING StyleA;
     UNICODE_STRING StyleW;
@@ -1916,7 +2126,9 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI)
     DWORD fs0;
     NTSTATUS status;
     FT_Face Face = FontGDI->SharedFace->Face;
+    UNICODE_STRING NameW;
 
+    RtlInitUnicodeString(&NameW, NULL);
     RtlZeroMemory(Info, sizeof(FONTFAMILYINFO));
     Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
     Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
@@ -1978,36 +2190,43 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI)
 
     ExFreePoolWithTag(Otm, GDITAG_TEXT);
 
-    /* try the localized name */
-    status = STATUS_UNSUCCESSFUL;
-    if (CharSetFromLangID(gusLanguageID) == FontGDI->CharSet)
+    /* face name */
+    if (FaceName)
     {
-        /* get localized name */
-        UNICODE_STRING LocalNameW;
-        status = IntGetFontLocalizedName(&LocalNameW, Face);
+        RtlStringCbCopyW(Lf->lfFaceName, sizeof(Lf->lfFaceName), FaceName);
+    }
+    else
+    {
+        status = IntGetFontLocalizedName(&NameW, Face, TT_NAME_ID_FONT_FAMILY,
+                                         gusLanguageID);
         if (NT_SUCCESS(status))
         {
             /* store it */
-            RtlStringCbCopyW(Info->EnumLogFontEx.elfLogFont.lfFaceName,
-                             sizeof(Info->EnumLogFontEx.elfLogFont.lfFaceName),
-                             LocalNameW.Buffer);
-            RtlStringCbCopyW(Info->EnumLogFontEx.elfFullName,
-                             sizeof(Info->EnumLogFontEx.elfFullName),
-                             LocalNameW.Buffer);
+            RtlStringCbCopyW(Lf->lfFaceName, sizeof(Lf->lfFaceName),
+                             NameW.Buffer);
+            RtlFreeUnicodeString(&NameW);
         }
-        RtlFreeUnicodeString(&LocalNameW);
     }
 
-    /* if localized name was unavailable */
-    if (!NT_SUCCESS(status))
+    /* full name */
+    if (FullName)
     {
-        /* store English name */
-        RtlStringCbCopyW(Info->EnumLogFontEx.elfLogFont.lfFaceName,
-                         sizeof(Info->EnumLogFontEx.elfLogFont.lfFaceName),
-                         FaceName);
         RtlStringCbCopyW(Info->EnumLogFontEx.elfFullName,
                          sizeof(Info->EnumLogFontEx.elfFullName),
-                         FaceName);
+                         FullName);
+    }
+    else
+    {
+        status = IntGetFontLocalizedName(&NameW, Face, TT_NAME_ID_FULL_NAME,
+                                         gusLanguageID);
+        if (NT_SUCCESS(status))
+        {
+            /* store it */
+            RtlStringCbCopyW(Info->EnumLogFontEx.elfFullName,
+                             sizeof(Info->EnumLogFontEx.elfFullName),
+                             NameW.Buffer);
+            RtlFreeUnicodeString(&NameW);
+        }
     }
 
     RtlInitAnsiString(&StyleA, Face->style_name);
@@ -2018,19 +2237,8 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI)
     {
         return;
     }
-    if (StyleW.Length)
-    {
-        if (wcslen(Info->EnumLogFontEx.elfFullName) +
-            StyleW.Length / sizeof(WCHAR) + 1 <=
-                sizeof(Info->EnumLogFontEx.elfFullName))
-        {
-            wcscat(Info->EnumLogFontEx.elfFullName, L" ");
-            wcscat(Info->EnumLogFontEx.elfFullName, StyleW.Buffer);
-        }
-    }
+    Info->EnumLogFontEx.elfScript[0] = UNICODE_NULL;
 
-    Info->EnumLogFontEx.elfLogFont.lfCharSet = DEFAULT_CHARSET;
-    Info->EnumLogFontEx.elfScript[0] = L'\0';
     IntLockFreeType;
     pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
 
@@ -2089,7 +2297,6 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI)
             }
             if (DEFAULT_CHARSET != CharSetInfo.ciCharset)
             {
-                Info->EnumLogFontEx.elfLogFont.lfCharSet = CharSetInfo.ciCharset;
                 if (ElfScripts[i])
                     wcscpy(Info->EnumLogFontEx.elfScript, ElfScripts[i]);
                 else
@@ -2175,7 +2382,8 @@ GetFontFamilyInfoForList(LPLOGFONTW LogFont,
         {
             if (*Count < Size)
             {
-                FontFamilyFillInfo(Info + *Count, EntryFaceNameW.Buffer, FontGDI);
+                FontFamilyFillInfo(Info + *Count, EntryFaceNameW.Buffer,
+                                   NULL, FontGDI);
             }
             (*Count)++;
         }
@@ -2246,7 +2454,7 @@ FontFamilyInfoQueryRegistryCallback(IN PWSTR ValueName, IN ULONG ValueType,
         if (InfoContext->Count < InfoContext->Size)
         {
             FontFamilyFillInfo(InfoContext->Info + InfoContext->Count,
-                               RegistryName.Buffer, FontGDI);
+                               RegistryName.Buffer, NULL, FontGDI);
         }
         InfoContext->Count++;
         return STATUS_SUCCESS;
@@ -2333,10 +2541,12 @@ ftGdiGlyphCacheGet(
     PLIST_ENTRY CurrentEntry;
     PFONT_CACHE_ENTRY FontEntry;
 
+    ASSERT_FREETYPE_LOCK_HELD();
+
     CurrentEntry = FontCacheListHead.Flink;
     while (CurrentEntry != &FontCacheListHead)
     {
-        FontEntry = (PFONT_CACHE_ENTRY)CurrentEntry;
+        FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
         if ((FontEntry->Face == Face) &&
             (FontEntry->GlyphIndex == GlyphIndex) &&
             (FontEntry->Height == Height) &&
@@ -2412,6 +2622,8 @@ ftGdiGlyphCacheSet(
     FT_Bitmap AlignedBitmap;
     FT_BitmapGlyph BitmapGlyph;
 
+    ASSERT_FREETYPE_LOCK_HELD();
+
     error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
     if (error)
     {
@@ -2441,6 +2653,7 @@ ftGdiGlyphCacheSet(
     {
         DPRINT1("Conversion failed\n");
         ExFreePoolWithTag(NewEntry, TAG_FONT);
+        FT_Bitmap_Done(GlyphSlot->library, &AlignedBitmap);
         FT_Done_Glyph((FT_Glyph)BitmapGlyph);
         return NULL;
     }
@@ -2455,13 +2668,10 @@ ftGdiGlyphCacheSet(
     NewEntry->mxWorldToDevice = *pmx;
 
     InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry);
-    if (FontCacheNumEntries++ > MAX_FONT_CACHE)
+    if (++FontCacheNumEntries > MAX_FONT_CACHE)
     {
-        NewEntry = (PFONT_CACHE_ENTRY)FontCacheListHead.Blink;
-        FT_Done_Glyph((FT_Glyph)NewEntry->BitmapGlyph);
-        RemoveTailList(&FontCacheListHead);
-        ExFreePoolWithTag(NewEntry, TAG_FONT);
-        FontCacheNumEntries--;
+        NewEntry = CONTAINING_RECORD(FontCacheListHead.Blink, FONT_CACHE_ENTRY, ListEntry);
+        RemoveCachedEntry(NewEntry);
     }
 
     return BitmapGlyph;
@@ -3727,6 +3937,7 @@ GetFontPenalty(LOGFONTW *               LogFont,
     LONG    Long;
     BOOL    fFixedSys = FALSE, fNeedScaling = FALSE;
     const BYTE UserCharSet = CharSetFromLangID(gusLanguageID);
+    NTSTATUS Status;
 
     /* FIXME: Aspect Penalty 30 */
     /* FIXME: IntSizeSynth Penalty 20 */
@@ -3862,32 +4073,58 @@ GetFontPenalty(LOGFONTW *               LogFont,
 
     if (RequestedNameW->Buffer[0])
     {
-        if (RtlEqualUnicodeString(RequestedNameW, FullFaceNameW, TRUE))
+        BOOL Found = FALSE;
+        FT_Face Face = FontGDI->SharedFace->Face;
+
+        /* localized family name */
+        if (!Found)
         {
-            /* matched with full face name */
+            Status = IntGetFontLocalizedName(ActualNameW, Face, TT_NAME_ID_FONT_FAMILY,
+                                             gusLanguageID);
+            if (NT_SUCCESS(Status))
+            {
+                Found = RtlEqualUnicodeString(RequestedNameW, ActualNameW, TRUE);
+            }
         }
-        else if (RtlEqualUnicodeString(RequestedNameW, ActualNameW, TRUE))
+        /* localized full name */
+        if (!Found)
         {
-            /* matched with actual name */
+            Status = IntGetFontLocalizedName(ActualNameW, Face, TT_NAME_ID_FULL_NAME,
+                                             gusLanguageID);
+            if (NT_SUCCESS(Status))
+            {
+                Found = RtlEqualUnicodeString(RequestedNameW, ActualNameW, TRUE);
+            }
         }
-        else
+        if (gusLanguageID != gusEnglishUS)
         {
-            /* try the localized name */
-            UNICODE_STRING LocalNameW;
-            FT_Face Face = FontGDI->SharedFace->Face;
-            IntGetFontLocalizedName(&LocalNameW, Face);
-            if (RtlEqualUnicodeString(RequestedNameW, &LocalNameW, TRUE))
+            /* English family name */
+            if (!Found)
             {
-                /* matched with localizied name */
+                Status = IntGetFontLocalizedName(ActualNameW, Face, TT_NAME_ID_FONT_FAMILY,
+                                                 gusEnglishUS);
+                if (NT_SUCCESS(Status))
+                {
+                    Found = RtlEqualUnicodeString(RequestedNameW, ActualNameW, TRUE);
+                }
             }
-            else
+            /* English full name */
+            if (!Found)
             {
-                /* FaceName Penalty 10000 */
-                /* Requested a face name, but the candidate's face name
-                   does not match. */
-                Penalty += 10000;
+                Status = IntGetFontLocalizedName(ActualNameW, Face, TT_NAME_ID_FULL_NAME,
+                                                 gusEnglishUS);
+                if (NT_SUCCESS(Status))
+                {
+                    Found = RtlEqualUnicodeString(RequestedNameW, ActualNameW, TRUE);
+                }
             }
-            RtlFreeUnicodeString(&LocalNameW);
+        }
+        if (!Found)
+        {
+            /* FaceName Penalty 10000 */
+            /* Requested a face name, but the candidate's face name
+               does not match. */
+            Penalty += 10000;
         }
     }
 
@@ -4116,7 +4353,9 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont,
     Entry = Head->Flink;
     while (Entry != Head)
     {
-        CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
+        CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
+        Entry = Entry->Flink;
+
         FontGDI = CurrentEntry->Font;
         ASSERT(FontGDI);
         Face = FontGDI->SharedFace->Face;
@@ -4126,8 +4365,6 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont,
         Status = RtlAnsiStringToUnicodeString(&ActualNameW, &ActualNameA, TRUE);
         if (!NT_SUCCESS(Status))
         {
-            /* next entry */
-            Entry = Entry->Flink;
             continue;
         }
 
@@ -4154,8 +4391,6 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont,
             {
                 RtlFreeUnicodeString(&ActualNameW);
                 RtlFreeUnicodeString(&FullFaceNameW);
-                /* next entry */
-                Entry = Entry->Flink;
                 continue;
             }
 
@@ -4177,9 +4412,6 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont,
 
         /* free strings */
         RtlFreeUnicodeString(&ActualNameW);
-
-        /* next entry */
-        Entry = Entry->Flink;
     }
 
     if (Otm)
@@ -4490,7 +4722,7 @@ IntGdiGetFontResourceInfo(
 
         IsEqual = FALSE;
         FontFamilyFillInfo(&FamInfo[Count], FontEntry->FaceName.Buffer,
-                           FontEntry->Font);
+                           NULL, FontEntry->Font);
         for (i = 0; i < Count; ++i)
         {
             if (EqualFamilyInfo(&FamInfo[i], &FamInfo[Count]))