Index: reactos/win32ss/gdi/ntgdi/freetype.c =================================================================== --- reactos/win32ss/gdi/ntgdi/freetype.c (revision 73465) +++ reactos/win32ss/gdi/ntgdi/freetype.c (working copy) @@ -3,8 +3,9 @@ * LICENSE: GPL - See COPYING in the top level directory * FILE: win32ss/gdi/ntgdi/freetype.c * PURPOSE: FreeType font engine interface - * PROGRAMMER: Copyright 2001 Huw D M Davies for CodeWeavers. + * PROGRAMMERS: Copyright 2001 Huw D M Davies for CodeWeavers. * Copyright 2006 Dmitry Timoshkov for CodeWeavers. + * Copyright 2016 Katayama Hirofumi MZ. */ /** Includes ******************************************************************/ @@ -30,6 +31,11 @@ ((DWORD)(BYTE)(ch2) << 8) | (DWORD)(BYTE)(ch3) ) #endif +/* TPMF_FIXED_PITCH is confusing; braindead api */ +#ifndef _TMPF_VARIABLE_PITCH + #define _TMPF_VARIABLE_PITCH TMPF_FIXED_PITCH +#endif + extern const MATRIX gmxWorldToDeviceDefault; extern const MATRIX gmxWorldToPageDefault; @@ -602,6 +608,7 @@ return FALSE; } + static void FASTCALL FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI, TT_OS2 *pOS2, TT_HoriHeader *pHori, FT_WinFNT_HeaderRec *pWin) { @@ -714,10 +721,9 @@ TM->tmUnderlined = FontGDI->Underline; TM->tmStruckOut = FontGDI->StrikeOut; - /* Yes TPMF_FIXED_PITCH is correct; braindead api */ - if (! FT_IS_FIXED_WIDTH(Face)) + if (!FT_IS_FIXED_WIDTH(Face)) { - TM->tmPitchAndFamily = TMPF_FIXED_PITCH; + TM->tmPitchAndFamily = _TMPF_VARIABLE_PITCH; } else { @@ -2708,104 +2714,418 @@ return Result; } -static UINT FASTCALL -GetFontScore(LOGFONTW *LogFont, PUNICODE_STRING FaceName, PFONTGDI FontGDI) +static __inline BOOLEAN +SubstituteFontFamilyKey(PUNICODE_STRING FaceName, + LPCWSTR Key) { - ANSI_STRING EntryFaceNameA; - UNICODE_STRING EntryFaceNameW; - unsigned Size; - OUTLINETEXTMETRICW *Otm; - LONG WeightDiff; + RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}}; NTSTATUS Status; - UINT Score = 1; + UNICODE_STRING Value; - RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name); - Status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE); + 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)) { - static const UNICODE_STRING MarlettFaceNameW = RTL_CONSTANT_STRING(L"Marlett"); - static const UNICODE_STRING SymbolFaceNameW = RTL_CONSTANT_STRING(L"Symbol"); - static const UNICODE_STRING VGAFaceNameW = RTL_CONSTANT_STRING(L"VGA"); + RtlFreeUnicodeString(FaceName); + *FaceName = Value; + } - if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length) + return NT_SUCCESS(Status); +} + +static __inline void +SubstituteFontFamily(PUNICODE_STRING FaceName, UINT Level) +{ + if (10 < Level) /* Enough is enough */ + { + return; + } + + /* NOTE: SubstituteFontFamilyKey changes FaceName. Be careful... */ + if (SubstituteFontFamilyKey(FaceName, L"FontSubstitutes")) + { + SubstituteFontFamily(FaceName, Level + 1); + } +} + +static __inline INT +CompareSubstituteFamily(PUNICODE_STRING FaceName, PUNICODE_STRING CompareWith, + UINT Level) +{ + if (10 < Level) /* Enough is enough */ + return 1; + + if (RtlCompareUnicodeString(FaceName, CompareWith, TRUE) == 0) + { + return 0; + } + + /* NOTE: SubstituteFontFamilyKey changes FaceName. Be careful... */ + if (SubstituteFontFamilyKey(FaceName, L"FontSubstitutes")) + { + return CompareSubstituteFamily(FaceName, CompareWith, Level + 1); + } + + return 1; +} + +static ULONG +GetFaceNamePenalty(LPWSTR pszLogFamilyName, UNICODE_STRING *pEntryFaceNameW) +{ + ULONG Penalty = 0; + UNICODE_STRING LogFaceNameW; + NTSTATUS Status; + BOOL bEqual = FALSE; + + Status = RtlCreateUnicodeString(&LogFaceNameW, pszLogFamilyName); + if (!NT_SUCCESS(Status)) + { + DPRINT("GetFaceNamePenalty: ERROR: RtlCreateUnicodeString\n"); + return 500; /* failure */ + } + + /* truncate */ + if ((LF_FACESIZE - 1) * sizeof(WCHAR) < LogFaceNameW.Length) + { + LogFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); + LogFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; + } + + /* compare */ + bEqual = (RtlCompareUnicodeString(&LogFaceNameW, pEntryFaceNameW, TRUE) == 0); + + /* NOTE: CompareSubstituteFamily changes LogFaceNameW. Be careful... */ + if (CompareSubstituteFamily(&LogFaceNameW, pEntryFaceNameW, 0) == 0) + { + /* substitute */ + DPRINT("GetFaceNamePenalty: \"%ls\" substituted by \"%ls\"\n", + pszLogFamilyName, pEntryFaceNameW->Buffer); + + RtlCopyMemory(pszLogFamilyName, pEntryFaceNameW->Buffer, + LogFaceNameW.Length); + pszLogFamilyName[LogFaceNameW.Length / sizeof(WCHAR)] = 0; + } + else + { + if (!bEqual) { - EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); - EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; + /* Penalty += 10000: lfFaceName: mismatched */ + Penalty += 10000; } + } - if (!RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE)) + RtlFreeUnicodeString(&LogFaceNameW); + + return Penalty; /* success */ +} + +static UINT FASTCALL +GetFontPenalty(LOGFONTW *LogFont, PUNICODE_STRING FaceName, PFONTGDI FontGDI) +{ + UINT Size; + TEXTMETRICW * TM; + ULONG Penalty = 0; + ANSI_STRING FaceNameA; + UNICODE_STRING FaceNameW; + NTSTATUS Status; + BYTE CharSet, Byte; + OUTLINETEXTMETRICW *Otm; + + /* get text metrics */ + Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); + if (Otm == NULL) + { + return 50000; /* failure */ + } + IntGetOutlineTextMetrics(FontGDI, Size, Otm); + TM = &Otm->otmTextMetrics; + + /* get face name */ + RtlInitAnsiString(&FaceNameA, FontGDI->face->family_name); + Status = RtlAnsiStringToUnicodeString(&FaceNameW, &FaceNameA, TRUE); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(Otm, GDITAG_TEXT); + return 50000; /* failure */ + } + + /* and truncate */ + if ((LF_FACESIZE - 1) * sizeof(WCHAR) < FaceNameW.Length) + { + FaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); + FaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; + } + + CharSet = TM->tmCharSet; + +#if 1 + /* FIXME: tmCharSet shoule be correct */ + { + static const UNICODE_STRING + MarlettFaceNameW = RTL_CONSTANT_STRING(L"Marlett"); + static const UNICODE_STRING + SymbolFaceNameW = RTL_CONSTANT_STRING(L"Symbol"); + static const UNICODE_STRING + VGAFaceNameW = RTL_CONSTANT_STRING(L"VGA"); + + if (RtlCompareUnicodeString(&FaceNameW, &MarlettFaceNameW, TRUE) == 0) { - Score += 49; + CharSet = SYMBOL_CHARSET; } - - /* FIXME: this is a work around to counter weird fonts on weird places. - A proper fix would be to score fonts on more attributes than - the ones in this function */ - if (!RtlCompareUnicodeString(&MarlettFaceNameW, &EntryFaceNameW, TRUE) && - RtlCompareUnicodeString(&MarlettFaceNameW, FaceName, TRUE)) + else if (RtlCompareUnicodeString(&FaceNameW, &SymbolFaceNameW, TRUE) == 0) { - Score = 0; + CharSet = GREEK_CHARSET; } + else if (RtlCompareUnicodeString(&FaceNameW, &VGAFaceNameW, TRUE) == 0) + { + CharSet = OEM_CHARSET; + } + else + { + if (CharSet == DEFAULT_CHARSET) + CharSet = -1; /* invalid value */ + } + } +#endif - if (!RtlCompareUnicodeString(&SymbolFaceNameW, &EntryFaceNameW, TRUE) && - RtlCompareUnicodeString(&SymbolFaceNameW, FaceName, TRUE)) + if (LogFont->lfCharSet != DEFAULT_CHARSET) + { + /* Penalty += 65000: lfCharSet: mismatched */ + if (LogFont->lfCharSet != CharSet) + Penalty += 65000; + } + + /* if face name is set */ + if (LogFont->lfFaceName[0]) + { + /* NOTE: This might change LogFont->lfFaceName */ + ULONG nResult = GetFaceNamePenalty(LogFont->lfFaceName, &FaceNameW); + Penalty += nResult; + } + + /* Penalty += 19000: lfOutPrecision: mismatched */ + if (LogFont->lfOutPrecision != OUT_DEFAULT_PRECIS) + { + switch (LogFont->lfOutPrecision) { - Score = 0; + case OUT_DEVICE_PRECIS: + if (!(TM->tmPitchAndFamily & TMPF_DEVICE)) + Penalty += 19000; + break; + case OUT_TT_ONLY_PRECIS: + if (!(TM->tmPitchAndFamily & TMPF_TRUETYPE)) + Penalty += 19000; + break; + default: + break; } + } - if (!RtlCompareUnicodeString(&VGAFaceNameW, &EntryFaceNameW, TRUE) && - RtlCompareUnicodeString(&VGAFaceNameW, FaceName, TRUE)) + /* get the pitch */ + Byte = (LogFont->lfPitchAndFamily & 0x0F); + if (Byte != DEFAULT_PITCH) + { + if (Byte == FIXED_PITCH) { - Score = 0; + /* Penalty += 15000: lfPitchAndFamily: variable pitch was set on fixed-pitch specified */ + if (TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH) + { + Penalty += 15000; + } } - - RtlFreeUnicodeString(&EntryFaceNameW); + /* Penalty += 350: lfPitchAndFamily: non-variable pitch on variable pitch */ + if (Byte == VARIABLE_PITCH && !(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH)) + { + Penalty += 350; + } } + else + { + /* Penalty += 1: pitch is not normal */ + if (!(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH)) + { + Penalty += 1; + } + } - Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); - Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); - if (NULL == Otm) + /* get family */ + Byte = (LogFont->lfPitchAndFamily & 0xF0); + if (Byte != FF_DONTCARE) { - return Score; + /* Penalty += 9000: lfPitchAndFamily: mismatched font family */ + if (Byte != (TM->tmPitchAndFamily & 0xF0)) + { + Penalty += 9000; + } + /* Penalty += 8000: lfPitchAndFamily: no family on family specified */ + if ((TM->tmPitchAndFamily & 0xF0) == FF_DONTCARE) + { + Penalty += 8000; + } } - IntGetOutlineTextMetrics(FontGDI, Size, Otm); - if ((0 != LogFont->lfItalic && 0 != Otm->otmTextMetrics.tmItalic) || - (0 == LogFont->lfItalic && 0 == Otm->otmTextMetrics.tmItalic)) + /* if the target is not scalable */ + if (!(TM->tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))) { - Score += 25; + if (LogFont->lfHeight != 0) + { + /* Penalty += 600: lfHeight: non-vector font and height was higher */ + if (labs(LogFont->lfHeight) < TM->tmHeight) + { + Penalty += 600; + } + /* Penalty += 600: lfHeight: non-vector font and height was higher */ + if (LogFont->lfHeight < 0 && labs(LogFont->lfHeight) < TM->tmHeight) + { + Penalty += 600; + } + /* Penalty += 150 * Diff * K: lfHeight: mismatched */ + if (labs(LogFont->lfHeight) != TM->tmHeight) + { + double K = 2; + /* lfHeight: avg. 16. */ + /* 16 * K = 32. */ + if (labs(LogFont->lfHeight) != TM->tmHeight) + { + LONG Diff = labs(labs(LogFont->lfHeight) - TM->tmHeight); + Penalty += (150 * Diff) * K; + } + } + } + + /* Penalty += 50: height or width scaling required */ + if ((LogFont->lfHeight != 0 && labs(LogFont->lfHeight) != TM->tmHeight) || + (LogFont->lfWidth != 0 && LogFont->lfWidth != TM->tmAveCharWidth)) + { + Penalty += 50; + } + + /* Penalty += 50 * Diff * K: mismatched width */ + if (LogFont->lfWidth != 0 && LogFont->lfWidth != TM->tmAveCharWidth) + { + double K = 0.2; + /* lfWidth: avg. 16. */ + /* 16 * K = 3.2. */ + if (LogFont->lfWidth != TM->tmAveCharWidth) + { + LONG Diff = labs(TM->tmAveCharWidth - LogFont->lfWidth); + Penalty += (50 * Diff) * K; + } + } + + /* Penalty += 1: it needs to be adjusted to italic */ + if (LogFont->lfItalic && !TM->tmItalic) + { + Penalty += 1; + } + /* Penalty += 1: it needs to be adjusted to underline */ + if (LogFont->lfUnderline && !TM->tmUnderlined) + { + Penalty += 1; + } + /* Penalty += 1: it needs to be adjusted to struke-out */ + if (LogFont->lfStrikeOut && !TM->tmStruckOut) + { + Penalty += 1; + } } + if (LogFont->lfWeight != FW_DONTCARE) { - if (LogFont->lfWeight < Otm->otmTextMetrics.tmWeight) + /* Penalty += 3 * Diff * K: lfWeight: weight mismatched */ + /* lfWeight: 100 to 900 */ + double K = 0.5; + /* K * 300 * 3 = 450 */ + if (LogFont->lfWeight != TM->tmWeight) { - WeightDiff = Otm->otmTextMetrics.tmWeight - LogFont->lfWeight; + LONG Diff = labs(LogFont->lfWeight - TM->tmWeight); + Penalty += (3 * Diff) * K; } - else + } + else + { + /* Penalty += 1: lfWeight: weight is not normal */ + if (TM->tmWeight != FW_NORMAL) { - WeightDiff = LogFont->lfWeight - Otm->otmTextMetrics.tmWeight; + Penalty += 1; } - Score += (1000 - WeightDiff) / (1000 / 25); } - else + + /* Penalty += 4: lfItalic: mismatched */ + if (!!LogFont->lfItalic != !!TM->tmItalic) { - Score += 25; + Penalty += 4; } + /* Penalty += 4: lfUnderline: mismatched */ + if (!!LogFont->lfUnderline != !!TM->tmUnderlined) + { + Penalty += 4; + } + /* Penalty += 4: lfStrikeOut: mismatched */ + if (!!LogFont->lfStrikeOut != !!TM->tmStruckOut) + { + Penalty += 4; + } + /* Penalty += 4: lfOutPrecision: non-TrueType on TrueType specified */ + if (LogFont->lfOutPrecision == OUT_TT_PRECIS) + { + if (!(TM->tmPitchAndFamily & TMPF_TRUETYPE)) + Penalty += 4; + } + + /* Penalty += 2: not device font */ + if (!(TM->tmPitchAndFamily & TMPF_DEVICE)) + { + Penalty += 2; + } + + if (LogFont->lfFaceName[0]) + { + if (Penalty < 200) + { + DPRINT("WARNING: GetFontPenalty: %ls: Penalty: %lu, " + "lfCharSet:%d, lfPitchAndFamily:%d, lfWeight:%d, " + "tmCharSet:%d, tmPitchAndFamily:%d, tmWeight:%d\n", + LogFont->lfFaceName, Penalty, + LogFont->lfCharSet, LogFont->lfPitchAndFamily, LogFont->lfWeight, + TM->tmCharSet, TM->tmPitchAndFamily, TM->tmWeight); + } + } + + RtlFreeUnicodeString(&FaceNameW); ExFreePoolWithTag(Otm, GDITAG_TEXT); - return Score; + return Penalty; /* success */ } static __inline VOID -FindBestFontFromList(FONTOBJ **FontObj, UINT *MatchScore, LOGFONTW *LogFont, +FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont, PUNICODE_STRING FaceName, PLIST_ENTRY Head) { PLIST_ENTRY Entry; PFONT_ENTRY CurrentEntry; FONTGDI *FontGDI; - UINT Score; -ASSERT(FontObj && MatchScore && LogFont && FaceName && Head); + ULONG Penalty; + ASSERT(FontObj && MatchPenalty && LogFont && FaceName && Head); Entry = Head->Flink; while (Entry != Head) { @@ -2814,66 +3134,16 @@ FontGDI = CurrentEntry->Font; ASSERT(FontGDI); - Score = GetFontScore(LogFont, FaceName, FontGDI); - if (*MatchScore == 0 || *MatchScore < Score) + Penalty = GetFontPenalty(LogFont, FaceName, FontGDI); + if (*MatchPenalty == 0xFFFFFFFF || Penalty < *MatchPenalty) { *FontObj = GDIToObj(FontGDI, FONT); - *MatchScore = Score; + *MatchPenalty = Penalty; } Entry = Entry->Flink; } } -static __inline BOOLEAN -SubstituteFontFamilyKey(PUNICODE_STRING FaceName, - LPCWSTR Key) -{ - RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}}; - 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"FontSubstitutes")) - { - SubstituteFontFamily(FaceName, Level + 1); - } -} - static VOID FASTCALL @@ -2913,7 +3183,7 @@ PTEXTOBJ TextObj; UNICODE_STRING FaceName; PPROCESSINFO Win32Process; - UINT MatchScore; + ULONG MatchPenalty; if (!pTextObj) { @@ -2938,13 +3208,14 @@ return STATUS_NO_MEMORY; } SubstituteFontFamily(&FaceName, 0); - MatchScore = 0; + MatchPenalty = 0xFFFFFFFF; TextObj->Font = NULL; - /* First search private fonts */ Win32Process = PsGetCurrentProcessWin32Process(); + + /* Search private fonts */ IntLockProcessPrivateFonts(Win32Process); - FindBestFontFromList(&TextObj->Font, &MatchScore, + FindBestFontFromList(&TextObj->Font, &MatchPenalty, &TextObj->logfont.elfEnumLogfontEx.elfLogFont, &FaceName, &Win32Process->PrivateFontListHead); IntUnLockProcessPrivateFonts(Win32Process); @@ -2951,10 +3222,11 @@ /* Search system fonts */ IntLockGlobalFonts; - FindBestFontFromList(&TextObj->Font, &MatchScore, + FindBestFontFromList(&TextObj->Font, &MatchPenalty, &TextObj->logfont.elfEnumLogfontEx.elfLogFont, &FaceName, &FontListHead); IntUnLockGlobalFonts; + if (NULL == TextObj->Font) { DPRINT1("Requested font %S not found, no fonts loaded at all\n",