/* NOTE: If nIndex < 0 then return the number of charsets. */ UINT FASTCALL IntGetCharSet(INT nIndex, FT_ULong CodePageRange1) { UINT BitIndex, CharSet; UINT nCount = 0; if (CodePageRange1 == 0) { return (nIndex < 0) ? 1 : DEFAULT_CHARSET; } for (BitIndex = 0; BitIndex < MAXTCIINDEX; ++BitIndex) { if (CodePageRange1 & (1 << BitIndex)) { CharSet = g_FontTci[BitIndex].ciCharset; if ((nIndex >= 0) && (nCount == (UINT)nIndex)) { return CharSet; } ++nCount; } } return (nIndex < 0) ? nCount : ANSI_CHARSET; } /* pixels to points */ #define PX2PT(pixels) FT_MulDiv((pixels), 72, 96) static INT FASTCALL IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont) { FT_Error Error; PFONT_ENTRY Entry; PFONT_ENTRY_MEM PrivateEntry; PFONTGDI FontGDI; FT_Face Face; NTSTATUS Status; ANSI_STRING AnsiString; FT_WinFNT_HeaderRec WinFNT; PUNICODE_STRING pFileName = pLoadFont->pFileName; DWORD Characteristics = pLoadFont->Characteristics; PUNICODE_STRING pValueName = &pLoadFont->RegValueName; TT_OS2 * pOS2; FT_ULong os2_ulCodePageRange1; PSHARED_FACE SharedFace; INT iCharSet, CharSetCount; FT_Long iFace, FaceCount; LIST_ENTRY LoadedFontList; USHORT NameLength; SIZE_T Length; PWCHAR pszBuffer; UNICODE_STRING NewString; WCHAR szSize[32]; /* Retrieve the number of faces */ IntLockFreeType(); Error = FT_New_Memory_Face(g_FreeTypeLibrary, pLoadFont->Memory->Buffer, pLoadFont->Memory->BufferSize, -1, &Face); if (!Error) { FaceCount = Face->num_faces; FT_Done_Face(Face); } IntUnLockFreeType(); if (Error) { UNICODE_STRING MemoryFont = RTL_CONSTANT_STRING(L"MemoryFont"); PUNICODE_STRING PrintFile = pFileName ? pFileName : &MemoryFont; if (Error == FT_Err_Unknown_File_Format) DPRINT1("Unknown font file format (%wZ)\n", PrintFile); else DPRINT1("Error reading font (FT_Error: %d, %wZ)\n", Error, PrintFile); return 0; /* failure */ } /* * Initialize the temporary font list that needs to be appended to the * global or per-process font table, in case font enumeration successes. * If an error happens while loading and enumerating the fonts, this list * is used to cleanup the allocated resources. */ InitializeListHead(&LoadedFontList); /* * Enumerate each typeface in the font. */ for (iFace = 0; iFace < FaceCount; ++iFace) { Face = NULL; SharedFace = NULL; IntLockFreeType(); Error = FT_New_Memory_Face(g_FreeTypeLibrary, pLoadFont->Memory->Buffer, pLoadFont->Memory->BufferSize, iFace, &Face); if (!Error) { SharedFace = SharedFace_Create(Face, pLoadFont->Memory); } IntUnLockFreeType(); if (Error || !SharedFace) { DPRINT1("Error reading font (FT_Error: %d)\n", Error); goto Finish; /* failure */ } /* os2_ulCodePageRange1 and CharSetCount and IsTrueType */ os2_ulCodePageRange1 = 0; if (FT_IS_SFNT(Face)) { IntLockFreeType(); pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2); if (pOS2) { os2_ulCodePageRange1 = pOS2->ulCodePageRange1; } IntUnLockFreeType(); CharSetCount = IntGetCharSet(-1, os2_ulCodePageRange1); pLoadFont->IsTrueType = TRUE; } else { CharSetCount = 1; pLoadFont->IsTrueType = FALSE; } /* * Enumerate all supported character sets for the selected typeface. */ for (iCharSet = 0; iCharSet < CharSetCount; ++iCharSet) { /* * Add a reference to SharedFace only when iCharSet is > 0, * since the first reference has been already done by the * SharedFace_Create() call above. */ if (iCharSet > 0) { IntLockFreeType(); SharedFace_AddRef(SharedFace); IntUnLockFreeType(); } /* Allocate a FONT_ENTRY */ Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT); if (!Entry) { DPRINT1("Failed to allocate FONT_ENTRY\n"); SharedFace_Release(SharedFace); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Finish; /* failure */ } /* Allocate a FONTGDI */ FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT); if (!FontGDI) { DPRINT1("Failed to allocate FontGDI\n"); SharedFace_Release(SharedFace); ExFreePoolWithTag(Entry, TAG_FONT); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Finish; /* failure */ } /* Set face */ FontGDI->SharedFace = SharedFace; FontGDI->CharSet = ANSI_CHARSET; FontGDI->OriginalItalic = FALSE; FontGDI->RequestItalic = FALSE; FontGDI->OriginalWeight = FW_NORMAL; FontGDI->RequestWeight = FW_NORMAL; IntLockFreeType(); if (FT_IS_SFNT(Face)) { pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2); if (pOS2) { FontGDI->OriginalItalic = !!(pOS2->fsSelection & 0x1); FontGDI->OriginalWeight = pOS2->usWeightClass; } } else { Error = FT_Get_WinFNT_Header(Face, &WinFNT); if (!Error) { FontGDI->OriginalItalic = !!WinFNT.italic; FontGDI->OriginalWeight = WinFNT.weight; } } IntUnLockFreeType(); /* Entry->FaceName */ RtlInitAnsiString(&AnsiString, Face->family_name); Status = RtlAnsiStringToUnicodeString(&Entry->FaceName, &AnsiString, TRUE); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to allocate Entry->FaceName\n"); CleanupFontEntryEx(Entry, FontGDI); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Finish; /* failure */ } /* Entry->StyleName */ RtlInitUnicodeString(&Entry->StyleName, NULL); if (Face->style_name && Face->style_name[0] && strcmp(Face->style_name, "Regular") != 0) { RtlInitAnsiString(&AnsiString, Face->style_name); Status = RtlAnsiStringToUnicodeString(&Entry->StyleName, &AnsiString, TRUE); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to allocate Entry->StyleName\n"); CleanupFontEntryEx(Entry, FontGDI); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Finish; /* failure */ } } /* FontGDI->CharSet */ if (FT_IS_SFNT(Face)) { FontGDI->CharSet = IntGetCharSet(iCharSet, os2_ulCodePageRange1); } else { IntLockFreeType(); Error = FT_Get_WinFNT_Header(Face, &WinFNT); if (!Error) { FontGDI->CharSet = WinFNT.charset; pLoadFont->CharSet = WinFNT.charset; } IntUnLockFreeType(); } /* Set the file name */ if (pFileName) { // TODO: Since this Filename is common to all the faces+charsets // inside the given font, it may be worth to somehow cache it // only once and share it amongst all these faces+charsets. Length = pFileName->Length + sizeof(UNICODE_NULL); FontGDI->Filename = ExAllocatePoolWithTag(PagedPool, Length, GDITAG_PFF); if (FontGDI->Filename == NULL) { DPRINT1("Failed to allocate FontGDI->Filename\n"); CleanupFontEntryEx(Entry, FontGDI); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Finish; /* failure */ } IntUnicodeStringToBuffer(FontGDI->Filename, Length, pFileName); } else { /* This is a memory font, initialize a suitable entry */ FontGDI->Filename = NULL; PrivateEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_MEM), TAG_FONT); if (!PrivateEntry) { DPRINT1("Failed to allocate PrivateEntry\n"); CleanupFontEntryEx(Entry, FontGDI); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Finish; /* failure */ } PrivateEntry->Entry = Entry; if (pLoadFont->PrivateEntry) { InsertTailList(&pLoadFont->PrivateEntry->ListEntry, &PrivateEntry->ListEntry); } else { InitializeListHead(&PrivateEntry->ListEntry); pLoadFont->PrivateEntry = PrivateEntry; } } /* Add this font resource to the font table */ Entry->Font = FontGDI; Entry->NotEnum = (Characteristics & FR_NOT_ENUM); InsertTailList(&LoadedFontList, &Entry->ListEntry); DPRINT("Font loaded: %s (%s), CharSet %u, Num glyphs %d\n", Face->family_name, Face->style_name, FontGDI->CharSet, Face->num_glyphs); } IntLockFreeType(); /* Error = */ IntRequestFontSize(NULL, FontGDI, 0, 0); IntUnLockFreeType(); /* * Initialize and build the registry font value entry, * only in the case we load fonts from a file and not from memory. */ if (!pFileName) continue; NameLength = Entry->FaceName.Length; if (pValueName->Length == 0) { if (FT_IS_SFNT(Face)) { // L"Name StyleName\0" Length = NameLength + sizeof(L' ') + Entry->StyleName.Length + sizeof(UNICODE_NULL); pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR); if (pszBuffer) { RtlInitEmptyUnicodeString(pValueName, pszBuffer, Length); RtlCopyUnicodeString(pValueName, &Entry->FaceName); if (Entry->StyleName.Length > 0) { RtlAppendUnicodeToString(pValueName, L" "); RtlAppendUnicodeStringToString(pValueName, &Entry->StyleName); } } else { break; /* failure */ } } else { szSize[0] = L' '; _itow(PX2PT(FontGDI->EmHeight), szSize+1, 10); Length = NameLength + (wcslen(szSize) + 1) * sizeof(WCHAR); pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR); if (pszBuffer) { RtlInitEmptyUnicodeString(pValueName, pszBuffer, Length); RtlCopyUnicodeString(pValueName, &Entry->FaceName); RtlAppendUnicodeToString(pValueName, szSize); } else { break; /* failure */ } } } else { if (FT_IS_SFNT(Face)) { // L"... & Name StyleName\0" Length = pValueName->Length + 3 * sizeof(WCHAR) + Entry->FaceName.Length + sizeof(L' ') + Entry->StyleName.Length + sizeof(UNICODE_NULL); pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR); if (pszBuffer) { RtlInitEmptyUnicodeString(&NewString, pszBuffer, Length); RtlCopyUnicodeString(&NewString, pValueName); RtlAppendUnicodeToString(&NewString, L" & "); RtlAppendUnicodeStringToString(&NewString, &Entry->FaceName); if (Entry->StyleName.Length > 0) { RtlAppendUnicodeToString(&NewString, L" "); RtlAppendUnicodeStringToString(&NewString, &Entry->StyleName); } } else { RtlFreeUnicodeString(pValueName); break; /* failure */ } } else { szSize[0] = L','; _itow(PX2PT(FontGDI->EmHeight), szSize+1, 10); Length = pValueName->Length + (wcslen(szSize) + 1) * sizeof(WCHAR); pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR); if (pszBuffer) { RtlInitEmptyUnicodeString(&NewString, pszBuffer, Length); RtlCopyUnicodeString(&NewString, pValueName); RtlAppendUnicodeToString(&NewString, szSize); } else { RtlFreeUnicodeString(pValueName); break; /* failure */ } } RtlFreeUnicodeString(pValueName); *pValueName = NewString; } } Finish: if (iFace == FaceCount) { /* * We succeeded, append the created font entries into the correct font table. */ PLIST_ENTRY ListToAppend; /* No typefaces were present */ if (FaceCount == 0) { ASSERT(IsListEmpty(&LoadedFontList)); return 0; } ASSERT(!IsListEmpty(&LoadedFontList)); /* * Remove the temporary font list' head and reinitialize it. * This effectively empties the list and at the same time transforms * 'ListToAppend' into a headless list, ready to be appended to the * suitable font table. */ ListToAppend = LoadedFontList.Flink; RemoveEntryList(&LoadedFontList); InitializeListHead(&LoadedFontList); if (Characteristics & FR_PRIVATE) { /* Private font */ PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process(); IntLockProcessPrivateFonts(Win32Process); AppendTailList(&Win32Process->PrivateFontListHead, ListToAppend); IntUnLockProcessPrivateFonts(Win32Process); } else { /* Global font */ IntLockGlobalFonts(); AppendTailList(&g_FontListHead, ListToAppend); IntUnLockGlobalFonts(); } return FaceCount; /* Number of loaded faces */ } else { /* We failed, cleanup the resources */ PLIST_ENTRY ListEntry; if (pLoadFont->PrivateEntry) { while (!IsListEmpty(&pLoadFont->PrivateEntry->ListEntry)) { ListEntry = RemoveHeadList(&pLoadFont->PrivateEntry->ListEntry); PrivateEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY_MEM, ListEntry); ExFreePoolWithTag(PrivateEntry, TAG_FONT); } ExFreePoolWithTag(pLoadFont->PrivateEntry, TAG_FONT); pLoadFont->PrivateEntry = NULL; } while (!IsListEmpty(&LoadedFontList)) { ListEntry = RemoveHeadList(&LoadedFontList); Entry = CONTAINING_RECORD(ListEntry, FONT_ENTRY, ListEntry); CleanupFontEntry(Entry); } return 0; /* No faces have been added */ } }