/*++ Copyright (c) 1990 Microsoft Corporation Module Name: misc.c Abstract: This file implements the NT console server font routines. Author: Therese Stowell (thereses) 22-Jan-1991 Revision History: --*/ #include "precomp.h" #pragma hdrstop #ifdef DEBUG_PRINT ULONG gDebugFlag = 0 ; // ULONG gDebugFlag = _DBGOUTPUT | _DBGCHARS | _DBGFONTS | _DBGFONTS2 ; #endif PFONT_INFO FontInfo = NULL; ULONG FontInfoLength; ULONG NumberOfFonts; BOOL gbEnumerateFaces = FALSE; #define FE_ABANDONFONT 0 #define FE_SKIPFONT 1 #define FE_FONTOK 2 /* * Initial default fonts and face names */ PFACENODE gpFaceNames = NULL; /* * TTPoints -- Initial font pixel heights for TT fonts */ SHORT TTPoints[] = { 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72 }; #if defined(FE_SB) /* * TTPointsDbcs -- Initial font pixel heights for TT fonts of DBCS. * So, This list except odd point size because font width is (SBCS:DBCS != 1:2). */ SHORT TTPointsDbcs[] = { 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72 }; #endif typedef struct _FONTENUMDATA { HDC hDC; BOOL bFindFaces; ULONG ulFE; PSHORT pTTPoints; UINT nTTPoints; } FONTENUMDATA, *PFONTENUMDATA; PFACENODE AddFaceNode(PFACENODE *ppStart, LPTSTR ptsz) { PFACENODE pNew; PFACENODE *ppTmp; int cb; /* * Is it already here? */ for (ppTmp = ppStart; *ppTmp; ppTmp = &((*ppTmp)->pNext)) { if (_tcscmp(((*ppTmp)->atch), ptsz) == 0) { // already there ! return *ppTmp; } } cb = (_tcslen(ptsz) + 1) * sizeof(TCHAR); pNew = (PFACENODE)HeapAlloc(RtlProcessHeap(),0,sizeof(FACENODE) + cb); if (pNew == NULL) { return NULL; } pNew->pNext = NULL; pNew->dwFlag = 0; _tcscpy(pNew->atch, ptsz); *ppTmp = pNew; return pNew; } VOID DestroyFaceNodes( VOID ) { PFACENODE pNext; PFACENODE pTmp; pTmp = gpFaceNames; while (pTmp != NULL) { pNext = pTmp->pNext; HeapFree(RtlProcessHeap(), 0, pTmp); pTmp = pNext; } gpFaceNames = NULL; } int AddFont( ENUMLOGFONT *pelf, NEWTEXTMETRIC *pntm, int nFontType, HDC hDC, PFACENODE pFN ) /*++ Add the font desribed by the LOGFONT structure to the font table if it's not already there. --*/ { HFONT hFont; TEXTMETRIC tm; LONG nFont; COORD SizeToShow; COORD SizeActual; COORD SizeWant; BYTE tmFamily; SIZE Size; LPTSTR ptszFace = pelf->elfLogFont.lfFaceName; /* get font info */ SizeWant.X = (SHORT)pelf->elfLogFont.lfWidth; SizeWant.Y = (SHORT)pelf->elfLogFont.lfHeight; CreateBoldFont: pelf->elfLogFont.lfQuality = DEFAULT_QUALITY; hFont = CreateFontIndirect(&pelf->elfLogFont); if (!hFont) { DBGFONTS((" REJECT font (can't create)\n")); return FE_SKIPFONT; // same font in other sizes may still be suitable } DBGFONTS2((" hFont = %lx\n", hFont)); // // BUGBUG // For reasons unbeknownst to me, removing this code causes GDI // to yack, claiming that the font is owned by another process. // SelectObject(hDC, hFont); GetTextMetrics(hDC, &tm); GetTextExtentPoint32(hDC, TEXT("0"), 1, &Size); SizeActual.X = (SHORT)Size.cx; SizeActual.Y = (SHORT)(tm.tmHeight + tm.tmExternalLeading); DBGFONTS2((" actual size %d,%d\n", SizeActual.X, SizeActual.Y)); tmFamily = tm.tmPitchAndFamily; if (TM_IS_TT_FONT(tmFamily) && (SizeWant.Y >= 0)) { SizeToShow = SizeWant; if (SizeWant.X == 0) { // Asking for zero width height gets a default aspect-ratio width. // It's better to show that width rather than 0. SizeToShow.X = SizeActual.X; } } else { SizeToShow = SizeActual; } // // The size shouldn't be zero. This is to help catch Windows Bug #332453. // ASSERT(SizeActual.X != 0 && SizeActual.Y != 0 && "If you hit this please e-mail jasonsch"); DBGFONTS2((" SizeToShow = (%d,%d), SizeActual = (%d,%d)\n", SizeToShow.X, SizeToShow.Y, SizeActual.X, SizeActual.Y)); // // BUGBUG // There's a GDI bug - this assert fails occasionally. //ASSERT (tm.tmMaxCharWidth == pntm->tmMaxCharWidth); /* * NOW, determine whether this font entry has already been cached * LATER : it may be possible to do this before creating the font, if * we can trust the dimensions & other info from pntm. * Sort by size: * 1) By pixelheight (negative Y values) * 2) By height (as shown) * 3) By width (as shown) */ for (nFont = 0; nFont < (LONG)NumberOfFonts; ++nFont) { COORD SizeShown; if (FontInfo[nFont].hFont == NULL) { DBGFONTS(("! Font %x has a NULL hFont\n", nFont)); continue; } if (FontInfo[nFont].SizeWant.X > 0) { SizeShown.X = FontInfo[nFont].SizeWant.X; } else { SizeShown.X = FontInfo[nFont].Size.X; } if (FontInfo[nFont].SizeWant.Y > 0) { // This is a font specified by cell height. SizeShown.Y = FontInfo[nFont].SizeWant.Y; } else { SizeShown.Y = FontInfo[nFont].Size.Y; if (FontInfo[nFont].SizeWant.Y < 0) { // This is a TT font specified by character height. if (SizeWant.Y < 0 && SizeWant.Y > FontInfo[nFont].SizeWant.Y) { // Requested pixelheight is smaller than this one. DBGFONTS(("INSERT %d pt at %x, before %d pt\n", -SizeWant.Y, nFont, -FontInfo[nFont].SizeWant.Y)); break; } } } // DBGFONTS((" SizeShown(%x) = (%d,%d)\n",nFont,SizeShown.X,SizeShown.Y)); if (SIZE_EQUAL(SizeShown, SizeToShow) && FontInfo[nFont].Family == tmFamily && FontInfo[nFont].Weight == tm.tmWeight && _tcscmp(FontInfo[nFont].FaceName, ptszFace) == 0) { /* * Already have this font */ DBGFONTS2((" Already have the font\n")); DeleteObject(hFont); return FE_FONTOK; } if ((SizeToShow.Y < SizeShown.Y) || (SizeToShow.Y == SizeShown.Y && SizeToShow.X < SizeShown.X)) { /* * This new font is smaller than nFont */ DBGFONTS(("INSERT at %x, SizeToShow = (%d,%d)\n", nFont, SizeToShow.X,SizeToShow.Y)); break; } } /* * If we have to grow our font table, do it */ if (NumberOfFonts == FontInfoLength) { PFONT_INFO Temp; FontInfoLength += FONT_INCREMENT; Temp = (PFONT_INFO)HeapReAlloc(RtlProcessHeap(), 0, FontInfo, sizeof(FONT_INFO) * FontInfoLength); if (Temp == NULL) { FontInfoLength -= FONT_INCREMENT; return FE_ABANDONFONT; // no point enumerating more - no memory! } FontInfo = Temp; } /* * The font we are adding should be inserted into the list, * if it is smaller than the last one. */ if (nFont < (LONG)NumberOfFonts) { RtlMoveMemory(&FontInfo[nFont+1], &FontInfo[nFont], sizeof(FONT_INFO) * (NumberOfFonts - nFont)); } /* * Store the font info */ FontInfo[nFont].hFont = hFont; FontInfo[nFont].Family = tmFamily; FontInfo[nFont].Size = SizeActual; if (TM_IS_TT_FONT(tmFamily)) { FontInfo[nFont].SizeWant = SizeWant; } else { FontInfo[nFont].SizeWant.X = 0; FontInfo[nFont].SizeWant.Y = 0; } FontInfo[nFont].Weight = tm.tmWeight; FontInfo[nFont].FaceName = pFN->atch; #if defined(FE_SB) FontInfo[nFont].tmCharSet = tm.tmCharSet; #endif ++NumberOfFonts; /* * If this is a true type font, create a bold version too. */ if (nFontType == TRUETYPE_FONTTYPE && !IS_BOLD(FontInfo[nFont].Weight)) { pelf->elfLogFont.lfWeight = FW_BOLD; goto CreateBoldFont; } return FE_FONTOK; // and continue enumeration } VOID InitializeFonts( VOID ) { EnumerateFonts(EF_DEFFACE); // Just the Default font } VOID DestroyFonts( VOID ) { ULONG FontIndex; if (FontInfo != NULL) { for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) { DeleteObject(FontInfo[FontIndex].hFont); } HeapFree(RtlProcessHeap(), 0, FontInfo); FontInfo = NULL; NumberOfFonts = 0; } DestroyFaceNodes(); } /* * Returns bit combination * FE_ABANDONFONT - do not continue enumerating this font * FE_SKIPFONT - skip this font but keep enumerating * FE_FONTOK - font was created and added to cache or already there */ int FontEnum( ENUMLOGFONT *pelf, NEWTEXTMETRIC *pntm, int nFontType, PFONTENUMDATA pfed ) /*++ Is called exactly once by GDI for each font in the system. This routine is used to store the FONT_INFO structure. --*/ { UINT i; LPTSTR ptszFace = pelf->elfLogFont.lfFaceName; PFACENODE pFN; DBGFONTS((" FontEnum \"%ls\" (%d,%d) weight 0x%lx(%d) %x -- %s\n", ptszFace, pelf->elfLogFont.lfWidth, pelf->elfLogFont.lfHeight, pelf->elfLogFont.lfWeight, pelf->elfLogFont.lfWeight, pelf->elfLogFont.lfCharSet, pfed->bFindFaces ? "Finding Faces" : "Creating Fonts")); // // reject variable width and italic fonts, also tt fonts with neg ac // if ( !(pelf->elfLogFont.lfPitchAndFamily & FIXED_PITCH) || (pelf->elfLogFont.lfItalic) || !(pntm->ntmFlags & NTM_NONNEGATIVE_AC) ) { if (! IsAvailableTTFont(ptszFace)) { DBGFONTS((" REJECT face (dbcs, variable pitch, italic, or neg a&c)\n")); return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT; } } /* * reject TT fonts for whoom family is not modern, that is do not use * FF_DONTCARE // may be surprised unpleasantly * FF_DECORATIVE // likely to be symbol fonts * FF_SCRIPT // cursive, inappropriate for console * FF_SWISS OR FF_ROMAN // variable pitch */ if ((nFontType == TRUETYPE_FONTTYPE) && ((pelf->elfLogFont.lfPitchAndFamily & 0xf0) != FF_MODERN)) { DBGFONTS((" REJECT face (TT but not FF_MODERN)\n")); return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT; } /* * reject non-TT fonts that aren't OEM */ if ((nFontType != TRUETYPE_FONTTYPE) && #if defined(FE_SB) (!gfFESystem || !IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet)) && #endif (pelf->elfLogFont.lfCharSet != OEM_CHARSET)) { DBGFONTS((" REJECT face (not TT nor OEM)\n")); return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT; } /* * reject non-TT fonts that are virtical font */ if ((nFontType != TRUETYPE_FONTTYPE) && (ptszFace[0] == TEXT('@'))) { DBGFONTS((" REJECT face (not TT and TATEGAKI)\n")); return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT; } /* * reject non-TT fonts that aren't Terminal */ if (gfFESystem && (nFontType != TRUETYPE_FONTTYPE) && (_tcscmp(ptszFace, TEXT("Terminal")) != 0)) { DBGFONTS((" REJECT face (not TT nor Terminal)\n")); return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT; } /* * reject Far East TT fonts that aren't Far East charset. */ if (IsAvailableTTFont(ptszFace) && !IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet) && !IsAvailableTTFontCP(ptszFace,0) ) { DBGFONTS((" REJECT face (Far East TT and not Far East charset)\n")); return FE_SKIPFONT; // should be enumerate next charset. } /* * Add or find the facename */ pFN = AddFaceNode(&gpFaceNames, ptszFace); if (pFN == NULL) { return FE_ABANDONFONT; } if (pfed->bFindFaces) { DWORD dwFontType; if (nFontType == TRUETYPE_FONTTYPE) { DBGFONTS(("NEW TT FACE %ls\n", ptszFace)); dwFontType = EF_TTFONT; } else if (nFontType == RASTER_FONTTYPE) { DBGFONTS(("NEW OEM FACE %ls\n",ptszFace)); dwFontType = EF_OEMFONT; } pFN->dwFlag |= dwFontType | EF_NEW; #if defined(FE_SB) if (IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet)) pFN->dwFlag |= EF_DBCSFONT; #endif return FE_SKIPFONT; } if (IS_BOLD(pelf->elfLogFont.lfWeight)) { DBGFONTS2((" A bold font (weight %d)\n", pelf->elfLogFont.lfWeight)); // return FE_SKIPFONT; } /* * Add the font to the table. If this is a true type font, add the * sizes from the array. Otherwise, just add the size we got. */ if (nFontType & TRUETYPE_FONTTYPE) { for (i = 0; i < pfed->nTTPoints; i++) { pelf->elfLogFont.lfHeight = pfed->pTTPoints[i]; pelf->elfLogFont.lfWidth = 0; pelf->elfLogFont.lfWeight = 400; pfed->ulFE |= AddFont(pelf, pntm, nFontType, pfed->hDC, pFN); if (pfed->ulFE & FE_ABANDONFONT) { return FE_ABANDONFONT; } } } else { pfed->ulFE |= AddFont(pelf, pntm, nFontType, pfed->hDC, pFN); if (pfed->ulFE & FE_ABANDONFONT) { return FE_ABANDONFONT; } } return FE_FONTOK; // and continue enumeration } BOOL DoFontEnum( HDC hDC, LPTSTR ptszFace, PSHORT pTTPoints, UINT nTTPoints) { BOOL bDeleteDC = FALSE; FONTENUMDATA fed; LOGFONT LogFont; DBGFONTS(("DoFontEnum \"%ls\"\n", ptszFace)); if (hDC == NULL) { hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL); bDeleteDC = TRUE; } fed.hDC = hDC; fed.bFindFaces = (ptszFace == NULL); fed.ulFE = 0; fed.pTTPoints = pTTPoints; fed.nTTPoints = nTTPoints; RtlZeroMemory(&LogFont, sizeof(LOGFONT)); LogFont.lfCharSet = DEFAULT_CHARSET; if (ptszFace) _tcscpy(LogFont.lfFaceName, ptszFace); /* * EnumFontFamiliesEx function enumerates one font in every face in every character set. */ EnumFontFamiliesEx(hDC, &LogFont, (FONTENUMPROC)FontEnum, (LPARAM)&fed, 0); if (bDeleteDC) { DeleteDC(hDC); } return (fed.ulFE & FE_FONTOK) != 0; } VOID RemoveFace(LPTSTR ptszFace) { DWORD i; int nToRemove = 0; DBGFONTS(("RemoveFace %ls\n", ptszFace)); // // Delete & Remove fonts with Face Name == ptszFace // for (i = 0; i < NumberOfFonts; i++) { if (_tcscmp(FontInfo[i].FaceName, ptszFace) == 0) { BOOL bDeleted = DeleteObject(FontInfo[i].hFont); DBGPRINT(("RemoveFace: hFont %lx was %sdeleted\n", FontInfo[i].hFont, bDeleted ? "" : "NOT ")); FontInfo[i].hFont = NULL; nToRemove++; } else if (nToRemove > 0) { /* * Shuffle from FontInfo[i] down nToRemove slots. */ RtlMoveMemory(&FontInfo[i - nToRemove], &FontInfo[i], sizeof(FONT_INFO)*(NumberOfFonts - i)); NumberOfFonts -= nToRemove; i -= nToRemove; nToRemove = 0; } } NumberOfFonts -= nToRemove; } TCHAR DefaultFaceName[LF_FACESIZE]; COORD DefaultFontSize; BYTE DefaultFontFamily; ULONG DefaultFontIndex = 0; ULONG CurrentFontIndex = 0; NTSTATUS EnumerateFonts( DWORD Flags) { TEXTMETRIC tm; HDC hDC; PFACENODE pFN; ULONG ulOldEnumFilter; BOOL bEnumOEMFace = TRUE; DWORD FontIndex; DWORD dwFontType = 0; DBGFONTS(("EnumerateFonts %lx\n", Flags)); dwFontType = (EF_TTFONT|EF_OEMFONT|EF_DEFFACE) & Flags; if (FontInfo == NULL) { // // allocate memory for the font array // NumberOfFonts = 0; FontInfo = (PFONT_INFO)HeapAlloc(RtlProcessHeap(),0,sizeof(FONT_INFO) * INITIAL_FONTS); if (FontInfo == NULL) return STATUS_NO_MEMORY; FontInfoLength = INITIAL_FONTS; } hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL); // Before enumeration, turn off font enumeration filters. ulOldEnumFilter = SetFontEnumeration(0); SetFontEnumeration(ulOldEnumFilter & ~FE_FILTER_TRUETYPE); if (Flags & EF_DEFFACE) { SelectObject(hDC, GetStockObject(OEM_FIXED_FONT)); GetTextMetrics(hDC, &tm); GetTextFace(hDC, LF_FACESIZE, DefaultFaceName); DefaultFontSize.X = (SHORT)(tm.tmMaxCharWidth); DefaultFontSize.Y = (SHORT)(tm.tmHeight+tm.tmExternalLeading); DefaultFontFamily = tm.tmPitchAndFamily; #if !defined(FE_SB) DBGFONTS(("Default (OEM) Font %ls (%d,%d)\n", DefaultFaceName, DefaultFontSize.X, DefaultFontSize.Y)); #else if (IS_ANY_DBCS_CHARSET(tm.tmCharSet)) DefaultFontSize.X /= 2; DBGFONTS(("Default (OEM) Font %ls (%d,%d) CharSet 0x%02X\n", DefaultFaceName, DefaultFontSize.X, DefaultFontSize.Y, tm.tmCharSet)); #endif // Make sure we are going to enumerate the OEM face. pFN = AddFaceNode(&gpFaceNames, DefaultFaceName); if (pFN) pFN->dwFlag |= EF_DEFFACE | EF_OEMFONT; } if (gbEnumerateFaces) { /* * Set the EF_OLD bit and clear the EF_NEW bit * for all previously available faces */ for (pFN = gpFaceNames; pFN; pFN = pFN->pNext) { pFN->dwFlag |= EF_OLD; pFN->dwFlag &= ~EF_NEW; } // // Use DoFontEnum to get the names of all the suitable Faces // All facenames found will be put in gpFaceNames with // the EF_NEW bit set. // DoFontEnum(hDC, NULL, TTPoints, 1); gbEnumerateFaces = FALSE; } // Use DoFontEnum to get all fonts from the system. Our FontEnum // proc puts just the ones we want into an array // for (pFN = gpFaceNames; pFN; pFN = pFN->pNext) { DBGFONTS(("\"%ls\" is %s%s%s%s%s%s\n", pFN->atch, pFN->dwFlag & EF_NEW ? "NEW " : " ", pFN->dwFlag & EF_OLD ? "OLD " : " ", pFN->dwFlag & EF_ENUMERATED ? "ENUMERATED " : " ", pFN->dwFlag & EF_OEMFONT ? "OEMFONT " : " ", pFN->dwFlag & EF_TTFONT ? "TTFONT " : " ", pFN->dwFlag & EF_DEFFACE ? "DEFFACE " : " ")); if ((pFN->dwFlag & (EF_OLD|EF_NEW)) == EF_OLD) { // The face is no longer available RemoveFace(pFN->atch); pFN->dwFlag &= ~EF_ENUMERATED; continue; } if ((pFN->dwFlag & dwFontType) == 0) { // not the kind of face we want continue; } if (pFN->dwFlag & EF_ENUMERATED) { // we already enumerated this face continue; } if (pFN->dwFlag & EF_TTFONT) { #if defined(FE_SB) if (gfFESystem && !IsAvailableTTFontCP(pFN->atch,0)) DoFontEnum(hDC, pFN->atch, TTPointsDbcs, NELEM(TTPointsDbcs)); else #endif DoFontEnum(hDC, pFN->atch, TTPoints, NELEM(TTPoints)); } else { DoFontEnum(hDC, pFN->atch, NULL, 0); // If we find that the face just enumerated is the same as OEM, // reset flag so we don't try to enumerate it again. if (!_tcsncmp(pFN->atch, DefaultFaceName, LF_FACESIZE)) { bEnumOEMFace = FALSE; } } pFN->dwFlag |= EF_ENUMERATED; } // After enumerating fonts, restore the font enumeration filter. SetFontEnumeration(ulOldEnumFilter); DeleteDC(hDC); #if defined(FE_SB) if (gfFESystem ) { for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) { if (FontInfo[FontIndex].Size.X == DefaultFontSize.X && FontInfo[FontIndex].Size.Y == DefaultFontSize.Y && IS_ANY_DBCS_CHARSET(FontInfo[FontIndex].tmCharSet) && FontInfo[FontIndex].Family == DefaultFontFamily) { break; } } } else { #endif for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) { if (FontInfo[FontIndex].Size.X == DefaultFontSize.X && FontInfo[FontIndex].Size.Y == DefaultFontSize.Y && FontInfo[FontIndex].Family == DefaultFontFamily) { break; } } #if defined(FE_SB) } #endif ASSERT(FontIndex < NumberOfFonts); if (FontIndex < NumberOfFonts) { DefaultFontIndex = FontIndex; } else { DefaultFontIndex = 0; } DBGFONTS(("EnumerateFonts : DefaultFontIndex = %ld\n", DefaultFontIndex)); return STATUS_SUCCESS; }