Index: reactos/win32ss/gdi/ntgdi/freetype.c =================================================================== --- reactos/win32ss/gdi/ntgdi/freetype.c (revision 73352) +++ 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; @@ -714,10 +720,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 +2713,376 @@ return Result; } +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 __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) + { + DPRINT("FaceName:%S\n", FaceName->Buffer); + DPRINT("CompareWith:%S\n", CompareWith->Buffer); + DPRINT("EQUAL\n"); + return 0; + } + + if (SubstituteFontFamilyKey(FaceName, L"FontSubstitutes")) + { + return CompareSubstituteFamily(FaceName, CompareWith, Level + 1); + } + DPRINT("FaceName:%S\n", FaceName->Buffer); + DPRINT("CompareWith:%S\n", CompareWith->Buffer); + DPRINT("NOT EQUAL\n"); + return 1; +} + +#define ABS(value) ((value) < 0 ? -(value) : (value)) + static UINT FASTCALL -GetFontScore(LOGFONTW *LogFont, PUNICODE_STRING FaceName, PFONTGDI FontGDI) +GetFontPenalty(LOGFONTW *LogFont, PUNICODE_STRING FaceName, PFONTGDI FontGDI) { ANSI_STRING EntryFaceNameA; - UNICODE_STRING EntryFaceNameW; + UNICODE_STRING EntryFaceNameW, LogFaceNameW; unsigned Size; OUTLINETEXTMETRICW *Otm; - LONG WeightDiff; + TEXTMETRICW *TM; NTSTATUS Status; - UINT Score = 1; + BYTE Byte; + ULONG Penalty = 0; - RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name); - Status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE); - if (NT_SUCCESS(Status)) + // get text metrics + Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); + if (Otm == NULL) { - 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"); + return 0xFFFFFFFF; + } + IntGetOutlineTextMetrics(FontGDI, Size, Otm); + TM = &Otm->otmTextMetrics; - if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length) + // Penalty += 65000: lfCharSet: mismatched + if (LogFont->lfCharSet != DEFAULT_CHARSET) + { + if (LogFont->lfCharSet != TM->tmCharSet) + Penalty += 65000; + } + + // Penalty += 19000: lfOutPrecision: mismatched + if (LogFont->lfOutPrecision != OUT_DEFAULT_PRECIS) + { + switch (LogFont->lfOutPrecision) { - EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); - EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\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(FaceName, &EntryFaceNameW, TRUE)) + // get the pitch + Byte = (LogFont->lfPitchAndFamily & 0x0F); + if (Byte != DEFAULT_PITCH) + { + if (Byte == FIXED_PITCH) { - Score += 49; + // Penalty += 15000: lfPitchAndFamily: variable pitch was set on fixed-pitch specified + if (TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH) + { + Penalty += 15000; + } } - - /* 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)) + // Penalty += 350: lfPitchAndFamily: non-variable pitch on variable pitch + if (Byte == VARIABLE_PITCH && !(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH)) { - Score = 0; + Penalty += 350; } + } - if (!RtlCompareUnicodeString(&SymbolFaceNameW, &EntryFaceNameW, TRUE) && - RtlCompareUnicodeString(&SymbolFaceNameW, FaceName, TRUE)) + // if face name is set + if (LogFont->lfFaceName[0]) + { + // create a Unicode string EntryFaceNameW from family_name + RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name); + Status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE); + if (NT_SUCCESS(Status)) { - Score = 0; - } + // truncate + if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length) + { + EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); + EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; + } - if (!RtlCompareUnicodeString(&VGAFaceNameW, &EntryFaceNameW, TRUE) && - RtlCompareUnicodeString(&VGAFaceNameW, FaceName, TRUE)) - { - Score = 0; + // create a Unicode string LogFaceNameW from LogFont->lfFaceName + Status = RtlCreateUnicodeString(&LogFaceNameW, LogFont->lfFaceName); + if (NT_SUCCESS(Status)) + { + // truncate + if ((LF_FACESIZE - 1) * sizeof(WCHAR) < LogFaceNameW.Length) + { + LogFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); + LogFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; + } + + // compare + DPRINT("LogFaceNameW: %S\n", LogFaceNameW.Buffer); + DPRINT("EntryFaceNameW: %S\n", EntryFaceNameW.Buffer); + if (RtlCompareUnicodeString(&LogFaceNameW, &EntryFaceNameW, TRUE) != 0) + { + DPRINT("NOT EQUAL\n"); + // compare + if (CompareSubstituteFamily(&LogFaceNameW, &EntryFaceNameW, 0) != 0) + { + DPRINT("NOT EQUAL\n"); + // Penalty += 10000: lfFaceName: mismatched + Penalty += 10000; + } + else + { + DPRINT("EQUAL\n"); + // Penalty += 500: lfFaceName: mismatched face name but matched substitute name + //Penalty += 500; //FIXME + } + } + RtlFreeUnicodeString(&LogFaceNameW); + } + RtlFreeUnicodeString(&EntryFaceNameW); } - - RtlFreeUnicodeString(&EntryFaceNameW); } - 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->lfWeight != FW_DONTCARE) - { - if (LogFont->lfWeight < Otm->otmTextMetrics.tmWeight) + if (LogFont->lfHeight != 0) { - WeightDiff = Otm->otmTextMetrics.tmWeight - LogFont->lfWeight; + // Penalty += 600: lfHeight: non-vector font and height was higher + if (ABS(LogFont->lfHeight) < TM->tmHeight) + { + Penalty += 600; + } + // Penalty += 600: lfHeight: non-vector font and height was higher + if (LogFont->lfHeight < 0 && ABS(LogFont->lfHeight) < TM->tmHeight) + { + Penalty += 600; + } + // Penalty += 150 * Diff * K: lfHeight: mismatched + if (ABS(LogFont->lfHeight) != TM->tmHeight) + { + double K = 0.04; + // lfHeight: avg. 16. + // 16 * 0.05 = 0.8. + if (ABS(LogFont->lfHeight) > TM->tmHeight) + { + LONG Diff = ABS(LogFont->lfHeight) - TM->tmHeight; + Penalty += 150 * Diff * K; + } + if (ABS(LogFont->lfHeight) < TM->tmHeight) + { + LONG Diff = TM->tmHeight - ABS(LogFont->lfHeight); + Penalty += 150 * Diff * K; + } + } } - else + + // Penalty += 50: height or width scaling required + if ((LogFont->lfHeight != 0 && ABS(LogFont->lfHeight) != TM->tmHeight) || + (LogFont->lfWidth != 0 && LogFont->lfWidth != TM->tmAveCharWidth)) { - WeightDiff = LogFont->lfWeight - Otm->otmTextMetrics.tmWeight; + Penalty += 50; } - Score += (1000 - WeightDiff) / (1000 / 25); + + // Penalty += 50 * Diff * K: mismatched width + if (LogFont->lfWidth != 0) + { + if (LogFont->lfWidth != TM->tmAveCharWidth) + { + double K = 0.08; + // lfWidth: avg. 16. + // 16 * 0.08 = 1.28. + if (LogFont->lfWidth < TM->tmAveCharWidth) + { + LONG Diff = TM->tmAveCharWidth - LogFont->lfWidth; + Penalty += 50 * Diff * K; + } + if (LogFont->lfWidth > TM->tmAveCharWidth) + { + LONG Diff = LogFont->lfWidth - TM->tmAveCharWidth; + 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; + } + + // Penalty += 3 * Diff * K: lfWeight: weight mismatched + if (LogFont->lfWeight != FW_DONTCARE) + { + // lfWeight: 0 to 900. + // 900 * 0.002 = 1.8. + double K = 0.002; + if (LogFont->lfWeight > TM->tmWeight) + { + LONG Diff = LogFont->lfWeight - TM->tmWeight; + Penalty += 3 * Diff * K; + } + if (LogFont->lfWeight < TM->tmWeight) + { + LONG Diff = TM->tmWeight - LogFont->lfWeight; + Penalty += 3 * Diff * K; + } + } } else { - Score += 25; + // Penalty += 3 * Diff * K: lfWeight: weight mismatched + if (LogFont->lfWeight != FW_DONTCARE) + { + // lfWeight: 0 to 900. + // 900 * 0.001 = 0.9. + double K = 0.001; + if (LogFont->lfWeight > TM->tmWeight) + { + LONG Diff = LogFont->lfWeight - TM->tmWeight; + Penalty += 3 * Diff * K; + } + if (LogFont->lfWeight < TM->tmWeight) + { + LONG Diff = TM->tmWeight - LogFont->lfWeight; + Penalty += 3 * Diff * K; + } + } } + // Penalty += 4: lfItalic: mismatched + if (!!LogFont->lfItalic != !!TM->tmItalic) + { + 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; + } + ExFreePoolWithTag(Otm, GDITAG_TEXT); - return Score; + return Penalty; } 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 +3091,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 +3140,7 @@ PTEXTOBJ TextObj; UNICODE_STRING FaceName; PPROCESSINFO Win32Process; - UINT MatchScore; + ULONG MatchPenalty; if (!pTextObj) { @@ -2938,13 +3165,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 +3179,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",