reactos/reactos/subsystems/win32/win32k/objects/text.c
James Tabor 5a3635d248 GetGlyphOutline, large wine port. Still testing.
svn path=/trunk/; revision=22523
2006-06-23 12:29:38 +00:00

3952 lines
104 KiB
C

/*
* ReactOS W32 Subsystem
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
*
* Parts based on Wine code
* Copyright 1993 Alexandre Julliard
* 1997 Alex Korobka
* Copyright 2002,2003 Shachar Shemesh
* Copyright 2001 Huw D M Davies for CodeWeavers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id$ */
#include <w32k.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/tttables.h>
#include <freetype/fttrigon.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/ftwinfnt.h>
#define NDEBUG
#include <debug.h>
FT_Library library;
typedef struct _FONT_ENTRY {
LIST_ENTRY ListEntry;
FONTGDI *Font;
UNICODE_STRING FaceName;
BYTE NotEnum;
} FONT_ENTRY, *PFONT_ENTRY;
/* The FreeType library is not thread safe, so we have
to serialize access to it */
static FAST_MUTEX FreeTypeLock;
static LIST_ENTRY FontListHead;
static FAST_MUTEX FontListLock;
static BOOL RenderingEnabled = TRUE;
static PWCHAR ElfScripts[32] = { /* these are in the order of the fsCsb[0] bits */
L"Western", /*00*/
L"Central_European",
L"Cyrillic",
L"Greek",
L"Turkish",
L"Hebrew",
L"Arabic",
L"Baltic",
L"Vietnamese", /*08*/
NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
L"Thai",
L"Japanese",
L"CHINESE_GB2312",
L"Hangul",
L"CHINESE_BIG5",
L"Hangul(Johab)",
NULL, NULL, /*23*/
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
L"Symbol" /*31*/
};
/*
* For NtGdiTranslateCharsetInfo
*/
#define FS(x) {{0,0,0,0},{0x1<<(x),0}}
#define MAXTCIINDEX 32
static CHARSETINFO FontTci[MAXTCIINDEX] = {
/* ANSI */
{ ANSI_CHARSET, 1252, FS(0)},
{ EASTEUROPE_CHARSET, 1250, FS(1)},
{ RUSSIAN_CHARSET, 1251, FS(2)},
{ GREEK_CHARSET, 1253, FS(3)},
{ TURKISH_CHARSET, 1254, FS(4)},
{ HEBREW_CHARSET, 1255, FS(5)},
{ ARABIC_CHARSET, 1256, FS(6)},
{ BALTIC_CHARSET, 1257, FS(7)},
{ VIETNAMESE_CHARSET, 1258, FS(8)},
/* reserved by ANSI */
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
/* ANSI and OEM */
{ THAI_CHARSET, 874, FS(16)},
{ SHIFTJIS_CHARSET, 932, FS(17)},
{ GB2312_CHARSET, 936, FS(18)},
{ HANGEUL_CHARSET, 949, FS(19)},
{ CHINESEBIG5_CHARSET, 950, FS(20)},
{ JOHAB_CHARSET, 1361, FS(21)},
/* reserved for alternate ANSI and OEM */
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
{ DEFAULT_CHARSET, 0, FS(0)},
/* reserved for system */
{ DEFAULT_CHARSET, 0, FS(0)},
{ SYMBOL_CHARSET, 42 /* CP_SYMBOL */, FS(31)},
};
VOID FASTCALL
IntLoadSystemFonts(VOID);
INT FASTCALL
IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics);
BOOL FASTCALL
InitFontSupport(VOID)
{
ULONG ulError;
InitializeListHead(&FontListHead);
ExInitializeFastMutex(&FontListLock);
ExInitializeFastMutex(&FreeTypeLock);
ulError = FT_Init_FreeType(&library);
if (ulError) {
DPRINT1("FT_Init_FreeType failed with error code 0x%x\n", ulError);
return FALSE;
}
IntLoadSystemFonts();
return TRUE;
}
/*
* IntLoadSystemFonts
*
* Search the system font directory and adds each font found.
*/
VOID FASTCALL
IntLoadSystemFonts(VOID)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING Directory, SearchPattern, FileName, TempString;
IO_STATUS_BLOCK Iosb;
HANDLE hDirectory;
BYTE *DirInfoBuffer;
PFILE_DIRECTORY_INFORMATION DirInfo;
BOOL bRestartScan = TRUE;
NTSTATUS Status;
RtlInitUnicodeString(&Directory, L"\\SystemRoot\\media\\fonts\\");
/* FIXME: Add support for other font types */
RtlInitUnicodeString(&SearchPattern, L"*.ttf");
InitializeObjectAttributes(
&ObjectAttributes,
&Directory,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenFile(
&hDirectory,
SYNCHRONIZE | FILE_LIST_DIRECTORY,
&ObjectAttributes,
&Iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
if (NT_SUCCESS(Status))
{
DirInfoBuffer = ExAllocatePool(PagedPool, 0x4000);
if (DirInfoBuffer == NULL)
{
ZwClose(hDirectory);
return;
}
FileName.Buffer = ExAllocatePool(PagedPool, MAX_PATH * sizeof(WCHAR));
if (FileName.Buffer == NULL)
{
ExFreePool(DirInfoBuffer);
ZwClose(hDirectory);
return;
}
FileName.Length = 0;
FileName.MaximumLength = MAX_PATH * sizeof(WCHAR);
while (1)
{
Status = ZwQueryDirectoryFile(
hDirectory,
NULL,
NULL,
NULL,
&Iosb,
DirInfoBuffer,
0x4000,
FileDirectoryInformation,
FALSE,
&SearchPattern,
bRestartScan);
if (!NT_SUCCESS(Status) || Status == STATUS_NO_MORE_FILES)
{
break;
}
DirInfo = (PFILE_DIRECTORY_INFORMATION)DirInfoBuffer;
while (1)
{
TempString.Buffer = DirInfo->FileName;
TempString.Length =
TempString.MaximumLength = DirInfo->FileNameLength;
RtlCopyUnicodeString(&FileName, &Directory);
RtlAppendUnicodeStringToString(&FileName, &TempString);
IntGdiAddFontResource(&FileName, 0);
if (DirInfo->NextEntryOffset == 0)
break;
DirInfo = (PFILE_DIRECTORY_INFORMATION)((ULONG_PTR)DirInfo + DirInfo->NextEntryOffset);
}
bRestartScan = FALSE;
}
ExFreePool(FileName.Buffer);
ExFreePool(DirInfoBuffer);
ZwClose(hDirectory);
}
}
/*
* IntGdiAddFontResource
*
* Adds the font resource from the specified file to the system.
*/
INT FASTCALL
IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
{
FONTGDI *FontGDI;
NTSTATUS Status;
HANDLE FileHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
PVOID Buffer = NULL;
IO_STATUS_BLOCK Iosb;
INT Error;
FT_Face Face;
ANSI_STRING AnsiFaceName;
PFONT_ENTRY Entry;
PSECTION_OBJECT SectionObject;
ULONG ViewSize = 0;
/* Open the font file */
InitializeObjectAttributes(&ObjectAttributes, FileName, 0, NULL, NULL);
Status = ZwOpenFile(
&FileHandle,
FILE_GENERIC_READ | SYNCHRONIZE,
&ObjectAttributes,
&Iosb,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(Status))
{
DPRINT("Could not font file: %wZ\n", FileName);
return 0;
}
Status = MmCreateSection((PVOID)&SectionObject, SECTION_ALL_ACCESS,
NULL, NULL, PAGE_READONLY,
0, FileHandle, NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("Could not map file: %wZ\n", FileName);
ZwClose(FileHandle);
return 0;
}
ZwClose(FileHandle);
Status = MmMapViewInSystemSpace(SectionObject, &Buffer, &ViewSize);
if (!NT_SUCCESS(Status))
{
DPRINT("Could not map file: %wZ\n", FileName);
return Status;
}
IntLockFreeType;
Error = FT_New_Memory_Face(
library,
Buffer,
ViewSize,
0,
&Face);
IntUnLockFreeType;
if (Error)
{
if (Error == FT_Err_Unknown_File_Format)
DPRINT("Unknown font file format\n");
else
DPRINT("Error reading font file (error code: %u)\n", Error);
ObDereferenceObject(SectionObject);
return 0;
}
Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT);
if (!Entry)
{
FT_Done_Face(Face);
ObDereferenceObject(SectionObject);
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), TAG_FONTOBJ);
if(FontGDI == NULL)
{
FT_Done_Face(Face);
ObDereferenceObject(SectionObject);
ExFreePool(Entry);
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
/* FontGDI->Filename = FileName; perform strcpy */
FontGDI->face = Face;
/* FIXME: Complete text metrics */
FontGDI->TextMetric.tmAscent = (Face->size->metrics.ascender + 32) >> 6; /* units above baseline */
FontGDI->TextMetric.tmDescent = (32 - Face->size->metrics.descender) >> 6; /* units below baseline */
FontGDI->TextMetric.tmHeight = (Face->size->metrics.ascender -
Face->size->metrics.descender) >> 6;
DPRINT("Font loaded: %s (%s)\n", Face->family_name, Face->style_name);
DPRINT("Num glyphs: %u\n", Face->num_glyphs);
/* Add this font resource to the font table */
Entry->Font = FontGDI;
Entry->NotEnum = (Characteristics & FR_NOT_ENUM);
RtlInitAnsiString(&AnsiFaceName, (LPSTR)Face->family_name);
RtlAnsiStringToUnicodeString(&Entry->FaceName, &AnsiFaceName, TRUE);
if (Characteristics & FR_PRIVATE)
{
PW32PROCESS Win32Process = PsGetWin32Process();
IntLockProcessPrivateFonts(Win32Process);
InsertTailList(&Win32Process->PrivateFontListHead, &Entry->ListEntry);
IntUnLockProcessPrivateFonts(Win32Process);
}
else
{
IntLockGlobalFonts;
InsertTailList(&FontListHead, &Entry->ListEntry);
IntUnLockGlobalFonts;
}
return 1;
}
BOOL FASTCALL
IntIsFontRenderingEnabled(VOID)
{
BOOL Ret = RenderingEnabled;
HDC hDC;
hDC = IntGetScreenDC();
if (hDC)
Ret = (NtGdiGetDeviceCaps(hDC, BITSPIXEL) > 8) && RenderingEnabled;
return Ret;
}
VOID FASTCALL
IntEnableFontRendering(BOOL Enable)
{
RenderingEnabled = Enable;
}
FT_Render_Mode FASTCALL
IntGetFontRenderMode(LOGFONTW *logfont)
{
switch(logfont->lfQuality)
{
case NONANTIALIASED_QUALITY:
return FT_RENDER_MODE_MONO;
case DRAFT_QUALITY:
return FT_RENDER_MODE_LIGHT;
/* case CLEARTYPE_QUALITY:
return FT_RENDER_MODE_LCD; */
}
return FT_RENDER_MODE_NORMAL;
}
int
STDCALL
NtGdiAddFontResource(PUNICODE_STRING Filename, DWORD fl)
{
UNICODE_STRING SafeFileName;
PWSTR src;
NTSTATUS Status;
int Ret;
/* Copy the UNICODE_STRING structure */
Status = MmCopyFromCaller(&SafeFileName, Filename, sizeof(UNICODE_STRING));
if(!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return 0;
}
/* Reserve for prepending '\??\' */
SafeFileName.Length += 4 * sizeof(WCHAR);
SafeFileName.MaximumLength += 4 * sizeof(WCHAR);
src = SafeFileName.Buffer;
SafeFileName.Buffer = (PWSTR)ExAllocatePoolWithTag(PagedPool, SafeFileName.MaximumLength, TAG_STRING);
if(!SafeFileName.Buffer)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
/* Prepend '\??\' */
RtlCopyMemory(SafeFileName.Buffer, L"\\??\\", 4 * sizeof(WCHAR));
Status = MmCopyFromCaller(SafeFileName.Buffer + 4, src, SafeFileName.MaximumLength - (4 * sizeof(WCHAR)));
if(!NT_SUCCESS(Status))
{
ExFreePool(SafeFileName.Buffer);
SetLastNtError(Status);
return 0;
}
Ret = IntGdiAddFontResource(&SafeFileName, fl);
ExFreePool(SafeFileName.Buffer);
return Ret;
}
NTSTATUS FASTCALL
TextIntCreateFontIndirect(CONST LPLOGFONTW lf, HFONT *NewFont)
{
PTEXTOBJ TextObj;
NTSTATUS Status = STATUS_SUCCESS;
*NewFont = TEXTOBJ_AllocText();
if (NULL != *NewFont)
{
TextObj = TEXTOBJ_LockText(*NewFont);
if (NULL != TextObj)
{
memcpy(&TextObj->logfont, lf, sizeof(LOGFONTW));
if (lf->lfEscapement != lf->lfOrientation)
{
/* this should really depend on whether GM_ADVANCED is set */
TextObj->logfont.lfOrientation = TextObj->logfont.lfEscapement;
}
TEXTOBJ_UnlockText(TextObj);
}
else
{
/* FIXME */
/* ASSERT(FALSE);*/
Status = STATUS_INVALID_HANDLE;
}
}
else
{
Status = STATUS_NO_MEMORY;
}
return Status;
}
HFONT
STDCALL
NtGdiCreateFont(int Height,
int Width,
int Escapement,
int Orientation,
int Weight,
DWORD Italic,
DWORD Underline,
DWORD StrikeOut,
DWORD CharSet,
DWORD OutputPrecision,
DWORD ClipPrecision,
DWORD Quality,
DWORD PitchAndFamily,
LPCWSTR Face)
{
LOGFONTW logfont;
HFONT NewFont;
NTSTATUS Status = STATUS_SUCCESS;
logfont.lfHeight = Height;
logfont.lfWidth = Width;
logfont.lfEscapement = Escapement;
logfont.lfOrientation = Orientation;
logfont.lfWeight = Weight;
logfont.lfItalic = Italic;
logfont.lfUnderline = Underline;
logfont.lfStrikeOut = StrikeOut;
logfont.lfCharSet = CharSet;
logfont.lfOutPrecision = OutputPrecision;
logfont.lfClipPrecision = ClipPrecision;
logfont.lfQuality = Quality;
logfont.lfPitchAndFamily = PitchAndFamily;
if (NULL != Face)
{
int Size = sizeof(logfont.lfFaceName) / sizeof(WCHAR);
wcsncpy((wchar_t *)logfont.lfFaceName, Face, Size - 1);
/* Be 101% sure to have '\0' at end of string */
logfont.lfFaceName[Size - 1] = '\0';
}
else
{
logfont.lfFaceName[0] = L'\0';
}
if (NT_SUCCESS(Status))
{
Status = TextIntCreateFontIndirect(&logfont, &NewFont);
}
return NT_SUCCESS(Status) ? NewFont : NULL;
}
HFONT
STDCALL
NtGdiCreateFontIndirect(CONST LPLOGFONTW lf)
{
LOGFONTW SafeLogfont;
HFONT NewFont;
NTSTATUS Status = STATUS_SUCCESS;
if (NULL != lf)
{
Status = MmCopyFromCaller(&SafeLogfont, lf, sizeof(LOGFONTW));
if (NT_SUCCESS(Status))
{
Status = TextIntCreateFontIndirect(&SafeLogfont, &NewFont);
}
}
else
{
Status = STATUS_INVALID_PARAMETER;
}
return NT_SUCCESS(Status) ? NewFont : NULL;
}
BOOL
STDCALL
NtGdiCreateScalableFontResource(DWORD Hidden,
LPCWSTR FontRes,
LPCWSTR FontFile,
LPCWSTR CurrentPath)
{
DPRINT1("NtGdiCreateScalableFontResource - is unimplemented, have a nice day and keep going");
return FALSE;
}
/*************************************************************************
* TranslateCharsetInfo
*
* Fills a CHARSETINFO structure for a character set, code page, or
* font. This allows making the correspondance between different labelings
* (character set, Windows, ANSI, and OEM codepages, and Unicode ranges)
* of the same encoding.
*
* Only one codepage will be set in Cs->fs. If TCI_SRCFONTSIG is used,
* only one codepage should be set in *Src.
*
* RETURNS
* TRUE on success, FALSE on failure.
*
*/
static BOOLEAN STDCALL
IntTranslateCharsetInfo(PDWORD Src, /* [in]
if flags == TCI_SRCFONTSIG: pointer to fsCsb of a FONTSIGNATURE
if flags == TCI_SRCCHARSET: a character set value
if flags == TCI_SRCCODEPAGE: a code page value */
LPCHARSETINFO Cs, /* [out] structure to receive charset information */
DWORD Flags /* [in] determines interpretation of lpSrc */)
{
int Index = 0;
switch (Flags)
{
case TCI_SRCFONTSIG:
while (0 == (*Src >> Index & 0x0001) && Index < MAXTCIINDEX)
{
Index++;
}
break;
case TCI_SRCCODEPAGE:
while ((UINT) (Src) != FontTci[Index].ciACP && Index < MAXTCIINDEX)
{
Index++;
}
break;
case TCI_SRCCHARSET:
while ((UINT) (Src) != FontTci[Index].ciCharset && Index < MAXTCIINDEX)
{
Index++;
}
break;
default:
return FALSE;
}
if (MAXTCIINDEX <= Index || DEFAULT_CHARSET == FontTci[Index].ciCharset)
{
return FALSE;
}
memcpy(Cs, &FontTci[Index], sizeof(CHARSETINFO));
return TRUE;
}
BOOL STDCALL
NtGdiTranslateCharsetInfo(PDWORD Src,
LPCHARSETINFO UnsafeCs,
DWORD Flags)
{
CHARSETINFO Cs;
BOOLEAN Ret;
NTSTATUS Status;
Ret = IntTranslateCharsetInfo(Src, &Cs, Flags);
if (Ret)
{
Status = MmCopyToCaller(UnsafeCs, &Cs, sizeof(CHARSETINFO));
if (! NT_SUCCESS(Status))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
return (BOOL) Ret;
}
static void FASTCALL
FillTM(TEXTMETRICW *TM, FT_Face Face, TT_OS2 *pOS2, TT_HoriHeader *pHori)
{
FT_Fixed XScale, YScale;
int Ascent, Descent;
XScale = Face->size->metrics.x_scale;
YScale = Face->size->metrics.y_scale;
if (0 == pOS2->usWinAscent + pOS2->usWinDescent)
{
Ascent = pHori->Ascender;
Descent = -pHori->Descender;
}
else
{
Ascent = pOS2->usWinAscent;
Descent = pOS2->usWinDescent;
}
#if 0 /* This (Wine) code doesn't seem to work correctly for us */
TM->tmAscent = (FT_MulFix(Ascent, YScale) + 32) >> 6;
TM->tmDescent = (FT_MulFix(Descent, YScale) + 32) >> 6;
#else
TM->tmAscent = (Face->size->metrics.ascender + 32) >> 6; /* units above baseline */
TM->tmDescent = (32 - Face->size->metrics.descender) >> 6; /* units below baseline */
#endif
TM->tmInternalLeading = (FT_MulFix(Ascent + Descent
- Face->units_per_EM, YScale) + 32) >> 6;
TM->tmHeight = TM->tmAscent + TM->tmDescent;
/* MSDN says:
* el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
*/
TM->tmExternalLeading = max(0, (FT_MulFix(pHori->Line_Gap
- ((Ascent + Descent)
- (pHori->Ascender - pHori->Descender)),
YScale) + 32) >> 6);
TM->tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, XScale) + 32) >> 6;
if (0 == TM->tmAveCharWidth)
{
TM->tmAveCharWidth = 1;
}
TM->tmMaxCharWidth = (FT_MulFix(Face->bbox.xMax - Face->bbox.xMin,
XScale) + 32) >> 6;
TM->tmWeight = pOS2->usWeightClass;
TM->tmOverhang = 0;
TM->tmDigitizedAspectX = 300;
TM->tmDigitizedAspectY = 300;
TM->tmFirstChar = pOS2->usFirstCharIndex;
TM->tmLastChar = pOS2->usLastCharIndex;
TM->tmDefaultChar = pOS2->usDefaultChar;
TM->tmBreakChar = L'\0' != pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
TM->tmItalic = (Face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0;
TM->tmUnderlined = 0; /* entry in OS2 table */
TM->tmStruckOut = 0; /* entry in OS2 table */
/* Yes TPMF_FIXED_PITCH is correct; braindead api */
if (! FT_IS_FIXED_WIDTH(Face))
{
TM->tmPitchAndFamily = TMPF_FIXED_PITCH;
}
else
{
TM->tmPitchAndFamily = 0;
}
switch (pOS2->panose[PAN_FAMILYTYPE_INDEX])
{
case PAN_FAMILY_SCRIPT:
TM->tmPitchAndFamily |= FF_SCRIPT;
break;
case PAN_FAMILY_DECORATIVE:
case PAN_FAMILY_PICTORIAL:
TM->tmPitchAndFamily |= FF_DECORATIVE;
break;
case PAN_FAMILY_TEXT_DISPLAY:
if (0 == TM->tmPitchAndFamily) /* fixed */
{
TM->tmPitchAndFamily = FF_MODERN;
}
else
{
switch (pOS2->panose[PAN_SERIFSTYLE_INDEX])
{
case PAN_SERIF_NORMAL_SANS:
case PAN_SERIF_OBTUSE_SANS:
case PAN_SERIF_PERP_SANS:
TM->tmPitchAndFamily |= FF_SWISS;
break;
default:
TM->tmPitchAndFamily |= FF_ROMAN;
break;
}
}
break;
default:
TM->tmPitchAndFamily |= FF_DONTCARE;
}
if (FT_IS_SCALABLE(Face))
{
TM->tmPitchAndFamily |= TMPF_VECTOR;
}
if (FT_IS_SFNT(Face))
{
TM->tmPitchAndFamily |= TMPF_TRUETYPE;
}
TM->tmCharSet = DEFAULT_CHARSET;
}
/*************************************************************
* IntGetOutlineTextMetrics
*
*/
static unsigned FASTCALL
IntGetOutlineTextMetrics(PFONTGDI FontGDI, UINT Size,
OUTLINETEXTMETRICW *Otm)
{
unsigned Needed;
TT_OS2 *pOS2;
TT_HoriHeader *pHori;
TT_Postscript *pPost;
FT_Fixed XScale, YScale;
ANSI_STRING FamilyNameA, StyleNameA;
UNICODE_STRING FamilyNameW, StyleNameW, Regular;
char *Cp;
Needed = sizeof(OUTLINETEXTMETRICW);
RtlInitAnsiString(&FamilyNameA, FontGDI->face->family_name);
RtlAnsiStringToUnicodeString(&FamilyNameW, &FamilyNameA, TRUE);
RtlInitAnsiString(&StyleNameA, FontGDI->face->style_name);
RtlAnsiStringToUnicodeString(&StyleNameW, &StyleNameA, TRUE);
/* These names should be read from the TT name table */
/* length of otmpFamilyName */
Needed += FamilyNameW.Length + sizeof(WCHAR);
RtlInitUnicodeString(&Regular, L"regular");
/* length of otmpFaceName */
if (0 == RtlCompareUnicodeString(&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 += StyleNameW.Length + sizeof(WCHAR);
/* length of otmpFullName */
Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1);
if (Size < Needed)
{
RtlFreeUnicodeString(&FamilyNameW);
RtlFreeUnicodeString(&StyleNameW);
return Needed;
}
XScale = FontGDI->face->size->metrics.x_scale;
YScale = FontGDI->face->size->metrics.y_scale;
IntLockFreeType;
pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
if (NULL == pOS2)
{
IntUnLockFreeType;
DPRINT1("Can't find OS/2 table - not TT font?\n");
RtlFreeUnicodeString(&StyleNameW);
RtlFreeUnicodeString(&FamilyNameW);
return 0;
}
pHori = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_hhea);
if (NULL == pHori)
{
IntUnLockFreeType;
DPRINT1("Can't find HHEA table - not TT font?\n");
RtlFreeUnicodeString(&StyleNameW);
RtlFreeUnicodeString(&FamilyNameW);
return 0;
}
pPost = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_post); /* we can live with this failing */
Otm->otmSize = Needed;
FillTM(&Otm->otmTextMetrics, FontGDI->face, pOS2, pHori);
Otm->otmFiller = 0;
memcpy(&Otm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
Otm->otmfsSelection = pOS2->fsSelection;
Otm->otmfsType = pOS2->fsType;
Otm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
Otm->otmsCharSlopeRun = pHori->caret_Slope_Run;
Otm->otmItalicAngle = 0; /* POST table */
Otm->otmEMSquare = FontGDI->face->units_per_EM;
Otm->otmAscent = (FT_MulFix(pOS2->sTypoAscender, YScale) + 32) >> 6;
Otm->otmDescent = (FT_MulFix(pOS2->sTypoDescender, YScale) + 32) >> 6;
Otm->otmLineGap = (FT_MulFix(pOS2->sTypoLineGap, YScale) + 32) >> 6;
Otm->otmsCapEmHeight = (FT_MulFix(pOS2->sCapHeight, YScale) + 32) >> 6;
Otm->otmsXHeight = (FT_MulFix(pOS2->sxHeight, YScale) + 32) >> 6;
Otm->otmrcFontBox.left = (FT_MulFix(FontGDI->face->bbox.xMin, XScale) + 32) >> 6;
Otm->otmrcFontBox.right = (FT_MulFix(FontGDI->face->bbox.xMax, XScale) + 32) >> 6;
Otm->otmrcFontBox.top = (FT_MulFix(FontGDI->face->bbox.yMax, YScale) + 32) >> 6;
Otm->otmrcFontBox.bottom = (FT_MulFix(FontGDI->face->bbox.yMin, YScale) + 32) >> 6;
Otm->otmMacAscent = 0; /* where do these come from ? */
Otm->otmMacDescent = 0;
Otm->otmMacLineGap = 0;
Otm->otmusMinimumPPEM = 0; /* TT Header */
Otm->otmptSubscriptSize.x = (FT_MulFix(pOS2->ySubscriptXSize, XScale) + 32) >> 6;
Otm->otmptSubscriptSize.y = (FT_MulFix(pOS2->ySubscriptYSize, YScale) + 32) >> 6;
Otm->otmptSubscriptOffset.x = (FT_MulFix(pOS2->ySubscriptXOffset, XScale) + 32) >> 6;
Otm->otmptSubscriptOffset.y = (FT_MulFix(pOS2->ySubscriptYOffset, YScale) + 32) >> 6;
Otm->otmptSuperscriptSize.x = (FT_MulFix(pOS2->ySuperscriptXSize, XScale) + 32) >> 6;
Otm->otmptSuperscriptSize.y = (FT_MulFix(pOS2->ySuperscriptYSize, YScale) + 32) >> 6;
Otm->otmptSuperscriptOffset.x = (FT_MulFix(pOS2->ySuperscriptXOffset, XScale) + 32) >> 6;
Otm->otmptSuperscriptOffset.y = (FT_MulFix(pOS2->ySuperscriptYOffset, YScale) + 32) >> 6;
Otm->otmsStrikeoutSize = (FT_MulFix(pOS2->yStrikeoutSize, YScale) + 32) >> 6;
Otm->otmsStrikeoutPosition = (FT_MulFix(pOS2->yStrikeoutPosition, YScale) + 32) >> 6;
if (NULL == pPost)
{
Otm->otmsUnderscoreSize = 0;
Otm->otmsUnderscorePosition = 0;
}
else
{
Otm->otmsUnderscoreSize = (FT_MulFix(pPost->underlineThickness, YScale) + 32) >> 6;
Otm->otmsUnderscorePosition = (FT_MulFix(pPost->underlinePosition, YScale) + 32) >> 6;
}
IntUnLockFreeType;
/* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
Cp = (char*) Otm + sizeof(OUTLINETEXTMETRICW);
Otm->otmpFamilyName = (LPSTR)(Cp - (char*) Otm);
wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
Cp += FamilyNameW.Length + sizeof(WCHAR);
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 (0 != RtlCompareUnicodeString(&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);
}
Otm->otmpFullName = (LPSTR)(Cp - (char*) Otm);
wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
wcscat((WCHAR*) Cp, L" ");
wcscat((WCHAR*) Cp, StyleNameW.Buffer);
RtlFreeUnicodeString(&StyleNameW);
RtlFreeUnicodeString(&FamilyNameW);
return Needed;
}
static PFONTGDI FASTCALL
FindFaceNameInList(PUNICODE_STRING FaceName, PLIST_ENTRY Head)
{
PLIST_ENTRY Entry;
PFONT_ENTRY CurrentEntry;
ANSI_STRING EntryFaceNameA;
UNICODE_STRING EntryFaceNameW;
FONTGDI *FontGDI;
Entry = Head->Flink;
while (Entry != Head)
{
CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
FontGDI = CurrentEntry->Font;
ASSERT(FontGDI);
RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
{
EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
}
if (0 == RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE))
{
RtlFreeUnicodeString(&EntryFaceNameW);
return FontGDI;
}
RtlFreeUnicodeString(&EntryFaceNameW);
Entry = Entry->Flink;
}
return NULL;
}
static PFONTGDI FASTCALL
FindFaceNameInLists(PUNICODE_STRING FaceName)
{
PW32PROCESS Win32Process;
PFONTGDI Font;
/* Search the process local list */
Win32Process = PsGetWin32Process();
IntLockProcessPrivateFonts(Win32Process);
Font = FindFaceNameInList(FaceName, &Win32Process->PrivateFontListHead);
IntUnLockProcessPrivateFonts(Win32Process);
if (NULL != Font)
{
return Font;
}
/* Search the global list */
IntLockGlobalFonts;
Font = FindFaceNameInList(FaceName, &FontListHead);
IntUnLockGlobalFonts;
return Font;
}
static void FASTCALL
FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI)
{
ANSI_STRING StyleA;
UNICODE_STRING StyleW;
TT_OS2 *pOS2;
FONTSIGNATURE fs;
DWORD fs_fsCsb0;
CHARSETINFO CharSetInfo;
unsigned i, Size;
OUTLINETEXTMETRICW *Otm;
LOGFONTW *Lf;
TEXTMETRICW *TM;
NEWTEXTMETRICW *Ntm;
RtlZeroMemory(Info, sizeof(FONTFAMILYINFO));
Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
Otm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
if (NULL == Otm)
{
return;
}
IntGetOutlineTextMetrics(FontGDI, Size, Otm);
Lf = &Info->EnumLogFontEx.elfLogFont;
TM = &Otm->otmTextMetrics;
Lf->lfHeight = TM->tmHeight;
Lf->lfWidth = TM->tmAveCharWidth;
Lf->lfWeight = TM->tmWeight;
Lf->lfItalic = TM->tmItalic;
Lf->lfPitchAndFamily = (TM->tmPitchAndFamily & 0xf1) + 1;
Lf->lfCharSet = TM->tmCharSet;
Lf->lfOutPrecision = OUT_STROKE_PRECIS;
Lf->lfClipPrecision = CLIP_STROKE_PRECIS;
Lf->lfQuality = DRAFT_QUALITY;
Ntm = &Info->NewTextMetricEx.ntmTm;
Ntm->tmHeight = TM->tmHeight;
Ntm->tmAscent = TM->tmAscent;
Ntm->tmDescent = TM->tmDescent;
Ntm->tmInternalLeading = TM->tmInternalLeading;
Ntm->tmExternalLeading = TM->tmExternalLeading;
Ntm->tmAveCharWidth = TM->tmAveCharWidth;
Ntm->tmMaxCharWidth = TM->tmMaxCharWidth;
Ntm->tmWeight = TM->tmWeight;
Ntm->tmOverhang = TM->tmOverhang;
Ntm->tmDigitizedAspectX = TM->tmDigitizedAspectX;
Ntm->tmDigitizedAspectY = TM->tmDigitizedAspectY;
Ntm->tmFirstChar = TM->tmFirstChar;
Ntm->tmLastChar = TM->tmLastChar;
Ntm->tmDefaultChar = TM->tmDefaultChar;
Ntm->tmBreakChar = TM->tmBreakChar;
Ntm->tmItalic = TM->tmItalic;
Ntm->tmUnderlined = TM->tmUnderlined;
Ntm->tmStruckOut = TM->tmStruckOut;
Ntm->tmPitchAndFamily = TM->tmPitchAndFamily;
Ntm->tmCharSet = TM->tmCharSet;
Ntm->ntmFlags = TM->tmItalic ? NTM_ITALIC : 0;
if (550 < TM->tmWeight)
{
Ntm->ntmFlags |= NTM_BOLD;
}
if (0 == Ntm->ntmFlags)
{
Ntm->ntmFlags = NTM_REGULAR;
}
Ntm->ntmSizeEM = Otm->otmEMSquare;
Ntm->ntmCellHeight = 0;
Ntm->ntmAvgWidth = 0;
Info->FontType = (0 != (TM->tmPitchAndFamily & TMPF_TRUETYPE)
? TRUETYPE_FONTTYPE : 0);
if (0 == (TM->tmPitchAndFamily & TMPF_VECTOR))
{
Info->FontType |= RASTER_FONTTYPE;
}
ExFreePool(Otm);
wcsncpy(Info->EnumLogFontEx.elfLogFont.lfFaceName, FaceName, LF_FACESIZE);
wcsncpy(Info->EnumLogFontEx.elfFullName, FaceName, LF_FULLFACESIZE);
RtlInitAnsiString(&StyleA, FontGDI->face->style_name);
RtlAnsiStringToUnicodeString(&StyleW, &StyleA, TRUE);
wcsncpy(Info->EnumLogFontEx.elfStyle, StyleW.Buffer, LF_FACESIZE);
RtlFreeUnicodeString(&StyleW);
Info->EnumLogFontEx.elfLogFont.lfCharSet = DEFAULT_CHARSET;
Info->EnumLogFontEx.elfScript[0] = L'\0';
IntLockFreeType;
pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
IntUnLockFreeType;
if (NULL != pOS2)
{
Info->NewTextMetricEx.ntmFontSig.fsCsb[0] = pOS2->ulCodePageRange1;
Info->NewTextMetricEx.ntmFontSig.fsCsb[1] = pOS2->ulCodePageRange2;
Info->NewTextMetricEx.ntmFontSig.fsUsb[0] = pOS2->ulUnicodeRange1;
Info->NewTextMetricEx.ntmFontSig.fsUsb[1] = pOS2->ulUnicodeRange2;
Info->NewTextMetricEx.ntmFontSig.fsUsb[2] = pOS2->ulUnicodeRange3;
Info->NewTextMetricEx.ntmFontSig.fsUsb[3] = pOS2->ulUnicodeRange4;
fs_fsCsb0 = pOS2->ulCodePageRange1;
if (0 == pOS2->version)
{
FT_UInt Dummy;
if (FT_Get_First_Char(FontGDI->face, &Dummy) < 0x100)
{
fs_fsCsb0 |= 1;
}
else
{
fs_fsCsb0 |= 1L << 31;
}
}
if (0 == fs_fsCsb0)
{ /* let's see if we can find any interesting cmaps */
for (i = 0; i < FontGDI->face->num_charmaps; i++)
{
switch (FontGDI->face->charmaps[i]->encoding)
{
case ft_encoding_unicode:
case ft_encoding_apple_roman:
fs_fsCsb0 |= 1;
break;
case ft_encoding_symbol:
fs_fsCsb0 |= 1L << 31;
break;
default:
break;
}
}
}
for(i = 0; i < 32; i++)
{
if (0 != (fs_fsCsb0 & (1L << i)))
{
fs.fsCsb[0] = 1L << i;
fs.fsCsb[1] = 0;
if (! IntTranslateCharsetInfo(fs.fsCsb, &CharSetInfo, TCI_SRCFONTSIG))
{
CharSetInfo.ciCharset = DEFAULT_CHARSET;
}
if (31 == i)
{
CharSetInfo.ciCharset = SYMBOL_CHARSET;
}
if (DEFAULT_CHARSET != CharSetInfo.ciCharset)
{
Info->EnumLogFontEx.elfLogFont.lfCharSet = CharSetInfo.ciCharset;
if (NULL != ElfScripts[i])
{
wcscpy(Info->EnumLogFontEx.elfScript, ElfScripts[i]);
}
else
{
DPRINT1("Unknown elfscript for bit %d\n", i);
}
}
}
}
}
}
static int FASTCALL
FindFaceNameInInfo(PUNICODE_STRING FaceName, PFONTFAMILYINFO Info, DWORD InfoEntries)
{
DWORD i;
UNICODE_STRING InfoFaceName;
for (i = 0; i < InfoEntries; i++)
{
RtlInitUnicodeString(&InfoFaceName, Info[i].EnumLogFontEx.elfLogFont.lfFaceName);
if (0 == RtlCompareUnicodeString(&InfoFaceName, FaceName, TRUE))
{
return i;
}
}
return -1;
}
static BOOLEAN FASTCALL
FontFamilyInclude(LPLOGFONTW LogFont, PUNICODE_STRING FaceName,
PFONTFAMILYINFO Info, DWORD InfoEntries)
{
UNICODE_STRING LogFontFaceName;
RtlInitUnicodeString(&LogFontFaceName, LogFont->lfFaceName);
if (0 != LogFontFaceName.Length
&& 0 != RtlCompareUnicodeString(&LogFontFaceName, FaceName, TRUE))
{
return FALSE;
}
return FindFaceNameInInfo(FaceName, Info, InfoEntries) < 0;
}
static BOOLEAN FASTCALL
GetFontFamilyInfoForList(LPLOGFONTW LogFont,
PFONTFAMILYINFO Info,
DWORD *Count,
DWORD Size,
PLIST_ENTRY Head)
{
PLIST_ENTRY Entry;
PFONT_ENTRY CurrentEntry;
ANSI_STRING EntryFaceNameA;
UNICODE_STRING EntryFaceNameW;
FONTGDI *FontGDI;
Entry = Head->Flink;
while (Entry != Head)
{
CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
FontGDI = CurrentEntry->Font;
ASSERT(FontGDI);
RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
{
EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
}
if (FontFamilyInclude(LogFont, &EntryFaceNameW, Info, min(*Count, Size)))
{
if (*Count < Size)
{
FontFamilyFillInfo(Info + *Count, EntryFaceNameW.Buffer, FontGDI);
}
(*Count)++;
}
RtlFreeUnicodeString(&EntryFaceNameW);
Entry = Entry->Flink;
}
return TRUE;
}
typedef struct FontFamilyInfoCallbackContext
{
LPLOGFONTW LogFont;
PFONTFAMILYINFO Info;
DWORD Count;
DWORD Size;
} FONT_FAMILY_INFO_CALLBACK_CONTEXT, *PFONT_FAMILY_INFO_CALLBACK_CONTEXT;
static NTSTATUS STDCALL
FontFamilyInfoQueryRegistryCallback(IN PWSTR ValueName, IN ULONG ValueType,
IN PVOID ValueData, IN ULONG ValueLength,
IN PVOID Context, IN PVOID EntryContext)
{
PFONT_FAMILY_INFO_CALLBACK_CONTEXT InfoContext;
UNICODE_STRING RegistryName, RegistryValue;
int Existing;
PFONTGDI FontGDI;
if (REG_SZ != ValueType)
{
return STATUS_SUCCESS;
}
InfoContext = (PFONT_FAMILY_INFO_CALLBACK_CONTEXT) Context;
RtlInitUnicodeString(&RegistryName, ValueName);
/* Do we need to include this font family? */
if (FontFamilyInclude(InfoContext->LogFont, &RegistryName, InfoContext->Info,
min(InfoContext->Count, InfoContext->Size)))
{
RtlInitUnicodeString(&RegistryValue, (PCWSTR) ValueData);
Existing = FindFaceNameInInfo(&RegistryValue, InfoContext->Info,
min(InfoContext->Count, InfoContext->Size));
if (0 <= Existing)
{
/* We already have the information about the "real" font. Just copy it */
if (InfoContext->Count < InfoContext->Size)
{
InfoContext->Info[InfoContext->Count] = InfoContext->Info[Existing];
wcsncpy(InfoContext->Info[InfoContext->Count].EnumLogFontEx.elfLogFont.lfFaceName,
RegistryName.Buffer, LF_FACESIZE);
}
InfoContext->Count++;
return STATUS_SUCCESS;
}
/* Try to find information about the "real" font */
FontGDI = FindFaceNameInLists(&RegistryValue);
if (NULL == FontGDI)
{
/* "Real" font not found, discard this registry entry */
return STATUS_SUCCESS;
}
/* Return info about the "real" font but with the name of the alias */
if (InfoContext->Count < InfoContext->Size)
{
FontFamilyFillInfo(InfoContext->Info + InfoContext->Count,
RegistryName.Buffer, FontGDI);
}
InfoContext->Count++;
return STATUS_SUCCESS;
}
return STATUS_SUCCESS;
}
static BOOLEAN FASTCALL
GetFontFamilyInfoForSubstitutes(LPLOGFONTW LogFont,
PFONTFAMILYINFO Info,
DWORD *Count,
DWORD Size)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
FONT_FAMILY_INFO_CALLBACK_CONTEXT Context;
NTSTATUS Status;
/* Enumerate font families found in HKLM\Software\Microsoft\Windows NT\CurrentVersion\SysFontSubstitutes
The real work is done in the registry callback function */
Context.LogFont = LogFont;
Context.Info = Info;
Context.Count = *Count;
Context.Size = Size;
QueryTable[0].QueryRoutine = FontFamilyInfoQueryRegistryCallback;
QueryTable[0].Flags = 0;
QueryTable[0].Name = NULL;
QueryTable[0].EntryContext = NULL;
QueryTable[0].DefaultType = REG_NONE;
QueryTable[0].DefaultData = NULL;
QueryTable[0].DefaultLength = 0;
QueryTable[1].QueryRoutine = NULL;
QueryTable[1].Name = NULL;
Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
L"SysFontSubstitutes",
QueryTable,
&Context,
NULL);
if (NT_SUCCESS(Status))
{
*Count = Context.Count;
}
return NT_SUCCESS(Status) || STATUS_OBJECT_NAME_NOT_FOUND == Status;
}
int STDCALL
NtGdiGetFontFamilyInfo(HDC Dc,
LPLOGFONTW UnsafeLogFont,
PFONTFAMILYINFO UnsafeInfo,
DWORD Size)
{
NTSTATUS Status;
LOGFONTW LogFont;
PFONTFAMILYINFO Info;
DWORD Count;
PW32PROCESS Win32Process;
/* Make a safe copy */
Status = MmCopyFromCaller(&LogFont, UnsafeLogFont, sizeof(LOGFONTW));
if (! NT_SUCCESS(Status))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return -1;
}
/* Allocate space for a safe copy */
Info = ExAllocatePoolWithTag(PagedPool, Size * sizeof(FONTFAMILYINFO), TAG_GDITEXT);
if (NULL == Info)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return -1;
}
/* Enumerate font families in the global list */
IntLockGlobalFonts;
Count = 0;
if (! GetFontFamilyInfoForList(&LogFont, Info, &Count, Size, &FontListHead) )
{
IntUnLockGlobalFonts;
ExFreePool(Info);
return -1;
}
IntUnLockGlobalFonts;
/* Enumerate font families in the process local list */
Win32Process = PsGetWin32Process();
IntLockProcessPrivateFonts(Win32Process);
if (! GetFontFamilyInfoForList(&LogFont, Info, &Count, Size,
&Win32Process->PrivateFontListHead))
{
IntUnLockProcessPrivateFonts(Win32Process);
ExFreePool(Info);
return -1;
}
IntUnLockProcessPrivateFonts(Win32Process);
/* Enumerate font families in the registry */
if (! GetFontFamilyInfoForSubstitutes(&LogFont, Info, &Count, Size))
{
ExFreePool(Info);
return -1;
}
/* Return data to caller */
if (0 != Count)
{
Status = MmCopyToCaller(UnsafeInfo, Info,
(Count < Size ? Count : Size) * sizeof(FONTFAMILYINFO));
if (! NT_SUCCESS(Status))
{
ExFreePool(Info);
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return -1;
}
}
ExFreePool(Info);
return Count;
}
int
STDCALL
NtGdiEnumFonts(HDC hDC,
LPCWSTR FaceName,
FONTENUMPROCW FontFunc,
LPARAM lParam)
{
UNIMPLEMENTED;
return 0;
}
BOOL STDCALL
NtGdiExtTextOut(
HDC hDC,
INT XStart,
INT YStart,
UINT fuOptions,
CONST RECT *lprc,
LPCWSTR String,
UINT Count,
CONST INT *UnsafeDx)
{
/*
* FIXME:
* Call EngTextOut, which does the real work (calling DrvTextOut where
* appropriate)
*/
DC *dc;
SURFOBJ *SurfObj;
BITMAPOBJ *BitmapObj = NULL;
int error, glyph_index, n, i;
FT_Face face;
FT_GlyphSlot glyph;
LONGLONG TextLeft, RealXStart;
ULONG TextTop, previous, BackgroundLeft;
FT_Bool use_kerning;
RECTL DestRect, MaskRect, SpecifiedDestRect;
POINTL SourcePoint, BrushOrigin;
HBRUSH hBrushFg = NULL;
PGDIBRUSHOBJ BrushFg = NULL;
GDIBRUSHINST BrushFgInst;
HBRUSH hBrushBg = NULL;
PGDIBRUSHOBJ BrushBg = NULL;
GDIBRUSHINST BrushBgInst;
HBITMAP HSourceGlyph;
SURFOBJ *SourceGlyphSurf;
SIZEL bitSize;
FT_CharMap found = 0, charmap;
INT yoff;
FONTOBJ *FontObj;
PFONTGDI FontGDI;
PTEXTOBJ TextObj = NULL;
PPALGDI PalDestGDI;
XLATEOBJ *XlateObj=NULL, *XlateObj2=NULL;
ULONG Mode;
FT_Render_Mode RenderMode;
BOOLEAN Render;
NTSTATUS Status;
INT *Dx = NULL;
POINT Start;
BOOL DoBreak = FALSE;
// TODO: Write test-cases to exactly match real Windows in different
// bad parameters (e.g. does Windows check the DC or the RECT first?).
dc = DC_LockDc(hDC);
if (!dc)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
if (dc->IsIC)
{
DC_UnlockDc(dc);
/* Yes, Windows really returns TRUE in this case */
return TRUE;
}
if (lprc && (fuOptions & (ETO_OPAQUE | ETO_CLIPPED)))
{
// At least one of the two flags were specified. Copy lprc. Once.
Status = MmCopyFromCaller(&SpecifiedDestRect, lprc, sizeof(RECT));
if (!NT_SUCCESS(Status))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return FALSE;
}
IntLPtoDP(dc, (POINT *) &SpecifiedDestRect, 2);
}
if (NULL != UnsafeDx && Count > 0)
{
Dx = ExAllocatePoolWithTag(PagedPool, Count * sizeof(INT), TAG_GDITEXT);
if (NULL == Dx)
{
goto fail;
}
Status = MmCopyFromCaller(Dx, UnsafeDx, Count * sizeof(INT));
if (! NT_SUCCESS(Status))
{
goto fail;
}
}
BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap);
if ( !BitmapObj )
{
goto fail;
}
SurfObj = &BitmapObj->SurfObj;
ASSERT(SurfObj);
Start.x = XStart; Start.y = YStart;
IntLPtoDP(dc, &Start, 1);
RealXStart = (Start.x + dc->w.DCOrgX) << 6;
YStart = Start.y + dc->w.DCOrgY;
/* Create the brushes */
PalDestGDI = PALETTE_LockPalette(dc->w.hPalette);
if ( !PalDestGDI )
Mode = PAL_RGB;
else
{
Mode = PalDestGDI->Mode;
PALETTE_UnlockPalette(PalDestGDI);
}
XlateObj = (XLATEOBJ*)IntEngCreateXlate(Mode, PAL_RGB, dc->w.hPalette, NULL);
if ( !XlateObj )
{
goto fail;
}
hBrushFg = NtGdiCreateSolidBrush(XLATEOBJ_iXlate(XlateObj, dc->w.textColor), 0);
if ( !hBrushFg )
{
goto fail;
}
BrushFg = BRUSHOBJ_LockBrush(hBrushFg);
if ( !BrushFg )
{
goto fail;
}
IntGdiInitBrushInstance(&BrushFgInst, BrushFg, NULL);
if ((fuOptions & ETO_OPAQUE) || dc->w.backgroundMode == OPAQUE)
{
hBrushBg = NtGdiCreateSolidBrush(XLATEOBJ_iXlate(XlateObj, dc->w.backgroundColor), 0);
if ( !hBrushBg )
{
goto fail;
}
BrushBg = BRUSHOBJ_LockBrush(hBrushBg);
if ( !BrushBg )
{
goto fail;
}
IntGdiInitBrushInstance(&BrushBgInst, BrushBg, NULL);
}
XlateObj2 = (XLATEOBJ*)IntEngCreateXlate(PAL_RGB, Mode, NULL, dc->w.hPalette);
if ( !XlateObj2 )
{
goto fail;
}
SourcePoint.x = 0;
SourcePoint.y = 0;
MaskRect.left = 0;
MaskRect.top = 0;
BrushOrigin.x = 0;
BrushOrigin.y = 0;
if ((fuOptions & ETO_OPAQUE) && lprc)
{
DestRect.left = SpecifiedDestRect.left + dc->w.DCOrgX;
DestRect.top = SpecifiedDestRect.top + dc->w.DCOrgY;
DestRect.right = SpecifiedDestRect.right + dc->w.DCOrgX;
DestRect.bottom = SpecifiedDestRect.bottom + dc->w.DCOrgY;
IntLPtoDP(dc, (LPPOINT)&DestRect, 2);
IntEngBitBlt(
&BitmapObj->SurfObj,
NULL,
NULL,
dc->CombinedClip,
NULL,
&DestRect,
&SourcePoint,
&SourcePoint,
&BrushBgInst.BrushObject,
&BrushOrigin,
ROP3_TO_ROP4(PATCOPY));
fuOptions &= ~ETO_OPAQUE;
}
else
{
if (dc->w.backgroundMode == OPAQUE)
{
fuOptions |= ETO_OPAQUE;
}
}
TextObj = TEXTOBJ_LockText(dc->w.hFont);
if(TextObj == NULL)
{
goto fail;
}
FontObj = TextObj->Font;
ASSERT(FontObj);
FontGDI = ObjToGDI(FontObj, FONT);
ASSERT(FontGDI);
IntLockFreeType;
face = FontGDI->face;
if (face->charmap == NULL)
{
DPRINT("WARNING: No charmap selected!\n");
DPRINT("This font face has %d charmaps\n", face->num_charmaps);
for (n = 0; n < face->num_charmaps; n++)
{
charmap = face->charmaps[n];
DPRINT("found charmap encoding: %u\n", charmap->encoding);
if (charmap->encoding != 0)
{
found = charmap;
break;
}
}
if (!found)
{
DPRINT1("WARNING: Could not find desired charmap!\n");
}
error = FT_Set_Charmap(face, found);
if (error)
{
DPRINT1("WARNING: Could not set the charmap!\n");
}
}
Render = IntIsFontRenderingEnabled();
if (Render)
RenderMode = IntGetFontRenderMode(&TextObj->logfont);
else
RenderMode = FT_RENDER_MODE_MONO;
error = FT_Set_Pixel_Sizes(
face,
TextObj->logfont.lfWidth,
/* FIXME should set character height if neg */
(TextObj->logfont.lfHeight < 0 ?
- TextObj->logfont.lfHeight :
TextObj->logfont.lfHeight == 0 ? 11 : TextObj->logfont.lfHeight));
if (error)
{
DPRINT1("Error in setting pixel sizes: %u\n", error);
IntUnLockFreeType;
goto fail;
}
/*
* Process the vertical alignment and determine the yoff.
*/
if (dc->w.textAlign & TA_BASELINE)
yoff = 0;
else if (dc->w.textAlign & TA_BOTTOM)
yoff = -face->size->metrics.descender >> 6;
else /* TA_TOP */
yoff = face->size->metrics.ascender >> 6;
use_kerning = FT_HAS_KERNING(face);
previous = 0;
/*
* Process the horizontal alignment and modify XStart accordingly.
*/
if (dc->w.textAlign & (TA_RIGHT | TA_CENTER))
{
ULONGLONG TextWidth = 0;
LPCWSTR TempText = String;
int Start;
/*
* Calculate width of the text.
*/
if (NULL != Dx)
{
Start = Count < 2 ? 0 : Count - 2;
TextWidth = Count < 2 ? 0 : (Dx[Count - 2] << 6);
}
else
{
Start = 0;
}
TempText = String + Start;
for (i = Start; i < Count; i++)
{
glyph_index = FT_Get_Char_Index(face, *TempText);
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error)
{
DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
}
glyph = face->glyph;
/* retrieve kerning distance */
if (use_kerning && previous && glyph_index)
{
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
TextWidth += delta.x;
}
TextWidth += glyph->advance.x;
previous = glyph_index;
TempText++;
}
previous = 0;
if (dc->w.textAlign & TA_RIGHT)
{
RealXStart -= TextWidth;
}
else
{
RealXStart -= TextWidth / 2;
}
}
TextLeft = RealXStart;
TextTop = YStart;
BackgroundLeft = (RealXStart + 32) >> 6;
/*
* The main rendering loop.
*/
for (i = 0; i < Count; i++)
{
glyph_index = FT_Get_Char_Index(face, *String);
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error)
{
DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
IntUnLockFreeType;
goto fail;
}
glyph = face->glyph;
/* retrieve kerning distance and move pen position */
if (use_kerning && previous && glyph_index && NULL == Dx)
{
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
TextLeft += delta.x;
}
if (glyph->format == ft_glyph_format_outline)
{
error = FT_Render_Glyph(glyph, RenderMode);
if (error)
{
DPRINT1("WARNING: Failed to render glyph!\n");
goto fail;
}
}
if (fuOptions & ETO_OPAQUE)
{
DestRect.left = BackgroundLeft;
DestRect.right = (TextLeft + glyph->advance.x + 32) >> 6;
DestRect.top = TextTop + yoff - ((face->size->metrics.ascender + 32) >> 6);
DestRect.bottom = TextTop + yoff + ((32 - face->size->metrics.descender) >> 6);
IntEngBitBlt(
&BitmapObj->SurfObj,
NULL,
NULL,
dc->CombinedClip,
NULL,
&DestRect,
&SourcePoint,
&SourcePoint,
&BrushBgInst.BrushObject,
&BrushOrigin,
ROP3_TO_ROP4(PATCOPY));
BackgroundLeft = DestRect.right;
}
DestRect.left = ((TextLeft + 32) >> 6) + glyph->bitmap_left;
DestRect.right = DestRect.left + glyph->bitmap.width;
DestRect.top = TextTop + yoff - glyph->bitmap_top;
DestRect.bottom = DestRect.top + glyph->bitmap.rows;
bitSize.cx = glyph->bitmap.width;
bitSize.cy = glyph->bitmap.rows;
MaskRect.right = glyph->bitmap.width;
MaskRect.bottom = glyph->bitmap.rows;
/*
* We should create the bitmap out of the loop at the biggest possible
* glyph size. Then use memset with 0 to clear it and sourcerect to
* limit the work of the transbitblt.
*
* FIXME: DIB bitmaps should have an lDelta which is a multiple of 4.
* Here we pass in the pitch from the FreeType bitmap, which is not
* guaranteed to be a multiple of 4. If it's not, we should expand
* the FreeType bitmap to a temporary bitmap.
*/
HSourceGlyph = EngCreateBitmap(bitSize, glyph->bitmap.pitch,
(glyph->bitmap.pixel_mode == ft_pixel_mode_grays) ?
BMF_8BPP : BMF_1BPP, BMF_TOPDOWN,
glyph->bitmap.buffer);
if ( !HSourceGlyph )
{
DPRINT1("WARNING: EngLockSurface() failed!\n");
IntUnLockFreeType;
goto fail;
}
SourceGlyphSurf = EngLockSurface((HSURF)HSourceGlyph);
if ( !SourceGlyphSurf )
{
EngDeleteSurface((HSURF)HSourceGlyph);
DPRINT1("WARNING: EngLockSurface() failed!\n");
IntUnLockFreeType;
goto fail;
}
/*
* Use the font data as a mask to paint onto the DCs surface using a
* brush.
*/
if (lprc &&
(fuOptions & ETO_CLIPPED) &&
DestRect.right >= SpecifiedDestRect.right + dc->w.DCOrgX)
{
// We do the check '>=' instead of '>' to possibly save an iteration
// through this loop, since it's breaking after the drawing is done,
// and x is always incremented.
DestRect.right = SpecifiedDestRect.right + dc->w.DCOrgX;
DoBreak = TRUE;
}
IntEngMaskBlt(
SurfObj,
SourceGlyphSurf,
dc->CombinedClip,
XlateObj,
XlateObj2,
&DestRect,
&SourcePoint,
(PPOINTL)&MaskRect,
&BrushFgInst.BrushObject,
&BrushOrigin);
EngUnlockSurface(SourceGlyphSurf);
EngDeleteSurface((HSURF)HSourceGlyph);
if (DoBreak)
{
break;
}
if (NULL == Dx)
{
TextLeft += glyph->advance.x;
}
else
{
TextLeft += Dx[i] << 6;
}
previous = glyph_index;
String++;
}
IntUnLockFreeType;
EngDeleteXlate(XlateObj);
EngDeleteXlate(XlateObj2);
BITMAPOBJ_UnlockBitmap(BitmapObj);
if(TextObj != NULL)
TEXTOBJ_UnlockText(TextObj);
if (hBrushBg != NULL)
{
BRUSHOBJ_UnlockBrush(BrushBg);
NtGdiDeleteObject(hBrushBg);
}
BRUSHOBJ_UnlockBrush(BrushFg);
NtGdiDeleteObject(hBrushFg);
if (NULL != Dx)
{
ExFreePool(Dx);
}
DC_UnlockDc( dc );
return TRUE;
fail:
if ( XlateObj2 != NULL )
EngDeleteXlate(XlateObj2);
if ( XlateObj != NULL )
EngDeleteXlate(XlateObj);
if(TextObj != NULL)
TEXTOBJ_UnlockText(TextObj);
BITMAPOBJ_UnlockBitmap(BitmapObj);
if (hBrushBg != NULL)
{
BRUSHOBJ_UnlockBrush(BrushBg);
NtGdiDeleteObject(hBrushBg);
}
if (hBrushFg != NULL)
{
BRUSHOBJ_UnlockBrush(BrushFg);
NtGdiDeleteObject(hBrushFg);
}
if (NULL != Dx)
{
ExFreePool(Dx);
}
DC_UnlockDc(dc);
return FALSE;
}
BOOL
STDCALL
NtGdiGetAspectRatioFilterEx(HDC hDC,
LPSIZE AspectRatio)
{
UNIMPLEMENTED;
return FALSE;
}
BOOL
STDCALL
NtGdiGetCharABCWidths(HDC hDC,
UINT FirstChar,
UINT LastChar,
LPABC abc)
{
LPABC SafeBuffer;
PDC dc;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
FT_Face face;
FT_CharMap charmap, found = NULL;
UINT i, glyph_index, BufferSize;
HFONT hFont = 0;
NTSTATUS Status;
if (LastChar < FirstChar)
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return FALSE;
}
BufferSize = (LastChar - FirstChar + 1) * sizeof(ABC);
SafeBuffer = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_GDITEXT);
if (SafeBuffer == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
dc = DC_LockDc(hDC);
if (dc == NULL)
{
ExFreePool(SafeBuffer);
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
hFont = dc->w.hFont;
TextObj = TEXTOBJ_LockText(hFont);
DC_UnlockDc(dc);
if (TextObj == NULL)
{
ExFreePool(SafeBuffer);
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
face = FontGDI->face;
if (face->charmap == NULL)
{
for (i = 0; i < face->num_charmaps; i++)
{
charmap = face->charmaps[i];
if (charmap->encoding != 0)
{
found = charmap;
break;
}
}
if (!found)
{
DPRINT1("WARNING: Could not find desired charmap!\n");
ExFreePool(SafeBuffer);
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
IntLockFreeType;
FT_Set_Charmap(face, found);
IntUnLockFreeType;
}
IntLockFreeType;
FT_Set_Pixel_Sizes(face,
TextObj->logfont.lfWidth,
/* FIXME should set character height if neg */
(TextObj->logfont.lfHeight < 0 ? - TextObj->logfont.lfHeight :
TextObj->logfont.lfHeight == 0 ? 11 : TextObj->logfont.lfHeight));
for (i = FirstChar; i <= LastChar; i++)
{
int adv, lsb, bbx, left, right;
glyph_index = FT_Get_Char_Index(face, i);
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
left = (INT)face->glyph->metrics.horiBearingX & -64;
right = (INT)((face->glyph->metrics.horiBearingX + face->glyph->metrics.width) + 63) & -64;
adv = (face->glyph->advance.x + 32) >> 6;
// int test = (INT)(face->glyph->metrics.horiAdvance + 63) >> 6;
// DPRINT1("Advance Wine %d and Advance Ros %d\n",test, adv ); /* It's the same!*/
lsb = left >> 6;
bbx = (right - left) >> 6;
/*
DPRINT1("lsb %d and bbx %d\n", lsb, bbx );
*/
SafeBuffer[i - FirstChar].abcA = lsb;
SafeBuffer[i - FirstChar].abcB = bbx;
SafeBuffer[i - FirstChar].abcC = adv - lsb - bbx;
}
IntUnLockFreeType;
TEXTOBJ_UnlockText(TextObj);
Status = MmCopyToCaller(abc, SafeBuffer, BufferSize);
if (! NT_SUCCESS(Status))
{
SetLastNtError(Status);
ExFreePool(SafeBuffer);
return FALSE;
}
ExFreePool(SafeBuffer);
DPRINT("NtGdiGetCharABCWidths Worked!\n");
return TRUE;
}
BOOL
STDCALL
NtGdiGetCharABCWidthsFloat(HDC hDC,
UINT FirstChar,
UINT LastChar,
LPABCFLOAT abcF)
{
UNIMPLEMENTED;
return FALSE;
}
DWORD
STDCALL
NtGdiGetCharacterPlacement(HDC hDC,
LPCWSTR String,
int Count,
int MaxExtent,
LPGCP_RESULTSW Results,
DWORD Flags)
{
UNIMPLEMENTED;
return 0;
}
BOOL
STDCALL
NtGdiGetCharWidth32(HDC hDC,
UINT FirstChar,
UINT LastChar,
LPINT Buffer)
{
LPINT SafeBuffer;
PDC dc;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
FT_Face face;
FT_CharMap charmap, found = NULL;
UINT i, glyph_index, BufferSize;
HFONT hFont = 0;
if (LastChar < FirstChar)
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return FALSE;
}
BufferSize = (LastChar - FirstChar + 1) * sizeof(INT);
SafeBuffer = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_GDITEXT);
if (SafeBuffer == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
dc = DC_LockDc(hDC);
if (dc == NULL)
{
ExFreePool(SafeBuffer);
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
hFont = dc->w.hFont;
TextObj = TEXTOBJ_LockText(hFont);
DC_UnlockDc(dc);
if (TextObj == NULL)
{
ExFreePool(SafeBuffer);
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
face = FontGDI->face;
if (face->charmap == NULL)
{
for (i = 0; i < face->num_charmaps; i++)
{
charmap = face->charmaps[i];
if (charmap->encoding != 0)
{
found = charmap;
break;
}
}
if (!found)
{
DPRINT1("WARNING: Could not find desired charmap!\n");
ExFreePool(SafeBuffer);
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
IntLockFreeType;
FT_Set_Charmap(face, found);
IntUnLockFreeType;
}
IntLockFreeType;
FT_Set_Pixel_Sizes(face,
TextObj->logfont.lfWidth,
/* FIXME should set character height if neg */
(TextObj->logfont.lfHeight < 0 ?
- TextObj->logfont.lfHeight :
TextObj->logfont.lfHeight == 0 ? 11 : TextObj->logfont.lfHeight));
for (i = FirstChar; i <= LastChar; i++)
{
glyph_index = FT_Get_Char_Index(face, i);
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
SafeBuffer[i - FirstChar] = (face->glyph->advance.x + 32) >> 6;
}
IntUnLockFreeType;
TEXTOBJ_UnlockText(TextObj);
MmCopyToCaller(Buffer, SafeBuffer, BufferSize);
ExFreePool(SafeBuffer);
return TRUE;
}
BOOL
STDCALL
NtGdiGetCharWidthFloat(HDC hDC,
UINT FirstChar,
UINT LastChar,
PFLOAT Buffer)
{
UNIMPLEMENTED;
return FALSE;
}
DWORD
STDCALL
NtGdiGetFontLanguageInfo(HDC hDC)
{
UNIMPLEMENTED;
return 0;
}
static
void
FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
{
pt->x.value = vec->x >> 6;
pt->x.fract = (vec->x & 0x3f) << 10;
pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
pt->y.value = vec->y >> 6;
pt->y.fract = (vec->y & 0x3f) << 10;
pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
return;
}
/*
This function builds an FT_Fixed from a float. It puts the integer part
in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed.
It fails if the integer part of the float number is greater than SHORT_MAX.
*/
static inline FT_Fixed FT_FixedFromFloat(float f)
{
short value = f;
unsigned short fract = (f - value) * 0xFFFF;
return (FT_Fixed)((long)value << 16 | (unsigned long)fract);
}
/*
This function builds an FT_Fixed from a FIXED. It simply put f.value
in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
*/
static inline FT_Fixed FT_FixedFromFIXED(FIXED f)
{
return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
}
/*
* Based on WineEngGetGlyphOutline
*
*/
ULONG
APIENTRY
NtGdiGetGlyphOutline(
IN HDC hdc,
IN WCHAR wch,
IN UINT iFormat,
OUT LPGLYPHMETRICS pgm,
IN ULONG cjBuf,
OUT OPTIONAL PVOID UnsafeBuf,
IN LPMAT2 pmat2,
IN BOOL bIgnoreRotation)
{
static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
PDC dc;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
HFONT hFont = 0;
NTSTATUS Status;
GLYPHMETRICS gm;
ULONG Size;
FT_Face ft_face;
FT_UInt glyph_index;
DWORD width, height, pitch, needed = 0;
FT_Bitmap ft_bitmap;
FT_Error error;
INT left, right, top = 0, bottom = 0;
FT_Angle angle = 0;
FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
FLOAT eM11, widthRatio = 1.0;
FT_Matrix transMat = identityMat;
BOOL needsTransform = FALSE;
INT orientation;
LONG aveWidth;
INT adv, lsb, bbx; /* These three hold to widths of the unrotated chars */
OUTLINETEXTMETRICW *potm;
PVOID pvBuf = NULL;
DPRINT("%p, %d, %08x, %p, %08lx, %p, %p\n", hdc, wch, iFormat, pgm,
cjBuf, UnsafeBuf, pmat2);
dc = DC_LockDc(hdc);
if (!dc)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
eM11 = dc->w.xformWorld2Vport.eM11;
hFont = dc->w.hFont;
TextObj = TEXTOBJ_LockText(hFont);
DC_UnlockDc(dc);
if (!TextObj)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
ft_face = FontGDI->face;
aveWidth = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.lfWidth: 0;
orientation = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.lfOrientation: 0;
Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
potm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
if (!potm)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
TEXTOBJ_UnlockText(TextObj);
return GDI_ERROR;
}
IntGetOutlineTextMetrics(FontGDI, Size, potm);
IntLockFreeType;
/* During testing, I never saw this used. In here just incase.*/
if (ft_face->charmap == NULL)
{
DPRINT("WARNING: No charmap selected!\n");
DPRINT("This font face has %d charmaps\n", ft_face->num_charmaps);
int n;
FT_CharMap found = 0, charmap;
for (n = 0; n < ft_face->num_charmaps; n++)
{
charmap = ft_face->charmaps[n];
DPRINT("found charmap encoding: %u\n", charmap->encoding);
if (charmap->encoding != 0)
{
found = charmap;
break;
}
}
if (!found)
{
DPRINT1("WARNING: Could not find desired charmap!\n");
}
error = FT_Set_Charmap(ft_face, found);
if (error)
{
DPRINT1("WARNING: Could not set the charmap!\n");
}
}
// FT_Set_Pixel_Sizes(ft_face,
// TextObj->logfont.lfWidth,
/* FIXME should set character height if neg */
// (TextObj->logfont.lfHeight < 0 ? - TextObj->logfont.lfHeight :
// TextObj->logfont.lfHeight == 0 ? 11 : TextObj->logfont.lfHeight));
TEXTOBJ_UnlockText(TextObj);
if (iFormat & GGO_GLYPH_INDEX)
{
glyph_index = wch;
iFormat &= ~GGO_GLYPH_INDEX;
}
else glyph_index = FT_Get_Char_Index(ft_face, wch);
if (orientation || (iFormat != GGO_METRICS && iFormat != GGO_BITMAP) || aveWidth || pmat2)
load_flags |= FT_LOAD_NO_BITMAP;
error = FT_Load_Glyph(ft_face, glyph_index, load_flags);
if (error)
{
DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
IntUnLockFreeType;
if (potm) ExFreePool(potm);
return GDI_ERROR;
}
IntUnLockFreeType;
if (aveWidth && potm)
{
widthRatio = (FLOAT)aveWidth * eM11 /
(FLOAT) potm->otmTextMetrics.tmAveCharWidth;
}
left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
right = (INT)((ft_face->glyph->metrics.horiBearingX +
ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
lsb = left >> 6;
bbx = (right - left) >> 6;
DPRINT("Advance = %d, lsb = %d, bbx = %d\n",adv, lsb, bbx);
IntLockFreeType;
/* Scaling transform */
if (aveWidth)
{
FT_Matrix scaleMat;
DPRINT("Scaling Trans!\n");
scaleMat.xx = FT_FixedFromFloat(widthRatio);
scaleMat.xy = 0;
scaleMat.yx = 0;
scaleMat.yy = (1 << 16);
FT_Matrix_Multiply(&scaleMat, &transMat);
needsTransform = TRUE;
}
/* Slant transform */
if (potm->otmTextMetrics.tmItalic)
{
FT_Matrix slantMat;
DPRINT("Slant Trans!\n");
slantMat.xx = (1 << 16);
slantMat.xy = ((1 << 16) >> 2);
slantMat.yx = 0;
slantMat.yy = (1 << 16);
FT_Matrix_Multiply(&slantMat, &transMat);
needsTransform = TRUE;
}
/* Rotation transform */
if (orientation)
{
FT_Matrix rotationMat;
FT_Vector vecAngle;
DPRINT("Rotation Trans!\n");
angle = FT_FixedFromFloat((float)orientation / 10.0);
FT_Vector_Unit(&vecAngle, angle);
rotationMat.xx = vecAngle.x;
rotationMat.xy = -vecAngle.y;
rotationMat.yx = -rotationMat.xy;
rotationMat.yy = rotationMat.xx;
FT_Matrix_Multiply(&rotationMat, &transMat);
needsTransform = TRUE;
}
/* Extra transformation specified by caller */
if (pmat2)
{
FT_Matrix extraMat;
DPRINT("MAT2 Matrix Trans!\n");
extraMat.xx = FT_FixedFromFIXED(pmat2->eM11);
extraMat.xy = FT_FixedFromFIXED(pmat2->eM21);
extraMat.yx = FT_FixedFromFIXED(pmat2->eM12);
extraMat.yy = FT_FixedFromFIXED(pmat2->eM22);
FT_Matrix_Multiply(&extraMat, &transMat);
needsTransform = TRUE;
}
if (potm) ExFreePool(potm); /* It looks like we are finished with potm ATM.*/
if (!needsTransform)
{
DPRINT("No Need to be Transformed!\n");
top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
bottom = (ft_face->glyph->metrics.horiBearingY -
ft_face->glyph->metrics.height) & -64;
gm.gmCellIncX = adv;
gm.gmCellIncY = 0;
}
else
{
INT xc, yc;
FT_Vector vec;
for(xc = 0; xc < 2; xc++)
{
for(yc = 0; yc < 2; yc++)
{
vec.x = (ft_face->glyph->metrics.horiBearingX +
xc * ft_face->glyph->metrics.width);
vec.y = ft_face->glyph->metrics.horiBearingY -
yc * ft_face->glyph->metrics.height;
DPRINT("Vec %ld,%ld\n", vec.x, vec.y);
FT_Vector_Transform(&vec, &transMat);
if(xc == 0 && yc == 0)
{
left = right = vec.x;
top = bottom = vec.y;
}
else
{
if(vec.x < left) left = vec.x;
else if(vec.x > right) right = vec.x;
if(vec.y < bottom) bottom = vec.y;
else if(vec.y > top) top = vec.y;
}
}
}
left = left & -64;
right = (right + 63) & -64;
bottom = bottom & -64;
top = (top + 63) & -64;
DPRINT("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
vec.x = ft_face->glyph->metrics.horiAdvance;
vec.y = 0;
FT_Vector_Transform(&vec, &transMat);
gm.gmCellIncX = (vec.x+63) >> 6;
gm.gmCellIncY = -((vec.y+63) >> 6);
}
gm.gmBlackBoxX = (right - left) >> 6;
gm.gmBlackBoxY = (top - bottom) >> 6;
gm.gmptGlyphOrigin.x = left >> 6;
gm.gmptGlyphOrigin.y = top >> 6;
DPRINT("CX %d CY %d BBX %d BBY %d GOX %d GOY %d\n",
gm.gmCellIncX, gm.gmCellIncY,
gm.gmBlackBoxX, gm.gmBlackBoxY,
gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
IntUnLockFreeType;
if (pgm)
{
Status = MmCopyToCaller(pgm, &gm, sizeof(GLYPHMETRICS));
if (! NT_SUCCESS(Status))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return GDI_ERROR;
}
DPRINT("Copied GLYPHMETRICS to User!\n");
}
if (iFormat == GGO_METRICS)
{
DPRINT("GGO_METRICS Exit!\n");
return 1; /* FIXME */
}
if (ft_face->glyph->format != ft_glyph_format_outline && iFormat != GGO_BITMAP)
{
DPRINT1("loaded a bitmap\n");
return GDI_ERROR;
}
if (UnsafeBuf && cjBuf)
{
pvBuf = ExAllocatePoolWithTag(PagedPool, cjBuf, TAG_GDITEXT);
if (pvBuf == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return GDI_ERROR;
}
RtlZeroMemory(pvBuf, cjBuf);
}
switch(iFormat)
{
case GGO_BITMAP:
width = gm.gmBlackBoxX;
height = gm.gmBlackBoxY;
pitch = ((width + 31) >> 5) << 2;
needed = pitch * height;
if(!pvBuf || !cjBuf) break;
switch(ft_face->glyph->format)
{
case ft_glyph_format_bitmap:
{
BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
INT h = ft_face->glyph->bitmap.rows;
while(h--)
{
RtlCopyMemory(dst, src, w);
src += ft_face->glyph->bitmap.pitch;
dst += pitch;
}
break;
}
case ft_glyph_format_outline:
ft_bitmap.width = width;
ft_bitmap.rows = height;
ft_bitmap.pitch = pitch;
ft_bitmap.pixel_mode = ft_pixel_mode_mono;
ft_bitmap.buffer = pvBuf;
IntLockFreeType;
if(needsTransform)
{
FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
}
FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
/* Note: FreeType will only set 'black' bits for us. */
RtlZeroMemory(pvBuf, needed);
FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
IntUnLockFreeType;
break;
default:
DPRINT1("loaded glyph format %x\n", ft_face->glyph->format);
if(pvBuf) ExFreePool(pvBuf);
return GDI_ERROR;
}
break;
case GGO_GRAY2_BITMAP:
case GGO_GRAY4_BITMAP:
case GGO_GRAY8_BITMAP:
{
unsigned int mult, row, col;
BYTE *start, *ptr;
width = gm.gmBlackBoxX;
height = gm.gmBlackBoxY;
pitch = (width + 3) / 4 * 4;
needed = pitch * height;
if(!pvBuf || !cjBuf) break;
ft_bitmap.width = width;
ft_bitmap.rows = height;
ft_bitmap.pitch = pitch;
ft_bitmap.pixel_mode = ft_pixel_mode_grays;
ft_bitmap.buffer = pvBuf;
IntLockFreeType;
if(needsTransform)
{
FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
}
FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
RtlZeroMemory(ft_bitmap.buffer, cjBuf);
FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
IntUnLockFreeType;
if(iFormat == GGO_GRAY2_BITMAP)
mult = 4;
else if(iFormat == GGO_GRAY4_BITMAP)
mult = 16;
else if(iFormat == GGO_GRAY8_BITMAP)
mult = 64;
else
{
ASSERT(0);
break;
}
start = pvBuf;
for(row = 0; row < height; row++)
{
ptr = start;
for(col = 0; col < width; col++, ptr++)
{
*ptr = (((int)*ptr) * mult + 128) / 256;
}
start += pitch;
}
break;
}
case GGO_NATIVE:
{
int contour, point = 0, first_pt;
FT_Outline *outline = &ft_face->glyph->outline;
TTPOLYGONHEADER *pph;
TTPOLYCURVE *ppc;
DWORD pph_start, cpfx, type;
if(cjBuf == 0) pvBuf = NULL; /* This is okay, need cjBuf to allocate. */
IntLockFreeType;
if (needsTransform && pvBuf) FT_Outline_Transform(outline, &transMat);
for(contour = 0; contour < outline->n_contours; contour++)
{
pph_start = needed;
pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed);
first_pt = point;
if(pvBuf)
{
pph->dwType = TT_POLYGON_TYPE;
FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
}
needed += sizeof(*pph);
point++;
while(point <= outline->contours[contour])
{
ppc = (TTPOLYCURVE *)((char *)pvBuf + needed);
type = (outline->tags[point] & FT_Curve_Tag_On) ?
TT_PRIM_LINE : TT_PRIM_QSPLINE;
cpfx = 0;
do
{
if(pvBuf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
} while(point <= outline->contours[contour] &&
(outline->tags[point] & FT_Curve_Tag_On) ==
(outline->tags[point-1] & FT_Curve_Tag_On));
/* At the end of a contour Windows adds the start point, but
only for Beziers */
if(point > outline->contours[contour] &&
!(outline->tags[point-1] & FT_Curve_Tag_On))
{
if(pvBuf)
FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
cpfx++;
}
else if(point <= outline->contours[contour] &&
outline->tags[point] & FT_Curve_Tag_On)
{
/* add closing pt for bezier */
if(pvBuf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
}
if(pvBuf)
{
ppc->wType = type;
ppc->cpfx = cpfx;
}
needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
}
if(pvBuf) pph->cb = needed - pph_start;
}
IntUnLockFreeType;
break;
}
case GGO_BEZIER:
{
/* Convert the quadratic Beziers to cubic Beziers.
The parametric eqn for a cubic Bezier is, from PLRM:
r(t) = at^3 + bt^2 + ct + r0
with the control points:
r1 = r0 + c/3
r2 = r1 + (c + b)/3
r3 = r0 + c + b + a
A quadratic Beizer has the form:
p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
So equating powers of t leads to:
r1 = 2/3 p1 + 1/3 p0
r2 = 2/3 p1 + 1/3 p2
and of course r0 = p0, r3 = p2
*/
int contour, point = 0, first_pt;
FT_Outline *outline = &ft_face->glyph->outline;
TTPOLYGONHEADER *pph;
TTPOLYCURVE *ppc;
DWORD pph_start, cpfx, type;
FT_Vector cubic_control[4];
if(cjBuf == 0) pvBuf = NULL;
if (needsTransform && pvBuf)
{
IntLockFreeType;
FT_Outline_Transform(outline, &transMat);
IntUnLockFreeType;
}
for(contour = 0; contour < outline->n_contours; contour++)
{
pph_start = needed;
pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed);
first_pt = point;
if(pvBuf)
{
pph->dwType = TT_POLYGON_TYPE;
FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
}
needed += sizeof(*pph);
point++;
while(point <= outline->contours[contour])
{
ppc = (TTPOLYCURVE *)((char *)pvBuf + needed);
type = (outline->tags[point] & FT_Curve_Tag_On) ?
TT_PRIM_LINE : TT_PRIM_CSPLINE;
cpfx = 0;
do
{
if(type == TT_PRIM_LINE)
{
if(pvBuf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
}
else
{
/* Unlike QSPLINEs, CSPLINEs always have their endpoint
so cpfx = 3n */
/* FIXME: Possible optimization in endpoint calculation
if there are two consecutive curves */
cubic_control[0] = outline->points[point-1];
if(!(outline->tags[point-1] & FT_Curve_Tag_On))
{
cubic_control[0].x += outline->points[point].x + 1;
cubic_control[0].y += outline->points[point].y + 1;
cubic_control[0].x >>= 1;
cubic_control[0].y >>= 1;
}
if(point+1 > outline->contours[contour])
cubic_control[3] = outline->points[first_pt];
else
{
cubic_control[3] = outline->points[point+1];
if(!(outline->tags[point+1] & FT_Curve_Tag_On))
{
cubic_control[3].x += outline->points[point].x + 1;
cubic_control[3].y += outline->points[point].y + 1;
cubic_control[3].x >>= 1;
cubic_control[3].y >>= 1;
}
}
/* r1 = 1/3 p0 + 2/3 p1
r2 = 1/3 p2 + 2/3 p1 */
cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
cubic_control[2] = cubic_control[1];
cubic_control[1].x += (cubic_control[0].x + 1) / 3;
cubic_control[1].y += (cubic_control[0].y + 1) / 3;
cubic_control[2].x += (cubic_control[3].x + 1) / 3;
cubic_control[2].y += (cubic_control[3].y + 1) / 3;
if(pvBuf)
{
FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
}
cpfx += 3;
point++;
}
}
while(point <= outline->contours[contour] &&
(outline->tags[point] & FT_Curve_Tag_On) ==
(outline->tags[point-1] & FT_Curve_Tag_On));
/* At the end of a contour Windows adds the start point,
but only for Beziers and we've already done that.
*/
if(point <= outline->contours[contour] &&
outline->tags[point] & FT_Curve_Tag_On)
{
/* This is the closing pt of a bezier, but we've already
added it, so just inc point and carry on */
point++;
}
if(pvBuf)
{
ppc->wType = type;
ppc->cpfx = cpfx;
}
needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
}
if(pvBuf) pph->cb = needed - pph_start;
}
break;
}
default:
DPRINT1("Unsupported format %d\n", iFormat);
if(pvBuf) ExFreePool(pvBuf);
return GDI_ERROR;
}
if (pvBuf)
{
Status = MmCopyToCaller(UnsafeBuf, pvBuf, cjBuf);
if (! NT_SUCCESS(Status))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
ExFreePool(pvBuf);
return GDI_ERROR;
}
DPRINT("NtGdiGetGlyphOutline K -> U worked!\n");
ExFreePool(pvBuf);
}
DPRINT("NtGdiGetGlyphOutline END and needed %d\n", needed);
return needed;
}
DWORD
STDCALL
NtGdiGetKerningPairs(HDC hDC,
DWORD NumPairs,
LPKERNINGPAIR krnpair)
{
UNIMPLEMENTED;
return 0;
}
/*
From "Undocumented Windows 2000 Secrets" Appendix B, Table B-2, page
472, this is NtGdiGetOutlineTextMetricsInternalW.
*/
UINT
STDCALL
NtGdiGetOutlineTextMetrics(HDC hDC,
UINT Data,
LPOUTLINETEXTMETRICW otm)
{
PDC dc;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
HFONT hFont = 0;
ULONG Size;
OUTLINETEXTMETRICW *potm;
NTSTATUS Status;
dc = DC_LockDc(hDC);
if (dc == NULL)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return 0;
}
hFont = dc->w.hFont;
TextObj = TEXTOBJ_LockText(hFont);
DC_UnlockDc(dc);
if (TextObj == NULL)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return 0;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
TEXTOBJ_UnlockText(TextObj);
Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
if (!otm) return Size;
if (Size > Data)
{
SetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
potm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
if (NULL == potm)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
IntGetOutlineTextMetrics(FontGDI, Size, potm);
if (otm)
{
Status = MmCopyToCaller(otm, potm, Size);
if (! NT_SUCCESS(Status))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
ExFreePool(potm);
return 0;
}
}
ExFreePool(potm);
return Size;
}
BOOL
APIENTRY
NtGdiGetRasterizerCaps(
OUT LPRASTERIZER_STATUS praststat,
IN ULONG cjBytes)
{
UNIMPLEMENTED;
return FALSE;
}
/*
Based on "Undocumented W2k Secrets", Table B-2, page 473.
This function does not exist. See note in gdi32/objects/text.c
GetTextCharset. This should be moved to include/win32k/ntgdibad.h.
*/
UINT
STDCALL
NtGdiGetTextCharset(HDC hDC)
{
UNIMPLEMENTED;
return 0;
}
INT
APIENTRY
NtGdiGetTextCharsetInfo(
IN HDC hdc,
OUT OPTIONAL LPFONTSIGNATURE lpSig,
IN DWORD dwFlags)
{
PDC Dc;
UINT Ret = DEFAULT_CHARSET, i = 0, fs_fsCsb0 = 0;
HFONT hFont;
PTEXTOBJ TextObj;
PFONTGDI FontGdi;
FONTSIGNATURE fs;
TT_OS2 *pOS2;
FT_Face Face;
NTSTATUS Status;
Dc = DC_LockDc(hdc);
if (!Dc)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return Ret;
}
hFont = Dc->w.hFont;
TextObj = TEXTOBJ_LockText(hFont);
DC_UnlockDc( Dc );
if ( TextObj == NULL)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return Ret;
}
FontGdi = ObjToGDI(TextObj->Font, FONT);
Face = FontGdi->face;
TEXTOBJ_UnlockText(TextObj);
IntLockFreeType;
pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
IntUnLockFreeType;
memset(&fs, 0, sizeof(FONTSIGNATURE));
if (NULL != pOS2)
{
fs.fsCsb[0] = pOS2->ulCodePageRange1;
fs.fsCsb[1] = pOS2->ulCodePageRange2;
fs.fsUsb[0] = pOS2->ulUnicodeRange1;
fs.fsUsb[1] = pOS2->ulUnicodeRange2;
fs.fsUsb[2] = pOS2->ulUnicodeRange3;
fs.fsUsb[3] = pOS2->ulUnicodeRange4;
fs_fsCsb0 = pOS2->ulCodePageRange1;
if (pOS2->version == 0)
{
FT_UInt dummy;
if(FT_Get_First_Char( Face, &dummy ) < 0x100)
fs_fsCsb0 |= 1;
else
fs_fsCsb0 |= 1L << 31;
}
}
DPRINT("Csb 1=%x 0=%x\n", fs.fsCsb[1],fs.fsCsb[0]);
if (lpSig)
{
Status = MmCopyToCaller(lpSig, &fs, sizeof(FONTSIGNATURE));
if (! NT_SUCCESS(Status))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return Ret;
}
}
if (0 == fs_fsCsb0)
{ /* let's see if we can find any interesting cmaps */
for (i = 0; i < Face->num_charmaps; i++)
{
switch (Face->charmaps[i]->encoding)
{
case ft_encoding_unicode:
case ft_encoding_apple_roman:
fs_fsCsb0 |= 1;
break;
case ft_encoding_symbol:
fs_fsCsb0 |= 1L << 31;
break;
default:
break;
}
}
}
while (0 == (fs_fsCsb0 >> i & 0x0001) && i < MAXTCIINDEX)
{
i++;
}
Ret = FontTci[i].ciCharset;
DPRINT("CharSet %d\n",Ret);
return Ret;
}
static BOOL
FASTCALL
TextIntGetTextExtentPoint(PDC dc,
PTEXTOBJ TextObj,
LPCWSTR String,
int Count,
int MaxExtent,
LPINT Fit,
LPINT Dx,
LPSIZE Size)
{
PFONTGDI FontGDI;
FT_Face face;
FT_GlyphSlot glyph;
INT error, n, glyph_index, i, previous;
ULONGLONG TotalWidth = 0;
FT_CharMap charmap, found = NULL;
BOOL use_kerning;
FontGDI = ObjToGDI(TextObj->Font, FONT);
face = FontGDI->face;
if (NULL != Fit)
{
*Fit = 0;
}
if (face->charmap == NULL)
{
DPRINT("WARNING: No charmap selected!\n");
DPRINT("This font face has %d charmaps\n", face->num_charmaps);
for (n = 0; n < face->num_charmaps; n++)
{
charmap = face->charmaps[n];
DPRINT("found charmap encoding: %u\n", charmap->encoding);
if (charmap->encoding != 0)
{
found = charmap;
break;
}
}
if (! found)
{
DPRINT1("WARNING: Could not find desired charmap!\n");
}
IntLockFreeType;
error = FT_Set_Charmap(face, found);
IntUnLockFreeType;
if (error)
{
DPRINT1("WARNING: Could not set the charmap!\n");
}
}
IntLockFreeType;
error = FT_Set_Pixel_Sizes(face,
TextObj->logfont.lfWidth,
/* FIXME should set character height if neg */
(TextObj->logfont.lfHeight < 0 ?
- TextObj->logfont.lfHeight :
TextObj->logfont.lfHeight == 0 ? 11 : TextObj->logfont.lfHeight));
IntUnLockFreeType;
if (error)
{
DPRINT1("Error in setting pixel sizes: %u\n", error);
}
use_kerning = FT_HAS_KERNING(face);
previous = 0;
for (i = 0; i < Count; i++)
{
IntLockFreeType;
glyph_index = FT_Get_Char_Index(face, *String);
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
IntUnLockFreeType;
if (error)
{
DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
}
glyph = face->glyph;
/* retrieve kerning distance */
if (use_kerning && previous && glyph_index)
{
FT_Vector delta;
IntLockFreeType;
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
IntUnLockFreeType;
TotalWidth += delta.x;
}
TotalWidth += glyph->advance.x;
if (((TotalWidth + 32) >> 6) <= MaxExtent && NULL != Fit)
{
*Fit = i + 1;
}
if (NULL != Dx)
{
Dx[i] = (TotalWidth + 32) >> 6;
}
previous = glyph_index;
String++;
}
Size->cx = (TotalWidth + 32) >> 6;
Size->cy = (TextObj->logfont.lfHeight < 0 ? - TextObj->logfont.lfHeight : TextObj->logfont.lfHeight);
Size->cy = EngMulDiv(Size->cy, IntGdiGetDeviceCaps(dc, LOGPIXELSY), 72);
return TRUE;
}
BOOL
STDCALL
NtGdiGetTextExtentExPoint(HDC hDC,
LPCWSTR UnsafeString,
int Count,
int MaxExtent,
LPINT UnsafeFit,
LPINT UnsafeDx,
LPSIZE UnsafeSize)
{
PDC dc;
LPWSTR String;
SIZE Size;
NTSTATUS Status;
BOOLEAN Result;
INT Fit;
LPINT Dx;
PTEXTOBJ TextObj;
if (Count < 0)
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (0 == Count)
{
Size.cx = 0;
Size.cy = 0;
Status = MmCopyToCaller(UnsafeSize, &Size, sizeof(SIZE));
if (! NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
return TRUE;
}
String = ExAllocatePoolWithTag(PagedPool, Count * sizeof(WCHAR), TAG_GDITEXT);
if (NULL == String)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
if (NULL != UnsafeDx)
{
Dx = ExAllocatePoolWithTag(PagedPool, Count * sizeof(INT), TAG_GDITEXT);
if (NULL == Dx)
{
ExFreePool(String);
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
}
else
{
Dx = NULL;
}
Status = MmCopyFromCaller(String, UnsafeString, Count * sizeof(WCHAR));
if (! NT_SUCCESS(Status))
{
if (NULL != Dx)
{
ExFreePool(Dx);
}
ExFreePool(String);
SetLastNtError(Status);
return FALSE;
}
dc = DC_LockDc(hDC);
if (NULL == dc)
{
if (NULL != Dx)
{
ExFreePool(Dx);
}
ExFreePool(String);
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
TextObj = TEXTOBJ_LockText(dc->w.hFont);
if ( TextObj )
{
Result = TextIntGetTextExtentPoint(dc, TextObj, String, Count, MaxExtent,
NULL == UnsafeFit ? NULL : &Fit, Dx, &Size);
}
else
Result = FALSE;
TEXTOBJ_UnlockText(TextObj);
DC_UnlockDc(dc);
ExFreePool(String);
if (! Result)
{
if (NULL != Dx)
{
ExFreePool(Dx);
}
return FALSE;
}
if (NULL != UnsafeFit)
{
Status = MmCopyToCaller(UnsafeFit, &Fit, sizeof(INT));
if (! NT_SUCCESS(Status))
{
if (NULL != Dx)
{
ExFreePool(Dx);
}
SetLastNtError(Status);
return FALSE;
}
}
if (NULL != UnsafeDx)
{
Status = MmCopyToCaller(UnsafeDx, Dx, Count * sizeof(INT));
if (! NT_SUCCESS(Status))
{
if (NULL != Dx)
{
ExFreePool(Dx);
}
SetLastNtError(Status);
return FALSE;
}
}
if (NULL != Dx)
{
ExFreePool(Dx);
}
Status = MmCopyToCaller(UnsafeSize, &Size, sizeof(SIZE));
if (! NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
return TRUE;
}
BOOL
STDCALL
NtGdiGetTextExtent(HDC hdc,
LPWSTR lpwsz,
INT cwc,
LPSIZE psize,
UINT flOpts)
{
return NtGdiGetTextExtentExPoint(hdc, lpwsz, cwc, 0, NULL, NULL, psize);
}
BOOL
STDCALL
NtGdiGetTextExtentPoint32(HDC hDC,
LPCWSTR UnsafeString,
int Count,
LPSIZE UnsafeSize)
{
PDC dc;
LPWSTR String;
SIZE Size;
NTSTATUS Status;
BOOLEAN Result;
PTEXTOBJ TextObj;
if (Count < 0)
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (0 == Count)
{
Size.cx = 0;
Size.cy = 0;
Status = MmCopyToCaller(UnsafeSize, &Size, sizeof(SIZE));
if (! NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
return TRUE;
}
String = ExAllocatePool(PagedPool, Count * sizeof(WCHAR));
if (NULL == String)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
Status = MmCopyFromCaller(String, UnsafeString, Count * sizeof(WCHAR));
if (! NT_SUCCESS(Status))
{
ExFreePool(String);
SetLastNtError(Status);
return FALSE;
}
dc = DC_LockDc(hDC);
if (NULL == dc)
{
ExFreePool(String);
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
TextObj = TEXTOBJ_LockText(dc->w.hFont);
if ( TextObj != NULL )
{
Result = TextIntGetTextExtentPoint (
dc, TextObj, String, Count, 0, NULL, NULL, &Size);
TEXTOBJ_UnlockText(TextObj);
}
else
Result = FALSE;
DC_UnlockDc(dc);
ExFreePool(String);
if (! Result)
{
return FALSE;
}
Status = MmCopyToCaller(UnsafeSize, &Size, sizeof(SIZE));
if (! NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
return TRUE;
}
INT STDCALL
NtGdiGetTextFace(HDC hDC, INT Count, LPWSTR FaceName)
{
PDC Dc;
HFONT hFont;
PTEXTOBJ TextObj;
NTSTATUS Status;
Dc = DC_LockDc(hDC);
if (Dc == NULL)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
hFont = Dc->w.hFont;
DC_UnlockDc(Dc);
TextObj = TEXTOBJ_LockText(hFont);
ASSERT(TextObj != NULL);
Count = min(Count, wcslen(TextObj->logfont.lfFaceName));
Status = MmCopyToCaller(FaceName, TextObj->logfont.lfFaceName, Count * sizeof(WCHAR));
TEXTOBJ_UnlockText(TextObj);
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return 0;
}
return Count;
}
BOOL
STDCALL
NtGdiGetTextMetrics(HDC hDC,
LPTEXTMETRICW tm)
{
PDC dc;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
NTSTATUS Status = STATUS_SUCCESS;
TEXTMETRICW SafeTm;
FT_Face Face;
TT_OS2 *pOS2;
TT_HoriHeader *pHori;
ULONG Error;
if (NULL == tm)
{
SetLastWin32Error(STATUS_INVALID_PARAMETER);
return FALSE;
}
if(!(dc = DC_LockDc(hDC)))
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
TextObj = TEXTOBJ_LockText(dc->w.hFont);
if (NULL != TextObj)
{
FontGDI = ObjToGDI(TextObj->Font, FONT);
Face = FontGDI->face;
IntLockFreeType;
Error = FT_Set_Pixel_Sizes(Face,
TextObj->logfont.lfWidth,
/* FIXME should set character height if neg */
(TextObj->logfont.lfHeight < 0 ?
- TextObj->logfont.lfHeight :
TextObj->logfont.lfHeight == 0 ? 11 : TextObj->logfont.lfHeight));
IntUnLockFreeType;
if (0 != Error)
{
DPRINT1("Error in setting pixel sizes: %u\n", Error);
Status = STATUS_UNSUCCESSFUL;
}
else
{
memcpy(&SafeTm, &FontGDI->TextMetric, sizeof(TEXTMETRICW));
Status = STATUS_SUCCESS;
IntLockFreeType;
pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
if (NULL == pOS2)
{
DPRINT1("Can't find OS/2 table - not TT font?\n");
Status = STATUS_INTERNAL_ERROR;
}
pHori = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_hhea);
if (NULL == pHori)
{
DPRINT1("Can't find HHEA table - not TT font?\n");
Status = STATUS_INTERNAL_ERROR;
}
IntUnLockFreeType;
if (NT_SUCCESS(Status))
{
FillTM(&SafeTm, FontGDI->face, pOS2, pHori);
Status = MmCopyToCaller(tm, &SafeTm, sizeof(TEXTMETRICW));
}
}
TEXTOBJ_UnlockText(TextObj);
}
else
{
Status = STATUS_INVALID_HANDLE;
}
DC_UnlockDc(dc);
if(!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
return TRUE;
}
BOOL
STDCALL
NtGdiPolyTextOut(HDC hDC,
CONST LPPOLYTEXTW txt,
int Count)
{
UNIMPLEMENTED;
return FALSE;
}
BOOL
STDCALL
NtGdiRemoveFontResource(LPCWSTR FileName)
{
DPRINT1("NtGdiRemoveFontResource is UNIMPLEMENTED\n");
return FALSE;
}
DWORD
STDCALL
NtGdiSetMapperFlags(HDC hDC,
DWORD Flag)
{
UNIMPLEMENTED;
return 0;
}
UINT
STDCALL
NtGdiSetTextAlign(HDC hDC,
UINT Mode)
{
UINT prevAlign;
DC *dc;
dc = DC_LockDc(hDC);
if (!dc)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
prevAlign = dc->w.textAlign;
dc->w.textAlign = Mode;
DC_UnlockDc( dc );
return prevAlign;
}
COLORREF
STDCALL
NtGdiSetTextColor(HDC hDC,
COLORREF color)
{
COLORREF oldColor;
PDC dc = DC_LockDc(hDC);
HBRUSH hBrush;
if (!dc)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return CLR_INVALID;
}
oldColor = dc->w.textColor;
dc->w.textColor = color;
hBrush = dc->w.hBrush;
DC_UnlockDc( dc );
NtGdiSelectObject(hDC, hBrush);
return oldColor;
}
BOOL
STDCALL
NtGdiSetTextJustification(HDC hDC,
int BreakExtra,
int BreakCount)
{
UNIMPLEMENTED;
return FALSE;
}
BOOL STDCALL
NtGdiTextOut(
HDC hDC,
INT XStart,
INT YStart,
LPCWSTR String,
INT Count)
{
return NtGdiExtTextOut(hDC, XStart, YStart, 0, NULL, String, Count, NULL);
}
DWORD STDCALL
NtGdiGetFontData(
HDC hDC,
DWORD Table,
DWORD Offset,
LPVOID Buffer,
DWORD Size)
{
PDC Dc;
HFONT hFont;
PTEXTOBJ TextObj;
PFONTGDI FontGdi;
DWORD Result = GDI_ERROR;
Dc = DC_LockDc(hDC);
if (Dc == NULL)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
hFont = Dc->w.hFont;
TextObj = TEXTOBJ_LockText(hFont);
DC_UnlockDc(Dc);
if (TextObj == NULL)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
FontGdi = ObjToGDI(TextObj->Font, FONT);
IntLockFreeType;
if (FT_IS_SFNT(FontGdi->face))
{
if (Table)
Table = Table >> 24 | Table << 24 | (Table >> 8 & 0xFF00) |
(Table << 8 & 0xFF0000);
if (Buffer == NULL)
Size = 0;
if (!FT_Load_Sfnt_Table(FontGdi->face, Table, Offset, Buffer, &Size))
Result = Size;
}
IntUnLockFreeType;
TEXTOBJ_UnlockText(TextObj);
return Result;
}
static UINT FASTCALL
GetFontScore(LOGFONTW *LogFont, PUNICODE_STRING FaceName, PFONTGDI FontGDI)
{
ANSI_STRING EntryFaceNameA;
UNICODE_STRING EntryFaceNameW;
unsigned Size;
OUTLINETEXTMETRICW *Otm;
LONG WeightDiff;
NTSTATUS Status;
UINT Score = 1;
RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
Status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
if (NT_SUCCESS(Status))
{
if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
{
EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
}
if (0 == RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE))
{
Score += 49;
}
RtlFreeUnicodeString(&EntryFaceNameW);
}
Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
Otm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
if (NULL == Otm)
{
return Score;
}
IntGetOutlineTextMetrics(FontGDI, Size, Otm);
if ((0 != LogFont->lfItalic && 0 != Otm->otmTextMetrics.tmItalic) ||
(0 == LogFont->lfItalic && 0 == Otm->otmTextMetrics.tmItalic))
{
Score += 25;
}
if (LogFont->lfWeight < Otm->otmTextMetrics.tmWeight)
{
WeightDiff = Otm->otmTextMetrics.tmWeight - LogFont->lfWeight;
}
else
{
WeightDiff = LogFont->lfWeight - Otm->otmTextMetrics.tmWeight;
}
Score += (1000 - WeightDiff) / (1000 / 25);
ExFreePool(Otm);
return Score;
}
static __inline VOID
FindBestFontFromList(FONTOBJ **FontObj, UINT *MatchScore, LOGFONTW *LogFont,
PUNICODE_STRING FaceName, PLIST_ENTRY Head)
{
PLIST_ENTRY Entry;
PFONT_ENTRY CurrentEntry;
FONTGDI *FontGDI;
UINT Score;
Entry = Head->Flink;
while (Entry != Head)
{
CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
FontGDI = CurrentEntry->Font;
ASSERT(FontGDI);
Score = GetFontScore(LogFont, FaceName, FontGDI);
if (*MatchScore == 0 || *MatchScore < Score)
{
*FontObj = GDIToObj(FontGDI, FONT);
*MatchScore = Score;
}
Entry = Entry->Flink;
}
}
static __inline BOOLEAN
SubstituteFontFamilyKey(PUNICODE_STRING FaceName,
LPCWSTR Key)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
NTSTATUS Status;
UNICODE_STRING Value;
RtlInitUnicodeString(&Value, NULL);
QueryTable[0].QueryRoutine = NULL;
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND |
RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[0].Name = FaceName->Buffer;
QueryTable[0].EntryContext = &Value;
QueryTable[0].DefaultType = REG_NONE;
QueryTable[0].DefaultData = NULL;
QueryTable[0].DefaultLength = 0;
QueryTable[1].QueryRoutine = NULL;
QueryTable[1].Name = NULL;
Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
Key,
QueryTable,
NULL,
NULL);
if (NT_SUCCESS(Status))
{
RtlFreeUnicodeString(FaceName);
*FaceName = Value;
}
return NT_SUCCESS(Status);
}
static __inline void
SubstituteFontFamily(PUNICODE_STRING FaceName, UINT Level)
{
if (10 < Level) /* Enough is enough */
{
return;
}
if (SubstituteFontFamilyKey(FaceName, L"SysFontSubstitutes") ||
SubstituteFontFamilyKey(FaceName, L"FontSubstitutes"))
{
SubstituteFontFamily(FaceName, Level + 1);
}
}
NTSTATUS FASTCALL
TextIntRealizeFont(HFONT FontHandle)
{
NTSTATUS Status = STATUS_SUCCESS;
PTEXTOBJ TextObj;
UNICODE_STRING FaceName;
PW32PROCESS Win32Process;
UINT MatchScore;
TextObj = TEXTOBJ_LockText(FontHandle);
if (NULL == TextObj)
{
return STATUS_INVALID_HANDLE;
}
if (TextObj->Initialized)
{
TEXTOBJ_UnlockText(TextObj);
return STATUS_SUCCESS;
}
if (! RtlCreateUnicodeString(&FaceName, TextObj->logfont.lfFaceName))
{
TEXTOBJ_UnlockText(TextObj);
return STATUS_NO_MEMORY;
}
SubstituteFontFamily(&FaceName, 0);
MatchScore = 0;
TextObj->Font = NULL;
/* First search private fonts */
Win32Process = PsGetWin32Process();
IntLockProcessPrivateFonts(Win32Process);
FindBestFontFromList(&TextObj->Font, &MatchScore,
&TextObj->logfont, &FaceName,
&Win32Process->PrivateFontListHead);
IntUnLockProcessPrivateFonts(Win32Process);
/* Search system fonts */
IntLockGlobalFonts;
FindBestFontFromList(&TextObj->Font, &MatchScore,
&TextObj->logfont, &FaceName,
&FontListHead);
IntUnLockGlobalFonts;
if (NULL == TextObj->Font)
{
DPRINT1("Requested font %S not found, no fonts loaded at all\n",
TextObj->logfont.lfFaceName);
Status = STATUS_NOT_FOUND;
}
else
{
TextObj->Initialized = TRUE;
Status = STATUS_SUCCESS;
}
RtlFreeUnicodeString(&FaceName);
TEXTOBJ_UnlockText(TextObj);
ASSERT((NT_SUCCESS(Status) ^ (NULL == TextObj->Font)) != 0);
return Status;
}
INT FASTCALL
FontGetObject(PTEXTOBJ Font, INT Count, PVOID Buffer)
{
if (Count < sizeof(LOGFONTW))
{
SetLastWin32Error(ERROR_BUFFER_OVERFLOW);
return 0;
}
RtlCopyMemory(Buffer, &Font->logfont, sizeof(LOGFONTW));
return sizeof(LOGFONTW);
}
/* Remove this HAX! after the below function is done in GDI32.DLL! */
INT
STDCALL
NtGdiGetSetTextCharExtra( HDC hDC, INT CharExtra, BOOL Set)
{
/* Ulta-Ugly Hax! */
INT Ret = 0x80000000;
DPRINT("TextCharacterExtra %d", CharExtra);
PDC dc = DC_LockDc ( hDC );
if (!dc)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return Ret;
}
Ret = dc->w.charExtra;
if( Set ) dc->w.charExtra = CharExtra;
DC_UnlockDc(dc);
return (Ret);
}
/* EOF */